| // Flags: --expose-internals --no-warnings |
| 'use strict'; |
| |
| // Tests a simple QUIC HTTP/3 client/server round-trip |
| |
| const common = require('../common'); |
| if (!common.hasQuic) |
| common.skip('missing quic'); |
| |
| const Countdown = require('../common/countdown'); |
| const assert = require('assert'); |
| const fs = require('fs'); |
| |
| const { |
| key, |
| cert, |
| ca, |
| debug, |
| kHttp3Alpn, |
| kServerPort, |
| kClientPort, |
| setupKeylog, |
| } = require('../common/quic'); |
| |
| const filedata = fs.readFileSync(__filename, { encoding: 'utf8' }); |
| |
| const { createQuicSocket } = require('net'); |
| const kServerName = 'agent2'; // Intentionally the wrong servername |
| |
| const options = { key, cert, ca, alpn: kHttp3Alpn }; |
| |
| const client = createQuicSocket({ |
| endpoint: { port: kClientPort }, |
| client: options |
| }); |
| const server = createQuicSocket({ |
| endpoint: { port: kServerPort }, |
| server: options |
| }); |
| |
| client.on('close', common.mustCall()); |
| server.on('close', common.mustCall()); |
| |
| const countdown = new Countdown(1, () => { |
| debug('Countdown expired. Destroying sockets'); |
| server.close(); |
| client.close(); |
| }); |
| |
| (async function() { |
| server.on('session', common.mustCall((session) => { |
| debug('QuicServerSession Created'); |
| |
| assert.strictEqual(session.maxStreams.bidi, 100); |
| assert.strictEqual(session.maxStreams.uni, 3); |
| |
| setupKeylog(session); |
| |
| session.on('secure', common.mustCall((_, alpn) => { |
| debug('QuicServerSession handshake completed'); |
| assert.strictEqual(session.alpnProtocol, alpn); |
| })); |
| |
| session.on('stream', common.mustCall((stream) => { |
| debug('Bidirectional, Client-initiated stream %d received', stream.id); |
| const file = fs.createReadStream(__filename); |
| let data = ''; |
| |
| assert(stream.submitInitialHeaders({ ':status': '200' })); |
| |
| file.pipe(stream); |
| stream.setEncoding('utf8'); |
| |
| stream.on('initialHeaders', common.mustCall((headers) => { |
| const expected = [ |
| [ ':path', '/' ], |
| [ ':authority', 'localhost' ], |
| [ ':scheme', 'https' ], |
| [ ':method', 'POST' ] |
| ]; |
| assert.deepStrictEqual(expected, headers); |
| debug('Received expected request headers'); |
| })); |
| stream.on('informationalHeaders', common.mustNotCall()); |
| stream.on('trailingHeaders', common.mustNotCall()); |
| |
| stream.on('data', (chunk) => { |
| data += chunk; |
| }); |
| stream.on('end', common.mustCall(() => { |
| assert.strictEqual(data, filedata); |
| debug('Server received expected data for stream %d', stream.id); |
| })); |
| stream.on('close', common.mustCall()); |
| stream.on('finish', common.mustCall()); |
| })); |
| |
| session.on('close', common.mustCall()); |
| })); |
| |
| await server.listen(); |
| |
| debug('Server is listening on port %d', server.endpoints[0].address.port); |
| |
| const req = await client.connect({ |
| address: 'localhost', |
| port: server.endpoints[0].address.port, |
| servername: kServerName, |
| h3: { maxPushes: 10 } |
| }); |
| debug('QuicClientSession Created'); |
| |
| req.on('close', common.mustCall()); |
| |
| const file = fs.createReadStream(__filename); |
| const stream = await req.openStream(); |
| |
| assert(stream.submitInitialHeaders({ |
| ':method': 'POST', |
| ':scheme': 'https', |
| ':authority': 'localhost', |
| ':path': '/', |
| })); |
| file.pipe(stream); |
| let data = ''; |
| stream.resume(); |
| stream.setEncoding('utf8'); |
| |
| stream.on('initialHeaders', common.mustCall((headers) => { |
| const expected = [ |
| [ ':status', '200' ] |
| ]; |
| assert.deepStrictEqual(expected, headers); |
| debug('Received expected response headers'); |
| })); |
| stream.on('informationalHeaders', common.mustNotCall()); |
| stream.on('trailingHeaders', common.mustNotCall()); |
| |
| stream.on('data', (chunk) => data += chunk); |
| stream.on('finish', common.mustCall()); |
| stream.on('end', common.mustCall(() => { |
| assert.strictEqual(data, filedata); |
| debug('Client received expected data for stream %d', stream.id); |
| })); |
| stream.on('close', common.mustCall(() => { |
| debug('Bidirectional, Client-initiated stream %d closed', stream.id); |
| countdown.dec(); |
| })); |
| debug('Bidirectional, Client-initiated stream %d opened', stream.id); |
| |
| })().then(common.mustCall()); |