@httptoolkit/httpolyglot 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  /// <reference types="node" />
2
2
  import * as net from 'net';
3
+ import * as tls from 'tls';
3
4
  import * as http from 'http';
4
5
  import * as https from 'https';
5
6
  declare module 'net' {
@@ -11,10 +12,41 @@ export declare class Server extends net.Server {
11
12
  private _httpServer;
12
13
  private _http2Server;
13
14
  private _tlsServer;
15
+ /**
16
+ * Create an Httpolyglot instance with just a request listener to support plain-text
17
+ * HTTP & HTTP/2 on the same port, with incoming TLS connections being closed immediately.
18
+ */
14
19
  constructor(requestListener: http.RequestListener);
15
- constructor(tlsConfig: https.ServerOptions, requestListener: http.RequestListener);
20
+ /**
21
+ * Call with a full TLS configuration to create a TLS+HTTP+HTTP/2 server, which can
22
+ * support all protocols on the same port.
23
+ */
24
+ constructor(config: https.ServerOptions, requestListener: http.RequestListener);
25
+ /**
26
+ * Pass an existing TLS server, instead of TLS configuration, to create a TLS+HTTP+HTTP/2
27
+ * server. All incoming TLS requests will be emitted as 'connection' events on the given
28
+ * TLS server, and all 'secureConnection' events coming from the TLS server will be
29
+ * handled according to the connection type detected on that socket.
30
+ */
31
+ constructor(tlsServer: tls.Server, requestListener: http.RequestListener);
16
32
  private connectionListener;
33
+ private tlsListener;
17
34
  private http2Listener;
18
35
  }
36
+ /**
37
+ * Create an Httpolyglot instance with just a request listener to support plain-text
38
+ * HTTP & HTTP/2 on the same port, with incoming TLS connections being closed immediately.
39
+ */
19
40
  export declare function createServer(requestListener: http.RequestListener): Server;
41
+ /**
42
+ * Create an instance with a full TLS configuration to create a TLS+HTTP+HTTP/2 server, which can
43
+ * support all protocols on the same port.
44
+ */
20
45
  export declare function createServer(tlsConfig: https.ServerOptions, requestListener: http.RequestListener): Server;
46
+ /**
47
+ * Create an instance around an existing TLS server, instead of TLS configuration, to create a
48
+ * TLS+HTTP+HTTP/2 server with custom TLS handling. All incoming TLS requests will be emitted as
49
+ * 'connection' events on the given TLS server, and all 'secureConnection' events coming from the
50
+ * TLS server will be handled according to the connection type detected on that socket.
51
+ */
52
+ export declare function createServer(tlsServer: tls.Server, requestListener: http.RequestListener): Server;
package/dist/index.js CHANGED
@@ -10,19 +10,25 @@ function onError(err) { }
10
10
  const TLS_HANDSHAKE_BYTE = 0x16; // SSLv3+ or TLS handshake
11
11
  const HTTP2_PREFACE = 'PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n';
12
12
  const HTTP2_PREFACE_BUFFER = Buffer.from(HTTP2_PREFACE);
13
+ const NODE_MAJOR_VERSION = parseInt(process.version.slice(1).split('.')[0], 10);
13
14
  class Server extends net.Server {
14
- constructor(configOrListener, listener) {
15
+ constructor(configOrServerOrListener, listener) {
15
16
  // We just act as a plain TCP server, accepting and examing
16
17
  // each connection, then passing it to the right subserver.
17
18
  super((socket) => this.connectionListener(socket));
18
19
  let tlsConfig;
20
+ let tlsServer;
19
21
  let requestListener;
20
- if (typeof configOrListener === 'function') {
21
- requestListener = configOrListener;
22
+ if (typeof configOrServerOrListener === 'function') {
23
+ requestListener = configOrServerOrListener;
22
24
  tlsConfig = undefined;
23
25
  }
26
+ else if (configOrServerOrListener instanceof tls.Server) {
27
+ tlsServer = configOrServerOrListener;
28
+ requestListener = listener;
29
+ }
24
30
  else {
25
- tlsConfig = configOrListener;
31
+ tlsConfig = configOrServerOrListener;
26
32
  requestListener = listener;
27
33
  }
28
34
  // We bind the request listener, so 'this' always refers to us, not each subserver.
@@ -31,17 +37,16 @@ class Server extends net.Server {
31
37
  // Create subservers for each supported protocol:
32
38
  this._httpServer = new http.Server(boundListener);
33
39
  this._http2Server = http2.createServer({}, boundListener);
34
- if (typeof tlsConfig === 'object') {
40
+ if (tlsServer) {
41
+ // If we've been given a preconfigured TLS server, we use that directly, and
42
+ // subscribe to connections there
43
+ this._tlsServer = tlsServer;
44
+ this._tlsServer.on('secureConnection', this.tlsListener.bind(this));
45
+ }
46
+ else if (typeof tlsConfig === 'object') {
35
47
  // If we have TLS config, create a TLS server, which will pass sockets to
36
48
  // the relevant subserver once the TLS connection is set up.
37
- this._tlsServer = new tls.Server(tlsConfig, (tlsSocket) => {
38
- if (tlsSocket.alpnProtocol === false || tlsSocket.alpnProtocol === 'http/1.1') {
39
- this._httpServer.emit('connection', tlsSocket);
40
- }
41
- else {
42
- this._http2Server.emit('connection', tlsSocket);
43
- }
44
- });
49
+ this._tlsServer = new tls.Server(tlsConfig, this.tlsListener.bind(this));
45
50
  }
46
51
  else {
47
52
  // Fake server that rejects all connections:
@@ -99,6 +104,14 @@ class Server extends net.Server {
99
104
  }
100
105
  }
101
106
  }
107
+ tlsListener(tlsSocket) {
108
+ if (tlsSocket.alpnProtocol === false || tlsSocket.alpnProtocol === 'http/1.1') {
109
+ this._httpServer.emit('connection', tlsSocket);
110
+ }
111
+ else {
112
+ this._http2Server.emit('connection', tlsSocket);
113
+ }
114
+ }
102
115
  http2Listener(socket, pastData) {
103
116
  const h1Server = this._httpServer;
104
117
  const h2Server = this._http2Server;
@@ -108,11 +121,18 @@ class Server extends net.Server {
108
121
  socket.unshift(data);
109
122
  if (data.slice(0, HTTP2_PREFACE_BUFFER.length).equals(HTTP2_PREFACE_BUFFER)) {
110
123
  // We have a full match for the preface - it's definitely HTTP/2.
111
- // For HTTP/2 we hit issues when passing non-socket streams (like HTTP/2 streams,
112
- // for proxying H2-over-H2). Marking the sockets like this resolves that:
113
- const socketWithInternals = socket;
114
- if (socketWithInternals._handle) {
115
- socketWithInternals._handle.isStreamBase = false;
124
+ // For HTTP/2 we hit issues when passing non-socket streams (like H2 streams for proxying H2-over-H2).
125
+ if (NODE_MAJOR_VERSION <= 12) {
126
+ // For Node 12 and older, we need a (later deprecated) stream wrapper:
127
+ const StreamWrapper = require('_stream_wrap');
128
+ socket = new StreamWrapper(socket);
129
+ }
130
+ else {
131
+ // For newer node, we can fix this with a quick patch here:
132
+ const socketWithInternals = socket;
133
+ if (socketWithInternals._handle) {
134
+ socketWithInternals._handle.isStreamBase = false;
135
+ }
116
136
  }
117
137
  h2Server.emit('connection', socket);
118
138
  return;
@@ -137,8 +157,8 @@ class Server extends net.Server {
137
157
  }
138
158
  }
139
159
  exports.Server = Server;
140
- function createServer(configOrListener, listener) {
141
- return new Server(configOrListener, listener);
160
+ function createServer(configOrServerOrListener, listener) {
161
+ return new Server(configOrServerOrListener, listener);
142
162
  }
143
163
  exports.createServer = createServer;
144
164
  ;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@httptoolkit/httpolyglot",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "author": "Tim Perry <pimterry@gmail.com>",
5
5
  "description": "Serve http and https connections over the same port with node.js",
6
6
  "main": "./dist/index.js",
package/src/index.ts CHANGED
@@ -18,6 +18,8 @@ const TLS_HANDSHAKE_BYTE = 0x16; // SSLv3+ or TLS handshake
18
18
  const HTTP2_PREFACE = 'PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n';
19
19
  const HTTP2_PREFACE_BUFFER = Buffer.from(HTTP2_PREFACE);
20
20
 
21
+ const NODE_MAJOR_VERSION = parseInt(process.version.slice(1).split('.')[0], 10);
22
+
21
23
  type Http2Listener = (request: http2.Http2ServerRequest, response: http2.Http2ServerResponse) => void;
22
24
 
23
25
  export class Server extends net.Server {
@@ -26,21 +28,46 @@ export class Server extends net.Server {
26
28
  private _http2Server: http2.Http2Server;
27
29
  private _tlsServer: EventEmitter;
28
30
 
31
+ /**
32
+ * Create an Httpolyglot instance with just a request listener to support plain-text
33
+ * HTTP & HTTP/2 on the same port, with incoming TLS connections being closed immediately.
34
+ */
29
35
  constructor(requestListener: http.RequestListener);
30
- constructor(tlsConfig: https.ServerOptions, requestListener: http.RequestListener);
31
- constructor(configOrListener: https.ServerOptions | http.RequestListener, listener?: http.RequestListener) {
36
+ /**
37
+ * Call with a full TLS configuration to create a TLS+HTTP+HTTP/2 server, which can
38
+ * support all protocols on the same port.
39
+ */
40
+ constructor(config: https.ServerOptions, requestListener: http.RequestListener);
41
+ /**
42
+ * Pass an existing TLS server, instead of TLS configuration, to create a TLS+HTTP+HTTP/2
43
+ * server. All incoming TLS requests will be emitted as 'connection' events on the given
44
+ * TLS server, and all 'secureConnection' events coming from the TLS server will be
45
+ * handled according to the connection type detected on that socket.
46
+ */
47
+ constructor(tlsServer: tls.Server, requestListener: http.RequestListener);
48
+ constructor(
49
+ configOrServerOrListener:
50
+ | https.ServerOptions
51
+ | tls.Server
52
+ | http.RequestListener,
53
+ listener?: http.RequestListener
54
+ ) {
32
55
  // We just act as a plain TCP server, accepting and examing
33
56
  // each connection, then passing it to the right subserver.
34
57
  super((socket) => this.connectionListener(socket));
35
58
 
36
59
  let tlsConfig: https.ServerOptions | undefined;
60
+ let tlsServer: tls.Server | undefined;
37
61
  let requestListener: http.RequestListener;
38
62
 
39
- if (typeof configOrListener === 'function') {
40
- requestListener = configOrListener;
63
+ if (typeof configOrServerOrListener === 'function') {
64
+ requestListener = configOrServerOrListener;
41
65
  tlsConfig = undefined;
66
+ } else if (configOrServerOrListener instanceof tls.Server) {
67
+ tlsServer = configOrServerOrListener;
68
+ requestListener = listener!;
42
69
  } else {
43
- tlsConfig = configOrListener;
70
+ tlsConfig = configOrServerOrListener;
44
71
  requestListener = listener!;
45
72
  }
46
73
 
@@ -52,16 +79,15 @@ export class Server extends net.Server {
52
79
  this._httpServer = new http.Server(boundListener);
53
80
  this._http2Server = http2.createServer({}, boundListener as any as Http2Listener);
54
81
 
55
- if (typeof tlsConfig === 'object') {
82
+ if (tlsServer) {
83
+ // If we've been given a preconfigured TLS server, we use that directly, and
84
+ // subscribe to connections there
85
+ this._tlsServer = tlsServer;
86
+ this._tlsServer.on('secureConnection', this.tlsListener.bind(this));
87
+ } else if (typeof tlsConfig === 'object') {
56
88
  // If we have TLS config, create a TLS server, which will pass sockets to
57
89
  // the relevant subserver once the TLS connection is set up.
58
- this._tlsServer = new tls.Server(tlsConfig, (tlsSocket) => {
59
- if (tlsSocket.alpnProtocol === false || tlsSocket.alpnProtocol === 'http/1.1') {
60
- this._httpServer.emit('connection', tlsSocket);
61
- } else {
62
- this._http2Server.emit('connection', tlsSocket);
63
- }
64
- });
90
+ this._tlsServer = new tls.Server(tlsConfig, this.tlsListener.bind(this));
65
91
  } else {
66
92
  // Fake server that rejects all connections:
67
93
  this._tlsServer = new EventEmitter();
@@ -124,6 +150,14 @@ export class Server extends net.Server {
124
150
  }
125
151
  }
126
152
 
153
+ private tlsListener(tlsSocket: tls.TLSSocket) {
154
+ if (tlsSocket.alpnProtocol === false || tlsSocket.alpnProtocol === 'http/1.1') {
155
+ this._httpServer.emit('connection', tlsSocket);
156
+ } else {
157
+ this._http2Server.emit('connection', tlsSocket);
158
+ }
159
+ }
160
+
127
161
  private http2Listener(socket: net.Socket, pastData?: Buffer) {
128
162
  const h1Server = this._httpServer;
129
163
  const h2Server = this._http2Server;
@@ -136,11 +170,17 @@ export class Server extends net.Server {
136
170
  if (data.slice(0, HTTP2_PREFACE_BUFFER.length).equals(HTTP2_PREFACE_BUFFER)) {
137
171
  // We have a full match for the preface - it's definitely HTTP/2.
138
172
 
139
- // For HTTP/2 we hit issues when passing non-socket streams (like HTTP/2 streams,
140
- // for proxying H2-over-H2). Marking the sockets like this resolves that:
141
- const socketWithInternals = socket as { _handle?: { isStreamBase?: boolean } };
142
- if (socketWithInternals._handle) {
143
- socketWithInternals._handle.isStreamBase = false;
173
+ // For HTTP/2 we hit issues when passing non-socket streams (like H2 streams for proxying H2-over-H2).
174
+ if (NODE_MAJOR_VERSION <= 12) {
175
+ // For Node 12 and older, we need a (later deprecated) stream wrapper:
176
+ const StreamWrapper = require('_stream_wrap');
177
+ socket = new StreamWrapper(socket);
178
+ } else {
179
+ // For newer node, we can fix this with a quick patch here:
180
+ const socketWithInternals = socket as { _handle?: { isStreamBase?: boolean } };
181
+ if (socketWithInternals._handle) {
182
+ socketWithInternals._handle.isStreamBase = false;
183
+ }
144
184
  }
145
185
 
146
186
  h2Server.emit('connection', socket);
@@ -165,8 +205,28 @@ export class Server extends net.Server {
165
205
  }
166
206
  }
167
207
 
208
+ /**
209
+ * Create an Httpolyglot instance with just a request listener to support plain-text
210
+ * HTTP & HTTP/2 on the same port, with incoming TLS connections being closed immediately.
211
+ */
168
212
  export function createServer(requestListener: http.RequestListener): Server;
213
+ /**
214
+ * Create an instance with a full TLS configuration to create a TLS+HTTP+HTTP/2 server, which can
215
+ * support all protocols on the same port.
216
+ */
169
217
  export function createServer(tlsConfig: https.ServerOptions, requestListener: http.RequestListener): Server;
170
- export function createServer(configOrListener: https.ServerOptions | http.RequestListener, listener?: http.RequestListener) {
171
- return new Server(configOrListener as any, listener as any);
218
+ /**
219
+ * Create an instance around an existing TLS server, instead of TLS configuration, to create a
220
+ * TLS+HTTP+HTTP/2 server with custom TLS handling. All incoming TLS requests will be emitted as
221
+ * 'connection' events on the given TLS server, and all 'secureConnection' events coming from the
222
+ * TLS server will be handled according to the connection type detected on that socket.
223
+ */
224
+ export function createServer(tlsServer: tls.Server, requestListener: http.RequestListener): Server;
225
+ export function createServer(configOrServerOrListener:
226
+ | https.ServerOptions
227
+ | http.RequestListener
228
+ | tls.Server,
229
+ listener?: http.RequestListener
230
+ ) {
231
+ return new Server(configOrServerOrListener as any, listener as any);
172
232
  };