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