1'use strict'; 2 3const common = require('../common'); 4const fixtures = require('../common/fixtures'); 5 6if (!common.hasCrypto) 7 common.skip('missing crypto'); 8 9const { strictEqual, ok } = require('assert'); 10const { createSecureContext } = require('tls'); 11const { createSecureServer, connect } = require('http2'); 12const { get } = require('https'); 13const { parse } = require('url'); 14const { connect: tls } = require('tls'); 15const { Duplex } = require('stream'); 16 17const countdown = (count, done) => () => --count === 0 && done(); 18 19const key = fixtures.readKey('agent8-key.pem'); 20const cert = fixtures.readKey('agent8-cert.pem'); 21const ca = fixtures.readKey('fake-startcom-root-cert.pem'); 22 23const clientOptions = { secureContext: createSecureContext({ ca }) }; 24 25function onRequest(request, response) { 26 const { socket: { alpnProtocol } } = request.httpVersion === '2.0' ? 27 request.stream.session : request; 28 response.writeHead(200, { 'content-type': 'application/json' }); 29 response.end(JSON.stringify({ 30 alpnProtocol, 31 httpVersion: request.httpVersion 32 })); 33} 34 35function onSession(session, next) { 36 const headers = { 37 ':path': '/', 38 ':method': 'GET', 39 ':scheme': 'https', 40 ':authority': `localhost:${this.server.address().port}` 41 }; 42 43 const request = session.request(headers); 44 request.on('response', common.mustCall((headers) => { 45 strictEqual(headers[':status'], 200); 46 strictEqual(headers['content-type'], 'application/json'); 47 })); 48 request.setEncoding('utf8'); 49 let raw = ''; 50 request.on('data', (chunk) => { raw += chunk; }); 51 request.on('end', common.mustCall(() => { 52 const { alpnProtocol, httpVersion } = JSON.parse(raw); 53 strictEqual(alpnProtocol, 'h2'); 54 strictEqual(httpVersion, '2.0'); 55 56 session.close(); 57 this.cleanup(); 58 59 if (typeof next === 'function') { 60 next(); 61 } 62 })); 63 request.end(); 64} 65 66// HTTP/2 & HTTP/1.1 server 67{ 68 const server = createSecureServer( 69 { cert, key, allowHTTP1: true }, 70 common.mustCall(onRequest, 2) 71 ); 72 73 server.listen(0); 74 75 server.on('listening', common.mustCall(() => { 76 const { port } = server.address(); 77 const origin = `https://localhost:${port}`; 78 79 const cleanup = countdown(2, () => server.close()); 80 81 // HTTP/2 client 82 connect( 83 origin, 84 clientOptions, 85 common.mustCall(onSession.bind({ cleanup, server })) 86 ); 87 88 // HTTP/1.1 client 89 get( 90 Object.assign(parse(origin), clientOptions), 91 common.mustCall((response) => { 92 strictEqual(response.statusCode, 200); 93 strictEqual(response.statusMessage, 'OK'); 94 strictEqual(response.headers['content-type'], 'application/json'); 95 96 response.setEncoding('utf8'); 97 let raw = ''; 98 response.on('data', (chunk) => { raw += chunk; }); 99 response.on('end', common.mustCall(() => { 100 const { alpnProtocol, httpVersion } = JSON.parse(raw); 101 strictEqual(alpnProtocol, false); 102 strictEqual(httpVersion, '1.1'); 103 104 cleanup(); 105 })); 106 }) 107 ); 108 })); 109} 110 111// HTTP/2-only server 112{ 113 const server = createSecureServer( 114 { cert, key }, 115 common.mustCall(onRequest) 116 ); 117 118 server.once('unknownProtocol', common.mustCall((socket) => { 119 strictEqual(socket instanceof Duplex, true); 120 socket.destroy(); 121 })); 122 123 server.listen(0); 124 125 server.on('listening', common.mustCall(() => { 126 const { port } = server.address(); 127 const origin = `https://localhost:${port}`; 128 129 const cleanup = countdown(3, () => server.close()); 130 131 // HTTP/2 client 132 connect( 133 origin, 134 clientOptions, 135 common.mustCall(function(session) { 136 onSession.call({ cleanup, server }, 137 session, 138 common.mustCall(testNoTls)); 139 }) 140 ); 141 142 function testNoTls() { 143 // HTTP/1.1 client 144 get(Object.assign(parse(origin), clientOptions), common.mustNotCall()) 145 .on('error', common.mustCall(cleanup)) 146 .on('error', common.mustCall(testWrongALPN)) 147 .end(); 148 } 149 150 function testWrongALPN() { 151 // Incompatible ALPN TLS client 152 let text = ''; 153 tls(Object.assign({ port, ALPNProtocols: ['fake'] }, clientOptions)) 154 .setEncoding('utf8') 155 .on('data', (chunk) => text += chunk) 156 .on('end', common.mustCall(() => { 157 ok(/Unknown ALPN Protocol, expected `h2` to be available/.test(text)); 158 cleanup(); 159 })); 160 } 161 })); 162} 163