http2: compatibility fixes, added docs - #12
Conversation
Allows TLS renegotiation to be disabled per `TLSSocket` instance. Per HTTP/2, TLS renegotiation is forbidden after the initial connection prefix is exchanged.
At long last: The initial *experimental* implementation of HTTP/2.
This is an accumulation of the work that has been done in the nodejs/http2
repository, squashed down to a couple of commits. The original commit
history has been preserved in the nodejs/http2 repository.
This PR introduces the nghttp2 C library as a new dependency. This library
provides the majority of the HTTP/2 protocol implementation, with the rest
of the code here providing the mapping of the library into a usable JS API.
Within src, a handful of new node_http2_*.c and node_http2_*.h files are
introduced. These provide the internal mechanisms that interface with nghttp
and define the `process.binding('http2')` interface.
The JS API is defined within `internal/http2/*.js`.
There are two APIs provided: Core and Compat.
The Core API is HTTP/2 specific and is designed to be as minimal and as
efficient as possible.
The Compat API is intended to be as close to the existing HTTP/1 API as
possible, with some exceptions.
Tests, documentation and initial benchmarks are included.
The `http2` module is gated by a new `--expose-http2` command line flag.
When used, `require('http2')` will be exposed to users. Note that there
is an existing `http2` module on npm that would be impacted by the introduction
of this module, which is the main reason for gating this behind a flag.
When using `require('http2')` the first time, a process warning will be
emitted indicating that an experimental feature is being used.
To run the benchmarks, the `h2load` tool (part of the nghttp project) is
required: `./node benchmarks/http2/simple.js benchmarker=h2load`. Only
two benchmarks are currently available.
Additional configuration options to enable verbose debugging are provided:
```
$ ./configure --debug-http2 --debug-nghttp2
$ NODE_DEBUG=http2 ./node
```
The `--debug-http2` configuration option enables verbose debug statements
from the `src/node_http2_*` files. The `--debug-nghttp2` enables the nghttp
library's own verbose debug output. The `NODE_DEBUG=http2` enables JS-level
debug output.
The following illustrates as simple HTTP/2 server and client interaction:
(The HTTP/2 client and server support both plain text and TLS connections)
```jt client = http2.connect('http://localhost:80');
const req = client.request({ ':path': '/some/path' });
req.on('data', (chunk) => { /* do something with the data */ });
req.on('end', () => {
client.destroy();
});
// Plain text (non-TLS server)
const server = http2.createServer();
server.on('stream', (stream, requestHeaders) => {
stream.respond({ ':status': 200 });
stream.write('hello ');
stream.end('world');
});
server.listen(80);
```
```js
const http2 = require('http2');
const client = http2.connect('http://localhost');
```
Author: Anna Henningsen <anna@addaleax.net>
Author: Colin Ihrig <cjihrig@gmail.com>
Author: Daniel Bevenius <daniel.bevenius@gmail.com>
Author: James M Snell <jasnell@gmail.com>
Author: Jun Mukai
Author: Kelvin Jin
Author: Matteo Collina <matteo.collina@gmail.com>
Author: Robert Kowalski <rok@kowalski.gd>
Author: Santiago Gimeno <santiago.gimeno@gmail.com>
Author: Sebastiaan Deckers <sebdeckers83@gmail.com>
Author: Yosuke Furukawa <yosuke.furukawa@gmail.com>Race condition in the closing of the stream causing failure on some platforms.
Fixes: nodejs/http2#184 Refines the `'socketError'` event a bit and adds a test for the emission of the `'socketError'` event on the server. Client side is tested separately
Fixes: nodejs/http2#179 Was fixing issue nodejs#179 and encountered a segault that was happening somewhat randomly on session destruction. Both should be fixed
* respondWithFD now supports optional statCheck * respondWithFD and respondWithFile both support offset/length for range requests * Fix linting nits following most recent update
sebdeckers
commented
Jul 25, 2017
@mcollina Excellent work on the docs! 👏 |
mcollina
commented
Jul 25, 2017
@sebdeckers I would prefer not to have it as it is undocumented. If people are using it, we should document it in HTTP/1 as well, and support it. My guess is that it is some leftover from the past To the best of your knowledge, is it used in Express or similar? |
mcollina
commented
Jul 25, 2017
@sebdeckers it's documented for the client request object (https://nodejs.org/api/http.html#http_request_flushheaders), but not for the server response. test-http-flush-response-headers.js covers it for the server response. @jasnell what do you think? |
jasnell
commented
Jul 25, 2017
Agree that it should be documented. |
There was a problem hiding this comment.
If we are going to list these here, I think we need to be more explicit about these being part of the compatibility API and calling out the differences between this and the 'stream' event.
There was a problem hiding this comment.
I think this is great progress, I would prefer landing this, and then we can check where things can be updated. Having some docs for the compat API would help.
There was a problem hiding this comment.
long line... will stop pointing these out :-)
6a36561 to
3311227Comparemcollina
commented
Jul 25, 2017
I would need to cherry pick the commits then, the two branches diverged too much. |
mcollina
commented
Jul 25, 2017
@sebdeckers I readded |
3311227 to
2c560bbComparemcollina
commented
Jul 25, 2017
@jasnell I've fixed the line length. |
This function call can fail with `Z_VERSION_ERROR` if the compiled library vs loaded library mismatched in version number or in stream structure size. In those cases, zlib doesn't initialize the `strm_.msg` field to null. Therefore, when a `CompressionError` object is created via `ErrorForMessage()`, it can read a stale or uninitialized `strm_.msg` pointer that will cause a crash. Example ASAN report: ``` AddressSanitizer: SEGV on unknown address #0 __strlen_avx2 string/../sysdeps/x86_64/multiarch/strlen-avx2.S:76 #1 strlen (/work/node/out/Debug/node+0x1a42ab7) #2 v8::(anonymous namespace)::StringLength(char const*) /work/node/out/../deps/v8/src/api/api.cc:7581:16 #3 v8::(anonymous namespace)::StringLength(unsigned char const*) /work/node/out/../deps/v8/src/api/api.cc:7587:10 #4 v8::String::NewFromOneByte(v8::Isolate*, unsigned char const*, v8::NewStringType, int) /work/node/out/../deps/v8/src/api/api.cc:7677:3 #5 node::OneByteString(v8::Isolate*, char const*, int, v8::NewStringType) /work/node/out/../src/util-inl.h:166:10 #6 node::(anonymous namespace)::CompressionStream< node::(anonymous namespace)::ZlibContext> ::EmitError(node::(anonymous namespace) ::CompressionError const&) /work/node/out/../src/node_zlib.cc:565:7 #7 node::(anonymous namespace)::CompressionStream< node::(anonymous namespace)::ZlibContext> ::CheckError() /work/node/out/../src/node_zlib.cc:519:5 #8 node::(anonymous namespace)::CompressionStream< node::(anonymous namespace)::ZlibContext> ::AfterThreadPoolWork(int) /work/node/out/../src/node_zlib.cc:543:10 #9 node::ThreadPoolWork::ScheduleWork() ::'lambda'(uv_work_s*, int) ::operator()(uv_work_s*, int) const /work/node/out/../src/threadpoolwork-inl.h:57:15 #10 node::ThreadPoolWork::ScheduleWork() ::'lambda'(uv_work_s*, int) ::__invoke(uv_work_s*, int) /work/node/out/../src/threadpoolwork-inl.h:48:7 #11 uv__work_done /work/libuv-1.51.0/src/threadpool.c:330:5 #12 uv__async_io.part.0 /work/libuv-1.51.0/src/unix/async.c:208:5 ``` Signed-off-by: ndossche <nora.dossche@ugent.be> PR-URL: nodejs#63476 Reviewed-By: Anna Henningsen <anna@addaleax.net>
I added all the docs (to the best of my knowledge) for the Compatibility API.
I also made
writeHeadbehave similarly between HTTP/1 and HTTP/2, and I removed flushHeaders because it was no longer needed after the change.cc @sebdeckers