Permalink
Browse files

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 <jasnell@gmail.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
  • Loading branch information...
apapirovski authored and mcollina committed Oct 3, 2017
1 parent 4f339b5 commit 2da7d9b8207d9c35d199734da75fa2bb15f326b6
View
@@ -802,6 +802,12 @@ SETTINGS. By default, a maximum number of un-acknowledged `SETTINGS` frame may
be sent at any given time. This error code is used when that limit has been
reached.
<a id="ERR_HTTP2_NO_SOCKET_MANIPULATION"></a>
### ERR_HTTP2_NO_SOCKET_MANIPULATION
Used when attempting to read, write, pause, and/or resume a socket attached to
an `Http2Session`.
<a id="ERR_HTTP2_OUT_OF_STREAMS"></a>
### ERR_HTTP2_OUT_OF_STREAMS
View
@@ -2046,7 +2046,7 @@ console.log(request.headers);
See [Headers Object][].
### request.httpVersion
#### request.httpVersion
<!-- YAML
added: v8.4.0
-->
@@ -2117,7 +2117,14 @@ added: v8.4.0
* `msecs` {number}
* `callback` {Function}
Calls `request.connection.setTimeout(msecs, callback)`.
Sets the [`Http2Stream`]()'s timeout value to `msecs`. If a callback is
provided, then it is added as a listener on the `'timeout'` event on
the response object.
If no `'timeout'` listener is added to the request, the response, or
the server, then [`Http2Stream`]()s are destroyed when they time out. If a
handler is assigned to the request, the response, or the server's `'timeout'`
events, timed out sockets must be handled explicitly.
Returns `request`.
@@ -2128,13 +2135,24 @@ added: v8.4.0
* {net.Socket}
The [`net.Socket`][] object associated with the connection.
Returns a Proxy object that acts as a `net.Socket` but applies getters,
setters and methods based on HTTP/2 logic.
`destroyed`, `readable`, and `writable` properties will be retrieved from and
set on `request.stream`.
With TLS support, use [`request.socket.getPeerCertificate()`][] to obtain the
client's authentication details.
`destroy`, `emit`, `end`, `on` and `once` methods will be called on
`request.stream`.
*Note*: do not use this socket object to send or receive any data. All
data transfers are managed by HTTP/2 and data might be lost.
`setTimeout` method will be called on `request.stream.session`.
`pause`, `read`, `resume`, and `write` will throw an error with code
`ERR_HTTP2_NO_SOCKET_MANIPULATION`. See [`Http2Session and Sockets`][] for
more information.
All other interactions will be routed directly to the socket. With TLS support,
use [`request.socket.getPeerCertificate()`][] to obtain the client's
authentication details.
#### request.stream
<!-- YAML
@@ -2232,15 +2250,15 @@ passed as the second parameter to the [`'request'`][] event.
The response implements, but does not inherit from, the [Writable Stream][]
interface. This is an [`EventEmitter`][] with the following events:
### Event: 'close'
#### Event: 'close'
<!-- YAML
added: v8.4.0
-->
Indicates that the underlying [`Http2Stream`]() was terminated before
[`response.end()`][] was called or able to flush.
### Event: 'finish'
#### Event: 'finish'
<!-- YAML
added: v8.4.0
-->
@@ -2252,7 +2270,7 @@ does not imply that the client has received anything yet.
After this event, no more events will be emitted on the response object.
### response.addTrailers(headers)
#### response.addTrailers(headers)
<!-- YAML
added: v8.4.0
-->
@@ -2265,7 +2283,7 @@ message) to the response.
Attempting to set a header field name or value that contains invalid characters
will result in a [`TypeError`][] being thrown.
### response.connection
#### response.connection
<!-- YAML
added: v8.4.0
-->
@@ -2274,7 +2292,7 @@ added: v8.4.0
See [`response.socket`][].
### response.end([data][, encoding][, callback])
#### response.end([data][, encoding][, callback])
<!-- YAML
added: v8.4.0
-->
@@ -2293,7 +2311,7 @@ If `data` is specified, it is equivalent to calling
If `callback` is specified, it will be called when the response stream
is finished.
### response.finished
#### response.finished
<!-- YAML
added: v8.4.0
-->
@@ -2303,7 +2321,7 @@ added: v8.4.0
Boolean value that indicates whether the response has completed. Starts
as `false`. After [`response.end()`][] executes, the value will be `true`.
### response.getHeader(name)
#### response.getHeader(name)
<!-- YAML
added: v8.4.0
-->
@@ -2320,7 +2338,7 @@ Example:
const contentType = response.getHeader('content-type');
```
### response.getHeaderNames()
#### response.getHeaderNames()
<!-- YAML
added: v8.4.0
-->
@@ -2340,7 +2358,7 @@ const headerNames = response.getHeaderNames();
// headerNames === ['foo', 'set-cookie']
```
### response.getHeaders()
#### response.getHeaders()
<!-- YAML
added: v8.4.0
-->
@@ -2368,7 +2386,7 @@ const headers = response.getHeaders();
// headers === { foo: 'bar', 'set-cookie': ['foo=bar', 'bar=baz'] }
```
### response.hasHeader(name)
#### response.hasHeader(name)
<!-- YAML
added: v8.4.0
-->
@@ -2385,7 +2403,7 @@ Example:
const hasContentType = response.hasHeader('content-type');
```
### response.headersSent
#### response.headersSent
<!-- YAML
added: v8.4.0
-->
@@ -2394,7 +2412,7 @@ added: v8.4.0
Boolean (read-only). True if headers were sent, false otherwise.
### response.removeHeader(name)
#### response.removeHeader(name)
<!-- YAML
added: v8.4.0
-->
@@ -2409,7 +2427,7 @@ Example:
response.removeHeader('Content-Encoding');
```
### response.sendDate
#### response.sendDate
<!-- YAML
added: v8.4.0
-->
@@ -2422,7 +2440,7 @@ the response if it is not already present in the headers. Defaults to true.
This should only be disabled for testing; HTTP requires the Date header
in responses.
### response.setHeader(name, value)
#### response.setHeader(name, value)
<!-- YAML
added: v8.4.0
-->
@@ -2463,7 +2481,7 @@ const server = http2.createServer((req, res) => {
});
```
### response.setTimeout(msecs[, callback])
#### response.setTimeout(msecs[, callback])
<!-- YAML
added: v8.4.0
-->
@@ -2482,18 +2500,29 @@ events, timed out sockets must be handled explicitly.
Returns `response`.
### response.socket
#### response.socket
<!-- YAML
added: v8.4.0
-->
* {net.Socket}
Reference to the underlying socket. Usually users will not want to access
this property. In particular, the socket will not emit `'readable'` events
because of how the protocol parser attaches to the socket. After
`response.end()`, the property is nulled. The `socket` may also be accessed
via `response.connection`.
Returns a Proxy object that acts as a `net.Socket` but applies getters,
setters and methods based on HTTP/2 logic.
`destroyed`, `readable`, and `writable` properties will be retrieved from and
set on `response.stream`.
`destroy`, `emit`, `end`, `on` and `once` methods will be called on
`response.stream`.
`setTimeout` method will be called on `response.stream.session`.
`pause`, `read`, `resume`, and `write` will throw an error with code
`ERR_HTTP2_NO_SOCKET_MANIPULATION`. See [`Http2Session and Sockets`][] for
more information.
All other interactions will be routed directly to the socket.
Example:
@@ -2506,7 +2535,7 @@ const server = http2.createServer((req, res) => {
}).listen(3000);
```
### response.statusCode
#### response.statusCode
<!-- YAML
added: v8.4.0
-->
@@ -2526,7 +2555,7 @@ response.statusCode = 404;
After response header was sent to the client, this property indicates the
status code which was sent out.
### response.statusMessage
#### response.statusMessage
<!-- YAML
added: v8.4.0
-->
@@ -2545,7 +2574,7 @@ added: v8.4.0
The [`Http2Stream`][] object backing the response.
### response.write(chunk[, encoding][, callback])
#### response.write(chunk[, encoding][, callback])
<!-- YAML
added: v8.4.0
-->
@@ -2583,7 +2612,7 @@ Returns `true` if the entire data was flushed successfully to the kernel
buffer. Returns `false` if all or part of the data was queued in user memory.
`'drain'` will be emitted when the buffer is free again.
### response.writeContinue()
#### response.writeContinue()
<!-- YAML
added: v8.4.0
-->
@@ -2592,7 +2621,7 @@ Sends a status `100 Continue` to the client, indicating that the request body
should be sent. See the [`'checkContinue'`][] event on `Http2Server` and
`Http2SecureServer`.
### response.writeHead(statusCode[, statusMessage][, headers])
#### response.writeHead(statusCode[, statusMessage][, headers])
<!-- YAML
added: v8.4.0
-->
@@ -2648,7 +2677,7 @@ const server = http2.createServer((req, res) => {
Attempting to set a header field name or value that contains invalid characters
will result in a [`TypeError`][] being thrown.
### response.createPushResponse(headers, callback)
#### response.createPushResponse(headers, callback)
<!-- YAML
added: v8.4.0
-->
View
@@ -206,6 +206,9 @@ E('ERR_HTTP2_INVALID_SETTING_VALUE',
E('ERR_HTTP2_INVALID_STREAM', 'The stream has been destroyed');
E('ERR_HTTP2_MAX_PENDING_SETTINGS_ACK',
(max) => `Maximum number of pending settings acknowledgements (${max})`);
E('ERR_HTTP2_NO_SOCKET_MANIPULATION',
'HTTP/2 sockets should not be directly read from, written to, ' +
'paused and/or resumed.');
E('ERR_HTTP2_OUT_OF_STREAMS',
'No stream ID is available because maximum stream ID has been reached');
E('ERR_HTTP2_PAYLOAD_FORBIDDEN',
Oops, something went wrong.

0 comments on commit 2da7d9b

Please sign in to comment.