Git Product home page Git Product logo

node-http2's Introduction

node-http2

An HTTP/2 (RFC 7540) client and server implementation for node.js.

Travis CI status

NOTE WELL This package is officially deprecated. As of node 9.0.0, there is an 'http2' package built-in. You should use that one instead.

Installation

npm install http2

API

The API is very similar to the standard node.js HTTPS API. The goal is the perfect API compatibility, with additional HTTP2 related extensions (like server push).

Detailed API documentation is primarily maintained in the lib/http.js file and is available in the wiki as well.

Examples

Using as a server

var options = {
  key: fs.readFileSync('./example/localhost.key'),
  cert: fs.readFileSync('./example/localhost.crt')
};

require('http2').createServer(options, function(request, response) {
  response.end('Hello world!');
}).listen(8080);

Using as a client

require('http2').get('https://localhost:8080/', function(response) {
  response.pipe(process.stdout);
});

Simple static file server

An simple static file server serving up content from its own directory is available in the example directory. Running the server:

$ node ./example/server.js

Simple command line client

An example client is also available. Downloading the server's own source code from the server:

$ node ./example/client.js 'https://localhost:8080/server.js' >/tmp/server.js

Server push

For a server push example, see the source code of the example server and client.

Status

  • ALPN is only supported in node.js >= 5.0
  • Upgrade mechanism to start HTTP/2 over unencrypted channel is not implemented yet (issue #4)
  • Other minor features found in this list are not implemented yet

Development

Development dependencies

There's a few library you will need to have installed to do anything described in the following sections. After installing/cloning node-http2, run npm install in its directory to install development dependencies.

Used libraries:

For pretty printing logs, you will also need a global install of bunyan (npm install -g bunyan).

Developer documentation

The developer documentation is generated from the source code using docco and can be viewed online here. If you'd like to have an offline copy, just run npm run-script doc.

Running the tests

It's easy, just run npm test. The tests are written in BDD style, so they are a good starting point to understand the code.

Test coverage

To generate a code coverage report, run npm test --coverage (which runs very slowly, be patient). Code coverage summary as of version 3.0.1:

Statements   : 92.09% ( 1759/1910 )
Branches     : 82.56% ( 696/843 )
Functions    : 91.38% ( 212/232 )
Lines        : 92.17% ( 1753/1902 )

There's a hosted version of the detailed (line-by-line) coverage report here.

Logging

Logging is turned off by default. You can turn it on by passing a bunyan logger as log option when creating a server or agent.

When using the example server or client, it's very easy to turn logging on: set the HTTP2_LOG environment variable to fatal, error, warn, info, debug or trace (the logging level). To log every single incoming and outgoing data chunk, use HTTP2_LOG_DATA=1 besides HTTP2_LOG=trace. Log output goes to the standard error output. If the standard error is redirected into a file, then the log output is in bunyan's JSON format for easier post-mortem analysis.

Running the example server and client with info level logging output:

$ HTTP2_LOG=info node ./example/server.js
$ HTTP2_LOG=info node ./example/client.js 'https://localhost:8080/server.js' >/dev/null

Contributors

The co-maintainer of the project is Nick Hurley.

Code contributions are always welcome! People who contributed to node-http2 so far:

Special thanks to Google for financing the development of this module as part of their Summer of Code program (project: HTTP/2 prototype server implementation), and Nick Hurley of Mozilla, my GSoC mentor, who helped with regular code review and technical advices.

License

The MIT License

Copyright (C) 2013 Gábor Molnár [email protected]

node-http2's People

Contributors

5lava avatar 890f2151c2be69c51db72017546d00fd avatar akc42 avatar avesus avatar felicienfrancois avatar iartem avatar ipetez avatar jhen0409 avatar jstourac avatar julien-f avatar kesla avatar mcmanus avatar mikefrey avatar mroutput avatar nwgh avatar oimou avatar olemchls avatar philippsoehnlein avatar ricordisamoa avatar sdavis-r7 avatar snorp avatar vsemogutor avatar watson avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

node-http2's Issues

No response back for a stream if multiple requests were sent

I tried https://gabor.molnar.es:8080/ with nghttp client. They work fine together for simple 1 request/response, which is great!
But when I sent several requests in a batch (say 5 streams), DATA frame for one stream did not sent back and the connection hanged.

Here is the log from the client side:

$ src/nghttp -nv https://gabor.molnar.es:8080/ -m5
[  0.479] NPN select next protocol: the remote server offers:
      * HTTP-draft-04/2.0
      * http/1.1
      * http/1.0
      NPN selected the protocol: HTTP-draft-04/2.0
[  0.720] send SETTINGS frame <length=16, flags=0x00, stream_id=0>
      (niv=2)
      [4:100]
      [7:65535]
[  0.720] send HEADERS frame <length=64, flags=0x05, stream_id=1>
      ; END_STREAM | END_HEADERS
      ; Open new stream
      :host: gabor.molnar.es:8080
      :method: GET
      :path: /
      :scheme: https
      accept: */*
      accept-encoding: gzip, deflate
      user-agent: nghttp2/0.1.0-DEV
[  0.720] send HEADERS frame <length=0, flags=0x05, stream_id=3>
      ; END_STREAM | END_HEADERS
      ; Open new stream
      :host: gabor.molnar.es:8080
      :method: GET
      :path: /
      :scheme: https
      accept: */*
      accept-encoding: gzip, deflate
      user-agent: nghttp2/0.1.0-DEV
[  0.720] send HEADERS frame <length=0, flags=0x05, stream_id=5>
      ; END_STREAM | END_HEADERS
      ; Open new stream
      :host: gabor.molnar.es:8080
      :method: GET
      :path: /
      :scheme: https
      accept: */*
      accept-encoding: gzip, deflate
      user-agent: nghttp2/0.1.0-DEV
[  0.720] send HEADERS frame <length=0, flags=0x05, stream_id=7>
      ; END_STREAM | END_HEADERS
      ; Open new stream
      :host: gabor.molnar.es:8080
      :method: GET
      :path: /
      :scheme: https
      accept: */*
      accept-encoding: gzip, deflate
      user-agent: nghttp2/0.1.0-DEV
[  0.720] send HEADERS frame <length=0, flags=0x05, stream_id=9>
      ; END_STREAM | END_HEADERS
      ; Open new stream
      :host: gabor.molnar.es:8080
      :method: GET
      :path: /
      :scheme: https
      accept: */*
      accept-encoding: gzip, deflate
      user-agent: nghttp2/0.1.0-DEV
[  0.947] recv SETTINGS frame <length=16, flags=0x00, stream_id=0>
      (niv=2)
      [4:10]
      [7:100000]
[  0.947] recv WINDOW_UPDATE frame <length=4, flags=0x01, stream_id=0>
      ; END_FLOW_CONTROL
      (window_size_increment=0)
[  0.958] recv HEADERS frame <length=1, flags=0x04, stream_id=1>
      ; END_HEADERS
      ; First response header
      :status: 200
[  0.958] recv HEADERS frame <length=0, flags=0x04, stream_id=3>
      ; END_HEADERS
      ; First response header
      :status: 200
[  0.958] recv HEADERS frame <length=0, flags=0x04, stream_id=5>
      ; END_HEADERS
      ; First response header
      :status: 200
[  1.425] recv DATA frame (length=14103, flags=0, stream_id=5)
[  1.425] recv HEADERS frame <length=0, flags=0x04, stream_id=7>
      ; END_HEADERS
      ; First response header
      :status: 200
[  1.682] recv DATA frame (length=14103, flags=0, stream_id=7)
[  1.682] recv HEADERS frame <length=0, flags=0x04, stream_id=9>
      ; END_HEADERS
      ; First response header
      :status: 200
[  1.882] recv DATA frame (length=14103, flags=1, stream_id=9)
      ; END_STREAM
[  2.110] recv DATA frame (length=14103, flags=1, stream_id=1)
      ; END_STREAM
[  2.110] recv DATA frame (length=0, flags=1, stream_id=5)
      ; END_STREAM
[  2.110] recv DATA frame (length=0, flags=1, stream_id=7)
      ; END_STREAM
^C

5 requests were sent. And all streams were responded with HEADERS.
But DATA frame for stream_id=3 did not appear in the log.

Make compression more efficient

The compression code is not as efficient as it could be. We need careful memory management to be able to fully utilize the possibilities given by the spec.

Unnecessary 0.5s wait before sending the last chunk of the frame

When testing with nghttp -nv -m600 https://192.168.0.134:8080/server.js, it's can be seen very clearly (last frames):

[  9.598] recv DATA frame <length=1214, flags=0x01, stream_id=303>
          ; END_STREAM
[  9.598] recv DATA frame <length=1214, flags=0x01, stream_id=305>
          ; END_STREAM
[  9.599] recv DATA frame <length=1214, flags=0x01, stream_id=307>
          ; END_STREAM
[  9.599] recv DATA frame <length=1214, flags=0x01, stream_id=309>
          ; END_STREAM
[  9.599] recv DATA frame <length=1214, flags=0x01, stream_id=311>
          ; END_STREAM
[  9.599] recv DATA frame <length=1214, flags=0x01, stream_id=313>
          ; END_STREAM
[  9.599] recv DATA frame <length=1214, flags=0x01, stream_id=315>
          ; END_STREAM
[  9.599] recv DATA frame <length=1214, flags=0x01, stream_id=317>
          ; END_STREAM
[  9.599] recv DATA frame <length=1214, flags=0x01, stream_id=319>
          ; END_STREAM
[  9.599] recv DATA frame <length=1214, flags=0x01, stream_id=321>
          ; END_STREAM
[  9.600] recv DATA frame <length=1193, flags=0x00, stream_id=323>
[ 11.025] recv DATA frame <length=21, flags=0x01, stream_id=323>
          ; END_STREAM
[ 11.025] send WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=0>
          (window_size_increment=32778)

Turn off flow control by default

It is said in the spec that endpoints without strict resource constraints should turn it off. It is not good enough for every use case anyway. (Because we don't estimate the BDP of the link, just set a constant value for the window size)

Compression: using 'literal header without indexing' with an index referring to the pre-existing entries

From Fred Akalin:

Your server seems to not like it when I encode my headers using 'literal header without indexing' with an index referring to the pre-existing entries, as opposed to simply literal-encoding both the names and the values. It closes the connection immediately when I do so.

That is, sending this doesn't work:

t=1375794189272 [st=244]        SSL_SOCKET_BYTES_SENT
                            --> byte_count = 485
                            --> hex_encoded_bytes =
                              01 DD 01 0D 00 00 00 01 00 00 00 00 62 14 67 61 62 6F 72 2E   ....   .    b.gabor.
                              6D 6F 6C 6E 61 72 2E 65 73 3A 38 30 38 30 64 03 47 45 54 63   molnar.es:8080d.GETc
                              01 2F 60 05 68 74 74 70 73 60 08 3A 76 65 72 73 69 6F 6E 08   ./`.https`.:version.
                              48 54 54 50 2F 31 2E 31 65 4A 74 65 78 74 2F 68 74 6D 6C 2C   HTTP/1.1eJtext/html,
                              61 70 70 6C 69 63 61 74 69 6F 6E 2F 78 68 74 6D 6C 2B 78 6D   application/xhtml+xm
                              6C 2C 61 70 70 6C 69 63 61 74 69 6F 6E 2F 78 6D 6C 3B 71 3D   l,application/xml;q=
                              30 2E 39 2C 69 6D 61 67 65 2F 77 65 62 70 2C 2A 2F 2A 3B 71   0.9,image/webp,*/*;q
                              3D 30 2E 38 67 11 67 7A 69 70 2C 64 65 66 6C 61 74 65 2C 73   =0.8g.gzip,deflate,s
                              64 63 68 68 0E 65 6E 2D 55 53 2C 65 6E 3B 71 3D 30 2E 38 72   dchh.en-US,en;q=0.8r
                              09 6D 61 78 2D 61 67 65 3D 30 69 AC 01 5F 5F 75 74 6D 61 3D   .max-age=0i..__utma=
                              37 30 30 30 30 36 34 36 2E 32 30 38 32 34 30 38 38 38 38 2E   70000646.2082408888.
                              31 33 37 35 37 39 33 35 39 35 2E 31 33 37 35 37 39 33 35 39   1375793595.137579359
                              35 2E 31 33 37 35 37 39 33 35 39 35 2E 31 3B 20 5F 5F 75 74   5.1375793595.1; __ut
                              6D 62 3D 37 30 30 30 30 36 34 36 2E 36 2E 31 30 2E 31 33 37   mb=70000646.6.10.137
                              35 37 39 33 35 39 35 3B 20 5F 5F 75 74 6D 7A 3D 37 30 30 30   5793595; __utmz=7000
                              30 36 34 36 2E 31 33 37 35 37 39 33 35 39 35 2E 31 2E 31 2E   0646.1375793595.1.1.
                              75 74 6D 63 73 72 3D 28 64 69 72 65 63 74 29 7C 75 74 6D 63   utmcsr=(direct)|utmc
                              63 6E 3D 28 64 69 72 65 63 74 29 7C 75 74 6D 63 6D 64 3D 28   cn=(direct)|utmcmd=(
                              6E 6F 6E 65 29 6C 76 4D 6F 7A 69 6C 6C 61 2F 35 2E 30 20 28   none)lvMozilla/5.0 (
                              4D 61 63 69 6E 74 6F 73 68 3B 20 49 6E 74 65 6C 20 4D 61 63   Macintosh; Intel Mac
                              20 4F 53 20 58 20 31 30 5F 38 5F 34 29 20 41 70 70 6C 65 57    OS X 10_8_4) AppleW
                              65 62 4B 69 74 2F 35 33 37 2E 33 36 20 28 4B 48 54 4D 4C 2C   ebKit/537.36 (KHTML,
                              20 6C 69 6B 65 20 47 65 63 6B 6F 29 20 43 68 72 6F 6D 65 2F    like Gecko) Chrome/
                              33 30 2E 30 2E 31 35 38 38 2E 30 20 53 61 66 61 72 69 2F 35   30.0.1588.0 Safari/5
                              33 37 2E 33 36                                                37.36

but this does:

t=1375794517875 [st= 205]        SSL_SOCKET_BYTES_SENT
                             --> byte_count = 584
                             --> hex_encoded_bytes =
                               02 40 01 0D 00 00 00 01 00 00 00 00 60 05 3A 68 6F 73 74 14   .@..   .    `.:host.
                               67 61 62 6F 72 2E 6D 6F 6C 6E 61 72 2E 65 73 3A 38 30 38 30   gabor.molnar.es:8080
                               60 07 3A 6D 65 74 68 6F 64 03 47 45 54 60 05 3A 70 61 74 68   `.:method.GET`.:path
                               01 2F 60 07 3A 73 63 68 65 6D 65 05 68 74 74 70 73 60 08 3A   ./`.:scheme.https`.:
                               76 65 72 73 69 6F 6E 08 48 54 54 50 2F 31 2E 31 60 06 61 63   version.HTTP/1.1`.ac
                               63 65 70 74 4A 74 65 78 74 2F 68 74 6D 6C 2C 61 70 70 6C 69   ceptJtext/html,appli
                               63 61 74 69 6F 6E 2F 78 68 74 6D 6C 2B 78 6D 6C 2C 61 70 70   cation/xhtml+xml,app
                               6C 69 63 61 74 69 6F 6E 2F 78 6D 6C 3B 71 3D 30 2E 39 2C 69   lication/xml;q=0.9,i
                               6D 61 67 65 2F 77 65 62 70 2C 2A 2F 2A 3B 71 3D 30 2E 38 60   mage/webp,*/*;q=0.8`
                               0F 61 63 63 65 70 74 2D 65 6E 63 6F 64 69 6E 67 11 67 7A 69   .accept-encoding.gzi
                               70 2C 64 65 66 6C 61 74 65 2C 73 64 63 68 60 0F 61 63 63 65   p,deflate,sdch`.acce
                               70 74 2D 6C 61 6E 67 75 61 67 65 0E 65 6E 2D 55 53 2C 65 6E   pt-language.en-US,en
                               3B 71 3D 30 2E 38 60 0D 63 61 63 68 65 2D 63 6F 6E 74 72 6F   ;q=0.8`.cache-contro
                               6C 09 6D 61 78 2D 61 67 65 3D 30 60 06 63 6F 6F 6B 69 65 AC   l.max-age=0`.cookie.
                               01 5F 5F 75 74 6D 61 3D 37 30 30 30 30 36 34 36 2E 32 30 38   .__utma=70000646.208
                               32 34 30 38 38 38 38 2E 31 33 37 35 37 39 33 35 39 35 2E 31   2408888.1375793595.1
                               33 37 35 37 39 33 35 39 35 2E 31 33 37 35 37 39 33 35 39 35   375793595.1375793595
                               2E 31 3B 20 5F 5F 75 74 6D 62 3D 37 30 30 30 30 36 34 36 2E   .1; __utmb=70000646.
                               36 2E 31 30 2E 31 33 37 35 37 39 33 35 39 35 3B 20 5F 5F 75   6.10.1375793595; __u
                               74 6D 7A 3D 37 30 30 30 30 36 34 36 2E 31 33 37 35 37 39 33   tmz=70000646.1375793
                               35 39 35 2E 31 2E 31 2E 75 74 6D 63 73 72 3D 28 64 69 72 65   595.1.1.utmcsr=(dire
                               63 74 29 7C 75 74 6D 63 63 6E 3D 28 64 69 72 65 63 74 29 7C   ct)|utmccn=(direct)|
                               75 74 6D 63 6D 64 3D 28 6E 6F 6E 65 29 60 0A 75 73 65 72 2D   utmcmd=(none)`.user-
                               61 67 65 6E 74 76 4D 6F 7A 69 6C 6C 61 2F 35 2E 30 20 28 4D   agentvMozilla/5.0 (M
                               61 63 69 6E 74 6F 73 68 3B 20 49 6E 74 65 6C 20 4D 61 63 20   acintosh; Intel Mac 
                               4F 53 20 58 20 31 30 5F 38 5F 34 29 20 41 70 70 6C 65 57 65   OS X 10_8_4) AppleWe
                               62 4B 69 74 2F 35 33 37 2E 33 36 20 28 4B 48 54 4D 4C 2C 20   bKit/537.36 (KHTML, 
                               6C 69 6B 65 20 47 65 63 6B 6F 29 20 43 68 72 6F 6D 65 2F 33   like Gecko) Chrome/3
                               30 2E 30 2E 31 35 38 38 2E 30 20 53 61 66 61 72 69 2F 35 33   0.0.1588.0 Safari/53
                               37 2E 33 36

Add :host, :method and :scheme in request headers

Looks like the current code only adds :path header field.
Maybe it is not a first priority issue, but adding the other 3 headers may help interop testing since server will respond 400 if these headers are missing.

Connection: check if the peer uses appropriate stream IDs

From Nick:

In the connection class, node-http2 will accept any incoming stream ID. This could result in accepting a stream ID that is less than the current max stream ID, which is out of spec (section 5.1.1). On top of that, it could end up sending the wrong stream ID in a GOAWAY frame, which could be especially disastrous if the stream that was processed (but didn't send in GOAWAY because of this bug) was non-idempotent (ie, anything other than a GET).

Move on('push') to request

There is a problem with the current API that arises because PUSH_PROMISE can sent before the HEADERS for the associated stream. This means that the client is required to track all the pushes that it receives until the HEADERS frame arrives. Given that this also means that the client might be required to hold the entirety of the pushed response, this is a problem.

A simple remedy is to move the event handler to the request object instead of the response.

node takes 100% CPU when large number of streams are requested

This is a bit pathological case, but I tend to throw large number of requests to my implementation. Today I did to node-http2 server.
The setup is:

$ node example/server.js

And I issued following command:

$ nghttp -nv -m600 https://localhost:8080/server.js

which issues 600 requests in one connection at once.

I observed that after all response data were sent, node server got really slow
and 0-length DATA with END_STREAM bit set was sent very slowly.
And node server's CPU usage became 100%. It kept busy state even after
I killed nghttp.

I use the latest git master of node-http2.

ALPN negotiation

With fallback to HTTP/1.1 over TLS if the other endpoint does not support HTTP/2.

Possible flow control bug

(node) warning: possible EventEmitter memory leak detected. 11 listeners added. Use emitter.setMaxListeners() to increase limit.
Trace
    at Serializer.EventEmitter.addListener (events.js:160:15)
    at Serializer.Readable.on (_stream_readable.js:679:33)
    at Serializer.EventEmitter.once (events.js:179:8)
    at Endpoint._read (/home/darkelf/test-server/node_modules/http2/lib/endpoint.js:148:22)
    at Serializer.g (events.js:175:14)
    at Serializer.EventEmitter.emit (events.js:117:20)
    at emitReadable_ (_stream_readable.js:408:10)
    at emitReadable (_stream_readable.js:404:5)
    at readableAddChunk (_stream_readable.js:165:9)
    at Serializer.Readable.push (_stream_readable.js:127:10)
    at Serializer.Transform.push (_stream_transform.js:140:32)

Problem with sending large responses

As reported by @shigeki in #15:

When I GET a large.html file which is more than 10Mbyte. The error is

unixjp:~/tmp/github/node-http2/example>       ~/tmp/oldnode/node-v0.10.15/node server.js
Listening on localhost:8443, serving up files from /home/ohtsu/tmp/github/node-http2/example
Incoming request: /large.html (/home/ohtsu/tmp/github/node-http2/example/large.html)
Reading file from disk.

/home/ohtsu/tmp/github/node-http2/lib/stream.js:350
      throw new Error('Sending illegal frame (' + frame.type + ') in ' + this.
            ^
Error: Sending illegal frame (DATA) in CLOSED state.
    at Stream.transition [as _transition] (/home/ohtsu/tmp/github/node-http2/lib/stream.js:350:13)
    at Duplex.EventEmitter.emit (events.js:95:17)
    at Stream._flush (/home/ohtsu/tmp/github/node-http2/lib/stream.js:122:19)
    at processImmediate [as _immediateCallback] (timers.js:330:15)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.