-
Notifications
You must be signed in to change notification settings - Fork 15
/
context-https.ts
102 lines (86 loc) · 2.41 KB
/
context-https.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
import { SecureClientSessionOptions } from "http2";
import { connect, ConnectionOptions, TLSSocket } from "tls";
import { HttpProtocols } from "./core";
import { AltNameMatch, parseOrigin } from "./san";
const needsSocketHack = [ "12", "13" ]
.includes( process.versions.node.split( '.' )[ 0 ] );
const alpnProtocols =
{
http1: Buffer.from( "\x08http/1.1" ),
http2: Buffer.from( "\x02h2" ),
};
export interface HttpsSocketResult
{
socket: TLSSocket;
protocol: "http1" | "http2";
altNameMatch: AltNameMatch;
}
const defaultMethod: Array< HttpProtocols > = [ "http2", "http1" ];
export function connectTLS(
host: string,
port: string,
protocols: ReadonlyArray< HttpProtocols >,
connOpts: SecureClientSessionOptions
): Promise< HttpsSocketResult >
{
const usedProtocol = new Set< string >( );
const _protocols = protocols.filter( protocol =>
{
if ( protocol !== "http1" && protocol !== "http2" )
return false;
if ( usedProtocol.has( protocol ) )
return false;
usedProtocol.add( protocol );
return true;
} );
const orderedProtocols = Buffer.concat(
( _protocols.length !== 0 ? _protocols : defaultMethod )
.map( protocol => alpnProtocols[ protocol ] )
);
const opts: ConnectionOptions = {
...connOpts,
ALPNProtocols: orderedProtocols,
servername: host,
};
return new Promise< HttpsSocketResult >( ( resolve, reject ) =>
{
const socket: TLSSocket = connect( parseInt( port, 10 ), host, opts,
( ) =>
{
const { authorized, authorizationError, alpnProtocol = "" } =
socket;
const cert = socket.getPeerCertificate( );
const altNameMatch = parseOrigin( cert );
if ( !authorized && opts.rejectUnauthorized !== false )
return reject( authorizationError );
if (
!alpnProtocol ||
![ "h2", "http/1.1", "http/1.0" ].includes( alpnProtocol )
)
{
// Maybe the server doesn't understand ALPN, enforce
// user-provided protocol, or fallback to HTTP/1
if ( _protocols.length === 1 )
return resolve( {
altNameMatch,
protocol: _protocols[ 0 ],
socket,
} );
else
return resolve( {
altNameMatch,
protocol: "http1",
socket,
} );
}
const protocol = alpnProtocol === "h2" ? "http2" : "http1";
resolve( { socket, protocol, altNameMatch } );
} );
if ( needsSocketHack )
socket.once( 'secureConnect', ( ) =>
{
( socket as any ).secureConnecting = false;
} );
socket.once( "error", reject );
} );
}