Skip to content

Commit 10622c6

Browse files
apapirovskiMylesBorins
authored andcommitted
http2: near full http1 compatibility, add tests
Extensive re-work of http1 compatibility layer based on tests in express, on-finished and finalhandler. Fix handling of HEAD method to match http1. Adjust write, end, etc. to call writeHead as in http1 and as expected by user-land modules. Add socket proxy that instead uses the Http2Stream for the vast majority of socket interactions. Add and change tests to closer represent http1 behaviour. Refs: #15633 Refs: https://github.com/expressjs/express/tree/master/test Refs: https://github.com/jshttp/on-finished/blob/master/test/test.js Refs: https://github.com/pillarjs/finalhandler/blob/master/test/test.js PR-URL: #15702 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]> Reviewed-By: Matteo Collina <[email protected]>
1 parent 824b8df commit 10622c6

21 files changed

+867
-179
lines changed

doc/api/errors.md

+6
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,12 @@ SETTINGS. By default, a maximum number of un-acknowledged `SETTINGS` frame may
722722
be sent at any given time. This error code is used when that limit has been
723723
reached.
724724

725+
<a id="ERR_HTTP2_NO_SOCKET_MANIPULATION"></a>
726+
### ERR_HTTP2_NO_SOCKET_MANIPULATION
727+
728+
Used when attempting to read, write, pause, and/or resume a socket attached to
729+
an `Http2Session`.
730+
725731
<a id="ERR_HTTP2_OUT_OF_STREAMS"></a>
726732
### ERR_HTTP2_OUT_OF_STREAMS
727733

doc/api/http2.md

+63-34
Original file line numberDiff line numberDiff line change
@@ -2049,7 +2049,7 @@ console.log(request.headers);
20492049

20502050
See [Headers Object][].
20512051

2052-
### request.httpVersion
2052+
#### request.httpVersion
20532053
<!-- YAML
20542054
added: v8.4.0
20552055
-->
@@ -2120,7 +2120,14 @@ added: v8.4.0
21202120
* `msecs` {number}
21212121
* `callback` {Function}
21222122

2123-
Calls `request.connection.setTimeout(msecs, callback)`.
2123+
Sets the [`Http2Stream`]()'s timeout value to `msecs`. If a callback is
2124+
provided, then it is added as a listener on the `'timeout'` event on
2125+
the response object.
2126+
2127+
If no `'timeout'` listener is added to the request, the response, or
2128+
the server, then [`Http2Stream`]()s are destroyed when they time out. If a
2129+
handler is assigned to the request, the response, or the server's `'timeout'`
2130+
events, timed out sockets must be handled explicitly.
21242131

21252132
Returns `request`.
21262133

@@ -2131,13 +2138,24 @@ added: v8.4.0
21312138

21322139
* {net.Socket}
21332140

2134-
The [`net.Socket`][] object associated with the connection.
2141+
Returns a Proxy object that acts as a `net.Socket` but applies getters,
2142+
setters and methods based on HTTP/2 logic.
2143+
2144+
`destroyed`, `readable`, and `writable` properties will be retrieved from and
2145+
set on `request.stream`.
21352146

2136-
With TLS support, use [`request.socket.getPeerCertificate()`][] to obtain the
2137-
client's authentication details.
2147+
`destroy`, `emit`, `end`, `on` and `once` methods will be called on
2148+
`request.stream`.
21382149

2139-
*Note*: do not use this socket object to send or receive any data. All
2140-
data transfers are managed by HTTP/2 and data might be lost.
2150+
`setTimeout` method will be called on `request.stream.session`.
2151+
2152+
`pause`, `read`, `resume`, and `write` will throw an error with code
2153+
`ERR_HTTP2_NO_SOCKET_MANIPULATION`. See [`Http2Session and Sockets`][] for
2154+
more information.
2155+
2156+
All other interactions will be routed directly to the socket. With TLS support,
2157+
use [`request.socket.getPeerCertificate()`][] to obtain the client's
2158+
authentication details.
21412159

21422160
#### request.stream
21432161
<!-- YAML
@@ -2235,15 +2253,15 @@ passed as the second parameter to the [`'request'`][] event.
22352253
The response implements, but does not inherit from, the [Writable Stream][]
22362254
interface. This is an [`EventEmitter`][] with the following events:
22372255

2238-
### Event: 'close'
2256+
#### Event: 'close'
22392257
<!-- YAML
22402258
added: v8.4.0
22412259
-->
22422260

22432261
Indicates that the underlying [`Http2Stream`]() was terminated before
22442262
[`response.end()`][] was called or able to flush.
22452263

2246-
### Event: 'finish'
2264+
#### Event: 'finish'
22472265
<!-- YAML
22482266
added: v8.4.0
22492267
-->
@@ -2255,7 +2273,7 @@ does not imply that the client has received anything yet.
22552273

22562274
After this event, no more events will be emitted on the response object.
22572275

2258-
### response.addTrailers(headers)
2276+
#### response.addTrailers(headers)
22592277
<!-- YAML
22602278
added: v8.4.0
22612279
-->
@@ -2268,7 +2286,7 @@ message) to the response.
22682286
Attempting to set a header field name or value that contains invalid characters
22692287
will result in a [`TypeError`][] being thrown.
22702288

2271-
### response.connection
2289+
#### response.connection
22722290
<!-- YAML
22732291
added: v8.4.0
22742292
-->
@@ -2277,7 +2295,7 @@ added: v8.4.0
22772295

22782296
See [`response.socket`][].
22792297

2280-
### response.end([data][, encoding][, callback])
2298+
#### response.end([data][, encoding][, callback])
22812299
<!-- YAML
22822300
added: v8.4.0
22832301
-->
@@ -2296,7 +2314,7 @@ If `data` is specified, it is equivalent to calling
22962314
If `callback` is specified, it will be called when the response stream
22972315
is finished.
22982316

2299-
### response.finished
2317+
#### response.finished
23002318
<!-- YAML
23012319
added: v8.4.0
23022320
-->
@@ -2306,7 +2324,7 @@ added: v8.4.0
23062324
Boolean value that indicates whether the response has completed. Starts
23072325
as `false`. After [`response.end()`][] executes, the value will be `true`.
23082326

2309-
### response.getHeader(name)
2327+
#### response.getHeader(name)
23102328
<!-- YAML
23112329
added: v8.4.0
23122330
-->
@@ -2323,7 +2341,7 @@ Example:
23232341
const contentType = response.getHeader('content-type');
23242342
```
23252343

2326-
### response.getHeaderNames()
2344+
#### response.getHeaderNames()
23272345
<!-- YAML
23282346
added: v8.4.0
23292347
-->
@@ -2343,7 +2361,7 @@ const headerNames = response.getHeaderNames();
23432361
// headerNames === ['foo', 'set-cookie']
23442362
```
23452363

2346-
### response.getHeaders()
2364+
#### response.getHeaders()
23472365
<!-- YAML
23482366
added: v8.4.0
23492367
-->
@@ -2371,7 +2389,7 @@ const headers = response.getHeaders();
23712389
// headers === { foo: 'bar', 'set-cookie': ['foo=bar', 'bar=baz'] }
23722390
```
23732391

2374-
### response.hasHeader(name)
2392+
#### response.hasHeader(name)
23752393
<!-- YAML
23762394
added: v8.4.0
23772395
-->
@@ -2388,7 +2406,7 @@ Example:
23882406
const hasContentType = response.hasHeader('content-type');
23892407
```
23902408

2391-
### response.headersSent
2409+
#### response.headersSent
23922410
<!-- YAML
23932411
added: v8.4.0
23942412
-->
@@ -2397,7 +2415,7 @@ added: v8.4.0
23972415

23982416
Boolean (read-only). True if headers were sent, false otherwise.
23992417

2400-
### response.removeHeader(name)
2418+
#### response.removeHeader(name)
24012419
<!-- YAML
24022420
added: v8.4.0
24032421
-->
@@ -2412,7 +2430,7 @@ Example:
24122430
response.removeHeader('Content-Encoding');
24132431
```
24142432

2415-
### response.sendDate
2433+
#### response.sendDate
24162434
<!-- YAML
24172435
added: v8.4.0
24182436
-->
@@ -2425,7 +2443,7 @@ the response if it is not already present in the headers. Defaults to true.
24252443
This should only be disabled for testing; HTTP requires the Date header
24262444
in responses.
24272445

2428-
### response.setHeader(name, value)
2446+
#### response.setHeader(name, value)
24292447
<!-- YAML
24302448
added: v8.4.0
24312449
-->
@@ -2466,7 +2484,7 @@ const server = http2.createServer((req, res) => {
24662484
});
24672485
```
24682486

2469-
### response.setTimeout(msecs[, callback])
2487+
#### response.setTimeout(msecs[, callback])
24702488
<!-- YAML
24712489
added: v8.4.0
24722490
-->
@@ -2485,18 +2503,29 @@ events, timed out sockets must be handled explicitly.
24852503

24862504
Returns `response`.
24872505

2488-
### response.socket
2506+
#### response.socket
24892507
<!-- YAML
24902508
added: v8.4.0
24912509
-->
24922510

24932511
* {net.Socket}
24942512

2495-
Reference to the underlying socket. Usually users will not want to access
2496-
this property. In particular, the socket will not emit `'readable'` events
2497-
because of how the protocol parser attaches to the socket. After
2498-
`response.end()`, the property is nulled. The `socket` may also be accessed
2499-
via `response.connection`.
2513+
Returns a Proxy object that acts as a `net.Socket` but applies getters,
2514+
setters and methods based on HTTP/2 logic.
2515+
2516+
`destroyed`, `readable`, and `writable` properties will be retrieved from and
2517+
set on `response.stream`.
2518+
2519+
`destroy`, `emit`, `end`, `on` and `once` methods will be called on
2520+
`response.stream`.
2521+
2522+
`setTimeout` method will be called on `response.stream.session`.
2523+
2524+
`pause`, `read`, `resume`, and `write` will throw an error with code
2525+
`ERR_HTTP2_NO_SOCKET_MANIPULATION`. See [`Http2Session and Sockets`][] for
2526+
more information.
2527+
2528+
All other interactions will be routed directly to the socket.
25002529

25012530
Example:
25022531

@@ -2509,7 +2538,7 @@ const server = http2.createServer((req, res) => {
25092538
}).listen(3000);
25102539
```
25112540

2512-
### response.statusCode
2541+
#### response.statusCode
25132542
<!-- YAML
25142543
added: v8.4.0
25152544
-->
@@ -2529,7 +2558,7 @@ response.statusCode = 404;
25292558
After response header was sent to the client, this property indicates the
25302559
status code which was sent out.
25312560

2532-
### response.statusMessage
2561+
#### response.statusMessage
25332562
<!-- YAML
25342563
added: v8.4.0
25352564
-->
@@ -2548,7 +2577,7 @@ added: v8.4.0
25482577

25492578
The [`Http2Stream`][] object backing the response.
25502579

2551-
### response.write(chunk[, encoding][, callback])
2580+
#### response.write(chunk[, encoding][, callback])
25522581
<!-- YAML
25532582
added: v8.4.0
25542583
-->
@@ -2586,7 +2615,7 @@ Returns `true` if the entire data was flushed successfully to the kernel
25862615
buffer. Returns `false` if all or part of the data was queued in user memory.
25872616
`'drain'` will be emitted when the buffer is free again.
25882617

2589-
### response.writeContinue()
2618+
#### response.writeContinue()
25902619
<!-- YAML
25912620
added: v8.4.0
25922621
-->
@@ -2595,7 +2624,7 @@ Sends a status `100 Continue` to the client, indicating that the request body
25952624
should be sent. See the [`'checkContinue'`][] event on `Http2Server` and
25962625
`Http2SecureServer`.
25972626

2598-
### response.writeHead(statusCode[, statusMessage][, headers])
2627+
#### response.writeHead(statusCode[, statusMessage][, headers])
25992628
<!-- YAML
26002629
added: v8.4.0
26012630
-->
@@ -2651,7 +2680,7 @@ const server = http2.createServer((req, res) => {
26512680
Attempting to set a header field name or value that contains invalid characters
26522681
will result in a [`TypeError`][] being thrown.
26532682

2654-
### response.createPushResponse(headers, callback)
2683+
#### response.createPushResponse(headers, callback)
26552684
<!-- YAML
26562685
added: v8.4.0
26572686
-->

lib/internal/errors.js

+3
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,9 @@ E('ERR_HTTP2_INVALID_SETTING_VALUE',
173173
E('ERR_HTTP2_INVALID_STREAM', 'The stream has been destroyed');
174174
E('ERR_HTTP2_MAX_PENDING_SETTINGS_ACK',
175175
(max) => `Maximum number of pending settings acknowledgements (${max})`);
176+
E('ERR_HTTP2_NO_SOCKET_MANIPULATION',
177+
'HTTP/2 sockets should not be directly read from, written to, ' +
178+
'paused and/or resumed.');
176179
E('ERR_HTTP2_OUT_OF_STREAMS',
177180
'No stream ID is available because maximum stream ID has been reached');
178181
E('ERR_HTTP2_PAYLOAD_FORBIDDEN',

0 commit comments

Comments
 (0)