Skip to content
This repository has been archived by the owner on Jul 6, 2018. It is now read-only.

Commit

Permalink
http2: significant internal refactoring
Browse files Browse the repository at this point in the history
Eliminate queuing of internal callbacks. Queuing these to fire on
the next event loop tick was throwing off timing. Now the js callbacks
are called directly as they happen. This will require a bit more
finesse on the javascript side (to ensure appropiate timing of
destroy/shutdown actions) but that is handled from within the core
api impl so users should not be affected.

Other needed changes are included.
  • Loading branch information
jasnell committed May 26, 2017
1 parent 96afff0 commit 9430a78
Show file tree
Hide file tree
Showing 20 changed files with 1,060 additions and 918 deletions.
227 changes: 159 additions & 68 deletions doc/api/http2.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ have occasion to work with the `Http2Session` object directly, with most
actions typically taken through interactions with either the `Http2Server` or
`Http2Stream` objects.

#### Http2Stream and Sockets
#### Http2Session and Sockets

Every `Http2Session` instance is associated with exactly one [`net.Socket`][] or
[`tls.TLSSocket`][] when it is created. When either the `Socket` or the
Expand Down Expand Up @@ -223,7 +223,7 @@ This method is only available if `http2session.type` is equal to
* stream {Http2Stream}
* code {number}

Sends an `RST-STREAM` frame to the connected HTTP/2 peer, causing the given
Sends an `RST_STREAM` frame to the connected HTTP/2 peer, causing the given
`Http2Stream` to be closed on both sides using error code `code`.

#### http2session.setTimeout(msecs, callback)
Expand All @@ -238,8 +238,6 @@ Sends an `RST-STREAM` frame to the connected HTTP/2 peer, causing the given
* `options` {Object}
* `graceful` {boolean} `true` to attempt a polite shutdown of the
`Http2Session`.
* `immediate` {boolean} `true` to force the shutdown to occur immediately,
regardless of any data transfer that may be pending.
* `errorCode` {number} The HTTP/2 Error Code to return. Note that this is
*not* the same thing as an HTTP Response Status Code.
* `lastStreamID` {number} The Stream ID of the last successfully processed
Expand Down Expand Up @@ -331,86 +329,91 @@ On the client, `Http2Stream` instances are created and returned when either the
`http2session.request()` method is called, or in response to an incoming
`'push'` event.

#### Event: 'aborted'
*Note*: The `Http2Stream` class is a base for the [`ServerHttp2Stream`][] and
[`ClientHttp2Stream`][] classes, each of which are used specifically by either
the Server or Client side, respectively.

The `'aborted'` event is emitted whenever a `Http2Stream` instance is
abnormally aborted in mid-communication.
All `Http2Stream` instances are [`Duplex`][] streams. The `Writable` side of the
`Duplex` is used to send data to the connected peer, while the `Readable` side
is used to receive data sent by the connected peer.

#### Event: 'error'
#### Http2Stream Lifecycle

(TODO: fill in detail)
##### Creation

#### Event: 'fetchTrailers'
On the server side, instances of [`ServerHttp2Stream`][] are created either
when:

The `'fetchTrailers`' event is emitted by the `Http2Stream` immediately after
queuing the last chunk of payload data to be sent. The listener callback is
passed a single object (with a `null` prototype) that the listener may used
to specify the trailing header fields to send to the peer.
* A new HTTP/2 `HEADERS` frame with a previously unused stream ID is received;
* The `http2stream.pushStream()` method is called.

```js
stream.on('fetchTrailers', (trailers) => {
trailers['ABC'] = 'some value to send';
});
```
On the client side, instances of [`ClientHttp2Stream`[] are created when the
`http2session.request()` method is called.

#### Event: 'headers'
*Note*: On the client, the `Http2Stream` instance returned by
`http2session.request()` may not be immediately ready for use if the parent
`Http2Session` has not yet been fully established. In such cases, operations
called on the `Http2Stream` will be buffered until the `'ready'` event is
emitted. User code should rarely, if ever, have need to handle the `'ready'`
event directly. The ready status of an `Http2Stream` can be determined by
checking the value of `http2stream.id`. If the value is `undefined`, the stream
is not yet ready for use.

The `'headers'` event is emitted when a block of headers has been received
on the `Http2Stream`, and the block does not correspond with an HTTP request,
response or push request. The listener callback is passed the [Headers Object][]
and flags associated with the headers.
##### Destruction

```js
stream.on('headers', (headers, flags) => {
// TODO(jasnell): Fill in example
});
```
All [`Http2Stream`][] instances are destroyed either when:

#### Event: 'push'
* An `RST_STREAM` frame for the stream is received by the connected peer.
* The `http2stream.rstStream()` or `http2session.rstStream()` methods are
called.
* The `http2stream.destroy()` or `http2session.destroy()` methods are called.

The `'push'` event is emitted when response headers for a Server Push stream
are received. The listener callback is passed the [Headers Object][] and flags
associated with the headers.
When an `Http2Stream` instance is destroyed, an attempt will be made to send an
`RST_STREAM` frame will be sent to the connected peer.

```js
stream.on('push', (headers, flags) => {
// TODO(jasnell): Fill in example
});
```
Once the `Http2Stream` instance is destroyed, the `'streamClosed'` event will
be emitted. Because `Http2Stream` is an instance of `stream.Duplex`, the
`'end'` event will also be emitted if the stream data is currently flowing.
The `'error'` event may also be emitted if `http2stream.destroy()` was called
with an `Error` passed as the first argument.

#### Event: 'request'
After the `Http2Stream` has been destroyed, the `http2stream.destroyed`
property will be `true` and the `http2stream.rstCode` property will specify the
`RST_STREAM` error code. The `Http2Stream` instance is no longer usable once
destroyed.

The `'request'` event is emitted when a block of headers associated with an
HTTP request is received. The listener callback is passed the [Headers Object][]
and flags associated with the headers.
#### Event: 'aborted'

```js
stream.on('request', (headers, flags) => {
// TODO(jasnell): Fill in example
});
```
The `'aborted'` event is emitted whenever a `Http2Stream` instance is
abnormally aborted in mid-communication.

This is emitted only when `http2session.type` is equal to
`http2.constants.NGHTTP_SESSION_SERVER`.
#### Event: 'error'

#### Event: 'response'
The `'error'` event is emitted when an error occurs during the processing of
an `Http2Stream`.

The `'response'` event is emitted when a block of headers associated with an
HTTP response is received. The listener callback is passed the
[Headers Object][] and flags associated with the headers.
#### Event: 'fetchTrailers'

The `'fetchTrailers`' event is emitted by the `Http2Stream` immediately after
queuing the last chunk of payload data to be sent. The listener callback is
passed a single object (with a `null` prototype) that the listener may used
to specify the trailing header fields to send to the peer.

```js
stream.on('response', (headers, flags) => {
// TODO(jasnell): Fill in example
stream.on('fetchTrailers', (trailers) => {
trailers['ABC'] = 'some value to send';
});
```

This is emitted only when `http2session.type` is equal to
`http2.constants.NGHTTP_SESSION_CLIENT`.
*Note*: The HTTP/1 specification forbids trailers from containing HTTP/2
"pseudo-header" fields (e.g. `':status'`, `':path'`, etc). An `'error'` event
will be emitted if the `'fetchTrailers'` event handler attempts to set such
header fields.

#### Event: 'streamClosed'

The `'streamClosed'` event is emitted when the `Http2Stream` is closed.
The `'streamClosed'` event is emitted when the `Http2Stream` is destroyed. Once
this event is emitted, the `Http2Stream` instance is no longer usable.

#### Event: 'timeout'

Expand All @@ -428,6 +431,13 @@ stream.on('trailers', (headers, flags) => {
});
```

#### http2stream.destroyed

* Value: {boolean}

Set to `true` if the `Http2Stream` instance has been destroyed and is no longer
usable.

#### http2stream.priority(options)

* `options` {Object}
Expand All @@ -445,11 +455,19 @@ Updates the priority for this `Http2Stream` instance. If `options.silent`
is `false`, causes a new `PRIORITY` frame to be sent to the connected HTTP/2
peer.

#### http2stream.rstCode

* Value: {number}

Set to the `RST_STREAM` error code when the `Http2Stream` is destroyed after
either receiving an `RST_STREAM` frame from the connected peer, calling
`http2stream.rstStream()`, or `http2stream.destroy()`.

#### http2stream.rstStream(code)

* `code` {number}

Sends an `RST-STREAM` frame to the connected HTTP/2 peer, causing this
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()
Expand All @@ -472,18 +490,12 @@ Shortcut for `http2stream.rstStream()` using error code `REFUSED_STREAM`.

Shortcut for `http2stream.rstStream()` using error code `INTERNAL_ERROR`.

#### http2stream.sendHeaders(headers)

* `headers` {[Headers Object][]}

Sends a `HEADERS` frame to the connected HTTP/2 peer.
(TODO: fill in detail)

#### http2stream.session

* Value: {Http2Sesssion}

A reference to the `Http2Session` instance that owns this `Http2Stream`.
A reference to the `Http2Session` instance that owns this `Http2Stream`. The
value will be `undefined` after the `Http2Stream` instance is destroyed.

#### http2stream.setTimeout(msecs, callback)

Expand All @@ -504,10 +516,85 @@ A reference to the `Http2Session` instance that owns this `Http2Stream`.

A current state of this `Http2Stream`.

### Class: ClientHttp2Stream

* Extends {Http2Stream}

The `ClientHttp2Stream` class is an extension of `Http2Stream` that is
used exclusively on HTTP/2 Clients. `Http2Stream` instances on the client
provide events such as `'response'` and `'push'` that are only relevant on
the client.

#### Event: 'headers'

The `'headers'` event is emitted when an additional block of headers is received
for a stream, such as when a block of `1xx` informational headers are received.
The listener callback is passed the [Headers Object][] and flags associated with
the headers.

```js
stream.on('headers', (headers, flags) => {
// TODO(jasnell): Fill in example
});
```

#### Event: 'push'

The `'push'` event is emitted when response headers for a Server Push stream
are received. The listener callback is passed the [Headers Object][] and flags
associated with the headers.

```js
stream.on('push', (headers, flags) => {
// TODO(jasnell): Fill in example
});
```

#### Event: 'response'

The `'response'` event is emitted when a response `HEADERS` frame has been
received for this stream from the connected HTTP/2 server. The listener is
invoked with two arguments: an Object containing the received
[Headers Object][], and flags associated with the headers.

For example:

```js
const http2 = require('http');
const client = http2.connect('https://localhost');
const req = client.request({ ':path': '/' });
req.on('response', (headers, flags) => {
console.log(headers[':status']);
});
```

### Class: ServerHttp2Stream

* Extends: {Http2Stream}

The `ServerHttp2Stream` class is an extension of [`Http2Stream`][] that is
used exclusively on HTTP/2 Servers. `Http2Stream` instances on the server
provide additional methods such as `http2stream.pushStream()` and
`http2stream.respond()` that are only relevant on the server.

#### Event: 'request'

The `'request'` event is emitted when a block of headers associated with an
HTTP request is received. The listener callback is passed the [Headers Object][]
and flags associated with the headers.

```js
stream.on('request', (headers, flags) => {
// TODO(jasnell): Fill in example
});
```

#### http2stream.additionalHeaders(headers)

* `headers` {[Headers Object][]}

Sends an additional informational `HEADERS` frame to the connected HTTP/2 peer.

#### http2stream.pushStream(headers[, options], callback)

* `headers` {[Headers Object][]}
Expand Down Expand Up @@ -916,8 +1003,12 @@ TBD
[`net.Socket`]: net.html
[`tls.TLSSocket`]: tls.html
[`tls.createServer()`]: tls.html#tls_tls_createserver_options_secureconnectionlistener
[ClientHttp2Stream]: #http2_class_clienthttp2stream
[Compatibility API: #http2_compatibility_api
[`Duplex`]: stream.html#stream_class_stream_duplex
[Headers Object]: #http2_headers_object
[Settings Object]: #http2_settings_object
[Http2Stream]: #http2_class_http2stream
[Http2Session and Sockets]: #http2_http2sesion_and_sockets
[ServerHttp2Stream]: #http2_class_serverhttp2stream
[Settings Object]: #http2_settings_object
[Using options.selectPadding]: #http2_using_options_selectpadding
Loading

0 comments on commit 9430a78

Please sign in to comment.