http2: cleanup Http2Stream/Http2Session destroy
PR-URL: https://github.com/nodejs/node/pull/17406 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Anatoli Papirovski <apapirovski@mac.com> This is a significant cleanup and refactoring of the cleanup/close/destroy logic for Http2Stream and Http2Session. There are significant changes here in the timing and ordering of cleanup logic, JS apis. and various related necessary edits.
This commit is contained in:
parent
18ca0b6442
commit
0babd181a0
@ -810,6 +810,11 @@ Status code was outside the regular status code range (100-999).
|
|||||||
The `Trailer` header was set even though the transfer encoding does not support
|
The `Trailer` header was set even though the transfer encoding does not support
|
||||||
that.
|
that.
|
||||||
|
|
||||||
|
<a id="ERR_HTTP2_ALREADY_SHUTDOWN"></a>
|
||||||
|
### ERR_HTTP2_ALREADY_SHUTDOWN
|
||||||
|
|
||||||
|
Occurs with multiple attempts to shutdown an HTTP/2 session.
|
||||||
|
|
||||||
<a id="ERR_HTTP2_CONNECT_AUTHORITY"></a>
|
<a id="ERR_HTTP2_CONNECT_AUTHORITY"></a>
|
||||||
### ERR_HTTP2_CONNECT_AUTHORITY
|
### ERR_HTTP2_CONNECT_AUTHORITY
|
||||||
|
|
||||||
@ -833,6 +838,12 @@ forbidden.
|
|||||||
|
|
||||||
A failure occurred sending an individual frame on the HTTP/2 session.
|
A failure occurred sending an individual frame on the HTTP/2 session.
|
||||||
|
|
||||||
|
<a id="ERR_HTTP2_GOAWAY_SESSION"></a>
|
||||||
|
### ERR_HTTP2_GOAWAY_SESSION
|
||||||
|
|
||||||
|
New HTTP/2 Streams may not be opened after the `Http2Session` has received a
|
||||||
|
`GOAWAY` frame from the connected peer.
|
||||||
|
|
||||||
<a id="ERR_HTTP2_HEADER_REQUIRED"></a>
|
<a id="ERR_HTTP2_HEADER_REQUIRED"></a>
|
||||||
### ERR_HTTP2_HEADER_REQUIRED
|
### ERR_HTTP2_HEADER_REQUIRED
|
||||||
|
|
||||||
@ -972,6 +983,11 @@ client.
|
|||||||
An attempt was made to use the `Http2Stream.prototype.responseWithFile()` API to
|
An attempt was made to use the `Http2Stream.prototype.responseWithFile()` API to
|
||||||
send something other than a regular file.
|
send something other than a regular file.
|
||||||
|
|
||||||
|
<a id="ERR_HTTP2_SESSION_ERROR"></a>
|
||||||
|
### ERR_HTTP2_SESSION_ERROR
|
||||||
|
|
||||||
|
The `Http2Session` closed with a non-zero error code.
|
||||||
|
|
||||||
<a id="ERR_HTTP2_SOCKET_BOUND"></a>
|
<a id="ERR_HTTP2_SOCKET_BOUND"></a>
|
||||||
### ERR_HTTP2_SOCKET_BOUND
|
### ERR_HTTP2_SOCKET_BOUND
|
||||||
|
|
||||||
@ -989,10 +1005,11 @@ Use of the `101` Informational status code is forbidden in HTTP/2.
|
|||||||
An invalid HTTP status code has been specified. Status codes must be an integer
|
An invalid HTTP status code has been specified. Status codes must be an integer
|
||||||
between `100` and `599` (inclusive).
|
between `100` and `599` (inclusive).
|
||||||
|
|
||||||
<a id="ERR_HTTP2_STREAM_CLOSED"></a>
|
<a id="ERR_HTTP2_STREAM_CANCEL"></a>
|
||||||
### ERR_HTTP2_STREAM_CLOSED
|
### ERR_HTTP2_STREAM_CANCEL
|
||||||
|
|
||||||
An action was performed on an HTTP/2 Stream that had already been closed.
|
An `Http2Stream` was destroyed before any data was transmitted to the connected
|
||||||
|
peer.
|
||||||
|
|
||||||
<a id="ERR_HTTP2_STREAM_ERROR"></a>
|
<a id="ERR_HTTP2_STREAM_ERROR"></a>
|
||||||
### ERR_HTTP2_STREAM_ERROR
|
### ERR_HTTP2_STREAM_ERROR
|
||||||
|
310
doc/api/http2.md
310
doc/api/http2.md
@ -67,8 +67,8 @@ const fs = require('fs');
|
|||||||
const client = http2.connect('https://localhost:8443', {
|
const client = http2.connect('https://localhost:8443', {
|
||||||
ca: fs.readFileSync('localhost-cert.pem')
|
ca: fs.readFileSync('localhost-cert.pem')
|
||||||
});
|
});
|
||||||
client.on('socketError', (err) => console.error(err));
|
|
||||||
client.on('error', (err) => console.error(err));
|
client.on('error', (err) => console.error(err));
|
||||||
|
client.on('socketError', (err) => console.error(err));
|
||||||
|
|
||||||
const req = client.request({ ':path': '/' });
|
const req = client.request({ ':path': '/' });
|
||||||
|
|
||||||
@ -83,7 +83,7 @@ let data = '';
|
|||||||
req.on('data', (chunk) => { data += chunk; });
|
req.on('data', (chunk) => { data += chunk; });
|
||||||
req.on('end', () => {
|
req.on('end', () => {
|
||||||
console.log(`\n${data}`);
|
console.log(`\n${data}`);
|
||||||
client.destroy();
|
client.close();
|
||||||
});
|
});
|
||||||
req.end();
|
req.end();
|
||||||
```
|
```
|
||||||
@ -127,7 +127,7 @@ solely on the API of the `Http2Session`.
|
|||||||
added: v8.4.0
|
added: v8.4.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The `'close'` event is emitted once the `Http2Session` has been terminated.
|
The `'close'` event is emitted once the `Http2Session` has been destroyed.
|
||||||
|
|
||||||
#### Event: 'connect'
|
#### Event: 'connect'
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
@ -234,19 +234,13 @@ of the stream.
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
const http2 = require('http2');
|
const http2 = require('http2');
|
||||||
const {
|
|
||||||
HTTP2_HEADER_METHOD,
|
|
||||||
HTTP2_HEADER_PATH,
|
|
||||||
HTTP2_HEADER_STATUS,
|
|
||||||
HTTP2_HEADER_CONTENT_TYPE
|
|
||||||
} = http2.constants;
|
|
||||||
session.on('stream', (stream, headers, flags) => {
|
session.on('stream', (stream, headers, flags) => {
|
||||||
const method = headers[HTTP2_HEADER_METHOD];
|
const method = headers[':method'];
|
||||||
const path = headers[HTTP2_HEADER_PATH];
|
const path = headers[':path'];
|
||||||
// ...
|
// ...
|
||||||
stream.respond({
|
stream.respond({
|
||||||
[HTTP2_HEADER_STATUS]: 200,
|
':status': 200,
|
||||||
[HTTP2_HEADER_CONTENT_TYPE]: 'text/plain'
|
'content-type': 'text/plain'
|
||||||
});
|
});
|
||||||
stream.write('hello ');
|
stream.write('hello ');
|
||||||
stream.end('world');
|
stream.end('world');
|
||||||
@ -275,19 +269,6 @@ server.on('stream', (stream, headers) => {
|
|||||||
server.listen(80);
|
server.listen(80);
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Event: 'socketError'
|
|
||||||
<!-- YAML
|
|
||||||
added: v8.4.0
|
|
||||||
-->
|
|
||||||
|
|
||||||
The `'socketError'` event is emitted when an `'error'` is emitted on the
|
|
||||||
`Socket` instance bound to the `Http2Session`. If this event is not handled,
|
|
||||||
the `'error'` event will be re-emitted on the `Socket`.
|
|
||||||
|
|
||||||
For `ServerHttp2Session` instances, a `'socketError'` event listener is always
|
|
||||||
registered that will, by default, forward the event on to the owning
|
|
||||||
`Http2Server` instance if no additional handlers are registered.
|
|
||||||
|
|
||||||
#### Event: 'timeout'
|
#### Event: 'timeout'
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v8.4.0
|
added: v8.4.0
|
||||||
@ -302,16 +283,53 @@ session.setTimeout(2000);
|
|||||||
session.on('timeout', () => { /** .. **/ });
|
session.on('timeout', () => { /** .. **/ });
|
||||||
```
|
```
|
||||||
|
|
||||||
#### http2session.destroy()
|
#### http2session.close([callback])
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
* `callback` {Function}
|
||||||
|
|
||||||
|
Gracefully closes the `Http2Session`, allowing any existing streams to
|
||||||
|
complete on their own and preventing new `Http2Stream` instances from being
|
||||||
|
created. Once closed, `http2session.destroy()` *might* be called if there
|
||||||
|
are no open `Http2Stream` instances.
|
||||||
|
|
||||||
|
If specified, the `callback` function is registered as a handler for the
|
||||||
|
`'close'` event.
|
||||||
|
|
||||||
|
#### http2session.closed
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
* Value: {boolean}
|
||||||
|
|
||||||
|
Will be `true` if this `Http2Session` instance has been closed, otherwise
|
||||||
|
`false`.
|
||||||
|
|
||||||
|
#### http2session.destroy([error,][code])
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v8.4.0
|
added: v8.4.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
* `error` {Error} An `Error` object if the `Http2Session` is being destroyed
|
||||||
|
due to an error.
|
||||||
|
* `code` {number} The HTTP/2 error code to send in the final `GOAWAY` frame.
|
||||||
|
If unspecified, and `error` is not undefined, the default is `INTERNAL_ERROR`,
|
||||||
|
otherwise defaults to `NO_ERROR`.
|
||||||
* Returns: {undefined}
|
* Returns: {undefined}
|
||||||
|
|
||||||
Immediately terminates the `Http2Session` and the associated `net.Socket` or
|
Immediately terminates the `Http2Session` and the associated `net.Socket` or
|
||||||
`tls.TLSSocket`.
|
`tls.TLSSocket`.
|
||||||
|
|
||||||
|
Once destroyed, the `Http2Session` will emit the `'close'` event. If `error`
|
||||||
|
is not undefined, an `'error'` event will be emitted immediately after the
|
||||||
|
`'close'` event.
|
||||||
|
|
||||||
|
If there are any remaining open `Http2Streams` associatd with the
|
||||||
|
`Http2Session`, those will also be destroyed.
|
||||||
|
|
||||||
#### http2session.destroyed
|
#### http2session.destroyed
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v8.4.0
|
added: v8.4.0
|
||||||
@ -322,6 +340,19 @@ added: v8.4.0
|
|||||||
Will be `true` if this `Http2Session` instance has been destroyed and must no
|
Will be `true` if this `Http2Session` instance has been destroyed and must no
|
||||||
longer be used, otherwise `false`.
|
longer be used, otherwise `false`.
|
||||||
|
|
||||||
|
#### http2session.goaway([code, [lastStreamID, [opaqueData]]])
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
* `code` {number} An HTTP/2 error code
|
||||||
|
* `lastStreamID` {number} The numeric ID of the last processed `Http2Stream`
|
||||||
|
* `opaqueData` {Buffer|TypedArray|DataView} A `TypedArray` or `DataView`
|
||||||
|
instance containing additional data to be carried within the GOAWAY frame.
|
||||||
|
|
||||||
|
Transmits a `GOAWAY` frame to the connected peer *without* shutting down the
|
||||||
|
`Http2Session`.
|
||||||
|
|
||||||
#### http2session.localSettings
|
#### http2session.localSettings
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v8.4.0
|
added: v8.4.0
|
||||||
@ -449,6 +480,12 @@ the trailing header fields to send to the peer.
|
|||||||
will be emitted if the `getTrailers` callback attempts to set such header
|
will be emitted if the `getTrailers` callback attempts to set such header
|
||||||
fields.
|
fields.
|
||||||
|
|
||||||
|
The the `:method` and `:path` pseudoheaders are not specified within `headers`,
|
||||||
|
they respectively default to:
|
||||||
|
|
||||||
|
* `:method` = `'GET'`
|
||||||
|
* `:path` = `/`
|
||||||
|
|
||||||
#### http2session.setTimeout(msecs, callback)
|
#### http2session.setTimeout(msecs, callback)
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v8.4.0
|
added: v8.4.0
|
||||||
@ -462,19 +499,18 @@ Used to set a callback function that is called when there is no activity on
|
|||||||
the `Http2Session` after `msecs` milliseconds. The given `callback` is
|
the `Http2Session` after `msecs` milliseconds. The given `callback` is
|
||||||
registered as a listener on the `'timeout'` event.
|
registered as a listener on the `'timeout'` event.
|
||||||
|
|
||||||
#### http2session.shutdown(options[, callback])
|
#### http2session.close(options[, callback])
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v8.4.0
|
added: v8.4.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
* `options` {Object}
|
* `options` {Object}
|
||||||
* `graceful` {boolean} `true` to attempt a polite shutdown of the
|
|
||||||
`Http2Session`.
|
|
||||||
* `errorCode` {number} The HTTP/2 [error code][] to return. Note that this is
|
* `errorCode` {number} The HTTP/2 [error code][] to return. Note that this is
|
||||||
*not* the same thing as an HTTP Response Status Code. **Default:** `0x00`
|
*not* the same thing as an HTTP Response Status Code. **Default:** `0x00`
|
||||||
(No Error).
|
(No Error).
|
||||||
* `lastStreamID` {number} The Stream ID of the last successfully processed
|
* `lastStreamID` {number} The Stream ID of the last successfully processed
|
||||||
`Http2Stream` on this `Http2Session`.
|
`Http2Stream` on this `Http2Session`. If unspecified, will default to the
|
||||||
|
ID of the most recently received stream.
|
||||||
* `opaqueData` {Buffer|Uint8Array} A `Buffer` or `Uint8Array` instance
|
* `opaqueData` {Buffer|Uint8Array} A `Buffer` or `Uint8Array` instance
|
||||||
containing arbitrary additional data to send to the peer upon disconnection.
|
containing arbitrary additional data to send to the peer upon disconnection.
|
||||||
This is used, typically, to provide additional data for debugging failures,
|
This is used, typically, to provide additional data for debugging failures,
|
||||||
@ -487,19 +523,16 @@ Attempts to shutdown this `Http2Session` using HTTP/2 defined procedures.
|
|||||||
If specified, the given `callback` function will be invoked once the shutdown
|
If specified, the given `callback` function will be invoked once the shutdown
|
||||||
process has completed.
|
process has completed.
|
||||||
|
|
||||||
Note that calling `http2session.shutdown()` does *not* destroy the session or
|
If the `Http2Session` instance is a server-side session and the `errorCode`
|
||||||
tear down the `Socket` connection. It merely prompts both sessions to begin
|
option is `0x00` (No Error), a "graceful" shutdown will be initiated. During a
|
||||||
preparing to cease activity.
|
"graceful" shutdown, the session will first send a `GOAWAY` frame to
|
||||||
|
the connected peer identifying the last processed stream as 2<sup>31</sup>-1.
|
||||||
During a "graceful" shutdown, the session will first send a `GOAWAY` frame to
|
|
||||||
the connected peer identifying the last processed stream as 2<sup>32</sup>-1.
|
|
||||||
Then, on the next tick of the event loop, a second `GOAWAY` frame identifying
|
Then, on the next tick of the event loop, a second `GOAWAY` frame identifying
|
||||||
the most recently processed stream identifier is sent. This process allows the
|
the most recently processed stream identifier is sent. This process allows the
|
||||||
remote peer to begin preparing for the connection to be terminated.
|
remote peer to begin preparing for the connection to be terminated.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
session.shutdown({
|
session.close({
|
||||||
graceful: true,
|
|
||||||
opaqueData: Buffer.from('add some debugging data here')
|
opaqueData: Buffer.from('add some debugging data here')
|
||||||
}, () => session.destroy());
|
}, () => session.destroy());
|
||||||
```
|
```
|
||||||
@ -627,7 +660,7 @@ is not yet ready for use.
|
|||||||
All [`Http2Stream`][] instances are destroyed either when:
|
All [`Http2Stream`][] instances are destroyed either when:
|
||||||
|
|
||||||
* An `RST_STREAM` frame for the stream is received by the connected peer.
|
* An `RST_STREAM` frame for the stream is received by the connected peer.
|
||||||
* The `http2stream.rstStream()` methods is called.
|
* The `http2stream.close()` method is called.
|
||||||
* The `http2stream.destroy()` or `http2session.destroy()` methods are called.
|
* The `http2stream.destroy()` or `http2session.destroy()` methods are called.
|
||||||
|
|
||||||
When an `Http2Stream` instance is destroyed, an attempt will be made to send an
|
When an `Http2Stream` instance is destroyed, an attempt will be made to send an
|
||||||
@ -720,6 +753,29 @@ added: v8.4.0
|
|||||||
Set to `true` if the `Http2Stream` instance was aborted abnormally. When set,
|
Set to `true` if the `Http2Stream` instance was aborted abnormally. When set,
|
||||||
the `'aborted'` event will have been emitted.
|
the `'aborted'` event will have been emitted.
|
||||||
|
|
||||||
|
#### http2stream.close(code[, callback])
|
||||||
|
<!-- YAML
|
||||||
|
added: v8.4.0
|
||||||
|
-->
|
||||||
|
|
||||||
|
* code {number} Unsigned 32-bit integer identifying the error code. **Default:**
|
||||||
|
`http2.constant.NGHTTP2_NO_ERROR` (`0x00`)
|
||||||
|
* `callback` {Function} An optional function registered to listen for the
|
||||||
|
`'close'` event.
|
||||||
|
* Returns: {undefined}
|
||||||
|
|
||||||
|
Closes the `Http2Stream` instance by sending an `RST_STREAM` frame to the
|
||||||
|
connected HTTP/2 peer.
|
||||||
|
|
||||||
|
#### http2stream.closed
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
* Value: {boolean}
|
||||||
|
|
||||||
|
Set to `true` if the `Http2Stream` instance has been closed.
|
||||||
|
|
||||||
#### http2stream.destroyed
|
#### http2stream.destroyed
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v8.4.0
|
added: v8.4.0
|
||||||
@ -730,6 +786,16 @@ added: v8.4.0
|
|||||||
Set to `true` if the `Http2Stream` instance has been destroyed and is no longer
|
Set to `true` if the `Http2Stream` instance has been destroyed and is no longer
|
||||||
usable.
|
usable.
|
||||||
|
|
||||||
|
#### http2stream.pending
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
* Value: {boolean}
|
||||||
|
|
||||||
|
Set to `true` if the `Http2Stream` instance has not yet been assigned a
|
||||||
|
numeric stream identifier.
|
||||||
|
|
||||||
#### http2stream.priority(options)
|
#### http2stream.priority(options)
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v8.4.0
|
added: v8.4.0
|
||||||
@ -760,66 +826,9 @@ added: v8.4.0
|
|||||||
|
|
||||||
Set to the `RST_STREAM` [error code][] reported when the `Http2Stream` is
|
Set to the `RST_STREAM` [error code][] reported when the `Http2Stream` is
|
||||||
destroyed after either receiving an `RST_STREAM` frame from the connected peer,
|
destroyed after either receiving an `RST_STREAM` frame from the connected peer,
|
||||||
calling `http2stream.rstStream()`, or `http2stream.destroy()`. Will be
|
calling `http2stream.close()`, or `http2stream.destroy()`. Will be
|
||||||
`undefined` if the `Http2Stream` has not been closed.
|
`undefined` if the `Http2Stream` has not been closed.
|
||||||
|
|
||||||
#### http2stream.rstStream(code)
|
|
||||||
<!-- YAML
|
|
||||||
added: v8.4.0
|
|
||||||
-->
|
|
||||||
|
|
||||||
* code {number} Unsigned 32-bit integer identifying the error code. **Default:**
|
|
||||||
`http2.constant.NGHTTP2_NO_ERROR` (`0x00`)
|
|
||||||
* Returns: {undefined}
|
|
||||||
|
|
||||||
Sends an `RST_STREAM` frame to the connected HTTP/2 peer, causing this
|
|
||||||
`Http2Stream` to be closed on both sides using [error code][] `code`.
|
|
||||||
|
|
||||||
#### http2stream.rstWithNoError()
|
|
||||||
<!-- YAML
|
|
||||||
added: v8.4.0
|
|
||||||
-->
|
|
||||||
|
|
||||||
* Returns: {undefined}
|
|
||||||
|
|
||||||
Shortcut for `http2stream.rstStream()` using error code `0x00` (No Error).
|
|
||||||
|
|
||||||
#### http2stream.rstWithProtocolError()
|
|
||||||
<!-- YAML
|
|
||||||
added: v8.4.0
|
|
||||||
-->
|
|
||||||
|
|
||||||
* Returns: {undefined}
|
|
||||||
|
|
||||||
Shortcut for `http2stream.rstStream()` using error code `0x01` (Protocol Error).
|
|
||||||
|
|
||||||
#### http2stream.rstWithCancel()
|
|
||||||
<!-- YAML
|
|
||||||
added: v8.4.0
|
|
||||||
-->
|
|
||||||
|
|
||||||
* Returns: {undefined}
|
|
||||||
|
|
||||||
Shortcut for `http2stream.rstStream()` using error code `0x08` (Cancel).
|
|
||||||
|
|
||||||
#### http2stream.rstWithRefuse()
|
|
||||||
<!-- YAML
|
|
||||||
added: v8.4.0
|
|
||||||
-->
|
|
||||||
|
|
||||||
* Returns: {undefined}
|
|
||||||
|
|
||||||
Shortcut for `http2stream.rstStream()` using error code `0x07` (Refused Stream).
|
|
||||||
|
|
||||||
#### http2stream.rstWithInternalError()
|
|
||||||
<!-- YAML
|
|
||||||
added: v8.4.0
|
|
||||||
-->
|
|
||||||
|
|
||||||
* Returns: {undefined}
|
|
||||||
|
|
||||||
Shortcut for `http2stream.rstStream()` using error code `0x02` (Internal Error).
|
|
||||||
|
|
||||||
#### http2stream.session
|
#### http2stream.session
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v8.4.0
|
added: v8.4.0
|
||||||
@ -842,11 +851,11 @@ added: v8.4.0
|
|||||||
```js
|
```js
|
||||||
const http2 = require('http2');
|
const http2 = require('http2');
|
||||||
const client = http2.connect('http://example.org:8000');
|
const client = http2.connect('http://example.org:8000');
|
||||||
|
const { NGHTTP2_CANCEL } = http2.constants;
|
||||||
const req = client.request({ ':path': '/' });
|
const req = client.request({ ':path': '/' });
|
||||||
|
|
||||||
// Cancel the stream if there's no activity after 5 seconds
|
// Cancel the stream if there's no activity after 5 seconds
|
||||||
req.setTimeout(5000, () => req.rstWithCancel());
|
req.setTimeout(5000, () => req.rstStream(NGHTTP2_CANCEL));
|
||||||
```
|
```
|
||||||
|
|
||||||
#### http2stream.state
|
#### http2stream.state
|
||||||
@ -1264,16 +1273,49 @@ added: v8.4.0
|
|||||||
|
|
||||||
In `Http2Server`, there is no `'clientError'` event as there is in
|
In `Http2Server`, there is no `'clientError'` event as there is in
|
||||||
HTTP1. However, there are `'socketError'`, `'sessionError'`, and
|
HTTP1. However, there are `'socketError'`, `'sessionError'`, and
|
||||||
`'streamError'`, for error happened on the socket, session, or stream
|
`'streamError'`, for errors emitted on the socket, `Http2Session`, or
|
||||||
respectively.
|
`Http2Stream`.
|
||||||
|
|
||||||
#### Event: 'socketError'
|
#### Event: 'checkContinue'
|
||||||
|
<!-- YAML
|
||||||
|
added: v8.5.0
|
||||||
|
-->
|
||||||
|
|
||||||
|
* `request` {http2.Http2ServerRequest}
|
||||||
|
* `response` {http2.Http2ServerResponse}
|
||||||
|
|
||||||
|
If a [`'request'`][] listener is registered or [`http2.createServer()`][] is
|
||||||
|
supplied a callback function, the `'checkContinue'` event is emitted each time
|
||||||
|
a request with an HTTP `Expect: 100-continue` is received. If this event is
|
||||||
|
not listened for, the server will automatically respond with a status
|
||||||
|
`100 Continue` as appropriate.
|
||||||
|
|
||||||
|
Handling this event involves calling [`response.writeContinue()`][] if the client
|
||||||
|
should continue to send the request body, or generating an appropriate HTTP
|
||||||
|
response (e.g. 400 Bad Request) if the client should not continue to send the
|
||||||
|
request body.
|
||||||
|
|
||||||
|
Note that when this event is emitted and handled, the [`'request'`][] event will
|
||||||
|
not be emitted.
|
||||||
|
|
||||||
|
#### Event: 'request'
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v8.4.0
|
added: v8.4.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The `'socketError'` event is emitted when a `'socketError'` event is emitted by
|
* `request` {http2.Http2ServerRequest}
|
||||||
an `Http2Session` associated with the server.
|
* `response` {http2.Http2ServerResponse}
|
||||||
|
|
||||||
|
Emitted each time there is a request. Note that there may be multiple requests
|
||||||
|
per session. See the [Compatibility API][].
|
||||||
|
|
||||||
|
#### Event: 'session'
|
||||||
|
<!-- YAML
|
||||||
|
added: v8.4.0
|
||||||
|
-->
|
||||||
|
|
||||||
|
The `'session'` event is emitted when a new `Http2Session` is created by the
|
||||||
|
`Http2Server`.
|
||||||
|
|
||||||
#### Event: 'sessionError'
|
#### Event: 'sessionError'
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
@ -1281,16 +1323,13 @@ added: v8.4.0
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
The `'sessionError'` event is emitted when an `'error'` event is emitted by
|
The `'sessionError'` event is emitted when an `'error'` event is emitted by
|
||||||
an `Http2Session` object. If no listener is registered for this event, an
|
an `Http2Session` object associated with the `Http2Server`.
|
||||||
`'error'` event is emitted.
|
|
||||||
|
|
||||||
#### Event: 'streamError'
|
#### Event: 'streamError'
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v8.5.0
|
added: v8.5.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
* `socket` {http2.ServerHttp2Stream}
|
|
||||||
|
|
||||||
If an `ServerHttp2Stream` emits an `'error'` event, it will be forwarded here.
|
If an `ServerHttp2Stream` emits an `'error'` event, it will be forwarded here.
|
||||||
The stream will already be destroyed when this event is triggered.
|
The stream will already be destroyed when this event is triggered.
|
||||||
|
|
||||||
@ -1325,17 +1364,6 @@ server.on('stream', (stream, headers, flags) => {
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Event: 'request'
|
|
||||||
<!-- YAML
|
|
||||||
added: v8.4.0
|
|
||||||
-->
|
|
||||||
|
|
||||||
* `request` {http2.Http2ServerRequest}
|
|
||||||
* `response` {http2.Http2ServerResponse}
|
|
||||||
|
|
||||||
Emitted each time there is a request. Note that there may be multiple requests
|
|
||||||
per session. See the [Compatibility API][].
|
|
||||||
|
|
||||||
#### Event: 'timeout'
|
#### Event: 'timeout'
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v8.4.0
|
added: v8.4.0
|
||||||
@ -1344,28 +1372,6 @@ added: v8.4.0
|
|||||||
The `'timeout'` event is emitted when there is no activity on the Server for
|
The `'timeout'` event is emitted when there is no activity on the Server for
|
||||||
a given number of milliseconds set using `http2server.setTimeout()`.
|
a given number of milliseconds set using `http2server.setTimeout()`.
|
||||||
|
|
||||||
#### Event: 'checkContinue'
|
|
||||||
<!-- YAML
|
|
||||||
added: v8.5.0
|
|
||||||
-->
|
|
||||||
|
|
||||||
* `request` {http2.Http2ServerRequest}
|
|
||||||
* `response` {http2.Http2ServerResponse}
|
|
||||||
|
|
||||||
If a [`'request'`][] listener is registered or [`http2.createServer()`][] is
|
|
||||||
supplied a callback function, the `'checkContinue'` event is emitted each time
|
|
||||||
a request with an HTTP `Expect: 100-continue` is received. If this event is
|
|
||||||
not listened for, the server will automatically respond with a status
|
|
||||||
`100 Continue` as appropriate.
|
|
||||||
|
|
||||||
Handling this event involves calling [`response.writeContinue()`][] if the client
|
|
||||||
should continue to send the request body, or generating an appropriate HTTP
|
|
||||||
response (e.g. 400 Bad Request) if the client should not continue to send the
|
|
||||||
request body.
|
|
||||||
|
|
||||||
Note that when this event is emitted and handled, the [`'request'`][] event will
|
|
||||||
not be emitted.
|
|
||||||
|
|
||||||
### Class: Http2SecureServer
|
### Class: Http2SecureServer
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v8.4.0
|
added: v8.4.0
|
||||||
@ -1379,16 +1385,7 @@ added: v8.4.0
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
The `'sessionError'` event is emitted when an `'error'` event is emitted by
|
The `'sessionError'` event is emitted when an `'error'` event is emitted by
|
||||||
an `Http2Session` object. If no listener is registered for this event, an
|
an `Http2Session` object associated with the `Http2SecureServer`.
|
||||||
`'error'` event is emitted on the `Http2Session` instance instead.
|
|
||||||
|
|
||||||
#### Event: 'socketError'
|
|
||||||
<!-- YAML
|
|
||||||
added: v8.4.0
|
|
||||||
-->
|
|
||||||
|
|
||||||
The `'socketError'` event is emitted when a `'socketError'` event is emitted by
|
|
||||||
an `Http2Session` associated with the server.
|
|
||||||
|
|
||||||
#### Event: 'unknownProtocol'
|
#### Event: 'unknownProtocol'
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
@ -1682,7 +1679,7 @@ const client = http2.connect('https://localhost:1234');
|
|||||||
|
|
||||||
/** use the client **/
|
/** use the client **/
|
||||||
|
|
||||||
client.destroy();
|
client.close();
|
||||||
```
|
```
|
||||||
|
|
||||||
### http2.constants
|
### http2.constants
|
||||||
@ -1938,6 +1935,7 @@ An HTTP/2 CONNECT proxy:
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
const http2 = require('http2');
|
const http2 = require('http2');
|
||||||
|
const { NGHTTP2_REFUSED_STREAM } = http2.constants;
|
||||||
const net = require('net');
|
const net = require('net');
|
||||||
const { URL } = require('url');
|
const { URL } = require('url');
|
||||||
|
|
||||||
@ -1945,7 +1943,7 @@ const proxy = http2.createServer();
|
|||||||
proxy.on('stream', (stream, headers) => {
|
proxy.on('stream', (stream, headers) => {
|
||||||
if (headers[':method'] !== 'CONNECT') {
|
if (headers[':method'] !== 'CONNECT') {
|
||||||
// Only accept CONNECT requests
|
// Only accept CONNECT requests
|
||||||
stream.rstWithRefused();
|
stream.close(NGHTTP2_REFUSED_STREAM);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auth = new URL(`tcp://${headers[':authority']}`);
|
const auth = new URL(`tcp://${headers[':authority']}`);
|
||||||
@ -1957,7 +1955,7 @@ proxy.on('stream', (stream, headers) => {
|
|||||||
stream.pipe(socket);
|
stream.pipe(socket);
|
||||||
});
|
});
|
||||||
socket.on('error', (error) => {
|
socket.on('error', (error) => {
|
||||||
stream.rstStream(http2.constants.NGHTTP2_CONNECT_ERROR);
|
stream.close(http2.constants.NGHTTP2_CONNECT_ERROR);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1986,7 +1984,7 @@ req.setEncoding('utf8');
|
|||||||
req.on('data', (chunk) => data += chunk);
|
req.on('data', (chunk) => data += chunk);
|
||||||
req.on('end', () => {
|
req.on('end', () => {
|
||||||
console.log(`The server says: ${data}`);
|
console.log(`The server says: ${data}`);
|
||||||
client.destroy();
|
client.close();
|
||||||
});
|
});
|
||||||
req.end('Jane');
|
req.end('Jane');
|
||||||
```
|
```
|
||||||
|
@ -307,6 +307,8 @@ E('ERR_ENCODING_INVALID_ENCODED_DATA',
|
|||||||
'The encoded data was not valid for encoding %s');
|
'The encoded data was not valid for encoding %s');
|
||||||
E('ERR_ENCODING_NOT_SUPPORTED', 'The "%s" encoding is not supported');
|
E('ERR_ENCODING_NOT_SUPPORTED', 'The "%s" encoding is not supported');
|
||||||
E('ERR_FALSY_VALUE_REJECTION', 'Promise was rejected with falsy value');
|
E('ERR_FALSY_VALUE_REJECTION', 'Promise was rejected with falsy value');
|
||||||
|
E('ERR_HTTP2_ALREADY_SHUTDOWN',
|
||||||
|
'Http2Session is already shutdown or destroyed');
|
||||||
E('ERR_HTTP2_CONNECT_AUTHORITY',
|
E('ERR_HTTP2_CONNECT_AUTHORITY',
|
||||||
':authority header is required for CONNECT requests');
|
':authority header is required for CONNECT requests');
|
||||||
E('ERR_HTTP2_CONNECT_PATH',
|
E('ERR_HTTP2_CONNECT_PATH',
|
||||||
@ -321,6 +323,8 @@ E('ERR_HTTP2_FRAME_ERROR',
|
|||||||
msg += ` with code ${code}`;
|
msg += ` with code ${code}`;
|
||||||
return msg;
|
return msg;
|
||||||
});
|
});
|
||||||
|
E('ERR_HTTP2_GOAWAY_SESSION',
|
||||||
|
'New streams cannot be created after receiving a GOAWAY');
|
||||||
E('ERR_HTTP2_HEADERS_AFTER_RESPOND',
|
E('ERR_HTTP2_HEADERS_AFTER_RESPOND',
|
||||||
'Cannot specify additional headers after response initiated');
|
'Cannot specify additional headers after response initiated');
|
||||||
E('ERR_HTTP2_HEADERS_OBJECT', 'Headers must be an object');
|
E('ERR_HTTP2_HEADERS_OBJECT', 'Headers must be an object');
|
||||||
@ -358,12 +362,13 @@ E('ERR_HTTP2_PING_LENGTH', 'HTTP2 ping payload must be 8 bytes');
|
|||||||
E('ERR_HTTP2_PSEUDOHEADER_NOT_ALLOWED', 'Cannot set HTTP/2 pseudo-headers');
|
E('ERR_HTTP2_PSEUDOHEADER_NOT_ALLOWED', 'Cannot set HTTP/2 pseudo-headers');
|
||||||
E('ERR_HTTP2_PUSH_DISABLED', 'HTTP/2 client has disabled push streams');
|
E('ERR_HTTP2_PUSH_DISABLED', 'HTTP/2 client has disabled push streams');
|
||||||
E('ERR_HTTP2_SEND_FILE', 'Only regular files can be sent');
|
E('ERR_HTTP2_SEND_FILE', 'Only regular files can be sent');
|
||||||
|
E('ERR_HTTP2_SESSION_ERROR', 'Session closed with error code %s');
|
||||||
E('ERR_HTTP2_SOCKET_BOUND',
|
E('ERR_HTTP2_SOCKET_BOUND',
|
||||||
'The socket is already bound to an Http2Session');
|
'The socket is already bound to an Http2Session');
|
||||||
E('ERR_HTTP2_STATUS_101',
|
E('ERR_HTTP2_STATUS_101',
|
||||||
'HTTP status code 101 (Switching Protocols) is forbidden in HTTP/2');
|
'HTTP status code 101 (Switching Protocols) is forbidden in HTTP/2');
|
||||||
E('ERR_HTTP2_STATUS_INVALID', 'Invalid status code: %s');
|
E('ERR_HTTP2_STATUS_INVALID', 'Invalid status code: %s');
|
||||||
E('ERR_HTTP2_STREAM_CLOSED', 'The stream is already closed');
|
E('ERR_HTTP2_STREAM_CANCEL', 'The pending stream has been canceled');
|
||||||
E('ERR_HTTP2_STREAM_ERROR', 'Stream closed with error code %s');
|
E('ERR_HTTP2_STREAM_ERROR', 'Stream closed with error code %s');
|
||||||
E('ERR_HTTP2_STREAM_SELF_DEPENDENCY', 'A stream cannot depend on itself');
|
E('ERR_HTTP2_STREAM_SELF_DEPENDENCY', 'A stream cannot depend on itself');
|
||||||
E('ERR_HTTP2_UNSUPPORTED_PROTOCOL', 'protocol "%s" is unsupported.');
|
E('ERR_HTTP2_UNSUPPORTED_PROTOCOL', 'protocol "%s" is unsupported.');
|
||||||
|
@ -126,14 +126,11 @@ function onStreamAbortedRequest() {
|
|||||||
const request = this[kRequest];
|
const request = this[kRequest];
|
||||||
if (request !== undefined && request[kState].closed === false) {
|
if (request !== undefined && request[kState].closed === false) {
|
||||||
request.emit('aborted');
|
request.emit('aborted');
|
||||||
request.emit('close');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onStreamAbortedResponse() {
|
function onStreamAbortedResponse() {
|
||||||
const response = this[kResponse];
|
// non-op for now
|
||||||
if (response !== undefined && response[kState].closed === false)
|
|
||||||
response.emit('close');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function resumeStream(stream) {
|
function resumeStream(stream) {
|
||||||
@ -234,9 +231,7 @@ class Http2ServerRequest extends Readable {
|
|||||||
stream.on('end', onStreamEnd);
|
stream.on('end', onStreamEnd);
|
||||||
stream.on('error', onStreamError);
|
stream.on('error', onStreamError);
|
||||||
stream.on('aborted', onStreamAbortedRequest);
|
stream.on('aborted', onStreamAbortedRequest);
|
||||||
const onfinish = this[kFinish].bind(this);
|
stream.on('close', this[kFinish].bind(this));
|
||||||
stream.on('close', onfinish);
|
|
||||||
stream.on('finish', onfinish);
|
|
||||||
this.on('pause', onRequestPause);
|
this.on('pause', onRequestPause);
|
||||||
this.on('resume', onRequestResume);
|
this.on('resume', onRequestResume);
|
||||||
}
|
}
|
||||||
@ -297,7 +292,7 @@ class Http2ServerRequest extends Readable {
|
|||||||
state.didRead = true;
|
state.didRead = true;
|
||||||
process.nextTick(resumeStream, this[kStream]);
|
process.nextTick(resumeStream, this[kStream]);
|
||||||
} else {
|
} else {
|
||||||
this.emit('error', new errors.Error('ERR_HTTP2_STREAM_CLOSED'));
|
this.emit('error', new errors.Error('ERR_HTTP2_INVALID_STREAM'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -345,6 +340,7 @@ class Http2ServerRequest extends Readable {
|
|||||||
// dump it for compatibility with http1
|
// dump it for compatibility with http1
|
||||||
if (!state.didRead && !this._readableState.resumeScheduled)
|
if (!state.didRead && !this._readableState.resumeScheduled)
|
||||||
this.resume();
|
this.resume();
|
||||||
|
this.emit('close');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -366,9 +362,7 @@ class Http2ServerResponse extends Stream {
|
|||||||
this.writable = true;
|
this.writable = true;
|
||||||
stream.on('drain', onStreamDrain);
|
stream.on('drain', onStreamDrain);
|
||||||
stream.on('aborted', onStreamAbortedResponse);
|
stream.on('aborted', onStreamAbortedResponse);
|
||||||
const onfinish = this[kFinish].bind(this);
|
stream.on('close', this[kFinish].bind(this));
|
||||||
stream.on('close', onfinish);
|
|
||||||
stream.on('finish', onfinish);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// User land modules such as finalhandler just check truthiness of this
|
// User land modules such as finalhandler just check truthiness of this
|
||||||
@ -520,7 +514,7 @@ class Http2ServerResponse extends Stream {
|
|||||||
const state = this[kState];
|
const state = this[kState];
|
||||||
|
|
||||||
if (state.closed)
|
if (state.closed)
|
||||||
throw new errors.Error('ERR_HTTP2_STREAM_CLOSED');
|
throw new errors.Error('ERR_HTTP2_INVALID_STREAM');
|
||||||
if (this[kStream].headersSent)
|
if (this[kStream].headersSent)
|
||||||
throw new errors.Error('ERR_HTTP2_HEADERS_SENT');
|
throw new errors.Error('ERR_HTTP2_HEADERS_SENT');
|
||||||
|
|
||||||
@ -550,7 +544,7 @@ class Http2ServerResponse extends Stream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this[kState].closed) {
|
if (this[kState].closed) {
|
||||||
const err = new errors.Error('ERR_HTTP2_STREAM_CLOSED');
|
const err = new errors.Error('ERR_HTTP2_INVALID_STREAM');
|
||||||
if (typeof cb === 'function')
|
if (typeof cb === 'function')
|
||||||
process.nextTick(cb, err);
|
process.nextTick(cb, err);
|
||||||
else
|
else
|
||||||
@ -620,12 +614,15 @@ class Http2ServerResponse extends Stream {
|
|||||||
if (typeof callback !== 'function')
|
if (typeof callback !== 'function')
|
||||||
throw new errors.TypeError('ERR_INVALID_CALLBACK');
|
throw new errors.TypeError('ERR_INVALID_CALLBACK');
|
||||||
if (this[kState].closed) {
|
if (this[kState].closed) {
|
||||||
process.nextTick(callback, new errors.Error('ERR_HTTP2_STREAM_CLOSED'));
|
process.nextTick(callback, new errors.Error('ERR_HTTP2_INVALID_STREAM'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this[kStream].pushStream(headers, {}, function(stream, headers, options) {
|
this[kStream].pushStream(headers, {}, (err, stream, headers, options) => {
|
||||||
const response = new Http2ServerResponse(stream);
|
if (err) {
|
||||||
callback(null, response);
|
callback(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
callback(null, new Http2ServerResponse(stream));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -649,6 +646,7 @@ class Http2ServerResponse extends Stream {
|
|||||||
this[kProxySocket] = null;
|
this[kProxySocket] = null;
|
||||||
stream[kResponse] = undefined;
|
stream[kResponse] = undefined;
|
||||||
this.emit('finish');
|
this.emit('finish');
|
||||||
|
this.emit('close');
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO doesn't support callbacks
|
// TODO doesn't support callbacks
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -70,7 +70,13 @@ void inline debug_vfprintf(const char* format, ...) {
|
|||||||
#define DEBUG_HTTP2STREAM2(...) do {} while (0)
|
#define DEBUG_HTTP2STREAM2(...) do {} while (0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// We strictly limit the number of outstanding unacknowledged PINGS a user
|
||||||
|
// may send in order to prevent abuse. The current default cap is 10. The
|
||||||
|
// user may set a different limit using a per Http2Session configuration
|
||||||
|
// option.
|
||||||
#define DEFAULT_MAX_PINGS 10
|
#define DEFAULT_MAX_PINGS 10
|
||||||
|
|
||||||
|
// These are the standard HTTP/2 defaults as specified by the RFC
|
||||||
#define DEFAULT_SETTINGS_HEADER_TABLE_SIZE 4096
|
#define DEFAULT_SETTINGS_HEADER_TABLE_SIZE 4096
|
||||||
#define DEFAULT_SETTINGS_ENABLE_PUSH 1
|
#define DEFAULT_SETTINGS_ENABLE_PUSH 1
|
||||||
#define DEFAULT_SETTINGS_INITIAL_WINDOW_SIZE 65535
|
#define DEFAULT_SETTINGS_INITIAL_WINDOW_SIZE 65535
|
||||||
@ -83,10 +89,10 @@ void inline debug_vfprintf(const char* format, ...) {
|
|||||||
#define MAX_MAX_HEADER_LIST_SIZE 16777215u
|
#define MAX_MAX_HEADER_LIST_SIZE 16777215u
|
||||||
#define DEFAULT_MAX_HEADER_LIST_PAIRS 128u
|
#define DEFAULT_MAX_HEADER_LIST_PAIRS 128u
|
||||||
|
|
||||||
struct nghttp2_stream_write_t;
|
|
||||||
|
|
||||||
#define MAX_BUFFER_COUNT 16
|
#define MAX_BUFFER_COUNT 16
|
||||||
|
|
||||||
|
struct nghttp2_stream_write_t;
|
||||||
|
|
||||||
enum nghttp2_session_type {
|
enum nghttp2_session_type {
|
||||||
NGHTTP2_SESSION_SERVER,
|
NGHTTP2_SESSION_SERVER,
|
||||||
NGHTTP2_SESSION_CLIENT
|
NGHTTP2_SESSION_CLIENT
|
||||||
@ -109,11 +115,15 @@ enum nghttp2_stream_flags {
|
|||||||
// Stream is destroyed
|
// Stream is destroyed
|
||||||
NGHTTP2_STREAM_FLAG_DESTROYED = 0x10,
|
NGHTTP2_STREAM_FLAG_DESTROYED = 0x10,
|
||||||
// Stream has trailers
|
// Stream has trailers
|
||||||
NGHTTP2_STREAM_FLAG_TRAILERS = 0x20
|
NGHTTP2_STREAM_FLAG_TRAILERS = 0x20,
|
||||||
|
// Stream has received all the data it can
|
||||||
|
NGHTTP2_STREAM_FLAG_EOS = 0x40
|
||||||
};
|
};
|
||||||
|
|
||||||
enum nghttp2_stream_options {
|
enum nghttp2_stream_options {
|
||||||
|
// Stream is not going to have any DATA frames
|
||||||
STREAM_OPTION_EMPTY_PAYLOAD = 0x1,
|
STREAM_OPTION_EMPTY_PAYLOAD = 0x1,
|
||||||
|
// Stream might have trailing headers
|
||||||
STREAM_OPTION_GET_TRAILERS = 0x2,
|
STREAM_OPTION_GET_TRAILERS = 0x2,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -136,7 +146,6 @@ struct nghttp2_header {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct nghttp2_stream_write_t {
|
struct nghttp2_stream_write_t {
|
||||||
void* data;
|
void* data;
|
||||||
int status;
|
int status;
|
||||||
@ -417,9 +426,10 @@ const char* nghttp2_errname(int rv) {
|
|||||||
|
|
||||||
enum session_state_flags {
|
enum session_state_flags {
|
||||||
SESSION_STATE_NONE = 0x0,
|
SESSION_STATE_NONE = 0x0,
|
||||||
SESSION_STATE_DESTROYING = 0x1,
|
SESSION_STATE_HAS_SCOPE = 0x1,
|
||||||
SESSION_STATE_HAS_SCOPE = 0x2,
|
SESSION_STATE_WRITE_SCHEDULED = 0x2,
|
||||||
SESSION_STATE_WRITE_SCHEDULED = 0x4
|
SESSION_STATE_CLOSED = 0x4,
|
||||||
|
SESSION_STATE_SENDING = 0x8,
|
||||||
};
|
};
|
||||||
|
|
||||||
// This allows for 4 default-sized frames with their frame headers
|
// This allows for 4 default-sized frames with their frame headers
|
||||||
@ -555,6 +565,8 @@ class Http2Stream : public AsyncWrap,
|
|||||||
unsigned int nbufs,
|
unsigned int nbufs,
|
||||||
nghttp2_stream_write_cb cb);
|
nghttp2_stream_write_cb cb);
|
||||||
|
|
||||||
|
inline bool HasDataChunks(bool ignore_eos = false);
|
||||||
|
|
||||||
inline void AddChunk(const uint8_t* data, size_t len);
|
inline void AddChunk(const uint8_t* data, size_t len);
|
||||||
|
|
||||||
inline void FlushDataChunks();
|
inline void FlushDataChunks();
|
||||||
@ -592,7 +604,7 @@ class Http2Stream : public AsyncWrap,
|
|||||||
bool silent = false);
|
bool silent = false);
|
||||||
|
|
||||||
// Submits an RST_STREAM frame using the given code
|
// Submits an RST_STREAM frame using the given code
|
||||||
inline int SubmitRstStream(const uint32_t code);
|
inline void SubmitRstStream(const uint32_t code);
|
||||||
|
|
||||||
// Submits a PUSH_PROMISE frame with this stream as the parent.
|
// Submits a PUSH_PROMISE frame with this stream as the parent.
|
||||||
inline Http2Stream* SubmitPushPromise(
|
inline Http2Stream* SubmitPushPromise(
|
||||||
@ -799,9 +811,11 @@ class Http2Session : public AsyncWrap {
|
|||||||
|
|
||||||
void Start();
|
void Start();
|
||||||
void Stop();
|
void Stop();
|
||||||
void Close();
|
void Close(uint32_t code = NGHTTP2_NO_ERROR,
|
||||||
|
bool socket_closed = false);
|
||||||
void Consume(Local<External> external);
|
void Consume(Local<External> external);
|
||||||
void Unconsume();
|
void Unconsume();
|
||||||
|
void Goaway(uint32_t code, int32_t lastStreamID, uint8_t* data, size_t len);
|
||||||
|
|
||||||
bool Ping(v8::Local<v8::Function> function);
|
bool Ping(v8::Local<v8::Function> function);
|
||||||
|
|
||||||
@ -827,8 +841,9 @@ class Http2Session : public AsyncWrap {
|
|||||||
|
|
||||||
inline const char* TypeName();
|
inline const char* TypeName();
|
||||||
|
|
||||||
inline void MarkDestroying() { flags_ |= SESSION_STATE_DESTROYING; }
|
inline bool IsDestroyed() {
|
||||||
inline bool IsDestroying() { return flags_ & SESSION_STATE_DESTROYING; }
|
return (flags_ & SESSION_STATE_CLOSED) || session_ == nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
// Schedule a write if nghttp2 indicates it wants to write to the socket.
|
// Schedule a write if nghttp2 indicates it wants to write to the socket.
|
||||||
void MaybeScheduleWrite();
|
void MaybeScheduleWrite();
|
||||||
@ -842,9 +857,6 @@ class Http2Session : public AsyncWrap {
|
|||||||
// Removes a stream instance from this session
|
// Removes a stream instance from this session
|
||||||
inline void RemoveStream(int32_t id);
|
inline void RemoveStream(int32_t id);
|
||||||
|
|
||||||
// Sends a notice to the connected peer that the session is shutting down.
|
|
||||||
inline void SubmitShutdownNotice();
|
|
||||||
|
|
||||||
// Submits a SETTINGS frame to the connected peer.
|
// Submits a SETTINGS frame to the connected peer.
|
||||||
inline void Settings(const nghttp2_settings_entry iv[], size_t niv);
|
inline void Settings(const nghttp2_settings_entry iv[], size_t niv);
|
||||||
|
|
||||||
@ -868,6 +880,7 @@ class Http2Session : public AsyncWrap {
|
|||||||
const uv_buf_t* bufs,
|
const uv_buf_t* bufs,
|
||||||
uv_handle_type pending,
|
uv_handle_type pending,
|
||||||
void* ctx);
|
void* ctx);
|
||||||
|
static void OnStreamDestructImpl(void* ctx);
|
||||||
|
|
||||||
// The JavaScript API
|
// The JavaScript API
|
||||||
static void New(const FunctionCallbackInfo<Value>& args);
|
static void New(const FunctionCallbackInfo<Value>& args);
|
||||||
@ -878,7 +891,6 @@ class Http2Session : public AsyncWrap {
|
|||||||
static void Settings(const FunctionCallbackInfo<Value>& args);
|
static void Settings(const FunctionCallbackInfo<Value>& args);
|
||||||
static void Request(const FunctionCallbackInfo<Value>& args);
|
static void Request(const FunctionCallbackInfo<Value>& args);
|
||||||
static void SetNextStreamID(const FunctionCallbackInfo<Value>& args);
|
static void SetNextStreamID(const FunctionCallbackInfo<Value>& args);
|
||||||
static void ShutdownNotice(const FunctionCallbackInfo<Value>& args);
|
|
||||||
static void Goaway(const FunctionCallbackInfo<Value>& args);
|
static void Goaway(const FunctionCallbackInfo<Value>& args);
|
||||||
static void UpdateChunksSent(const FunctionCallbackInfo<Value>& args);
|
static void UpdateChunksSent(const FunctionCallbackInfo<Value>& args);
|
||||||
static void RefreshState(const FunctionCallbackInfo<Value>& args);
|
static void RefreshState(const FunctionCallbackInfo<Value>& args);
|
||||||
|
@ -5,53 +5,37 @@ if (!common.hasCrypto)
|
|||||||
common.skip('missing crypto');
|
common.skip('missing crypto');
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const http2 = require('http2');
|
const http2 = require('http2');
|
||||||
|
const Countdown = require('../common/countdown');
|
||||||
|
|
||||||
const server = http2.createServer();
|
const server = http2.createServer();
|
||||||
server.on('stream', common.mustCall((stream, headers, flags) => {
|
server.on('stream', common.mustCall((stream, headers, flags) => {
|
||||||
const port = server.address().port;
|
|
||||||
if (headers[':path'] === '/') {
|
if (headers[':path'] === '/') {
|
||||||
stream.pushStream({
|
stream.pushStream({ ':path': '/foobar' }, (err, push, headers) => {
|
||||||
':scheme': 'http',
|
assert.ifError(err);
|
||||||
':path': '/foobar',
|
|
||||||
':authority': `localhost:${port}`,
|
|
||||||
}, (push, headers) => {
|
|
||||||
push.respond({
|
push.respond({
|
||||||
'content-type': 'text/html',
|
'content-type': 'text/html',
|
||||||
':status': 200,
|
|
||||||
'x-push-data': 'pushed by server',
|
'x-push-data': 'pushed by server',
|
||||||
});
|
});
|
||||||
push.write('pushed by server ');
|
push.write('pushed by server ');
|
||||||
// Sending in next immediate ensures that a second data frame
|
setImmediate(() => push.end('data'));
|
||||||
// will be sent to the client, which will cause the 'data' event
|
|
||||||
// to fire multiple times.
|
|
||||||
setImmediate(() => {
|
|
||||||
push.end('data');
|
|
||||||
});
|
|
||||||
stream.end('st');
|
stream.end('st');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
stream.respond({
|
stream.respond({ 'content-type': 'text/html' });
|
||||||
'content-type': 'text/html',
|
|
||||||
':status': 200
|
|
||||||
});
|
|
||||||
stream.write('te');
|
stream.write('te');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
server.listen(0, common.mustCall(() => {
|
server.listen(0, common.mustCall(() => {
|
||||||
const port = server.address().port;
|
const port = server.address().port;
|
||||||
const headers = { ':path': '/' };
|
|
||||||
const client = http2.connect(`http://localhost:${port}`);
|
const client = http2.connect(`http://localhost:${port}`);
|
||||||
|
|
||||||
const req = client.request(headers);
|
const req = client.request();
|
||||||
|
|
||||||
let expected = 2;
|
const countdown = new Countdown(2, () => {
|
||||||
function maybeClose() {
|
server.close();
|
||||||
if (--expected === 0) {
|
client.close();
|
||||||
server.close();
|
});
|
||||||
client.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
req.on('response', common.mustCall((headers) => {
|
req.on('response', common.mustCall((headers) => {
|
||||||
assert.strictEqual(headers[':status'], 200);
|
assert.strictEqual(headers[':status'], 200);
|
||||||
@ -70,13 +54,11 @@ server.listen(0, common.mustCall(() => {
|
|||||||
|
|
||||||
stream.setEncoding('utf8');
|
stream.setEncoding('utf8');
|
||||||
let pushData = '';
|
let pushData = '';
|
||||||
stream.on('data', common.mustCall((d) => {
|
stream.on('data', (d) => pushData += d);
|
||||||
pushData += d;
|
|
||||||
}, 2));
|
|
||||||
stream.on('end', common.mustCall(() => {
|
stream.on('end', common.mustCall(() => {
|
||||||
assert.strictEqual(pushData, 'pushed by server data');
|
assert.strictEqual(pushData, 'pushed by server data');
|
||||||
maybeClose();
|
|
||||||
}));
|
}));
|
||||||
|
stream.on('close', () => countdown.dec());
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let data = '';
|
let data = '';
|
||||||
@ -85,7 +67,6 @@ server.listen(0, common.mustCall(() => {
|
|||||||
req.on('data', common.mustCallAtLeast((d) => data += d));
|
req.on('data', common.mustCallAtLeast((d) => data += d));
|
||||||
req.on('end', common.mustCall(() => {
|
req.on('end', common.mustCall(() => {
|
||||||
assert.strictEqual(data, 'test');
|
assert.strictEqual(data, 'test');
|
||||||
maybeClose();
|
|
||||||
}));
|
}));
|
||||||
req.end();
|
req.on('close', () => countdown.dec());
|
||||||
}));
|
}));
|
||||||
|
@ -8,139 +8,115 @@ if (!common.hasCrypto)
|
|||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const h2 = require('http2');
|
const h2 = require('http2');
|
||||||
const { kSocket } = require('internal/http2/util');
|
const { kSocket } = require('internal/http2/util');
|
||||||
|
const Countdown = require('../common/countdown');
|
||||||
|
|
||||||
{
|
{
|
||||||
const server = h2.createServer();
|
const server = h2.createServer();
|
||||||
server.listen(
|
server.listen(0, common.mustCall(() => {
|
||||||
0,
|
const destroyCallbacks = [
|
||||||
common.mustCall(() => {
|
(client) => client.destroy(),
|
||||||
const destroyCallbacks = [
|
(client) => client[kSocket].destroy()
|
||||||
(client) => client.destroy(),
|
];
|
||||||
(client) => client[kSocket].destroy()
|
|
||||||
];
|
|
||||||
|
|
||||||
let remaining = destroyCallbacks.length;
|
const countdown = new Countdown(destroyCallbacks.length, () => {
|
||||||
|
server.close();
|
||||||
|
});
|
||||||
|
|
||||||
destroyCallbacks.forEach((destroyCallback) => {
|
destroyCallbacks.forEach((destroyCallback) => {
|
||||||
const client = h2.connect(`http://localhost:${server.address().port}`);
|
const client = h2.connect(`http://localhost:${server.address().port}`);
|
||||||
client.on(
|
client.on('connect', common.mustCall(() => {
|
||||||
'connect',
|
const socket = client[kSocket];
|
||||||
common.mustCall(() => {
|
|
||||||
const socket = client[kSocket];
|
|
||||||
|
|
||||||
assert(socket, 'client session has associated socket');
|
assert(socket, 'client session has associated socket');
|
||||||
assert(
|
assert(
|
||||||
!client.destroyed,
|
!client.destroyed,
|
||||||
'client has not been destroyed before destroy is called'
|
'client has not been destroyed before destroy is called'
|
||||||
);
|
|
||||||
assert(
|
|
||||||
!socket.destroyed,
|
|
||||||
'socket has not been destroyed before destroy is called'
|
|
||||||
);
|
|
||||||
|
|
||||||
// Ensure that 'close' event is emitted
|
|
||||||
client.on('close', common.mustCall());
|
|
||||||
|
|
||||||
destroyCallback(client);
|
|
||||||
|
|
||||||
assert(
|
|
||||||
!client[kSocket],
|
|
||||||
'client.socket undefined after destroy is called'
|
|
||||||
);
|
|
||||||
|
|
||||||
// Must must be closed
|
|
||||||
client.on(
|
|
||||||
'close',
|
|
||||||
common.mustCall(() => {
|
|
||||||
assert(client.destroyed);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
// socket will close on process.nextTick
|
|
||||||
socket.on(
|
|
||||||
'close',
|
|
||||||
common.mustCall(() => {
|
|
||||||
assert(socket.destroyed);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
if (--remaining === 0) {
|
|
||||||
server.close();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
);
|
||||||
});
|
assert(
|
||||||
})
|
!socket.destroyed,
|
||||||
);
|
'socket has not been destroyed before destroy is called'
|
||||||
|
);
|
||||||
|
|
||||||
|
destroyCallback(client);
|
||||||
|
|
||||||
|
client.on('close', common.mustCall(() => {
|
||||||
|
assert(client.destroyed);
|
||||||
|
}));
|
||||||
|
|
||||||
|
countdown.dec();
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
// test destroy before client operations
|
// test destroy before client operations
|
||||||
{
|
{
|
||||||
const server = h2.createServer();
|
const server = h2.createServer();
|
||||||
server.listen(
|
server.listen(0, common.mustCall(() => {
|
||||||
0,
|
const client = h2.connect(`http://localhost:${server.address().port}`);
|
||||||
common.mustCall(() => {
|
const socket = client[kSocket];
|
||||||
const client = h2.connect(`http://localhost:${server.address().port}`);
|
socket.on('close', common.mustCall(() => {
|
||||||
const req = client.request();
|
assert(socket.destroyed);
|
||||||
client.destroy();
|
}));
|
||||||
|
|
||||||
req.on('response', common.mustNotCall());
|
|
||||||
req.resume();
|
|
||||||
|
|
||||||
const sessionError = {
|
const req = client.request();
|
||||||
type: Error,
|
req.on('error', common.expectsError({
|
||||||
code: 'ERR_HTTP2_INVALID_SESSION',
|
code: 'ERR_HTTP2_STREAM_CANCEL',
|
||||||
message: 'The session has been destroyed'
|
type: Error,
|
||||||
};
|
message: 'The pending stream has been canceled'
|
||||||
|
}));
|
||||||
|
|
||||||
|
client.destroy();
|
||||||
|
|
||||||
|
req.on('response', common.mustNotCall());
|
||||||
|
|
||||||
|
const sessionError = {
|
||||||
|
type: Error,
|
||||||
|
code: 'ERR_HTTP2_INVALID_SESSION',
|
||||||
|
message: 'The session has been destroyed'
|
||||||
|
};
|
||||||
|
|
||||||
|
common.expectsError(() => client.request(), sessionError);
|
||||||
|
common.expectsError(() => client.settings({}), sessionError);
|
||||||
|
client.close(); // should be a non-op at this point
|
||||||
|
|
||||||
|
// Wait for setImmediate call from destroy() to complete
|
||||||
|
// so that state.destroyed is set to true
|
||||||
|
setImmediate(() => {
|
||||||
common.expectsError(() => client.request(), sessionError);
|
common.expectsError(() => client.request(), sessionError);
|
||||||
common.expectsError(() => client.settings({}), sessionError);
|
common.expectsError(() => client.settings({}), sessionError);
|
||||||
common.expectsError(() => client.shutdown(), sessionError);
|
client.close(); // should be a non-op at this point
|
||||||
|
});
|
||||||
|
|
||||||
// Wait for setImmediate call from destroy() to complete
|
req.resume();
|
||||||
// so that state.destroyed is set to true
|
req.on('end', common.mustCall());
|
||||||
setImmediate(() => {
|
req.on('close', common.mustCall(() => server.close()));
|
||||||
common.expectsError(() => client.request(), sessionError);
|
}));
|
||||||
common.expectsError(() => client.settings({}), sessionError);
|
|
||||||
common.expectsError(() => client.shutdown(), sessionError);
|
|
||||||
});
|
|
||||||
|
|
||||||
req.on(
|
|
||||||
'end',
|
|
||||||
common.mustCall(() => {
|
|
||||||
server.close();
|
|
||||||
})
|
|
||||||
);
|
|
||||||
req.end();
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// test destroy before goaway
|
// test destroy before goaway
|
||||||
{
|
{
|
||||||
const server = h2.createServer();
|
const server = h2.createServer();
|
||||||
server.on(
|
server.on('stream', common.mustCall((stream) => {
|
||||||
'stream',
|
stream.session.destroy();
|
||||||
common.mustCall((stream) => {
|
}));
|
||||||
stream.on('error', common.mustCall());
|
|
||||||
stream.session.shutdown();
|
|
||||||
})
|
|
||||||
);
|
|
||||||
server.listen(
|
|
||||||
0,
|
|
||||||
common.mustCall(() => {
|
|
||||||
const client = h2.connect(`http://localhost:${server.address().port}`);
|
|
||||||
|
|
||||||
client.on(
|
server.listen(0, common.mustCall(() => {
|
||||||
'goaway',
|
const client = h2.connect(`http://localhost:${server.address().port}`);
|
||||||
common.mustCall(() => {
|
// On some platforms (e.g. windows), an ECONNRESET may occur at this
|
||||||
// We ought to be able to destroy the client in here without an error
|
// point -- or it may not. Do not make this a mustCall
|
||||||
server.close();
|
client.on('error', () => {});
|
||||||
client.destroy();
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
client.request();
|
client.on('close', () => {
|
||||||
})
|
server.close();
|
||||||
);
|
// calling destroy in here should not matter
|
||||||
|
client.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
const req = client.request();
|
||||||
|
// On some platforms (e.g. windows), an ECONNRESET may occur at this
|
||||||
|
// point -- or it may not. Do not make this a mustCall
|
||||||
|
req.on('error', () => {});
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ if (!common.hasCrypto)
|
|||||||
const http = require('http');
|
const http = require('http');
|
||||||
const http2 = require('http2');
|
const http2 = require('http2');
|
||||||
|
|
||||||
|
// Creating an http1 server here...
|
||||||
const server = http.createServer(common.mustNotCall());
|
const server = http.createServer(common.mustNotCall());
|
||||||
|
|
||||||
server.listen(0, common.mustCall(() => {
|
server.listen(0, common.mustCall(() => {
|
||||||
@ -15,13 +16,17 @@ server.listen(0, common.mustCall(() => {
|
|||||||
const req = client.request();
|
const req = client.request();
|
||||||
req.on('close', common.mustCall());
|
req.on('close', common.mustCall());
|
||||||
|
|
||||||
|
req.on('error', common.expectsError({
|
||||||
|
code: 'ERR_HTTP2_ERROR',
|
||||||
|
type: Error,
|
||||||
|
message: 'Protocol error'
|
||||||
|
}));
|
||||||
|
|
||||||
client.on('error', common.expectsError({
|
client.on('error', common.expectsError({
|
||||||
code: 'ERR_HTTP2_ERROR',
|
code: 'ERR_HTTP2_ERROR',
|
||||||
type: Error,
|
type: Error,
|
||||||
message: 'Protocol error'
|
message: 'Protocol error'
|
||||||
}));
|
}));
|
||||||
|
|
||||||
client.on('close', (...args) => {
|
client.on('close', common.mustCall(() => server.close()));
|
||||||
server.close();
|
|
||||||
});
|
|
||||||
}));
|
}));
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
const common = require('../common');
|
||||||
|
if (!common.hasCrypto)
|
||||||
|
common.skip('missing crypto');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
constants,
|
constants,
|
||||||
Http2Session,
|
Http2Session,
|
||||||
nghttp2ErrorString
|
nghttp2ErrorString
|
||||||
} = process.binding('http2');
|
} = process.binding('http2');
|
||||||
const common = require('../common');
|
|
||||||
if (!common.hasCrypto)
|
|
||||||
common.skip('missing crypto');
|
|
||||||
const http2 = require('http2');
|
const http2 = require('http2');
|
||||||
|
|
||||||
// tests error handling within requestOnConnect
|
// tests error handling within requestOnConnect
|
||||||
@ -69,6 +70,8 @@ server.listen(0, common.mustCall(() => runTest(tests.shift())));
|
|||||||
|
|
||||||
function runTest(test) {
|
function runTest(test) {
|
||||||
const client = http2.connect(`http://localhost:${server.address().port}`);
|
const client = http2.connect(`http://localhost:${server.address().port}`);
|
||||||
|
client.on('close', common.mustCall());
|
||||||
|
|
||||||
const req = client.request({ ':method': 'POST' });
|
const req = client.request({ ':method': 'POST' });
|
||||||
|
|
||||||
currentError = test.ngError;
|
currentError = test.ngError;
|
||||||
@ -83,15 +86,15 @@ function runTest(test) {
|
|||||||
if (test.type === 'stream') {
|
if (test.type === 'stream') {
|
||||||
client.on('error', errorMustNotCall);
|
client.on('error', errorMustNotCall);
|
||||||
req.on('error', errorMustCall);
|
req.on('error', errorMustCall);
|
||||||
req.on('error', common.mustCall(() => {
|
|
||||||
client.destroy();
|
|
||||||
}));
|
|
||||||
} else {
|
} else {
|
||||||
client.on('error', errorMustCall);
|
client.on('error', errorMustCall);
|
||||||
req.on('error', errorMustNotCall);
|
req.on('error', common.expectsError({
|
||||||
|
code: 'ERR_HTTP2_STREAM_CANCEL'
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
req.on('end', common.mustCall(() => {
|
req.on('end', common.mustCall());
|
||||||
|
req.on('close', common.mustCall(() => {
|
||||||
client.destroy();
|
client.destroy();
|
||||||
|
|
||||||
if (!tests.length) {
|
if (!tests.length) {
|
||||||
|
@ -16,4 +16,10 @@ net.connect = common.mustCall((...args) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const client = http2.connect('http://localhost:80');
|
const client = http2.connect('http://localhost:80');
|
||||||
client.destroy();
|
|
||||||
|
// A socket error may or may not occur depending on whether there is something
|
||||||
|
// currently listening on port 80. Keep this as a non-op and not a mustCall or
|
||||||
|
// mustNotCall.
|
||||||
|
client.on('error', () => {});
|
||||||
|
|
||||||
|
client.close();
|
||||||
|
@ -8,31 +8,21 @@ const h2 = require('http2');
|
|||||||
const server = h2.createServer();
|
const server = h2.createServer();
|
||||||
|
|
||||||
// we use the lower-level API here
|
// we use the lower-level API here
|
||||||
server.on('stream', common.mustCall(onStream));
|
server.on('stream', common.mustCall((stream) => {
|
||||||
|
stream.respond();
|
||||||
function onStream(stream, headers, flags) {
|
stream.end('ok');
|
||||||
stream.respond({
|
}));
|
||||||
'content-type': 'text/html',
|
|
||||||
':status': 200
|
|
||||||
});
|
|
||||||
stream.end('hello world');
|
|
||||||
}
|
|
||||||
|
|
||||||
server.listen(0);
|
|
||||||
|
|
||||||
server.on('listening', common.mustCall(() => {
|
|
||||||
|
|
||||||
|
server.listen(0, common.mustCall(() => {
|
||||||
const client = h2.connect(`http://localhost:${server.address().port}`);
|
const client = h2.connect(`http://localhost:${server.address().port}`);
|
||||||
|
const req = client.request();
|
||||||
const req = client.request({ ':path': '/' });
|
|
||||||
req.priority({});
|
req.priority({});
|
||||||
|
|
||||||
req.on('response', common.mustCall());
|
req.on('response', common.mustCall());
|
||||||
req.resume();
|
req.resume();
|
||||||
req.on('end', common.mustCall(() => {
|
req.on('end', common.mustCall());
|
||||||
|
req.on('close', common.mustCall(() => {
|
||||||
server.close();
|
server.close();
|
||||||
client.destroy();
|
client.close();
|
||||||
}));
|
}));
|
||||||
req.end();
|
|
||||||
|
|
||||||
}));
|
}));
|
||||||
|
@ -15,7 +15,6 @@ server.on('stream', common.mustCall((stream) => {
|
|||||||
stream.end('ok');
|
stream.end('ok');
|
||||||
}));
|
}));
|
||||||
server.listen(0, common.mustCall(() => {
|
server.listen(0, common.mustCall(() => {
|
||||||
|
|
||||||
const connect = util.promisify(http2.connect);
|
const connect = util.promisify(http2.connect);
|
||||||
|
|
||||||
connect(`http://localhost:${server.address().port}`)
|
connect(`http://localhost:${server.address().port}`)
|
||||||
@ -28,7 +27,7 @@ server.listen(0, common.mustCall(() => {
|
|||||||
req.on('data', (chunk) => data += chunk);
|
req.on('data', (chunk) => data += chunk);
|
||||||
req.on('end', common.mustCall(() => {
|
req.on('end', common.mustCall(() => {
|
||||||
assert.strictEqual(data, 'ok');
|
assert.strictEqual(data, 'ok');
|
||||||
client.destroy();
|
client.close();
|
||||||
server.close();
|
server.close();
|
||||||
}));
|
}));
|
||||||
}));
|
}));
|
||||||
|
@ -33,29 +33,27 @@ server.listen(0, common.mustCall(() => {
|
|||||||
const port = server.address().port;
|
const port = server.address().port;
|
||||||
const client = http2.connect(`http://localhost:${port}`);
|
const client = http2.connect(`http://localhost:${port}`);
|
||||||
|
|
||||||
Object.keys(optionsToTest).forEach((option) => {
|
client.on('connect', () => {
|
||||||
Object.keys(types).forEach((type) => {
|
Object.keys(optionsToTest).forEach((option) => {
|
||||||
if (type === optionsToTest[option]) {
|
Object.keys(types).forEach((type) => {
|
||||||
return;
|
if (type === optionsToTest[option])
|
||||||
}
|
return;
|
||||||
|
|
||||||
common.expectsError(
|
common.expectsError(
|
||||||
() => client.request({
|
() => client.request({
|
||||||
':method': 'CONNECT',
|
':method': 'CONNECT',
|
||||||
':authority': `localhost:${port}`
|
':authority': `localhost:${port}`
|
||||||
}, {
|
}, {
|
||||||
[option]: types[type]
|
[option]: types[type]
|
||||||
}),
|
}), {
|
||||||
{
|
type: TypeError,
|
||||||
type: TypeError,
|
code: 'ERR_INVALID_OPT_VALUE',
|
||||||
code: 'ERR_INVALID_OPT_VALUE',
|
message: `The value "${String(types[type])}" is invalid ` +
|
||||||
message: `The value "${String(types[type])}" is invalid ` +
|
`for option "${option}"`
|
||||||
`for option "${option}"`
|
});
|
||||||
}
|
});
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
server.close();
|
||||||
|
client.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
server.close();
|
|
||||||
client.destroy();
|
|
||||||
}));
|
}));
|
||||||
|
@ -8,33 +8,37 @@ const h2 = require('http2');
|
|||||||
|
|
||||||
const server = h2.createServer();
|
const server = h2.createServer();
|
||||||
server.on('stream', (stream) => {
|
server.on('stream', (stream) => {
|
||||||
|
stream.on('close', common.mustCall());
|
||||||
stream.respond();
|
stream.respond();
|
||||||
stream.end('ok');
|
stream.end('ok');
|
||||||
});
|
});
|
||||||
|
|
||||||
server.listen(0);
|
server.listen(0, common.mustCall(() => {
|
||||||
|
|
||||||
server.on('listening', common.mustCall(() => {
|
|
||||||
|
|
||||||
const client = h2.connect(`http://localhost:${server.address().port}`);
|
const client = h2.connect(`http://localhost:${server.address().port}`);
|
||||||
|
const req = client.request();
|
||||||
const req = client.request({ ':path': '/' });
|
req.close(1);
|
||||||
req.rstStream(0);
|
assert.strictEqual(req.closed, true);
|
||||||
|
|
||||||
// make sure that destroy is called
|
// make sure that destroy is called
|
||||||
req._destroy = common.mustCall(req._destroy.bind(req));
|
req._destroy = common.mustCall(req._destroy.bind(req));
|
||||||
|
|
||||||
// second call doesn't do anything
|
// second call doesn't do anything
|
||||||
assert.doesNotThrow(() => req.rstStream(8));
|
assert.doesNotThrow(() => req.close(8));
|
||||||
|
|
||||||
req.on('close', common.mustCall((code) => {
|
req.on('close', common.mustCall((code) => {
|
||||||
assert.strictEqual(req.destroyed, true);
|
assert.strictEqual(req.destroyed, true);
|
||||||
assert.strictEqual(code, 0);
|
assert.strictEqual(code, 1);
|
||||||
server.close();
|
server.close();
|
||||||
client.destroy();
|
client.close();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
req.on('response', common.mustNotCall());
|
req.on('error', common.expectsError({
|
||||||
|
code: 'ERR_HTTP2_STREAM_ERROR',
|
||||||
|
type: Error,
|
||||||
|
message: 'Stream closed with error code 1'
|
||||||
|
}));
|
||||||
|
|
||||||
|
req.on('response', common.mustCall());
|
||||||
req.resume();
|
req.resume();
|
||||||
req.on('end', common.mustCall());
|
req.on('end', common.mustCall());
|
||||||
req.end();
|
req.end();
|
||||||
|
@ -10,26 +10,20 @@ const checkWeight = (actual, expect) => {
|
|||||||
const server = http2.createServer();
|
const server = http2.createServer();
|
||||||
server.on('stream', common.mustCall((stream, headers, flags) => {
|
server.on('stream', common.mustCall((stream, headers, flags) => {
|
||||||
assert.strictEqual(stream.state.weight, expect);
|
assert.strictEqual(stream.state.weight, expect);
|
||||||
stream.respond({
|
stream.respond();
|
||||||
'content-type': 'text/html',
|
|
||||||
':status': 200
|
|
||||||
});
|
|
||||||
stream.end('test');
|
stream.end('test');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
server.listen(0, common.mustCall(() => {
|
server.listen(0, common.mustCall(() => {
|
||||||
const port = server.address().port;
|
const client = http2.connect(`http://localhost:${server.address().port}`);
|
||||||
const client = http2.connect(`http://localhost:${port}`);
|
const req = client.request({}, { weight: actual });
|
||||||
|
|
||||||
const headers = { ':path': '/' };
|
req.on('data', common.mustCall());
|
||||||
const req = client.request(headers, { weight: actual });
|
req.on('end', common.mustCall());
|
||||||
|
req.on('close', common.mustCall(() => {
|
||||||
req.on('data', common.mustCall(() => {}));
|
|
||||||
req.on('end', common.mustCall(() => {
|
|
||||||
server.close();
|
server.close();
|
||||||
client.destroy();
|
client.close();
|
||||||
}));
|
}));
|
||||||
req.end();
|
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3,62 +3,53 @@
|
|||||||
const common = require('../common');
|
const common = require('../common');
|
||||||
if (!common.hasCrypto)
|
if (!common.hasCrypto)
|
||||||
common.skip('missing crypto');
|
common.skip('missing crypto');
|
||||||
const assert = require('assert');
|
|
||||||
const h2 = require('http2');
|
const h2 = require('http2');
|
||||||
|
|
||||||
const server = h2.createServer();
|
const server = h2.createServer();
|
||||||
|
|
||||||
// we use the lower-level API here
|
// we use the lower-level API here
|
||||||
server.on('stream', common.mustCall(onStream));
|
server.on('stream', common.mustCall((stream, headers, flags) => {
|
||||||
|
stream.respond();
|
||||||
function onStream(stream, headers, flags) {
|
stream.end('ok');
|
||||||
stream.respond({
|
}));
|
||||||
'content-type': 'text/html',
|
server.on('session', common.mustCall((session) => {
|
||||||
':status': 200
|
session.on('remoteSettings', common.mustCall(2));
|
||||||
});
|
}));
|
||||||
stream.end('hello world');
|
|
||||||
}
|
|
||||||
|
|
||||||
server.listen(0);
|
|
||||||
|
|
||||||
server.on('listening', common.mustCall(() => {
|
|
||||||
|
|
||||||
|
server.listen(0, common.mustCall(() => {
|
||||||
const client = h2.connect(`http://localhost:${server.address().port}`);
|
const client = h2.connect(`http://localhost:${server.address().port}`);
|
||||||
|
|
||||||
assert.throws(() => client.settings({ headerTableSize: -1 }),
|
[
|
||||||
RangeError);
|
['headerTableSize', -1, RangeError],
|
||||||
assert.throws(() => client.settings({ headerTableSize: 2 ** 32 }),
|
['headerTableSize', 2 ** 32, RangeError],
|
||||||
RangeError);
|
['initialWindowSize', -1, RangeError],
|
||||||
assert.throws(() => client.settings({ initialWindowSize: -1 }),
|
['initialWindowSize', 2 ** 32, RangeError],
|
||||||
RangeError);
|
['maxFrameSize', 1, RangeError],
|
||||||
assert.throws(() => client.settings({ initialWindowSize: 2 ** 32 }),
|
['maxFrameSize', 2 ** 24, RangeError],
|
||||||
RangeError);
|
['maxConcurrentStreams', -1, RangeError],
|
||||||
assert.throws(() => client.settings({ maxFrameSize: 1 }),
|
['maxConcurrentStreams', 2 ** 31, RangeError],
|
||||||
RangeError);
|
['maxHeaderListSize', -1, RangeError],
|
||||||
assert.throws(() => client.settings({ maxFrameSize: 2 ** 24 }),
|
['maxHeaderListSize', 2 ** 32, RangeError],
|
||||||
RangeError);
|
['enablePush', 'a', TypeError],
|
||||||
assert.throws(() => client.settings({ maxConcurrentStreams: -1 }),
|
['enablePush', 1, TypeError],
|
||||||
RangeError);
|
['enablePush', 0, TypeError],
|
||||||
assert.throws(() => client.settings({ maxConcurrentStreams: 2 ** 31 }),
|
['enablePush', null, TypeError],
|
||||||
RangeError);
|
['enablePush', {}, TypeError]
|
||||||
assert.throws(() => client.settings({ maxHeaderListSize: -1 }),
|
].forEach((i) => {
|
||||||
RangeError);
|
common.expectsError(
|
||||||
assert.throws(() => client.settings({ maxHeaderListSize: 2 ** 32 }),
|
() => client.settings({ [i[0]]: i[1] }),
|
||||||
RangeError);
|
{
|
||||||
['a', 1, 0, null, {}].forEach((i) => {
|
code: 'ERR_HTTP2_INVALID_SETTING_VALUE',
|
||||||
assert.throws(() => client.settings({ enablePush: i }), TypeError);
|
type: i[2] });
|
||||||
});
|
});
|
||||||
|
|
||||||
client.settings({ maxFrameSize: 1234567 });
|
client.settings({ maxFrameSize: 1234567 });
|
||||||
|
|
||||||
const req = client.request({ ':path': '/' });
|
const req = client.request();
|
||||||
|
|
||||||
req.on('response', common.mustCall());
|
req.on('response', common.mustCall());
|
||||||
req.resume();
|
req.resume();
|
||||||
req.on('end', common.mustCall(() => {
|
req.on('close', common.mustCall(() => {
|
||||||
server.close();
|
server.close();
|
||||||
client.destroy();
|
client.close();
|
||||||
}));
|
}));
|
||||||
req.end();
|
|
||||||
|
|
||||||
}));
|
}));
|
||||||
|
@ -10,15 +10,7 @@ const server = h2.createServer();
|
|||||||
// we use the lower-level API here
|
// we use the lower-level API here
|
||||||
server.on('stream', common.mustNotCall());
|
server.on('stream', common.mustNotCall());
|
||||||
|
|
||||||
server.listen(0);
|
server.listen(0, common.mustCall(() => {
|
||||||
|
|
||||||
server.on('listening', common.mustCall(() => {
|
|
||||||
|
|
||||||
const client = h2.connect(`http://localhost:${server.address().port}`);
|
const client = h2.connect(`http://localhost:${server.address().port}`);
|
||||||
|
client.close(common.mustCall(() => server.close()));
|
||||||
client.shutdown({ graceful: true }, common.mustCall(() => {
|
|
||||||
server.close();
|
|
||||||
client.destroy();
|
|
||||||
}));
|
|
||||||
|
|
||||||
}));
|
}));
|
||||||
|
@ -14,38 +14,27 @@ const body =
|
|||||||
const server = h2.createServer();
|
const server = h2.createServer();
|
||||||
|
|
||||||
// we use the lower-level API here
|
// we use the lower-level API here
|
||||||
server.on('stream', common.mustCall(onStream));
|
server.on('stream', common.mustCall((stream) => {
|
||||||
|
|
||||||
function onStream(stream) {
|
|
||||||
// The stream aborted event must have been triggered
|
|
||||||
stream.on('aborted', common.mustCall());
|
stream.on('aborted', common.mustCall());
|
||||||
|
stream.on('close', common.mustCall());
|
||||||
stream.respond({
|
stream.respond();
|
||||||
'content-type': 'text/html',
|
|
||||||
':status': 200
|
|
||||||
});
|
|
||||||
stream.write(body);
|
stream.write(body);
|
||||||
}
|
// purposefully do not end()
|
||||||
|
}));
|
||||||
|
|
||||||
server.listen(0);
|
server.listen(0, common.mustCall(function() {
|
||||||
|
|
||||||
server.on('listening', common.mustCall(function() {
|
|
||||||
const client = h2.connect(`http://localhost:${this.address().port}`);
|
const client = h2.connect(`http://localhost:${this.address().port}`);
|
||||||
|
const req = client.request();
|
||||||
const req = client.request({ ':path': '/' });
|
|
||||||
|
|
||||||
req.on('response', common.mustCall(() => {
|
req.on('response', common.mustCall(() => {
|
||||||
// send a premature socket close
|
// send a premature socket close
|
||||||
client[kSocket].destroy();
|
client[kSocket].destroy();
|
||||||
}));
|
}));
|
||||||
req.on('data', common.mustNotCall());
|
|
||||||
|
|
||||||
req.on('end', common.mustCall(() => {
|
req.resume();
|
||||||
server.close();
|
req.on('end', common.mustCall());
|
||||||
}));
|
req.on('close', common.mustCall(() => server.close()));
|
||||||
|
|
||||||
// On the client, the close event must call
|
// On the client, the close event must call
|
||||||
client.on('close', common.mustCall());
|
client.on('close', common.mustCall());
|
||||||
req.end();
|
|
||||||
|
|
||||||
}));
|
}));
|
||||||
|
@ -20,36 +20,29 @@ server.on('stream', (stream) => {
|
|||||||
assert.strictEqual(err.code, 'ERR_HTTP2_STREAM_ERROR');
|
assert.strictEqual(err.code, 'ERR_HTTP2_STREAM_ERROR');
|
||||||
assert.strictEqual(err.message, 'Stream closed with error code 2');
|
assert.strictEqual(err.message, 'Stream closed with error code 2');
|
||||||
});
|
});
|
||||||
stream.respond({});
|
stream.respond();
|
||||||
stream.end();
|
stream.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
server.listen(0);
|
server.listen(0, common.mustCall(() => {
|
||||||
|
|
||||||
server.on('listening', common.mustCall(() => {
|
|
||||||
|
|
||||||
const client = h2.connect(`http://localhost:${server.address().port}`);
|
const client = h2.connect(`http://localhost:${server.address().port}`);
|
||||||
|
|
||||||
const req = client.request({ ':path': '/' });
|
const req = client.request();
|
||||||
const err = new Error('test');
|
req.destroy(new Error('test'));
|
||||||
req.destroy(err);
|
|
||||||
|
|
||||||
req.on('error', common.mustCall((err) => {
|
req.on('error', common.expectsError({
|
||||||
common.expectsError({
|
type: Error,
|
||||||
type: Error,
|
message: 'test'
|
||||||
message: 'test'
|
|
||||||
})(err);
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
req.on('close', common.mustCall((code) => {
|
req.on('close', common.mustCall((code) => {
|
||||||
assert.strictEqual(req.rstCode, NGHTTP2_INTERNAL_ERROR);
|
assert.strictEqual(req.rstCode, NGHTTP2_INTERNAL_ERROR);
|
||||||
assert.strictEqual(code, NGHTTP2_INTERNAL_ERROR);
|
assert.strictEqual(code, NGHTTP2_INTERNAL_ERROR);
|
||||||
server.close();
|
server.close();
|
||||||
client.destroy();
|
client.close();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
req.on('response', common.mustNotCall());
|
req.on('response', common.mustNotCall());
|
||||||
req.resume();
|
req.resume();
|
||||||
req.on('end', common.mustCall());
|
req.on('end', common.mustCall());
|
||||||
|
|
||||||
}));
|
}));
|
||||||
|
@ -4,6 +4,7 @@ const common = require('../common');
|
|||||||
if (!common.hasCrypto)
|
if (!common.hasCrypto)
|
||||||
common.skip('missing crypto');
|
common.skip('missing crypto');
|
||||||
const http2 = require('http2');
|
const http2 = require('http2');
|
||||||
|
const Countdown = require('../common/countdown');
|
||||||
|
|
||||||
const server = http2.createServer();
|
const server = http2.createServer();
|
||||||
|
|
||||||
@ -13,14 +14,12 @@ const count = 32;
|
|||||||
|
|
||||||
server.listen(0, common.mustCall(() => {
|
server.listen(0, common.mustCall(() => {
|
||||||
const client = http2.connect(`http://localhost:${server.address().port}`);
|
const client = http2.connect(`http://localhost:${server.address().port}`);
|
||||||
|
client.setMaxListeners(33);
|
||||||
|
|
||||||
let remaining = count + 1;
|
const countdown = new Countdown(count + 1, () => {
|
||||||
function maybeClose() {
|
server.close();
|
||||||
if (--remaining === 0) {
|
client.close();
|
||||||
server.close();
|
});
|
||||||
client.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// nghttp2 will catch the bad header value for us.
|
// nghttp2 will catch the bad header value for us.
|
||||||
function doTest(i) {
|
function doTest(i) {
|
||||||
@ -30,7 +29,7 @@ server.listen(0, common.mustCall(() => {
|
|||||||
type: Error,
|
type: Error,
|
||||||
message: 'Stream closed with error code 1'
|
message: 'Stream closed with error code 1'
|
||||||
}));
|
}));
|
||||||
req.on('close', common.mustCall(maybeClose));
|
req.on('close', common.mustCall(() => countdown.dec()));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i <= count; i += 1)
|
for (let i = 0; i <= count; i += 1)
|
||||||
|
@ -9,6 +9,7 @@ const assert = require('assert');
|
|||||||
const http2 = require('http2');
|
const http2 = require('http2');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const fixtures = require('../common/fixtures');
|
const fixtures = require('../common/fixtures');
|
||||||
|
const Countdown = require('../common/countdown');
|
||||||
|
|
||||||
const loc = fixtures.path('person.jpg');
|
const loc = fixtures.path('person.jpg');
|
||||||
let fileData;
|
let fileData;
|
||||||
@ -34,20 +35,21 @@ fs.readFile(loc, common.mustCall((err, data) => {
|
|||||||
server.listen(0, common.mustCall(() => {
|
server.listen(0, common.mustCall(() => {
|
||||||
const client = http2.connect(`http://localhost:${server.address().port}`);
|
const client = http2.connect(`http://localhost:${server.address().port}`);
|
||||||
|
|
||||||
let remaining = 2;
|
const countdown = new Countdown(2, () => {
|
||||||
function maybeClose() {
|
server.close();
|
||||||
if (--remaining === 0) {
|
client.close();
|
||||||
server.close();
|
});
|
||||||
client.shutdown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const req = client.request({ ':method': 'POST' });
|
const req = client.request({ ':method': 'POST' });
|
||||||
req.on('response', common.mustCall());
|
req.on('response', common.mustCall());
|
||||||
|
|
||||||
req.resume();
|
req.resume();
|
||||||
req.on('end', common.mustCall(maybeClose));
|
req.on('end', common.mustCall());
|
||||||
|
|
||||||
|
req.on('finish', () => countdown.dec());
|
||||||
const str = fs.createReadStream(loc);
|
const str = fs.createReadStream(loc);
|
||||||
req.on('finish', common.mustCall(maybeClose));
|
str.on('end', common.mustCall());
|
||||||
|
str.on('close', () => countdown.dec());
|
||||||
str.pipe(req);
|
str.pipe(req);
|
||||||
}));
|
}));
|
||||||
}));
|
}));
|
||||||
|
@ -8,47 +8,30 @@ const h2 = require('http2');
|
|||||||
|
|
||||||
const server = h2.createServer();
|
const server = h2.createServer();
|
||||||
|
|
||||||
const {
|
|
||||||
HTTP2_HEADER_PATH,
|
|
||||||
HTTP2_HEADER_METHOD,
|
|
||||||
HTTP2_METHOD_POST
|
|
||||||
} = h2.constants;
|
|
||||||
|
|
||||||
// we use the lower-level API here
|
// we use the lower-level API here
|
||||||
server.on('stream', common.mustCall(onStream));
|
server.on('stream', common.mustCall((stream, headers, flags) => {
|
||||||
|
|
||||||
function onStream(stream, headers, flags) {
|
|
||||||
let data = '';
|
let data = '';
|
||||||
stream.setEncoding('utf8');
|
stream.setEncoding('utf8');
|
||||||
stream.on('data', (chunk) => data += chunk);
|
stream.on('data', (chunk) => data += chunk);
|
||||||
stream.on('end', common.mustCall(() => {
|
stream.on('end', common.mustCall(() => {
|
||||||
assert.strictEqual(data, 'some data more data');
|
assert.strictEqual(data, 'some data more data');
|
||||||
}));
|
}));
|
||||||
stream.respond({
|
stream.respond();
|
||||||
'content-type': 'text/html',
|
stream.end('ok');
|
||||||
':status': 200
|
}));
|
||||||
});
|
|
||||||
stream.end('hello world');
|
|
||||||
}
|
|
||||||
|
|
||||||
server.listen(0);
|
|
||||||
|
|
||||||
server.on('listening', common.mustCall(() => {
|
|
||||||
|
|
||||||
|
server.listen(0, common.mustCall(() => {
|
||||||
const client = h2.connect(`http://localhost:${server.address().port}`);
|
const client = h2.connect(`http://localhost:${server.address().port}`);
|
||||||
|
|
||||||
const req = client.request({
|
const req = client.request({ ':method': 'POST' });
|
||||||
[HTTP2_HEADER_PATH]: '/',
|
|
||||||
[HTTP2_HEADER_METHOD]: HTTP2_METHOD_POST });
|
|
||||||
req.write('some data ');
|
req.write('some data ');
|
||||||
req.write('more data');
|
req.end('more data');
|
||||||
|
|
||||||
req.on('response', common.mustCall());
|
req.on('response', common.mustCall());
|
||||||
req.resume();
|
req.resume();
|
||||||
req.on('end', common.mustCall(() => {
|
req.on('end', common.mustCall());
|
||||||
|
req.on('close', common.mustCall(() => {
|
||||||
server.close();
|
server.close();
|
||||||
client.destroy();
|
client.close();
|
||||||
}));
|
}));
|
||||||
req.end();
|
|
||||||
|
|
||||||
}));
|
}));
|
||||||
|
@ -4,9 +4,7 @@
|
|||||||
const common = require('../common');
|
const common = require('../common');
|
||||||
if (!common.hasCrypto)
|
if (!common.hasCrypto)
|
||||||
common.skip('missing crypto');
|
common.skip('missing crypto');
|
||||||
const assert = require('assert');
|
|
||||||
const h2 = require('http2');
|
const h2 = require('http2');
|
||||||
const { Http2Stream } = require('internal/http2/core');
|
|
||||||
|
|
||||||
// Errors should not be reported both in Http2ServerRequest
|
// Errors should not be reported both in Http2ServerRequest
|
||||||
// and Http2ServerResponse
|
// and Http2ServerResponse
|
||||||
@ -14,6 +12,7 @@ const { Http2Stream } = require('internal/http2/core');
|
|||||||
let expected = null;
|
let expected = null;
|
||||||
|
|
||||||
const server = h2.createServer(common.mustCall(function(req, res) {
|
const server = h2.createServer(common.mustCall(function(req, res) {
|
||||||
|
res.stream.on('error', common.mustCall());
|
||||||
req.on('error', common.mustNotCall());
|
req.on('error', common.mustNotCall());
|
||||||
res.on('error', common.mustNotCall());
|
res.on('error', common.mustNotCall());
|
||||||
req.on('aborted', common.mustCall());
|
req.on('aborted', common.mustCall());
|
||||||
@ -26,27 +25,12 @@ const server = h2.createServer(common.mustCall(function(req, res) {
|
|||||||
server.close();
|
server.close();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
server.on('streamError', common.mustCall(function(err, stream) {
|
|
||||||
assert.strictEqual(err, expected);
|
|
||||||
assert.strictEqual(stream instanceof Http2Stream, true);
|
|
||||||
}));
|
|
||||||
|
|
||||||
server.listen(0, common.mustCall(function() {
|
server.listen(0, common.mustCall(function() {
|
||||||
const port = server.address().port;
|
const url = `http://localhost:${server.address().port}`;
|
||||||
|
const client = h2.connect(url, common.mustCall(() => {
|
||||||
const url = `http://localhost:${port}`;
|
const request = client.request();
|
||||||
const client = h2.connect(url, common.mustCall(function() {
|
request.on('data', common.mustCall((chunk) => {
|
||||||
const headers = {
|
|
||||||
':path': '/foobar',
|
|
||||||
':method': 'GET',
|
|
||||||
':scheme': 'http',
|
|
||||||
':authority': `localhost:${port}`,
|
|
||||||
};
|
|
||||||
const request = client.request(headers);
|
|
||||||
request.on('data', common.mustCall(function(chunk) {
|
|
||||||
// cause an error on the server side
|
|
||||||
client.destroy();
|
client.destroy();
|
||||||
}));
|
}));
|
||||||
request.end();
|
|
||||||
}));
|
}));
|
||||||
}));
|
}));
|
||||||
|
@ -12,74 +12,47 @@ const testResBody = 'other stuff!\n';
|
|||||||
// through server receiving it, triggering 'checkContinue' custom handler,
|
// through server receiving it, triggering 'checkContinue' custom handler,
|
||||||
// writing the rest of the request to finally the client receiving to.
|
// writing the rest of the request to finally the client receiving to.
|
||||||
|
|
||||||
function handler(req, res) {
|
const server = http2.createServer(
|
||||||
console.error('Server sent full response');
|
common.mustNotCall('Full request received before 100 Continue')
|
||||||
|
);
|
||||||
|
|
||||||
res.writeHead(200, {
|
server.on('checkContinue', common.mustCall((req, res) => {
|
||||||
'content-type': 'text/plain',
|
res.writeContinue();
|
||||||
'abcd': '1'
|
res.writeHead(200, {});
|
||||||
});
|
|
||||||
res.end(testResBody);
|
res.end(testResBody);
|
||||||
// should simply return false if already too late to write
|
// should simply return false if already too late to write
|
||||||
assert.strictEqual(res.writeContinue(), false);
|
assert.strictEqual(res.writeContinue(), false);
|
||||||
res.on('finish', common.mustCall(
|
res.on('finish', common.mustCall(
|
||||||
() => process.nextTick(() => assert.strictEqual(res.writeContinue(), false))
|
() => process.nextTick(() => assert.strictEqual(res.writeContinue(), false))
|
||||||
));
|
));
|
||||||
}
|
|
||||||
|
|
||||||
const server = http2.createServer(
|
|
||||||
common.mustNotCall('Full request received before 100 Continue')
|
|
||||||
);
|
|
||||||
|
|
||||||
server.on('checkContinue', common.mustCall((req, res) => {
|
|
||||||
console.error('Server received Expect: 100-continue');
|
|
||||||
|
|
||||||
res.writeContinue();
|
|
||||||
|
|
||||||
// timeout so that we allow the client to receive continue first
|
|
||||||
setTimeout(
|
|
||||||
common.mustCall(() => handler(req, res)),
|
|
||||||
common.platformTimeout(100)
|
|
||||||
);
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
server.listen(0);
|
server.listen(0, common.mustCall(() => {
|
||||||
|
|
||||||
server.on('listening', common.mustCall(() => {
|
|
||||||
let body = '';
|
let body = '';
|
||||||
|
|
||||||
const port = server.address().port;
|
const client = http2.connect(`http://localhost:${server.address().port}`);
|
||||||
const client = http2.connect(`http://localhost:${port}`);
|
|
||||||
const req = client.request({
|
const req = client.request({
|
||||||
':method': 'POST',
|
':method': 'POST',
|
||||||
':path': '/world',
|
|
||||||
expect: '100-continue'
|
expect: '100-continue'
|
||||||
});
|
});
|
||||||
console.error('Client sent request');
|
|
||||||
|
|
||||||
let gotContinue = false;
|
let gotContinue = false;
|
||||||
req.on('continue', common.mustCall(() => {
|
req.on('continue', common.mustCall(() => {
|
||||||
console.error('Client received 100-continue');
|
|
||||||
gotContinue = true;
|
gotContinue = true;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
req.on('response', common.mustCall((headers) => {
|
req.on('response', common.mustCall((headers) => {
|
||||||
console.error('Client received response headers');
|
|
||||||
|
|
||||||
assert.strictEqual(gotContinue, true);
|
assert.strictEqual(gotContinue, true);
|
||||||
assert.strictEqual(headers[':status'], 200);
|
assert.strictEqual(headers[':status'], 200);
|
||||||
assert.strictEqual(headers['abcd'], '1');
|
req.end();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
req.setEncoding('utf-8');
|
req.setEncoding('utf-8');
|
||||||
req.on('data', common.mustCall((chunk) => { body += chunk; }));
|
req.on('data', common.mustCall((chunk) => { body += chunk; }));
|
||||||
|
|
||||||
req.on('end', common.mustCall(() => {
|
req.on('end', common.mustCall(() => {
|
||||||
console.error('Client received full response');
|
|
||||||
|
|
||||||
assert.strictEqual(body, testResBody);
|
assert.strictEqual(body, testResBody);
|
||||||
|
client.close();
|
||||||
client.destroy();
|
|
||||||
server.close();
|
server.close();
|
||||||
}));
|
}));
|
||||||
}));
|
}));
|
||||||
|
@ -17,8 +17,6 @@ const server = http2.createServer();
|
|||||||
let sentResponse = false;
|
let sentResponse = false;
|
||||||
|
|
||||||
server.on('request', common.mustCall((req, res) => {
|
server.on('request', common.mustCall((req, res) => {
|
||||||
console.error('Server sent full response');
|
|
||||||
|
|
||||||
res.end(testResBody);
|
res.end(testResBody);
|
||||||
sentResponse = true;
|
sentResponse = true;
|
||||||
}));
|
}));
|
||||||
@ -28,38 +26,29 @@ server.listen(0);
|
|||||||
server.on('listening', common.mustCall(() => {
|
server.on('listening', common.mustCall(() => {
|
||||||
let body = '';
|
let body = '';
|
||||||
|
|
||||||
const port = server.address().port;
|
const client = http2.connect(`http://localhost:${server.address().port}`);
|
||||||
const client = http2.connect(`http://localhost:${port}`);
|
|
||||||
const req = client.request({
|
const req = client.request({
|
||||||
':method': 'POST',
|
':method': 'POST',
|
||||||
':path': '/world',
|
|
||||||
expect: '100-continue'
|
expect: '100-continue'
|
||||||
});
|
});
|
||||||
console.error('Client sent request');
|
|
||||||
|
|
||||||
let gotContinue = false;
|
let gotContinue = false;
|
||||||
req.on('continue', common.mustCall(() => {
|
req.on('continue', common.mustCall(() => {
|
||||||
console.error('Client received 100-continue');
|
|
||||||
gotContinue = true;
|
gotContinue = true;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
req.on('response', common.mustCall((headers) => {
|
req.on('response', common.mustCall((headers) => {
|
||||||
console.error('Client received response headers');
|
|
||||||
|
|
||||||
assert.strictEqual(gotContinue, true);
|
assert.strictEqual(gotContinue, true);
|
||||||
assert.strictEqual(sentResponse, true);
|
assert.strictEqual(sentResponse, true);
|
||||||
assert.strictEqual(headers[':status'], 200);
|
assert.strictEqual(headers[':status'], 200);
|
||||||
|
req.end();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
req.setEncoding('utf8');
|
req.setEncoding('utf8');
|
||||||
req.on('data', common.mustCall((chunk) => { body += chunk; }));
|
req.on('data', common.mustCall((chunk) => { body += chunk; }));
|
||||||
|
|
||||||
req.on('end', common.mustCall(() => {
|
req.on('end', common.mustCall(() => {
|
||||||
console.error('Client received full response');
|
|
||||||
|
|
||||||
assert.strictEqual(body, testResBody);
|
assert.strictEqual(body, testResBody);
|
||||||
|
client.close();
|
||||||
client.destroy();
|
|
||||||
server.close();
|
server.close();
|
||||||
}));
|
}));
|
||||||
}));
|
}));
|
||||||
|
@ -39,7 +39,7 @@ function nextTest(testsToRun) {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
req.on('end', common.mustCall(() => {
|
req.on('end', common.mustCall(() => {
|
||||||
client.destroy();
|
client.close();
|
||||||
nextTest(testsToRun - 1);
|
nextTest(testsToRun - 1);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ function testMethodConnect(testsToRun) {
|
|||||||
}));
|
}));
|
||||||
req.resume();
|
req.resume();
|
||||||
req.on('end', common.mustCall(() => {
|
req.on('end', common.mustCall(() => {
|
||||||
client.destroy();
|
client.close();
|
||||||
testMethodConnect(testsToRun - 1);
|
testMethodConnect(testsToRun - 1);
|
||||||
}));
|
}));
|
||||||
req.end();
|
req.end();
|
||||||
|
@ -31,18 +31,11 @@ server.listen(0, common.mustCall(function() {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
const url = `http://localhost:${port}`;
|
const url = `http://localhost:${port}`;
|
||||||
const client = h2.connect(url, common.mustCall(function() {
|
const client = h2.connect(url, common.mustCall(() => {
|
||||||
const headers = {
|
const request = client.request();
|
||||||
':path': '/foobar',
|
|
||||||
':method': 'GET',
|
|
||||||
':scheme': 'http',
|
|
||||||
':authority': `localhost:${port}`
|
|
||||||
};
|
|
||||||
const request = client.request(headers);
|
|
||||||
request.resume();
|
request.resume();
|
||||||
request.on('end', common.mustCall(function() {
|
request.on('end', common.mustCall(() => {
|
||||||
client.destroy();
|
client.close();
|
||||||
}));
|
}));
|
||||||
request.end();
|
|
||||||
}));
|
}));
|
||||||
}));
|
}));
|
||||||
|
@ -79,7 +79,7 @@ server.listen(0, common.mustCall(function() {
|
|||||||
};
|
};
|
||||||
const request = client.request(headers);
|
const request = client.request(headers);
|
||||||
request.on('end', common.mustCall(function() {
|
request.on('end', common.mustCall(function() {
|
||||||
client.destroy();
|
client.close();
|
||||||
}));
|
}));
|
||||||
request.end();
|
request.end();
|
||||||
request.resume();
|
request.resume();
|
||||||
|
@ -46,7 +46,7 @@ server.listen(0, common.mustCall(() => {
|
|||||||
request.resume();
|
request.resume();
|
||||||
request.end(testStr);
|
request.end(testStr);
|
||||||
request.on('end', common.mustCall(function() {
|
request.on('end', common.mustCall(function() {
|
||||||
client.destroy();
|
client.close();
|
||||||
server.close();
|
server.close();
|
||||||
}));
|
}));
|
||||||
}));
|
}));
|
||||||
|
@ -35,7 +35,7 @@ server.listen(0, common.mustCall(() => {
|
|||||||
function maybeClose() {
|
function maybeClose() {
|
||||||
if (--remaining === 0) {
|
if (--remaining === 0) {
|
||||||
server.close();
|
server.close();
|
||||||
client.destroy();
|
client.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,6 @@ const server = http2.createServer();
|
|||||||
server.on('request', (req, res) => {
|
server.on('request', (req, res) => {
|
||||||
req.setTimeout(msecs, common.mustCall(() => {
|
req.setTimeout(msecs, common.mustCall(() => {
|
||||||
res.end();
|
res.end();
|
||||||
req.setTimeout(msecs, common.mustNotCall());
|
|
||||||
}));
|
}));
|
||||||
res.on('finish', common.mustCall(() => {
|
res.on('finish', common.mustCall(() => {
|
||||||
req.setTimeout(msecs, common.mustNotCall());
|
req.setTimeout(msecs, common.mustNotCall());
|
||||||
@ -35,7 +34,7 @@ server.listen(0, common.mustCall(() => {
|
|||||||
':authority': `localhost:${port}`
|
':authority': `localhost:${port}`
|
||||||
});
|
});
|
||||||
req.on('end', common.mustCall(() => {
|
req.on('end', common.mustCall(() => {
|
||||||
client.destroy();
|
client.close();
|
||||||
}));
|
}));
|
||||||
req.resume();
|
req.resume();
|
||||||
req.end();
|
req.end();
|
||||||
|
@ -62,7 +62,7 @@ server.listen(0, common.mustCall(function() {
|
|||||||
request.resume();
|
request.resume();
|
||||||
request.on('end', common.mustCall(function() {
|
request.on('end', common.mustCall(function() {
|
||||||
server.close();
|
server.close();
|
||||||
client.destroy();
|
client.close();
|
||||||
}));
|
}));
|
||||||
request.write('test\n');
|
request.write('test\n');
|
||||||
request.end('test');
|
request.end('test');
|
||||||
|
@ -46,7 +46,7 @@ server.listen(0, common.mustCall(function() {
|
|||||||
};
|
};
|
||||||
const request = client.request(headers);
|
const request = client.request(headers);
|
||||||
request.on('end', common.mustCall(function() {
|
request.on('end', common.mustCall(function() {
|
||||||
client.destroy();
|
client.close();
|
||||||
}));
|
}));
|
||||||
request.end();
|
request.end();
|
||||||
request.resume();
|
request.resume();
|
||||||
|
@ -16,26 +16,17 @@ const server = h2.createServer(common.mustCall((req, res) => {
|
|||||||
|
|
||||||
req.on('close', common.mustCall());
|
req.on('close', common.mustCall());
|
||||||
res.on('close', common.mustCall());
|
res.on('close', common.mustCall());
|
||||||
|
req.on('error', common.mustNotCall());
|
||||||
}));
|
}));
|
||||||
server.listen(0);
|
server.listen(0);
|
||||||
|
|
||||||
server.on('listening', function() {
|
server.on('listening', () => {
|
||||||
const port = server.address().port;
|
const url = `http://localhost:${server.address().port}`;
|
||||||
|
const client = h2.connect(url, common.mustCall(() => {
|
||||||
const url = `http://localhost:${port}`;
|
const request = client.request();
|
||||||
const client = h2.connect(url, common.mustCall(function() {
|
|
||||||
const headers = {
|
|
||||||
':path': '/foobar',
|
|
||||||
':method': 'GET',
|
|
||||||
':scheme': 'http',
|
|
||||||
':authority': `localhost:${port}`,
|
|
||||||
};
|
|
||||||
const request = client.request(headers);
|
|
||||||
request.on('data', common.mustCall(function(chunk) {
|
request.on('data', common.mustCall(function(chunk) {
|
||||||
// cause an error on the server side
|
|
||||||
client.destroy();
|
client.destroy();
|
||||||
server.close();
|
server.close();
|
||||||
}));
|
}));
|
||||||
request.end();
|
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
@ -43,7 +43,7 @@ const server = h2.createServer((request, response) => {
|
|||||||
':path': '/pushed',
|
':path': '/pushed',
|
||||||
':method': 'GET'
|
':method': 'GET'
|
||||||
}, common.mustCall((error) => {
|
}, common.mustCall((error) => {
|
||||||
assert.strictEqual(error.code, 'ERR_HTTP2_STREAM_CLOSED');
|
assert.strictEqual(error.code, 'ERR_HTTP2_INVALID_STREAM');
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
@ -61,7 +61,7 @@ server.listen(0, common.mustCall(() => {
|
|||||||
let remaining = 2;
|
let remaining = 2;
|
||||||
function maybeClose() {
|
function maybeClose() {
|
||||||
if (--remaining === 0) {
|
if (--remaining === 0) {
|
||||||
client.destroy();
|
client.close();
|
||||||
server.close();
|
server.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ if (!common.hasCrypto)
|
|||||||
common.skip('missing crypto');
|
common.skip('missing crypto');
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const http2 = require('http2');
|
const http2 = require('http2');
|
||||||
|
const Countdown = require('../common/countdown');
|
||||||
|
|
||||||
// Check that destroying the Http2ServerResponse stream produces
|
// Check that destroying the Http2ServerResponse stream produces
|
||||||
// the expected result, including the ability to throw an error
|
// the expected result, including the ability to throw an error
|
||||||
@ -30,63 +31,54 @@ const server = http2.createServer(common.mustCall((req, res) => {
|
|||||||
if (req.url !== '/') {
|
if (req.url !== '/') {
|
||||||
nextError = errors.shift();
|
nextError = errors.shift();
|
||||||
}
|
}
|
||||||
|
|
||||||
res.destroy(nextError);
|
res.destroy(nextError);
|
||||||
}, 3));
|
}, 3));
|
||||||
|
|
||||||
server.on(
|
|
||||||
'streamError',
|
|
||||||
common.mustCall((err) => assert.strictEqual(err, nextError), 2)
|
|
||||||
);
|
|
||||||
|
|
||||||
server.listen(0, common.mustCall(() => {
|
server.listen(0, common.mustCall(() => {
|
||||||
const port = server.address().port;
|
const client = http2.connect(`http://localhost:${server.address().port}`);
|
||||||
const client = http2.connect(`http://localhost:${port}`);
|
|
||||||
const req = client.request({
|
|
||||||
':path': '/',
|
|
||||||
':method': 'GET',
|
|
||||||
':scheme': 'http',
|
|
||||||
':authority': `localhost:${port}`
|
|
||||||
});
|
|
||||||
|
|
||||||
req.on('response', common.mustNotCall());
|
const countdown = new Countdown(3, () => {
|
||||||
req.on('error', common.mustNotCall());
|
|
||||||
req.on('end', common.mustCall());
|
|
||||||
|
|
||||||
req.resume();
|
|
||||||
req.end();
|
|
||||||
|
|
||||||
const req2 = client.request({
|
|
||||||
':path': '/error',
|
|
||||||
':method': 'GET',
|
|
||||||
':scheme': 'http',
|
|
||||||
':authority': `localhost:${port}`
|
|
||||||
});
|
|
||||||
|
|
||||||
req2.on('response', common.mustNotCall());
|
|
||||||
req2.on('error', common.mustNotCall());
|
|
||||||
req2.on('end', common.mustCall());
|
|
||||||
|
|
||||||
req2.resume();
|
|
||||||
req2.end();
|
|
||||||
|
|
||||||
const req3 = client.request({
|
|
||||||
':path': '/error',
|
|
||||||
':method': 'GET',
|
|
||||||
':scheme': 'http',
|
|
||||||
':authority': `localhost:${port}`
|
|
||||||
});
|
|
||||||
|
|
||||||
req3.on('response', common.mustNotCall());
|
|
||||||
req3.on('error', common.expectsError({
|
|
||||||
code: 'ERR_HTTP2_STREAM_ERROR',
|
|
||||||
type: Error,
|
|
||||||
message: 'Stream closed with error code 2'
|
|
||||||
}));
|
|
||||||
req3.on('end', common.mustCall(() => {
|
|
||||||
server.close();
|
server.close();
|
||||||
client.destroy();
|
client.close();
|
||||||
}));
|
});
|
||||||
|
|
||||||
req3.resume();
|
{
|
||||||
req3.end();
|
const req = client.request();
|
||||||
|
req.on('response', common.mustNotCall());
|
||||||
|
req.on('error', common.mustNotCall());
|
||||||
|
req.on('end', common.mustCall());
|
||||||
|
req.on('close', common.mustCall(() => countdown.dec()));
|
||||||
|
req.resume();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const req = client.request({ ':path': '/error' });
|
||||||
|
|
||||||
|
req.on('response', common.mustNotCall());
|
||||||
|
req.on('error', common.expectsError({
|
||||||
|
code: 'ERR_HTTP2_STREAM_ERROR',
|
||||||
|
type: Error,
|
||||||
|
message: 'Stream closed with error code 2'
|
||||||
|
}));
|
||||||
|
req.on('close', common.mustCall(() => countdown.dec()));
|
||||||
|
|
||||||
|
req.resume();
|
||||||
|
req.on('end', common.mustCall());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const req = client.request({ ':path': '/error' });
|
||||||
|
|
||||||
|
req.on('response', common.mustNotCall());
|
||||||
|
req.on('error', common.expectsError({
|
||||||
|
code: 'ERR_HTTP2_STREAM_ERROR',
|
||||||
|
type: Error,
|
||||||
|
message: 'Stream closed with error code 2'
|
||||||
|
}));
|
||||||
|
req.on('close', common.mustCall(() => countdown.dec()));
|
||||||
|
|
||||||
|
req.resume();
|
||||||
|
req.on('end', common.mustCall());
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
|
@ -37,7 +37,7 @@ server.listen(0, common.mustCall(() => {
|
|||||||
|
|
||||||
request.on('end', common.mustCall(function() {
|
request.on('end', common.mustCall(function() {
|
||||||
assert.strictEqual(data, testString.repeat(2));
|
assert.strictEqual(data, testString.repeat(2));
|
||||||
client.destroy();
|
client.close();
|
||||||
server.close();
|
server.close();
|
||||||
}));
|
}));
|
||||||
}));
|
}));
|
||||||
|
@ -52,7 +52,7 @@ const {
|
|||||||
request.on('data', (chunk) => (data += chunk));
|
request.on('data', (chunk) => (data += chunk));
|
||||||
request.on('end', mustCall(() => {
|
request.on('end', mustCall(() => {
|
||||||
strictEqual(data, 'end');
|
strictEqual(data, 'end');
|
||||||
client.destroy();
|
client.close();
|
||||||
}));
|
}));
|
||||||
request.end();
|
request.end();
|
||||||
request.resume();
|
request.resume();
|
||||||
@ -83,7 +83,7 @@ const {
|
|||||||
request.on('data', (chunk) => (data += chunk));
|
request.on('data', (chunk) => (data += chunk));
|
||||||
request.on('end', mustCall(() => {
|
request.on('end', mustCall(() => {
|
||||||
strictEqual(data, 'test\uD83D\uDE00');
|
strictEqual(data, 'test\uD83D\uDE00');
|
||||||
client.destroy();
|
client.close();
|
||||||
}));
|
}));
|
||||||
request.end();
|
request.end();
|
||||||
request.resume();
|
request.resume();
|
||||||
@ -110,7 +110,7 @@ const {
|
|||||||
};
|
};
|
||||||
const request = client.request(headers);
|
const request = client.request(headers);
|
||||||
request.on('data', mustNotCall());
|
request.on('data', mustNotCall());
|
||||||
request.on('end', mustCall(() => client.destroy()));
|
request.on('end', mustCall(() => client.close()));
|
||||||
request.end();
|
request.end();
|
||||||
request.resume();
|
request.resume();
|
||||||
}));
|
}));
|
||||||
@ -143,7 +143,7 @@ const {
|
|||||||
}));
|
}));
|
||||||
request.on('data', mustNotCall());
|
request.on('data', mustNotCall());
|
||||||
request.on('end', mustCall(() => {
|
request.on('end', mustCall(() => {
|
||||||
client.destroy();
|
client.close();
|
||||||
server.close();
|
server.close();
|
||||||
}));
|
}));
|
||||||
request.end();
|
request.end();
|
||||||
@ -172,7 +172,7 @@ const {
|
|||||||
const request = client.request(headers);
|
const request = client.request(headers);
|
||||||
request.on('data', mustNotCall());
|
request.on('data', mustNotCall());
|
||||||
request.on('end', mustCall(() => {
|
request.on('end', mustCall(() => {
|
||||||
client.destroy();
|
client.close();
|
||||||
server.close();
|
server.close();
|
||||||
}));
|
}));
|
||||||
request.end();
|
request.end();
|
||||||
@ -208,7 +208,7 @@ const {
|
|||||||
}));
|
}));
|
||||||
request.on('data', mustNotCall());
|
request.on('data', mustNotCall());
|
||||||
request.on('end', mustCall(() => {
|
request.on('end', mustCall(() => {
|
||||||
client.destroy();
|
client.close();
|
||||||
server.close();
|
server.close();
|
||||||
}));
|
}));
|
||||||
request.end();
|
request.end();
|
||||||
@ -243,7 +243,7 @@ const {
|
|||||||
}));
|
}));
|
||||||
request.on('data', mustNotCall());
|
request.on('data', mustNotCall());
|
||||||
request.on('end', mustCall(() => {
|
request.on('end', mustCall(() => {
|
||||||
client.destroy();
|
client.close();
|
||||||
server.close();
|
server.close();
|
||||||
}));
|
}));
|
||||||
request.end();
|
request.end();
|
||||||
@ -283,7 +283,7 @@ const {
|
|||||||
}));
|
}));
|
||||||
request.on('data', mustNotCall());
|
request.on('data', mustNotCall());
|
||||||
request.on('end', mustCall(() => {
|
request.on('end', mustCall(() => {
|
||||||
client.destroy();
|
client.close();
|
||||||
server.close();
|
server.close();
|
||||||
}));
|
}));
|
||||||
request.end();
|
request.end();
|
||||||
@ -315,7 +315,7 @@ const {
|
|||||||
}));
|
}));
|
||||||
request.on('data', mustNotCall());
|
request.on('data', mustNotCall());
|
||||||
request.on('end', mustCall(() => {
|
request.on('end', mustCall(() => {
|
||||||
client.destroy();
|
client.close();
|
||||||
server.close();
|
server.close();
|
||||||
}));
|
}));
|
||||||
request.end();
|
request.end();
|
||||||
|
@ -39,7 +39,7 @@ server.listen(0, common.mustCall(function() {
|
|||||||
};
|
};
|
||||||
const request = client.request(headers);
|
const request = client.request(headers);
|
||||||
request.on('end', common.mustCall(function() {
|
request.on('end', common.mustCall(function() {
|
||||||
client.destroy();
|
client.close();
|
||||||
}));
|
}));
|
||||||
request.end();
|
request.end();
|
||||||
request.resume();
|
request.resume();
|
||||||
|
@ -51,7 +51,7 @@ server.listen(0, common.mustCall(function() {
|
|||||||
serverResponse.end();
|
serverResponse.end();
|
||||||
}, 1));
|
}, 1));
|
||||||
request.on('end', common.mustCall(function() {
|
request.on('end', common.mustCall(function() {
|
||||||
client.destroy();
|
client.close();
|
||||||
}));
|
}));
|
||||||
request.end();
|
request.end();
|
||||||
request.resume();
|
request.resume();
|
||||||
|
@ -13,8 +13,6 @@ const server = h2.createServer();
|
|||||||
server.listen(0, common.mustCall(function() {
|
server.listen(0, common.mustCall(function() {
|
||||||
const port = server.address().port;
|
const port = server.address().port;
|
||||||
server.once('request', common.mustCall(function(request, response) {
|
server.once('request', common.mustCall(function(request, response) {
|
||||||
response.destroy();
|
|
||||||
|
|
||||||
response.on('finish', common.mustCall(() => {
|
response.on('finish', common.mustCall(() => {
|
||||||
assert.strictEqual(response.headersSent, false);
|
assert.strictEqual(response.headersSent, false);
|
||||||
assert.doesNotThrow(() => response.setHeader('test', 'value'));
|
assert.doesNotThrow(() => response.setHeader('test', 'value'));
|
||||||
@ -27,6 +25,8 @@ server.listen(0, common.mustCall(function() {
|
|||||||
server.close();
|
server.close();
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
response.destroy();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const url = `http://localhost:${port}`;
|
const url = `http://localhost:${port}`;
|
||||||
@ -39,7 +39,7 @@ server.listen(0, common.mustCall(function() {
|
|||||||
};
|
};
|
||||||
const request = client.request(headers);
|
const request = client.request(headers);
|
||||||
request.on('end', common.mustCall(function() {
|
request.on('end', common.mustCall(function() {
|
||||||
client.destroy();
|
client.close();
|
||||||
}));
|
}));
|
||||||
request.end();
|
request.end();
|
||||||
request.resume();
|
request.resume();
|
||||||
|
@ -179,7 +179,7 @@ server.listen(0, common.mustCall(function() {
|
|||||||
};
|
};
|
||||||
const request = client.request(headers);
|
const request = client.request(headers);
|
||||||
request.on('end', common.mustCall(function() {
|
request.on('end', common.mustCall(function() {
|
||||||
client.destroy();
|
client.close();
|
||||||
}));
|
}));
|
||||||
request.end();
|
request.end();
|
||||||
request.resume();
|
request.resume();
|
||||||
|
@ -12,7 +12,6 @@ const server = http2.createServer();
|
|||||||
server.on('request', (req, res) => {
|
server.on('request', (req, res) => {
|
||||||
res.setTimeout(msecs, common.mustCall(() => {
|
res.setTimeout(msecs, common.mustCall(() => {
|
||||||
res.end();
|
res.end();
|
||||||
res.setTimeout(msecs, common.mustNotCall());
|
|
||||||
}));
|
}));
|
||||||
res.on('finish', common.mustCall(() => {
|
res.on('finish', common.mustCall(() => {
|
||||||
res.setTimeout(msecs, common.mustNotCall());
|
res.setTimeout(msecs, common.mustNotCall());
|
||||||
@ -35,7 +34,7 @@ server.listen(0, common.mustCall(() => {
|
|||||||
':authority': `localhost:${port}`
|
':authority': `localhost:${port}`
|
||||||
});
|
});
|
||||||
req.on('end', common.mustCall(() => {
|
req.on('end', common.mustCall(() => {
|
||||||
client.destroy();
|
client.close();
|
||||||
}));
|
}));
|
||||||
req.resume();
|
req.resume();
|
||||||
req.end();
|
req.end();
|
||||||
|
@ -69,7 +69,7 @@ server.listen(0, common.mustCall(function() {
|
|||||||
};
|
};
|
||||||
const request = client.request(headers);
|
const request = client.request(headers);
|
||||||
request.on('end', common.mustCall(function() {
|
request.on('end', common.mustCall(function() {
|
||||||
client.destroy();
|
client.close();
|
||||||
}));
|
}));
|
||||||
request.end();
|
request.end();
|
||||||
request.resume();
|
request.resume();
|
||||||
|
@ -42,7 +42,7 @@ server.listen(0, common.mustCall(function() {
|
|||||||
assert.strictEqual(headers[':status'], 200);
|
assert.strictEqual(headers[':status'], 200);
|
||||||
}, 1));
|
}, 1));
|
||||||
request.on('end', common.mustCall(function() {
|
request.on('end', common.mustCall(function() {
|
||||||
client.destroy();
|
client.close();
|
||||||
}));
|
}));
|
||||||
request.end();
|
request.end();
|
||||||
request.resume();
|
request.resume();
|
||||||
|
@ -41,7 +41,7 @@ server.listen(0, common.mustCall(function() {
|
|||||||
assert.strictEqual(headers[':status'], 200);
|
assert.strictEqual(headers[':status'], 200);
|
||||||
}, 1));
|
}, 1));
|
||||||
request.on('end', common.mustCall(function() {
|
request.on('end', common.mustCall(function() {
|
||||||
client.destroy();
|
client.close();
|
||||||
}));
|
}));
|
||||||
request.end();
|
request.end();
|
||||||
request.resume();
|
request.resume();
|
||||||
|
@ -45,7 +45,7 @@ server.listen(0, common.mustCall(function() {
|
|||||||
assert.strictEqual(headers['foo-bar'], 'abc123');
|
assert.strictEqual(headers['foo-bar'], 'abc123');
|
||||||
}, 1));
|
}, 1));
|
||||||
request.on('end', common.mustCall(function() {
|
request.on('end', common.mustCall(function() {
|
||||||
client.destroy();
|
client.close();
|
||||||
}));
|
}));
|
||||||
request.end();
|
request.end();
|
||||||
request.resume();
|
request.resume();
|
||||||
|
@ -68,7 +68,7 @@ server.listen(0, common.mustCall(() => {
|
|||||||
}));
|
}));
|
||||||
request.resume();
|
request.resume();
|
||||||
request.on('end', common.mustCall(() => {
|
request.on('end', common.mustCall(() => {
|
||||||
client.destroy();
|
client.close();
|
||||||
server.close();
|
server.close();
|
||||||
}));
|
}));
|
||||||
}));
|
}));
|
||||||
|
@ -6,44 +6,33 @@ const { mustCall,
|
|||||||
hasCrypto, skip } = require('../common');
|
hasCrypto, skip } = require('../common');
|
||||||
if (!hasCrypto)
|
if (!hasCrypto)
|
||||||
skip('missing crypto');
|
skip('missing crypto');
|
||||||
const { throws } = require('assert');
|
|
||||||
const { createServer, connect } = require('http2');
|
const { createServer, connect } = require('http2');
|
||||||
|
|
||||||
// Http2ServerResponse.write does not imply there is a callback
|
// Http2ServerResponse.write does not imply there is a callback
|
||||||
|
|
||||||
const expectedError = expectsError({
|
|
||||||
code: 'ERR_HTTP2_STREAM_CLOSED',
|
|
||||||
message: 'The stream is already closed'
|
|
||||||
}, 2);
|
|
||||||
|
|
||||||
{
|
{
|
||||||
const server = createServer();
|
const server = createServer();
|
||||||
server.listen(0, mustCall(() => {
|
server.listen(0, mustCall(() => {
|
||||||
const port = server.address().port;
|
const port = server.address().port;
|
||||||
const url = `http://localhost:${port}`;
|
const url = `http://localhost:${port}`;
|
||||||
const client = connect(url, mustCall(() => {
|
const client = connect(url, mustCall(() => {
|
||||||
const headers = {
|
const request = client.request();
|
||||||
':path': '/',
|
|
||||||
':method': 'GET',
|
|
||||||
':scheme': 'http',
|
|
||||||
':authority': `localhost:${port}`
|
|
||||||
};
|
|
||||||
const request = client.request(headers);
|
|
||||||
request.end();
|
|
||||||
request.resume();
|
request.resume();
|
||||||
|
request.on('end', mustCall());
|
||||||
|
request.on('close', mustCall(() => {
|
||||||
|
client.close();
|
||||||
|
}));
|
||||||
}));
|
}));
|
||||||
|
|
||||||
server.once('request', mustCall((request, response) => {
|
server.once('request', mustCall((request, response) => {
|
||||||
client.destroy();
|
client.destroy();
|
||||||
response.stream.session.on('close', mustCall(() => {
|
response.stream.session.on('close', mustCall(() => {
|
||||||
response.on('error', mustNotCall());
|
response.on('error', mustNotCall());
|
||||||
throws(
|
expectsError(
|
||||||
() => { response.write('muahaha'); },
|
() => { response.write('muahaha'); },
|
||||||
expectsError({
|
{
|
||||||
code: 'ERR_HTTP2_STREAM_CLOSED',
|
code: 'ERR_HTTP2_INVALID_STREAM'
|
||||||
type: Error,
|
}
|
||||||
message: 'The stream is already closed'
|
|
||||||
})
|
|
||||||
);
|
);
|
||||||
server.close();
|
server.close();
|
||||||
}));
|
}));
|
||||||
@ -57,21 +46,21 @@ const expectedError = expectsError({
|
|||||||
const port = server.address().port;
|
const port = server.address().port;
|
||||||
const url = `http://localhost:${port}`;
|
const url = `http://localhost:${port}`;
|
||||||
const client = connect(url, mustCall(() => {
|
const client = connect(url, mustCall(() => {
|
||||||
const headers = {
|
const request = client.request();
|
||||||
':path': '/',
|
|
||||||
':method': 'get',
|
|
||||||
':scheme': 'http',
|
|
||||||
':authority': `localhost:${port}`
|
|
||||||
};
|
|
||||||
const request = client.request(headers);
|
|
||||||
request.end();
|
|
||||||
request.resume();
|
request.resume();
|
||||||
|
request.on('end', mustCall());
|
||||||
|
request.on('close', mustCall(() => client.close()));
|
||||||
}));
|
}));
|
||||||
|
|
||||||
server.once('request', mustCall((request, response) => {
|
server.once('request', mustCall((request, response) => {
|
||||||
client.destroy();
|
client.destroy();
|
||||||
response.stream.session.on('close', mustCall(() => {
|
response.stream.session.on('close', mustCall(() => {
|
||||||
response.write('muahaha', mustCall(expectedError));
|
expectsError(
|
||||||
|
() => response.write('muahaha'),
|
||||||
|
{
|
||||||
|
code: 'ERR_HTTP2_INVALID_STREAM'
|
||||||
|
}
|
||||||
|
);
|
||||||
server.close();
|
server.close();
|
||||||
}));
|
}));
|
||||||
}));
|
}));
|
||||||
@ -84,20 +73,20 @@ const expectedError = expectsError({
|
|||||||
const port = server.address().port;
|
const port = server.address().port;
|
||||||
const url = `http://localhost:${port}`;
|
const url = `http://localhost:${port}`;
|
||||||
const client = connect(url, mustCall(() => {
|
const client = connect(url, mustCall(() => {
|
||||||
const headers = {
|
const request = client.request();
|
||||||
':path': '/',
|
|
||||||
':method': 'get',
|
|
||||||
':scheme': 'http',
|
|
||||||
':authority': `localhost:${port}`
|
|
||||||
};
|
|
||||||
const request = client.request(headers);
|
|
||||||
request.end();
|
|
||||||
request.resume();
|
request.resume();
|
||||||
|
request.on('end', mustCall());
|
||||||
|
request.on('close', mustCall(() => client.close()));
|
||||||
}));
|
}));
|
||||||
|
|
||||||
server.once('request', mustCall((request, response) => {
|
server.once('request', mustCall((request, response) => {
|
||||||
response.stream.session.on('close', mustCall(() => {
|
response.stream.session.on('close', mustCall(() => {
|
||||||
response.write('muahaha', 'utf8', mustCall(expectedError));
|
expectsError(
|
||||||
|
() => response.write('muahaha', 'utf8'),
|
||||||
|
{
|
||||||
|
code: 'ERR_HTTP2_INVALID_STREAM'
|
||||||
|
}
|
||||||
|
);
|
||||||
server.close();
|
server.close();
|
||||||
}));
|
}));
|
||||||
client.destroy();
|
client.destroy();
|
||||||
|
@ -23,7 +23,7 @@ server.listen(0, common.mustCall(function() {
|
|||||||
server.close();
|
server.close();
|
||||||
process.nextTick(common.mustCall(() => {
|
process.nextTick(common.mustCall(() => {
|
||||||
common.expectsError(() => { response.writeHead(300); }, {
|
common.expectsError(() => { response.writeHead(300); }, {
|
||||||
code: 'ERR_HTTP2_STREAM_CLOSED'
|
code: 'ERR_HTTP2_INVALID_STREAM'
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
}));
|
}));
|
||||||
@ -44,7 +44,7 @@ server.listen(0, common.mustCall(function() {
|
|||||||
assert.strictEqual(headers[':status'], 418);
|
assert.strictEqual(headers[':status'], 418);
|
||||||
}, 1));
|
}, 1));
|
||||||
request.on('end', common.mustCall(function() {
|
request.on('end', common.mustCall(function() {
|
||||||
client.destroy();
|
client.close();
|
||||||
}));
|
}));
|
||||||
request.end();
|
request.end();
|
||||||
request.resume();
|
request.resume();
|
||||||
|
@ -98,7 +98,7 @@ server.listen(0, common.mustCall(function() {
|
|||||||
};
|
};
|
||||||
const request = client.request(headers);
|
const request = client.request(headers);
|
||||||
request.on('end', common.mustCall(() => {
|
request.on('end', common.mustCall(() => {
|
||||||
client.destroy();
|
client.close();
|
||||||
server.close();
|
server.close();
|
||||||
}));
|
}));
|
||||||
request.end();
|
request.end();
|
||||||
|
@ -81,7 +81,7 @@ server.listen(0, common.mustCall(function() {
|
|||||||
};
|
};
|
||||||
const request = client.request(headers);
|
const request = client.request(headers);
|
||||||
request.on('end', common.mustCall(() => {
|
request.on('end', common.mustCall(() => {
|
||||||
client.destroy();
|
client.close();
|
||||||
}));
|
}));
|
||||||
request.end();
|
request.end();
|
||||||
request.resume();
|
request.resume();
|
||||||
|
@ -13,7 +13,8 @@ const {
|
|||||||
HTTP2_HEADER_AUTHORITY,
|
HTTP2_HEADER_AUTHORITY,
|
||||||
HTTP2_HEADER_SCHEME,
|
HTTP2_HEADER_SCHEME,
|
||||||
HTTP2_HEADER_PATH,
|
HTTP2_HEADER_PATH,
|
||||||
NGHTTP2_CONNECT_ERROR
|
NGHTTP2_CONNECT_ERROR,
|
||||||
|
NGHTTP2_REFUSED_STREAM
|
||||||
} = http2.constants;
|
} = http2.constants;
|
||||||
|
|
||||||
const server = net.createServer(common.mustCall((socket) => {
|
const server = net.createServer(common.mustCall((socket) => {
|
||||||
@ -34,7 +35,7 @@ server.listen(0, common.mustCall(() => {
|
|||||||
const proxy = http2.createServer();
|
const proxy = http2.createServer();
|
||||||
proxy.on('stream', common.mustCall((stream, headers) => {
|
proxy.on('stream', common.mustCall((stream, headers) => {
|
||||||
if (headers[HTTP2_HEADER_METHOD] !== 'CONNECT') {
|
if (headers[HTTP2_HEADER_METHOD] !== 'CONNECT') {
|
||||||
stream.rstWithRefused();
|
stream.close(NGHTTP2_REFUSED_STREAM);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auth = new URL(`tcp://${headers[HTTP2_HEADER_AUTHORITY]}`);
|
const auth = new URL(`tcp://${headers[HTTP2_HEADER_AUTHORITY]}`);
|
||||||
@ -47,7 +48,7 @@ server.listen(0, common.mustCall(() => {
|
|||||||
});
|
});
|
||||||
socket.on('close', common.mustCall());
|
socket.on('close', common.mustCall());
|
||||||
socket.on('error', (error) => {
|
socket.on('error', (error) => {
|
||||||
stream.rstStream(NGHTTP2_CONNECT_ERROR);
|
stream.close(NGHTTP2_CONNECT_ERROR);
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -99,7 +100,7 @@ server.listen(0, common.mustCall(() => {
|
|||||||
req.on('data', (chunk) => data += chunk);
|
req.on('data', (chunk) => data += chunk);
|
||||||
req.on('end', common.mustCall(() => {
|
req.on('end', common.mustCall(() => {
|
||||||
assert.strictEqual(data, 'hello');
|
assert.strictEqual(data, 'hello');
|
||||||
client.destroy();
|
client.close();
|
||||||
proxy.close();
|
proxy.close();
|
||||||
server.close();
|
server.close();
|
||||||
}));
|
}));
|
||||||
|
@ -20,7 +20,7 @@ const { createServer, connect } = require('http2');
|
|||||||
|
|
||||||
for (const client of clients) {
|
for (const client of clients) {
|
||||||
client.once('connect', mustCall((headers) => {
|
client.once('connect', mustCall((headers) => {
|
||||||
client.destroy();
|
client.close();
|
||||||
clients.delete(client);
|
clients.delete(client);
|
||||||
if (clients.size === 0) {
|
if (clients.size === 0) {
|
||||||
server.close();
|
server.close();
|
||||||
@ -33,7 +33,11 @@ const { createServer, connect } = require('http2');
|
|||||||
// check for https as protocol
|
// check for https as protocol
|
||||||
{
|
{
|
||||||
const authority = 'https://localhost';
|
const authority = 'https://localhost';
|
||||||
doesNotThrow(() => connect(authority));
|
doesNotThrow(() => {
|
||||||
|
// A socket error may or may not be reported, keep this as a non-op
|
||||||
|
// instead of a mustCall or mustNotCall
|
||||||
|
connect(authority).on('error', () => {});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for error for an invalid protocol (not http or https)
|
// check for error for an invalid protocol (not http or https)
|
||||||
|
@ -54,7 +54,7 @@ server.on('listening', common.mustCall(() => {
|
|||||||
|
|
||||||
req.on('end', common.mustCall(() => {
|
req.on('end', common.mustCall(() => {
|
||||||
server.close();
|
server.close();
|
||||||
client.destroy();
|
client.close();
|
||||||
}));
|
}));
|
||||||
req.end();
|
req.end();
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ const URL = url.URL;
|
|||||||
() => setImmediate(() => server.close()));
|
() => setImmediate(() => server.close()));
|
||||||
|
|
||||||
const maybeClose = common.mustCall((client) => {
|
const maybeClose = common.mustCall((client) => {
|
||||||
client.destroy();
|
client.close();
|
||||||
serverClose.dec();
|
serverClose.dec();
|
||||||
}, items.length);
|
}, items.length);
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ const URL = url.URL;
|
|||||||
|
|
||||||
// Will fail because protocol does not match the server.
|
// Will fail because protocol does not match the server.
|
||||||
h2.connect({ port: port, protocol: 'https:' })
|
h2.connect({ port: port, protocol: 'https:' })
|
||||||
.on('socketError', common.mustCall(() => serverClose.dec()));
|
.on('error', common.mustCall(() => serverClose.dec()));
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,10 +55,8 @@ const URL = url.URL;
|
|||||||
};
|
};
|
||||||
|
|
||||||
const server = h2.createSecureServer(options);
|
const server = h2.createSecureServer(options);
|
||||||
server.listen(0);
|
server.listen(0, common.mustCall(() => {
|
||||||
|
const port = server.address().port;
|
||||||
server.on('listening', common.mustCall(function() {
|
|
||||||
const port = this.address().port;
|
|
||||||
|
|
||||||
const opts = { rejectUnauthorized: false };
|
const opts = { rejectUnauthorized: false };
|
||||||
|
|
||||||
@ -74,7 +72,7 @@ const URL = url.URL;
|
|||||||
() => setImmediate(() => server.close()));
|
() => setImmediate(() => server.close()));
|
||||||
|
|
||||||
const maybeClose = common.mustCall((client) => {
|
const maybeClose = common.mustCall((client) => {
|
||||||
client.destroy();
|
client.close();
|
||||||
serverClose.dec();
|
serverClose.dec();
|
||||||
}, items.length);
|
}, items.length);
|
||||||
|
|
||||||
|
@ -5,6 +5,8 @@ if (!common.hasCrypto)
|
|||||||
common.skip('missing crypto');
|
common.skip('missing crypto');
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const h2 = require('http2');
|
const h2 = require('http2');
|
||||||
|
const Countdown = require('../common/countdown');
|
||||||
|
|
||||||
const body =
|
const body =
|
||||||
'<html><head></head><body><h1>this is some data</h2></body></html>';
|
'<html><head></head><body><h1>this is some data</h2></body></html>';
|
||||||
|
|
||||||
@ -23,21 +25,26 @@ function onStream(stream, headers, flags) {
|
|||||||
'content-type': 'text/html',
|
'content-type': 'text/html',
|
||||||
':status': 200
|
':status': 200
|
||||||
});
|
});
|
||||||
stream.end(body);
|
stream.write(body.slice(0, 20));
|
||||||
|
stream.end(body.slice(20));
|
||||||
}
|
}
|
||||||
|
|
||||||
server.listen(0);
|
server.listen(0);
|
||||||
|
|
||||||
let expected = count;
|
server.on('listening', common.mustCall(() => {
|
||||||
|
|
||||||
server.on('listening', common.mustCall(function() {
|
const client = h2.connect(`http://localhost:${server.address().port}`);
|
||||||
|
client.setMaxListeners(100);
|
||||||
|
|
||||||
const client = h2.connect(`http://localhost:${this.address().port}`);
|
client.on('goaway', console.log);
|
||||||
|
|
||||||
const headers = { ':path': '/' };
|
const countdown = new Countdown(count, () => {
|
||||||
|
client.close();
|
||||||
|
server.close();
|
||||||
|
});
|
||||||
|
|
||||||
for (let n = 0; n < count; n++) {
|
for (let n = 0; n < count; n++) {
|
||||||
const req = client.request(headers);
|
const req = client.request();
|
||||||
|
|
||||||
req.on('response', common.mustCall(function(headers) {
|
req.on('response', common.mustCall(function(headers) {
|
||||||
assert.strictEqual(headers[':status'], 200, 'status code is set');
|
assert.strictEqual(headers[':status'], 200, 'status code is set');
|
||||||
@ -51,12 +58,7 @@ server.on('listening', common.mustCall(function() {
|
|||||||
req.on('data', (d) => data += d);
|
req.on('data', (d) => data += d);
|
||||||
req.on('end', common.mustCall(() => {
|
req.on('end', common.mustCall(() => {
|
||||||
assert.strictEqual(body, data);
|
assert.strictEqual(body, data);
|
||||||
if (--expected === 0) {
|
|
||||||
server.close();
|
|
||||||
client.destroy();
|
|
||||||
}
|
|
||||||
}));
|
}));
|
||||||
req.end();
|
req.on('close', common.mustCall(() => countdown.dec()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}));
|
}));
|
||||||
|
@ -6,7 +6,7 @@ if (!common.hasCrypto)
|
|||||||
|
|
||||||
const http2 = require('http2');
|
const http2 = require('http2');
|
||||||
|
|
||||||
const invalidOptions = [() => {}, 1, 'test', null, undefined];
|
const invalidOptions = [() => {}, 1, 'test', null];
|
||||||
const invalidArgTypeError = {
|
const invalidArgTypeError = {
|
||||||
type: TypeError,
|
type: TypeError,
|
||||||
code: 'ERR_INVALID_ARG_TYPE',
|
code: 'ERR_INVALID_ARG_TYPE',
|
||||||
@ -14,9 +14,9 @@ const invalidArgTypeError = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Error if options are not passed to createSecureServer
|
// Error if options are not passed to createSecureServer
|
||||||
invalidOptions.forEach((invalidOption) =>
|
invalidOptions.forEach((invalidOption) => {
|
||||||
common.expectsError(
|
common.expectsError(
|
||||||
() => http2.createSecureServer(invalidOption),
|
() => http2.createSecureServer(invalidOption),
|
||||||
invalidArgTypeError
|
invalidArgTypeError
|
||||||
)
|
);
|
||||||
);
|
});
|
||||||
|
@ -54,7 +54,7 @@ server.listen(0, common.mustCall(function() {
|
|||||||
req.resume();
|
req.resume();
|
||||||
|
|
||||||
req.on('end', common.mustCall(function() {
|
req.on('end', common.mustCall(function() {
|
||||||
client.destroy();
|
client.close();
|
||||||
testsFinished++;
|
testsFinished++;
|
||||||
|
|
||||||
if (testsFinished === testsToRun) {
|
if (testsFinished === testsToRun) {
|
||||||
|
@ -24,6 +24,6 @@ server.listen(0, common.mustCall(() => {
|
|||||||
req.resume();
|
req.resume();
|
||||||
req.on('end', common.mustCall(() => {
|
req.on('end', common.mustCall(() => {
|
||||||
server.close();
|
server.close();
|
||||||
client.destroy();
|
client.close();
|
||||||
}));
|
}));
|
||||||
}));
|
}));
|
||||||
|
58
test/parallel/test-http2-dont-lose-data.js
Normal file
58
test/parallel/test-http2-dont-lose-data.js
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const common = require('../common');
|
||||||
|
if (!common.hasCrypto)
|
||||||
|
common.skip('missing crypto');
|
||||||
|
const assert = require('assert');
|
||||||
|
const http2 = require('http2');
|
||||||
|
|
||||||
|
const server = http2.createServer();
|
||||||
|
|
||||||
|
server.on('stream', (s) => {
|
||||||
|
assert(s.pushAllowed);
|
||||||
|
|
||||||
|
s.pushStream({ ':path': '/file' }, common.mustCall((err, pushStream) => {
|
||||||
|
assert.ifError(err);
|
||||||
|
pushStream.respond();
|
||||||
|
pushStream.end('a push stream');
|
||||||
|
}));
|
||||||
|
|
||||||
|
s.respond();
|
||||||
|
s.end('hello world');
|
||||||
|
});
|
||||||
|
|
||||||
|
server.listen(0, () => {
|
||||||
|
server.unref();
|
||||||
|
|
||||||
|
const url = `http://localhost:${server.address().port}`;
|
||||||
|
|
||||||
|
const client = http2.connect(url);
|
||||||
|
const req = client.request();
|
||||||
|
|
||||||
|
let pushStream;
|
||||||
|
|
||||||
|
client.on('stream', common.mustCall((s, headers) => {
|
||||||
|
assert.strictEqual(headers[':path'], '/file');
|
||||||
|
pushStream = s;
|
||||||
|
}));
|
||||||
|
|
||||||
|
req.on('response', common.mustCall((headers) => {
|
||||||
|
let pushData = '';
|
||||||
|
pushStream.setEncoding('utf8');
|
||||||
|
pushStream.on('data', (d) => pushData += d);
|
||||||
|
pushStream.on('end', common.mustCall(() => {
|
||||||
|
assert.strictEqual(pushData, 'a push stream');
|
||||||
|
|
||||||
|
// removing the setImmediate causes the test to pass
|
||||||
|
setImmediate(function() {
|
||||||
|
let data = '';
|
||||||
|
req.setEncoding('utf8');
|
||||||
|
req.on('data', (d) => data += d);
|
||||||
|
req.on('end', common.mustCall(() => {
|
||||||
|
assert.strictEqual(data, 'hello world');
|
||||||
|
client.close();
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
}));
|
||||||
|
});
|
@ -44,6 +44,6 @@ server.listen(0, common.mustCall(() => {
|
|||||||
req.resume();
|
req.resume();
|
||||||
req.on('end', common.mustCall(() => {
|
req.on('end', common.mustCall(() => {
|
||||||
server.close();
|
server.close();
|
||||||
client.destroy();
|
client.close();
|
||||||
}));
|
}));
|
||||||
}));
|
}));
|
||||||
|
@ -20,7 +20,7 @@ const makeDuplexPair = require('../common/duplexpair');
|
|||||||
createConnection: common.mustCall(() => clientSide)
|
createConnection: common.mustCall(() => clientSide)
|
||||||
});
|
});
|
||||||
|
|
||||||
const req = client.request({ ':path': '/' });
|
const req = client.request();
|
||||||
|
|
||||||
req.on('response', common.mustCall((headers) => {
|
req.on('response', common.mustCall((headers) => {
|
||||||
assert.strictEqual(headers[':status'], 200);
|
assert.strictEqual(headers[':status'], 200);
|
||||||
@ -28,9 +28,7 @@ const makeDuplexPair = require('../common/duplexpair');
|
|||||||
|
|
||||||
req.setEncoding('utf8');
|
req.setEncoding('utf8');
|
||||||
let data = '';
|
let data = '';
|
||||||
req.on('data', (chunk) => {
|
req.on('data', (chunk) => data += chunk);
|
||||||
data += chunk;
|
|
||||||
});
|
|
||||||
req.on('end', common.mustCall(() => {
|
req.on('end', common.mustCall(() => {
|
||||||
assert.strictEqual(data, fs.readFileSync(__filename, 'utf8'));
|
assert.strictEqual(data, fs.readFileSync(__filename, 'utf8'));
|
||||||
clientSide.destroy();
|
clientSide.destroy();
|
||||||
|
@ -10,32 +10,23 @@ const server = http2.createServer();
|
|||||||
const data = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5]);
|
const data = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5]);
|
||||||
|
|
||||||
server.on('stream', common.mustCall((stream) => {
|
server.on('stream', common.mustCall((stream) => {
|
||||||
stream.session.shutdown({
|
stream.session.goaway(0, 0, data);
|
||||||
errorCode: 1,
|
stream.respond();
|
||||||
opaqueData: data
|
|
||||||
});
|
|
||||||
stream.end();
|
stream.end();
|
||||||
stream.on('error', common.mustCall(common.expectsError({
|
|
||||||
code: 'ERR_HTTP2_STREAM_ERROR',
|
|
||||||
type: Error,
|
|
||||||
message: 'Stream closed with error code 7'
|
|
||||||
})));
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
server.listen(0, () => {
|
server.listen(0, () => {
|
||||||
|
|
||||||
const client = http2.connect(`http://localhost:${server.address().port}`);
|
const client = http2.connect(`http://localhost:${server.address().port}`);
|
||||||
client.on('goaway', common.mustCall((code, lastStreamID, buf) => {
|
client.once('goaway', common.mustCall((code, lastStreamID, buf) => {
|
||||||
assert.deepStrictEqual(code, 1);
|
assert.deepStrictEqual(code, 0);
|
||||||
assert.deepStrictEqual(lastStreamID, 0);
|
assert.deepStrictEqual(lastStreamID, 1);
|
||||||
assert.deepStrictEqual(data, buf);
|
assert.deepStrictEqual(data, buf);
|
||||||
// Call shutdown() here so that emitGoaway calls destroy()
|
|
||||||
client.shutdown();
|
|
||||||
server.close();
|
server.close();
|
||||||
}));
|
}));
|
||||||
const req = client.request({ ':path': '/' });
|
const req = client.request();
|
||||||
req.resume();
|
req.resume();
|
||||||
req.on('end', common.mustCall());
|
req.on('end', common.mustCall());
|
||||||
|
req.on('close', common.mustCall());
|
||||||
req.end();
|
req.end();
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -54,6 +54,6 @@ server.listen(0, () => {
|
|||||||
req.on('data', common.mustNotCall());
|
req.on('data', common.mustNotCall());
|
||||||
req.on('end', common.mustCall(() => {
|
req.on('end', common.mustCall(() => {
|
||||||
server.close();
|
server.close();
|
||||||
client.destroy();
|
client.close();
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
@ -52,7 +52,7 @@ function onSession(session) {
|
|||||||
strictEqual(alpnProtocol, 'h2');
|
strictEqual(alpnProtocol, 'h2');
|
||||||
strictEqual(httpVersion, '2.0');
|
strictEqual(httpVersion, '2.0');
|
||||||
|
|
||||||
session.destroy();
|
session.close();
|
||||||
this.cleanup();
|
this.cleanup();
|
||||||
}));
|
}));
|
||||||
request.end();
|
request.end();
|
||||||
|
@ -48,10 +48,6 @@ server.on('stream', common.mustCall((stream, headers) => {
|
|||||||
if (currentError.type === 'stream') {
|
if (currentError.type === 'stream') {
|
||||||
stream.session.on('error', errorMustNotCall);
|
stream.session.on('error', errorMustNotCall);
|
||||||
stream.on('error', errorMustCall);
|
stream.on('error', errorMustCall);
|
||||||
stream.on('error', common.mustCall(() => {
|
|
||||||
stream.respond();
|
|
||||||
stream.end();
|
|
||||||
}));
|
|
||||||
} else {
|
} else {
|
||||||
stream.session.once('error', errorMustCall);
|
stream.session.once('error', errorMustCall);
|
||||||
stream.on('error', errorMustNotCall);
|
stream.on('error', errorMustNotCall);
|
||||||
@ -63,24 +59,21 @@ server.on('stream', common.mustCall((stream, headers) => {
|
|||||||
server.listen(0, common.mustCall(() => runTest(tests.shift())));
|
server.listen(0, common.mustCall(() => runTest(tests.shift())));
|
||||||
|
|
||||||
function runTest(test) {
|
function runTest(test) {
|
||||||
const port = server.address().port;
|
const client = http2.connect(`http://localhost:${server.address().port}`);
|
||||||
const url = `http://localhost:${port}`;
|
const req = client.request({ ':method': 'POST' });
|
||||||
const headers = {
|
|
||||||
':path': '/',
|
|
||||||
':method': 'POST',
|
|
||||||
':scheme': 'http',
|
|
||||||
':authority': `localhost:${port}`
|
|
||||||
};
|
|
||||||
|
|
||||||
const client = http2.connect(url);
|
|
||||||
const req = client.request(headers);
|
|
||||||
|
|
||||||
currentError = test;
|
currentError = test;
|
||||||
req.resume();
|
req.resume();
|
||||||
req.end();
|
req.end();
|
||||||
|
|
||||||
req.on('end', common.mustCall(() => {
|
req.on('error', common.expectsError({
|
||||||
client.destroy();
|
code: 'ERR_HTTP2_STREAM_ERROR',
|
||||||
|
type: Error,
|
||||||
|
message: 'Stream closed with error code 2'
|
||||||
|
}));
|
||||||
|
|
||||||
|
req.on('close', common.mustCall(() => {
|
||||||
|
client.close();
|
||||||
|
|
||||||
if (!tests.length) {
|
if (!tests.length) {
|
||||||
server.close();
|
server.close();
|
||||||
|
@ -88,7 +88,7 @@ server.on('listening', common.mustCall(() => {
|
|||||||
|
|
||||||
req.on('end', common.mustCall(() => {
|
req.on('end', common.mustCall(() => {
|
||||||
server.close();
|
server.close();
|
||||||
client.destroy();
|
client.close();
|
||||||
}));
|
}));
|
||||||
req.end();
|
req.end();
|
||||||
|
|
||||||
|
@ -7,29 +7,25 @@ const http2 = require('http2');
|
|||||||
|
|
||||||
const server = http2.createServer();
|
const server = http2.createServer();
|
||||||
|
|
||||||
server.on(
|
server.on('stream', common.mustCall((stream) => {
|
||||||
'stream',
|
common.expectsError(
|
||||||
common.mustCall((stream) => {
|
() => stream.close('string'),
|
||||||
const invalidArgTypeError = (param, type) => ({
|
{
|
||||||
type: TypeError,
|
|
||||||
code: 'ERR_INVALID_ARG_TYPE',
|
code: 'ERR_INVALID_ARG_TYPE',
|
||||||
message: `The "${param}" argument must be of type ${type}`
|
type: TypeError,
|
||||||
});
|
message: 'The "code" argument must be of type number'
|
||||||
common.expectsError(
|
}
|
||||||
() => stream.rstStream('string'),
|
);
|
||||||
invalidArgTypeError('code', 'number')
|
stream.respond();
|
||||||
);
|
stream.end('ok');
|
||||||
stream.session.destroy();
|
}));
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
server.listen(
|
server.listen(0, common.mustCall(() => {
|
||||||
0,
|
const client = http2.connect(`http://localhost:${server.address().port}`);
|
||||||
common.mustCall(() => {
|
const req = client.request();
|
||||||
const client = http2.connect(`http://localhost:${server.address().port}`);
|
req.resume();
|
||||||
const req = client.request();
|
req.on('close', common.mustCall(() => {
|
||||||
req.resume();
|
server.close();
|
||||||
req.on('end', common.mustCall(() => server.close()));
|
client.close();
|
||||||
req.end();
|
}));
|
||||||
})
|
}));
|
||||||
);
|
|
||||||
|
@ -5,64 +5,52 @@ if (!common.hasCrypto)
|
|||||||
common.skip('missing crypto');
|
common.skip('missing crypto');
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const h2 = require('http2');
|
const h2 = require('http2');
|
||||||
|
const Countdown = require('../common/countdown');
|
||||||
const {
|
|
||||||
HTTP2_HEADER_METHOD,
|
|
||||||
HTTP2_HEADER_STATUS,
|
|
||||||
HTTP2_HEADER_PATH,
|
|
||||||
HTTP2_METHOD_POST
|
|
||||||
} = h2.constants;
|
|
||||||
|
|
||||||
// Only allow one stream to be open at a time
|
// Only allow one stream to be open at a time
|
||||||
const server = h2.createServer({ settings: { maxConcurrentStreams: 1 } });
|
const server = h2.createServer({ settings: { maxConcurrentStreams: 1 } });
|
||||||
|
|
||||||
// The stream handler must be called only once
|
// The stream handler must be called only once
|
||||||
server.on('stream', common.mustCall((stream) => {
|
server.on('stream', common.mustCall((stream) => {
|
||||||
stream.respond({ [HTTP2_HEADER_STATUS]: 200 });
|
stream.respond();
|
||||||
stream.end('hello world');
|
stream.end('hello world');
|
||||||
}));
|
}));
|
||||||
server.listen(0);
|
|
||||||
|
|
||||||
server.on('listening', common.mustCall(() => {
|
|
||||||
|
|
||||||
|
server.listen(0, common.mustCall(() => {
|
||||||
const client = h2.connect(`http://localhost:${server.address().port}`);
|
const client = h2.connect(`http://localhost:${server.address().port}`);
|
||||||
|
|
||||||
let reqs = 2;
|
const countdown = new Countdown(2, () => {
|
||||||
function onEnd() {
|
server.close();
|
||||||
if (--reqs === 0) {
|
client.close();
|
||||||
server.close();
|
});
|
||||||
client.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
client.on('remoteSettings', common.mustCall((settings) => {
|
client.on('remoteSettings', common.mustCall((settings) => {
|
||||||
assert.strictEqual(settings.maxConcurrentStreams, 1);
|
assert.strictEqual(settings.maxConcurrentStreams, 1);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// This one should go through with no problems
|
// This one should go through with no problems
|
||||||
const req1 = client.request({
|
{
|
||||||
[HTTP2_HEADER_PATH]: '/',
|
const req = client.request({ ':method': 'POST' });
|
||||||
[HTTP2_HEADER_METHOD]: HTTP2_METHOD_POST
|
req.on('aborted', common.mustNotCall());
|
||||||
});
|
req.on('response', common.mustCall());
|
||||||
req1.on('aborted', common.mustNotCall());
|
req.resume();
|
||||||
req1.on('response', common.mustCall());
|
req.on('end', common.mustCall());
|
||||||
req1.resume();
|
req.on('close', common.mustCall(() => countdown.dec()));
|
||||||
req1.on('end', onEnd);
|
req.end();
|
||||||
req1.end();
|
}
|
||||||
|
|
||||||
// This one should be aborted
|
|
||||||
const req2 = client.request({
|
|
||||||
[HTTP2_HEADER_PATH]: '/',
|
|
||||||
[HTTP2_HEADER_METHOD]: HTTP2_METHOD_POST
|
|
||||||
});
|
|
||||||
req2.on('aborted', common.mustCall());
|
|
||||||
req2.on('response', common.mustNotCall());
|
|
||||||
req2.resume();
|
|
||||||
req2.on('end', onEnd);
|
|
||||||
req2.on('error', common.mustCall(common.expectsError({
|
|
||||||
code: 'ERR_HTTP2_STREAM_ERROR',
|
|
||||||
type: Error,
|
|
||||||
message: 'Stream closed with error code 7'
|
|
||||||
})));
|
|
||||||
|
|
||||||
|
{
|
||||||
|
// This one should be aborted
|
||||||
|
const req = client.request({ ':method': 'POST' });
|
||||||
|
req.on('aborted', common.mustCall());
|
||||||
|
req.on('response', common.mustNotCall());
|
||||||
|
req.resume();
|
||||||
|
req.on('end', common.mustCall());
|
||||||
|
req.on('close', common.mustCall(() => countdown.dec()));
|
||||||
|
req.on('error', common.expectsError({
|
||||||
|
code: 'ERR_HTTP2_STREAM_ERROR',
|
||||||
|
type: Error,
|
||||||
|
message: 'Stream closed with error code 7'
|
||||||
|
}));
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
|
@ -41,7 +41,7 @@ server.on('listening', common.mustCall(() => {
|
|||||||
req.on('end', common.mustCall(() => {
|
req.on('end', common.mustCall(() => {
|
||||||
if (--expected === 0) {
|
if (--expected === 0) {
|
||||||
server.close();
|
server.close();
|
||||||
client.destroy();
|
client.close();
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
req.end();
|
req.end();
|
||||||
|
@ -56,32 +56,24 @@ let client;
|
|||||||
|
|
||||||
const server = h2.createServer({ settings: { initialWindowSize: 36 } });
|
const server = h2.createServer({ settings: { initialWindowSize: 36 } });
|
||||||
server.on('stream', (stream) => {
|
server.on('stream', (stream) => {
|
||||||
|
|
||||||
// Not reading causes the flow control window to get backed up.
|
|
||||||
stream.pause();
|
stream.pause();
|
||||||
|
stream.on('error', common.expectsError({
|
||||||
stream.on('error', common.mustCall((err) => {
|
code: 'ERR_HTTP2_STREAM_ERROR',
|
||||||
common.expectsError({
|
type: Error,
|
||||||
code: 'ERR_HTTP2_STREAM_ERROR',
|
message: 'Stream closed with error code 3'
|
||||||
type: Error,
|
}));
|
||||||
message: 'Stream closed with error code 3'
|
stream.on('close', common.mustCall(() => {
|
||||||
})(err);
|
|
||||||
server.close();
|
server.close();
|
||||||
client.destroy();
|
client.destroy();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
stream.on('end', common.mustNotCall());
|
stream.on('end', common.mustNotCall());
|
||||||
|
|
||||||
stream.respond();
|
stream.respond();
|
||||||
stream.end('ok');
|
stream.end('ok');
|
||||||
});
|
});
|
||||||
|
|
||||||
server.listen(0, () => {
|
server.listen(0, () => {
|
||||||
client = net.connect(server.address().port, () => {
|
client = net.connect(server.address().port, () => {
|
||||||
client.on('error', console.log);
|
|
||||||
|
|
||||||
client.write(preamble);
|
client.write(preamble);
|
||||||
|
|
||||||
client.write(data);
|
client.write(data);
|
||||||
client.write(data);
|
client.write(data);
|
||||||
client.write(data);
|
client.write(data);
|
||||||
|
@ -29,6 +29,21 @@ const preamble = Buffer.from([
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
const data = Buffer.from([
|
const data = Buffer.from([
|
||||||
|
0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d,
|
||||||
|
0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a, 0x68, 0x65, 0x6c,
|
||||||
|
0x6c, 0x6f, 0x0a, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a,
|
||||||
|
0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d,
|
||||||
|
0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a, 0x68, 0x65, 0x6c,
|
||||||
|
0x6c, 0x6f, 0x0a, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a,
|
||||||
|
0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d,
|
||||||
|
0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a, 0x68, 0x65, 0x6c,
|
||||||
|
0x6c, 0x6f, 0x0a, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a,
|
||||||
|
0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d,
|
||||||
|
0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a, 0x68, 0x65, 0x6c,
|
||||||
|
0x6c, 0x6f, 0x0a, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a,
|
||||||
|
0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d,
|
||||||
|
0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a, 0x68, 0x65, 0x6c,
|
||||||
|
0x6c, 0x6f, 0x0a, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a,
|
||||||
0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d,
|
0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d,
|
||||||
0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a, 0x68, 0x65, 0x6c,
|
0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a, 0x68, 0x65, 0x6c,
|
||||||
0x6c, 0x6f, 0x0a, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a
|
0x6c, 0x6f, 0x0a, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a
|
||||||
@ -51,30 +66,23 @@ const data = Buffer.from([
|
|||||||
let client;
|
let client;
|
||||||
const server = h2.createServer({ settings: { initialWindowSize: 18 } });
|
const server = h2.createServer({ settings: { initialWindowSize: 18 } });
|
||||||
server.on('stream', (stream) => {
|
server.on('stream', (stream) => {
|
||||||
|
stream.on('error', common.expectsError({
|
||||||
stream.resume();
|
code: 'ERR_HTTP2_STREAM_ERROR',
|
||||||
|
type: Error,
|
||||||
stream.on('error', common.mustCall((err) => {
|
message: 'Stream closed with error code 3'
|
||||||
common.expectsError({
|
}));
|
||||||
code: 'ERR_HTTP2_STREAM_ERROR',
|
stream.on('close', common.mustCall(() => {
|
||||||
type: Error,
|
|
||||||
message: 'Stream closed with error code 3'
|
|
||||||
})(err);
|
|
||||||
server.close();
|
server.close();
|
||||||
client.destroy();
|
client.destroy();
|
||||||
}));
|
}));
|
||||||
|
stream.resume();
|
||||||
stream.respond();
|
stream.respond();
|
||||||
stream.end('ok');
|
stream.end('ok');
|
||||||
});
|
});
|
||||||
|
|
||||||
server.listen(0, () => {
|
server.listen(0, () => {
|
||||||
client = net.connect(server.address().port, () => {
|
client = net.connect(server.address().port, () => {
|
||||||
client.on('error', console.log);
|
|
||||||
|
|
||||||
client.write(preamble);
|
client.write(preamble);
|
||||||
|
|
||||||
client.write(data);
|
|
||||||
client.write(data);
|
client.write(data);
|
||||||
client.write(data);
|
client.write(data);
|
||||||
});
|
});
|
||||||
|
@ -7,11 +7,7 @@ const h2 = require('http2');
|
|||||||
|
|
||||||
const server = h2.createServer();
|
const server = h2.createServer();
|
||||||
|
|
||||||
// we use the lower-level API here
|
server.on('stream', common.mustCall((stream) => {
|
||||||
server.on('stream', common.mustCall(onStream));
|
|
||||||
|
|
||||||
function onStream(stream, headers, flags) {
|
|
||||||
|
|
||||||
[
|
[
|
||||||
':path',
|
':path',
|
||||||
':authority',
|
':authority',
|
||||||
@ -24,10 +20,7 @@ function onStream(stream, headers, flags) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
stream.respond({
|
stream.respond({}, {
|
||||||
'content-type': 'text/html',
|
|
||||||
':status': 200
|
|
||||||
}, {
|
|
||||||
getTrailers: common.mustCall((trailers) => {
|
getTrailers: common.mustCall((trailers) => {
|
||||||
trailers[':status'] = 'bar';
|
trailers[':status'] = 'bar';
|
||||||
})
|
})
|
||||||
@ -38,22 +31,24 @@ function onStream(stream, headers, flags) {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
stream.end('hello world');
|
stream.end('hello world');
|
||||||
}
|
}));
|
||||||
|
|
||||||
server.listen(0);
|
|
||||||
|
|
||||||
server.on('listening', common.mustCall(() => {
|
|
||||||
|
|
||||||
|
server.listen(0, common.mustCall(() => {
|
||||||
const client = h2.connect(`http://localhost:${server.address().port}`);
|
const client = h2.connect(`http://localhost:${server.address().port}`);
|
||||||
|
const req = client.request();
|
||||||
|
|
||||||
const req = client.request({ ':path': '/' });
|
req.on('error', common.expectsError({
|
||||||
|
code: 'ERR_HTTP2_STREAM_ERROR',
|
||||||
|
type: Error,
|
||||||
|
message: 'Stream closed with error code 2'
|
||||||
|
}));
|
||||||
|
|
||||||
req.on('response', common.mustCall());
|
req.on('response', common.mustCall());
|
||||||
req.resume();
|
req.resume();
|
||||||
req.on('end', common.mustCall(() => {
|
req.on('end', common.mustCall());
|
||||||
|
req.on('close', common.mustCall(() => {
|
||||||
server.close();
|
server.close();
|
||||||
client.destroy();
|
client.close();
|
||||||
}));
|
}));
|
||||||
req.end();
|
|
||||||
|
|
||||||
}));
|
}));
|
||||||
|
@ -4,6 +4,7 @@ const common = require('../common');
|
|||||||
if (!common.hasCrypto)
|
if (!common.hasCrypto)
|
||||||
common.skip('missing crypto');
|
common.skip('missing crypto');
|
||||||
const http2 = require('http2');
|
const http2 = require('http2');
|
||||||
|
const Countdown = require('../common/countdown');
|
||||||
|
|
||||||
const server = http2.createServer();
|
const server = http2.createServer();
|
||||||
|
|
||||||
@ -15,29 +16,25 @@ server.on('stream', common.mustCall((stream) => {
|
|||||||
server.listen(0, common.mustCall(() => {
|
server.listen(0, common.mustCall(() => {
|
||||||
const client = http2.connect(`http://localhost:${server.address().port}`);
|
const client = http2.connect(`http://localhost:${server.address().port}`);
|
||||||
|
|
||||||
let remaining = 3;
|
const countdown = new Countdown(2, () => {
|
||||||
function maybeClose() {
|
server.close();
|
||||||
if (--remaining === 0) {
|
client.close();
|
||||||
server.close();
|
});
|
||||||
client.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
// Request 1 will fail because there are two content-length header values
|
||||||
// Request 1 will fail because there are two content-length header values
|
common.expectsError(
|
||||||
const req = client.request({
|
() => {
|
||||||
':method': 'POST',
|
client.request({
|
||||||
'content-length': 1,
|
':method': 'POST',
|
||||||
'Content-Length': 2
|
'content-length': 1,
|
||||||
});
|
'Content-Length': 2
|
||||||
req.on('error', common.expectsError({
|
});
|
||||||
|
}, {
|
||||||
code: 'ERR_HTTP2_HEADER_SINGLE_VALUE',
|
code: 'ERR_HTTP2_HEADER_SINGLE_VALUE',
|
||||||
type: Error,
|
type: Error,
|
||||||
message: 'Header field "content-length" must have only a single value'
|
message: 'Header field "content-length" must have only a single value'
|
||||||
}));
|
}
|
||||||
req.on('error', common.mustCall(maybeClose));
|
);
|
||||||
req.end('a');
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
// Request 2 will succeed
|
// Request 2 will succeed
|
||||||
@ -46,7 +43,8 @@ server.listen(0, common.mustCall(() => {
|
|||||||
'content-length': 1
|
'content-length': 1
|
||||||
});
|
});
|
||||||
req.resume();
|
req.resume();
|
||||||
req.on('end', common.mustCall(maybeClose));
|
req.on('end', common.mustCall());
|
||||||
|
req.on('close', common.mustCall(() => countdown.dec()));
|
||||||
req.end('a');
|
req.end('a');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,7 +53,8 @@ server.listen(0, common.mustCall(() => {
|
|||||||
// header to be set for non-payload bearing requests...
|
// header to be set for non-payload bearing requests...
|
||||||
const req = client.request({ 'content-length': 1 });
|
const req = client.request({ 'content-length': 1 });
|
||||||
req.resume();
|
req.resume();
|
||||||
req.on('end', common.mustCall(maybeClose));
|
req.on('end', common.mustCall());
|
||||||
|
req.on('close', common.mustCall(() => countdown.dec()));
|
||||||
req.on('error', common.expectsError({
|
req.on('error', common.expectsError({
|
||||||
code: 'ERR_HTTP2_STREAM_ERROR',
|
code: 'ERR_HTTP2_STREAM_ERROR',
|
||||||
type: Error,
|
type: Error,
|
||||||
|
@ -44,6 +44,6 @@ server.listen(0, common.mustCall(() => {
|
|||||||
const req = client.request(src);
|
const req = client.request(src);
|
||||||
req.on('close', common.mustCall(() => {
|
req.on('close', common.mustCall(() => {
|
||||||
server.close();
|
server.close();
|
||||||
client.destroy();
|
client.close();
|
||||||
}));
|
}));
|
||||||
}));
|
}));
|
||||||
|
@ -56,6 +56,6 @@ server.listen(0, common.mustCall(() => {
|
|||||||
req.on('response', common.mustCall(checkHeaders));
|
req.on('response', common.mustCall(checkHeaders));
|
||||||
req.on('close', common.mustCall(() => {
|
req.on('close', common.mustCall(() => {
|
||||||
server.close();
|
server.close();
|
||||||
client.destroy();
|
client.close();
|
||||||
}));
|
}));
|
||||||
}));
|
}));
|
||||||
|
@ -8,6 +8,7 @@ if (!common.hasCrypto)
|
|||||||
common.skip('missing crypto');
|
common.skip('missing crypto');
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const http2 = require('http2');
|
const http2 = require('http2');
|
||||||
|
const Countdown = require('../common/countdown');
|
||||||
|
|
||||||
const server = http2.createServer();
|
const server = http2.createServer();
|
||||||
|
|
||||||
@ -20,15 +21,12 @@ server.on('stream', common.mustCall((stream) => {
|
|||||||
|
|
||||||
server.listen(0, common.mustCall(() => {
|
server.listen(0, common.mustCall(() => {
|
||||||
const client = http2.connect(`http://localhost:${server.address().port}`);
|
const client = http2.connect(`http://localhost:${server.address().port}`);
|
||||||
|
client.setMaxListeners(100);
|
||||||
|
|
||||||
let remaining = count;
|
const countdown = new Countdown(count, () => {
|
||||||
|
server.close();
|
||||||
function maybeClose() {
|
client.close();
|
||||||
if (--remaining === 0) {
|
});
|
||||||
server.close();
|
|
||||||
client.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function doRequest() {
|
function doRequest() {
|
||||||
const req = client.request({ ':method': 'POST ' });
|
const req = client.request({ ':method': 'POST ' });
|
||||||
@ -38,8 +36,8 @@ server.listen(0, common.mustCall(() => {
|
|||||||
req.on('data', (chunk) => data += chunk);
|
req.on('data', (chunk) => data += chunk);
|
||||||
req.on('end', common.mustCall(() => {
|
req.on('end', common.mustCall(() => {
|
||||||
assert.strictEqual(data, 'abcdefghij');
|
assert.strictEqual(data, 'abcdefghij');
|
||||||
maybeClose();
|
|
||||||
}));
|
}));
|
||||||
|
req.on('close', common.mustCall(() => countdown.dec()));
|
||||||
|
|
||||||
let n = 0;
|
let n = 0;
|
||||||
function writeChunk() {
|
function writeChunk() {
|
||||||
|
@ -25,7 +25,7 @@ server.listen(0, common.mustCall(() => {
|
|||||||
|
|
||||||
const countdown = new Countdown(2, common.mustCall(() => {
|
const countdown = new Countdown(2, common.mustCall(() => {
|
||||||
server.close();
|
server.close();
|
||||||
client.destroy();
|
client.close();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -10,9 +10,7 @@ const server = h2.createServer();
|
|||||||
|
|
||||||
// we use the lower-level API here
|
// we use the lower-level API here
|
||||||
server.on('stream', common.mustNotCall());
|
server.on('stream', common.mustNotCall());
|
||||||
server.listen(0);
|
server.listen(0, common.mustCall(() => {
|
||||||
|
|
||||||
server.on('listening', common.mustCall(() => {
|
|
||||||
|
|
||||||
// Setting the maxSendHeaderBlockLength, then attempting to send a
|
// Setting the maxSendHeaderBlockLength, then attempting to send a
|
||||||
// headers block that is too big should cause a 'frameError' to
|
// headers block that is too big should cause a 'frameError' to
|
||||||
@ -24,13 +22,13 @@ server.on('listening', common.mustCall(() => {
|
|||||||
const client = h2.connect(`http://localhost:${server.address().port}`,
|
const client = h2.connect(`http://localhost:${server.address().port}`,
|
||||||
options);
|
options);
|
||||||
|
|
||||||
const req = client.request({ ':path': '/' });
|
const req = client.request();
|
||||||
|
|
||||||
req.on('response', common.mustNotCall());
|
req.on('response', common.mustNotCall());
|
||||||
|
|
||||||
req.resume();
|
req.resume();
|
||||||
req.on('end', common.mustCall(() => {
|
req.on('close', common.mustCall(() => {
|
||||||
client.destroy();
|
client.close();
|
||||||
|
server.close();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
req.on('frameError', common.mustCall((type, code) => {
|
req.on('frameError', common.mustCall((type, code) => {
|
||||||
@ -42,33 +40,4 @@ server.on('listening', common.mustCall(() => {
|
|||||||
type: Error,
|
type: Error,
|
||||||
message: 'Stream closed with error code 7'
|
message: 'Stream closed with error code 7'
|
||||||
}));
|
}));
|
||||||
|
|
||||||
req.end();
|
|
||||||
|
|
||||||
// if no frameError listener, should emit 'error' with
|
|
||||||
// code ERR_HTTP2_FRAME_ERROR
|
|
||||||
const req2 = client.request({ ':path': '/' });
|
|
||||||
|
|
||||||
req2.on('response', common.mustNotCall());
|
|
||||||
|
|
||||||
req2.resume();
|
|
||||||
req2.on('end', common.mustCall(() => {
|
|
||||||
server.close();
|
|
||||||
client.destroy();
|
|
||||||
}));
|
|
||||||
|
|
||||||
req2.once('error', common.mustCall((err) => {
|
|
||||||
common.expectsError({
|
|
||||||
code: 'ERR_HTTP2_FRAME_ERROR',
|
|
||||||
type: Error
|
|
||||||
})(err);
|
|
||||||
req2.on('error', common.expectsError({
|
|
||||||
code: 'ERR_HTTP2_STREAM_ERROR',
|
|
||||||
type: Error,
|
|
||||||
message: 'Stream closed with error code 7'
|
|
||||||
}));
|
|
||||||
}));
|
|
||||||
|
|
||||||
req2.end();
|
|
||||||
|
|
||||||
}));
|
}));
|
||||||
|
@ -5,20 +5,24 @@ if (!common.hasCrypto)
|
|||||||
common.skip('missing crypto');
|
common.skip('missing crypto');
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const h2 = require('http2');
|
const h2 = require('http2');
|
||||||
|
const Countdown = require('../common/countdown');
|
||||||
|
|
||||||
const server = h2.createServer();
|
const server = h2.createServer();
|
||||||
|
let client;
|
||||||
|
|
||||||
|
const countdown = new Countdown(3, () => {
|
||||||
|
server.close();
|
||||||
|
client.close();
|
||||||
|
});
|
||||||
|
|
||||||
// we use the lower-level API here
|
// we use the lower-level API here
|
||||||
server.on('stream', common.mustCall((stream) => {
|
server.on('stream', common.mustCall((stream) => {
|
||||||
stream.respond({ ':status': 200 });
|
|
||||||
|
|
||||||
// The first pushStream will complete as normal
|
// The first pushStream will complete as normal
|
||||||
stream.pushStream({
|
stream.pushStream({
|
||||||
':scheme': 'http',
|
|
||||||
':path': '/foobar',
|
':path': '/foobar',
|
||||||
':authority': `localhost:${server.address().port}`,
|
}, common.mustCall((err, pushedStream) => {
|
||||||
}, common.mustCall((pushedStream) => {
|
assert.ifError(err);
|
||||||
pushedStream.respond({ ':status': 200 });
|
pushedStream.respond();
|
||||||
pushedStream.end();
|
pushedStream.end();
|
||||||
pushedStream.on('aborted', common.mustNotCall());
|
pushedStream.on('aborted', common.mustNotCall());
|
||||||
}));
|
}));
|
||||||
@ -27,52 +31,41 @@ server.on('stream', common.mustCall((stream) => {
|
|||||||
// will reject it due to the maxReservedRemoteStreams option
|
// will reject it due to the maxReservedRemoteStreams option
|
||||||
// being set to only 1
|
// being set to only 1
|
||||||
stream.pushStream({
|
stream.pushStream({
|
||||||
':scheme': 'http',
|
|
||||||
':path': '/foobar',
|
':path': '/foobar',
|
||||||
':authority': `localhost:${server.address().port}`,
|
}, common.mustCall((err, pushedStream) => {
|
||||||
}, common.mustCall((pushedStream) => {
|
assert.ifError(err);
|
||||||
pushedStream.respond({ ':status': 200 });
|
pushedStream.respond();
|
||||||
pushedStream.on('aborted', common.mustCall());
|
pushedStream.on('aborted', common.mustCall());
|
||||||
pushedStream.on('error', common.mustNotCall());
|
pushedStream.on('error', common.mustNotCall());
|
||||||
pushedStream.on('close',
|
pushedStream.on('close', common.mustCall((code) => {
|
||||||
common.mustCall((code) => assert.strictEqual(code, 8)));
|
assert.strictEqual(code, 8);
|
||||||
|
countdown.dec();
|
||||||
|
}));
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
stream.respond();
|
||||||
stream.end('hello world');
|
stream.end('hello world');
|
||||||
}));
|
}));
|
||||||
server.listen(0);
|
server.listen(0);
|
||||||
|
|
||||||
server.on('listening', common.mustCall(() => {
|
server.on('listening', common.mustCall(() => {
|
||||||
|
client = h2.connect(`http://localhost:${server.address().port}`,
|
||||||
|
{ maxReservedRemoteStreams: 1 });
|
||||||
|
|
||||||
const options = {
|
const req = client.request();
|
||||||
maxReservedRemoteStreams: 1
|
|
||||||
};
|
|
||||||
|
|
||||||
const client = h2.connect(`http://localhost:${server.address().port}`,
|
|
||||||
options);
|
|
||||||
|
|
||||||
let remaining = 2;
|
|
||||||
function maybeClose() {
|
|
||||||
if (--remaining === 0) {
|
|
||||||
server.close();
|
|
||||||
client.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const req = client.request({ ':path': '/' });
|
|
||||||
|
|
||||||
// Because maxReservedRemoteStream is 1, the stream event
|
// Because maxReservedRemoteStream is 1, the stream event
|
||||||
// must only be emitted once, even tho the server sends
|
// must only be emitted once, even tho the server sends
|
||||||
// two push streams.
|
// two push streams.
|
||||||
client.on('stream', common.mustCall((stream) => {
|
client.on('stream', common.mustCall((stream) => {
|
||||||
stream.resume();
|
stream.resume();
|
||||||
|
stream.on('push', common.mustCall());
|
||||||
stream.on('end', common.mustCall());
|
stream.on('end', common.mustCall());
|
||||||
stream.on('close', common.mustCall(maybeClose));
|
stream.on('close', common.mustCall(() => countdown.dec()));
|
||||||
}));
|
}));
|
||||||
|
|
||||||
req.on('response', common.mustCall());
|
req.on('response', common.mustCall());
|
||||||
|
|
||||||
req.resume();
|
req.resume();
|
||||||
req.on('end', common.mustCall());
|
req.on('end', common.mustCall());
|
||||||
req.on('close', common.mustCall(maybeClose));
|
req.on('close', common.mustCall(() => countdown.dec()));
|
||||||
}));
|
}));
|
||||||
|
@ -45,7 +45,7 @@ server.on('listening', common.mustCall(() => {
|
|||||||
req.resume();
|
req.resume();
|
||||||
req.on('end', common.mustCall(() => {
|
req.on('end', common.mustCall(() => {
|
||||||
server.close();
|
server.close();
|
||||||
client.destroy();
|
client.close();
|
||||||
}));
|
}));
|
||||||
req.end();
|
req.end();
|
||||||
}));
|
}));
|
||||||
|
@ -80,7 +80,7 @@ server.listen(0, common.mustCall(() => {
|
|||||||
const req = client.request();
|
const req = client.request();
|
||||||
req.resume();
|
req.resume();
|
||||||
req.on('end', common.mustCall(() => {
|
req.on('end', common.mustCall(() => {
|
||||||
client.destroy();
|
client.close();
|
||||||
server.close();
|
server.close();
|
||||||
}));
|
}));
|
||||||
}));
|
}));
|
||||||
|
@ -8,7 +8,6 @@ const assert = require('assert');
|
|||||||
const http2 = require('http2');
|
const http2 = require('http2');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const Countdown = require('../common/countdown');
|
|
||||||
|
|
||||||
// piping should work as expected with createWriteStream
|
// piping should work as expected with createWriteStream
|
||||||
|
|
||||||
@ -20,28 +19,28 @@ const server = http2.createServer();
|
|||||||
|
|
||||||
server.on('stream', common.mustCall((stream) => {
|
server.on('stream', common.mustCall((stream) => {
|
||||||
const dest = stream.pipe(fs.createWriteStream(fn));
|
const dest = stream.pipe(fs.createWriteStream(fn));
|
||||||
dest.on('finish', common.mustCall(() => {
|
|
||||||
assert.strictEqual(fs.readFileSync(loc).length, fs.readFileSync(fn).length);
|
dest.on('finish', () => {
|
||||||
fs.unlinkSync(fn);
|
assert.strictEqual(fs.readFileSync(loc).length,
|
||||||
stream.respond();
|
fs.readFileSync(fn).length);
|
||||||
stream.end();
|
});
|
||||||
}));
|
stream.respond();
|
||||||
|
stream.end();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
server.listen(0, common.mustCall(() => {
|
server.listen(0, common.mustCall(() => {
|
||||||
const port = server.address().port;
|
const client = http2.connect(`http://localhost:${server.address().port}`);
|
||||||
const client = http2.connect(`http://localhost:${port}`);
|
|
||||||
|
|
||||||
const countdown = new Countdown(2, common.mustCall(() => {
|
|
||||||
server.close();
|
|
||||||
client.destroy();
|
|
||||||
}));
|
|
||||||
|
|
||||||
const req = client.request({ ':method': 'POST' });
|
const req = client.request({ ':method': 'POST' });
|
||||||
req.on('response', common.mustCall());
|
req.on('response', common.mustCall());
|
||||||
req.resume();
|
req.resume();
|
||||||
req.on('end', common.mustCall(() => countdown.dec()));
|
|
||||||
|
req.on('close', common.mustCall(() => {
|
||||||
|
server.close();
|
||||||
|
client.close();
|
||||||
|
}));
|
||||||
|
|
||||||
const str = fs.createReadStream(loc);
|
const str = fs.createReadStream(loc);
|
||||||
str.on('end', common.mustCall(() => countdown.dec()));
|
str.on('end', common.mustCall());
|
||||||
str.pipe(req);
|
str.pipe(req);
|
||||||
}));
|
}));
|
||||||
|
@ -54,7 +54,7 @@ server.on('listening', common.mustCall(() => {
|
|||||||
req.resume();
|
req.resume();
|
||||||
req.on('end', common.mustCall(() => {
|
req.on('end', common.mustCall(() => {
|
||||||
server.close();
|
server.close();
|
||||||
client.destroy();
|
client.close();
|
||||||
}));
|
}));
|
||||||
req.end();
|
req.end();
|
||||||
|
|
||||||
|
@ -74,13 +74,18 @@ function runTest(test) {
|
|||||||
|
|
||||||
const client = http2.connect(url);
|
const client = http2.connect(url);
|
||||||
const req = client.request(headers);
|
const req = client.request(headers);
|
||||||
|
req.on('error', common.expectsError({
|
||||||
|
code: 'ERR_HTTP2_STREAM_ERROR',
|
||||||
|
type: Error,
|
||||||
|
message: 'Stream closed with error code 2'
|
||||||
|
}));
|
||||||
|
|
||||||
currentError = test;
|
currentError = test;
|
||||||
req.resume();
|
req.resume();
|
||||||
req.end();
|
req.end();
|
||||||
|
|
||||||
req.on('end', common.mustCall(() => {
|
req.on('end', common.mustCall(() => {
|
||||||
client.destroy();
|
client.close();
|
||||||
|
|
||||||
if (!tests.length) {
|
if (!tests.length) {
|
||||||
server.close();
|
server.close();
|
||||||
|
@ -34,7 +34,7 @@ server.listen(0, () => {
|
|||||||
req.on('response', common.mustCall());
|
req.on('response', common.mustCall());
|
||||||
req.on('data', common.mustNotCall());
|
req.on('data', common.mustNotCall());
|
||||||
req.on('end', common.mustCall(() => {
|
req.on('end', common.mustCall(() => {
|
||||||
client.destroy();
|
client.close();
|
||||||
server.close();
|
server.close();
|
||||||
}));
|
}));
|
||||||
req.end();
|
req.end();
|
||||||
|
@ -38,7 +38,7 @@ server.listen(0, () => {
|
|||||||
|
|
||||||
req.on('data', common.mustNotCall());
|
req.on('data', common.mustNotCall());
|
||||||
req.on('end', common.mustCall(() => {
|
req.on('end', common.mustCall(() => {
|
||||||
client.destroy();
|
client.close();
|
||||||
server.close();
|
server.close();
|
||||||
}));
|
}));
|
||||||
req.end();
|
req.end();
|
||||||
|
@ -40,7 +40,7 @@ server.listen(0, () => {
|
|||||||
}));
|
}));
|
||||||
req.on('data', common.mustNotCall());
|
req.on('data', common.mustNotCall());
|
||||||
req.on('end', common.mustCall(() => {
|
req.on('end', common.mustCall(() => {
|
||||||
client.destroy();
|
client.close();
|
||||||
server.close();
|
server.close();
|
||||||
}));
|
}));
|
||||||
req.end();
|
req.end();
|
||||||
|
@ -16,7 +16,7 @@ server.listen(0, () => {
|
|||||||
const req = client.request();
|
const req = client.request();
|
||||||
req.on('response', common.mustCall());
|
req.on('response', common.mustCall());
|
||||||
req.on('end', common.mustCall(() => {
|
req.on('end', common.mustCall(() => {
|
||||||
client.destroy();
|
client.close();
|
||||||
server.close();
|
server.close();
|
||||||
}));
|
}));
|
||||||
req.end();
|
req.end();
|
||||||
|
@ -6,14 +6,10 @@ if (!common.hasCrypto)
|
|||||||
const http2 = require('http2');
|
const http2 = require('http2');
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
|
|
||||||
const {
|
|
||||||
HTTP2_HEADER_CONTENT_TYPE
|
|
||||||
} = http2.constants;
|
|
||||||
|
|
||||||
const server = http2.createServer();
|
const server = http2.createServer();
|
||||||
server.on('stream', (stream) => {
|
server.on('stream', (stream) => {
|
||||||
stream.respondWithFile(process.cwd(), {
|
stream.respondWithFile(process.cwd(), {
|
||||||
[HTTP2_HEADER_CONTENT_TYPE]: 'text/plain'
|
'content-type': 'text/plain'
|
||||||
}, {
|
}, {
|
||||||
onError(err) {
|
onError(err) {
|
||||||
common.expectsError({
|
common.expectsError({
|
||||||
@ -38,7 +34,7 @@ server.listen(0, () => {
|
|||||||
}));
|
}));
|
||||||
req.on('data', common.mustNotCall());
|
req.on('data', common.mustNotCall());
|
||||||
req.on('end', common.mustCall(() => {
|
req.on('end', common.mustCall(() => {
|
||||||
client.destroy();
|
client.close();
|
||||||
server.close();
|
server.close();
|
||||||
}));
|
}));
|
||||||
req.end();
|
req.end();
|
||||||
|
@ -6,11 +6,6 @@ if (!common.hasCrypto)
|
|||||||
const fixtures = require('../common/fixtures');
|
const fixtures = require('../common/fixtures');
|
||||||
const http2 = require('http2');
|
const http2 = require('http2');
|
||||||
|
|
||||||
const {
|
|
||||||
HTTP2_HEADER_CONTENT_TYPE,
|
|
||||||
HTTP2_HEADER_METHOD
|
|
||||||
} = http2.constants;
|
|
||||||
|
|
||||||
const optionsWithTypeError = {
|
const optionsWithTypeError = {
|
||||||
offset: 'number',
|
offset: 'number',
|
||||||
length: 'number',
|
length: 'number',
|
||||||
@ -33,6 +28,7 @@ const fname = fixtures.path('elipses.txt');
|
|||||||
const server = http2.createServer();
|
const server = http2.createServer();
|
||||||
|
|
||||||
server.on('stream', common.mustCall((stream) => {
|
server.on('stream', common.mustCall((stream) => {
|
||||||
|
|
||||||
// Check for all possible TypeError triggers on options
|
// Check for all possible TypeError triggers on options
|
||||||
Object.keys(optionsWithTypeError).forEach((option) => {
|
Object.keys(optionsWithTypeError).forEach((option) => {
|
||||||
Object.keys(types).forEach((type) => {
|
Object.keys(types).forEach((type) => {
|
||||||
@ -42,7 +38,7 @@ server.on('stream', common.mustCall((stream) => {
|
|||||||
|
|
||||||
common.expectsError(
|
common.expectsError(
|
||||||
() => stream.respondWithFile(fname, {
|
() => stream.respondWithFile(fname, {
|
||||||
[http2.constants.HTTP2_HEADER_CONTENT_TYPE]: 'text/plain'
|
'content-type': 'text/plain'
|
||||||
}, {
|
}, {
|
||||||
[option]: types[type]
|
[option]: types[type]
|
||||||
}),
|
}),
|
||||||
@ -59,7 +55,7 @@ server.on('stream', common.mustCall((stream) => {
|
|||||||
// Should throw if :status 204, 205 or 304
|
// Should throw if :status 204, 205 or 304
|
||||||
[204, 205, 304].forEach((status) => common.expectsError(
|
[204, 205, 304].forEach((status) => common.expectsError(
|
||||||
() => stream.respondWithFile(fname, {
|
() => stream.respondWithFile(fname, {
|
||||||
[HTTP2_HEADER_CONTENT_TYPE]: 'text/plain',
|
'content-type': 'text/plain',
|
||||||
':status': status,
|
':status': status,
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
@ -68,31 +64,11 @@ server.on('stream', common.mustCall((stream) => {
|
|||||||
}
|
}
|
||||||
));
|
));
|
||||||
|
|
||||||
// should emit an error on the stream if headers aren't valid
|
|
||||||
stream.respondWithFile(fname, {
|
|
||||||
[HTTP2_HEADER_METHOD]: 'POST'
|
|
||||||
}, {
|
|
||||||
statCheck: common.mustCall(() => {
|
|
||||||
// give time to the current test case to finish
|
|
||||||
process.nextTick(continueTest, stream);
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
});
|
|
||||||
stream.once('error', common.expectsError({
|
|
||||||
code: 'ERR_HTTP2_INVALID_PSEUDOHEADER',
|
|
||||||
type: Error,
|
|
||||||
message: '":method" is an invalid pseudoheader or is used incorrectly'
|
|
||||||
}));
|
|
||||||
}));
|
|
||||||
|
|
||||||
function continueTest(stream) {
|
|
||||||
// Should throw if headers already sent
|
// Should throw if headers already sent
|
||||||
stream.respond({
|
stream.respond({ ':status': 200 });
|
||||||
':status': 200,
|
|
||||||
});
|
|
||||||
common.expectsError(
|
common.expectsError(
|
||||||
() => stream.respondWithFile(fname, {
|
() => stream.respondWithFile(fname, {
|
||||||
[HTTP2_HEADER_CONTENT_TYPE]: 'text/plain'
|
'content-type': 'text/plain'
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
code: 'ERR_HTTP2_HEADERS_SENT',
|
code: 'ERR_HTTP2_HEADERS_SENT',
|
||||||
@ -104,21 +80,21 @@ function continueTest(stream) {
|
|||||||
stream.destroy();
|
stream.destroy();
|
||||||
common.expectsError(
|
common.expectsError(
|
||||||
() => stream.respondWithFile(fname, {
|
() => stream.respondWithFile(fname, {
|
||||||
[HTTP2_HEADER_CONTENT_TYPE]: 'text/plain'
|
'content-type': 'text/plain'
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
code: 'ERR_HTTP2_INVALID_STREAM',
|
code: 'ERR_HTTP2_INVALID_STREAM',
|
||||||
message: 'The stream has been destroyed'
|
message: 'The stream has been destroyed'
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}));
|
||||||
|
|
||||||
server.listen(0, common.mustCall(() => {
|
server.listen(0, common.mustCall(() => {
|
||||||
const client = http2.connect(`http://localhost:${server.address().port}`);
|
const client = http2.connect(`http://localhost:${server.address().port}`);
|
||||||
const req = client.request();
|
const req = client.request();
|
||||||
|
|
||||||
req.on('close', common.mustCall(() => {
|
req.on('close', common.mustCall(() => {
|
||||||
client.destroy();
|
client.close();
|
||||||
server.close();
|
server.close();
|
||||||
}));
|
}));
|
||||||
req.end();
|
req.end();
|
||||||
|
@ -7,11 +7,6 @@ const fixtures = require('../common/fixtures');
|
|||||||
const http2 = require('http2');
|
const http2 = require('http2');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
|
||||||
const {
|
|
||||||
HTTP2_HEADER_CONTENT_TYPE,
|
|
||||||
HTTP2_HEADER_METHOD
|
|
||||||
} = http2.constants;
|
|
||||||
|
|
||||||
const optionsWithTypeError = {
|
const optionsWithTypeError = {
|
||||||
offset: 'number',
|
offset: 'number',
|
||||||
length: 'number',
|
length: 'number',
|
||||||
@ -43,7 +38,7 @@ server.on('stream', common.mustCall((stream) => {
|
|||||||
|
|
||||||
common.expectsError(
|
common.expectsError(
|
||||||
() => stream.respondWithFD(types[type], {
|
() => stream.respondWithFD(types[type], {
|
||||||
[HTTP2_HEADER_CONTENT_TYPE]: 'text/plain'
|
'content-type': 'text/plain'
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
type: TypeError,
|
type: TypeError,
|
||||||
@ -62,7 +57,7 @@ server.on('stream', common.mustCall((stream) => {
|
|||||||
|
|
||||||
common.expectsError(
|
common.expectsError(
|
||||||
() => stream.respondWithFD(fd, {
|
() => stream.respondWithFD(fd, {
|
||||||
[HTTP2_HEADER_CONTENT_TYPE]: 'text/plain'
|
'content-type': 'text/plain'
|
||||||
}, {
|
}, {
|
||||||
[option]: types[type]
|
[option]: types[type]
|
||||||
}),
|
}),
|
||||||
@ -79,7 +74,7 @@ server.on('stream', common.mustCall((stream) => {
|
|||||||
// Should throw if :status 204, 205 or 304
|
// Should throw if :status 204, 205 or 304
|
||||||
[204, 205, 304].forEach((status) => common.expectsError(
|
[204, 205, 304].forEach((status) => common.expectsError(
|
||||||
() => stream.respondWithFD(fd, {
|
() => stream.respondWithFD(fd, {
|
||||||
[HTTP2_HEADER_CONTENT_TYPE]: 'text/plain',
|
'content-type': 'text/plain',
|
||||||
':status': status,
|
':status': status,
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
@ -89,35 +84,11 @@ server.on('stream', common.mustCall((stream) => {
|
|||||||
}
|
}
|
||||||
));
|
));
|
||||||
|
|
||||||
// should emit an error on the stream if headers aren't valid
|
|
||||||
stream.respondWithFD(fd, {
|
|
||||||
[HTTP2_HEADER_METHOD]: 'POST'
|
|
||||||
}, {
|
|
||||||
statCheck() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
stream.once('error', common.expectsError({
|
|
||||||
code: 'ERR_HTTP2_INVALID_PSEUDOHEADER',
|
|
||||||
type: Error,
|
|
||||||
message: '":method" is an invalid pseudoheader or is used incorrectly'
|
|
||||||
}));
|
|
||||||
stream.respondWithFD(fd, {
|
|
||||||
[HTTP2_HEADER_METHOD]: 'POST'
|
|
||||||
});
|
|
||||||
stream.once('error', common.expectsError({
|
|
||||||
code: 'ERR_HTTP2_INVALID_PSEUDOHEADER',
|
|
||||||
type: Error,
|
|
||||||
message: '":method" is an invalid pseudoheader or is used incorrectly'
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Should throw if headers already sent
|
// Should throw if headers already sent
|
||||||
stream.respond({
|
stream.respond();
|
||||||
':status': 200,
|
|
||||||
});
|
|
||||||
common.expectsError(
|
common.expectsError(
|
||||||
() => stream.respondWithFD(fd, {
|
() => stream.respondWithFD(fd, {
|
||||||
[HTTP2_HEADER_CONTENT_TYPE]: 'text/plain'
|
'content-type': 'text/plain'
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
code: 'ERR_HTTP2_HEADERS_SENT',
|
code: 'ERR_HTTP2_HEADERS_SENT',
|
||||||
@ -130,7 +101,7 @@ server.on('stream', common.mustCall((stream) => {
|
|||||||
stream.destroy();
|
stream.destroy();
|
||||||
common.expectsError(
|
common.expectsError(
|
||||||
() => stream.respondWithFD(fd, {
|
() => stream.respondWithFD(fd, {
|
||||||
[HTTP2_HEADER_CONTENT_TYPE]: 'text/plain'
|
'content-type': 'text/plain'
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
code: 'ERR_HTTP2_INVALID_STREAM',
|
code: 'ERR_HTTP2_INVALID_STREAM',
|
||||||
@ -145,7 +116,7 @@ server.listen(0, common.mustCall(() => {
|
|||||||
const req = client.request();
|
const req = client.request();
|
||||||
|
|
||||||
req.on('close', common.mustCall(() => {
|
req.on('close', common.mustCall(() => {
|
||||||
client.destroy();
|
client.close();
|
||||||
server.close();
|
server.close();
|
||||||
}));
|
}));
|
||||||
req.end();
|
req.end();
|
||||||
|
@ -31,7 +31,7 @@ server.listen(0, () => {
|
|||||||
req.on('data', common.mustNotCall());
|
req.on('data', common.mustNotCall());
|
||||||
req.on('end', common.mustCall(() => {
|
req.on('end', common.mustCall(() => {
|
||||||
assert.strictEqual(req.rstCode, NGHTTP2_INTERNAL_ERROR);
|
assert.strictEqual(req.rstCode, NGHTTP2_INTERNAL_ERROR);
|
||||||
client.destroy();
|
client.close();
|
||||||
server.close();
|
server.close();
|
||||||
}));
|
}));
|
||||||
req.end();
|
req.end();
|
||||||
|
@ -9,6 +9,7 @@ const fixtures = require('../common/fixtures');
|
|||||||
const http2 = require('http2');
|
const http2 = require('http2');
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
const Countdown = require('../common/countdown');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
HTTP2_HEADER_CONTENT_TYPE,
|
HTTP2_HEADER_CONTENT_TYPE,
|
||||||
@ -39,7 +40,7 @@ server.on('stream', (stream, headers) => {
|
|||||||
statCheck: common.mustCall((stat, headers, options) => {
|
statCheck: common.mustCall((stat, headers, options) => {
|
||||||
assert.strictEqual(options.length, length);
|
assert.strictEqual(options.length, length);
|
||||||
assert.strictEqual(options.offset, offset);
|
assert.strictEqual(options.offset, offset);
|
||||||
headers[HTTP2_HEADER_CONTENT_LENGTH] =
|
headers['content-length'] =
|
||||||
Math.min(options.length, stat.size - offset);
|
Math.min(options.length, stat.size - offset);
|
||||||
}),
|
}),
|
||||||
offset: offset,
|
offset: offset,
|
||||||
@ -47,23 +48,21 @@ server.on('stream', (stream, headers) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
server.on('close', common.mustCall(() => fs.closeSync(fd)));
|
server.on('close', common.mustCall(() => fs.closeSync(fd)));
|
||||||
|
|
||||||
server.listen(0, () => {
|
server.listen(0, () => {
|
||||||
const client = http2.connect(`http://localhost:${server.address().port}`);
|
const client = http2.connect(`http://localhost:${server.address().port}`);
|
||||||
|
|
||||||
let remaining = 2;
|
const countdown = new Countdown(2, () => {
|
||||||
function maybeClose() {
|
client.close();
|
||||||
if (--remaining === 0) {
|
server.close();
|
||||||
client.destroy();
|
});
|
||||||
server.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
const req = client.request({ range: 'bytes=8-11' });
|
const req = client.request({ range: 'bytes=8-11' });
|
||||||
|
|
||||||
req.on('response', common.mustCall((headers) => {
|
req.on('response', common.mustCall((headers) => {
|
||||||
assert.strictEqual(headers[HTTP2_HEADER_CONTENT_TYPE], 'text/plain');
|
assert.strictEqual(headers['content-type'], 'text/plain');
|
||||||
assert.strictEqual(+headers[HTTP2_HEADER_CONTENT_LENGTH], 3);
|
assert.strictEqual(+headers['content-length'], 3);
|
||||||
}));
|
}));
|
||||||
req.setEncoding('utf8');
|
req.setEncoding('utf8');
|
||||||
let check = '';
|
let check = '';
|
||||||
@ -71,7 +70,7 @@ server.listen(0, () => {
|
|||||||
req.on('end', common.mustCall(() => {
|
req.on('end', common.mustCall(() => {
|
||||||
assert.strictEqual(check, data.toString('utf8', 8, 11));
|
assert.strictEqual(check, data.toString('utf8', 8, 11));
|
||||||
}));
|
}));
|
||||||
req.on('close', common.mustCall(maybeClose));
|
req.on('close', common.mustCall(() => countdown.dec()));
|
||||||
req.end();
|
req.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,7 +87,7 @@ server.listen(0, () => {
|
|||||||
req.on('end', common.mustCall(() => {
|
req.on('end', common.mustCall(() => {
|
||||||
assert.strictEqual(check, data.toString('utf8', 8, 28));
|
assert.strictEqual(check, data.toString('utf8', 8, 28));
|
||||||
}));
|
}));
|
||||||
req.on('close', common.mustCall(maybeClose));
|
req.on('close', common.mustCall(() => countdown.dec()));
|
||||||
req.end();
|
req.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ server.listen(0, () => {
|
|||||||
req.on('data', (chunk) => check += chunk);
|
req.on('data', (chunk) => check += chunk);
|
||||||
req.on('end', common.mustCall(() => {
|
req.on('end', common.mustCall(() => {
|
||||||
assert.strictEqual(check, data.toString('utf8'));
|
assert.strictEqual(check, data.toString('utf8'));
|
||||||
client.destroy();
|
client.close();
|
||||||
server.close();
|
server.close();
|
||||||
}));
|
}));
|
||||||
req.end();
|
req.end();
|
||||||
|
@ -29,7 +29,8 @@ server.on('stream', (stream) => {
|
|||||||
stream.pushStream({
|
stream.pushStream({
|
||||||
':path': '/file.txt',
|
':path': '/file.txt',
|
||||||
':method': 'GET'
|
':method': 'GET'
|
||||||
}, (stream) => {
|
}, (err, stream) => {
|
||||||
|
assert.ifError(err);
|
||||||
stream.respondWithFD(fd, {
|
stream.respondWithFD(fd, {
|
||||||
[HTTP2_HEADER_CONTENT_TYPE]: 'text/plain',
|
[HTTP2_HEADER_CONTENT_TYPE]: 'text/plain',
|
||||||
[HTTP2_HEADER_CONTENT_LENGTH]: stat.size,
|
[HTTP2_HEADER_CONTENT_LENGTH]: stat.size,
|
||||||
@ -50,7 +51,7 @@ server.listen(0, () => {
|
|||||||
function maybeClose() {
|
function maybeClose() {
|
||||||
if (--expected === 0) {
|
if (--expected === 0) {
|
||||||
server.close();
|
server.close();
|
||||||
client.destroy();
|
client.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user