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
|
||||
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>
|
||||
### ERR_HTTP2_CONNECT_AUTHORITY
|
||||
|
||||
@ -833,6 +838,12 @@ forbidden.
|
||||
|
||||
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>
|
||||
### ERR_HTTP2_HEADER_REQUIRED
|
||||
|
||||
@ -972,6 +983,11 @@ client.
|
||||
An attempt was made to use the `Http2Stream.prototype.responseWithFile()` API to
|
||||
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>
|
||||
### 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
|
||||
between `100` and `599` (inclusive).
|
||||
|
||||
<a id="ERR_HTTP2_STREAM_CLOSED"></a>
|
||||
### ERR_HTTP2_STREAM_CLOSED
|
||||
<a id="ERR_HTTP2_STREAM_CANCEL"></a>
|
||||
### 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>
|
||||
### 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', {
|
||||
ca: fs.readFileSync('localhost-cert.pem')
|
||||
});
|
||||
client.on('socketError', (err) => console.error(err));
|
||||
client.on('error', (err) => console.error(err));
|
||||
client.on('socketError', (err) => console.error(err));
|
||||
|
||||
const req = client.request({ ':path': '/' });
|
||||
|
||||
@ -83,7 +83,7 @@ let data = '';
|
||||
req.on('data', (chunk) => { data += chunk; });
|
||||
req.on('end', () => {
|
||||
console.log(`\n${data}`);
|
||||
client.destroy();
|
||||
client.close();
|
||||
});
|
||||
req.end();
|
||||
```
|
||||
@ -127,7 +127,7 @@ solely on the API of the `Http2Session`.
|
||||
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'
|
||||
<!-- YAML
|
||||
@ -234,19 +234,13 @@ of the stream.
|
||||
|
||||
```js
|
||||
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) => {
|
||||
const method = headers[HTTP2_HEADER_METHOD];
|
||||
const path = headers[HTTP2_HEADER_PATH];
|
||||
const method = headers[':method'];
|
||||
const path = headers[':path'];
|
||||
// ...
|
||||
stream.respond({
|
||||
[HTTP2_HEADER_STATUS]: 200,
|
||||
[HTTP2_HEADER_CONTENT_TYPE]: 'text/plain'
|
||||
':status': 200,
|
||||
'content-type': 'text/plain'
|
||||
});
|
||||
stream.write('hello ');
|
||||
stream.end('world');
|
||||
@ -275,19 +269,6 @@ server.on('stream', (stream, headers) => {
|
||||
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'
|
||||
<!-- YAML
|
||||
added: v8.4.0
|
||||
@ -302,16 +283,53 @@ session.setTimeout(2000);
|
||||
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
|
||||
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}
|
||||
|
||||
Immediately terminates the `Http2Session` and the associated `net.Socket` or
|
||||
`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
|
||||
<!-- YAML
|
||||
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
|
||||
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
|
||||
<!-- YAML
|
||||
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
|
||||
fields.
|
||||
|
||||
The the `:method` and `:path` pseudoheaders are not specified within `headers`,
|
||||
they respectively default to:
|
||||
|
||||
* `:method` = `'GET'`
|
||||
* `:path` = `/`
|
||||
|
||||
#### http2session.setTimeout(msecs, callback)
|
||||
<!-- YAML
|
||||
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
|
||||
registered as a listener on the `'timeout'` event.
|
||||
|
||||
#### http2session.shutdown(options[, callback])
|
||||
#### http2session.close(options[, callback])
|
||||
<!-- YAML
|
||||
added: v8.4.0
|
||||
-->
|
||||
|
||||
* `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
|
||||
*not* the same thing as an HTTP Response Status Code. **Default:** `0x00`
|
||||
(No Error).
|
||||
* `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
|
||||
containing arbitrary additional data to send to the peer upon disconnection.
|
||||
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
|
||||
process has completed.
|
||||
|
||||
Note that calling `http2session.shutdown()` does *not* destroy the session or
|
||||
tear down the `Socket` connection. It merely prompts both sessions to begin
|
||||
preparing to cease activity.
|
||||
|
||||
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.
|
||||
If the `Http2Session` instance is a server-side session and the `errorCode`
|
||||
option is `0x00` (No Error), a "graceful" shutdown will be initiated. During a
|
||||
"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.
|
||||
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
|
||||
remote peer to begin preparing for the connection to be terminated.
|
||||
|
||||
```js
|
||||
session.shutdown({
|
||||
graceful: true,
|
||||
session.close({
|
||||
opaqueData: Buffer.from('add some debugging data here')
|
||||
}, () => session.destroy());
|
||||
```
|
||||
@ -627,7 +660,7 @@ is not yet ready for use.
|
||||
All [`Http2Stream`][] instances are destroyed either when:
|
||||
|
||||
* 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.
|
||||
|
||||
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,
|
||||
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
|
||||
<!-- YAML
|
||||
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
|
||||
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)
|
||||
<!-- YAML
|
||||
added: v8.4.0
|
||||
@ -760,66 +826,9 @@ added: v8.4.0
|
||||
|
||||
Set to the `RST_STREAM` [error code][] reported when the `Http2Stream` is
|
||||
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.
|
||||
|
||||
#### 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
|
||||
<!-- YAML
|
||||
added: v8.4.0
|
||||
@ -842,11 +851,11 @@ added: v8.4.0
|
||||
```js
|
||||
const http2 = require('http2');
|
||||
const client = http2.connect('http://example.org:8000');
|
||||
|
||||
const { NGHTTP2_CANCEL } = http2.constants;
|
||||
const req = client.request({ ':path': '/' });
|
||||
|
||||
// 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
|
||||
@ -1264,16 +1273,49 @@ added: v8.4.0
|
||||
|
||||
In `Http2Server`, there is no `'clientError'` event as there is in
|
||||
HTTP1. However, there are `'socketError'`, `'sessionError'`, and
|
||||
`'streamError'`, for error happened on the socket, session, or stream
|
||||
respectively.
|
||||
`'streamError'`, for errors emitted on the socket, `Http2Session`, or
|
||||
`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
|
||||
added: v8.4.0
|
||||
-->
|
||||
|
||||
The `'socketError'` event is emitted when a `'socketError'` event is emitted by
|
||||
an `Http2Session` associated with the server.
|
||||
* `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: 'session'
|
||||
<!-- YAML
|
||||
added: v8.4.0
|
||||
-->
|
||||
|
||||
The `'session'` event is emitted when a new `Http2Session` is created by the
|
||||
`Http2Server`.
|
||||
|
||||
#### Event: 'sessionError'
|
||||
<!-- YAML
|
||||
@ -1281,16 +1323,13 @@ added: v8.4.0
|
||||
-->
|
||||
|
||||
The `'sessionError'` event is emitted when an `'error'` event is emitted by
|
||||
an `Http2Session` object. If no listener is registered for this event, an
|
||||
`'error'` event is emitted.
|
||||
an `Http2Session` object associated with the `Http2Server`.
|
||||
|
||||
#### Event: 'streamError'
|
||||
<!-- YAML
|
||||
added: v8.5.0
|
||||
-->
|
||||
|
||||
* `socket` {http2.ServerHttp2Stream}
|
||||
|
||||
If an `ServerHttp2Stream` emits an `'error'` event, it will be forwarded here.
|
||||
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'
|
||||
<!-- YAML
|
||||
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
|
||||
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
|
||||
<!-- YAML
|
||||
added: v8.4.0
|
||||
@ -1379,16 +1385,7 @@ added: v8.4.0
|
||||
-->
|
||||
|
||||
The `'sessionError'` event is emitted when an `'error'` event is emitted by
|
||||
an `Http2Session` object. If no listener is registered for this event, an
|
||||
`'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.
|
||||
an `Http2Session` object associated with the `Http2SecureServer`.
|
||||
|
||||
#### Event: 'unknownProtocol'
|
||||
<!-- YAML
|
||||
@ -1682,7 +1679,7 @@ const client = http2.connect('https://localhost:1234');
|
||||
|
||||
/** use the client **/
|
||||
|
||||
client.destroy();
|
||||
client.close();
|
||||
```
|
||||
|
||||
### http2.constants
|
||||
@ -1938,6 +1935,7 @@ An HTTP/2 CONNECT proxy:
|
||||
|
||||
```js
|
||||
const http2 = require('http2');
|
||||
const { NGHTTP2_REFUSED_STREAM } = http2.constants;
|
||||
const net = require('net');
|
||||
const { URL } = require('url');
|
||||
|
||||
@ -1945,7 +1943,7 @@ const proxy = http2.createServer();
|
||||
proxy.on('stream', (stream, headers) => {
|
||||
if (headers[':method'] !== 'CONNECT') {
|
||||
// Only accept CONNECT requests
|
||||
stream.rstWithRefused();
|
||||
stream.close(NGHTTP2_REFUSED_STREAM);
|
||||
return;
|
||||
}
|
||||
const auth = new URL(`tcp://${headers[':authority']}`);
|
||||
@ -1957,7 +1955,7 @@ proxy.on('stream', (stream, headers) => {
|
||||
stream.pipe(socket);
|
||||
});
|
||||
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('end', () => {
|
||||
console.log(`The server says: ${data}`);
|
||||
client.destroy();
|
||||
client.close();
|
||||
});
|
||||
req.end('Jane');
|
||||
```
|
||||
|
@ -307,6 +307,8 @@ E('ERR_ENCODING_INVALID_ENCODED_DATA',
|
||||
'The encoded data was not valid for encoding %s');
|
||||
E('ERR_ENCODING_NOT_SUPPORTED', 'The "%s" encoding is not supported');
|
||||
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',
|
||||
':authority header is required for CONNECT requests');
|
||||
E('ERR_HTTP2_CONNECT_PATH',
|
||||
@ -321,6 +323,8 @@ E('ERR_HTTP2_FRAME_ERROR',
|
||||
msg += ` with code ${code}`;
|
||||
return msg;
|
||||
});
|
||||
E('ERR_HTTP2_GOAWAY_SESSION',
|
||||
'New streams cannot be created after receiving a GOAWAY');
|
||||
E('ERR_HTTP2_HEADERS_AFTER_RESPOND',
|
||||
'Cannot specify additional headers after response initiated');
|
||||
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_PUSH_DISABLED', 'HTTP/2 client has disabled push streams');
|
||||
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',
|
||||
'The socket is already bound to an Http2Session');
|
||||
E('ERR_HTTP2_STATUS_101',
|
||||
'HTTP status code 101 (Switching Protocols) is forbidden in HTTP/2');
|
||||
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_SELF_DEPENDENCY', 'A stream cannot depend on itself');
|
||||
E('ERR_HTTP2_UNSUPPORTED_PROTOCOL', 'protocol "%s" is unsupported.');
|
||||
|
@ -126,14 +126,11 @@ function onStreamAbortedRequest() {
|
||||
const request = this[kRequest];
|
||||
if (request !== undefined && request[kState].closed === false) {
|
||||
request.emit('aborted');
|
||||
request.emit('close');
|
||||
}
|
||||
}
|
||||
|
||||
function onStreamAbortedResponse() {
|
||||
const response = this[kResponse];
|
||||
if (response !== undefined && response[kState].closed === false)
|
||||
response.emit('close');
|
||||
// non-op for now
|
||||
}
|
||||
|
||||
function resumeStream(stream) {
|
||||
@ -234,9 +231,7 @@ class Http2ServerRequest extends Readable {
|
||||
stream.on('end', onStreamEnd);
|
||||
stream.on('error', onStreamError);
|
||||
stream.on('aborted', onStreamAbortedRequest);
|
||||
const onfinish = this[kFinish].bind(this);
|
||||
stream.on('close', onfinish);
|
||||
stream.on('finish', onfinish);
|
||||
stream.on('close', this[kFinish].bind(this));
|
||||
this.on('pause', onRequestPause);
|
||||
this.on('resume', onRequestResume);
|
||||
}
|
||||
@ -297,7 +292,7 @@ class Http2ServerRequest extends Readable {
|
||||
state.didRead = true;
|
||||
process.nextTick(resumeStream, this[kStream]);
|
||||
} 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
|
||||
if (!state.didRead && !this._readableState.resumeScheduled)
|
||||
this.resume();
|
||||
this.emit('close');
|
||||
}
|
||||
}
|
||||
|
||||
@ -366,9 +362,7 @@ class Http2ServerResponse extends Stream {
|
||||
this.writable = true;
|
||||
stream.on('drain', onStreamDrain);
|
||||
stream.on('aborted', onStreamAbortedResponse);
|
||||
const onfinish = this[kFinish].bind(this);
|
||||
stream.on('close', onfinish);
|
||||
stream.on('finish', onfinish);
|
||||
stream.on('close', this[kFinish].bind(this));
|
||||
}
|
||||
|
||||
// User land modules such as finalhandler just check truthiness of this
|
||||
@ -520,7 +514,7 @@ class Http2ServerResponse extends Stream {
|
||||
const state = this[kState];
|
||||
|
||||
if (state.closed)
|
||||
throw new errors.Error('ERR_HTTP2_STREAM_CLOSED');
|
||||
throw new errors.Error('ERR_HTTP2_INVALID_STREAM');
|
||||
if (this[kStream].headersSent)
|
||||
throw new errors.Error('ERR_HTTP2_HEADERS_SENT');
|
||||
|
||||
@ -550,7 +544,7 @@ class Http2ServerResponse extends Stream {
|
||||
}
|
||||
|
||||
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')
|
||||
process.nextTick(cb, err);
|
||||
else
|
||||
@ -620,12 +614,15 @@ class Http2ServerResponse extends Stream {
|
||||
if (typeof callback !== 'function')
|
||||
throw new errors.TypeError('ERR_INVALID_CALLBACK');
|
||||
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;
|
||||
}
|
||||
this[kStream].pushStream(headers, {}, function(stream, headers, options) {
|
||||
const response = new Http2ServerResponse(stream);
|
||||
callback(null, response);
|
||||
this[kStream].pushStream(headers, {}, (err, stream, headers, options) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
callback(null, new Http2ServerResponse(stream));
|
||||
});
|
||||
}
|
||||
|
||||
@ -649,6 +646,7 @@ class Http2ServerResponse extends Stream {
|
||||
this[kProxySocket] = null;
|
||||
stream[kResponse] = undefined;
|
||||
this.emit('finish');
|
||||
this.emit('close');
|
||||
}
|
||||
|
||||
// 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)
|
||||
#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
|
||||
|
||||
// These are the standard HTTP/2 defaults as specified by the RFC
|
||||
#define DEFAULT_SETTINGS_HEADER_TABLE_SIZE 4096
|
||||
#define DEFAULT_SETTINGS_ENABLE_PUSH 1
|
||||
#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 DEFAULT_MAX_HEADER_LIST_PAIRS 128u
|
||||
|
||||
struct nghttp2_stream_write_t;
|
||||
|
||||
#define MAX_BUFFER_COUNT 16
|
||||
|
||||
struct nghttp2_stream_write_t;
|
||||
|
||||
enum nghttp2_session_type {
|
||||
NGHTTP2_SESSION_SERVER,
|
||||
NGHTTP2_SESSION_CLIENT
|
||||
@ -109,11 +115,15 @@ enum nghttp2_stream_flags {
|
||||
// Stream is destroyed
|
||||
NGHTTP2_STREAM_FLAG_DESTROYED = 0x10,
|
||||
// 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 {
|
||||
// Stream is not going to have any DATA frames
|
||||
STREAM_OPTION_EMPTY_PAYLOAD = 0x1,
|
||||
// Stream might have trailing headers
|
||||
STREAM_OPTION_GET_TRAILERS = 0x2,
|
||||
};
|
||||
|
||||
@ -136,7 +146,6 @@ struct nghttp2_header {
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct nghttp2_stream_write_t {
|
||||
void* data;
|
||||
int status;
|
||||
@ -417,9 +426,10 @@ const char* nghttp2_errname(int rv) {
|
||||
|
||||
enum session_state_flags {
|
||||
SESSION_STATE_NONE = 0x0,
|
||||
SESSION_STATE_DESTROYING = 0x1,
|
||||
SESSION_STATE_HAS_SCOPE = 0x2,
|
||||
SESSION_STATE_WRITE_SCHEDULED = 0x4
|
||||
SESSION_STATE_HAS_SCOPE = 0x1,
|
||||
SESSION_STATE_WRITE_SCHEDULED = 0x2,
|
||||
SESSION_STATE_CLOSED = 0x4,
|
||||
SESSION_STATE_SENDING = 0x8,
|
||||
};
|
||||
|
||||
// This allows for 4 default-sized frames with their frame headers
|
||||
@ -555,6 +565,8 @@ class Http2Stream : public AsyncWrap,
|
||||
unsigned int nbufs,
|
||||
nghttp2_stream_write_cb cb);
|
||||
|
||||
inline bool HasDataChunks(bool ignore_eos = false);
|
||||
|
||||
inline void AddChunk(const uint8_t* data, size_t len);
|
||||
|
||||
inline void FlushDataChunks();
|
||||
@ -592,7 +604,7 @@ class Http2Stream : public AsyncWrap,
|
||||
bool silent = false);
|
||||
|
||||
// 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.
|
||||
inline Http2Stream* SubmitPushPromise(
|
||||
@ -799,9 +811,11 @@ class Http2Session : public AsyncWrap {
|
||||
|
||||
void Start();
|
||||
void Stop();
|
||||
void Close();
|
||||
void Close(uint32_t code = NGHTTP2_NO_ERROR,
|
||||
bool socket_closed = false);
|
||||
void Consume(Local<External> external);
|
||||
void Unconsume();
|
||||
void Goaway(uint32_t code, int32_t lastStreamID, uint8_t* data, size_t len);
|
||||
|
||||
bool Ping(v8::Local<v8::Function> function);
|
||||
|
||||
@ -827,8 +841,9 @@ class Http2Session : public AsyncWrap {
|
||||
|
||||
inline const char* TypeName();
|
||||
|
||||
inline void MarkDestroying() { flags_ |= SESSION_STATE_DESTROYING; }
|
||||
inline bool IsDestroying() { return flags_ & SESSION_STATE_DESTROYING; }
|
||||
inline bool IsDestroyed() {
|
||||
return (flags_ & SESSION_STATE_CLOSED) || session_ == nullptr;
|
||||
}
|
||||
|
||||
// Schedule a write if nghttp2 indicates it wants to write to the socket.
|
||||
void MaybeScheduleWrite();
|
||||
@ -842,9 +857,6 @@ class Http2Session : public AsyncWrap {
|
||||
// Removes a stream instance from this session
|
||||
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.
|
||||
inline void Settings(const nghttp2_settings_entry iv[], size_t niv);
|
||||
|
||||
@ -868,6 +880,7 @@ class Http2Session : public AsyncWrap {
|
||||
const uv_buf_t* bufs,
|
||||
uv_handle_type pending,
|
||||
void* ctx);
|
||||
static void OnStreamDestructImpl(void* ctx);
|
||||
|
||||
// The JavaScript API
|
||||
static void New(const FunctionCallbackInfo<Value>& args);
|
||||
@ -878,7 +891,6 @@ class Http2Session : public AsyncWrap {
|
||||
static void Settings(const FunctionCallbackInfo<Value>& args);
|
||||
static void Request(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 UpdateChunksSent(const FunctionCallbackInfo<Value>& args);
|
||||
static void RefreshState(const FunctionCallbackInfo<Value>& args);
|
||||
|
@ -5,53 +5,37 @@ if (!common.hasCrypto)
|
||||
common.skip('missing crypto');
|
||||
const assert = require('assert');
|
||||
const http2 = require('http2');
|
||||
const Countdown = require('../common/countdown');
|
||||
|
||||
const server = http2.createServer();
|
||||
server.on('stream', common.mustCall((stream, headers, flags) => {
|
||||
const port = server.address().port;
|
||||
if (headers[':path'] === '/') {
|
||||
stream.pushStream({
|
||||
':scheme': 'http',
|
||||
':path': '/foobar',
|
||||
':authority': `localhost:${port}`,
|
||||
}, (push, headers) => {
|
||||
stream.pushStream({ ':path': '/foobar' }, (err, push, headers) => {
|
||||
assert.ifError(err);
|
||||
push.respond({
|
||||
'content-type': 'text/html',
|
||||
':status': 200,
|
||||
'x-push-data': 'pushed by server',
|
||||
});
|
||||
push.write('pushed by server ');
|
||||
// Sending in next immediate ensures that a second data frame
|
||||
// will be sent to the client, which will cause the 'data' event
|
||||
// to fire multiple times.
|
||||
setImmediate(() => {
|
||||
push.end('data');
|
||||
});
|
||||
setImmediate(() => push.end('data'));
|
||||
stream.end('st');
|
||||
});
|
||||
}
|
||||
stream.respond({
|
||||
'content-type': 'text/html',
|
||||
':status': 200
|
||||
});
|
||||
stream.respond({ 'content-type': 'text/html' });
|
||||
stream.write('te');
|
||||
}));
|
||||
|
||||
|
||||
server.listen(0, common.mustCall(() => {
|
||||
const port = server.address().port;
|
||||
const headers = { ':path': '/' };
|
||||
const client = http2.connect(`http://localhost:${port}`);
|
||||
|
||||
const req = client.request(headers);
|
||||
const req = client.request();
|
||||
|
||||
let expected = 2;
|
||||
function maybeClose() {
|
||||
if (--expected === 0) {
|
||||
server.close();
|
||||
client.destroy();
|
||||
}
|
||||
}
|
||||
const countdown = new Countdown(2, () => {
|
||||
server.close();
|
||||
client.close();
|
||||
});
|
||||
|
||||
req.on('response', common.mustCall((headers) => {
|
||||
assert.strictEqual(headers[':status'], 200);
|
||||
@ -70,13 +54,11 @@ server.listen(0, common.mustCall(() => {
|
||||
|
||||
stream.setEncoding('utf8');
|
||||
let pushData = '';
|
||||
stream.on('data', common.mustCall((d) => {
|
||||
pushData += d;
|
||||
}, 2));
|
||||
stream.on('data', (d) => pushData += d);
|
||||
stream.on('end', common.mustCall(() => {
|
||||
assert.strictEqual(pushData, 'pushed by server data');
|
||||
maybeClose();
|
||||
}));
|
||||
stream.on('close', () => countdown.dec());
|
||||
}));
|
||||
|
||||
let data = '';
|
||||
@ -85,7 +67,6 @@ server.listen(0, common.mustCall(() => {
|
||||
req.on('data', common.mustCallAtLeast((d) => data += d));
|
||||
req.on('end', common.mustCall(() => {
|
||||
assert.strictEqual(data, 'test');
|
||||
maybeClose();
|
||||
}));
|
||||
req.end();
|
||||
req.on('close', () => countdown.dec());
|
||||
}));
|
||||
|
@ -8,139 +8,115 @@ if (!common.hasCrypto)
|
||||
const assert = require('assert');
|
||||
const h2 = require('http2');
|
||||
const { kSocket } = require('internal/http2/util');
|
||||
const Countdown = require('../common/countdown');
|
||||
|
||||
{
|
||||
const server = h2.createServer();
|
||||
server.listen(
|
||||
0,
|
||||
common.mustCall(() => {
|
||||
const destroyCallbacks = [
|
||||
(client) => client.destroy(),
|
||||
(client) => client[kSocket].destroy()
|
||||
];
|
||||
server.listen(0, common.mustCall(() => {
|
||||
const destroyCallbacks = [
|
||||
(client) => client.destroy(),
|
||||
(client) => client[kSocket].destroy()
|
||||
];
|
||||
|
||||
let remaining = destroyCallbacks.length;
|
||||
const countdown = new Countdown(destroyCallbacks.length, () => {
|
||||
server.close();
|
||||
});
|
||||
|
||||
destroyCallbacks.forEach((destroyCallback) => {
|
||||
const client = h2.connect(`http://localhost:${server.address().port}`);
|
||||
client.on(
|
||||
'connect',
|
||||
common.mustCall(() => {
|
||||
const socket = client[kSocket];
|
||||
destroyCallbacks.forEach((destroyCallback) => {
|
||||
const client = h2.connect(`http://localhost:${server.address().port}`);
|
||||
client.on('connect', common.mustCall(() => {
|
||||
const socket = client[kSocket];
|
||||
|
||||
assert(socket, 'client session has associated socket');
|
||||
assert(
|
||||
!client.destroyed,
|
||||
'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, 'client session has associated socket');
|
||||
assert(
|
||||
!client.destroyed,
|
||||
'client has not been destroyed before destroy is called'
|
||||
);
|
||||
});
|
||||
})
|
||||
);
|
||||
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
|
||||
{
|
||||
const server = h2.createServer();
|
||||
server.listen(
|
||||
0,
|
||||
common.mustCall(() => {
|
||||
const client = h2.connect(`http://localhost:${server.address().port}`);
|
||||
const req = client.request();
|
||||
client.destroy();
|
||||
server.listen(0, common.mustCall(() => {
|
||||
const client = h2.connect(`http://localhost:${server.address().port}`);
|
||||
const socket = client[kSocket];
|
||||
socket.on('close', common.mustCall(() => {
|
||||
assert(socket.destroyed);
|
||||
}));
|
||||
|
||||
req.on('response', common.mustNotCall());
|
||||
req.resume();
|
||||
|
||||
const sessionError = {
|
||||
type: Error,
|
||||
code: 'ERR_HTTP2_INVALID_SESSION',
|
||||
message: 'The session has been destroyed'
|
||||
};
|
||||
const req = client.request();
|
||||
req.on('error', common.expectsError({
|
||||
code: 'ERR_HTTP2_STREAM_CANCEL',
|
||||
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.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
|
||||
// so that state.destroyed is set to true
|
||||
setImmediate(() => {
|
||||
common.expectsError(() => client.request(), sessionError);
|
||||
common.expectsError(() => client.settings({}), sessionError);
|
||||
common.expectsError(() => client.shutdown(), sessionError);
|
||||
});
|
||||
|
||||
req.on(
|
||||
'end',
|
||||
common.mustCall(() => {
|
||||
server.close();
|
||||
})
|
||||
);
|
||||
req.end();
|
||||
})
|
||||
);
|
||||
req.resume();
|
||||
req.on('end', common.mustCall());
|
||||
req.on('close', common.mustCall(() => server.close()));
|
||||
}));
|
||||
}
|
||||
|
||||
// test destroy before goaway
|
||||
{
|
||||
const server = h2.createServer();
|
||||
server.on(
|
||||
'stream',
|
||||
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}`);
|
||||
server.on('stream', common.mustCall((stream) => {
|
||||
stream.session.destroy();
|
||||
}));
|
||||
|
||||
client.on(
|
||||
'goaway',
|
||||
common.mustCall(() => {
|
||||
// We ought to be able to destroy the client in here without an error
|
||||
server.close();
|
||||
client.destroy();
|
||||
})
|
||||
);
|
||||
server.listen(0, common.mustCall(() => {
|
||||
const client = h2.connect(`http://localhost:${server.address().port}`);
|
||||
// On some platforms (e.g. windows), an ECONNRESET may occur at this
|
||||
// point -- or it may not. Do not make this a mustCall
|
||||
client.on('error', () => {});
|
||||
|
||||
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 http2 = require('http2');
|
||||
|
||||
// Creating an http1 server here...
|
||||
const server = http.createServer(common.mustNotCall());
|
||||
|
||||
server.listen(0, common.mustCall(() => {
|
||||
@ -15,13 +16,17 @@ server.listen(0, common.mustCall(() => {
|
||||
const req = client.request();
|
||||
req.on('close', common.mustCall());
|
||||
|
||||
req.on('error', common.expectsError({
|
||||
code: 'ERR_HTTP2_ERROR',
|
||||
type: Error,
|
||||
message: 'Protocol error'
|
||||
}));
|
||||
|
||||
client.on('error', common.expectsError({
|
||||
code: 'ERR_HTTP2_ERROR',
|
||||
type: Error,
|
||||
message: 'Protocol error'
|
||||
}));
|
||||
|
||||
client.on('close', (...args) => {
|
||||
server.close();
|
||||
});
|
||||
client.on('close', common.mustCall(() => server.close()));
|
||||
}));
|
||||
|
@ -1,13 +1,14 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
if (!common.hasCrypto)
|
||||
common.skip('missing crypto');
|
||||
|
||||
const {
|
||||
constants,
|
||||
Http2Session,
|
||||
nghttp2ErrorString
|
||||
} = process.binding('http2');
|
||||
const common = require('../common');
|
||||
if (!common.hasCrypto)
|
||||
common.skip('missing crypto');
|
||||
const http2 = require('http2');
|
||||
|
||||
// tests error handling within requestOnConnect
|
||||
@ -69,6 +70,8 @@ server.listen(0, common.mustCall(() => runTest(tests.shift())));
|
||||
|
||||
function runTest(test) {
|
||||
const client = http2.connect(`http://localhost:${server.address().port}`);
|
||||
client.on('close', common.mustCall());
|
||||
|
||||
const req = client.request({ ':method': 'POST' });
|
||||
|
||||
currentError = test.ngError;
|
||||
@ -83,15 +86,15 @@ function runTest(test) {
|
||||
if (test.type === 'stream') {
|
||||
client.on('error', errorMustNotCall);
|
||||
req.on('error', errorMustCall);
|
||||
req.on('error', common.mustCall(() => {
|
||||
client.destroy();
|
||||
}));
|
||||
} else {
|
||||
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();
|
||||
|
||||
if (!tests.length) {
|
||||
|
@ -16,4 +16,10 @@ net.connect = common.mustCall((...args) => {
|
||||
});
|
||||
|
||||
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();
|
||||
|
||||
// we use the lower-level API here
|
||||
server.on('stream', common.mustCall(onStream));
|
||||
|
||||
function onStream(stream, headers, flags) {
|
||||
stream.respond({
|
||||
'content-type': 'text/html',
|
||||
':status': 200
|
||||
});
|
||||
stream.end('hello world');
|
||||
}
|
||||
|
||||
server.listen(0);
|
||||
|
||||
server.on('listening', common.mustCall(() => {
|
||||
server.on('stream', common.mustCall((stream) => {
|
||||
stream.respond();
|
||||
stream.end('ok');
|
||||
}));
|
||||
|
||||
server.listen(0, common.mustCall(() => {
|
||||
const client = h2.connect(`http://localhost:${server.address().port}`);
|
||||
|
||||
const req = client.request({ ':path': '/' });
|
||||
const req = client.request();
|
||||
req.priority({});
|
||||
|
||||
req.on('response', common.mustCall());
|
||||
req.resume();
|
||||
req.on('end', common.mustCall(() => {
|
||||
req.on('end', common.mustCall());
|
||||
req.on('close', common.mustCall(() => {
|
||||
server.close();
|
||||
client.destroy();
|
||||
client.close();
|
||||
}));
|
||||
req.end();
|
||||
|
||||
}));
|
||||
|
@ -15,7 +15,6 @@ server.on('stream', common.mustCall((stream) => {
|
||||
stream.end('ok');
|
||||
}));
|
||||
server.listen(0, common.mustCall(() => {
|
||||
|
||||
const connect = util.promisify(http2.connect);
|
||||
|
||||
connect(`http://localhost:${server.address().port}`)
|
||||
@ -28,7 +27,7 @@ server.listen(0, common.mustCall(() => {
|
||||
req.on('data', (chunk) => data += chunk);
|
||||
req.on('end', common.mustCall(() => {
|
||||
assert.strictEqual(data, 'ok');
|
||||
client.destroy();
|
||||
client.close();
|
||||
server.close();
|
||||
}));
|
||||
}));
|
||||
|
@ -33,29 +33,27 @@ server.listen(0, common.mustCall(() => {
|
||||
const port = server.address().port;
|
||||
const client = http2.connect(`http://localhost:${port}`);
|
||||
|
||||
Object.keys(optionsToTest).forEach((option) => {
|
||||
Object.keys(types).forEach((type) => {
|
||||
if (type === optionsToTest[option]) {
|
||||
return;
|
||||
}
|
||||
client.on('connect', () => {
|
||||
Object.keys(optionsToTest).forEach((option) => {
|
||||
Object.keys(types).forEach((type) => {
|
||||
if (type === optionsToTest[option])
|
||||
return;
|
||||
|
||||
common.expectsError(
|
||||
() => client.request({
|
||||
':method': 'CONNECT',
|
||||
':authority': `localhost:${port}`
|
||||
}, {
|
||||
[option]: types[type]
|
||||
}),
|
||||
{
|
||||
type: TypeError,
|
||||
code: 'ERR_INVALID_OPT_VALUE',
|
||||
message: `The value "${String(types[type])}" is invalid ` +
|
||||
`for option "${option}"`
|
||||
}
|
||||
);
|
||||
common.expectsError(
|
||||
() => client.request({
|
||||
':method': 'CONNECT',
|
||||
':authority': `localhost:${port}`
|
||||
}, {
|
||||
[option]: types[type]
|
||||
}), {
|
||||
type: TypeError,
|
||||
code: 'ERR_INVALID_OPT_VALUE',
|
||||
message: `The value "${String(types[type])}" is invalid ` +
|
||||
`for option "${option}"`
|
||||
});
|
||||
});
|
||||
});
|
||||
server.close();
|
||||
client.close();
|
||||
});
|
||||
|
||||
server.close();
|
||||
client.destroy();
|
||||
}));
|
||||
|
@ -8,33 +8,37 @@ const h2 = require('http2');
|
||||
|
||||
const server = h2.createServer();
|
||||
server.on('stream', (stream) => {
|
||||
stream.on('close', common.mustCall());
|
||||
stream.respond();
|
||||
stream.end('ok');
|
||||
});
|
||||
|
||||
server.listen(0);
|
||||
|
||||
server.on('listening', common.mustCall(() => {
|
||||
|
||||
server.listen(0, common.mustCall(() => {
|
||||
const client = h2.connect(`http://localhost:${server.address().port}`);
|
||||
|
||||
const req = client.request({ ':path': '/' });
|
||||
req.rstStream(0);
|
||||
const req = client.request();
|
||||
req.close(1);
|
||||
assert.strictEqual(req.closed, true);
|
||||
|
||||
// make sure that destroy is called
|
||||
req._destroy = common.mustCall(req._destroy.bind(req));
|
||||
|
||||
// second call doesn't do anything
|
||||
assert.doesNotThrow(() => req.rstStream(8));
|
||||
assert.doesNotThrow(() => req.close(8));
|
||||
|
||||
req.on('close', common.mustCall((code) => {
|
||||
assert.strictEqual(req.destroyed, true);
|
||||
assert.strictEqual(code, 0);
|
||||
assert.strictEqual(code, 1);
|
||||
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.on('end', common.mustCall());
|
||||
req.end();
|
||||
|
@ -10,26 +10,20 @@ const checkWeight = (actual, expect) => {
|
||||
const server = http2.createServer();
|
||||
server.on('stream', common.mustCall((stream, headers, flags) => {
|
||||
assert.strictEqual(stream.state.weight, expect);
|
||||
stream.respond({
|
||||
'content-type': 'text/html',
|
||||
':status': 200
|
||||
});
|
||||
stream.respond();
|
||||
stream.end('test');
|
||||
}));
|
||||
|
||||
server.listen(0, common.mustCall(() => {
|
||||
const port = server.address().port;
|
||||
const client = http2.connect(`http://localhost:${port}`);
|
||||
const client = http2.connect(`http://localhost:${server.address().port}`);
|
||||
const req = client.request({}, { weight: actual });
|
||||
|
||||
const headers = { ':path': '/' };
|
||||
const req = client.request(headers, { weight: actual });
|
||||
|
||||
req.on('data', common.mustCall(() => {}));
|
||||
req.on('end', common.mustCall(() => {
|
||||
req.on('data', common.mustCall());
|
||||
req.on('end', common.mustCall());
|
||||
req.on('close', common.mustCall(() => {
|
||||
server.close();
|
||||
client.destroy();
|
||||
client.close();
|
||||
}));
|
||||
req.end();
|
||||
}));
|
||||
};
|
||||
|
||||
|
@ -3,62 +3,53 @@
|
||||
const common = require('../common');
|
||||
if (!common.hasCrypto)
|
||||
common.skip('missing crypto');
|
||||
const assert = require('assert');
|
||||
const h2 = require('http2');
|
||||
|
||||
const server = h2.createServer();
|
||||
|
||||
// we use the lower-level API here
|
||||
server.on('stream', common.mustCall(onStream));
|
||||
|
||||
function onStream(stream, headers, flags) {
|
||||
stream.respond({
|
||||
'content-type': 'text/html',
|
||||
':status': 200
|
||||
});
|
||||
stream.end('hello world');
|
||||
}
|
||||
|
||||
server.listen(0);
|
||||
|
||||
server.on('listening', common.mustCall(() => {
|
||||
server.on('stream', common.mustCall((stream, headers, flags) => {
|
||||
stream.respond();
|
||||
stream.end('ok');
|
||||
}));
|
||||
server.on('session', common.mustCall((session) => {
|
||||
session.on('remoteSettings', common.mustCall(2));
|
||||
}));
|
||||
|
||||
server.listen(0, common.mustCall(() => {
|
||||
const client = h2.connect(`http://localhost:${server.address().port}`);
|
||||
|
||||
assert.throws(() => client.settings({ headerTableSize: -1 }),
|
||||
RangeError);
|
||||
assert.throws(() => client.settings({ headerTableSize: 2 ** 32 }),
|
||||
RangeError);
|
||||
assert.throws(() => client.settings({ initialWindowSize: -1 }),
|
||||
RangeError);
|
||||
assert.throws(() => client.settings({ initialWindowSize: 2 ** 32 }),
|
||||
RangeError);
|
||||
assert.throws(() => client.settings({ maxFrameSize: 1 }),
|
||||
RangeError);
|
||||
assert.throws(() => client.settings({ maxFrameSize: 2 ** 24 }),
|
||||
RangeError);
|
||||
assert.throws(() => client.settings({ maxConcurrentStreams: -1 }),
|
||||
RangeError);
|
||||
assert.throws(() => client.settings({ maxConcurrentStreams: 2 ** 31 }),
|
||||
RangeError);
|
||||
assert.throws(() => client.settings({ maxHeaderListSize: -1 }),
|
||||
RangeError);
|
||||
assert.throws(() => client.settings({ maxHeaderListSize: 2 ** 32 }),
|
||||
RangeError);
|
||||
['a', 1, 0, null, {}].forEach((i) => {
|
||||
assert.throws(() => client.settings({ enablePush: i }), TypeError);
|
||||
[
|
||||
['headerTableSize', -1, RangeError],
|
||||
['headerTableSize', 2 ** 32, RangeError],
|
||||
['initialWindowSize', -1, RangeError],
|
||||
['initialWindowSize', 2 ** 32, RangeError],
|
||||
['maxFrameSize', 1, RangeError],
|
||||
['maxFrameSize', 2 ** 24, RangeError],
|
||||
['maxConcurrentStreams', -1, RangeError],
|
||||
['maxConcurrentStreams', 2 ** 31, RangeError],
|
||||
['maxHeaderListSize', -1, RangeError],
|
||||
['maxHeaderListSize', 2 ** 32, RangeError],
|
||||
['enablePush', 'a', TypeError],
|
||||
['enablePush', 1, TypeError],
|
||||
['enablePush', 0, TypeError],
|
||||
['enablePush', null, TypeError],
|
||||
['enablePush', {}, TypeError]
|
||||
].forEach((i) => {
|
||||
common.expectsError(
|
||||
() => client.settings({ [i[0]]: i[1] }),
|
||||
{
|
||||
code: 'ERR_HTTP2_INVALID_SETTING_VALUE',
|
||||
type: i[2] });
|
||||
});
|
||||
|
||||
client.settings({ maxFrameSize: 1234567 });
|
||||
|
||||
const req = client.request({ ':path': '/' });
|
||||
|
||||
const req = client.request();
|
||||
req.on('response', common.mustCall());
|
||||
req.resume();
|
||||
req.on('end', common.mustCall(() => {
|
||||
req.on('close', common.mustCall(() => {
|
||||
server.close();
|
||||
client.destroy();
|
||||
client.close();
|
||||
}));
|
||||
req.end();
|
||||
|
||||
}));
|
||||
|
@ -10,15 +10,7 @@ const server = h2.createServer();
|
||||
// we use the lower-level API here
|
||||
server.on('stream', common.mustNotCall());
|
||||
|
||||
server.listen(0);
|
||||
|
||||
server.on('listening', common.mustCall(() => {
|
||||
|
||||
server.listen(0, common.mustCall(() => {
|
||||
const client = h2.connect(`http://localhost:${server.address().port}`);
|
||||
|
||||
client.shutdown({ graceful: true }, common.mustCall(() => {
|
||||
server.close();
|
||||
client.destroy();
|
||||
}));
|
||||
|
||||
client.close(common.mustCall(() => server.close()));
|
||||
}));
|
||||
|
@ -14,38 +14,27 @@ const body =
|
||||
const server = h2.createServer();
|
||||
|
||||
// we use the lower-level API here
|
||||
server.on('stream', common.mustCall(onStream));
|
||||
|
||||
function onStream(stream) {
|
||||
// The stream aborted event must have been triggered
|
||||
server.on('stream', common.mustCall((stream) => {
|
||||
stream.on('aborted', common.mustCall());
|
||||
|
||||
stream.respond({
|
||||
'content-type': 'text/html',
|
||||
':status': 200
|
||||
});
|
||||
stream.on('close', common.mustCall());
|
||||
stream.respond();
|
||||
stream.write(body);
|
||||
}
|
||||
// purposefully do not end()
|
||||
}));
|
||||
|
||||
server.listen(0);
|
||||
|
||||
server.on('listening', common.mustCall(function() {
|
||||
server.listen(0, common.mustCall(function() {
|
||||
const client = h2.connect(`http://localhost:${this.address().port}`);
|
||||
|
||||
const req = client.request({ ':path': '/' });
|
||||
const req = client.request();
|
||||
|
||||
req.on('response', common.mustCall(() => {
|
||||
// send a premature socket close
|
||||
client[kSocket].destroy();
|
||||
}));
|
||||
req.on('data', common.mustNotCall());
|
||||
|
||||
req.on('end', common.mustCall(() => {
|
||||
server.close();
|
||||
}));
|
||||
req.resume();
|
||||
req.on('end', common.mustCall());
|
||||
req.on('close', common.mustCall(() => server.close()));
|
||||
|
||||
// On the client, the close event must call
|
||||
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.message, 'Stream closed with error code 2');
|
||||
});
|
||||
stream.respond({});
|
||||
stream.respond();
|
||||
stream.end();
|
||||
});
|
||||
|
||||
server.listen(0);
|
||||
|
||||
server.on('listening', common.mustCall(() => {
|
||||
|
||||
server.listen(0, common.mustCall(() => {
|
||||
const client = h2.connect(`http://localhost:${server.address().port}`);
|
||||
|
||||
const req = client.request({ ':path': '/' });
|
||||
const err = new Error('test');
|
||||
req.destroy(err);
|
||||
const req = client.request();
|
||||
req.destroy(new Error('test'));
|
||||
|
||||
req.on('error', common.mustCall((err) => {
|
||||
common.expectsError({
|
||||
type: Error,
|
||||
message: 'test'
|
||||
})(err);
|
||||
req.on('error', common.expectsError({
|
||||
type: Error,
|
||||
message: 'test'
|
||||
}));
|
||||
|
||||
req.on('close', common.mustCall((code) => {
|
||||
assert.strictEqual(req.rstCode, NGHTTP2_INTERNAL_ERROR);
|
||||
assert.strictEqual(code, NGHTTP2_INTERNAL_ERROR);
|
||||
server.close();
|
||||
client.destroy();
|
||||
client.close();
|
||||
}));
|
||||
|
||||
req.on('response', common.mustNotCall());
|
||||
req.resume();
|
||||
req.on('end', common.mustCall());
|
||||
|
||||
}));
|
||||
|
@ -4,6 +4,7 @@ const common = require('../common');
|
||||
if (!common.hasCrypto)
|
||||
common.skip('missing crypto');
|
||||
const http2 = require('http2');
|
||||
const Countdown = require('../common/countdown');
|
||||
|
||||
const server = http2.createServer();
|
||||
|
||||
@ -13,14 +14,12 @@ const count = 32;
|
||||
|
||||
server.listen(0, common.mustCall(() => {
|
||||
const client = http2.connect(`http://localhost:${server.address().port}`);
|
||||
client.setMaxListeners(33);
|
||||
|
||||
let remaining = count + 1;
|
||||
function maybeClose() {
|
||||
if (--remaining === 0) {
|
||||
server.close();
|
||||
client.destroy();
|
||||
}
|
||||
}
|
||||
const countdown = new Countdown(count + 1, () => {
|
||||
server.close();
|
||||
client.close();
|
||||
});
|
||||
|
||||
// nghttp2 will catch the bad header value for us.
|
||||
function doTest(i) {
|
||||
@ -30,7 +29,7 @@ server.listen(0, common.mustCall(() => {
|
||||
type: Error,
|
||||
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)
|
||||
|
@ -9,6 +9,7 @@ const assert = require('assert');
|
||||
const http2 = require('http2');
|
||||
const fs = require('fs');
|
||||
const fixtures = require('../common/fixtures');
|
||||
const Countdown = require('../common/countdown');
|
||||
|
||||
const loc = fixtures.path('person.jpg');
|
||||
let fileData;
|
||||
@ -34,20 +35,21 @@ fs.readFile(loc, common.mustCall((err, data) => {
|
||||
server.listen(0, common.mustCall(() => {
|
||||
const client = http2.connect(`http://localhost:${server.address().port}`);
|
||||
|
||||
let remaining = 2;
|
||||
function maybeClose() {
|
||||
if (--remaining === 0) {
|
||||
server.close();
|
||||
client.shutdown();
|
||||
}
|
||||
}
|
||||
const countdown = new Countdown(2, () => {
|
||||
server.close();
|
||||
client.close();
|
||||
});
|
||||
|
||||
const req = client.request({ ':method': 'POST' });
|
||||
req.on('response', common.mustCall());
|
||||
|
||||
req.resume();
|
||||
req.on('end', common.mustCall(maybeClose));
|
||||
req.on('end', common.mustCall());
|
||||
|
||||
req.on('finish', () => countdown.dec());
|
||||
const str = fs.createReadStream(loc);
|
||||
req.on('finish', common.mustCall(maybeClose));
|
||||
str.on('end', common.mustCall());
|
||||
str.on('close', () => countdown.dec());
|
||||
str.pipe(req);
|
||||
}));
|
||||
}));
|
||||
|
@ -8,47 +8,30 @@ const h2 = require('http2');
|
||||
|
||||
const server = h2.createServer();
|
||||
|
||||
const {
|
||||
HTTP2_HEADER_PATH,
|
||||
HTTP2_HEADER_METHOD,
|
||||
HTTP2_METHOD_POST
|
||||
} = h2.constants;
|
||||
|
||||
// we use the lower-level API here
|
||||
server.on('stream', common.mustCall(onStream));
|
||||
|
||||
function onStream(stream, headers, flags) {
|
||||
server.on('stream', common.mustCall((stream, headers, flags) => {
|
||||
let data = '';
|
||||
stream.setEncoding('utf8');
|
||||
stream.on('data', (chunk) => data += chunk);
|
||||
stream.on('end', common.mustCall(() => {
|
||||
assert.strictEqual(data, 'some data more data');
|
||||
}));
|
||||
stream.respond({
|
||||
'content-type': 'text/html',
|
||||
':status': 200
|
||||
});
|
||||
stream.end('hello world');
|
||||
}
|
||||
|
||||
server.listen(0);
|
||||
|
||||
server.on('listening', common.mustCall(() => {
|
||||
stream.respond();
|
||||
stream.end('ok');
|
||||
}));
|
||||
|
||||
server.listen(0, common.mustCall(() => {
|
||||
const client = h2.connect(`http://localhost:${server.address().port}`);
|
||||
|
||||
const req = client.request({
|
||||
[HTTP2_HEADER_PATH]: '/',
|
||||
[HTTP2_HEADER_METHOD]: HTTP2_METHOD_POST });
|
||||
const req = client.request({ ':method': 'POST' });
|
||||
req.write('some data ');
|
||||
req.write('more data');
|
||||
req.end('more data');
|
||||
|
||||
req.on('response', common.mustCall());
|
||||
req.resume();
|
||||
req.on('end', common.mustCall(() => {
|
||||
req.on('end', common.mustCall());
|
||||
req.on('close', common.mustCall(() => {
|
||||
server.close();
|
||||
client.destroy();
|
||||
client.close();
|
||||
}));
|
||||
req.end();
|
||||
|
||||
}));
|
||||
|
@ -4,9 +4,7 @@
|
||||
const common = require('../common');
|
||||
if (!common.hasCrypto)
|
||||
common.skip('missing crypto');
|
||||
const assert = require('assert');
|
||||
const h2 = require('http2');
|
||||
const { Http2Stream } = require('internal/http2/core');
|
||||
|
||||
// Errors should not be reported both in Http2ServerRequest
|
||||
// and Http2ServerResponse
|
||||
@ -14,6 +12,7 @@ const { Http2Stream } = require('internal/http2/core');
|
||||
let expected = null;
|
||||
|
||||
const server = h2.createServer(common.mustCall(function(req, res) {
|
||||
res.stream.on('error', common.mustCall());
|
||||
req.on('error', common.mustNotCall());
|
||||
res.on('error', common.mustNotCall());
|
||||
req.on('aborted', common.mustCall());
|
||||
@ -26,27 +25,12 @@ const server = h2.createServer(common.mustCall(function(req, res) {
|
||||
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() {
|
||||
const port = server.address().port;
|
||||
|
||||
const url = `http://localhost:${port}`;
|
||||
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) {
|
||||
// cause an error on the server side
|
||||
const url = `http://localhost:${server.address().port}`;
|
||||
const client = h2.connect(url, common.mustCall(() => {
|
||||
const request = client.request();
|
||||
request.on('data', common.mustCall((chunk) => {
|
||||
client.destroy();
|
||||
}));
|
||||
request.end();
|
||||
}));
|
||||
}));
|
||||
|
@ -12,74 +12,47 @@ const testResBody = 'other stuff!\n';
|
||||
// through server receiving it, triggering 'checkContinue' custom handler,
|
||||
// writing the rest of the request to finally the client receiving to.
|
||||
|
||||
function handler(req, res) {
|
||||
console.error('Server sent full response');
|
||||
const server = http2.createServer(
|
||||
common.mustNotCall('Full request received before 100 Continue')
|
||||
);
|
||||
|
||||
res.writeHead(200, {
|
||||
'content-type': 'text/plain',
|
||||
'abcd': '1'
|
||||
});
|
||||
server.on('checkContinue', common.mustCall((req, res) => {
|
||||
res.writeContinue();
|
||||
res.writeHead(200, {});
|
||||
res.end(testResBody);
|
||||
// should simply return false if already too late to write
|
||||
assert.strictEqual(res.writeContinue(), false);
|
||||
res.on('finish', common.mustCall(
|
||||
() => 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.on('listening', common.mustCall(() => {
|
||||
server.listen(0, common.mustCall(() => {
|
||||
let body = '';
|
||||
|
||||
const port = server.address().port;
|
||||
const client = http2.connect(`http://localhost:${port}`);
|
||||
const client = http2.connect(`http://localhost:${server.address().port}`);
|
||||
const req = client.request({
|
||||
':method': 'POST',
|
||||
':path': '/world',
|
||||
expect: '100-continue'
|
||||
});
|
||||
console.error('Client sent request');
|
||||
|
||||
let gotContinue = false;
|
||||
req.on('continue', common.mustCall(() => {
|
||||
console.error('Client received 100-continue');
|
||||
gotContinue = true;
|
||||
}));
|
||||
|
||||
req.on('response', common.mustCall((headers) => {
|
||||
console.error('Client received response headers');
|
||||
|
||||
assert.strictEqual(gotContinue, true);
|
||||
assert.strictEqual(headers[':status'], 200);
|
||||
assert.strictEqual(headers['abcd'], '1');
|
||||
req.end();
|
||||
}));
|
||||
|
||||
req.setEncoding('utf-8');
|
||||
req.on('data', common.mustCall((chunk) => { body += chunk; }));
|
||||
|
||||
req.on('end', common.mustCall(() => {
|
||||
console.error('Client received full response');
|
||||
|
||||
assert.strictEqual(body, testResBody);
|
||||
|
||||
client.destroy();
|
||||
client.close();
|
||||
server.close();
|
||||
}));
|
||||
}));
|
||||
|
@ -17,8 +17,6 @@ const server = http2.createServer();
|
||||
let sentResponse = false;
|
||||
|
||||
server.on('request', common.mustCall((req, res) => {
|
||||
console.error('Server sent full response');
|
||||
|
||||
res.end(testResBody);
|
||||
sentResponse = true;
|
||||
}));
|
||||
@ -28,38 +26,29 @@ server.listen(0);
|
||||
server.on('listening', common.mustCall(() => {
|
||||
let body = '';
|
||||
|
||||
const port = server.address().port;
|
||||
const client = http2.connect(`http://localhost:${port}`);
|
||||
const client = http2.connect(`http://localhost:${server.address().port}`);
|
||||
const req = client.request({
|
||||
':method': 'POST',
|
||||
':path': '/world',
|
||||
expect: '100-continue'
|
||||
});
|
||||
console.error('Client sent request');
|
||||
|
||||
let gotContinue = false;
|
||||
req.on('continue', common.mustCall(() => {
|
||||
console.error('Client received 100-continue');
|
||||
gotContinue = true;
|
||||
}));
|
||||
|
||||
req.on('response', common.mustCall((headers) => {
|
||||
console.error('Client received response headers');
|
||||
|
||||
assert.strictEqual(gotContinue, true);
|
||||
assert.strictEqual(sentResponse, true);
|
||||
assert.strictEqual(headers[':status'], 200);
|
||||
req.end();
|
||||
}));
|
||||
|
||||
req.setEncoding('utf8');
|
||||
req.on('data', common.mustCall((chunk) => { body += chunk; }));
|
||||
|
||||
req.on('end', common.mustCall(() => {
|
||||
console.error('Client received full response');
|
||||
|
||||
assert.strictEqual(body, testResBody);
|
||||
|
||||
client.destroy();
|
||||
client.close();
|
||||
server.close();
|
||||
}));
|
||||
}));
|
||||
|
@ -39,7 +39,7 @@ function nextTest(testsToRun) {
|
||||
}));
|
||||
|
||||
req.on('end', common.mustCall(() => {
|
||||
client.destroy();
|
||||
client.close();
|
||||
nextTest(testsToRun - 1);
|
||||
}));
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ function testMethodConnect(testsToRun) {
|
||||
}));
|
||||
req.resume();
|
||||
req.on('end', common.mustCall(() => {
|
||||
client.destroy();
|
||||
client.close();
|
||||
testMethodConnect(testsToRun - 1);
|
||||
}));
|
||||
req.end();
|
||||
|
@ -31,18 +31,11 @@ server.listen(0, common.mustCall(function() {
|
||||
}));
|
||||
|
||||
const url = `http://localhost:${port}`;
|
||||
const client = h2.connect(url, common.mustCall(function() {
|
||||
const headers = {
|
||||
':path': '/foobar',
|
||||
':method': 'GET',
|
||||
':scheme': 'http',
|
||||
':authority': `localhost:${port}`
|
||||
};
|
||||
const request = client.request(headers);
|
||||
const client = h2.connect(url, common.mustCall(() => {
|
||||
const request = client.request();
|
||||
request.resume();
|
||||
request.on('end', common.mustCall(function() {
|
||||
client.destroy();
|
||||
request.on('end', common.mustCall(() => {
|
||||
client.close();
|
||||
}));
|
||||
request.end();
|
||||
}));
|
||||
}));
|
||||
|
@ -79,7 +79,7 @@ server.listen(0, common.mustCall(function() {
|
||||
};
|
||||
const request = client.request(headers);
|
||||
request.on('end', common.mustCall(function() {
|
||||
client.destroy();
|
||||
client.close();
|
||||
}));
|
||||
request.end();
|
||||
request.resume();
|
||||
|
@ -46,7 +46,7 @@ server.listen(0, common.mustCall(() => {
|
||||
request.resume();
|
||||
request.end(testStr);
|
||||
request.on('end', common.mustCall(function() {
|
||||
client.destroy();
|
||||
client.close();
|
||||
server.close();
|
||||
}));
|
||||
}));
|
||||
|
@ -35,7 +35,7 @@ server.listen(0, common.mustCall(() => {
|
||||
function maybeClose() {
|
||||
if (--remaining === 0) {
|
||||
server.close();
|
||||
client.destroy();
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,6 @@ const server = http2.createServer();
|
||||
server.on('request', (req, res) => {
|
||||
req.setTimeout(msecs, common.mustCall(() => {
|
||||
res.end();
|
||||
req.setTimeout(msecs, common.mustNotCall());
|
||||
}));
|
||||
res.on('finish', common.mustCall(() => {
|
||||
req.setTimeout(msecs, common.mustNotCall());
|
||||
@ -35,7 +34,7 @@ server.listen(0, common.mustCall(() => {
|
||||
':authority': `localhost:${port}`
|
||||
});
|
||||
req.on('end', common.mustCall(() => {
|
||||
client.destroy();
|
||||
client.close();
|
||||
}));
|
||||
req.resume();
|
||||
req.end();
|
||||
|
@ -62,7 +62,7 @@ server.listen(0, common.mustCall(function() {
|
||||
request.resume();
|
||||
request.on('end', common.mustCall(function() {
|
||||
server.close();
|
||||
client.destroy();
|
||||
client.close();
|
||||
}));
|
||||
request.write('test\n');
|
||||
request.end('test');
|
||||
|
@ -46,7 +46,7 @@ server.listen(0, common.mustCall(function() {
|
||||
};
|
||||
const request = client.request(headers);
|
||||
request.on('end', common.mustCall(function() {
|
||||
client.destroy();
|
||||
client.close();
|
||||
}));
|
||||
request.end();
|
||||
request.resume();
|
||||
|
@ -16,26 +16,17 @@ const server = h2.createServer(common.mustCall((req, res) => {
|
||||
|
||||
req.on('close', common.mustCall());
|
||||
res.on('close', common.mustCall());
|
||||
req.on('error', common.mustNotCall());
|
||||
}));
|
||||
server.listen(0);
|
||||
|
||||
server.on('listening', function() {
|
||||
const port = server.address().port;
|
||||
|
||||
const url = `http://localhost:${port}`;
|
||||
const client = h2.connect(url, common.mustCall(function() {
|
||||
const headers = {
|
||||
':path': '/foobar',
|
||||
':method': 'GET',
|
||||
':scheme': 'http',
|
||||
':authority': `localhost:${port}`,
|
||||
};
|
||||
const request = client.request(headers);
|
||||
server.on('listening', () => {
|
||||
const url = `http://localhost:${server.address().port}`;
|
||||
const client = h2.connect(url, common.mustCall(() => {
|
||||
const request = client.request();
|
||||
request.on('data', common.mustCall(function(chunk) {
|
||||
// cause an error on the server side
|
||||
client.destroy();
|
||||
server.close();
|
||||
}));
|
||||
request.end();
|
||||
}));
|
||||
});
|
||||
|
@ -43,7 +43,7 @@ const server = h2.createServer((request, response) => {
|
||||
':path': '/pushed',
|
||||
':method': 'GET'
|
||||
}, 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;
|
||||
function maybeClose() {
|
||||
if (--remaining === 0) {
|
||||
client.destroy();
|
||||
client.close();
|
||||
server.close();
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ if (!common.hasCrypto)
|
||||
common.skip('missing crypto');
|
||||
const assert = require('assert');
|
||||
const http2 = require('http2');
|
||||
const Countdown = require('../common/countdown');
|
||||
|
||||
// Check that destroying the Http2ServerResponse stream produces
|
||||
// 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 !== '/') {
|
||||
nextError = errors.shift();
|
||||
}
|
||||
|
||||
res.destroy(nextError);
|
||||
}, 3));
|
||||
|
||||
server.on(
|
||||
'streamError',
|
||||
common.mustCall((err) => assert.strictEqual(err, nextError), 2)
|
||||
);
|
||||
|
||||
server.listen(0, common.mustCall(() => {
|
||||
const port = server.address().port;
|
||||
const client = http2.connect(`http://localhost:${port}`);
|
||||
const req = client.request({
|
||||
':path': '/',
|
||||
':method': 'GET',
|
||||
':scheme': 'http',
|
||||
':authority': `localhost:${port}`
|
||||
});
|
||||
const client = http2.connect(`http://localhost:${server.address().port}`);
|
||||
|
||||
req.on('response', common.mustNotCall());
|
||||
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(() => {
|
||||
const countdown = new Countdown(3, () => {
|
||||
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() {
|
||||
assert.strictEqual(data, testString.repeat(2));
|
||||
client.destroy();
|
||||
client.close();
|
||||
server.close();
|
||||
}));
|
||||
}));
|
||||
|
@ -52,7 +52,7 @@ const {
|
||||
request.on('data', (chunk) => (data += chunk));
|
||||
request.on('end', mustCall(() => {
|
||||
strictEqual(data, 'end');
|
||||
client.destroy();
|
||||
client.close();
|
||||
}));
|
||||
request.end();
|
||||
request.resume();
|
||||
@ -83,7 +83,7 @@ const {
|
||||
request.on('data', (chunk) => (data += chunk));
|
||||
request.on('end', mustCall(() => {
|
||||
strictEqual(data, 'test\uD83D\uDE00');
|
||||
client.destroy();
|
||||
client.close();
|
||||
}));
|
||||
request.end();
|
||||
request.resume();
|
||||
@ -110,7 +110,7 @@ const {
|
||||
};
|
||||
const request = client.request(headers);
|
||||
request.on('data', mustNotCall());
|
||||
request.on('end', mustCall(() => client.destroy()));
|
||||
request.on('end', mustCall(() => client.close()));
|
||||
request.end();
|
||||
request.resume();
|
||||
}));
|
||||
@ -143,7 +143,7 @@ const {
|
||||
}));
|
||||
request.on('data', mustNotCall());
|
||||
request.on('end', mustCall(() => {
|
||||
client.destroy();
|
||||
client.close();
|
||||
server.close();
|
||||
}));
|
||||
request.end();
|
||||
@ -172,7 +172,7 @@ const {
|
||||
const request = client.request(headers);
|
||||
request.on('data', mustNotCall());
|
||||
request.on('end', mustCall(() => {
|
||||
client.destroy();
|
||||
client.close();
|
||||
server.close();
|
||||
}));
|
||||
request.end();
|
||||
@ -208,7 +208,7 @@ const {
|
||||
}));
|
||||
request.on('data', mustNotCall());
|
||||
request.on('end', mustCall(() => {
|
||||
client.destroy();
|
||||
client.close();
|
||||
server.close();
|
||||
}));
|
||||
request.end();
|
||||
@ -243,7 +243,7 @@ const {
|
||||
}));
|
||||
request.on('data', mustNotCall());
|
||||
request.on('end', mustCall(() => {
|
||||
client.destroy();
|
||||
client.close();
|
||||
server.close();
|
||||
}));
|
||||
request.end();
|
||||
@ -283,7 +283,7 @@ const {
|
||||
}));
|
||||
request.on('data', mustNotCall());
|
||||
request.on('end', mustCall(() => {
|
||||
client.destroy();
|
||||
client.close();
|
||||
server.close();
|
||||
}));
|
||||
request.end();
|
||||
@ -315,7 +315,7 @@ const {
|
||||
}));
|
||||
request.on('data', mustNotCall());
|
||||
request.on('end', mustCall(() => {
|
||||
client.destroy();
|
||||
client.close();
|
||||
server.close();
|
||||
}));
|
||||
request.end();
|
||||
|
@ -39,7 +39,7 @@ server.listen(0, common.mustCall(function() {
|
||||
};
|
||||
const request = client.request(headers);
|
||||
request.on('end', common.mustCall(function() {
|
||||
client.destroy();
|
||||
client.close();
|
||||
}));
|
||||
request.end();
|
||||
request.resume();
|
||||
|
@ -51,7 +51,7 @@ server.listen(0, common.mustCall(function() {
|
||||
serverResponse.end();
|
||||
}, 1));
|
||||
request.on('end', common.mustCall(function() {
|
||||
client.destroy();
|
||||
client.close();
|
||||
}));
|
||||
request.end();
|
||||
request.resume();
|
||||
|
@ -13,8 +13,6 @@ const server = h2.createServer();
|
||||
server.listen(0, common.mustCall(function() {
|
||||
const port = server.address().port;
|
||||
server.once('request', common.mustCall(function(request, response) {
|
||||
response.destroy();
|
||||
|
||||
response.on('finish', common.mustCall(() => {
|
||||
assert.strictEqual(response.headersSent, false);
|
||||
assert.doesNotThrow(() => response.setHeader('test', 'value'));
|
||||
@ -27,6 +25,8 @@ server.listen(0, common.mustCall(function() {
|
||||
server.close();
|
||||
});
|
||||
}));
|
||||
|
||||
response.destroy();
|
||||
}));
|
||||
|
||||
const url = `http://localhost:${port}`;
|
||||
@ -39,7 +39,7 @@ server.listen(0, common.mustCall(function() {
|
||||
};
|
||||
const request = client.request(headers);
|
||||
request.on('end', common.mustCall(function() {
|
||||
client.destroy();
|
||||
client.close();
|
||||
}));
|
||||
request.end();
|
||||
request.resume();
|
||||
|
@ -179,7 +179,7 @@ server.listen(0, common.mustCall(function() {
|
||||
};
|
||||
const request = client.request(headers);
|
||||
request.on('end', common.mustCall(function() {
|
||||
client.destroy();
|
||||
client.close();
|
||||
}));
|
||||
request.end();
|
||||
request.resume();
|
||||
|
@ -12,7 +12,6 @@ const server = http2.createServer();
|
||||
server.on('request', (req, res) => {
|
||||
res.setTimeout(msecs, common.mustCall(() => {
|
||||
res.end();
|
||||
res.setTimeout(msecs, common.mustNotCall());
|
||||
}));
|
||||
res.on('finish', common.mustCall(() => {
|
||||
res.setTimeout(msecs, common.mustNotCall());
|
||||
@ -35,7 +34,7 @@ server.listen(0, common.mustCall(() => {
|
||||
':authority': `localhost:${port}`
|
||||
});
|
||||
req.on('end', common.mustCall(() => {
|
||||
client.destroy();
|
||||
client.close();
|
||||
}));
|
||||
req.resume();
|
||||
req.end();
|
||||
|
@ -69,7 +69,7 @@ server.listen(0, common.mustCall(function() {
|
||||
};
|
||||
const request = client.request(headers);
|
||||
request.on('end', common.mustCall(function() {
|
||||
client.destroy();
|
||||
client.close();
|
||||
}));
|
||||
request.end();
|
||||
request.resume();
|
||||
|
@ -42,7 +42,7 @@ server.listen(0, common.mustCall(function() {
|
||||
assert.strictEqual(headers[':status'], 200);
|
||||
}, 1));
|
||||
request.on('end', common.mustCall(function() {
|
||||
client.destroy();
|
||||
client.close();
|
||||
}));
|
||||
request.end();
|
||||
request.resume();
|
||||
|
@ -41,7 +41,7 @@ server.listen(0, common.mustCall(function() {
|
||||
assert.strictEqual(headers[':status'], 200);
|
||||
}, 1));
|
||||
request.on('end', common.mustCall(function() {
|
||||
client.destroy();
|
||||
client.close();
|
||||
}));
|
||||
request.end();
|
||||
request.resume();
|
||||
|
@ -45,7 +45,7 @@ server.listen(0, common.mustCall(function() {
|
||||
assert.strictEqual(headers['foo-bar'], 'abc123');
|
||||
}, 1));
|
||||
request.on('end', common.mustCall(function() {
|
||||
client.destroy();
|
||||
client.close();
|
||||
}));
|
||||
request.end();
|
||||
request.resume();
|
||||
|
@ -68,7 +68,7 @@ server.listen(0, common.mustCall(() => {
|
||||
}));
|
||||
request.resume();
|
||||
request.on('end', common.mustCall(() => {
|
||||
client.destroy();
|
||||
client.close();
|
||||
server.close();
|
||||
}));
|
||||
}));
|
||||
|
@ -6,44 +6,33 @@ const { mustCall,
|
||||
hasCrypto, skip } = require('../common');
|
||||
if (!hasCrypto)
|
||||
skip('missing crypto');
|
||||
const { throws } = require('assert');
|
||||
const { createServer, connect } = require('http2');
|
||||
|
||||
// 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();
|
||||
server.listen(0, mustCall(() => {
|
||||
const port = server.address().port;
|
||||
const url = `http://localhost:${port}`;
|
||||
const client = connect(url, mustCall(() => {
|
||||
const headers = {
|
||||
':path': '/',
|
||||
':method': 'GET',
|
||||
':scheme': 'http',
|
||||
':authority': `localhost:${port}`
|
||||
};
|
||||
const request = client.request(headers);
|
||||
request.end();
|
||||
const request = client.request();
|
||||
request.resume();
|
||||
request.on('end', mustCall());
|
||||
request.on('close', mustCall(() => {
|
||||
client.close();
|
||||
}));
|
||||
}));
|
||||
|
||||
server.once('request', mustCall((request, response) => {
|
||||
client.destroy();
|
||||
response.stream.session.on('close', mustCall(() => {
|
||||
response.on('error', mustNotCall());
|
||||
throws(
|
||||
expectsError(
|
||||
() => { response.write('muahaha'); },
|
||||
expectsError({
|
||||
code: 'ERR_HTTP2_STREAM_CLOSED',
|
||||
type: Error,
|
||||
message: 'The stream is already closed'
|
||||
})
|
||||
{
|
||||
code: 'ERR_HTTP2_INVALID_STREAM'
|
||||
}
|
||||
);
|
||||
server.close();
|
||||
}));
|
||||
@ -57,21 +46,21 @@ const expectedError = expectsError({
|
||||
const port = server.address().port;
|
||||
const url = `http://localhost:${port}`;
|
||||
const client = connect(url, mustCall(() => {
|
||||
const headers = {
|
||||
':path': '/',
|
||||
':method': 'get',
|
||||
':scheme': 'http',
|
||||
':authority': `localhost:${port}`
|
||||
};
|
||||
const request = client.request(headers);
|
||||
request.end();
|
||||
const request = client.request();
|
||||
request.resume();
|
||||
request.on('end', mustCall());
|
||||
request.on('close', mustCall(() => client.close()));
|
||||
}));
|
||||
|
||||
server.once('request', mustCall((request, response) => {
|
||||
client.destroy();
|
||||
response.stream.session.on('close', mustCall(() => {
|
||||
response.write('muahaha', mustCall(expectedError));
|
||||
expectsError(
|
||||
() => response.write('muahaha'),
|
||||
{
|
||||
code: 'ERR_HTTP2_INVALID_STREAM'
|
||||
}
|
||||
);
|
||||
server.close();
|
||||
}));
|
||||
}));
|
||||
@ -84,20 +73,20 @@ const expectedError = expectsError({
|
||||
const port = server.address().port;
|
||||
const url = `http://localhost:${port}`;
|
||||
const client = connect(url, mustCall(() => {
|
||||
const headers = {
|
||||
':path': '/',
|
||||
':method': 'get',
|
||||
':scheme': 'http',
|
||||
':authority': `localhost:${port}`
|
||||
};
|
||||
const request = client.request(headers);
|
||||
request.end();
|
||||
const request = client.request();
|
||||
request.resume();
|
||||
request.on('end', mustCall());
|
||||
request.on('close', mustCall(() => client.close()));
|
||||
}));
|
||||
|
||||
server.once('request', mustCall((request, response) => {
|
||||
response.stream.session.on('close', mustCall(() => {
|
||||
response.write('muahaha', 'utf8', mustCall(expectedError));
|
||||
expectsError(
|
||||
() => response.write('muahaha', 'utf8'),
|
||||
{
|
||||
code: 'ERR_HTTP2_INVALID_STREAM'
|
||||
}
|
||||
);
|
||||
server.close();
|
||||
}));
|
||||
client.destroy();
|
||||
|
@ -23,7 +23,7 @@ server.listen(0, common.mustCall(function() {
|
||||
server.close();
|
||||
process.nextTick(common.mustCall(() => {
|
||||
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);
|
||||
}, 1));
|
||||
request.on('end', common.mustCall(function() {
|
||||
client.destroy();
|
||||
client.close();
|
||||
}));
|
||||
request.end();
|
||||
request.resume();
|
||||
|
@ -98,7 +98,7 @@ server.listen(0, common.mustCall(function() {
|
||||
};
|
||||
const request = client.request(headers);
|
||||
request.on('end', common.mustCall(() => {
|
||||
client.destroy();
|
||||
client.close();
|
||||
server.close();
|
||||
}));
|
||||
request.end();
|
||||
|
@ -81,7 +81,7 @@ server.listen(0, common.mustCall(function() {
|
||||
};
|
||||
const request = client.request(headers);
|
||||
request.on('end', common.mustCall(() => {
|
||||
client.destroy();
|
||||
client.close();
|
||||
}));
|
||||
request.end();
|
||||
request.resume();
|
||||
|
@ -13,7 +13,8 @@ const {
|
||||
HTTP2_HEADER_AUTHORITY,
|
||||
HTTP2_HEADER_SCHEME,
|
||||
HTTP2_HEADER_PATH,
|
||||
NGHTTP2_CONNECT_ERROR
|
||||
NGHTTP2_CONNECT_ERROR,
|
||||
NGHTTP2_REFUSED_STREAM
|
||||
} = http2.constants;
|
||||
|
||||
const server = net.createServer(common.mustCall((socket) => {
|
||||
@ -34,7 +35,7 @@ server.listen(0, common.mustCall(() => {
|
||||
const proxy = http2.createServer();
|
||||
proxy.on('stream', common.mustCall((stream, headers) => {
|
||||
if (headers[HTTP2_HEADER_METHOD] !== 'CONNECT') {
|
||||
stream.rstWithRefused();
|
||||
stream.close(NGHTTP2_REFUSED_STREAM);
|
||||
return;
|
||||
}
|
||||
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('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('end', common.mustCall(() => {
|
||||
assert.strictEqual(data, 'hello');
|
||||
client.destroy();
|
||||
client.close();
|
||||
proxy.close();
|
||||
server.close();
|
||||
}));
|
||||
|
@ -20,7 +20,7 @@ const { createServer, connect } = require('http2');
|
||||
|
||||
for (const client of clients) {
|
||||
client.once('connect', mustCall((headers) => {
|
||||
client.destroy();
|
||||
client.close();
|
||||
clients.delete(client);
|
||||
if (clients.size === 0) {
|
||||
server.close();
|
||||
@ -33,7 +33,11 @@ const { createServer, connect } = require('http2');
|
||||
// check for https as protocol
|
||||
{
|
||||
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)
|
||||
|
@ -54,7 +54,7 @@ server.on('listening', common.mustCall(() => {
|
||||
|
||||
req.on('end', common.mustCall(() => {
|
||||
server.close();
|
||||
client.destroy();
|
||||
client.close();
|
||||
}));
|
||||
req.end();
|
||||
|
||||
|
@ -30,7 +30,7 @@ const URL = url.URL;
|
||||
() => setImmediate(() => server.close()));
|
||||
|
||||
const maybeClose = common.mustCall((client) => {
|
||||
client.destroy();
|
||||
client.close();
|
||||
serverClose.dec();
|
||||
}, items.length);
|
||||
|
||||
@ -42,7 +42,7 @@ const URL = url.URL;
|
||||
|
||||
// Will fail because protocol does not match the server.
|
||||
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);
|
||||
server.listen(0);
|
||||
|
||||
server.on('listening', common.mustCall(function() {
|
||||
const port = this.address().port;
|
||||
server.listen(0, common.mustCall(() => {
|
||||
const port = server.address().port;
|
||||
|
||||
const opts = { rejectUnauthorized: false };
|
||||
|
||||
@ -74,7 +72,7 @@ const URL = url.URL;
|
||||
() => setImmediate(() => server.close()));
|
||||
|
||||
const maybeClose = common.mustCall((client) => {
|
||||
client.destroy();
|
||||
client.close();
|
||||
serverClose.dec();
|
||||
}, items.length);
|
||||
|
||||
|
@ -5,6 +5,8 @@ if (!common.hasCrypto)
|
||||
common.skip('missing crypto');
|
||||
const assert = require('assert');
|
||||
const h2 = require('http2');
|
||||
const Countdown = require('../common/countdown');
|
||||
|
||||
const body =
|
||||
'<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',
|
||||
':status': 200
|
||||
});
|
||||
stream.end(body);
|
||||
stream.write(body.slice(0, 20));
|
||||
stream.end(body.slice(20));
|
||||
}
|
||||
|
||||
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++) {
|
||||
const req = client.request(headers);
|
||||
const req = client.request();
|
||||
|
||||
req.on('response', common.mustCall(function(headers) {
|
||||
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('end', common.mustCall(() => {
|
||||
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 invalidOptions = [() => {}, 1, 'test', null, undefined];
|
||||
const invalidOptions = [() => {}, 1, 'test', null];
|
||||
const invalidArgTypeError = {
|
||||
type: TypeError,
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
@ -14,9 +14,9 @@ const invalidArgTypeError = {
|
||||
};
|
||||
|
||||
// Error if options are not passed to createSecureServer
|
||||
invalidOptions.forEach((invalidOption) =>
|
||||
invalidOptions.forEach((invalidOption) => {
|
||||
common.expectsError(
|
||||
() => http2.createSecureServer(invalidOption),
|
||||
invalidArgTypeError
|
||||
)
|
||||
);
|
||||
);
|
||||
});
|
||||
|
@ -54,7 +54,7 @@ server.listen(0, common.mustCall(function() {
|
||||
req.resume();
|
||||
|
||||
req.on('end', common.mustCall(function() {
|
||||
client.destroy();
|
||||
client.close();
|
||||
testsFinished++;
|
||||
|
||||
if (testsFinished === testsToRun) {
|
||||
|
@ -24,6 +24,6 @@ server.listen(0, common.mustCall(() => {
|
||||
req.resume();
|
||||
req.on('end', common.mustCall(() => {
|
||||
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.on('end', common.mustCall(() => {
|
||||
server.close();
|
||||
client.destroy();
|
||||
client.close();
|
||||
}));
|
||||
}));
|
||||
|
@ -20,7 +20,7 @@ const makeDuplexPair = require('../common/duplexpair');
|
||||
createConnection: common.mustCall(() => clientSide)
|
||||
});
|
||||
|
||||
const req = client.request({ ':path': '/' });
|
||||
const req = client.request();
|
||||
|
||||
req.on('response', common.mustCall((headers) => {
|
||||
assert.strictEqual(headers[':status'], 200);
|
||||
@ -28,9 +28,7 @@ const makeDuplexPair = require('../common/duplexpair');
|
||||
|
||||
req.setEncoding('utf8');
|
||||
let data = '';
|
||||
req.on('data', (chunk) => {
|
||||
data += chunk;
|
||||
});
|
||||
req.on('data', (chunk) => data += chunk);
|
||||
req.on('end', common.mustCall(() => {
|
||||
assert.strictEqual(data, fs.readFileSync(__filename, 'utf8'));
|
||||
clientSide.destroy();
|
||||
|
@ -10,32 +10,23 @@ const server = http2.createServer();
|
||||
const data = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5]);
|
||||
|
||||
server.on('stream', common.mustCall((stream) => {
|
||||
stream.session.shutdown({
|
||||
errorCode: 1,
|
||||
opaqueData: data
|
||||
});
|
||||
stream.session.goaway(0, 0, data);
|
||||
stream.respond();
|
||||
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, () => {
|
||||
|
||||
const client = http2.connect(`http://localhost:${server.address().port}`);
|
||||
client.on('goaway', common.mustCall((code, lastStreamID, buf) => {
|
||||
assert.deepStrictEqual(code, 1);
|
||||
assert.deepStrictEqual(lastStreamID, 0);
|
||||
client.once('goaway', common.mustCall((code, lastStreamID, buf) => {
|
||||
assert.deepStrictEqual(code, 0);
|
||||
assert.deepStrictEqual(lastStreamID, 1);
|
||||
assert.deepStrictEqual(data, buf);
|
||||
// Call shutdown() here so that emitGoaway calls destroy()
|
||||
client.shutdown();
|
||||
server.close();
|
||||
}));
|
||||
const req = client.request({ ':path': '/' });
|
||||
const req = client.request();
|
||||
req.resume();
|
||||
req.on('end', common.mustCall());
|
||||
req.on('close', common.mustCall());
|
||||
req.end();
|
||||
|
||||
});
|
||||
|
@ -54,6 +54,6 @@ server.listen(0, () => {
|
||||
req.on('data', common.mustNotCall());
|
||||
req.on('end', common.mustCall(() => {
|
||||
server.close();
|
||||
client.destroy();
|
||||
client.close();
|
||||
}));
|
||||
});
|
||||
|
@ -52,7 +52,7 @@ function onSession(session) {
|
||||
strictEqual(alpnProtocol, 'h2');
|
||||
strictEqual(httpVersion, '2.0');
|
||||
|
||||
session.destroy();
|
||||
session.close();
|
||||
this.cleanup();
|
||||
}));
|
||||
request.end();
|
||||
|
@ -48,10 +48,6 @@ server.on('stream', common.mustCall((stream, headers) => {
|
||||
if (currentError.type === 'stream') {
|
||||
stream.session.on('error', errorMustNotCall);
|
||||
stream.on('error', errorMustCall);
|
||||
stream.on('error', common.mustCall(() => {
|
||||
stream.respond();
|
||||
stream.end();
|
||||
}));
|
||||
} else {
|
||||
stream.session.once('error', errorMustCall);
|
||||
stream.on('error', errorMustNotCall);
|
||||
@ -63,24 +59,21 @@ server.on('stream', common.mustCall((stream, headers) => {
|
||||
server.listen(0, common.mustCall(() => runTest(tests.shift())));
|
||||
|
||||
function runTest(test) {
|
||||
const port = server.address().port;
|
||||
const url = `http://localhost:${port}`;
|
||||
const headers = {
|
||||
':path': '/',
|
||||
':method': 'POST',
|
||||
':scheme': 'http',
|
||||
':authority': `localhost:${port}`
|
||||
};
|
||||
|
||||
const client = http2.connect(url);
|
||||
const req = client.request(headers);
|
||||
const client = http2.connect(`http://localhost:${server.address().port}`);
|
||||
const req = client.request({ ':method': 'POST' });
|
||||
|
||||
currentError = test;
|
||||
req.resume();
|
||||
req.end();
|
||||
|
||||
req.on('end', common.mustCall(() => {
|
||||
client.destroy();
|
||||
req.on('error', common.expectsError({
|
||||
code: 'ERR_HTTP2_STREAM_ERROR',
|
||||
type: Error,
|
||||
message: 'Stream closed with error code 2'
|
||||
}));
|
||||
|
||||
req.on('close', common.mustCall(() => {
|
||||
client.close();
|
||||
|
||||
if (!tests.length) {
|
||||
server.close();
|
||||
|
@ -88,7 +88,7 @@ server.on('listening', common.mustCall(() => {
|
||||
|
||||
req.on('end', common.mustCall(() => {
|
||||
server.close();
|
||||
client.destroy();
|
||||
client.close();
|
||||
}));
|
||||
req.end();
|
||||
|
||||
|
@ -7,29 +7,25 @@ const http2 = require('http2');
|
||||
|
||||
const server = http2.createServer();
|
||||
|
||||
server.on(
|
||||
'stream',
|
||||
common.mustCall((stream) => {
|
||||
const invalidArgTypeError = (param, type) => ({
|
||||
type: TypeError,
|
||||
server.on('stream', common.mustCall((stream) => {
|
||||
common.expectsError(
|
||||
() => stream.close('string'),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
message: `The "${param}" argument must be of type ${type}`
|
||||
});
|
||||
common.expectsError(
|
||||
() => stream.rstStream('string'),
|
||||
invalidArgTypeError('code', 'number')
|
||||
);
|
||||
stream.session.destroy();
|
||||
})
|
||||
);
|
||||
type: TypeError,
|
||||
message: 'The "code" argument must be of type number'
|
||||
}
|
||||
);
|
||||
stream.respond();
|
||||
stream.end('ok');
|
||||
}));
|
||||
|
||||
server.listen(
|
||||
0,
|
||||
common.mustCall(() => {
|
||||
const client = http2.connect(`http://localhost:${server.address().port}`);
|
||||
const req = client.request();
|
||||
req.resume();
|
||||
req.on('end', common.mustCall(() => server.close()));
|
||||
req.end();
|
||||
})
|
||||
);
|
||||
server.listen(0, common.mustCall(() => {
|
||||
const client = http2.connect(`http://localhost:${server.address().port}`);
|
||||
const req = client.request();
|
||||
req.resume();
|
||||
req.on('close', common.mustCall(() => {
|
||||
server.close();
|
||||
client.close();
|
||||
}));
|
||||
}));
|
||||
|
@ -5,64 +5,52 @@ if (!common.hasCrypto)
|
||||
common.skip('missing crypto');
|
||||
const assert = require('assert');
|
||||
const h2 = require('http2');
|
||||
|
||||
const {
|
||||
HTTP2_HEADER_METHOD,
|
||||
HTTP2_HEADER_STATUS,
|
||||
HTTP2_HEADER_PATH,
|
||||
HTTP2_METHOD_POST
|
||||
} = h2.constants;
|
||||
const Countdown = require('../common/countdown');
|
||||
|
||||
// Only allow one stream to be open at a time
|
||||
const server = h2.createServer({ settings: { maxConcurrentStreams: 1 } });
|
||||
|
||||
// The stream handler must be called only once
|
||||
server.on('stream', common.mustCall((stream) => {
|
||||
stream.respond({ [HTTP2_HEADER_STATUS]: 200 });
|
||||
stream.respond();
|
||||
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}`);
|
||||
|
||||
let reqs = 2;
|
||||
function onEnd() {
|
||||
if (--reqs === 0) {
|
||||
server.close();
|
||||
client.destroy();
|
||||
}
|
||||
}
|
||||
const countdown = new Countdown(2, () => {
|
||||
server.close();
|
||||
client.close();
|
||||
});
|
||||
|
||||
client.on('remoteSettings', common.mustCall((settings) => {
|
||||
assert.strictEqual(settings.maxConcurrentStreams, 1);
|
||||
}));
|
||||
|
||||
// This one should go through with no problems
|
||||
const req1 = client.request({
|
||||
[HTTP2_HEADER_PATH]: '/',
|
||||
[HTTP2_HEADER_METHOD]: HTTP2_METHOD_POST
|
||||
});
|
||||
req1.on('aborted', common.mustNotCall());
|
||||
req1.on('response', common.mustCall());
|
||||
req1.resume();
|
||||
req1.on('end', onEnd);
|
||||
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'
|
||||
})));
|
||||
{
|
||||
const req = client.request({ ':method': 'POST' });
|
||||
req.on('aborted', common.mustNotCall());
|
||||
req.on('response', common.mustCall());
|
||||
req.resume();
|
||||
req.on('end', common.mustCall());
|
||||
req.on('close', common.mustCall(() => countdown.dec()));
|
||||
req.end();
|
||||
}
|
||||
|
||||
{
|
||||
// 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(() => {
|
||||
if (--expected === 0) {
|
||||
server.close();
|
||||
client.destroy();
|
||||
client.close();
|
||||
}
|
||||
}));
|
||||
req.end();
|
||||
|
@ -56,32 +56,24 @@ let client;
|
||||
|
||||
const server = h2.createServer({ settings: { initialWindowSize: 36 } });
|
||||
server.on('stream', (stream) => {
|
||||
|
||||
// Not reading causes the flow control window to get backed up.
|
||||
stream.pause();
|
||||
|
||||
stream.on('error', common.mustCall((err) => {
|
||||
common.expectsError({
|
||||
code: 'ERR_HTTP2_STREAM_ERROR',
|
||||
type: Error,
|
||||
message: 'Stream closed with error code 3'
|
||||
})(err);
|
||||
stream.on('error', common.expectsError({
|
||||
code: 'ERR_HTTP2_STREAM_ERROR',
|
||||
type: Error,
|
||||
message: 'Stream closed with error code 3'
|
||||
}));
|
||||
stream.on('close', common.mustCall(() => {
|
||||
server.close();
|
||||
client.destroy();
|
||||
}));
|
||||
|
||||
stream.on('end', common.mustNotCall());
|
||||
|
||||
stream.respond();
|
||||
stream.end('ok');
|
||||
});
|
||||
|
||||
server.listen(0, () => {
|
||||
client = net.connect(server.address().port, () => {
|
||||
client.on('error', console.log);
|
||||
|
||||
client.write(preamble);
|
||||
|
||||
client.write(data);
|
||||
client.write(data);
|
||||
client.write(data);
|
||||
|
@ -29,6 +29,21 @@ const preamble = 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,
|
||||
0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a, 0x68, 0x65, 0x6c,
|
||||
0x6c, 0x6f, 0x0a, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a
|
||||
@ -51,30 +66,23 @@ const data = Buffer.from([
|
||||
let client;
|
||||
const server = h2.createServer({ settings: { initialWindowSize: 18 } });
|
||||
server.on('stream', (stream) => {
|
||||
|
||||
stream.resume();
|
||||
|
||||
stream.on('error', common.mustCall((err) => {
|
||||
common.expectsError({
|
||||
code: 'ERR_HTTP2_STREAM_ERROR',
|
||||
type: Error,
|
||||
message: 'Stream closed with error code 3'
|
||||
})(err);
|
||||
stream.on('error', common.expectsError({
|
||||
code: 'ERR_HTTP2_STREAM_ERROR',
|
||||
type: Error,
|
||||
message: 'Stream closed with error code 3'
|
||||
}));
|
||||
stream.on('close', common.mustCall(() => {
|
||||
server.close();
|
||||
client.destroy();
|
||||
}));
|
||||
|
||||
stream.resume();
|
||||
stream.respond();
|
||||
stream.end('ok');
|
||||
});
|
||||
|
||||
server.listen(0, () => {
|
||||
client = net.connect(server.address().port, () => {
|
||||
client.on('error', console.log);
|
||||
|
||||
client.write(preamble);
|
||||
|
||||
client.write(data);
|
||||
client.write(data);
|
||||
client.write(data);
|
||||
});
|
||||
|
@ -7,11 +7,7 @@ const h2 = require('http2');
|
||||
|
||||
const server = h2.createServer();
|
||||
|
||||
// we use the lower-level API here
|
||||
server.on('stream', common.mustCall(onStream));
|
||||
|
||||
function onStream(stream, headers, flags) {
|
||||
|
||||
server.on('stream', common.mustCall((stream) => {
|
||||
[
|
||||
':path',
|
||||
':authority',
|
||||
@ -24,10 +20,7 @@ function onStream(stream, headers, flags) {
|
||||
});
|
||||
});
|
||||
|
||||
stream.respond({
|
||||
'content-type': 'text/html',
|
||||
':status': 200
|
||||
}, {
|
||||
stream.respond({}, {
|
||||
getTrailers: common.mustCall((trailers) => {
|
||||
trailers[':status'] = 'bar';
|
||||
})
|
||||
@ -38,22 +31,24 @@ function onStream(stream, headers, flags) {
|
||||
}));
|
||||
|
||||
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 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.resume();
|
||||
req.on('end', common.mustCall(() => {
|
||||
req.on('end', common.mustCall());
|
||||
req.on('close', common.mustCall(() => {
|
||||
server.close();
|
||||
client.destroy();
|
||||
client.close();
|
||||
}));
|
||||
req.end();
|
||||
|
||||
}));
|
||||
|
@ -4,6 +4,7 @@ const common = require('../common');
|
||||
if (!common.hasCrypto)
|
||||
common.skip('missing crypto');
|
||||
const http2 = require('http2');
|
||||
const Countdown = require('../common/countdown');
|
||||
|
||||
const server = http2.createServer();
|
||||
|
||||
@ -15,29 +16,25 @@ server.on('stream', common.mustCall((stream) => {
|
||||
server.listen(0, common.mustCall(() => {
|
||||
const client = http2.connect(`http://localhost:${server.address().port}`);
|
||||
|
||||
let remaining = 3;
|
||||
function maybeClose() {
|
||||
if (--remaining === 0) {
|
||||
server.close();
|
||||
client.destroy();
|
||||
}
|
||||
}
|
||||
const countdown = new Countdown(2, () => {
|
||||
server.close();
|
||||
client.close();
|
||||
});
|
||||
|
||||
{
|
||||
// Request 1 will fail because there are two content-length header values
|
||||
const req = client.request({
|
||||
':method': 'POST',
|
||||
'content-length': 1,
|
||||
'Content-Length': 2
|
||||
});
|
||||
req.on('error', common.expectsError({
|
||||
// Request 1 will fail because there are two content-length header values
|
||||
common.expectsError(
|
||||
() => {
|
||||
client.request({
|
||||
':method': 'POST',
|
||||
'content-length': 1,
|
||||
'Content-Length': 2
|
||||
});
|
||||
}, {
|
||||
code: 'ERR_HTTP2_HEADER_SINGLE_VALUE',
|
||||
type: Error,
|
||||
message: 'Header field "content-length" must have only a single value'
|
||||
}));
|
||||
req.on('error', common.mustCall(maybeClose));
|
||||
req.end('a');
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
{
|
||||
// Request 2 will succeed
|
||||
@ -46,7 +43,8 @@ server.listen(0, common.mustCall(() => {
|
||||
'content-length': 1
|
||||
});
|
||||
req.resume();
|
||||
req.on('end', common.mustCall(maybeClose));
|
||||
req.on('end', common.mustCall());
|
||||
req.on('close', common.mustCall(() => countdown.dec()));
|
||||
req.end('a');
|
||||
}
|
||||
|
||||
@ -55,7 +53,8 @@ server.listen(0, common.mustCall(() => {
|
||||
// header to be set for non-payload bearing requests...
|
||||
const req = client.request({ 'content-length': 1 });
|
||||
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({
|
||||
code: 'ERR_HTTP2_STREAM_ERROR',
|
||||
type: Error,
|
||||
|
@ -44,6 +44,6 @@ server.listen(0, common.mustCall(() => {
|
||||
const req = client.request(src);
|
||||
req.on('close', common.mustCall(() => {
|
||||
server.close();
|
||||
client.destroy();
|
||||
client.close();
|
||||
}));
|
||||
}));
|
||||
|
@ -56,6 +56,6 @@ server.listen(0, common.mustCall(() => {
|
||||
req.on('response', common.mustCall(checkHeaders));
|
||||
req.on('close', common.mustCall(() => {
|
||||
server.close();
|
||||
client.destroy();
|
||||
client.close();
|
||||
}));
|
||||
}));
|
||||
|
@ -8,6 +8,7 @@ if (!common.hasCrypto)
|
||||
common.skip('missing crypto');
|
||||
const assert = require('assert');
|
||||
const http2 = require('http2');
|
||||
const Countdown = require('../common/countdown');
|
||||
|
||||
const server = http2.createServer();
|
||||
|
||||
@ -20,15 +21,12 @@ server.on('stream', common.mustCall((stream) => {
|
||||
|
||||
server.listen(0, common.mustCall(() => {
|
||||
const client = http2.connect(`http://localhost:${server.address().port}`);
|
||||
client.setMaxListeners(100);
|
||||
|
||||
let remaining = count;
|
||||
|
||||
function maybeClose() {
|
||||
if (--remaining === 0) {
|
||||
server.close();
|
||||
client.destroy();
|
||||
}
|
||||
}
|
||||
const countdown = new Countdown(count, () => {
|
||||
server.close();
|
||||
client.close();
|
||||
});
|
||||
|
||||
function doRequest() {
|
||||
const req = client.request({ ':method': 'POST ' });
|
||||
@ -38,8 +36,8 @@ server.listen(0, common.mustCall(() => {
|
||||
req.on('data', (chunk) => data += chunk);
|
||||
req.on('end', common.mustCall(() => {
|
||||
assert.strictEqual(data, 'abcdefghij');
|
||||
maybeClose();
|
||||
}));
|
||||
req.on('close', common.mustCall(() => countdown.dec()));
|
||||
|
||||
let n = 0;
|
||||
function writeChunk() {
|
||||
|
@ -25,7 +25,7 @@ server.listen(0, common.mustCall(() => {
|
||||
|
||||
const countdown = new Countdown(2, common.mustCall(() => {
|
||||
server.close();
|
||||
client.destroy();
|
||||
client.close();
|
||||
}));
|
||||
|
||||
{
|
||||
|
@ -10,9 +10,7 @@ const server = h2.createServer();
|
||||
|
||||
// we use the lower-level API here
|
||||
server.on('stream', common.mustNotCall());
|
||||
server.listen(0);
|
||||
|
||||
server.on('listening', common.mustCall(() => {
|
||||
server.listen(0, common.mustCall(() => {
|
||||
|
||||
// Setting the maxSendHeaderBlockLength, then attempting to send a
|
||||
// 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}`,
|
||||
options);
|
||||
|
||||
const req = client.request({ ':path': '/' });
|
||||
|
||||
const req = client.request();
|
||||
req.on('response', common.mustNotCall());
|
||||
|
||||
req.resume();
|
||||
req.on('end', common.mustCall(() => {
|
||||
client.destroy();
|
||||
req.on('close', common.mustCall(() => {
|
||||
client.close();
|
||||
server.close();
|
||||
}));
|
||||
|
||||
req.on('frameError', common.mustCall((type, code) => {
|
||||
@ -42,33 +40,4 @@ server.on('listening', common.mustCall(() => {
|
||||
type: Error,
|
||||
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');
|
||||
const assert = require('assert');
|
||||
const h2 = require('http2');
|
||||
const Countdown = require('../common/countdown');
|
||||
|
||||
const server = h2.createServer();
|
||||
let client;
|
||||
|
||||
const countdown = new Countdown(3, () => {
|
||||
server.close();
|
||||
client.close();
|
||||
});
|
||||
|
||||
// we use the lower-level API here
|
||||
server.on('stream', common.mustCall((stream) => {
|
||||
stream.respond({ ':status': 200 });
|
||||
|
||||
// The first pushStream will complete as normal
|
||||
stream.pushStream({
|
||||
':scheme': 'http',
|
||||
':path': '/foobar',
|
||||
':authority': `localhost:${server.address().port}`,
|
||||
}, common.mustCall((pushedStream) => {
|
||||
pushedStream.respond({ ':status': 200 });
|
||||
}, common.mustCall((err, pushedStream) => {
|
||||
assert.ifError(err);
|
||||
pushedStream.respond();
|
||||
pushedStream.end();
|
||||
pushedStream.on('aborted', common.mustNotCall());
|
||||
}));
|
||||
@ -27,52 +31,41 @@ server.on('stream', common.mustCall((stream) => {
|
||||
// will reject it due to the maxReservedRemoteStreams option
|
||||
// being set to only 1
|
||||
stream.pushStream({
|
||||
':scheme': 'http',
|
||||
':path': '/foobar',
|
||||
':authority': `localhost:${server.address().port}`,
|
||||
}, common.mustCall((pushedStream) => {
|
||||
pushedStream.respond({ ':status': 200 });
|
||||
}, common.mustCall((err, pushedStream) => {
|
||||
assert.ifError(err);
|
||||
pushedStream.respond();
|
||||
pushedStream.on('aborted', common.mustCall());
|
||||
pushedStream.on('error', common.mustNotCall());
|
||||
pushedStream.on('close',
|
||||
common.mustCall((code) => assert.strictEqual(code, 8)));
|
||||
pushedStream.on('close', common.mustCall((code) => {
|
||||
assert.strictEqual(code, 8);
|
||||
countdown.dec();
|
||||
}));
|
||||
}));
|
||||
|
||||
stream.respond();
|
||||
stream.end('hello world');
|
||||
}));
|
||||
server.listen(0);
|
||||
|
||||
server.on('listening', common.mustCall(() => {
|
||||
client = h2.connect(`http://localhost:${server.address().port}`,
|
||||
{ maxReservedRemoteStreams: 1 });
|
||||
|
||||
const options = {
|
||||
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': '/' });
|
||||
const req = client.request();
|
||||
|
||||
// Because maxReservedRemoteStream is 1, the stream event
|
||||
// must only be emitted once, even tho the server sends
|
||||
// two push streams.
|
||||
client.on('stream', common.mustCall((stream) => {
|
||||
stream.resume();
|
||||
stream.on('push', 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.resume();
|
||||
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.on('end', common.mustCall(() => {
|
||||
server.close();
|
||||
client.destroy();
|
||||
client.close();
|
||||
}));
|
||||
req.end();
|
||||
}));
|
||||
|
@ -80,7 +80,7 @@ server.listen(0, common.mustCall(() => {
|
||||
const req = client.request();
|
||||
req.resume();
|
||||
req.on('end', common.mustCall(() => {
|
||||
client.destroy();
|
||||
client.close();
|
||||
server.close();
|
||||
}));
|
||||
}));
|
||||
|
@ -8,7 +8,6 @@ const assert = require('assert');
|
||||
const http2 = require('http2');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const Countdown = require('../common/countdown');
|
||||
|
||||
// piping should work as expected with createWriteStream
|
||||
|
||||
@ -20,28 +19,28 @@ const server = http2.createServer();
|
||||
|
||||
server.on('stream', common.mustCall((stream) => {
|
||||
const dest = stream.pipe(fs.createWriteStream(fn));
|
||||
dest.on('finish', common.mustCall(() => {
|
||||
assert.strictEqual(fs.readFileSync(loc).length, fs.readFileSync(fn).length);
|
||||
fs.unlinkSync(fn);
|
||||
stream.respond();
|
||||
stream.end();
|
||||
}));
|
||||
|
||||
dest.on('finish', () => {
|
||||
assert.strictEqual(fs.readFileSync(loc).length,
|
||||
fs.readFileSync(fn).length);
|
||||
});
|
||||
stream.respond();
|
||||
stream.end();
|
||||
}));
|
||||
|
||||
server.listen(0, common.mustCall(() => {
|
||||
const port = server.address().port;
|
||||
const client = http2.connect(`http://localhost:${port}`);
|
||||
|
||||
const countdown = new Countdown(2, common.mustCall(() => {
|
||||
server.close();
|
||||
client.destroy();
|
||||
}));
|
||||
const client = http2.connect(`http://localhost:${server.address().port}`);
|
||||
|
||||
const req = client.request({ ':method': 'POST' });
|
||||
req.on('response', common.mustCall());
|
||||
req.resume();
|
||||
req.on('end', common.mustCall(() => countdown.dec()));
|
||||
|
||||
req.on('close', common.mustCall(() => {
|
||||
server.close();
|
||||
client.close();
|
||||
}));
|
||||
|
||||
const str = fs.createReadStream(loc);
|
||||
str.on('end', common.mustCall(() => countdown.dec()));
|
||||
str.on('end', common.mustCall());
|
||||
str.pipe(req);
|
||||
}));
|
||||
|
@ -54,7 +54,7 @@ server.on('listening', common.mustCall(() => {
|
||||
req.resume();
|
||||
req.on('end', common.mustCall(() => {
|
||||
server.close();
|
||||
client.destroy();
|
||||
client.close();
|
||||
}));
|
||||
req.end();
|
||||
|
||||
|
@ -74,13 +74,18 @@ function runTest(test) {
|
||||
|
||||
const client = http2.connect(url);
|
||||
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;
|
||||
req.resume();
|
||||
req.end();
|
||||
|
||||
req.on('end', common.mustCall(() => {
|
||||
client.destroy();
|
||||
client.close();
|
||||
|
||||
if (!tests.length) {
|
||||
server.close();
|
||||
|
@ -34,7 +34,7 @@ server.listen(0, () => {
|
||||
req.on('response', common.mustCall());
|
||||
req.on('data', common.mustNotCall());
|
||||
req.on('end', common.mustCall(() => {
|
||||
client.destroy();
|
||||
client.close();
|
||||
server.close();
|
||||
}));
|
||||
req.end();
|
||||
|
@ -38,7 +38,7 @@ server.listen(0, () => {
|
||||
|
||||
req.on('data', common.mustNotCall());
|
||||
req.on('end', common.mustCall(() => {
|
||||
client.destroy();
|
||||
client.close();
|
||||
server.close();
|
||||
}));
|
||||
req.end();
|
||||
|
@ -40,7 +40,7 @@ server.listen(0, () => {
|
||||
}));
|
||||
req.on('data', common.mustNotCall());
|
||||
req.on('end', common.mustCall(() => {
|
||||
client.destroy();
|
||||
client.close();
|
||||
server.close();
|
||||
}));
|
||||
req.end();
|
||||
|
@ -16,7 +16,7 @@ server.listen(0, () => {
|
||||
const req = client.request();
|
||||
req.on('response', common.mustCall());
|
||||
req.on('end', common.mustCall(() => {
|
||||
client.destroy();
|
||||
client.close();
|
||||
server.close();
|
||||
}));
|
||||
req.end();
|
||||
|
@ -6,14 +6,10 @@ if (!common.hasCrypto)
|
||||
const http2 = require('http2');
|
||||
const assert = require('assert');
|
||||
|
||||
const {
|
||||
HTTP2_HEADER_CONTENT_TYPE
|
||||
} = http2.constants;
|
||||
|
||||
const server = http2.createServer();
|
||||
server.on('stream', (stream) => {
|
||||
stream.respondWithFile(process.cwd(), {
|
||||
[HTTP2_HEADER_CONTENT_TYPE]: 'text/plain'
|
||||
'content-type': 'text/plain'
|
||||
}, {
|
||||
onError(err) {
|
||||
common.expectsError({
|
||||
@ -38,7 +34,7 @@ server.listen(0, () => {
|
||||
}));
|
||||
req.on('data', common.mustNotCall());
|
||||
req.on('end', common.mustCall(() => {
|
||||
client.destroy();
|
||||
client.close();
|
||||
server.close();
|
||||
}));
|
||||
req.end();
|
||||
|
@ -6,11 +6,6 @@ if (!common.hasCrypto)
|
||||
const fixtures = require('../common/fixtures');
|
||||
const http2 = require('http2');
|
||||
|
||||
const {
|
||||
HTTP2_HEADER_CONTENT_TYPE,
|
||||
HTTP2_HEADER_METHOD
|
||||
} = http2.constants;
|
||||
|
||||
const optionsWithTypeError = {
|
||||
offset: 'number',
|
||||
length: 'number',
|
||||
@ -33,6 +28,7 @@ const fname = fixtures.path('elipses.txt');
|
||||
const server = http2.createServer();
|
||||
|
||||
server.on('stream', common.mustCall((stream) => {
|
||||
|
||||
// Check for all possible TypeError triggers on options
|
||||
Object.keys(optionsWithTypeError).forEach((option) => {
|
||||
Object.keys(types).forEach((type) => {
|
||||
@ -42,7 +38,7 @@ server.on('stream', common.mustCall((stream) => {
|
||||
|
||||
common.expectsError(
|
||||
() => stream.respondWithFile(fname, {
|
||||
[http2.constants.HTTP2_HEADER_CONTENT_TYPE]: 'text/plain'
|
||||
'content-type': 'text/plain'
|
||||
}, {
|
||||
[option]: types[type]
|
||||
}),
|
||||
@ -59,7 +55,7 @@ server.on('stream', common.mustCall((stream) => {
|
||||
// Should throw if :status 204, 205 or 304
|
||||
[204, 205, 304].forEach((status) => common.expectsError(
|
||||
() => stream.respondWithFile(fname, {
|
||||
[HTTP2_HEADER_CONTENT_TYPE]: 'text/plain',
|
||||
'content-type': 'text/plain',
|
||||
':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
|
||||
stream.respond({
|
||||
':status': 200,
|
||||
});
|
||||
stream.respond({ ':status': 200 });
|
||||
common.expectsError(
|
||||
() => stream.respondWithFile(fname, {
|
||||
[HTTP2_HEADER_CONTENT_TYPE]: 'text/plain'
|
||||
'content-type': 'text/plain'
|
||||
}),
|
||||
{
|
||||
code: 'ERR_HTTP2_HEADERS_SENT',
|
||||
@ -104,21 +80,21 @@ function continueTest(stream) {
|
||||
stream.destroy();
|
||||
common.expectsError(
|
||||
() => stream.respondWithFile(fname, {
|
||||
[HTTP2_HEADER_CONTENT_TYPE]: 'text/plain'
|
||||
'content-type': 'text/plain'
|
||||
}),
|
||||
{
|
||||
code: 'ERR_HTTP2_INVALID_STREAM',
|
||||
message: 'The stream has been destroyed'
|
||||
}
|
||||
);
|
||||
}
|
||||
}));
|
||||
|
||||
server.listen(0, common.mustCall(() => {
|
||||
const client = http2.connect(`http://localhost:${server.address().port}`);
|
||||
const req = client.request();
|
||||
|
||||
req.on('close', common.mustCall(() => {
|
||||
client.destroy();
|
||||
client.close();
|
||||
server.close();
|
||||
}));
|
||||
req.end();
|
||||
|
@ -7,11 +7,6 @@ const fixtures = require('../common/fixtures');
|
||||
const http2 = require('http2');
|
||||
const fs = require('fs');
|
||||
|
||||
const {
|
||||
HTTP2_HEADER_CONTENT_TYPE,
|
||||
HTTP2_HEADER_METHOD
|
||||
} = http2.constants;
|
||||
|
||||
const optionsWithTypeError = {
|
||||
offset: 'number',
|
||||
length: 'number',
|
||||
@ -43,7 +38,7 @@ server.on('stream', common.mustCall((stream) => {
|
||||
|
||||
common.expectsError(
|
||||
() => stream.respondWithFD(types[type], {
|
||||
[HTTP2_HEADER_CONTENT_TYPE]: 'text/plain'
|
||||
'content-type': 'text/plain'
|
||||
}),
|
||||
{
|
||||
type: TypeError,
|
||||
@ -62,7 +57,7 @@ server.on('stream', common.mustCall((stream) => {
|
||||
|
||||
common.expectsError(
|
||||
() => stream.respondWithFD(fd, {
|
||||
[HTTP2_HEADER_CONTENT_TYPE]: 'text/plain'
|
||||
'content-type': 'text/plain'
|
||||
}, {
|
||||
[option]: types[type]
|
||||
}),
|
||||
@ -79,7 +74,7 @@ server.on('stream', common.mustCall((stream) => {
|
||||
// Should throw if :status 204, 205 or 304
|
||||
[204, 205, 304].forEach((status) => common.expectsError(
|
||||
() => stream.respondWithFD(fd, {
|
||||
[HTTP2_HEADER_CONTENT_TYPE]: 'text/plain',
|
||||
'content-type': 'text/plain',
|
||||
':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
|
||||
stream.respond({
|
||||
':status': 200,
|
||||
});
|
||||
stream.respond();
|
||||
common.expectsError(
|
||||
() => stream.respondWithFD(fd, {
|
||||
[HTTP2_HEADER_CONTENT_TYPE]: 'text/plain'
|
||||
'content-type': 'text/plain'
|
||||
}),
|
||||
{
|
||||
code: 'ERR_HTTP2_HEADERS_SENT',
|
||||
@ -130,7 +101,7 @@ server.on('stream', common.mustCall((stream) => {
|
||||
stream.destroy();
|
||||
common.expectsError(
|
||||
() => stream.respondWithFD(fd, {
|
||||
[HTTP2_HEADER_CONTENT_TYPE]: 'text/plain'
|
||||
'content-type': 'text/plain'
|
||||
}),
|
||||
{
|
||||
code: 'ERR_HTTP2_INVALID_STREAM',
|
||||
@ -145,7 +116,7 @@ server.listen(0, common.mustCall(() => {
|
||||
const req = client.request();
|
||||
|
||||
req.on('close', common.mustCall(() => {
|
||||
client.destroy();
|
||||
client.close();
|
||||
server.close();
|
||||
}));
|
||||
req.end();
|
||||
|
@ -31,7 +31,7 @@ server.listen(0, () => {
|
||||
req.on('data', common.mustNotCall());
|
||||
req.on('end', common.mustCall(() => {
|
||||
assert.strictEqual(req.rstCode, NGHTTP2_INTERNAL_ERROR);
|
||||
client.destroy();
|
||||
client.close();
|
||||
server.close();
|
||||
}));
|
||||
req.end();
|
||||
|
@ -9,6 +9,7 @@ const fixtures = require('../common/fixtures');
|
||||
const http2 = require('http2');
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const Countdown = require('../common/countdown');
|
||||
|
||||
const {
|
||||
HTTP2_HEADER_CONTENT_TYPE,
|
||||
@ -39,7 +40,7 @@ server.on('stream', (stream, headers) => {
|
||||
statCheck: common.mustCall((stat, headers, options) => {
|
||||
assert.strictEqual(options.length, length);
|
||||
assert.strictEqual(options.offset, offset);
|
||||
headers[HTTP2_HEADER_CONTENT_LENGTH] =
|
||||
headers['content-length'] =
|
||||
Math.min(options.length, stat.size - offset);
|
||||
}),
|
||||
offset: offset,
|
||||
@ -47,23 +48,21 @@ server.on('stream', (stream, headers) => {
|
||||
});
|
||||
});
|
||||
server.on('close', common.mustCall(() => fs.closeSync(fd)));
|
||||
|
||||
server.listen(0, () => {
|
||||
const client = http2.connect(`http://localhost:${server.address().port}`);
|
||||
|
||||
let remaining = 2;
|
||||
function maybeClose() {
|
||||
if (--remaining === 0) {
|
||||
client.destroy();
|
||||
server.close();
|
||||
}
|
||||
}
|
||||
const countdown = new Countdown(2, () => {
|
||||
client.close();
|
||||
server.close();
|
||||
});
|
||||
|
||||
{
|
||||
const req = client.request({ range: 'bytes=8-11' });
|
||||
|
||||
req.on('response', common.mustCall((headers) => {
|
||||
assert.strictEqual(headers[HTTP2_HEADER_CONTENT_TYPE], 'text/plain');
|
||||
assert.strictEqual(+headers[HTTP2_HEADER_CONTENT_LENGTH], 3);
|
||||
assert.strictEqual(headers['content-type'], 'text/plain');
|
||||
assert.strictEqual(+headers['content-length'], 3);
|
||||
}));
|
||||
req.setEncoding('utf8');
|
||||
let check = '';
|
||||
@ -71,7 +70,7 @@ server.listen(0, () => {
|
||||
req.on('end', common.mustCall(() => {
|
||||
assert.strictEqual(check, data.toString('utf8', 8, 11));
|
||||
}));
|
||||
req.on('close', common.mustCall(maybeClose));
|
||||
req.on('close', common.mustCall(() => countdown.dec()));
|
||||
req.end();
|
||||
}
|
||||
|
||||
@ -88,7 +87,7 @@ server.listen(0, () => {
|
||||
req.on('end', common.mustCall(() => {
|
||||
assert.strictEqual(check, data.toString('utf8', 8, 28));
|
||||
}));
|
||||
req.on('close', common.mustCall(maybeClose));
|
||||
req.on('close', common.mustCall(() => countdown.dec()));
|
||||
req.end();
|
||||
}
|
||||
|
||||
|
@ -40,7 +40,7 @@ server.listen(0, () => {
|
||||
req.on('data', (chunk) => check += chunk);
|
||||
req.on('end', common.mustCall(() => {
|
||||
assert.strictEqual(check, data.toString('utf8'));
|
||||
client.destroy();
|
||||
client.close();
|
||||
server.close();
|
||||
}));
|
||||
req.end();
|
||||
|
@ -29,7 +29,8 @@ server.on('stream', (stream) => {
|
||||
stream.pushStream({
|
||||
':path': '/file.txt',
|
||||
':method': 'GET'
|
||||
}, (stream) => {
|
||||
}, (err, stream) => {
|
||||
assert.ifError(err);
|
||||
stream.respondWithFD(fd, {
|
||||
[HTTP2_HEADER_CONTENT_TYPE]: 'text/plain',
|
||||
[HTTP2_HEADER_CONTENT_LENGTH]: stat.size,
|
||||
@ -50,7 +51,7 @@ server.listen(0, () => {
|
||||
function maybeClose() {
|
||||
if (--expected === 0) {
|
||||
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