|
1 | 1 | import _ = require('lodash');
|
2 |
| -import now = require("performance-now"); |
| 2 | +import now = require('performance-now'); |
3 | 3 | import net = require('net');
|
4 | 4 | import tls = require('tls');
|
5 | 5 | import http = require('http');
|
6 | 6 | import http2 = require('http2');
|
7 | 7 | import * as streams from 'stream';
|
| 8 | + |
| 9 | +import * as semver from 'semver'; |
8 | 10 | import { makeDestroyable, DestroyableServer } from 'destroyable-server';
|
9 | 11 | import httpolyglot = require('@httptoolkit/httpolyglot');
|
10 | 12 | import {
|
@@ -148,17 +150,37 @@ export async function createComboServer(
|
148 | 150 | const ca = await getCA(options.https);
|
149 | 151 | const defaultCert = ca.generateCertificate(options.https.defaultDomain ?? 'localhost');
|
150 | 152 |
|
| 153 | + const serverProtocolPreferences = options.http2 === true |
| 154 | + ? ['h2', 'http/1.1', 'http 1.1'] // 'http 1.1' is non-standard, but used by https-proxy-agent |
| 155 | + : options.http2 === 'fallback' |
| 156 | + ? ['http/1.1', 'http 1.1', 'h2'] |
| 157 | + // options.http2 === false: |
| 158 | + : ['http/1.1', 'http 1.1']; |
| 159 | + |
| 160 | + const ALPNOption: tls.TlsOptions = semver.satisfies(process.version, '>=20.4.0') |
| 161 | + ? { |
| 162 | + // In modern Node (20+), ALPNProtocols will reject unknown protocols. To allow those (so we can |
| 163 | + // at least read the request, and hopefully handle HTTP-like cases - not uncommon) we use the new |
| 164 | + // ALPNCallback feature instead, which lets us dynamically accept unrecognized protocols: |
| 165 | + ALPNCallback: ({ protocols: clientProtocols }) => { |
| 166 | + const preferredProtocol = serverProtocolPreferences.find(p => clientProtocols.includes(p)); |
| 167 | + |
| 168 | + // Wherever possible, we tell the client to use our preferred protocol |
| 169 | + if (preferredProtocol) return preferredProtocol; |
| 170 | + |
| 171 | + // If the client only offers protocols that we don't understand, shrug and accept: |
| 172 | + else return clientProtocols[1]; |
| 173 | + } |
| 174 | + } : { |
| 175 | + // In Node versions without ALPNCallback, we just set preferences directly: |
| 176 | + ALPNProtocols: serverProtocolPreferences |
| 177 | + } |
| 178 | + |
151 | 179 | const tlsServer = tls.createServer({
|
152 | 180 | key: defaultCert.key,
|
153 | 181 | cert: defaultCert.cert,
|
154 | 182 | ca: [defaultCert.ca],
|
155 |
| - ALPNProtocols: options.http2 === true |
156 |
| - ? ['h2', 'http/1.1', 'http 1.1'] // 'http 1.1' is non-standard, but used by https-proxy-agent |
157 |
| - : options.http2 === 'fallback' |
158 |
| - ? ['http/1.1', 'http 1.1', 'h2'] |
159 |
| - // false |
160 |
| - : ['http/1.1', 'http 1.1'], |
161 |
| - |
| 183 | + ...ALPNOption, |
162 | 184 | SNICallback: (domain: string, cb: Function) => {
|
163 | 185 | if (options.debug) console.log(`Generating certificate for ${domain}`);
|
164 | 186 |
|
|
0 commit comments