summaryrefslogtreecommitdiff
path: root/doc
diff options
context:
space:
mode:
authorMatteo Collina <hello@matteocollina.com>2017-07-24 18:34:17 +0100
committerJames M Snell <jasnell@gmail.com>2017-08-04 12:56:50 -0700
commita4017736d213a580a8bdd1ac3184e74d29b87d67 (patch)
tree656ffd377232a6ab5624c720f5d0b7ec916dc3d9 /doc
parentd6a774b1bd2c7ccbd783b06a95f6eeeb8dca210c (diff)
downloadnode-new-a4017736d213a580a8bdd1ac3184e74d29b87d67.tar.gz
http2: doc and fixes to the Compatibility API
PR-URL: https://github.com/nodejs/node/pull/14239 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Diffstat (limited to 'doc')
-rwxr-xr-xdoc/api/http2.md777
1 files changed, 773 insertions, 4 deletions
diff --git a/doc/api/http2.md b/doc/api/http2.md
index cc7f3ea3b5..6f106c69c1 100755
--- a/doc/api/http2.md
+++ b/doc/api/http2.md
@@ -16,9 +16,10 @@ in order to use the `'http2'` module.
The Core API provides a low-level interface designed specifically around
support for HTTP/2 protocol features. It is specifically *not* designed for
-compatibility with the existing [HTTP/1][] module API.
+compatibility with the existing [HTTP/1][] module API. However, the [Compatibility API][] is.
-The following illustrates a simple, plain-text HTTP/2 server:
+The following illustrates a simple, plain-text HTTP/2 server using the
+Core API:
```js
const http2 = require('http2');
@@ -27,6 +28,7 @@ const http2 = require('http2');
const server = http2.createServer();
server.on('stream', (stream, headers) => {
+ // stream is a Duplex
stream.respond({
'content-type': 'text/html',
':status': 200
@@ -44,6 +46,7 @@ const http2 = require('http2');
const client = http2.connect('http://localhost:80');
+// req is a Duplex
const req = client.request({ ':path': '/' });
req.on('response', (headers) => {
@@ -1171,6 +1174,17 @@ server.on('stream', (stream, headers, flags) => {
});
```
+#### Event: 'request'
+<!-- YAML
+added: REPLACEME
+-->
+
+* `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](compatiblity-api).
+
#### Event: 'timeout'
<!-- YAML
added: REPLACEME
@@ -1246,6 +1260,17 @@ server.on('stream', (stream, headers, flags) => {
});
```
+#### Event: 'request'
+<!-- YAML
+added: REPLACEME
+-->
+
+* `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](compatiblity-api).
+
#### Event: 'timeout'
<!-- YAML
added: REPLACEME
@@ -1314,7 +1339,8 @@ added: REPLACEME
* `options` {Object}
* `allowHTTP1` {boolean} Incoming client connections that do not support
HTTP/2 will be downgraded to HTTP/1.x when set to `true`. The default value
- is `false`. See the [`'unknownProtocol'`][] event.
+ is `false`. See the [`'unknownProtocol'`][] event. See [ALPN
+ negotiation](#alpn-negotiation).
* `maxDeflateDynamicTableSize` {number} Sets the maximum dynamic table size
for deflating header fields. Defaults to 4Kib.
* `maxSendHeaderBlockLength` {number} Sets the maximum allowed size for a
@@ -1701,16 +1727,755 @@ req.end('Jane');
## Compatibility API
-TBD
+The Compatibility API has the goal of providing a similar developer experience of
+HTTP/1 when using HTTP/2, making it possible to develop applications
+that supports both [HTTP/1](HTTP/1) and HTTP/2. This API targets only the **public
+API** of the [HTTP/1](HTTP/1), however many modules uses internal
+methods or state, and those _are not supported_ as it is a completely
+different implementation.
+
+The following example creates an HTTP/2 server using the compatibility
+API:
+
+```js
+const http2 = require('http2');
+const server = http2.createServer((req, res) => {
+ res.setHeader('Content-Type', 'text/html');
+ res.setHeader('X-Foo', 'bar');
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
+ res.end('ok');
+});
+```
+
+In order to create a mixed [HTTPs](https) and HTTP/2 server, refer to the
+[ALPN negotiation](alpn-negotiation) section.
+Upgrading from non-tls HTTP/1 servers is not supported.
+
+The HTTP2 compatibility API is composed of [`Http2ServerRequest`]() and
+[`Http2ServerResponse`](). They aim at API compatibility with HTTP/1, but
+they do not hide the differences between the protocols. As an example,
+the status message for HTTP codes is ignored.
+
+### ALPN negotiation
+
+ALPN negotiation allows to support both [HTTPs](https) and HTTP/2 over
+the same socket. the `req` and `res` object could be either HTTP/1 or
+HTTP/2, and an application **must** restrict itself to the public API of
+[HTTP/1](), and detect if it is possible to use the more advanced
+features of HTTP/2.
+
+The following example creates a server that supports both protocols:
+
+```js
+const { createSecureServer } = require('http2');
+const { readFileSync } = require('fs');
+
+const cert = fs.readFileSync('./cert.pem');
+const key = fs.readFileSync('./key.pem');
+
+const server = createSecureServer(
+ { cert, key, allowHTTP1: true },
+ onRequest
+).listen(4443);
+
+function onRequest(req, res) {
+ // detects if it is a HTTPs request or HTTP/2
+ const { socket: { alpnProtocol } } = request.httpVersion === '2.0' ?
+ request.stream.session : request;
+ response.writeHead(200, { 'content-type': 'application/json' });
+ response.end(JSON.stringify({
+ alpnProtocol,
+ httpVersion: request.httpVersion
+ }));
+}
+```
+
+The `'request'` event works identically on both [HTTPs](https) and
+HTTP/2.
+
+### Class: http2.Http2ServerRequest
+<!-- YAML
+added: REPLACEME
+-->
+
+A `Http2ServerRequest` object is created by [`http2.Server`][] or
+[`http2.SecureServer`][] and passed as the first argument to the [`'request'`][] event. It may be used to access a request status,
+headers and data.
+
+It implements the [Readable Stream][] interface, as well as the
+following additional events, methods, and properties.
+
+#### Event: 'aborted'
+<!-- YAML
+added: REPLACEME
+-->
+
+The `'aborted'` event is emitted whenever a `Http2ServerRequest` instance is
+abnormally aborted in mid-communication.
+
+*Note*: The `'aborted'` event will only be emitted if the
+`Http2ServerRequest` writable side has not been ended.
+
+#### Event: 'close'
+<!-- YAML
+added: REPLACEME
+-->
+
+Indicates that the underlying [`Http2Stream`]() was closed.
+Just like `'end'`, this event occurs only once per response.
+
+#### request.destroy([error])
+<!-- YAML
+added: REPLACEME
+-->
+
+* `error` {Error}
+
+Calls `destroy()` on the [Http2Stream]() that received the `ServerRequest`. If `error`
+is provided, an `'error'` event is emitted and `error` is passed as an argument
+to any listeners on the event.
+
+It does nothing if the stream was already destroyed.
+
+#### request.headers
+<!-- YAML
+added: REPLACEME
+-->
+
+* {Object}
+
+The request/response headers object.
+
+Key-value pairs of header names and values. Header names are lower-cased.
+Example:
+
+```js
+// Prints something like:
+//
+// { 'user-agent': 'curl/7.22.0',
+// host: '127.0.0.1:8000',
+// accept: '*/*' }
+console.log(request.headers);
+```
+
+See [Headers Object][].
+
+### request.httpVersion
+<!-- YAML
+added: REPLACEME
+-->
+
+* {string}
+
+In case of server request, the HTTP version sent by the client. In the case of
+client response, the HTTP version of the connected-to server. Returns
+`'2.0'`.
+
+Also `message.httpVersionMajor` is the first integer and
+`message.httpVersionMinor` is the second.
+
+#### request.method
+<!-- YAML
+added: REPLACEME
+-->
+
+* {string}
+
+The request method as a string. Read only. Example:
+`'GET'`, `'DELETE'`.
+
+#### request.rawHeaders
+<!-- YAML
+added: REPLACEME
+-->
+
+* {Array}
+
+The raw request/response headers list exactly as they were received.
+
+Note that the keys and values are in the same list. It is *not* a
+list of tuples. So, the even-numbered offsets are key values, and the
+odd-numbered offsets are the associated values.
+
+Header names are not lowercased, and duplicates are not merged.
+
+```js
+// Prints something like:
+//
+// [ 'user-agent',
+// 'this is invalid because there can be only one',
+// 'User-Agent',
+// 'curl/7.22.0',
+// 'Host',
+// '127.0.0.1:8000',
+// 'ACCEPT',
+// '*/*' ]
+console.log(request.rawHeaders);
+```
+
+#### request.rawTrailers
+<!-- YAML
+added: REPLACEME
+-->
+
+* {Array}
+
+The raw request/response trailer keys and values exactly as they were
+received. Only populated at the `'end'` event.
+
+#### request.setTimeout(msecs, callback)
+<!-- YAML
+added: REPLACEME
+-->
+
+* `msecs` {number}
+* `callback` {Function}
+
+Calls `request.connection.setTimeout(msecs, callback)`.
+
+Returns `request`.
+
+#### request.socket
+<!-- YAML
+added: REPLACEME
+-->
+
+* {net.Socket}
+
+The [`net.Socket`][] object associated with the connection.
+
+With TLS support, use [`request.socket.getPeerCertificate()`][] to obtain the
+client's authentication details.
+
+*Note*: do not use this socket object to send or receive any data. All
+data transfers are managed by HTTP/2 and data might be lost.
+
+#### request.stream
+<!-- YAML
+added: REPLACEME
+-->
+
+* {http2.Http2Stream}
+
+The [`Http2Stream`][] object backing the request.
+
+#### request.trailers
+<!-- YAML
+added: REPLACEME
+-->
+
+* {Object}
+
+The request/response trailers object. Only populated at the `'end'` event.
+
+#### request.url
+<!-- YAML
+added: REPLACEME
+-->
+
+* {string}
+
+Request URL string. This contains only the URL that is
+present in the actual HTTP request. If the request is:
+
+```txt
+GET /status?name=ryan HTTP/1.1\r\n
+Accept: text/plain\r\n
+\r\n
+```
+
+Then `request.url` will be:
+
+<!-- eslint-disable semi -->
+```js
+'/status?name=ryan'
+```
+
+To parse the url into its parts `require('url').parse(request.url)`
+can be used. Example:
+
+```txt
+$ node
+> require('url').parse('/status?name=ryan')
+Url {
+ protocol: null,
+ slashes: null,
+ auth: null,
+ host: null,
+ port: null,
+ hostname: null,
+ hash: null,
+ search: '?name=ryan',
+ query: 'name=ryan',
+ pathname: '/status',
+ path: '/status?name=ryan',
+ href: '/status?name=ryan' }
+```
+
+To extract the parameters from the query string, the
+`require('querystring').parse` function can be used, or
+`true` can be passed as the second argument to `require('url').parse`.
+Example:
+
+```txt
+$ node
+> require('url').parse('/status?name=ryan', true)
+Url {
+ protocol: null,
+ slashes: null,
+ auth: null,
+ host: null,
+ port: null,
+ hostname: null,
+ hash: null,
+ search: '?name=ryan',
+ query: { name: 'ryan' },
+ pathname: '/status',
+ path: '/status?name=ryan',
+ href: '/status?name=ryan' }
+```
+
+### Class: http2.Http2ServerResponse
+<!-- YAML
+added: REPLACEME
+-->
+
+This object is created internally by an HTTP server--not by the user. It is
+passed as the second parameter to the [`'request'`][] event.
+
+The response implements, but does not inherit from, the [Writable Stream][]
+interface. This is an [`EventEmitter`][] with the following events:
+
+### Event: 'close'
+<!-- YAML
+added: REPLACEME
+-->
+
+Indicates that the underlying [`Http2Stream`]() was terminated before
+[`response.end()`][] was called or able to flush.
+
+### Event: 'finish'
+<!-- YAML
+added: REPLACEME
+-->
+
+Emitted when the response has been sent. More specifically, this event is
+emitted when the last segment of the response headers and body have been
+handed off to the HTTP/2 multiplexing for transmission over the network. It
+does not imply that the client has received anything yet.
+
+After this event, no more events will be emitted on the response object.
+
+### response.addTrailers(headers)
+<!-- YAML
+added: REPLACEME
+-->
+
+* `headers` {Object}
+
+This method adds HTTP trailing headers (a header but at the end of the
+message) to the response.
+
+Attempting to set a header field name or value that contains invalid characters
+will result in a [`TypeError`][] being thrown.
+
+### response.connection
+<!-- YAML
+added: REPLACEME
+-->
+
+* {net.Socket}
+
+See [`response.socket`][].
+
+### response.end([data][, encoding][, callback])
+<!-- YAML
+added: REPLACEME
+-->
+
+* `data` {string|Buffer}
+* `encoding` {string}
+* `callback` {Function}
+
+This method signals to the server that all of the response headers and body
+have been sent; that server should consider this message complete.
+The method, `response.end()`, MUST be called on each response.
+
+If `data` is specified, it is equivalent to calling
+[`response.write(data, encoding)`][] followed by `response.end(callback)`.
+
+If `callback` is specified, it will be called when the response stream
+is finished.
+
+### response.finished
+<!-- YAML
+added: REPLACEME
+-->
+
+* {boolean}
+
+Boolean value that indicates whether the response has completed. Starts
+as `false`. After [`response.end()`][] executes, the value will be `true`.
+
+### response.getHeader(name)
+<!-- YAML
+added: REPLACEME
+-->
+
+* `name` {string}
+* Returns: {string}
+
+Reads out a header that's already been queued but not sent to the client.
+Note that the name is case insensitive.
+
+Example:
+
+```js
+const contentType = response.getHeader('content-type');
+```
+
+### response.getHeaderNames()
+<!-- YAML
+added: REPLACEME
+-->
+
+* Returns: {Array}
+
+Returns an array containing the unique names of the current outgoing headers.
+All header names are lowercase.
+
+Example:
+
+```js
+response.setHeader('Foo', 'bar');
+response.setHeader('Set-Cookie', ['foo=bar', 'bar=baz']);
+
+const headerNames = response.getHeaderNames();
+// headerNames === ['foo', 'set-cookie']
+```
+
+### response.getHeaders()
+<!-- YAML
+added: REPLACEME
+-->
+
+* Returns: {Object}
+
+Returns a shallow copy of the current outgoing headers. Since a shallow copy
+is used, array values may be mutated without additional calls to various
+header-related http module methods. The keys of the returned object are the
+header names and the values are the respective header values. All header names
+are lowercase.
+
+*Note*: The object returned by the `response.getHeaders()` method _does not_
+prototypically inherit from the JavaScript `Object`. This means that typical
+`Object` methods such as `obj.toString()`, `obj.hasOwnProperty()`, and others
+are not defined and *will not work*.
+
+Example:
+
+```js
+response.setHeader('Foo', 'bar');
+response.setHeader('Set-Cookie', ['foo=bar', 'bar=baz']);
+
+const headers = response.getHeaders();
+// headers === { foo: 'bar', 'set-cookie': ['foo=bar', 'bar=baz'] }
+```
+
+### response.hasHeader(name)
+<!-- YAML
+added: REPLACEME
+-->
+
+* `name` {string}
+* Returns: {boolean}
+
+Returns `true` if the header identified by `name` is currently set in the
+outgoing headers. Note that the header name matching is case-insensitive.
+
+Example:
+
+```js
+const hasContentType = response.hasHeader('content-type');
+```
+
+### response.headersSent
+<!-- YAML
+added: REPLACEME
+-->
+
+* {boolean}
+
+Boolean (read-only). True if headers were sent, false otherwise.
+
+### response.removeHeader(name)
+<!-- YAML
+added: REPLACEME
+-->
+
+* `name` {string}
+
+Removes a header that's queued for implicit sending.
+
+Example:
+
+```js
+response.removeHeader('Content-Encoding');
+```
+
+### response.sendDate
+<!-- YAML
+added: REPLACEME
+-->
+
+* {boolean}
+
+When true, the Date header will be automatically generated and sent in
+the response if it is not already present in the headers. Defaults to true.
+
+This should only be disabled for testing; HTTP requires the Date header
+in responses.
+
+### response.setHeader(name, value)
+<!-- YAML
+added: REPLACEME
+-->
+
+* `name` {string}
+* `value` {string | string[]}
+
+Sets a single header value for implicit headers. If this header already exists
+in the to-be-sent headers, its value will be replaced. Use an array of strings
+here to send multiple headers with the same name.
+
+Example:
+
+```js
+response.setHeader('Content-Type', 'text/html');
+```
+
+or
+
+```js
+response.setHeader('Set-Cookie', ['type=ninja', 'language=javascript']);
+```
+
+Attempting to set a header field name or value that contains invalid characters
+will result in a [`TypeError`][] being thrown.
+
+When headers have been set with [`response.setHeader()`][], they will be merged with
+any headers passed to [`response.writeHead()`][], with the headers passed to
+[`response.writeHead()`][] given precedence.
+
+```js
+// returns content-type = text/plain
+const server = http.createServer((req, res) => {
+ res.setHeader('Content-Type', 'text/html');
+ res.setHeader('X-Foo', 'bar');
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
+ res.end('ok');
+});
+```
+
+### response.setTimeout(msecs[, callback])
+<!-- YAML
+added: REPLACEME
+-->
+
+* `msecs` {number}
+* `callback` {Function}
+
+Sets the [`Http2Stream`]()'s timeout value to `msecs`. If a callback is
+provided, then it is added as a listener on the `'timeout'` event on
+the response object.
+
+If no `'timeout'` listener is added to the request, the response, or
+the server, then [`Http2Stream`]()s are destroyed when they time out. If a handler is
+assigned to the request, the response, or the server's `'timeout'` events,
+timed out sockets must be handled explicitly.
+
+Returns `response`.
+
+### response.socket
+<!-- YAML
+added: REPLACEME
+-->
+
+* {net.Socket}
+
+Reference to the underlying socket. Usually users will not want to access
+this property. In particular, the socket will not emit `'readable'` events
+because of how the protocol parser attaches to the socket. After
+`response.end()`, the property is nulled. The `socket` may also be accessed
+via `response.connection`.
+
+Example:
+
+```js
+const http = require('http');
+const server = http.createServer((req, res) => {
+ const ip = req.socket.remoteAddress;
+ const port = req.socket.remotePort;
+ res.end(`Your IP address is ${ip} and your source port is ${port}.`);
+}).listen(3000);
+```
+
+### response.statusCode
+<!-- YAML
+added: REPLACEME
+-->
+
+* {number}
+
+When using implicit headers (not calling [`response.writeHead()`][] explicitly),
+this property controls the status code that will be sent to the client when
+the headers get flushed.
+
+Example:
+
+```js
+response.statusCode = 404;
+```
+
+After response header was sent to the client, this property indicates the
+status code which was sent out.
+
+### response.statusMessage
+<!-- YAML
+added: REPLACEME
+-->
+
+* {string}
+
+Status message is not supported by HTTP/2 (RFC7540 8.1.2.4). It returns
+an empty string.
+
+#### response.stream
+<!-- YAML
+added: REPLACEME
+-->
+
+* {http2.Http2Stream}
+
+The [`Http2Stream`][] object backing the response.
+
+### response.write(chunk[, encoding][, callback])
+<!-- YAML
+added: REPLACEME
+-->
+
+* `chunk` {string|Buffer}
+* `encoding` {string}
+* `callback` {Function}
+* Returns: {boolean}
+
+If this method is called and [`response.writeHead()`][] has not been called,
+it will switch to implicit header mode and flush the implicit headers.
+
+This sends a chunk of the response body. This method may
+be called multiple times to provide successive parts of the body.
+
+Note that in the `http` module, the response body is omitted when the
+request is a HEAD request. Similarly, the `204` and `304` responses
+_must not_ include a message body.
+
+`chunk` can be a string or a buffer. If `chunk` is a string,
+the second parameter specifies how to encode it into a byte stream.
+By default the `encoding` is `'utf8'`. `callback` will be called when this chunk
+of data is flushed.
+
+*Note*: This is the raw HTTP body and has nothing to do with
+higher-level multi-part body encodings that may be used.
+
+The first time [`response.write()`][] is called, it will send the buffered
+header information and the first chunk of the body to the client. The second
+time [`response.write()`][] is called, Node.js assumes data will be streamed,
+and sends the new data separately. That is, the response is buffered up to the
+first chunk of the body.
+
+Returns `true` if the entire data was flushed successfully to the kernel
+buffer. Returns `false` if all or part of the data was queued in user memory.
+`'drain'` will be emitted when the buffer is free again.
+
+### response.writeContinue()
+<!-- YAML
+added: REPLACEME
+-->
+
+Does nothing. Added for parity with [HTTP/1]().
+
+### response.writeHead(statusCode[, statusMessage][, headers])
+<!-- YAML
+added: REPLACEME
+-->
+
+* `statusCode` {number}
+* `statusMessage` {string}
+* `headers` {Object}
+
+Sends a response header to the request. The status code is a 3-digit HTTP
+status code, like `404`. The last argument, `headers`, are the response headers.
+For compatibility with [HTTP/1](), one can give a human-readable `statusMessage` as the second argument, which will be silenty ignored and emit a warning.
+
+Example:
+
+```js
+const body = 'hello world';
+response.writeHead(200, {
+ 'Content-Length': Buffer.byteLength(body),
+ 'Content-Type': 'text/plain' });
+```
+
+This method must only be called once on a message and it must
+be called before [`response.end()`][] is called.
+
+If [`response.write()`][] or [`response.end()`][] are called before calling
+this, the implicit/mutable headers will be calculated and call this function.
+
+When headers have been set with [`response.setHeader()`][], they will be merged with
+any headers passed to [`response.writeHead()`][], with the headers passed to
+[`response.writeHead()`][] given precedence.
+
+```js
+// returns content-type = text/plain
+const server = http2.createServer((req, res) => {
+ res.setHeader('Content-Type', 'text/html');
+ res.setHeader('X-Foo', 'bar');
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
+ res.end('ok');
+});
+```
+
+Note that Content-Length is given in bytes not characters. The above example
+works because the string `'hello world'` contains only single byte characters.
+If the body contains higher coded characters then `Buffer.byteLength()`
+should be used to determine the number of bytes in a given encoding.
+And Node.js does not check whether Content-Length and the length of the body
+which has been transmitted are equal or not.
+
+Attempting to set a header field name or value that contains invalid characters
+will result in a [`TypeError`][] being thrown.
+
+### response.createPushResponse(headers, callback)
+<!-- YAML
+added: REPLACEME
+-->
+
+Call [`stream.pushStream()`]() with the given headers, and wraps the
+given newly created [`Http2Stream`] on `Http2ServerRespose`.
+The callback will be called with an error with code `ERR_HTTP2_STREAM_CLOSED`
+if the stream is closed.
[HTTP/2]: https://tools.ietf.org/html/rfc7540
[HTTP/1]: http.html
+[https]: https.html
[`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
+[alpn-negotiation]: #http2_alpn_negotiation
[`Duplex`]: stream.html#stream_class_stream_duplex
[Headers Object]: #http2_headers_object
[`Http2Stream`]: #http2_class_http2stream
@@ -1720,3 +2485,7 @@ TBD
[Using options.selectPadding]: #http2_using_options_selectpadding
[error code]: #error_codes
[`'unknownProtocol'`]: #http2_event_unknownprotocol
+[`'request'`]: #http2_event_request
+[Readable Stream]: stream.html#stream_class_stream_readable
+[`ServerRequest`]: #http2_class_server_request
+[`stream.pushStream()`]: #http2_stream-pushstream