mirror of
https://github.com/nghttp2/nghttp2.git
synced 2025-12-06 18:18:52 +08:00
Compare commits
171 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a61054e1f7 | ||
|
|
749f5d6a32 | ||
|
|
f09bd821ac | ||
|
|
6d5c00b8eb | ||
|
|
532bffdb01 | ||
|
|
c6c7145167 | ||
|
|
b5717cd288 | ||
|
|
d4d7597efb | ||
|
|
2952706b53 | ||
|
|
9b0ccdef34 | ||
|
|
41dd5f6897 | ||
|
|
f9c60d5e9d | ||
|
|
c2ca04c1db | ||
|
|
5fbb99e3d0 | ||
|
|
ba34313f9f | ||
|
|
aa6699435a | ||
|
|
e857e99df8 | ||
|
|
6d537c419e | ||
|
|
4894e24dc8 | ||
|
|
bbbddedb8f | ||
|
|
163dfb3150 | ||
|
|
4f3d20e024 | ||
|
|
b7ba1baf48 | ||
|
|
00efa86fb6 | ||
|
|
43b3640836 | ||
|
|
708eb2a217 | ||
|
|
590a5c3ff3 | ||
|
|
28b528dc9e | ||
|
|
a50f6e5d50 | ||
|
|
1534edd3f3 | ||
|
|
892c159d8f | ||
|
|
0cb4750e3c | ||
|
|
ee4d53a9e4 | ||
|
|
ad150c3ab1 | ||
|
|
7eafebfeb9 | ||
|
|
3d59c6c0b7 | ||
|
|
34efc6b7a4 | ||
|
|
7582640fd5 | ||
|
|
ad265aa9d0 | ||
|
|
1f7e6ea3fe | ||
|
|
a0a5f4f93e | ||
|
|
59e6272ba4 | ||
|
|
323fc8c552 | ||
|
|
cbb10aa80f | ||
|
|
abe4c7c92a | ||
|
|
326b4c467b | ||
|
|
7e51a87111 | ||
|
|
5fdb36239a | ||
|
|
9f99cad9ab | ||
|
|
7c7d4700f1 | ||
|
|
8e5c8430f1 | ||
|
|
92ecd3c0fb | ||
|
|
90eac0709d | ||
|
|
791660ef8d | ||
|
|
1c06cfd29f | ||
|
|
f0379aa428 | ||
|
|
9a0b9428da | ||
|
|
b20abfc11a | ||
|
|
d983dd81ec | ||
|
|
bcf9e66dbc | ||
|
|
890a10b216 | ||
|
|
28adb2dad3 | ||
|
|
c795018f29 | ||
|
|
c1a663b577 | ||
|
|
2b450f26ba | ||
|
|
1ddff5bbf9 | ||
|
|
3a4c8bc8f1 | ||
|
|
fe752174a9 | ||
|
|
494ed221b6 | ||
|
|
14f971d71f | ||
|
|
ddee5d3896 | ||
|
|
447e346b1e | ||
|
|
553d741f03 | ||
|
|
6bd728b3c2 | ||
|
|
a99085891a | ||
|
|
68d3724fad | ||
|
|
fe39ec8697 | ||
|
|
c896118747 | ||
|
|
b89140c311 | ||
|
|
a869c39a2c | ||
|
|
0b27f005e0 | ||
|
|
92a20c76e6 | ||
|
|
42ccea806c | ||
|
|
805f36d134 | ||
|
|
e2c0a3e43b | ||
|
|
3572e7c634 | ||
|
|
0479f833fc | ||
|
|
252aeb43e1 | ||
|
|
24fe24b37d | ||
|
|
32603d7eff | ||
|
|
53bfc70c9e | ||
|
|
c4068cd404 | ||
|
|
0aa17f64c1 | ||
|
|
38cfc5c47c | ||
|
|
91c8f085ef | ||
|
|
5da49989f8 | ||
|
|
260131966d | ||
|
|
6862f66c23 | ||
|
|
2f2a7ace81 | ||
|
|
132719f752 | ||
|
|
64b1aae567 | ||
|
|
a8625e15f0 | ||
|
|
e63d6e490a | ||
|
|
7f60de0c51 | ||
|
|
3a46a2c0a4 | ||
|
|
fbff101165 | ||
|
|
526d2c727d | ||
|
|
6c232da679 | ||
|
|
1241c951d6 | ||
|
|
73e79130d1 | ||
|
|
1a1902350b | ||
|
|
eec65826cf | ||
|
|
fc17c0a618 | ||
|
|
2620992003 | ||
|
|
5a2069b55c | ||
|
|
14adcb2d81 | ||
|
|
13660edef2 | ||
|
|
98034286ac | ||
|
|
2a37a28d72 | ||
|
|
7bb154f768 | ||
|
|
eb96aa261f | ||
|
|
232d359cbb | ||
|
|
2d5d9d5d04 | ||
|
|
7ecca39025 | ||
|
|
bc0190c19f | ||
|
|
9a162b81f0 | ||
|
|
a67a8fabff | ||
|
|
989d381aab | ||
|
|
1d65d82cb5 | ||
|
|
4be4d875f3 | ||
|
|
1ab707713f | ||
|
|
cc46d363c5 | ||
|
|
fa082cbdd0 | ||
|
|
016d40ea0f | ||
|
|
fe6d065bb4 | ||
|
|
b4e8bea4b5 | ||
|
|
555d5abac9 | ||
|
|
3137dc4a70 | ||
|
|
4bba4bf66c | ||
|
|
7b3a33a313 | ||
|
|
ee52290de7 | ||
|
|
8f0899a190 | ||
|
|
f6cfd082c7 | ||
|
|
9d81be4b35 | ||
|
|
a62778d6b0 | ||
|
|
ea612a2dce | ||
|
|
026521b097 | ||
|
|
9dc5259593 | ||
|
|
ea8a566d98 | ||
|
|
8c6f9e899f | ||
|
|
552f675466 | ||
|
|
f9a50333d2 | ||
|
|
de4735092a | ||
|
|
1c4df1832b | ||
|
|
1ad1fe6005 | ||
|
|
f05a4830c5 | ||
|
|
9e1b068a4b | ||
|
|
54bff91762 | ||
|
|
db4a68454a | ||
|
|
5937b4b6f7 | ||
|
|
90bfea77e0 | ||
|
|
cf0576253f | ||
|
|
59e3783f3f | ||
|
|
084e4487ed | ||
|
|
24897aa50d | ||
|
|
3e50ef439d | ||
|
|
87602e5d72 | ||
|
|
d0c27d5229 | ||
|
|
ebf214c8fc | ||
|
|
250ea53e4b | ||
|
|
01af6ea70c |
@@ -10,7 +10,7 @@
|
||||
#
|
||||
# $ sudo docker run -v /path/to/dest:/out nghttp2-android cp /root/build/nghttp2/src/nghttpx /out
|
||||
|
||||
FROM ubuntu:trusty
|
||||
FROM ubuntu:vivid
|
||||
|
||||
MAINTAINER Tatsuhiro Tsujikawa
|
||||
|
||||
@@ -30,12 +30,12 @@ RUN apt-get install -y make binutils autoconf automake autotools-dev libtool \
|
||||
genisoimage libc6-i386 lib32stdc++6
|
||||
|
||||
WORKDIR /root/build
|
||||
RUN curl -L -O http://dl.google.com/android/ndk/android-ndk-r10c-linux-x86_64.bin && \
|
||||
chmod a+x android-ndk-r10c-linux-x86_64.bin && \
|
||||
./android-ndk-r10c-linux-x86_64.bin && \
|
||||
rm android-ndk-r10c-linux-x86_64.bin
|
||||
RUN curl -L -O http://dl.google.com/android/ndk/android-ndk-r10d-linux-x86_64.bin && \
|
||||
chmod a+x android-ndk-r10d-linux-x86_64.bin && \
|
||||
./android-ndk-r10d-linux-x86_64.bin && \
|
||||
rm android-ndk-r10d-linux-x86_64.bin
|
||||
|
||||
WORKDIR /root/build/android-ndk-r10c
|
||||
WORKDIR /root/build/android-ndk-r10d
|
||||
RUN /bin/bash build/tools/make-standalone-toolchain.sh \
|
||||
--install-dir=$ANDROID_HOME/toolchain \
|
||||
--toolchain=arm-linux-androideabi-4.9 --llvm-version=3.5 \
|
||||
@@ -86,6 +86,25 @@ RUN patch -p1 < ../libev-4.19-android.patch && \
|
||||
LDFLAGS=-L$PREFIX/lib && \
|
||||
make install
|
||||
|
||||
WORKDIR /root/build
|
||||
RUN curl -L -O http://zlib.net/zlib-1.2.8.tar.gz && \
|
||||
tar xf zlib-1.2.8.tar.gz && \
|
||||
rm zlib-1.2.8.tar.gz
|
||||
|
||||
WORKDIR /root/build/zlib-1.2.8
|
||||
RUN HOST=arm-linux-androideabi \
|
||||
CC=$HOST-gcc \
|
||||
AR=$HOST-ar \
|
||||
LD=$HOST-ld \
|
||||
RANLIB=$HOST-ranlib \
|
||||
STRIP=$HOST-strip \
|
||||
./configure \
|
||||
--prefix=$PREFIX \
|
||||
--libdir=$PREFIX/lib \
|
||||
--includedir=$PREFIX/include \
|
||||
--static && \
|
||||
make install
|
||||
|
||||
WORKDIR /root/build
|
||||
RUN git clone https://github.com/tatsuhiro-t/nghttp2
|
||||
WORKDIR /root/build/nghttp2
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
SUBDIRS = lib third-party src examples python tests integration-tests \
|
||||
doc contrib
|
||||
doc contrib script
|
||||
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
|
||||
|
||||
588
README.rst
588
README.rst
@@ -13,26 +13,18 @@ An HPACK encoder and decoder are available as a public API.
|
||||
|
||||
An experimental high level C++ library is also available.
|
||||
|
||||
We have Python bindings of this libary, but we do not have full
|
||||
We have Python bindings of this library, but we do not have full
|
||||
code coverage yet.
|
||||
|
||||
Development Status
|
||||
------------------
|
||||
|
||||
We started to implement h2-14
|
||||
(http://tools.ietf.org/html/draft-ietf-httpbis-http2-14), and header
|
||||
compression
|
||||
(http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-09).
|
||||
We have implemented `RFC 7540 <https://tools.ietf.org/html/rfc7540>`_
|
||||
HTTP/2 and `RFC 7541 <https://tools.ietf.org/html/rfc7541>`_ HPACK -
|
||||
Header Compression for HTTP/2
|
||||
|
||||
The nghttp2 code base was forked from the spdylay project.
|
||||
|
||||
=========================== =======
|
||||
HTTP/2 Features Support
|
||||
=========================== =======
|
||||
Core frames handling Yes
|
||||
Dependency Tree Yes
|
||||
Large header (CONTINUATION) Yes
|
||||
=========================== =======
|
||||
The nghttp2 code base was forked from the spdylay
|
||||
(https://github.com/tatsuhiro-t/spdylay) project.
|
||||
|
||||
Public Test Server
|
||||
------------------
|
||||
@@ -46,9 +38,9 @@ implementation.
|
||||
and ``http/1.1`` via ALPN/NPN and requires TLSv1.2 for HTTP/2
|
||||
connection.
|
||||
|
||||
* http://nghttp2.org/ (Upgrade / Direct)
|
||||
* http://nghttp2.org/ (HTTP Upgrade and HTTP/2 Direct)
|
||||
|
||||
``h2c-14`` and ``http/1.1``.
|
||||
``h2c`` and ``http/1.1``.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
@@ -79,7 +71,7 @@ ALPN support requires OpenSSL >= 1.0.2 (released 22 January 2015).
|
||||
To enable the SPDY protocol in the application program ``nghttpx`` and
|
||||
``h2load``, the following package is required:
|
||||
|
||||
* spdylay >= 1.3.0
|
||||
* spdylay >= 1.3.2
|
||||
|
||||
To enable ``-a`` option (getting linked assets from the downloaded
|
||||
resource) in ``nghttp``, the following package is required:
|
||||
@@ -187,7 +179,7 @@ https://nghttp2.org/documentation/
|
||||
Unit tests
|
||||
----------
|
||||
|
||||
Unit tests are done by simply running `make check`.
|
||||
Unit tests are done by simply running ``make check``.
|
||||
|
||||
Integration tests
|
||||
-----------------
|
||||
@@ -198,7 +190,8 @@ its testing framework. We depend on the following libraries:
|
||||
|
||||
* https://github.com/bradfitz/http2
|
||||
* https://github.com/tatsuhiro-t/go-nghttp2
|
||||
* https://golang.org/x/net/spdy
|
||||
* https://github.com/tatsuhiro-t/spdy
|
||||
* golang.org/x/net/websocket
|
||||
|
||||
To download the above packages, after settings ``GOPATH``, run the
|
||||
following command under ``integration-tests`` directory::
|
||||
@@ -212,6 +205,109 @@ To run the tests, run the following command under
|
||||
|
||||
Inside the tests, we use port 3009 to run the test subject server.
|
||||
|
||||
.. note::
|
||||
|
||||
github.com/tatsuhiro-t/spdy is a copy used to be available at
|
||||
golang.org/x/net/spdy, but it is now gone.
|
||||
|
||||
Migration from v0.7.15 or earlier
|
||||
---------------------------------
|
||||
|
||||
nghttp2 v1.0.0 introduced several backward incompatible changes. In
|
||||
this section, we describe these changes and how to migrate to v1.0.0.
|
||||
|
||||
ALPN protocol ID is now ``h2`` and ``h2c``
|
||||
++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
Previously we announced ``h2-14`` and ``h2c-14``. v1.0.0 implements
|
||||
final protocol version, and we changed ALPN ID to ``h2`` and ``h2c``.
|
||||
The macros ``NGHTTP2_PROTO_VERSION_ID``,
|
||||
``NGHTTP2_PROTO_VERSION_ID_LEN``,
|
||||
``NGHTTP2_CLEARTEXT_PROTO_VERSION_ID``, and
|
||||
``NGHTTP2_CLEARTEXT_PROTO_VERSION_ID_LEN`` have been updated to
|
||||
reflect this change.
|
||||
|
||||
Basically, existing applications do not have to do anything, just
|
||||
recompiling is enough for this change.
|
||||
|
||||
Use word "client magic" where we use "client connection preface"
|
||||
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
We use "client connection preface" to mean first 24 bytes of client
|
||||
connection preface. This is technically not correct, since client
|
||||
connection preface is composed of 24 bytes client magic byte string
|
||||
followed by SETTINGS frame. For clarification, we call "client magic"
|
||||
for this 24 bytes byte string and updated API.
|
||||
|
||||
* ``NGHTTP2_CLIENT_CONNECTION_PREFACE`` was replaced with
|
||||
``NGHTTP2_CLIENT_MAGIC``.
|
||||
* ``NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN`` was replaced with
|
||||
``NGHTTP2_CLIENT_MAGIC_LEN``.
|
||||
* ``NGHTTP2_BAD_PREFACE`` was renamed as ``NGHTTP2_BAD_CLIENT_MAGIC``
|
||||
|
||||
The alreay deprecated ``NGHTTP2_CLIENT_CONNECTION_HEADER`` and
|
||||
``NGHTTP2_CLIENT_CONNECTION_HEADER_LEN`` were removed.
|
||||
|
||||
If application uses these macros, just replace old ones with new ones.
|
||||
Since v1.0.0, client magic is sent by library (see next subsection),
|
||||
so client application may just remove these macro use.
|
||||
|
||||
Client magic is sent by library
|
||||
+++++++++++++++++++++++++++++++
|
||||
|
||||
Previously nghttp2 library did not send client magic, which is first
|
||||
24 bytes byte string of client connection preface, and client
|
||||
applications have to send it by themselves. Since v1.0.0, client
|
||||
magic is sent by library via first call of ``nghttp2_session_send()``
|
||||
or ``nghttp2_session_mem_send()``.
|
||||
|
||||
The client applications which send client magic must remove the
|
||||
relevant code.
|
||||
|
||||
Remove HTTP Alternative Services (Alt-Svc) related code
|
||||
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
Alt-Svc specification is not finalized yet. To make our API stable,
|
||||
we have decided to remove all Alt-Svc related API from nghttp2.
|
||||
|
||||
* ``NGHTTP2_EXT_ALTSVC`` was removed.
|
||||
* ``nghttp2_ext_altsvc`` was removed.
|
||||
|
||||
We have already removed the functionality of Alt-Svc in v0.7 series
|
||||
and they have been essentially noop. The application using these
|
||||
macro and struct, remove those lines.
|
||||
|
||||
Use nghttp2_error in nghttp2_on_invalid_frame_recv_callback
|
||||
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
Previously ``nghttp2_on_invalid_frame_recv_cb_called`` took the
|
||||
``error_code``, defined in ``nghttp2_error_code``, as parameter. But
|
||||
they are not detailed enough to debug. Therefore, we decided to use
|
||||
more detailed ``nghttp2_error`` values instead.
|
||||
|
||||
The application using this callback should update the callback
|
||||
signature. If it treats ``error_code`` as HTTP/2 error code, update
|
||||
the code so that it is treated as ``nghttp2_error``.
|
||||
|
||||
Receive client magic by default
|
||||
+++++++++++++++++++++++++++++++
|
||||
|
||||
Previously nghttp2 did not process client magic (24 bytes byte
|
||||
string). To make it deal with it, we had to use
|
||||
``nghttp2_option_set_recv_client_preface()``. Since v1.0.0, nghttp2
|
||||
processes client magic by default and
|
||||
``nghttp2_option_set_recv_client_preface()`` was removed.
|
||||
|
||||
Some application may want to disable this behaviour, so we added
|
||||
``nghttp2_option_set_no_recv_client_magic()`` to achieve this.
|
||||
|
||||
The application using ``nghttp2_option_set_recv_client_preface()``
|
||||
with nonzero value, just remove it.
|
||||
|
||||
The application using ``nghttp2_option_set_recv_client_preface()``
|
||||
with zero value or not using it must use
|
||||
``nghttp2_option_set_no_recv_client_magic()`` with nonzero value.
|
||||
|
||||
Client, Server and Proxy programs
|
||||
---------------------------------
|
||||
|
||||
@@ -227,140 +323,180 @@ It has verbose output mode for framing information. Here is sample
|
||||
output from ``nghttp`` client::
|
||||
|
||||
$ nghttp -nv https://nghttp2.org
|
||||
[ 0.033][NPN] server offers:
|
||||
* h2-14
|
||||
* spdy/3.1
|
||||
* http/1.1
|
||||
The negotiated protocol: h2-14
|
||||
[ 0.068] send SETTINGS frame <length=15, flags=0x00, stream_id=0>
|
||||
(niv=3)
|
||||
[SETTINGS_MAX_CONCURRENT_STREAMS(3):100]
|
||||
[SETTINGS_INITIAL_WINDOW_SIZE(4):65535]
|
||||
[SETTINGS_COMPRESS_DATA(5):1]
|
||||
[ 0.068] send HEADERS frame <length=46, flags=0x05, stream_id=1>
|
||||
; END_STREAM | END_HEADERS
|
||||
(padlen=0)
|
||||
; Open new stream
|
||||
:authority: nghttp2.org
|
||||
:method: GET
|
||||
:path: /
|
||||
:scheme: https
|
||||
accept: */*
|
||||
accept-encoding: gzip, deflate
|
||||
user-agent: nghttp2/0.4.0-DEV
|
||||
[ 0.068] recv SETTINGS frame <length=10, flags=0x00, stream_id=0>
|
||||
(niv=2)
|
||||
[SETTINGS_MAX_CONCURRENT_STREAMS(3):100]
|
||||
[SETTINGS_INITIAL_WINDOW_SIZE(4):65535]
|
||||
[ 0.068] send SETTINGS frame <length=0, flags=0x01, stream_id=0>
|
||||
; ACK
|
||||
(niv=0)
|
||||
[ 0.079] recv SETTINGS frame <length=0, flags=0x01, stream_id=0>
|
||||
; ACK
|
||||
(niv=0)
|
||||
[ 0.080] (stream_id=1, noind=0) :status: 200
|
||||
[ 0.080] (stream_id=1, noind=0) accept-ranges: bytes
|
||||
[ 0.080] (stream_id=1, noind=0) age: 15
|
||||
[ 0.080] (stream_id=1, noind=0) content-length: 40243
|
||||
[ 0.080] (stream_id=1, noind=0) content-type: text/html
|
||||
[ 0.080] (stream_id=1, noind=0) date: Wed, 14 May 2014 15:14:30 GMT
|
||||
[ 0.080] (stream_id=1, noind=0) etag: "535d0eea-9d33"
|
||||
[ 0.080] (stream_id=1, noind=0) last-modified: Sun, 27 Apr 2014 14:06:34 GMT
|
||||
[ 0.080] (stream_id=1, noind=0) server: nginx/1.4.6 (Ubuntu)
|
||||
[ 0.080] (stream_id=1, noind=0) x-varnish: 2114900538 2114900537
|
||||
[ 0.080] (stream_id=1, noind=0) via: 1.1 varnish, 1.1 nghttpx
|
||||
[ 0.080] (stream_id=1, noind=0) strict-transport-security: max-age=31536000
|
||||
[ 0.080] recv HEADERS frame <length=162, flags=0x04, stream_id=1>
|
||||
; END_HEADERS
|
||||
(padlen=0)
|
||||
; First response header
|
||||
[ 0.080] recv DATA frame <length=3786, flags=0x00, stream_id=1>
|
||||
[ 0.080] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.081] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.093] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.093] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.094] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.094] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.094] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.096] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.096] send WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=0>
|
||||
(window_size_increment=36554)
|
||||
[ 0.096] send WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=1>
|
||||
(window_size_increment=36554)
|
||||
[ 0.108] recv DATA frame <length=3689, flags=0x00, stream_id=1>
|
||||
[ 0.108] recv DATA frame <length=0, flags=0x01, stream_id=1>
|
||||
; END_STREAM
|
||||
[ 0.108] send GOAWAY frame <length=8, flags=0x00, stream_id=0>
|
||||
(last_stream_id=0, error_code=NO_ERROR(0), opaque_data(0)=[])
|
||||
[ 0.190] Connected
|
||||
The negotiated protocol: h2
|
||||
[ 0.212] recv SETTINGS frame <length=12, flags=0x00, stream_id=0>
|
||||
(niv=2)
|
||||
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
|
||||
[SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535]
|
||||
[ 0.212] send SETTINGS frame <length=12, flags=0x00, stream_id=0>
|
||||
(niv=2)
|
||||
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
|
||||
[SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535]
|
||||
[ 0.212] send SETTINGS frame <length=0, flags=0x01, stream_id=0>
|
||||
; ACK
|
||||
(niv=0)
|
||||
[ 0.212] send PRIORITY frame <length=5, flags=0x00, stream_id=3>
|
||||
(dep_stream_id=0, weight=201, exclusive=0)
|
||||
[ 0.212] send PRIORITY frame <length=5, flags=0x00, stream_id=5>
|
||||
(dep_stream_id=0, weight=101, exclusive=0)
|
||||
[ 0.212] send PRIORITY frame <length=5, flags=0x00, stream_id=7>
|
||||
(dep_stream_id=0, weight=1, exclusive=0)
|
||||
[ 0.212] send PRIORITY frame <length=5, flags=0x00, stream_id=9>
|
||||
(dep_stream_id=7, weight=1, exclusive=0)
|
||||
[ 0.212] send PRIORITY frame <length=5, flags=0x00, stream_id=11>
|
||||
(dep_stream_id=3, weight=1, exclusive=0)
|
||||
[ 0.212] send HEADERS frame <length=39, flags=0x25, stream_id=13>
|
||||
; END_STREAM | END_HEADERS | PRIORITY
|
||||
(padlen=0, dep_stream_id=11, weight=16, exclusive=0)
|
||||
; Open new stream
|
||||
:method: GET
|
||||
:path: /
|
||||
:scheme: https
|
||||
:authority: nghttp2.org
|
||||
accept: */*
|
||||
accept-encoding: gzip, deflate
|
||||
user-agent: nghttp2/1.0.1-DEV
|
||||
[ 0.221] recv SETTINGS frame <length=0, flags=0x01, stream_id=0>
|
||||
; ACK
|
||||
(niv=0)
|
||||
[ 0.221] recv (stream_id=13) :method: GET
|
||||
[ 0.221] recv (stream_id=13) :scheme: https
|
||||
[ 0.221] recv (stream_id=13) :path: /stylesheets/screen.css
|
||||
[ 0.221] recv (stream_id=13) :authority: nghttp2.org
|
||||
[ 0.221] recv (stream_id=13) accept-encoding: gzip, deflate
|
||||
[ 0.222] recv (stream_id=13) user-agent: nghttp2/1.0.1-DEV
|
||||
[ 0.222] recv PUSH_PROMISE frame <length=50, flags=0x04, stream_id=13>
|
||||
; END_HEADERS
|
||||
(padlen=0, promised_stream_id=2)
|
||||
[ 0.222] recv (stream_id=13) :status: 200
|
||||
[ 0.222] recv (stream_id=13) date: Thu, 21 May 2015 16:38:14 GMT
|
||||
[ 0.222] recv (stream_id=13) content-type: text/html
|
||||
[ 0.222] recv (stream_id=13) last-modified: Fri, 15 May 2015 15:38:06 GMT
|
||||
[ 0.222] recv (stream_id=13) etag: W/"555612de-19f6"
|
||||
[ 0.222] recv (stream_id=13) link: </stylesheets/screen.css>; rel=preload; as=stylesheet
|
||||
[ 0.222] recv (stream_id=13) content-encoding: gzip
|
||||
[ 0.222] recv (stream_id=13) server: nghttpx nghttp2/1.0.1-DEV
|
||||
[ 0.222] recv (stream_id=13) via: 1.1 nghttpx
|
||||
[ 0.222] recv (stream_id=13) strict-transport-security: max-age=31536000
|
||||
[ 0.222] recv HEADERS frame <length=166, flags=0x04, stream_id=13>
|
||||
; END_HEADERS
|
||||
(padlen=0)
|
||||
; First response header
|
||||
[ 0.222] recv DATA frame <length=2601, flags=0x01, stream_id=13>
|
||||
; END_STREAM
|
||||
[ 0.222] recv (stream_id=2) :status: 200
|
||||
[ 0.222] recv (stream_id=2) date: Thu, 21 May 2015 16:38:14 GMT
|
||||
[ 0.222] recv (stream_id=2) content-type: text/css
|
||||
[ 0.222] recv (stream_id=2) last-modified: Fri, 15 May 2015 15:38:06 GMT
|
||||
[ 0.222] recv (stream_id=2) etag: W/"555612de-9845"
|
||||
[ 0.222] recv (stream_id=2) content-encoding: gzip
|
||||
[ 0.222] recv (stream_id=2) server: nghttpx nghttp2/1.0.1-DEV
|
||||
[ 0.222] recv (stream_id=2) via: 1.1 nghttpx
|
||||
[ 0.222] recv (stream_id=2) strict-transport-security: max-age=31536000
|
||||
[ 0.222] recv HEADERS frame <length=32, flags=0x04, stream_id=2>
|
||||
; END_HEADERS
|
||||
(padlen=0)
|
||||
; First push response header
|
||||
[ 0.228] recv DATA frame <length=8715, flags=0x01, stream_id=2>
|
||||
; END_STREAM
|
||||
[ 0.228] send GOAWAY frame <length=8, flags=0x00, stream_id=0>
|
||||
(last_stream_id=2, error_code=NO_ERROR(0x00), opaque_data(0)=[])
|
||||
|
||||
The HTTP Upgrade is performed like this::
|
||||
The HTTP Upgrade is performed like so::
|
||||
|
||||
$ nghttp -nvu http://nghttp2.org
|
||||
[ 0.013] HTTP Upgrade request
|
||||
[ 0.011] Connected
|
||||
[ 0.011] HTTP Upgrade request
|
||||
GET / HTTP/1.1
|
||||
Host: nghttp2.org
|
||||
Connection: Upgrade, HTTP2-Settings
|
||||
Upgrade: h2c-14
|
||||
HTTP2-Settings: AwAAAGQEAAD__wUAAAAB
|
||||
Upgrade: h2c
|
||||
HTTP2-Settings: AAMAAABkAAQAAP__
|
||||
Accept: */*
|
||||
User-Agent: nghttp2/0.4.0-DEV
|
||||
User-Agent: nghttp2/1.0.1-DEV
|
||||
|
||||
|
||||
[ 0.024] HTTP Upgrade response
|
||||
[ 0.018] HTTP Upgrade response
|
||||
HTTP/1.1 101 Switching Protocols
|
||||
Connection: Upgrade
|
||||
Upgrade: h2c-14
|
||||
Upgrade: h2c
|
||||
|
||||
|
||||
[ 0.024] HTTP Upgrade success
|
||||
[ 0.024] send SETTINGS frame <length=15, flags=0x00, stream_id=0>
|
||||
(niv=3)
|
||||
[SETTINGS_MAX_CONCURRENT_STREAMS(3):100]
|
||||
[SETTINGS_INITIAL_WINDOW_SIZE(4):65535]
|
||||
[SETTINGS_COMPRESS_DATA(5):1]
|
||||
[ 0.024] recv SETTINGS frame <length=10, flags=0x00, stream_id=0>
|
||||
(niv=2)
|
||||
[SETTINGS_MAX_CONCURRENT_STREAMS(3):100]
|
||||
[SETTINGS_INITIAL_WINDOW_SIZE(4):65535]
|
||||
[ 0.024] send SETTINGS frame <length=0, flags=0x01, stream_id=0>
|
||||
; ACK
|
||||
(niv=0)
|
||||
[ 0.024] (stream_id=1, noind=0) :status: 200
|
||||
[ 0.024] (stream_id=1, noind=0) accept-ranges: bytes
|
||||
[ 0.024] (stream_id=1, noind=0) age: 10
|
||||
[ 0.024] (stream_id=1, noind=0) content-length: 40243
|
||||
[ 0.024] (stream_id=1, noind=0) content-type: text/html
|
||||
[ 0.024] (stream_id=1, noind=0) date: Wed, 14 May 2014 15:16:34 GMT
|
||||
[ 0.024] (stream_id=1, noind=0) etag: "535d0eea-9d33"
|
||||
[ 0.024] (stream_id=1, noind=0) last-modified: Sun, 27 Apr 2014 14:06:34 GMT
|
||||
[ 0.024] (stream_id=1, noind=0) server: nginx/1.4.6 (Ubuntu)
|
||||
[ 0.024] (stream_id=1, noind=0) x-varnish: 2114900541 2114900540
|
||||
[ 0.024] (stream_id=1, noind=0) via: 1.1 varnish, 1.1 nghttpx
|
||||
[ 0.024] recv HEADERS frame <length=148, flags=0x04, stream_id=1>
|
||||
; END_HEADERS
|
||||
(padlen=0)
|
||||
; First response header
|
||||
[ 0.024] recv DATA frame <length=3786, flags=0x00, stream_id=1>
|
||||
[ 0.025] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.031] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.031] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.032] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.032] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.033] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.033] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.033] send WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=0>
|
||||
(window_size_increment=33164)
|
||||
[ 0.033] send WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=1>
|
||||
(window_size_increment=33164)
|
||||
[ 0.038] recv DATA frame <length=4096, flags=0x00, stream_id=1>
|
||||
[ 0.038] recv DATA frame <length=3689, flags=0x00, stream_id=1>
|
||||
[ 0.038] recv DATA frame <length=0, flags=0x01, stream_id=1>
|
||||
; END_STREAM
|
||||
[ 0.038] recv SETTINGS frame <length=0, flags=0x01, stream_id=0>
|
||||
; ACK
|
||||
(niv=0)
|
||||
[ 0.038] send GOAWAY frame <length=8, flags=0x00, stream_id=0>
|
||||
(last_stream_id=0, error_code=NO_ERROR(0), opaque_data(0)=[])
|
||||
[ 0.018] HTTP Upgrade success
|
||||
[ 0.018] recv SETTINGS frame <length=12, flags=0x00, stream_id=0>
|
||||
(niv=2)
|
||||
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
|
||||
[SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535]
|
||||
[ 0.018] send SETTINGS frame <length=12, flags=0x00, stream_id=0>
|
||||
(niv=2)
|
||||
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
|
||||
[SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535]
|
||||
[ 0.018] send SETTINGS frame <length=0, flags=0x01, stream_id=0>
|
||||
; ACK
|
||||
(niv=0)
|
||||
[ 0.018] send PRIORITY frame <length=5, flags=0x00, stream_id=3>
|
||||
(dep_stream_id=0, weight=201, exclusive=0)
|
||||
[ 0.018] send PRIORITY frame <length=5, flags=0x00, stream_id=5>
|
||||
(dep_stream_id=0, weight=101, exclusive=0)
|
||||
[ 0.018] send PRIORITY frame <length=5, flags=0x00, stream_id=7>
|
||||
(dep_stream_id=0, weight=1, exclusive=0)
|
||||
[ 0.018] send PRIORITY frame <length=5, flags=0x00, stream_id=9>
|
||||
(dep_stream_id=7, weight=1, exclusive=0)
|
||||
[ 0.018] send PRIORITY frame <length=5, flags=0x00, stream_id=11>
|
||||
(dep_stream_id=3, weight=1, exclusive=0)
|
||||
[ 0.018] send PRIORITY frame <length=5, flags=0x00, stream_id=1>
|
||||
(dep_stream_id=11, weight=16, exclusive=0)
|
||||
[ 0.019] recv (stream_id=1) :method: GET
|
||||
[ 0.019] recv (stream_id=1) :scheme: http
|
||||
[ 0.019] recv (stream_id=1) :path: /stylesheets/screen.css
|
||||
[ 0.019] recv (stream_id=1) host: nghttp2.org
|
||||
[ 0.019] recv (stream_id=1) user-agent: nghttp2/1.0.1-DEV
|
||||
[ 0.019] recv PUSH_PROMISE frame <length=49, flags=0x04, stream_id=1>
|
||||
; END_HEADERS
|
||||
(padlen=0, promised_stream_id=2)
|
||||
[ 0.019] recv (stream_id=1) :status: 200
|
||||
[ 0.019] recv (stream_id=1) date: Thu, 21 May 2015 16:39:16 GMT
|
||||
[ 0.019] recv (stream_id=1) content-type: text/html
|
||||
[ 0.019] recv (stream_id=1) content-length: 6646
|
||||
[ 0.019] recv (stream_id=1) last-modified: Fri, 15 May 2015 15:38:06 GMT
|
||||
[ 0.019] recv (stream_id=1) etag: "555612de-19f6"
|
||||
[ 0.019] recv (stream_id=1) link: </stylesheets/screen.css>; rel=preload; as=stylesheet
|
||||
[ 0.019] recv (stream_id=1) accept-ranges: bytes
|
||||
[ 0.019] recv (stream_id=1) server: nghttpx nghttp2/1.0.1-DEV
|
||||
[ 0.019] recv (stream_id=1) via: 1.1 nghttpx
|
||||
[ 0.019] recv HEADERS frame <length=157, flags=0x04, stream_id=1>
|
||||
; END_HEADERS
|
||||
(padlen=0)
|
||||
; First response header
|
||||
[ 0.019] recv DATA frame <length=6646, flags=0x01, stream_id=1>
|
||||
; END_STREAM
|
||||
[ 0.019] recv (stream_id=2) :status: 200
|
||||
[ 0.019] recv (stream_id=2) date: Thu, 21 May 2015 16:39:16 GMT
|
||||
[ 0.019] recv (stream_id=2) content-type: text/css
|
||||
[ 0.019] recv (stream_id=2) content-length: 38981
|
||||
[ 0.019] recv (stream_id=2) last-modified: Fri, 15 May 2015 15:38:06 GMT
|
||||
[ 0.019] recv (stream_id=2) etag: "555612de-9845"
|
||||
[ 0.019] recv (stream_id=2) accept-ranges: bytes
|
||||
[ 0.019] recv (stream_id=2) server: nghttpx nghttp2/1.0.1-DEV
|
||||
[ 0.019] recv (stream_id=2) via: 1.1 nghttpx
|
||||
[ 0.019] recv HEADERS frame <length=36, flags=0x04, stream_id=2>
|
||||
; END_HEADERS
|
||||
(padlen=0)
|
||||
; First push response header
|
||||
[ 0.026] recv DATA frame <length=16384, flags=0x00, stream_id=2>
|
||||
[ 0.027] recv DATA frame <length=7952, flags=0x00, stream_id=2>
|
||||
[ 0.027] send WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=0>
|
||||
(window_size_increment=33343)
|
||||
[ 0.032] send WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=2>
|
||||
(window_size_increment=33707)
|
||||
[ 0.032] recv DATA frame <length=14645, flags=0x01, stream_id=2>
|
||||
; END_STREAM
|
||||
[ 0.032] recv SETTINGS frame <length=0, flags=0x01, stream_id=0>
|
||||
; ACK
|
||||
(niv=0)
|
||||
[ 0.032] send GOAWAY frame <length=8, flags=0x00, stream_id=0>
|
||||
(last_stream_id=2, error_code=NO_ERROR(0x00), opaque_data(0)=[])
|
||||
|
||||
Using the ``-s`` option, ``nghttp`` prints out some timing information for
|
||||
requests, sorted by completion time::
|
||||
@@ -369,24 +505,26 @@ requests, sorted by completion time::
|
||||
***** Statistics *****
|
||||
|
||||
Request timing:
|
||||
complete: relative time from protocol handshake to stream close
|
||||
request: relative time from protocol handshake to request
|
||||
transmission. If '*' is shown, this was pushed by server.
|
||||
process: time for request and response
|
||||
code: HTTP status code
|
||||
size: number of bytes received as response body without
|
||||
inflation.
|
||||
URI: request URI
|
||||
responseEnd: the time when last byte of response was received
|
||||
relative to connectEnd
|
||||
requestStart: the time just before first byte of request was sent
|
||||
relative to connectEnd. If '*' is shown, this was
|
||||
pushed by server.
|
||||
process: responseEnd - requestStart
|
||||
code: HTTP status code
|
||||
size: number of bytes received as response body without
|
||||
inflation.
|
||||
URI: request URI
|
||||
|
||||
see http://www.w3.org/TR/resource-timing/#processing-model
|
||||
|
||||
sorted by 'complete'
|
||||
|
||||
complete request process code size request path
|
||||
+11.07ms +120us 10.95ms 200 9K /
|
||||
+16.77ms * +8.80ms 7.98ms 200 8K /stylesheets/screen.css
|
||||
+27.00ms +11.16ms 15.84ms 200 3K /javascripts/octopress.js
|
||||
+27.40ms +11.16ms 16.24ms 200 3K /javascripts/modernizr-2.0.js
|
||||
+76.14ms +11.17ms 64.97ms 200 171K /images/posts/with-pri-blog.png
|
||||
+88.52ms +11.17ms 77.36ms 200 174K /images/posts/without-pri-blog.png
|
||||
id responseEnd requestStart process code size request path
|
||||
13 +37.19ms +280us 36.91ms 200 2K /
|
||||
2 +72.65ms * +36.38ms 36.26ms 200 8K /stylesheets/screen.css
|
||||
17 +77.43ms +38.67ms 38.75ms 200 3K /javascripts/octopress.js
|
||||
15 +78.12ms +38.66ms 39.46ms 200 3K /javascripts/modernizr-2.0.js
|
||||
|
||||
Using the ``-r`` option, ``nghttp`` writes more detailed timing data to
|
||||
the given file in HAR format.
|
||||
@@ -408,58 +546,65 @@ Just like ``nghttp``, it has a verbose output mode for framing
|
||||
information. Here is sample output from ``nghttpd``::
|
||||
|
||||
$ nghttpd --no-tls -v 8080
|
||||
IPv4: listen on port 8080
|
||||
IPv6: listen on port 8080
|
||||
[id=1] [ 15.921] send SETTINGS frame <length=10, flags=0x00, stream_id=0>
|
||||
IPv4: listen 0.0.0.0:8080
|
||||
IPv6: listen :::8080
|
||||
[id=1] [ 1.521] send SETTINGS frame <length=6, flags=0x00, stream_id=0>
|
||||
(niv=1)
|
||||
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
|
||||
[id=1] [ 1.521] recv SETTINGS frame <length=12, flags=0x00, stream_id=0>
|
||||
(niv=2)
|
||||
[SETTINGS_MAX_CONCURRENT_STREAMS(3):100]
|
||||
[SETTINGS_COMPRESS_DATA(5):1]
|
||||
[id=1] [ 15.921] recv SETTINGS frame <length=15, flags=0x00, stream_id=0>
|
||||
(niv=3)
|
||||
[SETTINGS_MAX_CONCURRENT_STREAMS(3):100]
|
||||
[SETTINGS_INITIAL_WINDOW_SIZE(4):65535]
|
||||
[SETTINGS_COMPRESS_DATA(5):1]
|
||||
[id=1] [ 15.921] (stream_id=1, noind=0) :authority: localhost:8080
|
||||
[id=1] [ 15.921] (stream_id=1, noind=0) :method: GET
|
||||
[id=1] [ 15.921] (stream_id=1, noind=0) :path: /
|
||||
[id=1] [ 15.921] (stream_id=1, noind=0) :scheme: http
|
||||
[id=1] [ 15.921] (stream_id=1, noind=0) accept: */*
|
||||
[id=1] [ 15.921] (stream_id=1, noind=0) accept-encoding: gzip, deflate
|
||||
[id=1] [ 15.921] (stream_id=1, noind=0) user-agent: nghttp2/0.4.0-DEV
|
||||
[id=1] [ 15.921] recv HEADERS frame <length=48, flags=0x05, stream_id=1>
|
||||
; END_STREAM | END_HEADERS
|
||||
(padlen=0)
|
||||
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
|
||||
[SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535]
|
||||
[id=1] [ 1.521] recv SETTINGS frame <length=0, flags=0x01, stream_id=0>
|
||||
; ACK
|
||||
(niv=0)
|
||||
[id=1] [ 1.521] recv PRIORITY frame <length=5, flags=0x00, stream_id=3>
|
||||
(dep_stream_id=0, weight=201, exclusive=0)
|
||||
[id=1] [ 1.521] recv PRIORITY frame <length=5, flags=0x00, stream_id=5>
|
||||
(dep_stream_id=0, weight=101, exclusive=0)
|
||||
[id=1] [ 1.521] recv PRIORITY frame <length=5, flags=0x00, stream_id=7>
|
||||
(dep_stream_id=0, weight=1, exclusive=0)
|
||||
[id=1] [ 1.521] recv PRIORITY frame <length=5, flags=0x00, stream_id=9>
|
||||
(dep_stream_id=7, weight=1, exclusive=0)
|
||||
[id=1] [ 1.521] recv PRIORITY frame <length=5, flags=0x00, stream_id=11>
|
||||
(dep_stream_id=3, weight=1, exclusive=0)
|
||||
[id=1] [ 1.521] recv (stream_id=13) :method: GET
|
||||
[id=1] [ 1.521] recv (stream_id=13) :path: /
|
||||
[id=1] [ 1.521] recv (stream_id=13) :scheme: http
|
||||
[id=1] [ 1.521] recv (stream_id=13) :authority: localhost:8080
|
||||
[id=1] [ 1.521] recv (stream_id=13) accept: */*
|
||||
[id=1] [ 1.521] recv (stream_id=13) accept-encoding: gzip, deflate
|
||||
[id=1] [ 1.521] recv (stream_id=13) user-agent: nghttp2/1.0.0-DEV
|
||||
[id=1] [ 1.521] recv HEADERS frame <length=41, flags=0x25, stream_id=13>
|
||||
; END_STREAM | END_HEADERS | PRIORITY
|
||||
(padlen=0, dep_stream_id=11, weight=16, exclusive=0)
|
||||
; Open new stream
|
||||
[id=1] [ 15.921] recv SETTINGS frame <length=0, flags=0x01, stream_id=0>
|
||||
[id=1] [ 1.521] send SETTINGS frame <length=0, flags=0x01, stream_id=0>
|
||||
; ACK
|
||||
(niv=0)
|
||||
[id=1] [ 15.921] send SETTINGS frame <length=0, flags=0x01, stream_id=0>
|
||||
; ACK
|
||||
(niv=0)
|
||||
[id=1] [ 15.921] send HEADERS frame <length=82, flags=0x04, stream_id=1>
|
||||
[id=1] [ 1.521] send HEADERS frame <length=86, flags=0x04, stream_id=13>
|
||||
; END_HEADERS
|
||||
(padlen=0)
|
||||
; First response header
|
||||
:status: 200
|
||||
server: nghttpd nghttp2/1.0.0-DEV
|
||||
content-length: 10
|
||||
cache-control: max-age=3600
|
||||
content-length: 612
|
||||
date: Wed, 14 May 2014 15:19:03 GMT
|
||||
last-modified: Sat, 08 Mar 2014 16:04:06 GMT
|
||||
server: nghttpd nghttp2/0.4.0-DEV
|
||||
[id=1] [ 15.922] send DATA frame <length=381, flags=0x20, stream_id=1>
|
||||
; COMPRESSED
|
||||
[id=1] [ 15.922] send DATA frame <length=0, flags=0x01, stream_id=1>
|
||||
date: Fri, 15 May 2015 14:49:04 GMT
|
||||
last-modified: Tue, 30 Sep 2014 12:40:52 GMT
|
||||
[id=1] [ 1.522] send DATA frame <length=10, flags=0x01, stream_id=13>
|
||||
; END_STREAM
|
||||
[id=1] [ 15.922] stream_id=1 closed
|
||||
[id=1] [ 15.922] recv GOAWAY frame <length=8, flags=0x00, stream_id=0>
|
||||
(last_stream_id=0, error_code=NO_ERROR(0), opaque_data(0)=[])
|
||||
[id=1] [ 15.922] closed
|
||||
[id=1] [ 1.522] stream_id=13 closed
|
||||
[id=1] [ 1.522] recv GOAWAY frame <length=8, flags=0x00, stream_id=0>
|
||||
(last_stream_id=0, error_code=NO_ERROR(0x00), opaque_data(0)=[])
|
||||
[id=1] [ 1.522] closed
|
||||
|
||||
nghttpx - proxy
|
||||
+++++++++++++++
|
||||
|
||||
``nghttpx`` is a multi-threaded reverse proxy for ``h2-14``, SPDY and
|
||||
HTTP/1.1, and powers http://nghttp2.org and supports HTTP/2 server push.
|
||||
``nghttpx`` is a multi-threaded reverse proxy for HTTP/2, SPDY and
|
||||
HTTP/1.1, and powers http://nghttp2.org and supports HTTP/2 server
|
||||
push.
|
||||
|
||||
``nghttpx`` implements `important performance-oriented features
|
||||
<https://istlsfastyet.com/#server-performance>`_ in TLS, such as
|
||||
@@ -480,8 +625,8 @@ default mode HTTP/2, SPDY, HTTP/1.1 (TLS) HTTP/1.1 Reverse proxy
|
||||
================== ============================ ============== =============
|
||||
|
||||
The interesting mode at the moment is the default mode. It works like
|
||||
a reverse proxy and listens for ``h2-14``, SPDY and HTTP/1.1 and can
|
||||
be deployed as a SSL/TLS terminator for existing web server.
|
||||
a reverse proxy and listens for HTTP/2, SPDY and HTTP/1.1 and can be
|
||||
deployed as a SSL/TLS terminator for existing web server.
|
||||
|
||||
The default mode, ``--http2-proxy`` and ``--http2-bridge`` modes use
|
||||
SSL/TLS in the frontend connection by default. To disable SSL/TLS,
|
||||
@@ -490,7 +635,7 @@ disabled in the frontend and incoming HTTP/1.1 connections can be
|
||||
upgraded to HTTP/2 through HTTP Upgrade.
|
||||
|
||||
The ``--http2-bridge``, ``--client`` and ``--client-proxy`` modes use
|
||||
SSL/TLS in the backend connection by deafult. To disable SSL/TLS, use
|
||||
SSL/TLS in the backend connection by default. To disable SSL/TLS, use
|
||||
the ``--backend-no-tls`` option.
|
||||
|
||||
``nghttpx`` supports a configuration file. See the ``--conf`` option and
|
||||
@@ -582,6 +727,7 @@ follows::
|
||||
spawning thread #0: 100 concurrent clients, 100000 total requests
|
||||
Protocol: TLSv1.2
|
||||
Cipher: ECDHE-RSA-AES128-GCM-SHA256
|
||||
Server Temp Key: ECDH P-256 256 bits
|
||||
progress: 10% done
|
||||
progress: 20% done
|
||||
progress: 30% done
|
||||
@@ -593,12 +739,14 @@ follows::
|
||||
progress: 90% done
|
||||
progress: 100% done
|
||||
|
||||
finished in 7.10s, 14092 req/s, 55.67MB/s
|
||||
finished in 771.26ms, 129658 req/s, 4.71MB/s
|
||||
requests: 100000 total, 100000 started, 100000 done, 100000 succeeded, 0 failed, 0 errored
|
||||
status codes: 100000 2xx, 0 3xx, 0 4xx, 0 5xx
|
||||
traffic: 414200800 bytes total, 2723100 bytes headers, 409600000 bytes data
|
||||
traffic: 3812300 bytes total, 1009900 bytes headers, 1000000 bytes data
|
||||
min max mean sd +/- sd
|
||||
time for request: 283.86ms 1.46s 659.70ms 150.87ms 84.68%
|
||||
time for request: 25.12ms 124.55ms 51.07ms 15.36ms 84.87%
|
||||
time for connect: 208.94ms 254.67ms 241.38ms 7.95ms 63.00%
|
||||
time to 1st byte: 209.11ms 254.80ms 241.51ms 7.94ms 63.00%
|
||||
|
||||
The above example issued total 100,000 requests, using 100 concurrent
|
||||
clients (in other words, 100 HTTP/2 sessions), and a maximum of 100 streams
|
||||
@@ -785,7 +933,7 @@ max_deflate_size
|
||||
The maximum header table size the encoder uses. This can be smaller
|
||||
than ``max_size``. In this case, the encoder only uses up to first
|
||||
``max_deflate_size`` buffer. Since the header table size is still
|
||||
``max_size``, the encoder has to keep track of entries ouside the
|
||||
``max_size``, the encoder has to keep track of entries outside the
|
||||
``max_deflate_size`` but inside the ``max_size`` and make sure
|
||||
that they are no longer referenced.
|
||||
|
||||
@@ -1108,32 +1256,32 @@ Here is sample code to use the client API:
|
||||
session sess(io_service, "localhost", "3000");
|
||||
|
||||
sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) {
|
||||
boost::system::error_code ec;
|
||||
boost::system::error_code ec;
|
||||
|
||||
auto req = sess.submit(ec, "GET", "http://localhost:3000/");
|
||||
auto req = sess.submit(ec, "GET", "http://localhost:3000/");
|
||||
|
||||
req->on_response([](const response &res) {
|
||||
// print status code and response header fields.
|
||||
std::cerr << "HTTP/2 " << res.status_code() << std::endl;
|
||||
for (auto &kv : res.header()) {
|
||||
std::cerr << kv.first << ": " << kv.second.value << "\n";
|
||||
}
|
||||
std::cerr << std::endl;
|
||||
req->on_response([](const response &res) {
|
||||
// print status code and response header fields.
|
||||
std::cerr << "HTTP/2 " << res.status_code() << std::endl;
|
||||
for (auto &kv : res.header()) {
|
||||
std::cerr << kv.first << ": " << kv.second.value << "\n";
|
||||
}
|
||||
std::cerr << std::endl;
|
||||
|
||||
res.on_data([](const uint8_t *data, std::size_t len) {
|
||||
std::cerr.write(reinterpret_cast<const char *>(data), len);
|
||||
std::cerr << std::endl;
|
||||
});
|
||||
});
|
||||
res.on_data([](const uint8_t *data, std::size_t len) {
|
||||
std::cerr.write(reinterpret_cast<const char *>(data), len);
|
||||
std::cerr << std::endl;
|
||||
});
|
||||
});
|
||||
|
||||
req->on_close([&sess](uint32_t error_code) {
|
||||
// shutdown session after first request was done.
|
||||
sess.shutdown();
|
||||
});
|
||||
req->on_close([&sess](uint32_t error_code) {
|
||||
// shutdown session after first request was done.
|
||||
sess.shutdown();
|
||||
});
|
||||
});
|
||||
|
||||
sess.on_error([](const boost::system::error_code &ec) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
});
|
||||
|
||||
io_service.run();
|
||||
@@ -1273,7 +1421,7 @@ original creator(s) or those who have been assigned copyright by the
|
||||
original author(s).
|
||||
|
||||
By submitting a patch to the nghttp2 project, you (or your employer, as
|
||||
the case may be) agree to assign the copyright of your submission to us.
|
||||
the case may be) agree to assign the copyright of your submission to us.
|
||||
.. the above really needs to be reworded to pass legal muster.
|
||||
We will credit you for your
|
||||
changes as far as possible, to give credit but also to keep a trace
|
||||
|
||||
137
configure.ac
137
configure.ac
@@ -25,29 +25,16 @@ dnl Do not change user variables!
|
||||
dnl http://www.gnu.org/software/automake/manual/html_node/Flag-Variables-Ordering.html
|
||||
|
||||
AC_PREREQ(2.61)
|
||||
AC_INIT([nghttp2], [0.7.13], [t-tujikawa@users.sourceforge.net])
|
||||
AC_INIT([nghttp2], [1.0.2], [t-tujikawa@users.sourceforge.net])
|
||||
AC_USE_SYSTEM_EXTENSIONS
|
||||
|
||||
LT_PREREQ([2.2.6])
|
||||
LT_INIT()
|
||||
dnl See versioning rule:
|
||||
dnl http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
|
||||
AC_SUBST(LT_CURRENT, 13)
|
||||
AC_SUBST(LT_REVISION, 2)
|
||||
AC_SUBST(LT_AGE, 8)
|
||||
|
||||
major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/[^0-9]//g"`
|
||||
minor=`echo $PACKAGE_VERSION |cut -d. -f2 | sed -e "s/[^0-9]//g"`
|
||||
patch=`echo $PACKAGE_VERSION |cut -d. -f3 | cut -d- -f1 | sed -e "s/[^0-9]//g"`
|
||||
|
||||
PACKAGE_VERSION_NUM=`printf "0x%02x%02x%02x" "$major" "$minor" "$patch"`
|
||||
|
||||
AC_SUBST(PACKAGE_VERSION_NUM)
|
||||
|
||||
AC_CANONICAL_BUILD
|
||||
AC_CANONICAL_HOST
|
||||
AC_CANONICAL_TARGET
|
||||
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
|
||||
AM_INIT_AUTOMAKE([subdir-objects])
|
||||
# comment out for now since this requires automake 1.13 or higher and
|
||||
# travis has older one.
|
||||
@@ -55,8 +42,23 @@ AM_INIT_AUTOMAKE([subdir-objects])
|
||||
|
||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
|
||||
dnl See versioning rule:
|
||||
dnl http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
|
||||
AC_SUBST(LT_CURRENT, 14)
|
||||
AC_SUBST(LT_REVISION, 2)
|
||||
AC_SUBST(LT_AGE, 0)
|
||||
|
||||
major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/[^0-9]//g"`
|
||||
minor=`echo $PACKAGE_VERSION |cut -d. -f2 | sed -e "s/[^0-9]//g"`
|
||||
patch=`echo $PACKAGE_VERSION |cut -d. -f3 | cut -d- -f1 | sed -e "s/[^0-9]//g"`
|
||||
|
||||
PACKAGE_VERSION_NUM=`printf "0x%02x%02x%02x" "$major" "$minor" "$patch"`
|
||||
|
||||
AC_SUBST(PACKAGE_VERSION_NUM)
|
||||
|
||||
dnl Checks for command-line options
|
||||
AC_ARG_ENABLE([werror],
|
||||
[AS_HELP_STRING([--enable-werror],
|
||||
@@ -129,14 +131,17 @@ AC_ARG_VAR([CYTHON], [the Cython executable])
|
||||
dnl Checks for programs
|
||||
AC_PROG_CC
|
||||
AC_PROG_CXX
|
||||
AC_PROG_CPP
|
||||
AC_PROG_INSTALL
|
||||
AC_PROG_LN_S
|
||||
AC_PROG_MAKE_SET
|
||||
AM_PROG_CC_C_O
|
||||
AC_PROG_MKDIR_P
|
||||
|
||||
PKG_PROG_PKG_CONFIG([0.20])
|
||||
|
||||
AM_PATH_PYTHON([2.7],, [:])
|
||||
|
||||
if [test "x$request_python_bindings" != "xno"]; then
|
||||
AM_PATH_PYTHON([2.7],, [:])
|
||||
AX_PYTHON_DEVEL([>= '2.7'])
|
||||
fi
|
||||
|
||||
@@ -226,20 +231,6 @@ TESTLDADD=
|
||||
# Additional libraries required for programs under src directory.
|
||||
APPLDFLAGS=
|
||||
|
||||
LIBS_OLD=$LIBS
|
||||
# Search for dlsym function, which is used in tests. Linux needs -ldl,
|
||||
# but netbsd does not need it.
|
||||
AC_SEARCH_LIBS([dlsym], [dl])
|
||||
TESTLDADD="$LIBS $TESTLDADD"
|
||||
LIBS=$LIBS_OLD
|
||||
|
||||
LIBS_OLD=$LIBS
|
||||
AC_SEARCH_LIBS([clock_gettime], [rt],
|
||||
[AC_DEFINE([HAVE_CLOCK_GETTIME], [1],
|
||||
[Define to 1 if you have the `clock_gettime`.])])
|
||||
APPLDFLAGS="$LIBS $APPLDFLAGS"
|
||||
LIBS=$LIBS_OLD
|
||||
|
||||
case "$host" in
|
||||
*android*)
|
||||
android_build=yes
|
||||
@@ -253,16 +244,10 @@ case "$host" in
|
||||
esac
|
||||
|
||||
# zlib
|
||||
if test "x$android_build" = "xyes"; then
|
||||
# Use zlib provided by NDK
|
||||
APPLDFLAGS="-lz $APPLDFLAGS"
|
||||
have_zlib=yes
|
||||
else
|
||||
PKG_CHECK_MODULES([ZLIB], [zlib >= 1.2.3], [have_zlib=yes], [have_zlib=no])
|
||||
PKG_CHECK_MODULES([ZLIB], [zlib >= 1.2.3], [have_zlib=yes], [have_zlib=no])
|
||||
|
||||
if test "x${have_zlib}" = "xno"; then
|
||||
AC_MSG_NOTICE($ZLIB_PKG_ERRORS)
|
||||
fi
|
||||
if test "x${have_zlib}" = "xno"; then
|
||||
AC_MSG_NOTICE($ZLIB_PKG_ERRORS)
|
||||
fi
|
||||
|
||||
# cunit
|
||||
@@ -328,7 +313,7 @@ fi
|
||||
# jansson (for src/nghttp, src/deflatehd and src/inflatehd)
|
||||
PKG_CHECK_MODULES([JANSSON], [jansson >= 2.5],
|
||||
[have_jansson=yes], [have_jansson=no])
|
||||
if test "x${have_jansson}" == "xyes"; then
|
||||
if test "x${have_jansson}" = "xyes"; then
|
||||
AC_DEFINE([HAVE_JANSSON], [1],
|
||||
[Define to 1 if you have `libjansson` library.])
|
||||
else
|
||||
@@ -358,9 +343,23 @@ if test "x${request_jemalloc}" != "xno"; then
|
||||
AC_SEARCH_LIBS([malloc_stats_print], [jemalloc], [have_jemalloc=yes], [],
|
||||
[$PTHREAD_LDFLAGS])
|
||||
LIBS=$LIBS_OLD
|
||||
|
||||
if test "x${have_jemalloc}" = "xyes"; then
|
||||
jemalloc_libs=${ac_cv_search_malloc_stats_print}
|
||||
else
|
||||
# On Darwin, malloc_stats_print is je_malloc_stats_print
|
||||
AC_SEARCH_LIBS([je_malloc_stats_print], [jemalloc], [have_jemalloc=yes], [],
|
||||
[$PTHREAD_LDFLAGS])
|
||||
LIBS=$LIBS_OLD
|
||||
|
||||
if test "x${have_jemalloc}" = "xyes"; then
|
||||
jemalloc_libs=${ac_cv_search_je_malloc_stats_print}
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "x${have_jemalloc}" = "xyes" &&
|
||||
test "x${ac_cv_search_malloc_stats_print}" != "xnone required"; then
|
||||
JEMALLOC_LIBS=${ac_cv_search_malloc_stats_print}
|
||||
test "x${jemalloc_libs}" != "xnone required"; then
|
||||
JEMALLOC_LIBS=${jemalloc_libs}
|
||||
AC_SUBST([JEMALLOC_LIBS])
|
||||
fi
|
||||
fi
|
||||
@@ -465,6 +464,16 @@ fi
|
||||
|
||||
AM_CONDITIONAL([ENABLE_EXAMPLES], [ test "x${enable_examples}" = "xyes" ])
|
||||
|
||||
# third-party only be built if either enable_examples or enable_app is
|
||||
# yes
|
||||
|
||||
enable_third_party=no
|
||||
if test "x${enable_examples}" = "xyes" || test "x${enable_app}" = "xyes"; then
|
||||
enable_third_party=yes
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL([ENABLE_THIRD_PARTY], [ test "x${enable_third_party}" = "xyes" ])
|
||||
|
||||
# Python bindings
|
||||
enable_python_bindings=no
|
||||
if test "x${request_python_bindings}" != "xno" &&
|
||||
@@ -498,12 +507,19 @@ AM_CONDITIONAL([ENABLE_FAILMALLOC], [ test "x${enable_failmalloc}" = "xyes" ])
|
||||
AC_HEADER_ASSERT
|
||||
AC_CHECK_HEADERS([ \
|
||||
arpa/inet.h \
|
||||
fcntl.h \
|
||||
inttypes.h \
|
||||
limits.h \
|
||||
netdb.h \
|
||||
netinet/in.h \
|
||||
pwd.h \
|
||||
stddef.h \
|
||||
stdint.h \
|
||||
stdlib.h \
|
||||
string.h \
|
||||
sys/socket.h \
|
||||
sys/time.h \
|
||||
syslog.h \
|
||||
time.h \
|
||||
unistd.h \
|
||||
])
|
||||
@@ -515,8 +531,16 @@ AC_TYPE_UINT8_T
|
||||
AC_TYPE_UINT16_T
|
||||
AC_TYPE_UINT32_T
|
||||
AC_TYPE_UINT64_T
|
||||
AC_TYPE_INT8_T
|
||||
AC_TYPE_INT16_T
|
||||
AC_TYPE_INT32_T
|
||||
AC_TYPE_INT64_T
|
||||
AC_TYPE_OFF_T
|
||||
AC_TYPE_PID_T
|
||||
AC_TYPE_UID_T
|
||||
AC_CHECK_TYPES([ptrdiff_t])
|
||||
AC_C_BIGENDIAN
|
||||
AC_C_INLINE
|
||||
AC_SYS_LARGEFILE
|
||||
|
||||
AC_CHECK_MEMBER([struct tm.tm_gmtoff], [have_struct_tm_tm_gmtoff=yes],
|
||||
@@ -535,12 +559,34 @@ AC_CHECK_SIZEOF([int *])
|
||||
if test "x$cross_compiling" != "xyes"; then
|
||||
AC_FUNC_MALLOC
|
||||
fi
|
||||
|
||||
AC_FUNC_CHOWN
|
||||
AC_FUNC_ERROR_AT_LINE
|
||||
AC_FUNC_FORK
|
||||
# Don't check realloc, since LeakSanitizer detects memory leak during check
|
||||
# AC_FUNC_REALLOC
|
||||
AC_FUNC_STRERROR_R
|
||||
AC_FUNC_STRNLEN
|
||||
|
||||
AC_CHECK_FUNCS([ \
|
||||
_Exit \
|
||||
accept4 \
|
||||
dup2 \
|
||||
getcwd \
|
||||
getpwnam \
|
||||
localtime_r \
|
||||
memchr \
|
||||
memmove \
|
||||
memset \
|
||||
socket \
|
||||
sqrt \
|
||||
strchr \
|
||||
strdup \
|
||||
strerror \
|
||||
strndup \
|
||||
strstr \
|
||||
strtol \
|
||||
strtoul \
|
||||
timegm \
|
||||
])
|
||||
|
||||
@@ -650,6 +696,7 @@ AC_CONFIG_FILES([
|
||||
doc/asio_http2_client.h.rst
|
||||
doc/contribute.rst
|
||||
contrib/Makefile
|
||||
script/Makefile
|
||||
])
|
||||
AC_OUTPUT
|
||||
|
||||
@@ -688,6 +735,7 @@ AC_MSG_NOTICE([summary of build options:
|
||||
Spdylay: ${have_spdylay}
|
||||
Jansson: ${have_jansson}
|
||||
Jemalloc: ${have_jemalloc}
|
||||
Zlib: ${have_zlib}
|
||||
Boost CPPFLAGS: ${BOOST_CPPFLAGS}
|
||||
Boost LDFLAGS: ${BOOST_LDFLAGS}
|
||||
Boost::ASIO: ${BOOST_ASIO_LIB}
|
||||
@@ -700,4 +748,5 @@ AC_MSG_NOTICE([summary of build options:
|
||||
Examples: ${enable_examples}
|
||||
Python bindings:${enable_python_bindings}
|
||||
Threading: ${enable_threads}
|
||||
Third-party: ${enable_third_party}
|
||||
])
|
||||
|
||||
2
contrib/.gitignore
vendored
2
contrib/.gitignore
vendored
@@ -1 +1,3 @@
|
||||
nghttpx-init
|
||||
nghttpx.service
|
||||
nghttpx-upstart.conf
|
||||
|
||||
@@ -21,19 +21,24 @@
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
EXTRA_DIST = nghttpx-init.in nghttpx-logrotate
|
||||
configfiles = nghttpx-init nghttpx.service nghttpx-upstart.conf
|
||||
|
||||
EXTRA_DIST = $(configfiles:%=%.in) nghttpx-logrotate
|
||||
|
||||
edit = sed -e 's|@bindir[@]|$(bindir)|g'
|
||||
|
||||
nghttpx-init: Makefile
|
||||
nghttpx-init: %: $(srcdir)/%.in
|
||||
rm -f $@ $@.tmp
|
||||
$(edit) $(srcdir)/$@.in > $@.tmp
|
||||
$(edit) $< > $@.tmp
|
||||
chmod +x $@.tmp
|
||||
mv $@.tmp $@
|
||||
|
||||
nghttpx-init: $(srcdir)/nghttpx-init.in
|
||||
nghttpx.service nghttpx-upstart.conf: %: $(srcdir)/%.in
|
||||
$(edit) $< > $@
|
||||
|
||||
all-local: nghttpx-init
|
||||
$(configfiles): Makefile
|
||||
|
||||
all-local: $(configfiles)
|
||||
|
||||
clean-local:
|
||||
-rm -f nghttpx-init nghttpx-init.tmp
|
||||
-rm -f nghttpx-init.tmp $(configfiles)
|
||||
|
||||
@@ -1,18 +1,11 @@
|
||||
/var/log/nghttpx/*.log {
|
||||
weekly
|
||||
missingok
|
||||
rotate 52
|
||||
compress
|
||||
delaycompress
|
||||
notifempty
|
||||
create 0640 www-data adm
|
||||
sharedscripts
|
||||
prerotate
|
||||
if [ -d /etc/logrotate.d/httpd-prerotate ]; then \
|
||||
run-parts /etc/logrotate.d/httpd-prerotate; \
|
||||
fi \
|
||||
endscript
|
||||
postrotate
|
||||
[ -s /run/nghttpx.pid ] && kill -USR1 `cat /run/nghttpx.pid`
|
||||
endscript
|
||||
weekly
|
||||
rotate 52
|
||||
missingok
|
||||
compress
|
||||
delaycompress
|
||||
notifempty
|
||||
postrotate
|
||||
killall -USR1 nghttpx 2> /dev/null || true
|
||||
endscript
|
||||
}
|
||||
|
||||
8
contrib/nghttpx-upstart.conf.in
Normal file
8
contrib/nghttpx-upstart.conf.in
Normal file
@@ -0,0 +1,8 @@
|
||||
# vim: ft=upstart:
|
||||
|
||||
description "HTTP/2 reverse proxy"
|
||||
|
||||
start on runlevel [2]
|
||||
stop on runlevel [016]
|
||||
|
||||
exec @bindir@/nghttpx
|
||||
10
contrib/nghttpx.service.in
Normal file
10
contrib/nghttpx.service.in
Normal file
@@ -0,0 +1,10 @@
|
||||
[Unit]
|
||||
Description=HTTP/2 experimental proxy
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=@bindir@/nghttpx --errorlog-syslog
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -49,7 +49,7 @@ APIDOCS= \
|
||||
nghttp2_option_set_no_auto_window_update.rst \
|
||||
nghttp2_option_set_no_http_messaging.rst \
|
||||
nghttp2_option_set_peer_max_concurrent_streams.rst \
|
||||
nghttp2_option_set_recv_client_preface.rst \
|
||||
nghttp2_option_set_no_recv_client_magic.rst \
|
||||
nghttp2_pack_settings_payload.rst \
|
||||
nghttp2_priority_spec_check_default.rst \
|
||||
nghttp2_priority_spec_default_init.rst \
|
||||
@@ -108,7 +108,6 @@ APIDOCS= \
|
||||
nghttp2_session_want_read.rst \
|
||||
nghttp2_session_want_write.rst \
|
||||
nghttp2_strerror.rst \
|
||||
nghttp2_submit_altsvc.rst \
|
||||
nghttp2_submit_data.rst \
|
||||
nghttp2_submit_goaway.rst \
|
||||
nghttp2_submit_headers.rst \
|
||||
|
||||
2
doc/_themes/sphinx_rtd_theme/__init__.py
vendored
2
doc/_themes/sphinx_rtd_theme/__init__.py
vendored
@@ -5,7 +5,7 @@ From https://github.com/ryan-roemer/sphinx-bootstrap-theme.
|
||||
"""
|
||||
import os
|
||||
|
||||
VERSION = (0, 1, 7)
|
||||
VERSION = (0, 1, 8)
|
||||
|
||||
__version__ = ".".join(str(v) for v in VERSION)
|
||||
__version_full__ = __version__
|
||||
|
||||
18
doc/_themes/sphinx_rtd_theme/breadcrumbs.html
vendored
18
doc/_themes/sphinx_rtd_theme/breadcrumbs.html
vendored
@@ -6,14 +6,16 @@
|
||||
{% endfor %}
|
||||
<li>{{ title }}</li>
|
||||
<li class="wy-breadcrumbs-aside">
|
||||
{% if display_github %}
|
||||
<a href="https://{{ github_host|default("github.com") }}/{{ github_user }}/{{ github_repo }}/blob/{{ github_version }}{{ conf_py_path }}{{ pagename }}{{ source_suffix }}" class="fa fa-github"> Edit on GitHub</a>
|
||||
{% elif display_bitbucket %}
|
||||
<a href="https://bitbucket.org/{{ bitbucket_user }}/{{ bitbucket_repo }}/src/{{ bitbucket_version}}{{ conf_py_path }}{{ pagename }}{{ source_suffix }}" class="fa fa-bitbucket"> Edit on Bitbucket</a>
|
||||
{% elif show_source and source_url_prefix %}
|
||||
<a href="{{ source_url_prefix }}{{ pagename }}{{ source_suffix }}">View page source</a>
|
||||
{% elif show_source and has_source and sourcename %}
|
||||
<a href="{{ pathto('_sources/' + sourcename, true)|e }}" rel="nofollow"> View page source</a>
|
||||
{% if pagename != "search" %}
|
||||
{% if display_github %}
|
||||
<a href="https://{{ github_host|default("github.com") }}/{{ github_user }}/{{ github_repo }}/blob/{{ github_version }}{{ conf_py_path }}{{ pagename }}{{ source_suffix }}" class="fa fa-github"> Edit on GitHub</a>
|
||||
{% elif display_bitbucket %}
|
||||
<a href="https://bitbucket.org/{{ bitbucket_user }}/{{ bitbucket_repo }}/src/{{ bitbucket_version}}{{ conf_py_path }}{{ pagename }}{{ source_suffix }}" class="fa fa-bitbucket"> Edit on Bitbucket</a>
|
||||
{% elif show_source and source_url_prefix %}
|
||||
<a href="{{ source_url_prefix }}{{ pagename }}{{ source_suffix }}">View page source</a>
|
||||
{% elif show_source and has_source and sourcename %}
|
||||
<a href="{{ pathto('_sources/' + sourcename, true)|e }}" rel="nofollow"> View page source</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
4
doc/_themes/sphinx_rtd_theme/footer.html
vendored
4
doc/_themes/sphinx_rtd_theme/footer.html
vendored
@@ -2,10 +2,10 @@
|
||||
{% if next or prev %}
|
||||
<div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
|
||||
{% if next %}
|
||||
<a href="{{ next.link|e }}" class="btn btn-neutral float-right" title="{{ next.title|striptags|e }}">Next <span class="fa fa-arrow-circle-right"></span></a>
|
||||
<a href="{{ next.link|e }}" class="btn btn-neutral float-right" title="{{ next.title|striptags|e }}" accesskey="n">Next <span class="fa fa-arrow-circle-right"></span></a>
|
||||
{% endif %}
|
||||
{% if prev %}
|
||||
<a href="{{ prev.link|e }}" class="btn btn-neutral" title="{{ prev.title|striptags|e }}"><span class="fa fa-arrow-circle-left"></span> Previous</a>
|
||||
<a href="{{ prev.link|e }}" class="btn btn-neutral" title="{{ prev.title|striptags|e }}" accesskey="p"><span class="fa fa-arrow-circle-left"></span> Previous</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
2
doc/_themes/sphinx_rtd_theme/layout.html
vendored
2
doc/_themes/sphinx_rtd_theme/layout.html
vendored
@@ -107,7 +107,7 @@
|
||||
|
||||
<div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
|
||||
{% block menu %}
|
||||
{% set toctree = toctree(maxdepth=2, collapse=False, includehidden=True) %}
|
||||
{% set toctree = toctree(maxdepth=4, collapse=False, includehidden=True) %}
|
||||
{% if toctree %}
|
||||
{{ toctree }}
|
||||
{% else %}
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
101
doc/_themes/sphinx_rtd_theme/static/js/theme.js
vendored
101
doc/_themes/sphinx_rtd_theme/static/js/theme.js
vendored
@@ -1,50 +1,113 @@
|
||||
$( document ).ready(function() {
|
||||
function toggleCurrent (elem) {
|
||||
var parent_li = elem.closest('li');
|
||||
parent_li.siblings('li.current').removeClass('current');
|
||||
parent_li.siblings().find('li.current').removeClass('current');
|
||||
parent_li.find('> ul li.current').removeClass('current');
|
||||
parent_li.toggleClass('current');
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
// Shift nav in mobile when clicking the menu.
|
||||
$(document).on('click', "[data-toggle='wy-nav-top']", function() {
|
||||
$("[data-toggle='wy-nav-shift']").toggleClass("shift");
|
||||
$("[data-toggle='rst-versions']").toggleClass("shift");
|
||||
$("[data-toggle='wy-nav-shift']").toggleClass("shift");
|
||||
$("[data-toggle='rst-versions']").toggleClass("shift");
|
||||
});
|
||||
// Close menu when you click a link.
|
||||
// Nav menu link click operations
|
||||
$(document).on('click', ".wy-menu-vertical .current ul li a", function() {
|
||||
$("[data-toggle='wy-nav-shift']").removeClass("shift");
|
||||
$("[data-toggle='rst-versions']").toggleClass("shift");
|
||||
var target = $(this);
|
||||
// Close menu when you click a link.
|
||||
$("[data-toggle='wy-nav-shift']").removeClass("shift");
|
||||
$("[data-toggle='rst-versions']").toggleClass("shift");
|
||||
// Handle dynamic display of l3 and l4 nav lists
|
||||
toggleCurrent(target);
|
||||
if (typeof(window.SphinxRtdTheme) != 'undefined') {
|
||||
window.SphinxRtdTheme.StickyNav.hashChange();
|
||||
}
|
||||
});
|
||||
$(document).on('click', "[data-toggle='rst-current-version']", function() {
|
||||
$("[data-toggle='rst-versions']").toggleClass("shift-up");
|
||||
});
|
||||
$("[data-toggle='rst-versions']").toggleClass("shift-up");
|
||||
});
|
||||
// Make tables responsive
|
||||
$("table.docutils:not(.field-list)").wrap("<div class='wy-table-responsive'></div>");
|
||||
|
||||
// Add expand links to all parents of nested ul
|
||||
$('.wy-menu-vertical ul').siblings('a').each(function () {
|
||||
var link = $(this);
|
||||
expand = $('<span class="toctree-expand"></span>');
|
||||
expand.on('click', function (ev) {
|
||||
toggleCurrent(link);
|
||||
ev.stopPropagation();
|
||||
return false;
|
||||
});
|
||||
link.prepend(expand);
|
||||
});
|
||||
});
|
||||
|
||||
// Sphinx theme state
|
||||
window.SphinxRtdTheme = (function (jquery) {
|
||||
var stickyNav = (function () {
|
||||
var navBar,
|
||||
win,
|
||||
stickyNavCssClass = 'stickynav',
|
||||
winScroll = false,
|
||||
linkScroll = false,
|
||||
winPosition = 0,
|
||||
enable = function () {
|
||||
navBar.addClass(stickyNavCssClass);
|
||||
win.on('scroll', function() { // set flag on scroll event
|
||||
winScroll = true;
|
||||
init();
|
||||
reset();
|
||||
win.on('hashchange', reset);
|
||||
|
||||
// Set scrolling
|
||||
win.on('scroll', function () {
|
||||
if (!linkScroll) {
|
||||
winScroll = true;
|
||||
}
|
||||
});
|
||||
// use setInterval to only handle a subset of scroll events so we don't kill scroll performance
|
||||
setInterval(function() {
|
||||
setInterval(function () {
|
||||
if (winScroll) {
|
||||
winScroll = false;
|
||||
navBar.scrollTop(win.scrollTop());
|
||||
var newWinPosition = win.scrollTop(),
|
||||
navPosition = navBar.scrollTop(),
|
||||
newNavPosition = navPosition + (newWinPosition - winPosition);
|
||||
navBar.scrollTop(newNavPosition);
|
||||
winPosition = newWinPosition;
|
||||
}
|
||||
}, 100);
|
||||
}, 25);
|
||||
},
|
||||
init = function () {
|
||||
navBar = jquery('nav.wy-nav-side:first');
|
||||
win = jquery(window);
|
||||
win = jquery(window);
|
||||
},
|
||||
reset = function () {
|
||||
// Get anchor from URL and open up nested nav
|
||||
var anchor = encodeURI(window.location.hash);
|
||||
if (anchor) {
|
||||
try {
|
||||
var link = $('.wy-menu-vertical')
|
||||
.find('[href="' + anchor + '"]');
|
||||
$('.wy-menu-vertical li.toctree-l1 li.current')
|
||||
.removeClass('current');
|
||||
link.closest('li.toctree-l2').addClass('current');
|
||||
link.closest('li.toctree-l3').addClass('current');
|
||||
link.closest('li.toctree-l4').addClass('current');
|
||||
}
|
||||
catch (err) {
|
||||
console.log("Error expanding nav for anchor", err);
|
||||
}
|
||||
}
|
||||
},
|
||||
hashChange = function () {
|
||||
linkScroll = true;
|
||||
win.one('hashchange', function () {
|
||||
linkScroll = false;
|
||||
});
|
||||
};
|
||||
jquery(init);
|
||||
return {
|
||||
enable : enable
|
||||
enable: enable,
|
||||
hashChange: hashChange
|
||||
};
|
||||
}());
|
||||
return {
|
||||
StickyNav : stickyNav
|
||||
StickyNav: stickyNav
|
||||
};
|
||||
}($));
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import unicode_literals
|
||||
from __future__ import print_function
|
||||
import subprocess
|
||||
from StringIO import StringIO
|
||||
import io
|
||||
import re
|
||||
import sys
|
||||
import os.path
|
||||
@@ -14,10 +17,10 @@ class Option:
|
||||
def get_all_options(cmd):
|
||||
opt_pattern = re.compile(r' (?:(-.), )?(--[^\s\[=]+)(\[)?')
|
||||
proc = subprocess.Popen([cmd, "--help"], stdout=subprocess.PIPE)
|
||||
stdoutdata, stderrdata = proc.communicate()
|
||||
stdoutdata, _ = proc.communicate()
|
||||
cur_option = None
|
||||
opts = {}
|
||||
for line in StringIO(stdoutdata):
|
||||
for line in io.StringIO(stdoutdata.decode('utf-8')):
|
||||
match = opt_pattern.match(line)
|
||||
if not match:
|
||||
continue
|
||||
@@ -45,7 +48,7 @@ _{name}()
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W '\
|
||||
''')
|
||||
for opt in opts.itervalues():
|
||||
for opt in opts.values():
|
||||
out.write(opt.long_opt)
|
||||
out.write(' ')
|
||||
|
||||
@@ -66,8 +69,8 @@ complete -F _{name} {name}
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) < 2:
|
||||
print "Generates bash_completion using `/path/to/cmd --help'"
|
||||
print "Usage: make_bash_completion.py /path/to/cmd"
|
||||
print("Generates bash_completion using `/path/to/cmd --help'")
|
||||
print("Usage: make_bash_completion.py /path/to/cmd")
|
||||
exit(1)
|
||||
name = os.path.basename(sys.argv[1])
|
||||
opts = get_all_options(sys.argv[1])
|
||||
|
||||
@@ -8,7 +8,7 @@ _nghttpd()
|
||||
_get_comp_words_by_ref cur prev
|
||||
case $cur in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W '--error-gzip --push --header-table-size --trailer --htdocs --address --padding --verbose --version --help --hexdump --daemon --verify-client --workers --no-tls --color --early-response --dh-param-file ' -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W '--error-gzip --push --header-table-size --trailer --htdocs --address --padding --verbose --version --help --hexdump --dh-param-file --daemon --verify-client --echo-upload --workers --no-tls --color --early-response --max-concurrent-streams ' -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
_filedir
|
||||
|
||||
@@ -8,7 +8,7 @@ _nghttpx()
|
||||
_get_comp_words_by_ref cur prev
|
||||
case $cur in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W '--frontend-http2-connection-window-bits --worker-read-rate --frontend-no-tls --frontend-http2-dump-request-header --daemon --write-rate --altsvc --frontend-http2-dump-response-header --backend-http1-connections-per-frontend --tls-ticket-key-file --ciphers --verify-client-cacert --backend-keep-alive-timeout --strip-incoming-x-forwarded-for --errorlog-file --private-key-passwd-file --version --backlog --backend-http-proxy-uri --add-response-header --backend-write-timeout --backend-request-buffer --add-x-forwarded-for --write-burst --backend-http2-connection-window-bits --insecure --rlimit-nofile --backend-http2-window-bits --tls-proto-list --no-location-rewrite --padding --conf --accesslog-syslog --backend-http2-connections-per-worker --http2-max-concurrent-streams --client-proxy --worker-frontend-connections --ocsp-update-interval --cacert --frontend-read-timeout --worker-write-burst --npn-list --syslog-facility --backend-http1-connections-per-host --no-server-push --client --http2-bridge --fetch-ocsp-response-file --no-via --user --stream-write-timeout --no-ocsp --backend-response-buffer --http2-no-cookie-crumbling --backend-read-timeout --stream-read-timeout --workers --worker-read-burst --dh-param-file --errorlog-syslog --frontend --accesslog-file --http2-proxy --frontend-http2-read-timeout --accesslog-format --frontend-http2-window-bits --backend-no-tls --client-private-key-file --pid-file --client-cert-file --no-host-rewrite --log-level --worker-write-rate --help --backend-tls-sni-field --subcert --frontend-frame-debug --frontend-write-timeout --verify-client --read-rate --read-burst --backend-ipv4 --listener-disable-timeout --backend-ipv6 --backend ' -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W '--worker-read-rate --frontend-no-tls --frontend-http2-dump-response-header --backend-http1-connections-per-frontend --tls-ticket-key-file --verify-client-cacert --backend-request-buffer --backend-http2-connection-window-bits --conf --worker-write-burst --npn-list --fetch-ocsp-response-file --stream-read-timeout --accesslog-syslog --frontend-http2-read-timeout --listener-disable-timeout --frontend-http2-connection-window-bits --ciphers --strip-incoming-x-forwarded-for --daemon --backend-keep-alive-timeout --backend-http-proxy-uri --backend-http1-connections-per-host --rlimit-nofile --no-via --ocsp-update-interval --backend-write-timeout --client --http2-no-cookie-crumbling --worker-read-burst --client-proxy --http2-bridge --accesslog-format --errorlog-syslog --errorlog-file --http2-max-concurrent-streams --frontend-write-timeout --read-burst --backend-ipv4 --backend-ipv6 --backend --insecure --log-level --tls-proto-list --backend-http2-connections-per-worker --dh-param-file --worker-frontend-connections --header-field-buffer --no-server-push --no-location-rewrite --no-ocsp --backend-response-buffer --workers --frontend-http2-window-bits --no-host-rewrite --worker-write-rate --add-request-header --backend-tls-sni-field --subcert --help --frontend-frame-debug --pid-file --frontend-http2-dump-request-header --private-key-passwd-file --write-rate --altsvc --user --add-x-forwarded-for --syslog-facility --frontend-read-timeout --backlog --write-burst --backend-http2-window-bits --padding --stream-write-timeout --cacert --version --verify-client --backend-read-timeout --frontend --accesslog-file --http2-proxy --max-header-fields --backend-no-tls --client-private-key-file --client-cert-file --add-response-header --read-rate ' -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
_filedir
|
||||
|
||||
53
doc/h2load.1
53
doc/h2load.1
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "H2LOAD" "1" "April 27, 2015" "0.7.13" "nghttp2"
|
||||
.TH "H2LOAD" "1" "June 12, 2015" "1.0.2" "nghttp2"
|
||||
.SH NAME
|
||||
h2load \- HTTP/2 benchmarking tool
|
||||
.
|
||||
@@ -71,7 +71,7 @@ Default: \fB1\fP
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-i, \-\-input\-file=<FILE>
|
||||
Path of a file with multiple URIs are seperated by EOLs.
|
||||
Path of a file with multiple URIs are separated by EOLs.
|
||||
This option will disable URIs getting from command\-line.
|
||||
If \(aq\-\(aq is given as <FILE>, URIs will be read from stdin.
|
||||
URIs are used in this order for each client. All URIs
|
||||
@@ -116,9 +116,9 @@ Add/Override a header to the requests.
|
||||
.B \-p, \-\-no\-tls\-proto=<PROTOID>
|
||||
Specify ALPN identifier of the protocol to be used when
|
||||
accessing http URI without SSL/TLS.
|
||||
Available protocols: spdy/2, spdy/3, spdy/3.1 and h2c\-14
|
||||
Available protocols: spdy/2, spdy/3, spdy/3.1 and h2c
|
||||
.sp
|
||||
Default: \fBh2c\-14\fP
|
||||
Default: \fBh2c\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -205,12 +205,55 @@ The maximum time taken for request and response.
|
||||
The mean time taken for request and response.
|
||||
.TP
|
||||
.B sd
|
||||
The standard deviation of the time for request and response.
|
||||
The standard deviation of the time taken for request and response.
|
||||
.TP
|
||||
.B +/\- sd
|
||||
The fraction of the number of requests within standard deviation
|
||||
range (mean +/\- sd) against total number of successful requests.
|
||||
.UNINDENT
|
||||
.TP
|
||||
.B time for connect
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B min
|
||||
The minimum time taken to connect to a server.
|
||||
.TP
|
||||
.B max
|
||||
The maximum time taken to connect to a server.
|
||||
.TP
|
||||
.B mean
|
||||
The mean time taken to connect to a server.
|
||||
.TP
|
||||
.B sd
|
||||
The standard deviation of the time taken to connect to a server.
|
||||
.TP
|
||||
.B +/\- sd
|
||||
The fraction of the number of connections within standard
|
||||
deviation range (mean +/\- sd) against total number of successful
|
||||
connections.
|
||||
.UNINDENT
|
||||
.TP
|
||||
.B time for 1st byte (of (decrypted in case of TLS) application data)
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B min
|
||||
The minimum time taken to get 1st byte from a server.
|
||||
.TP
|
||||
.B max
|
||||
The maximum time taken to get 1st byte from a server.
|
||||
.TP
|
||||
.B mean
|
||||
The mean time taken to get 1st byte from a server.
|
||||
.TP
|
||||
.B sd
|
||||
The standard deviation of the time taken to get 1st byte from a
|
||||
server.
|
||||
.TP
|
||||
.B +/\- sd
|
||||
The fraction of the number of connections within standard
|
||||
deviation range (mean +/\- sd) against total number of successful
|
||||
connections.
|
||||
.UNINDENT
|
||||
.UNINDENT
|
||||
.SH FLOW CONTROL
|
||||
.sp
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
|
||||
.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY.
|
||||
|
||||
.. program:: h2load
|
||||
|
||||
h2load(1)
|
||||
@@ -46,7 +48,7 @@ OPTIONS
|
||||
|
||||
.. option:: -i, --input-file=<FILE>
|
||||
|
||||
Path of a file with multiple URIs are seperated by EOLs.
|
||||
Path of a file with multiple URIs are separated by EOLs.
|
||||
This option will disable URIs getting from command-line.
|
||||
If '-' is given as <FILE>, URIs will be read from stdin.
|
||||
URIs are used in this order for each client. All URIs
|
||||
@@ -86,9 +88,9 @@ OPTIONS
|
||||
|
||||
Specify ALPN identifier of the protocol to be used when
|
||||
accessing http URI without SSL/TLS.
|
||||
Available protocols: spdy/2, spdy/3, spdy/3.1 and h2c-14
|
||||
Available protocols: spdy/2, spdy/3, spdy/3.1 and h2c
|
||||
|
||||
Default: ``h2c-14``
|
||||
Default: ``h2c``
|
||||
|
||||
.. option:: -d, --data=<FILE>
|
||||
|
||||
@@ -153,11 +155,40 @@ time for request
|
||||
mean
|
||||
The mean time taken for request and response.
|
||||
sd
|
||||
The standard deviation of the time for request and response.
|
||||
The standard deviation of the time taken for request and response.
|
||||
+/- sd
|
||||
The fraction of the number of requests within standard deviation
|
||||
range (mean +/- sd) against total number of successful requests.
|
||||
|
||||
time for connect
|
||||
min
|
||||
The minimum time taken to connect to a server.
|
||||
max
|
||||
The maximum time taken to connect to a server.
|
||||
mean
|
||||
The mean time taken to connect to a server.
|
||||
sd
|
||||
The standard deviation of the time taken to connect to a server.
|
||||
+/- sd
|
||||
The fraction of the number of connections within standard
|
||||
deviation range (mean +/- sd) against total number of successful
|
||||
connections.
|
||||
|
||||
time for 1st byte (of (decrypted in case of TLS) application data)
|
||||
min
|
||||
The minimum time taken to get 1st byte from a server.
|
||||
max
|
||||
The maximum time taken to get 1st byte from a server.
|
||||
mean
|
||||
The mean time taken to get 1st byte from a server.
|
||||
sd
|
||||
The standard deviation of the time taken to get 1st byte from a
|
||||
server.
|
||||
+/- sd
|
||||
The fraction of the number of connections within standard
|
||||
deviation range (mean +/- sd) against total number of successful
|
||||
connections.
|
||||
|
||||
FLOW CONTROL
|
||||
------------
|
||||
|
||||
|
||||
@@ -44,11 +44,40 @@ time for request
|
||||
mean
|
||||
The mean time taken for request and response.
|
||||
sd
|
||||
The standard deviation of the time for request and response.
|
||||
The standard deviation of the time taken for request and response.
|
||||
+/- sd
|
||||
The fraction of the number of requests within standard deviation
|
||||
range (mean +/- sd) against total number of successful requests.
|
||||
|
||||
time for connect
|
||||
min
|
||||
The minimum time taken to connect to a server.
|
||||
max
|
||||
The maximum time taken to connect to a server.
|
||||
mean
|
||||
The mean time taken to connect to a server.
|
||||
sd
|
||||
The standard deviation of the time taken to connect to a server.
|
||||
+/- sd
|
||||
The fraction of the number of connections within standard
|
||||
deviation range (mean +/- sd) against total number of successful
|
||||
connections.
|
||||
|
||||
time for 1st byte (of (decrypted in case of TLS) application data)
|
||||
min
|
||||
The minimum time taken to get 1st byte from a server.
|
||||
max
|
||||
The maximum time taken to get 1st byte from a server.
|
||||
mean
|
||||
The mean time taken to get 1st byte from a server.
|
||||
sd
|
||||
The standard deviation of the time taken to get 1st byte from a
|
||||
server.
|
||||
+/- sd
|
||||
The fraction of the number of connections within standard
|
||||
deviation range (mean +/- sd) against total number of successful
|
||||
connections.
|
||||
|
||||
FLOW CONTROL
|
||||
------------
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# nghttp2 - HTTP/2 C Library
|
||||
|
||||
# Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
@@ -23,6 +24,8 @@
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
# Generates API reference from C source code.
|
||||
|
||||
from __future__ import unicode_literals
|
||||
from __future__ import print_function # At least python 2.6 is required
|
||||
import re, sys, argparse, os.path
|
||||
|
||||
@@ -222,6 +225,7 @@ def process_function(domain, infile):
|
||||
func_proto = ''.join(func_proto)
|
||||
func_proto = re.sub(r';\n$', '', func_proto)
|
||||
func_proto = re.sub(r'\s+', ' ', func_proto)
|
||||
func_proto = re.sub(r'NGHTTP2_EXTERN ', '', func_proto)
|
||||
return FunctionDoc(func_proto, content, domain)
|
||||
|
||||
def read_content(infile):
|
||||
|
||||
12
doc/nghttp.1
12
doc/nghttp.1
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTP" "1" "April 27, 2015" "0.7.13" "nghttp2"
|
||||
.TH "NGHTTP" "1" "June 12, 2015" "1.0.2" "nghttp2"
|
||||
.SH NAME
|
||||
nghttp \- HTTP/2 experimental client
|
||||
.
|
||||
@@ -64,8 +64,9 @@ yet.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-t, \-\-timeout=<SEC>
|
||||
Timeout each request after <SEC> seconds.
|
||||
.B \-t, \-\-timeout=<DURATION>
|
||||
Timeout each request after <DURATION>. Set 0 to disable
|
||||
timeout.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -215,6 +216,11 @@ Display this help and exit.
|
||||
.sp
|
||||
The <SIZE> argument is an integer and an optional unit (e.g., 10K is
|
||||
10 * 1024). Units are K, M and G (powers of 1024).
|
||||
.sp
|
||||
The <DURATION> argument is an integer and an optional unit (e.g., 1s
|
||||
is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms
|
||||
(hours, minutes, seconds and milliseconds, respectively). If a unit
|
||||
is omitted, a second is used as unit.
|
||||
.SH DEPENDENCY BASED PRIORITY
|
||||
.sp
|
||||
nghttp sends priority hints to server by default unless
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
|
||||
.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY.
|
||||
|
||||
.. program:: nghttp
|
||||
|
||||
nghttp(1)
|
||||
@@ -38,9 +40,10 @@ OPTIONS
|
||||
'index.html' is used as a filename. Not implemented
|
||||
yet.
|
||||
|
||||
.. option:: -t, --timeout=<SEC>
|
||||
.. option:: -t, --timeout=<DURATION>
|
||||
|
||||
Timeout each request after <SEC> seconds.
|
||||
Timeout each request after <DURATION>. Set 0 to disable
|
||||
timeout.
|
||||
|
||||
.. option:: -w, --window-bits=<N>
|
||||
|
||||
@@ -168,6 +171,11 @@ OPTIONS
|
||||
The <SIZE> argument is an integer and an optional unit (e.g., 10K is
|
||||
10 * 1024). Units are K, M and G (powers of 1024).
|
||||
|
||||
The <DURATION> argument is an integer and an optional unit (e.g., 1s
|
||||
is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms
|
||||
(hours, minutes, seconds and milliseconds, respectively). If a unit
|
||||
is omitted, a second is used as unit.
|
||||
|
||||
DEPENDENCY BASED PRIORITY
|
||||
-------------------------
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTPD" "1" "April 27, 2015" "0.7.13" "nghttp2"
|
||||
.TH "NGHTTPD" "1" "June 12, 2015" "1.0.2" "nghttp2"
|
||||
.SH NAME
|
||||
nghttpd \- HTTP/2 experimental server
|
||||
.
|
||||
@@ -119,6 +119,14 @@ Specify 0 to disable padding.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-m, \-\-max\-concurrent\-streams=<N>
|
||||
Set the maximum number of the concurrent streams in one
|
||||
HTTP/2 session.
|
||||
.sp
|
||||
Default: \fB100\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-n, \-\-workers=<N>
|
||||
Set the number of worker threads.
|
||||
.sp
|
||||
@@ -159,6 +167,11 @@ are used.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-echo\-upload
|
||||
Send back uploaded content if method is POST or PUT.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-version
|
||||
Display version information and exit.
|
||||
.UNINDENT
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
|
||||
.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY.
|
||||
|
||||
.. program:: nghttpd
|
||||
|
||||
nghttpd(1)
|
||||
@@ -85,6 +87,13 @@ OPTIONS
|
||||
Add at most <N> bytes to a frame payload as padding.
|
||||
Specify 0 to disable padding.
|
||||
|
||||
.. option:: -m, --max-concurrent-streams=<N>
|
||||
|
||||
Set the maximum number of the concurrent streams in one
|
||||
HTTP/2 session.
|
||||
|
||||
Default: ``100``
|
||||
|
||||
.. option:: -n, --workers=<N>
|
||||
|
||||
Set the number of worker threads.
|
||||
@@ -119,6 +128,10 @@ OPTIONS
|
||||
hex+ASCII display). If SSL/TLS is used, decrypted data
|
||||
are used.
|
||||
|
||||
.. option:: --echo-upload
|
||||
|
||||
Send back uploaded content if method is POST or PUT.
|
||||
|
||||
.. option:: --version
|
||||
|
||||
Display version information and exit.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTPX" "1" "April 27, 2015" "0.7.13" "nghttp2"
|
||||
.TH "NGHTTPX" "1" "June 12, 2015" "1.0.2" "nghttp2"
|
||||
.SH NAME
|
||||
nghttpx \- HTTP/2 experimental proxy
|
||||
.
|
||||
@@ -690,10 +690,19 @@ will not be altered regardless of this option.
|
||||
.B \-\-altsvc=<PROTOID,PORT[,HOST,[ORIGIN]]>
|
||||
Specify protocol ID, port, host and origin of
|
||||
alternative service. <HOST> and <ORIGIN> are optional.
|
||||
They are advertised in alt\-svc header field or HTTP/2
|
||||
ALTSVC frame. This option can be used multiple times to
|
||||
specify multiple alternative services. Example:
|
||||
\fI\%\-\-altsvc\fP=h2,443
|
||||
They are advertised in alt\-svc header field only in
|
||||
HTTP/1.1 frontend. This option can be used multiple
|
||||
times to specify multiple alternative services.
|
||||
Example: \fI\%\-\-altsvc\fP=h2,443
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-add\-request\-header=<HEADER>
|
||||
Specify additional header field to add to request header
|
||||
set. This option just appends header field and won\(aqt
|
||||
replace anything already set. This option can be used
|
||||
several times to specify multiple header fields.
|
||||
Example: \fI\%\-\-add\-request\-header\fP="foo: bar"
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -704,6 +713,23 @@ won\(aqt replace anything already set. This option can be
|
||||
used several times to specify multiple header fields.
|
||||
Example: \fI\%\-\-add\-response\-header\fP="foo: bar"
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-header\-field\-buffer=<SIZE>
|
||||
Set maximum buffer size for incoming HTTP header field
|
||||
list. This is the sum of header name and value in
|
||||
bytes.
|
||||
.sp
|
||||
Default: \fB64K\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-max\-header\-fields=<N>
|
||||
Set maximum number of incoming HTTP header fields, which
|
||||
appear in one request or response header field list.
|
||||
.sp
|
||||
Default: \fB100\fP
|
||||
.UNINDENT
|
||||
.SS Debug
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -795,7 +821,7 @@ argument in the configuration file. Specify \fByes\fP as an argument
|
||||
ignored.
|
||||
.sp
|
||||
To specify private key and certificate file which are given as
|
||||
positional arguments in commnad\-line, use \fBprivate\-key\-file\fP and
|
||||
positional arguments in command\-line, use \fBprivate\-key\-file\fP and
|
||||
\fBcertificate\-file\fP\&.
|
||||
.sp
|
||||
\fI\%\-\-conf\fP option cannot be used in the configuration file and
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
|
||||
.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY.
|
||||
|
||||
.. program:: nghttpx
|
||||
|
||||
nghttpx(1)
|
||||
@@ -606,10 +608,18 @@ HTTP
|
||||
|
||||
Specify protocol ID, port, host and origin of
|
||||
alternative service. <HOST> and <ORIGIN> are optional.
|
||||
They are advertised in alt-svc header field or HTTP/2
|
||||
ALTSVC frame. This option can be used multiple times to
|
||||
specify multiple alternative services. Example:
|
||||
:option:`--altsvc`\=h2,443
|
||||
They are advertised in alt-svc header field only in
|
||||
HTTP/1.1 frontend. This option can be used multiple
|
||||
times to specify multiple alternative services.
|
||||
Example: :option:`--altsvc`\=h2,443
|
||||
|
||||
.. option:: --add-request-header=<HEADER>
|
||||
|
||||
Specify additional header field to add to request header
|
||||
set. This option just appends header field and won't
|
||||
replace anything already set. This option can be used
|
||||
several times to specify multiple header fields.
|
||||
Example: :option:`--add-request-header`\="foo: bar"
|
||||
|
||||
.. option:: --add-response-header=<HEADER>
|
||||
|
||||
@@ -619,6 +629,21 @@ HTTP
|
||||
used several times to specify multiple header fields.
|
||||
Example: :option:`--add-response-header`\="foo: bar"
|
||||
|
||||
.. option:: --header-field-buffer=<SIZE>
|
||||
|
||||
Set maximum buffer size for incoming HTTP header field
|
||||
list. This is the sum of header name and value in
|
||||
bytes.
|
||||
|
||||
Default: ``64K``
|
||||
|
||||
.. option:: --max-header-fields=<N>
|
||||
|
||||
Set maximum number of incoming HTTP header fields, which
|
||||
appear in one request or response header field list.
|
||||
|
||||
Default: ``100``
|
||||
|
||||
|
||||
Debug
|
||||
~~~~~
|
||||
@@ -712,7 +737,7 @@ FILES
|
||||
ignored.
|
||||
|
||||
To specify private key and certificate file which are given as
|
||||
positional arguments in commnad-line, use ``private-key-file`` and
|
||||
positional arguments in command-line, use ``private-key-file`` and
|
||||
``certificate-file``.
|
||||
|
||||
:option:`--conf` option cannot be used in the configuration file and
|
||||
|
||||
@@ -19,7 +19,7 @@ FILES
|
||||
ignored.
|
||||
|
||||
To specify private key and certificate file which are given as
|
||||
positional arguments in commnad-line, use ``private-key-file`` and
|
||||
positional arguments in command-line, use ``private-key-file`` and
|
||||
``certificate-file``.
|
||||
|
||||
:option:`--conf` option cannot be used in the configuration file and
|
||||
|
||||
@@ -20,17 +20,15 @@ nghttp2 callback functions directly or indirectly. It will lead to the
|
||||
crash. You can submit requests or frames in the callbacks then call
|
||||
these functions outside the callbacks.
|
||||
|
||||
Currently, `nghttp2_session_send()` and `nghttp2_session_mem_send()`
|
||||
do not send client connection preface
|
||||
(:macro:`NGHTTP2_CLIENT_CONNECTION_PREFACE`). The applications are
|
||||
responsible to send it before sending any HTTP/2 frames using these
|
||||
functions if :type:`nghttp2_session` is configured as client.
|
||||
Similarly, `nghttp2_session_recv()` and `nghttp2_session_mem_recv()`
|
||||
do not consume client connection preface unless
|
||||
`nghttp2_option_set_recv_client_preface()` is used with nonzero option
|
||||
value. The applications are responsible to receive it before calling
|
||||
these functions if :type:`nghttp2_session` is configured as server and
|
||||
`nghttp2_option_set_recv_client_preface()` is not used.
|
||||
`nghttp2_session_send()` and `nghttp2_session_mem_send()` send first
|
||||
24 bytes of client magic string (MAGIC)
|
||||
(:macro:`NGHTTP2_CLIENT_MAGIC`) on client configuration. The
|
||||
applications are responsible to send SETTINGS frame as part of
|
||||
connection preface using `nghttp2_submit_settings()`. Similarly,
|
||||
`nghttp2_session_recv()` and `nghttp2_session_mem_recv()` consume
|
||||
MAGIC on server configuration unless
|
||||
`nghttp2_option_set_no_recv_client_magic()` is used with nonzero
|
||||
option value.
|
||||
|
||||
.. _http-messaging:
|
||||
|
||||
|
||||
@@ -36,8 +36,9 @@ with the toolchain and installed under ``$ANDROID_HOME/usr/local``.
|
||||
We recommend to build these libraries as static library to make the
|
||||
deployment easier. libxml2 support is currently disabled.
|
||||
|
||||
We use zlib which comes with Android NDK, so we don't have to build it
|
||||
by ourselves.
|
||||
Although zlib comes with Android NDK, it seems not to be a part of
|
||||
public API, so we have to built it for our own. That also provides us
|
||||
proper .pc file as a bonus.
|
||||
|
||||
If SPDY support is required for nghttpx and h2load, build and install
|
||||
spdylay as well.
|
||||
@@ -95,10 +96,41 @@ patch, to configure libev, use the following script:
|
||||
|
||||
And run ``make install`` to build and install.
|
||||
|
||||
To configure zlib, use the following script:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
#!/bin/sh -e
|
||||
|
||||
if [ -z "$ANDROID_HOME" ]; then
|
||||
echo 'No $ANDROID_HOME specified.'
|
||||
exit 1
|
||||
fi
|
||||
PREFIX=$ANDROID_HOME/usr/local
|
||||
TOOLCHAIN=$ANDROID_HOME/toolchain
|
||||
PATH=$TOOLCHAIN/bin:$PATH
|
||||
|
||||
HOST=arm-linux-androideabi
|
||||
|
||||
CC=$HOST-gcc \
|
||||
AR=$HOST-ar \
|
||||
LD=$HOST-ld \
|
||||
RANLIB=$HOST-ranlib \
|
||||
STRIP=$HOST-strip \
|
||||
./configure \
|
||||
--prefix=$PREFIX \
|
||||
--libdir=$PREFIX/lib \
|
||||
--includedir=$PREFIX/include \
|
||||
--static
|
||||
|
||||
And run ``make install`` to build and install.
|
||||
|
||||
To configure spdylay, use the following script:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
#!/bin/sh -e
|
||||
|
||||
if [ -z "$ANDROID_HOME" ]; then
|
||||
echo 'No $ANDROID_HOME specified.'
|
||||
exit 1
|
||||
@@ -119,11 +151,7 @@ To configure spdylay, use the following script:
|
||||
PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \
|
||||
LDFLAGS="-L$PREFIX/lib"
|
||||
|
||||
And run ``make install`` to build and install. After spdylay
|
||||
installation, edit $ANDROID_HOME/usr/local/lib/pkgconfig/libspdylay.pc
|
||||
and remove the following line::
|
||||
|
||||
Requires.private: zlib
|
||||
And run ``make install`` to build and install.
|
||||
|
||||
After prerequisite libraries are prepared, run ``android-config`` and
|
||||
then ``android-make`` to compile nghttp2 source files.
|
||||
|
||||
@@ -87,5 +87,5 @@ If multiple URIs are specified, they are used in round robin manner.
|
||||
|
||||
.. note::
|
||||
|
||||
Please note that h2load uses sheme, host and port in the first URI
|
||||
Please note that h2load uses scheme, host and port in the first URI
|
||||
and ignores those parts in the rest of the URIs.
|
||||
|
||||
@@ -49,5 +49,5 @@ https://github.com/tatsuhiro-t/nghttp2/releases
|
||||
Resources
|
||||
---------
|
||||
|
||||
* http://tools.ietf.org/html/draft-ietf-httpbis-http2-14
|
||||
* http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-09
|
||||
* HTTP/2 https://tools.ietf.org/html/rfc7540
|
||||
* HPACK https://tools.ietf.org/html/rfc7541
|
||||
|
||||
@@ -80,7 +80,7 @@ status code, in the above example, which is 200. The second argument,
|
||||
which is omitted in the above example, is additional header fields to
|
||||
send.
|
||||
|
||||
``nghttp2::asio_http2::server::response::end`` sends responde body.
|
||||
``nghttp2::asio_http2::server::response::end`` sends response body.
|
||||
In the above example, we send string "hello, world".
|
||||
|
||||
The life time of req and res object ends after the callback set by
|
||||
@@ -277,7 +277,7 @@ response header fields and response body to the console screen:
|
||||
``boost::asio::io_service`` object and remote server address. When
|
||||
connection is made, the callback function passed to
|
||||
``nghttp2::asio_http2::client::on_connect`` is invoked with connected
|
||||
address as its paramter. After this callback call, use
|
||||
address as its parameter. After this callback call, use
|
||||
``nghttp2::asio_http2::session::submit`` to send request to the
|
||||
server. You can submit multiple requests at once without waiting for
|
||||
the completion of previous request.
|
||||
|
||||
@@ -21,7 +21,7 @@ SSL/TLS, the frontend also supports SPDY protocol.
|
||||
By default, this mode's frontend connection is encrypted using
|
||||
SSL/TLS. So server's private key and certificate must be supplied to
|
||||
the command line (or through configuration file). In this case, the
|
||||
fontend protocol selection will is done via ALPN or NPN.
|
||||
frontend protocol selection will is done via ALPN or NPN.
|
||||
|
||||
With ``--frontend-no-tls`` option, user can turn off SSL/TLS in
|
||||
frontend connection. In this case, SPDY protocol is not available
|
||||
@@ -243,7 +243,7 @@ Read/write rate limit
|
||||
---------------------
|
||||
|
||||
nghttpx supports transfer rate limiting on frontend connections. You
|
||||
can do rate limit per frontend connection for reading and writeing
|
||||
can do rate limit per frontend connection for reading and writing
|
||||
individually.
|
||||
|
||||
To perform rate limit for reading, use ``--read-rate`` and
|
||||
|
||||
@@ -267,7 +267,7 @@ HTTP/2 servers
|
||||
(byte string could be ``None``), :py:data:`DATA_EOF` must be
|
||||
returned as flag. If there is no data available right now, but
|
||||
additional data are anticipated, return tuple (``None``,
|
||||
:py:data:`DATA_DEFERRD`). When data arrived, call
|
||||
:py:data:`DATA_DEFERRED`). When data arrived, call
|
||||
:py:meth:`resume()` and restart response body transmission.
|
||||
|
||||
Only the body generator can pause response body generation;
|
||||
|
||||
@@ -65,11 +65,11 @@ its stream specific data in ``http2_stream_data`` structure and the
|
||||
defined as follows::
|
||||
|
||||
typedef struct {
|
||||
/* The NULL-terminated URI string to retreive. */
|
||||
/* The NULL-terminated URI string to retrieve. */
|
||||
const char *uri;
|
||||
/* Parsed result of the |uri| */
|
||||
struct http_parser_url *u;
|
||||
/* The authroity portion of the |uri|, not NULL-terminated */
|
||||
/* The authority portion of the |uri|, not NULL-terminated */
|
||||
char *authority;
|
||||
/* The path portion of the |uri|, including query, not
|
||||
NULL-terminated */
|
||||
@@ -184,9 +184,9 @@ its bufferevent, so it closes underlying connection as well. It also
|
||||
calls `nghttp2_session_del()` to delete nghttp2 session object.
|
||||
|
||||
We begin HTTP/2 communication by sending client connection preface,
|
||||
which is 24 bytes magic byte sequence
|
||||
(:macro:`NGHTTP2_CLIENT_CONNECTION_PREFACE`) and SETTINGS frame. The
|
||||
transmission of client connection header is done in
|
||||
which is 24 bytes magic byte string (:macro:`NGHTTP2_CLIENT_MAGIC`)
|
||||
followed by SETTINGS frame. First 24 bytes magic string is
|
||||
automatically sent by nghttp2 library. We send SETTINGS frame in
|
||||
``send_client_connection_header()``::
|
||||
|
||||
static void send_client_connection_header(http2_session_data *session_data) {
|
||||
@@ -194,8 +194,7 @@ transmission of client connection header is done in
|
||||
{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
|
||||
int rv;
|
||||
|
||||
bufferevent_write(session_data->bev, NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
|
||||
/* client 24 bytes magic string will be sent by nghttp2 library */
|
||||
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
|
||||
ARRLEN(iv));
|
||||
if (rv != 0) {
|
||||
@@ -204,7 +203,7 @@ transmission of client connection header is done in
|
||||
}
|
||||
|
||||
Here we specify SETTINGS_MAX_CONCURRENT_STREAMS to 100, which is
|
||||
really not needed for this tiny example progoram, but we are
|
||||
really not needed for this tiny example program, but we are
|
||||
demonstrating the use of SETTINGS frame. To queue the SETTINGS frame
|
||||
for the transmission, we use `nghttp2_submit_settings()`. Note that
|
||||
`nghttp2_submit_settings()` function only queues the frame and not
|
||||
@@ -388,7 +387,7 @@ After all name/value pairs are emitted for a frame,
|
||||
}
|
||||
|
||||
In this tutorial, we are just interested in the HTTP response
|
||||
HEADERS. We check te frame type and its category (it should be
|
||||
HEADERS. We check the frame type and its category (it should be
|
||||
:macro:`NGHTTP2_HCAT_RESPONSE` for HTTP response HEADERS). Also check
|
||||
its stream ID.
|
||||
|
||||
@@ -407,7 +406,7 @@ of data is received from the remote peer::
|
||||
}
|
||||
|
||||
In our case, a chunk of data is response body. After checking stream
|
||||
ID, we just write the recieved data to the stdout. Note that the
|
||||
ID, we just write the received data to the stdout. Note that the
|
||||
output in the terminal may be corrupted if the response body contains
|
||||
some binary data.
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ bound of encoded result, use `nghttp2_hd_deflate_bound()` function::
|
||||
size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater,
|
||||
const nghttp2_nv *nva, size_t nvlen);
|
||||
|
||||
Pass this function with the same paramters *deflater*, *nva* and
|
||||
Pass this function with the same parameters *deflater*, *nva* and
|
||||
*nvlen* which will be passed to `nghttp2_hd_deflate_hd()`.
|
||||
|
||||
The subsequent call of `nghttp2_hd_deflate_hd()` will use current
|
||||
|
||||
@@ -194,15 +194,8 @@ We initialize a nghttp2 session object which is done in
|
||||
``initialize_nghttp2_session()``::
|
||||
|
||||
static void initialize_nghttp2_session(http2_session_data *session_data) {
|
||||
nghttp2_option *option;
|
||||
nghttp2_session_callbacks *callbacks;
|
||||
|
||||
nghttp2_option_new(&option);
|
||||
|
||||
/* Tells nghttp2_session object that it handles client connection
|
||||
preface */
|
||||
nghttp2_option_set_recv_client_preface(option, 1);
|
||||
|
||||
nghttp2_session_callbacks_new(&callbacks);
|
||||
|
||||
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
||||
@@ -219,20 +212,15 @@ We initialize a nghttp2 session object which is done in
|
||||
nghttp2_session_callbacks_set_on_begin_headers_callback(
|
||||
callbacks, on_begin_headers_callback);
|
||||
|
||||
nghttp2_session_server_new2(&session_data->session, callbacks, session_data,
|
||||
option);
|
||||
nghttp2_session_server_new(&session_data->session, callbacks, session_data);
|
||||
|
||||
nghttp2_session_callbacks_del(callbacks);
|
||||
nghttp2_option_del(option);
|
||||
}
|
||||
|
||||
Since we are creating a server and uses options, the nghttp2 session
|
||||
object is created using `nghttp2_session_server_new2()` function. We
|
||||
registers five callbacks for nghttp2 session object. We'll talk about
|
||||
these callbacks later. Our server only speaks HTTP/2. In this case,
|
||||
we use `nghttp2_option_set_recv_client_preface()` to make
|
||||
:type:`nghttp2_session` object handle client connection preface, which
|
||||
saves some lines of application code.
|
||||
these callbacks later.
|
||||
|
||||
After initialization of the nghttp2 session object, we are going to send
|
||||
a server connection header in ``send_server_connection_header()``::
|
||||
|
||||
@@ -35,8 +35,12 @@
|
||||
//
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif // HAVE_UNISTD_H
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif // HAVE_FCNTL_H
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
|
||||
@@ -28,16 +28,26 @@
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /* !HAVE_CONFIG_H */
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif /* HAVE_UNISTD_H */
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif /* HAVE_FCNTL_H */
|
||||
#include <sys/types.h>
|
||||
#ifdef HAVE_SYS_SOCKET_H
|
||||
#include <sys/socket.h>
|
||||
#endif /* HAVE_SYS_SOCKET_H */
|
||||
#ifdef HAVE_NETDB_H
|
||||
#include <netdb.h>
|
||||
#endif /* HAVE_NETDB_H */
|
||||
#ifdef HAVE_NETINET_IN_H
|
||||
#include <netinet/in.h>
|
||||
#endif /* HAVE_NETINET_IN_H */
|
||||
#include <netinet/tcp.h>
|
||||
#include <poll.h>
|
||||
#include <signal.h>
|
||||
@@ -528,14 +538,6 @@ static void fetch_uri(const struct URI *uri) {
|
||||
connection.ssl = ssl;
|
||||
connection.want_io = IO_NONE;
|
||||
|
||||
/* Send connection header in blocking I/O mode */
|
||||
rv = SSL_write(ssl, NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
|
||||
if (rv <= 0) {
|
||||
dief("SSL_write failed: could not send connection preface",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
|
||||
/* Here make file descriptor non-block */
|
||||
make_non_block(fd);
|
||||
set_tcp_nodelay(fd);
|
||||
@@ -687,10 +689,10 @@ int main(int argc, char **argv) {
|
||||
act.sa_handler = SIG_IGN;
|
||||
sigaction(SIGPIPE, &act, 0);
|
||||
|
||||
OPENSSL_config(NULL);
|
||||
OpenSSL_add_all_algorithms();
|
||||
SSL_load_error_strings();
|
||||
SSL_library_init();
|
||||
OpenSSL_add_all_algorithms();
|
||||
OPENSSL_config(NULL);
|
||||
|
||||
rv = parse_uri(&uri, argv[1]);
|
||||
if (rv != 0) {
|
||||
|
||||
@@ -24,12 +24,18 @@
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /* !HAVE_CONFIG_H */
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <sys/types.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif /* HAVE_UNISTD_H */
|
||||
#ifdef HAVE_SYS_SOCKET_H
|
||||
#include <sys/socket.h>
|
||||
#endif /* HAVE_SYS_SOCKET_H */
|
||||
#ifdef HAVE_NETINET_IN_H
|
||||
#include <netinet/in.h>
|
||||
#endif /* HAVE_NETINET_IN_H */
|
||||
#include <netinet/tcp.h>
|
||||
#include <err.h>
|
||||
#include <signal.h>
|
||||
@@ -50,11 +56,11 @@
|
||||
#define ARRLEN(x) (sizeof(x) / sizeof(x[0]))
|
||||
|
||||
typedef struct {
|
||||
/* The NULL-terminated URI string to retreive. */
|
||||
/* The NULL-terminated URI string to retrieve. */
|
||||
const char *uri;
|
||||
/* Parsed result of the |uri| */
|
||||
struct http_parser_url *u;
|
||||
/* The authroity portion of the |uri|, not NULL-terminated */
|
||||
/* The authority portion of the |uri|, not NULL-terminated */
|
||||
char *authority;
|
||||
/* The path portion of the |uri|, including query, not
|
||||
NULL-terminated */
|
||||
@@ -94,7 +100,8 @@ static http2_stream_data *create_http2_stream_data(const char *uri,
|
||||
":%u", u->port);
|
||||
}
|
||||
|
||||
stream_data->pathlen = 0;
|
||||
/* If we don't have path in URI, we use "/" as path. */
|
||||
stream_data->pathlen = 1;
|
||||
if (u->field_set & (1 << UF_PATH)) {
|
||||
stream_data->pathlen = u->field_data[UF_PATH].len;
|
||||
}
|
||||
@@ -102,19 +109,22 @@ static http2_stream_data *create_http2_stream_data(const char *uri,
|
||||
/* +1 for '?' character */
|
||||
stream_data->pathlen += u->field_data[UF_QUERY].len + 1;
|
||||
}
|
||||
if (stream_data->pathlen > 0) {
|
||||
stream_data->path = malloc(stream_data->pathlen);
|
||||
if (u->field_set & (1 << UF_PATH)) {
|
||||
memcpy(stream_data->path, &uri[u->field_data[UF_PATH].off],
|
||||
u->field_data[UF_PATH].len);
|
||||
}
|
||||
if (u->field_set & (1 << UF_QUERY)) {
|
||||
memcpy(stream_data->path + u->field_data[UF_PATH].len + 1,
|
||||
&uri[u->field_data[UF_QUERY].off], u->field_data[UF_QUERY].len);
|
||||
}
|
||||
|
||||
stream_data->path = malloc(stream_data->pathlen);
|
||||
if (u->field_set & (1 << UF_PATH)) {
|
||||
memcpy(stream_data->path, &uri[u->field_data[UF_PATH].off],
|
||||
u->field_data[UF_PATH].len);
|
||||
} else {
|
||||
stream_data->path = NULL;
|
||||
stream_data->path[0] = '/';
|
||||
}
|
||||
if (u->field_set & (1 << UF_QUERY)) {
|
||||
stream_data->path[stream_data->pathlen - u->field_data[UF_QUERY].len - 1] =
|
||||
'?';
|
||||
memcpy(stream_data->path + stream_data->pathlen -
|
||||
u->field_data[UF_QUERY].len,
|
||||
&uri[u->field_data[UF_QUERY].off], u->field_data[UF_QUERY].len);
|
||||
}
|
||||
|
||||
return stream_data;
|
||||
}
|
||||
|
||||
@@ -344,8 +354,7 @@ static void send_client_connection_header(http2_session_data *session_data) {
|
||||
{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
|
||||
int rv;
|
||||
|
||||
bufferevent_write(session_data->bev, NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
|
||||
/* client 24 bytes magic string will be sent by nghttp2 library */
|
||||
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
|
||||
ARRLEN(iv));
|
||||
if (rv != 0) {
|
||||
@@ -546,10 +555,10 @@ int main(int argc, char **argv) {
|
||||
act.sa_handler = SIG_IGN;
|
||||
sigaction(SIGPIPE, &act, NULL);
|
||||
|
||||
OPENSSL_config(NULL);
|
||||
OpenSSL_add_all_algorithms();
|
||||
SSL_load_error_strings();
|
||||
SSL_library_init();
|
||||
OpenSSL_add_all_algorithms();
|
||||
OPENSSL_config(NULL);
|
||||
|
||||
run(argv[1]);
|
||||
return 0;
|
||||
|
||||
@@ -24,17 +24,27 @@
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /* !HAVE_CONFIG_H */
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <sys/types.h>
|
||||
#ifdef HAVE_SYS_SOCKET_H
|
||||
#include <sys/socket.h>
|
||||
#endif /* HAVE_SYS_SOCKET_H */
|
||||
#ifdef HAVE_NETDB_H
|
||||
#include <netdb.h>
|
||||
#endif /* HAVE_NETDB_H */
|
||||
#include <signal.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif /* HAVE_UNISTD_H */
|
||||
#include <sys/stat.h>
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif /* HAVE_FCNTL_H */
|
||||
#include <ctype.h>
|
||||
#ifdef HAVE_NETINET_IN_H
|
||||
#include <netinet/in.h>
|
||||
#endif /* HAVE_NETINET_IN_H */
|
||||
#include <netinet/tcp.h>
|
||||
#include <err.h>
|
||||
|
||||
@@ -537,15 +547,8 @@ static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||
}
|
||||
|
||||
static void initialize_nghttp2_session(http2_session_data *session_data) {
|
||||
nghttp2_option *option;
|
||||
nghttp2_session_callbacks *callbacks;
|
||||
|
||||
nghttp2_option_new(&option);
|
||||
|
||||
/* Tells nghttp2_session object that it handles client connection
|
||||
preface */
|
||||
nghttp2_option_set_recv_client_preface(option, 1);
|
||||
|
||||
nghttp2_session_callbacks_new(&callbacks);
|
||||
|
||||
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
||||
@@ -562,11 +565,9 @@ static void initialize_nghttp2_session(http2_session_data *session_data) {
|
||||
nghttp2_session_callbacks_set_on_begin_headers_callback(
|
||||
callbacks, on_begin_headers_callback);
|
||||
|
||||
nghttp2_session_server_new2(&session_data->session, callbacks, session_data,
|
||||
option);
|
||||
nghttp2_session_server_new(&session_data->session, callbacks, session_data);
|
||||
|
||||
nghttp2_session_callbacks_del(callbacks);
|
||||
nghttp2_option_del(option);
|
||||
}
|
||||
|
||||
/* Send HTTP/2 client connection header, which includes 24 bytes
|
||||
@@ -723,10 +724,10 @@ int main(int argc, char **argv) {
|
||||
act.sa_handler = SIG_IGN;
|
||||
sigaction(SIGPIPE, &act, NULL);
|
||||
|
||||
OPENSSL_config(NULL);
|
||||
OpenSSL_add_all_algorithms();
|
||||
SSL_load_error_strings();
|
||||
SSL_library_init();
|
||||
OpenSSL_add_all_algorithms();
|
||||
OPENSSL_config(NULL);
|
||||
|
||||
run(argv[1], argv[2], argv[3]);
|
||||
return 0;
|
||||
|
||||
@@ -30,18 +30,30 @@
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /* !HAVE_CONFIG_H */
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <sys/types.h>
|
||||
#ifdef HAVE_SYS_SOCKET_H
|
||||
#include <sys/socket.h>
|
||||
#endif /* HAVE_SYS_SOCKET_H */
|
||||
#include <sys/stat.h>
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif /* HAVE_FCNTL_H */
|
||||
#ifdef HAVE_NETDB_H
|
||||
#include <netdb.h>
|
||||
#endif /* HAVE_NETDB_H */
|
||||
#ifdef HAVE_NETINET_IN_H
|
||||
#include <netinet/in.h>
|
||||
#endif /* HAVE_NETINET_IN_H */
|
||||
#include <netinet/tcp.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif /* HAVE_UNISTD_H */
|
||||
#include <stdlib.h>
|
||||
#ifdef HAVE_TIME_H
|
||||
#include <time.h>
|
||||
#endif /* HAVE_TIME_H */
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
@@ -153,7 +165,6 @@ const char *docroot;
|
||||
size_t docrootlen;
|
||||
|
||||
nghttp2_session_callbacks *shared_callbacks;
|
||||
nghttp2_option *shared_option;
|
||||
|
||||
static int handle_accept(io_loop *loop, uint32_t events, void *ptr);
|
||||
static int handle_connection(io_loop *loop, uint32_t events, void *ptr);
|
||||
@@ -190,7 +201,7 @@ typedef enum {
|
||||
NGHTTP2_TOKEN__METHOD,
|
||||
NGHTTP2_TOKEN__PATH,
|
||||
NGHTTP2_TOKEN__SCHEME,
|
||||
NGHTTP2_TOKEN_HOST,
|
||||
NGHTTP2_TOKEN_HOST
|
||||
} nghttp2_token;
|
||||
|
||||
/* Inspired by h2o header lookup. https://github.com/h2o/h2o */
|
||||
@@ -388,8 +399,7 @@ static connection *connection_new(int fd) {
|
||||
|
||||
conn = malloc(sizeof(connection));
|
||||
|
||||
rv = nghttp2_session_server_new2(&conn->session, shared_callbacks, conn,
|
||||
shared_option);
|
||||
rv = nghttp2_session_server_new(&conn->session, shared_callbacks, conn);
|
||||
|
||||
if (rv != 0) {
|
||||
goto cleanup;
|
||||
@@ -1310,14 +1320,6 @@ int main(int argc, char **argv) {
|
||||
nghttp2_session_callbacks_set_send_data_callback(shared_callbacks,
|
||||
send_data_callback);
|
||||
|
||||
rv = nghttp2_option_new(&shared_option);
|
||||
if (rv != 0) {
|
||||
fprintf(stderr, "nghttp2_option_new: %s", nghttp2_strerror(rv));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
nghttp2_option_set_recv_client_preface(shared_option, 1);
|
||||
|
||||
rv = io_loop_add(&loop, serv.fd, EPOLLIN, &serv);
|
||||
|
||||
if (rv != 0) {
|
||||
@@ -1334,7 +1336,6 @@ int main(int argc, char **argv) {
|
||||
|
||||
io_loop_run(&loop, &serv);
|
||||
|
||||
nghttp2_option_del(shared_option);
|
||||
nghttp2_session_callbacks_del(shared_callbacks);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from gentokenlookup import gentokenlookup
|
||||
|
||||
HEADERS = [
|
||||
':authority',
|
||||
':method',
|
||||
@@ -34,70 +36,5 @@ HEADERS = [
|
||||
'upgrade'
|
||||
]
|
||||
|
||||
def to_enum_hd(k):
|
||||
res = 'HD_'
|
||||
for c in k.upper():
|
||||
if c == ':' or c == '-':
|
||||
res += '_'
|
||||
continue
|
||||
res += c
|
||||
return res
|
||||
|
||||
def build_header(headers):
|
||||
res = {}
|
||||
for k in headers:
|
||||
size = len(k)
|
||||
if size not in res:
|
||||
res[size] = {}
|
||||
ent = res[size]
|
||||
c = k[-1]
|
||||
if c not in ent:
|
||||
ent[c] = []
|
||||
ent[c].append(k)
|
||||
|
||||
return res
|
||||
|
||||
def gen_enum():
|
||||
print '''\
|
||||
enum {'''
|
||||
for k in sorted(HEADERS):
|
||||
print '''\
|
||||
{},'''.format(to_enum_hd(k))
|
||||
print '''\
|
||||
HD_MAXIDX,
|
||||
};'''
|
||||
|
||||
def gen_index_header():
|
||||
print '''\
|
||||
int lookup_token(const uint8_t *name, size_t namelen) {
|
||||
switch (namelen) {'''
|
||||
b = build_header(HEADERS)
|
||||
for size in sorted(b.keys()):
|
||||
ents = b[size]
|
||||
print '''\
|
||||
case {}:'''.format(size)
|
||||
print '''\
|
||||
switch (name[{}]) {{'''.format(size - 1)
|
||||
for c in sorted(ents.keys()):
|
||||
headers = sorted(ents[c])
|
||||
print '''\
|
||||
case '{}':'''.format(c)
|
||||
for k in headers:
|
||||
print '''\
|
||||
if (util::streq_l("{}", name, {})) {{
|
||||
return {};
|
||||
}}'''.format(k[:-1], size - 1, to_enum_hd(k))
|
||||
print '''\
|
||||
break;'''
|
||||
print '''\
|
||||
}
|
||||
break;'''
|
||||
print '''\
|
||||
}
|
||||
return -1;
|
||||
}'''
|
||||
|
||||
if __name__ == '__main__':
|
||||
gen_enum()
|
||||
print ''
|
||||
gen_index_header()
|
||||
gentokenlookup(HEADERS, 'HD')
|
||||
|
||||
53
genmethodfunc.py
Executable file
53
genmethodfunc.py
Executable file
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env python
|
||||
from __future__ import unicode_literals
|
||||
from io import StringIO
|
||||
|
||||
from gentokenlookup import gentokenlookup
|
||||
|
||||
# copied from http-parser/http_parser.h, and stripped trailing spaces
|
||||
# and backslashes.
|
||||
SRC = '''
|
||||
XX(0, DELETE, DELETE)
|
||||
XX(1, GET, GET)
|
||||
XX(2, HEAD, HEAD)
|
||||
XX(3, POST, POST)
|
||||
XX(4, PUT, PUT)
|
||||
/* pathological */
|
||||
XX(5, CONNECT, CONNECT)
|
||||
XX(6, OPTIONS, OPTIONS)
|
||||
XX(7, TRACE, TRACE)
|
||||
/* webdav */
|
||||
XX(8, COPY, COPY)
|
||||
XX(9, LOCK, LOCK)
|
||||
XX(10, MKCOL, MKCOL)
|
||||
XX(11, MOVE, MOVE)
|
||||
XX(12, PROPFIND, PROPFIND)
|
||||
XX(13, PROPPATCH, PROPPATCH)
|
||||
XX(14, SEARCH, SEARCH)
|
||||
XX(15, UNLOCK, UNLOCK)
|
||||
/* subversion */
|
||||
XX(16, REPORT, REPORT)
|
||||
XX(17, MKACTIVITY, MKACTIVITY)
|
||||
XX(18, CHECKOUT, CHECKOUT)
|
||||
XX(19, MERGE, MERGE)
|
||||
/* upnp */
|
||||
XX(20, MSEARCH, M-SEARCH)
|
||||
XX(21, NOTIFY, NOTIFY)
|
||||
XX(22, SUBSCRIBE, SUBSCRIBE)
|
||||
XX(23, UNSUBSCRIBE, UNSUBSCRIBE)
|
||||
/* RFC-5789 */
|
||||
XX(24, PATCH, PATCH)
|
||||
XX(25, PURGE, PURGE)
|
||||
/* CalDAV */
|
||||
XX(26, MKCALENDAR, MKCALENDAR)
|
||||
'''
|
||||
|
||||
if __name__ == '__main__':
|
||||
methods = []
|
||||
for line in StringIO(SRC):
|
||||
line = line.strip()
|
||||
if not line.startswith('XX'):
|
||||
continue
|
||||
_, m, _ = line.split(',', 2)
|
||||
methods.append(m.strip())
|
||||
gentokenlookup(methods, 'HTTP')
|
||||
69
gentokenlookup.py
Normal file
69
gentokenlookup.py
Normal file
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
def to_enum_hd(k, prefix):
|
||||
res = prefix + '_'
|
||||
for c in k.upper():
|
||||
if c == ':' or c == '-':
|
||||
res += '_'
|
||||
continue
|
||||
res += c
|
||||
return res
|
||||
|
||||
def build_header(headers):
|
||||
res = {}
|
||||
for k in headers:
|
||||
size = len(k)
|
||||
if size not in res:
|
||||
res[size] = {}
|
||||
ent = res[size]
|
||||
c = k[-1]
|
||||
if c not in ent:
|
||||
ent[c] = []
|
||||
ent[c].append(k)
|
||||
|
||||
return res
|
||||
|
||||
def gen_enum(tokens, prefix):
|
||||
print '''\
|
||||
enum {'''
|
||||
for k in sorted(tokens):
|
||||
print '''\
|
||||
{},'''.format(to_enum_hd(k, prefix))
|
||||
print '''\
|
||||
{}_MAXIDX,
|
||||
}};'''.format(prefix)
|
||||
|
||||
def gen_index_header(tokens, prefix):
|
||||
print '''\
|
||||
int lookup_token(const uint8_t *name, size_t namelen) {
|
||||
switch (namelen) {'''
|
||||
b = build_header(tokens)
|
||||
for size in sorted(b.keys()):
|
||||
ents = b[size]
|
||||
print '''\
|
||||
case {}:'''.format(size)
|
||||
print '''\
|
||||
switch (name[{}]) {{'''.format(size - 1)
|
||||
for c in sorted(ents.keys()):
|
||||
headers = sorted(ents[c])
|
||||
print '''\
|
||||
case '{}':'''.format(c)
|
||||
for k in headers:
|
||||
print '''\
|
||||
if (util::streq_l("{}", name, {})) {{
|
||||
return {};
|
||||
}}'''.format(k[:-1], size - 1, to_enum_hd(k, prefix))
|
||||
print '''\
|
||||
break;'''
|
||||
print '''\
|
||||
}
|
||||
break;'''
|
||||
print '''\
|
||||
}
|
||||
return -1;
|
||||
}'''
|
||||
|
||||
def gentokenlookup(tokens, prefix):
|
||||
gen_enum(tokens, prefix)
|
||||
print ''
|
||||
gen_index_header(tokens, prefix)
|
||||
53
help2rst.py
53
help2rst.py
@@ -4,6 +4,7 @@
|
||||
# script to produce rst file from program's help output.
|
||||
|
||||
from __future__ import unicode_literals
|
||||
from __future__ import print_function
|
||||
import sys
|
||||
import re
|
||||
import argparse
|
||||
@@ -44,8 +45,8 @@ def help2man(infile):
|
||||
line = infile.readline().strip()
|
||||
m = re.match(r'^Usage: (.*)', line)
|
||||
if not m:
|
||||
print 'usage line is invalid. Expected following lines:'
|
||||
print 'Usage: cmdname ...'
|
||||
print('usage line is invalid. Expected following lines:')
|
||||
print('Usage: cmdname ...')
|
||||
sys.exit(1)
|
||||
synopsis = m.group(1).split(' ', 1)
|
||||
if len(synopsis) == 2:
|
||||
@@ -60,7 +61,9 @@ def help2man(infile):
|
||||
break
|
||||
description.append(line)
|
||||
|
||||
print '''
|
||||
print('''
|
||||
.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY.
|
||||
|
||||
.. program:: {cmdname}
|
||||
|
||||
{cmdname}(1)
|
||||
@@ -77,7 +80,7 @@ DESCRIPTION
|
||||
{description}
|
||||
'''.format(cmdname=cmdname, args=args,
|
||||
cmdnameunderline='=' * (len(cmdname) + 3),
|
||||
synopsis=synopsis, description=format_text('\n'.join(description)))
|
||||
synopsis=synopsis, description=format_text('\n'.join(description))))
|
||||
|
||||
in_arg = False
|
||||
in_footer = False
|
||||
@@ -86,16 +89,16 @@ DESCRIPTION
|
||||
line = line.rstrip()
|
||||
|
||||
if not line.strip() and in_arg:
|
||||
print ''
|
||||
print()
|
||||
continue
|
||||
if line.startswith(' ') and in_arg:
|
||||
if not line.startswith(arg_indent):
|
||||
sys.stderr.write('warning: argument description is not indented correctly. We need {} spaces as indentation.\n'.format(len(arg_indent)))
|
||||
print '{}'.format(format_arg_text(line[len(arg_indent):]))
|
||||
print('{}'.format(format_arg_text(line[len(arg_indent):])))
|
||||
continue
|
||||
|
||||
if in_arg:
|
||||
print ''
|
||||
print()
|
||||
in_arg = False
|
||||
|
||||
if line == '--':
|
||||
@@ -103,22 +106,22 @@ DESCRIPTION
|
||||
continue
|
||||
|
||||
if in_footer:
|
||||
print line.strip()
|
||||
print(line.strip())
|
||||
continue
|
||||
|
||||
if line == 'Options:':
|
||||
print 'OPTIONS'
|
||||
print '-------'
|
||||
print ''
|
||||
print('OPTIONS')
|
||||
print('-------')
|
||||
print()
|
||||
continue
|
||||
|
||||
if line.startswith(' <'):
|
||||
# positional argument
|
||||
m = re.match(r'^(?:\s+)([a-zA-Z0-9-_<>]+)(.*)', line)
|
||||
argname, rest = m.group(1), m.group(2)
|
||||
print '.. describe:: {}'.format(argname)
|
||||
print ''
|
||||
print '{}'.format(format_arg_text(rest.strip()))
|
||||
print('.. describe:: {}'.format(argname))
|
||||
print()
|
||||
print('{}'.format(format_arg_text(rest.strip())))
|
||||
in_arg = True
|
||||
continue
|
||||
|
||||
@@ -126,9 +129,9 @@ DESCRIPTION
|
||||
# positional argument
|
||||
m = re.match(r'^(?:\s+)(\([a-zA-Z0-9-_<> ]+\))(.*)', line)
|
||||
argname, rest = m.group(1), m.group(2)
|
||||
print '.. describe:: {}'.format(argname)
|
||||
print ''
|
||||
print '{}'.format(format_arg_text(rest.strip()))
|
||||
print('.. describe:: {}'.format(argname))
|
||||
print()
|
||||
print('{}'.format(format_arg_text(rest.strip())))
|
||||
in_arg = True
|
||||
continue
|
||||
|
||||
@@ -138,23 +141,23 @@ DESCRIPTION
|
||||
r'^(?:\s+)(-\S+?(?:, -\S+?)*)($| .*)',
|
||||
line)
|
||||
argname, rest = m.group(1), m.group(2)
|
||||
print '.. option:: {}'.format(argname)
|
||||
print ''
|
||||
print('.. option:: {}'.format(argname))
|
||||
print()
|
||||
rest = rest.strip()
|
||||
if len(rest):
|
||||
print '{}'.format(format_arg_text(rest))
|
||||
print('{}'.format(format_arg_text(rest)))
|
||||
in_arg = True
|
||||
continue
|
||||
|
||||
if not line.startswith(' ') and line.endswith(':'):
|
||||
# subsection
|
||||
subsec = line.strip()[:-1]
|
||||
print '{}'.format(subsec)
|
||||
print '{}'.format('~' * len(subsec))
|
||||
print ''
|
||||
print('{}'.format(subsec))
|
||||
print('{}'.format('~' * len(subsec)))
|
||||
print()
|
||||
continue
|
||||
|
||||
print line.strip()
|
||||
print(line.strip())
|
||||
|
||||
def format_text(text):
|
||||
# escape *
|
||||
@@ -184,6 +187,6 @@ if __name__ == '__main__':
|
||||
args = parser.parse_args()
|
||||
help2man(sys.stdin)
|
||||
if args.include:
|
||||
print ''
|
||||
print()
|
||||
with open(args.include) as f:
|
||||
sys.stdout.write(f.read())
|
||||
|
||||
@@ -37,7 +37,8 @@ EXTRA_DIST = \
|
||||
itprep:
|
||||
go get -d -v github.com/bradfitz/http2
|
||||
go get -d -v github.com/tatsuhiro-t/go-nghttp2
|
||||
go get -d -v golang.org/x/net/spdy
|
||||
go get -d -v github.com/tatsuhiro-t/spdy
|
||||
go get -d -v golang.org/x/net/websocket
|
||||
|
||||
it:
|
||||
sh setenv go test -v
|
||||
|
||||
@@ -2,8 +2,10 @@ package nghttp2
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/bradfitz/http2/hpack"
|
||||
"golang.org/x/net/websocket"
|
||||
"io"
|
||||
"net/http"
|
||||
"syscall"
|
||||
@@ -50,6 +52,27 @@ func TestH1H1PlainGETClose(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H1InvalidMethod tests that server rejects invalid method with
|
||||
// 501 status code
|
||||
func TestH1H1InvalidMethod(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Errorf("server should not forward this request")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http1(requestParam{
|
||||
name: "TestH1H1InvalidMethod",
|
||||
method: "get",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http1() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := res.status, 501; got != want {
|
||||
t.Errorf("status = %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H1MultipleRequestCL tests that server rejects request which
|
||||
// contains multiple Content-Length header fields.
|
||||
func TestH1H1MultipleRequestCL(t *testing.T) {
|
||||
@@ -246,6 +269,92 @@ func TestH1H1RequestTrailer(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H1HeaderFieldBufferPath tests that request with request path
|
||||
// larger than configured buffer size is rejected.
|
||||
func TestH1H1HeaderFieldBufferPath(t *testing.T) {
|
||||
// The value 100 is chosen so that sum of header fields bytes
|
||||
// does not exceed it. We use > 100 bytes URI to exceed this
|
||||
// limit.
|
||||
st := newServerTester([]string{"--header-field-buffer=100"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Fatal("execution path should not be here")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http1(requestParam{
|
||||
name: "TestH1H1HeaderFieldBufferPath",
|
||||
path: "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http1() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 431; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H1HeaderFieldBuffer tests that request with header fields
|
||||
// larger than configured buffer size is rejected.
|
||||
func TestH1H1HeaderFieldBuffer(t *testing.T) {
|
||||
st := newServerTester([]string{"--header-field-buffer=10"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Fatal("execution path should not be here")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http1(requestParam{
|
||||
name: "TestH1H1HeaderFieldBuffer",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http1() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 431; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H1HeaderFields tests that request with header fields more
|
||||
// than configured number is rejected.
|
||||
func TestH1H1HeaderFields(t *testing.T) {
|
||||
st := newServerTester([]string{"--max-header-fields=1"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Fatal("execution path should not be here")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http1(requestParam{
|
||||
name: "TestH1H1HeaderFields",
|
||||
header: []hpack.HeaderField{
|
||||
// Add extra header field to ensure that
|
||||
// header field limit exceeds
|
||||
pair("Connection", "close"),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http1() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 431; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H1Websocket tests that HTTP Upgrade to WebSocket works.
|
||||
func TestH1H1Websocket(t *testing.T) {
|
||||
st := newServerTesterHandler(nil, t, websocket.Handler(func(ws *websocket.Conn) {
|
||||
io.Copy(ws, ws)
|
||||
}))
|
||||
defer st.Close()
|
||||
|
||||
content := []byte("hello world")
|
||||
res, err := st.websocket(requestParam{
|
||||
name: "TestH1H1Websocket",
|
||||
body: content,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.websocket() = %v", err)
|
||||
}
|
||||
if got, want := res.body, content; !bytes.Equal(got, want) {
|
||||
t.Errorf("echo: %q; want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H2ConnectFailure tests that server handles the situation that
|
||||
// connection attempt to HTTP/2 backend failed.
|
||||
func TestH1H2ConnectFailure(t *testing.T) {
|
||||
|
||||
@@ -404,6 +404,26 @@ func TestH2H1ConnectFailure(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1InvalidMethod tests that server rejects invalid method with
|
||||
// 501.
|
||||
func TestH2H1InvalidMethod(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Errorf("server should not forward this request")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1InvalidMethod",
|
||||
method: "get",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 501; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1AssembleCookies tests that crumbled cookies in HTTP/2
|
||||
// request is assembled into 1 when forwarding to HTTP/1 backend link.
|
||||
func TestH2H1AssembleCookies(t *testing.T) {
|
||||
@@ -558,6 +578,46 @@ func TestH2H1RequestTrailer(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1HeaderFieldBuffer tests that request with header fields
|
||||
// larger than configured buffer size is rejected.
|
||||
func TestH2H1HeaderFieldBuffer(t *testing.T) {
|
||||
st := newServerTester([]string{"--header-field-buffer=10"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Fatal("execution path should not be here")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1HeaderFieldBuffer",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 431; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1HeaderFields tests that request with header fields more
|
||||
// than configured number is rejected.
|
||||
func TestH2H1HeaderFields(t *testing.T) {
|
||||
st := newServerTester([]string{"--max-header-fields=1"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Fatal("execution path should not be here")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1HeaderFields",
|
||||
// we have at least 4 pseudo-header fields sent, and
|
||||
// that ensures that buffer limit exceeds.
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 431; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1Upgrade tests HTTP Upgrade to HTTP/2
|
||||
func TestH2H1Upgrade(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {})
|
||||
@@ -567,7 +627,7 @@ func TestH2H1Upgrade(t *testing.T) {
|
||||
name: "TestH2H1Upgrade",
|
||||
header: []hpack.HeaderField{
|
||||
pair("Connection", "Upgrade, HTTP2-Settings"),
|
||||
pair("Upgrade", "h2c-14"),
|
||||
pair("Upgrade", "h2c"),
|
||||
pair("HTTP2-Settings", "AAMAAABkAAQAAP__"),
|
||||
},
|
||||
})
|
||||
|
||||
@@ -2,7 +2,7 @@ package nghttp2
|
||||
|
||||
import (
|
||||
"github.com/bradfitz/http2/hpack"
|
||||
"golang.org/x/net/spdy"
|
||||
"github.com/tatsuhiro-t/spdy"
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
@@ -170,6 +170,66 @@ func TestS3H1NoVia(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestS3H1HeaderFieldBuffer tests that request with header fields
|
||||
// larger than configured buffer size is rejected.
|
||||
func TestS3H1HeaderFieldBuffer(t *testing.T) {
|
||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--header-field-buffer=10"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Fatal("execution path should not be here")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.spdy(requestParam{
|
||||
name: "TestS3H1HeaderFieldBuffer",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.spdy() = %v", err)
|
||||
}
|
||||
if got, want := res.spdyRstErrCode, spdy.InternalError; got != want {
|
||||
t.Errorf("res.spdyRstErrCode: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestS3H1HeaderFields tests that request with header fields more
|
||||
// than configured number is rejected.
|
||||
func TestS3H1HeaderFields(t *testing.T) {
|
||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--max-header-fields=1"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Fatal("execution path should not be here")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.spdy(requestParam{
|
||||
name: "TestS3H1HeaderFields",
|
||||
// we have at least 5 pseudo-header fields sent, and
|
||||
// that ensures that buffer limit exceeds.
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.spdy() = %v", err)
|
||||
}
|
||||
if got, want := res.spdyRstErrCode, spdy.InternalError; got != want {
|
||||
t.Errorf("res.spdyRstErrCode: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestS3H1InvalidMethod tests that server rejects invalid method with
|
||||
// 501.
|
||||
func TestS3H1InvalidMethod(t *testing.T) {
|
||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Errorf("server should not forward this request")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.spdy(requestParam{
|
||||
name: "TestS3H1InvalidMethod",
|
||||
method: "get",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.spdy() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 501; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestS3H2ConnectFailure tests that server handles the situation that
|
||||
// connection attempt to HTTP/2 backend failed.
|
||||
func TestS3H2ConnectFailure(t *testing.T) {
|
||||
|
||||
@@ -9,7 +9,8 @@ import (
|
||||
"github.com/bradfitz/http2"
|
||||
"github.com/bradfitz/http2/hpack"
|
||||
"github.com/tatsuhiro-t/go-nghttp2"
|
||||
"golang.org/x/net/spdy"
|
||||
"github.com/tatsuhiro-t/spdy"
|
||||
"golang.org/x/net/websocket"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
@@ -66,6 +67,10 @@ func newServerTester(args []string, t *testing.T, handler http.HandlerFunc) *ser
|
||||
return newServerTesterInternal(args, t, handler, false, nil)
|
||||
}
|
||||
|
||||
func newServerTesterHandler(args []string, t *testing.T, handler http.Handler) *serverTester {
|
||||
return newServerTesterInternal(args, t, handler, false, nil)
|
||||
}
|
||||
|
||||
// newServerTester creates test context for TLS frontend connection.
|
||||
func newServerTesterTLS(args []string, t *testing.T, handler http.HandlerFunc) *serverTester {
|
||||
return newServerTesterInternal(args, t, handler, true, nil)
|
||||
@@ -79,7 +84,7 @@ func newServerTesterTLSConfig(args []string, t *testing.T, handler http.HandlerF
|
||||
|
||||
// newServerTesterInternal creates test context. If frontendTLS is
|
||||
// true, set up TLS frontend connection.
|
||||
func newServerTesterInternal(args []string, t *testing.T, handler http.HandlerFunc, frontendTLS bool, clientConfig *tls.Config) *serverTester {
|
||||
func newServerTesterInternal(args []string, t *testing.T, handler http.Handler, frontendTLS bool, clientConfig *tls.Config) *serverTester {
|
||||
ts := httptest.NewUnstartedServer(handler)
|
||||
|
||||
backendTLS := false
|
||||
@@ -279,6 +284,41 @@ func (cbr *chunkedBodyReader) Read(p []byte) (n int, err error) {
|
||||
return cbr.body.Read(p)
|
||||
}
|
||||
|
||||
func (st *serverTester) websocket(rp requestParam) (*serverResponse, error) {
|
||||
urlstring := st.url + "/echo"
|
||||
|
||||
config, err := websocket.NewConfig(urlstring, st.url)
|
||||
if err != nil {
|
||||
st.t.Fatalf("websocket.NewConfig(%q, %q) returned error: %v", urlstring, st.url, err)
|
||||
}
|
||||
|
||||
config.Header.Add("Test-Case", rp.name)
|
||||
for _, h := range rp.header {
|
||||
config.Header.Add(h.Name, h.Value)
|
||||
}
|
||||
|
||||
ws, err := websocket.NewClient(config, st.conn)
|
||||
if err != nil {
|
||||
st.t.Fatalf("Error creating websocket client: %v", err)
|
||||
}
|
||||
|
||||
if _, err := ws.Write(rp.body); err != nil {
|
||||
st.t.Fatalf("ws.Write() returned error: %v", err)
|
||||
}
|
||||
|
||||
msg := make([]byte, 1024)
|
||||
var n int
|
||||
if n, err = ws.Read(msg); err != nil {
|
||||
st.t.Fatalf("ws.Read() returned error: %v", err)
|
||||
}
|
||||
|
||||
res := &serverResponse{
|
||||
body: msg[:n],
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (st *serverTester) http1(rp requestParam) (*serverResponse, error) {
|
||||
method := "GET"
|
||||
if rp.method != "" {
|
||||
@@ -297,7 +337,19 @@ func (st *serverTester) http1(rp requestParam) (*serverResponse, error) {
|
||||
body = cbr
|
||||
}
|
||||
}
|
||||
req, err := http.NewRequest(method, st.url, body)
|
||||
|
||||
reqURL := st.url
|
||||
|
||||
if rp.path != "" {
|
||||
u, err := url.Parse(st.url)
|
||||
if err != nil {
|
||||
st.t.Fatalf("Error parsing URL from st.url %v: %v", st.url, err)
|
||||
}
|
||||
u.Path = rp.path
|
||||
reqURL = u.String()
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(method, reqURL, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -25,7 +25,8 @@ SUBDIRS = includes
|
||||
EXTRA_DIST = Makefile.msvc
|
||||
|
||||
AM_CFLAGS = $(WARNCFLAGS)
|
||||
AM_CPPFLAGS = -I$(srcdir)/includes -I$(builddir)/includes @DEFS@
|
||||
AM_CPPFLAGS = -I$(srcdir)/includes -I$(builddir)/includes -DBUILDING_NGHTTP2 \
|
||||
@DEFS@
|
||||
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
pkgconfig_DATA = libnghttp2.pc
|
||||
|
||||
@@ -36,7 +36,14 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#if defined(_MSC_VER) && (_MSC_VER < 1800)
|
||||
/* MSVC < 2013 does not have inttypes.h because it is not C99
|
||||
compliant. See compiler macros and version number in
|
||||
https://sourceforge.net/p/predef/wiki/Compilers/ */
|
||||
#include <stdint.h>
|
||||
#else /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */
|
||||
#include <inttypes.h>
|
||||
#endif /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <nghttp2/nghttp2ver.h>
|
||||
@@ -44,8 +51,12 @@ extern "C" {
|
||||
#ifdef NGHTTP2_STATICLIB
|
||||
#define NGHTTP2_EXTERN
|
||||
#elif defined(WIN32)
|
||||
#ifdef BUILDING_NGHTTP2
|
||||
#define NGHTTP2_EXTERN __declspec(dllexport)
|
||||
#else /* !defined(WIN32) */
|
||||
#else /* !BUILDING_NGHTTP2 */
|
||||
#define NGHTTP2_EXTERN __declspec(dllimport)
|
||||
#endif /* !BUILDING_NGHTTP2 */
|
||||
#else /* !defined(WIN32) */
|
||||
#define NGHTTP2_EXTERN
|
||||
#endif /* !defined(WIN32) */
|
||||
|
||||
@@ -55,13 +66,13 @@ extern "C" {
|
||||
* The protocol version identification string of this library
|
||||
* supports. This identifier is used if HTTP/2 is used over TLS.
|
||||
*/
|
||||
#define NGHTTP2_PROTO_VERSION_ID "h2-14"
|
||||
#define NGHTTP2_PROTO_VERSION_ID "h2"
|
||||
/**
|
||||
* @macro
|
||||
*
|
||||
* The length of :macro:`NGHTTP2_PROTO_VERSION_ID`.
|
||||
*/
|
||||
#define NGHTTP2_PROTO_VERSION_ID_LEN 5
|
||||
#define NGHTTP2_PROTO_VERSION_ID_LEN 2
|
||||
|
||||
/**
|
||||
* @macro
|
||||
@@ -72,7 +83,7 @@ extern "C" {
|
||||
* extension <https://tools.ietf.org/html/rfc7301>`_. This is useful
|
||||
* to process incoming ALPN tokens in wire format.
|
||||
*/
|
||||
#define NGHTTP2_PROTO_ALPN "\x5h2-14"
|
||||
#define NGHTTP2_PROTO_ALPN "\x2h2"
|
||||
|
||||
/**
|
||||
* @macro
|
||||
@@ -88,14 +99,14 @@ extern "C" {
|
||||
* supports. This identifier is used if HTTP/2 is used over cleartext
|
||||
* TCP.
|
||||
*/
|
||||
#define NGHTTP2_CLEARTEXT_PROTO_VERSION_ID "h2c-14"
|
||||
#define NGHTTP2_CLEARTEXT_PROTO_VERSION_ID "h2c"
|
||||
|
||||
/**
|
||||
* @macro
|
||||
*
|
||||
* The length of :macro:`NGHTTP2_CLEARTEXT_PROTO_VERSION_ID`.
|
||||
*/
|
||||
#define NGHTTP2_CLEARTEXT_PROTO_VERSION_ID_LEN 6
|
||||
#define NGHTTP2_CLEARTEXT_PROTO_VERSION_ID_LEN 3
|
||||
|
||||
struct nghttp2_session;
|
||||
/**
|
||||
@@ -194,32 +205,17 @@ typedef struct {
|
||||
/**
|
||||
* @macro
|
||||
*
|
||||
* The client connection preface.
|
||||
* The client magic string, which is the first 24 bytes byte string of
|
||||
* client connection preface.
|
||||
*/
|
||||
#define NGHTTP2_CLIENT_CONNECTION_PREFACE "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
|
||||
#define NGHTTP2_CLIENT_MAGIC "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
|
||||
|
||||
/**
|
||||
* @macro
|
||||
*
|
||||
* The length of :macro:`NGHTTP2_CLIENT_CONNECTION_PREFACE`.
|
||||
* The length of :macro:`NGHTTP2_CLIENT_MAGIC`.
|
||||
*/
|
||||
#define NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN 24
|
||||
|
||||
/**
|
||||
* @macro
|
||||
*
|
||||
* The client connection header. This macro is obsoleted by
|
||||
* NGHTTP2_CLIENT_CONNECTION_PREFACE.
|
||||
*/
|
||||
#define NGHTTP2_CLIENT_CONNECTION_HEADER NGHTTP2_CLIENT_CONNECTION_PREFACE
|
||||
|
||||
/**
|
||||
* @macro
|
||||
*
|
||||
* The length of :macro:`NGHTTP2_CLIENT_CONNECTION_HEADER`.
|
||||
*/
|
||||
#define NGHTTP2_CLIENT_CONNECTION_HEADER_LEN \
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN
|
||||
#define NGHTTP2_CLIENT_MAGIC_LEN 24
|
||||
|
||||
/**
|
||||
* @enum
|
||||
@@ -368,6 +364,18 @@ typedef enum {
|
||||
* closed.
|
||||
*/
|
||||
NGHTTP2_ERR_HTTP_HEADER = -531,
|
||||
/**
|
||||
* Violation in HTTP messaging rule.
|
||||
*/
|
||||
NGHTTP2_ERR_HTTP_MESSAGING = -532,
|
||||
/**
|
||||
* Stream was refused.
|
||||
*/
|
||||
NGHTTP2_ERR_REFUSED_STREAM = -533,
|
||||
/**
|
||||
* Unexpected internal error, but recovered.
|
||||
*/
|
||||
NGHTTP2_ERR_INTERNAL = -534,
|
||||
/**
|
||||
* The errors < :enum:`NGHTTP2_ERR_FATAL` mean that the library is
|
||||
* under unexpected condition and processing was terminated (e.g.,
|
||||
@@ -386,10 +394,10 @@ typedef enum {
|
||||
*/
|
||||
NGHTTP2_ERR_CALLBACK_FAILURE = -902,
|
||||
/**
|
||||
* Invalid connection preface was received and further processing is
|
||||
* not possible.
|
||||
* Invalid client magic (see :macro:`NGHTTP2_CLIENT_MAGIC`) was
|
||||
* received and further processing is not possible.
|
||||
*/
|
||||
NGHTTP2_ERR_BAD_PREFACE = -903
|
||||
NGHTTP2_ERR_BAD_CLIENT_MAGIC = -903
|
||||
} nghttp2_error;
|
||||
|
||||
/**
|
||||
@@ -496,21 +504,6 @@ typedef enum {
|
||||
NGHTTP2_CONTINUATION = 0x09
|
||||
} nghttp2_frame_type;
|
||||
|
||||
/**
|
||||
* @enum
|
||||
*
|
||||
* The extension frame types.
|
||||
*
|
||||
* TODO: The assigned frame types were carried from draft-12, and now
|
||||
* actually TBD.
|
||||
*/
|
||||
typedef enum {
|
||||
/**
|
||||
* The ALTSVC extension frame.
|
||||
*/
|
||||
NGHTTP2_EXT_ALTSVC = 0x0a
|
||||
} nghttp2_ext_frame_type;
|
||||
|
||||
/**
|
||||
* @enum
|
||||
*
|
||||
@@ -1079,52 +1072,12 @@ typedef struct {
|
||||
* The pointer to extension payload. The exact pointer type is
|
||||
* determined by hd.type.
|
||||
*
|
||||
* If hd.type == :enum:`NGHTTP2_EXT_ALTSVC`, it is a pointer to
|
||||
* :type:`nghttp2_ext_altsvc`.
|
||||
* Currently, no extension is supported. This is a place holder for
|
||||
* the future extensions.
|
||||
*/
|
||||
void *payload;
|
||||
} nghttp2_extension;
|
||||
|
||||
/**
|
||||
* @struct
|
||||
*
|
||||
* The ALTSVC extension frame payload. It has following members:
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* Protocol ID
|
||||
*/
|
||||
uint8_t *protocol_id;
|
||||
/**
|
||||
* Host
|
||||
*/
|
||||
uint8_t *host;
|
||||
/**
|
||||
* Origin
|
||||
*/
|
||||
uint8_t *origin;
|
||||
/**
|
||||
* The length of |protocol_id|
|
||||
*/
|
||||
size_t protocol_id_len;
|
||||
/**
|
||||
* The length of |host|
|
||||
*/
|
||||
size_t host_len;
|
||||
/**
|
||||
* The length of |origin|
|
||||
*/
|
||||
size_t origin_len;
|
||||
/**
|
||||
* Max-Age
|
||||
*/
|
||||
uint32_t max_age;
|
||||
/**
|
||||
* Port
|
||||
*/
|
||||
uint16_t port;
|
||||
} nghttp2_ext_altsvc;
|
||||
|
||||
/**
|
||||
* @union
|
||||
*
|
||||
@@ -1199,6 +1152,15 @@ typedef union {
|
||||
*
|
||||
* To set this callback to :type:`nghttp2_session_callbacks`, use
|
||||
* `nghttp2_session_callbacks_set_send_callback()`.
|
||||
*
|
||||
* .. note::
|
||||
*
|
||||
* The |length| may be very small. If that is the case, and
|
||||
* application disables Nagle algorithm (``TCP_NODELAY``), then just
|
||||
* writing |data| to the network stack leads to very small packet,
|
||||
* and it is very inefficient. An application should be responsible
|
||||
* to buffer up small chunks of data as necessary to avoid this
|
||||
* situation.
|
||||
*/
|
||||
typedef ssize_t (*nghttp2_send_callback)(nghttp2_session *session,
|
||||
const uint8_t *data, size_t length,
|
||||
@@ -1316,11 +1278,11 @@ typedef int (*nghttp2_on_frame_recv_callback)(nghttp2_session *session,
|
||||
*
|
||||
* Callback function invoked by `nghttp2_session_recv()` and
|
||||
* `nghttp2_session_mem_recv()` when an invalid non-DATA frame is
|
||||
* received. The |error_code| indicates the error. It is usually one
|
||||
* of the :enum:`nghttp2_error_code` but that is not guaranteed. When
|
||||
* this callback function is invoked, the library automatically
|
||||
* submits either RST_STREAM or GOAWAY frame. The |user_data| pointer
|
||||
* is the third argument passed in to the call to
|
||||
* received. The error is indicated by the |lib_error_code|, which is
|
||||
* one of the values defined in :type:`nghttp2_error`. When this
|
||||
* callback function is invoked, the library automatically submits
|
||||
* either RST_STREAM or GOAWAY frame. The |user_data| pointer is the
|
||||
* third argument passed in to the call to
|
||||
* `nghttp2_session_client_new()` or `nghttp2_session_server_new()`.
|
||||
*
|
||||
* If frame is HEADERS or PUSH_PROMISE, the ``nva`` and ``nvlen``
|
||||
@@ -1336,7 +1298,7 @@ typedef int (*nghttp2_on_frame_recv_callback)(nghttp2_session *session,
|
||||
* `nghttp2_session_callbacks_set_on_invalid_frame_recv_callback()`.
|
||||
*/
|
||||
typedef int (*nghttp2_on_invalid_frame_recv_callback)(
|
||||
nghttp2_session *session, const nghttp2_frame *frame, uint32_t error_code,
|
||||
nghttp2_session *session, const nghttp2_frame *frame, int lib_error_code,
|
||||
void *user_data);
|
||||
|
||||
/**
|
||||
@@ -2036,31 +1998,33 @@ nghttp2_option_set_peer_max_concurrent_streams(nghttp2_option *option,
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* By default, nghttp2 library only handles HTTP/2 frames and does not
|
||||
* recognize first 24 bytes of client connection preface. This design
|
||||
* choice is done due to the fact that server may want to detect the
|
||||
* application protocol based on first few bytes on clear text
|
||||
* communication. But for simple servers which only speak HTTP/2, it
|
||||
* is easier for developers if nghttp2 library takes care of client
|
||||
* connection preface.
|
||||
* By default, nghttp2 library, if configured as server, requires
|
||||
* first 24 bytes of client magic byte string (MAGIC). In most cases,
|
||||
* this will simplify the implementation of server. But sometimes
|
||||
* server may want to detect the application protocol based on first
|
||||
* few bytes on clear text communication.
|
||||
*
|
||||
* If this option is used with nonzero |val|, nghttp2 library checks
|
||||
* first 24 bytes client connection preface. If it is not a valid
|
||||
* one, `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` will
|
||||
* return error :enum:`NGHTTP2_ERR_BAD_PREFACE`, which is fatal error.
|
||||
* If this option is used with nonzero |val|, nghttp2 library does not
|
||||
* handle MAGIC. It still checks following SETTINGS frame. This
|
||||
* means that applications should deal with MAGIC by themselves.
|
||||
*
|
||||
* If this option is not used or used with zero value, if MAGIC does
|
||||
* not match :macro:`NGHTTP2_CLIENT_MAGIC`, `nghttp2_session_recv()`
|
||||
* and `nghttp2_session_mem_recv()` will return error
|
||||
* :enum:`NGHTTP2_ERR_BAD_CLIENT_MAGIC`, which is fatal error.
|
||||
*/
|
||||
NGHTTP2_EXTERN void
|
||||
nghttp2_option_set_recv_client_preface(nghttp2_option *option, int val);
|
||||
nghttp2_option_set_no_recv_client_magic(nghttp2_option *option, int val);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* By default, nghttp2 library enforces subset of HTTP Messaging rules
|
||||
* described in `HTTP/2 specification, section 8
|
||||
* <https://tools.ietf.org/html/draft-ietf-httpbis-http2-17#section-8>`_.
|
||||
* See :ref:`http-messaging` section for details. For those
|
||||
* applications who use nghttp2 library as non-HTTP use, give nonzero
|
||||
* to |val| to disable this enforcement.
|
||||
* <https://tools.ietf.org/html/rfc7540#section-8>`_. See
|
||||
* :ref:`http-messaging` section for details. For those applications
|
||||
* who use nghttp2 library as non-HTTP use, give nonzero to |val| to
|
||||
* disable this enforcement.
|
||||
*/
|
||||
NGHTTP2_EXTERN void nghttp2_option_set_no_http_messaging(nghttp2_option *option,
|
||||
int val);
|
||||
@@ -2305,6 +2269,15 @@ NGHTTP2_EXTERN int nghttp2_session_send(nghttp2_session *session);
|
||||
*
|
||||
* :enum:`NGHTTP2_ERR_NOMEM`
|
||||
* Out of memory.
|
||||
*
|
||||
* .. note::
|
||||
*
|
||||
* This function may produce very small byte string. If that is the
|
||||
* case, and application disables Nagle algorithm (``TCP_NODELAY``),
|
||||
* then writing this small chunk leads to very small packet, and it
|
||||
* is very inefficient. An application should be responsible to
|
||||
* buffer up small chunks of data as necessary to avoid this
|
||||
* situation.
|
||||
*/
|
||||
NGHTTP2_EXTERN ssize_t nghttp2_session_mem_send(nghttp2_session *session,
|
||||
const uint8_t **data_ptr);
|
||||
@@ -2367,10 +2340,11 @@ NGHTTP2_EXTERN ssize_t nghttp2_session_mem_send(nghttp2_session *session,
|
||||
* Out of memory.
|
||||
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`
|
||||
* The callback function failed.
|
||||
* :enum:`NGHTTP2_ERR_BAD_PREFACE`
|
||||
* Invalid client preface was detected. This error only returns
|
||||
* :enum:`NGHTTP2_ERR_BAD_CLIENT_MAGIC`
|
||||
* Invalid client magic was detected. This error only returns
|
||||
* when |session| was configured as server and
|
||||
* `nghttp2_option_set_recv_client_preface()` is used.
|
||||
* `nghttp2_option_set_no_recv_client_magic()` is not used with
|
||||
* nonzero value.
|
||||
*/
|
||||
NGHTTP2_EXTERN int nghttp2_session_recv(nghttp2_session *session);
|
||||
|
||||
@@ -2402,10 +2376,11 @@ NGHTTP2_EXTERN int nghttp2_session_recv(nghttp2_session *session);
|
||||
* Out of memory.
|
||||
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`
|
||||
* The callback function failed.
|
||||
* :enum:`NGHTTP2_ERR_BAD_PREFACE`
|
||||
* Invalid client preface was detected. This error only returns
|
||||
* :enum:`NGHTTP2_ERR_BAD_CLIENT_MAGIC`
|
||||
* Invalid client magic was detected. This error only returns
|
||||
* when |session| was configured as server and
|
||||
* `nghttp2_option_set_recv_client_preface()` is used.
|
||||
* `nghttp2_option_set_no_recv_client_magic()` is not used with
|
||||
* nonzero value.
|
||||
*/
|
||||
NGHTTP2_EXTERN ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
|
||||
const uint8_t *in,
|
||||
@@ -2949,13 +2924,13 @@ nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec);
|
||||
* If |data_prd| is not ``NULL``, it provides data which will be sent
|
||||
* in subsequent DATA frames. In this case, a method that allows
|
||||
* request message bodies
|
||||
* (http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9) must
|
||||
* be specified with ``:method`` key in |nva| (e.g. ``POST``). This
|
||||
* function does not take ownership of the |data_prd|. The function
|
||||
* copies the members of the |data_prd|. If |data_prd| is ``NULL``,
|
||||
* HEADERS have END_STREAM set. The |stream_user_data| is data
|
||||
* associated to the stream opened by this request and can be an
|
||||
* arbitrary pointer, which can be retrieved later by
|
||||
* (https://tools.ietf.org/html/rfc7231#section-4) must be specified
|
||||
* with ``:method`` key in |nva| (e.g. ``POST``). This function does
|
||||
* not take ownership of the |data_prd|. The function copies the
|
||||
* members of the |data_prd|. If |data_prd| is ``NULL``, HEADERS have
|
||||
* END_STREAM set. The |stream_user_data| is data associated to the
|
||||
* stream opened by this request and can be an arbitrary pointer,
|
||||
* which can be retrieved later by
|
||||
* `nghttp2_session_get_stream_user_data()`.
|
||||
*
|
||||
* This function returns assigned stream ID if it succeeds, or one of
|
||||
@@ -3488,20 +3463,6 @@ NGHTTP2_EXTERN int nghttp2_submit_window_update(nghttp2_session *session,
|
||||
int32_t stream_id,
|
||||
int32_t window_size_increment);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* This function previously submits ALTSVC frame with given
|
||||
* parameters, but is deprecated and will be removed in a future
|
||||
* release. This function does nothing and just return 0.
|
||||
*/
|
||||
NGHTTP2_EXTERN int
|
||||
nghttp2_submit_altsvc(nghttp2_session *session, uint8_t flags,
|
||||
int32_t stream_id, uint32_t max_age, uint16_t port,
|
||||
const uint8_t *protocol_id, size_t protocol_id_len,
|
||||
const uint8_t *host, size_t host_len,
|
||||
const uint8_t *origin, size_t origin_len);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
@@ -3520,14 +3481,14 @@ NGHTTP2_EXTERN int nghttp2_nv_compare_name(const nghttp2_nv *lhs,
|
||||
* A helper function for dealing with NPN in client side or ALPN in
|
||||
* server side. The |in| contains peer's protocol list in preferable
|
||||
* order. The format of |in| is length-prefixed and not
|
||||
* null-terminated. For example, ``HTTP-draft-04/2.0`` and
|
||||
* null-terminated. For example, ``h2`` and
|
||||
* ``http/1.1`` stored in |in| like this::
|
||||
*
|
||||
* in[0] = 17
|
||||
* in[1..17] = "HTTP-draft-04/2.0"
|
||||
* in[18] = 8
|
||||
* in[19..26] = "http/1.1"
|
||||
* inlen = 27
|
||||
* in[0] = 2
|
||||
* in[1..2] = "h2"
|
||||
* in[3] = 8
|
||||
* in[4..11] = "http/1.1"
|
||||
* inlen = 12
|
||||
*
|
||||
* The selection algorithm is as follows:
|
||||
*
|
||||
@@ -3541,12 +3502,10 @@ NGHTTP2_EXTERN int nghttp2_nv_compare_name(const nghttp2_nv *lhs,
|
||||
* non-overlap case). In this case, |out| and |outlen| are left
|
||||
* untouched.
|
||||
*
|
||||
* Selecting ``HTTP-draft-04/2.0`` means that ``HTTP-draft-04/2.0`` is
|
||||
* written into |*out| and its length (which is 17) is assigned to
|
||||
* |*outlen|.
|
||||
* Selecting ``h2`` means that ``h2`` is written into |*out| and its
|
||||
* length (which is 2) is assigned to |*outlen|.
|
||||
*
|
||||
* For ALPN, refer to
|
||||
* https://tools.ietf.org/html/draft-ietf-tls-applayerprotoneg-05
|
||||
* For ALPN, refer to https://tools.ietf.org/html/rfc7301
|
||||
*
|
||||
* See http://technotes.googlecode.com/git/nextprotoneg.html for more
|
||||
* details about NPN.
|
||||
@@ -3562,7 +3521,10 @@ NGHTTP2_EXTERN int nghttp2_nv_compare_name(const nghttp2_nv *lhs,
|
||||
* {
|
||||
* int rv;
|
||||
* rv = nghttp2_select_next_protocol(out, outlen, in, inlen);
|
||||
* if(rv == 1) {
|
||||
* if (rv == -1) {
|
||||
* return SSL_TLSEXT_ERR_NOACK;
|
||||
* }
|
||||
* if (rv == 1) {
|
||||
* ((MyType*)arg)->http2_selected = 1;
|
||||
* }
|
||||
* return SSL_TLSEXT_ERR_OK;
|
||||
@@ -3593,7 +3555,7 @@ NGHTTP2_EXTERN nghttp2_info *nghttp2_version(int least_version);
|
||||
* Returns nonzero if the :type:`nghttp2_error` library error code
|
||||
* |lib_error| is fatal.
|
||||
*/
|
||||
NGHTTP2_EXTERN int nghttp2_is_fatal(int lib_error);
|
||||
NGHTTP2_EXTERN int nghttp2_is_fatal(int lib_error_code);
|
||||
|
||||
/**
|
||||
* @function
|
||||
|
||||
@@ -320,7 +320,7 @@ int nghttp2_bufs_add(nghttp2_bufs *bufs, const void *data, size_t len) {
|
||||
}
|
||||
|
||||
buf->last = nghttp2_cpymem(buf->last, p, nwrite);
|
||||
p += len;
|
||||
p += nwrite;
|
||||
len -= nwrite;
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
#define NGHTTP2_MAX_PADLEN 256
|
||||
|
||||
/* Union of extension frame payload */
|
||||
typedef union { nghttp2_ext_altsvc altsvc; } nghttp2_ext_frame_payload;
|
||||
typedef union { int dummy; } nghttp2_ext_frame_payload;
|
||||
|
||||
void nghttp2_frame_pack_frame_hd(uint8_t *buf, const nghttp2_frame_hd *hd);
|
||||
|
||||
|
||||
@@ -544,7 +544,8 @@ int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t flags, uint8_t *name,
|
||||
return 0;
|
||||
|
||||
fail2:
|
||||
if (flags & NGHTTP2_HD_FLAG_NAME_ALLOC) {
|
||||
if ((flags & NGHTTP2_HD_FLAG_NAME_ALLOC) &&
|
||||
(flags & NGHTTP2_HD_FLAG_NAME_GIFT) == 0) {
|
||||
nghttp2_mem_free(mem, ent->nv.name);
|
||||
}
|
||||
fail:
|
||||
@@ -1157,15 +1158,11 @@ static nghttp2_hd_entry *add_hd_table_incremental(nghttp2_hd_context *context,
|
||||
}
|
||||
|
||||
static int name_eq(const nghttp2_nv *a, const nghttp2_nv *b) {
|
||||
return a->namelen == b->namelen &&
|
||||
a->name[a->namelen - 1] == b->name[a->namelen - 1] &&
|
||||
memeq(a->name, b->name, a->namelen);
|
||||
return a->namelen == b->namelen && memeq(a->name, b->name, a->namelen);
|
||||
}
|
||||
|
||||
static int value_eq(const nghttp2_nv *a, const nghttp2_nv *b) {
|
||||
return a->valuelen == b->valuelen &&
|
||||
a->value[a->valuelen - 1] == b->value[a->valuelen - 1] &&
|
||||
memeq(a->value, b->value, a->valuelen);
|
||||
return a->valuelen == b->valuelen && memeq(a->value, b->value, a->valuelen);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
@@ -1733,7 +1730,9 @@ static int hd_inflate_remove_bufs(nghttp2_hd_inflater *inflater, nghttp2_nv *nv,
|
||||
static int hd_inflate_remove_bufs_with_name(nghttp2_hd_inflater *inflater,
|
||||
nghttp2_nv *nv,
|
||||
nghttp2_hd_entry *ent_name) {
|
||||
#ifndef NDEBUG
|
||||
size_t rv;
|
||||
#endif
|
||||
size_t buflen;
|
||||
uint8_t *buf;
|
||||
nghttp2_mem *mem;
|
||||
@@ -1751,8 +1750,11 @@ static int hd_inflate_remove_bufs_with_name(nghttp2_hd_inflater *inflater,
|
||||
|
||||
/* Copy including terminal NULL */
|
||||
memcpy(buf, ent_name->nv.name, ent_name->nv.namelen + 1);
|
||||
rv = nghttp2_bufs_remove_copy(&inflater->nvbufs,
|
||||
buf + ent_name->nv.namelen + 1);
|
||||
#ifndef NDEBUG
|
||||
rv =
|
||||
#endif
|
||||
nghttp2_bufs_remove_copy(&inflater->nvbufs,
|
||||
buf + ent_name->nv.namelen + 1);
|
||||
assert(ent_name->nv.namelen + 1 + rv == buflen);
|
||||
|
||||
nghttp2_bufs_reset(&inflater->nvbufs);
|
||||
@@ -1893,7 +1895,11 @@ static int hd_inflate_commit_indname(nghttp2_hd_inflater *inflater,
|
||||
return 0;
|
||||
}
|
||||
|
||||
nghttp2_mem_free(mem, nv.value);
|
||||
if (inflater->index < NGHTTP2_STATIC_TABLE_LENGTH) {
|
||||
nghttp2_mem_free(mem, nv.value);
|
||||
} else {
|
||||
nghttp2_mem_free(mem, nv.name);
|
||||
}
|
||||
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ typedef enum {
|
||||
NGHTTP2_TOKEN_CONNECTION,
|
||||
NGHTTP2_TOKEN_KEEP_ALIVE,
|
||||
NGHTTP2_TOKEN_PROXY_CONNECTION,
|
||||
NGHTTP2_TOKEN_UPGRADE,
|
||||
NGHTTP2_TOKEN_UPGRADE
|
||||
} nghttp2_token;
|
||||
|
||||
typedef enum {
|
||||
|
||||
@@ -297,12 +297,18 @@ const char *nghttp2_strerror(int error_code) {
|
||||
return "The current session is closing";
|
||||
case NGHTTP2_ERR_HTTP_HEADER:
|
||||
return "Invalid HTTP header field was received";
|
||||
case NGHTTP2_ERR_HTTP_MESSAGING:
|
||||
return "Violation in HTTP messaging rule";
|
||||
case NGHTTP2_ERR_REFUSED_STREAM:
|
||||
return "Stream was refused";
|
||||
case NGHTTP2_ERR_INTERNAL:
|
||||
return "Internal error";
|
||||
case NGHTTP2_ERR_NOMEM:
|
||||
return "Out of memory";
|
||||
case NGHTTP2_ERR_CALLBACK_FAILURE:
|
||||
return "The user callback function failed";
|
||||
case NGHTTP2_ERR_BAD_PREFACE:
|
||||
return "Received bad connection preface";
|
||||
case NGHTTP2_ERR_BAD_CLIENT_MAGIC:
|
||||
return "Received bad clinet magic byte string";
|
||||
default:
|
||||
return "Unknown error code";
|
||||
}
|
||||
|
||||
@@ -47,9 +47,9 @@ void nghttp2_option_set_peer_max_concurrent_streams(nghttp2_option *option,
|
||||
option->peer_max_concurrent_streams = val;
|
||||
}
|
||||
|
||||
void nghttp2_option_set_recv_client_preface(nghttp2_option *option, int val) {
|
||||
option->opt_set_mask |= NGHTTP2_OPT_RECV_CLIENT_PREFACE;
|
||||
option->recv_client_preface = val;
|
||||
void nghttp2_option_set_no_recv_client_magic(nghttp2_option *option, int val) {
|
||||
option->opt_set_mask |= NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC;
|
||||
option->no_recv_client_magic = val;
|
||||
}
|
||||
|
||||
void nghttp2_option_set_no_http_messaging(nghttp2_option *option, int val) {
|
||||
|
||||
@@ -57,8 +57,8 @@ typedef enum {
|
||||
* SETTINGS_MAX_CONCURRENT_STREAMS from the remote endpoint.
|
||||
*/
|
||||
NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS = 1 << 1,
|
||||
NGHTTP2_OPT_RECV_CLIENT_PREFACE = 1 << 2,
|
||||
NGHTTP2_OPT_NO_HTTP_MESSAGING = 1 << 3,
|
||||
NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC = 1 << 2,
|
||||
NGHTTP2_OPT_NO_HTTP_MESSAGING = 1 << 3
|
||||
} nghttp2_option_flag;
|
||||
|
||||
/**
|
||||
@@ -79,9 +79,9 @@ struct nghttp2_option {
|
||||
*/
|
||||
uint8_t no_auto_window_update;
|
||||
/**
|
||||
* NGHTTP2_OPT_RECV_CLIENT_PREFACE
|
||||
* NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC
|
||||
*/
|
||||
uint8_t recv_client_preface;
|
||||
uint8_t no_recv_client_magic;
|
||||
/**
|
||||
* NGHTTP2_OPT_NO_HTTP_MESSAGING
|
||||
*/
|
||||
|
||||
@@ -81,7 +81,7 @@ typedef enum {
|
||||
/* indicates that this GOAWAY is just a notification for graceful
|
||||
shutdown. No nghttp2_session.goaway_flags should be updated on
|
||||
the reaction to this frame. */
|
||||
NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE = 0x2,
|
||||
NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE = 0x2
|
||||
} nghttp2_goaway_aux_flag;
|
||||
|
||||
/* struct used for GOAWAY frame */
|
||||
|
||||
@@ -71,11 +71,13 @@ session_is_incoming_concurrent_streams_pending_max(nghttp2_session *session) {
|
||||
/*
|
||||
* Returns non-zero if |lib_error| is non-fatal error.
|
||||
*/
|
||||
static int is_non_fatal(int lib_error) {
|
||||
return lib_error < 0 && lib_error > NGHTTP2_ERR_FATAL;
|
||||
static int is_non_fatal(int lib_error_code) {
|
||||
return lib_error_code < 0 && lib_error_code > NGHTTP2_ERR_FATAL;
|
||||
}
|
||||
|
||||
int nghttp2_is_fatal(int lib_error) { return lib_error < NGHTTP2_ERR_FATAL; }
|
||||
int nghttp2_is_fatal(int lib_error_code) {
|
||||
return lib_error_code < NGHTTP2_ERR_FATAL;
|
||||
}
|
||||
|
||||
static int session_enforce_http_messaging(nghttp2_session *session) {
|
||||
return (session->opt_flags & NGHTTP2_OPTMASK_NO_HTTP_MESSAGING) == 0;
|
||||
@@ -303,9 +305,10 @@ static void active_outbound_item_reset(nghttp2_active_outbound_item *aob,
|
||||
aob->state = NGHTTP2_OB_POP_ITEM;
|
||||
}
|
||||
|
||||
/* This global variable exists for tests where we want to disable this
|
||||
check. */
|
||||
int nghttp2_enable_strict_first_settings_check = 1;
|
||||
/* The global variable for tests where we want to disable strict
|
||||
preface handling. */
|
||||
/* Specify NGHTTP2_EXTERN, so that we can test using Win build dll. */
|
||||
NGHTTP2_EXTERN int nghttp2_enable_strict_preface = 1;
|
||||
|
||||
static int session_new(nghttp2_session **session_ptr,
|
||||
const nghttp2_session_callbacks *callbacks,
|
||||
@@ -395,10 +398,10 @@ static int session_new(nghttp2_session **session_ptr,
|
||||
option->peer_max_concurrent_streams;
|
||||
}
|
||||
|
||||
if ((option->opt_set_mask & NGHTTP2_OPT_RECV_CLIENT_PREFACE) &&
|
||||
option->recv_client_preface) {
|
||||
if ((option->opt_set_mask & NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC) &&
|
||||
option->no_recv_client_magic) {
|
||||
|
||||
(*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_RECV_CLIENT_PREFACE;
|
||||
(*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC;
|
||||
}
|
||||
|
||||
if ((option->opt_set_mask & NGHTTP2_OPT_NO_HTTP_MESSAGING) &&
|
||||
@@ -413,17 +416,23 @@ static int session_new(nghttp2_session **session_ptr,
|
||||
|
||||
session_inbound_frame_reset(*session_ptr);
|
||||
|
||||
if (server &&
|
||||
((*session_ptr)->opt_flags & NGHTTP2_OPTMASK_RECV_CLIENT_PREFACE)) {
|
||||
|
||||
if (nghttp2_enable_strict_preface) {
|
||||
nghttp2_inbound_frame *iframe = &(*session_ptr)->iframe;
|
||||
|
||||
iframe->state = NGHTTP2_IB_READ_CLIENT_PREFACE;
|
||||
iframe->payloadleft = NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN;
|
||||
} else if (nghttp2_enable_strict_first_settings_check) {
|
||||
nghttp2_inbound_frame *iframe = &(*session_ptr)->iframe;
|
||||
if (server &&
|
||||
((*session_ptr)->opt_flags & NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC) ==
|
||||
0) {
|
||||
iframe->state = NGHTTP2_IB_READ_CLIENT_MAGIC;
|
||||
iframe->payloadleft = NGHTTP2_CLIENT_MAGIC_LEN;
|
||||
} else {
|
||||
iframe->state = NGHTTP2_IB_READ_FIRST_SETTINGS;
|
||||
}
|
||||
|
||||
iframe->state = NGHTTP2_IB_READ_FIRST_SETTINGS;
|
||||
if (!server) {
|
||||
(*session_ptr)->aob.state = NGHTTP2_OB_SEND_CLIENT_MAGIC;
|
||||
nghttp2_bufs_add(&(*session_ptr)->aob.framebufs, NGHTTP2_CLIENT_MAGIC,
|
||||
NGHTTP2_CLIENT_MAGIC_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -2852,6 +2861,25 @@ static ssize_t nghttp2_session_mem_send_internal(nghttp2_session *session,
|
||||
|
||||
break;
|
||||
}
|
||||
case NGHTTP2_OB_SEND_CLIENT_MAGIC: {
|
||||
size_t datalen;
|
||||
nghttp2_buf *buf;
|
||||
|
||||
buf = &framebufs->cur->buf;
|
||||
|
||||
if (buf->pos == buf->last) {
|
||||
DEBUGF(fprintf(stderr, "send: end transmission of client magic\n"));
|
||||
active_outbound_item_reset(aob, mem);
|
||||
break;
|
||||
}
|
||||
|
||||
*data_ptr = buf->pos;
|
||||
datalen = nghttp2_buf_len(buf);
|
||||
|
||||
buf->pos += datalen;
|
||||
|
||||
return datalen;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2866,14 +2894,16 @@ ssize_t nghttp2_session_mem_send(nghttp2_session *session,
|
||||
return len;
|
||||
}
|
||||
|
||||
/* We have to call session_after_frame_sent1 here to handle stream
|
||||
closure upon transmission of frames. Otherwise, END_STREAM may
|
||||
be reached to client before we call nghttp2_session_mem_send
|
||||
again and we may get exceeding number of incoming streams. */
|
||||
rv = session_after_frame_sent1(session);
|
||||
if (rv < 0) {
|
||||
assert(nghttp2_is_fatal(rv));
|
||||
return (ssize_t)rv;
|
||||
if (session->aob.item) {
|
||||
/* We have to call session_after_frame_sent1 here to handle stream
|
||||
closure upon transmission of frames. Otherwise, END_STREAM may
|
||||
be reached to client before we call nghttp2_session_mem_send
|
||||
again and we may get exceeding number of incoming streams. */
|
||||
rv = session_after_frame_sent1(session);
|
||||
if (rv < 0) {
|
||||
assert(nghttp2_is_fatal(rv));
|
||||
return (ssize_t)rv;
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
@@ -3006,18 +3036,40 @@ static int session_handle_frame_size_error(nghttp2_session *session,
|
||||
return nghttp2_session_terminate_session(session, NGHTTP2_FRAME_SIZE_ERROR);
|
||||
}
|
||||
|
||||
static int get_error_code_from_lib_error_code(int lib_error_code) {
|
||||
switch (lib_error_code) {
|
||||
case NGHTTP2_ERR_STREAM_CLOSED:
|
||||
return NGHTTP2_STREAM_CLOSED;
|
||||
case NGHTTP2_ERR_HEADER_COMP:
|
||||
return NGHTTP2_COMPRESSION_ERROR;
|
||||
case NGHTTP2_ERR_FRAME_SIZE_ERROR:
|
||||
return NGHTTP2_FRAME_SIZE_ERROR;
|
||||
case NGHTTP2_ERR_FLOW_CONTROL:
|
||||
return NGHTTP2_FLOW_CONTROL_ERROR;
|
||||
case NGHTTP2_ERR_REFUSED_STREAM:
|
||||
return NGHTTP2_REFUSED_STREAM;
|
||||
case NGHTTP2_ERR_PROTO:
|
||||
case NGHTTP2_ERR_HTTP_HEADER:
|
||||
case NGHTTP2_ERR_HTTP_MESSAGING:
|
||||
return NGHTTP2_PROTOCOL_ERROR;
|
||||
default:
|
||||
return NGHTTP2_INTERNAL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
static int session_handle_invalid_stream2(nghttp2_session *session,
|
||||
int32_t stream_id,
|
||||
nghttp2_frame *frame,
|
||||
uint32_t error_code) {
|
||||
int lib_error_code) {
|
||||
int rv;
|
||||
rv = nghttp2_session_add_rst_stream(session, stream_id, error_code);
|
||||
rv = nghttp2_session_add_rst_stream(
|
||||
session, stream_id, get_error_code_from_lib_error_code(lib_error_code));
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
if (session->callbacks.on_invalid_frame_recv_callback) {
|
||||
if (session->callbacks.on_invalid_frame_recv_callback(
|
||||
session, frame, error_code, session->user_data) != 0) {
|
||||
session, frame, lib_error_code, session->user_data) != 0) {
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
}
|
||||
@@ -3026,16 +3078,16 @@ static int session_handle_invalid_stream2(nghttp2_session *session,
|
||||
|
||||
static int session_handle_invalid_stream(nghttp2_session *session,
|
||||
nghttp2_frame *frame,
|
||||
uint32_t error_code) {
|
||||
int lib_error_code) {
|
||||
return session_handle_invalid_stream2(session, frame->hd.stream_id, frame,
|
||||
error_code);
|
||||
lib_error_code);
|
||||
}
|
||||
|
||||
static int session_inflate_handle_invalid_stream(nghttp2_session *session,
|
||||
nghttp2_frame *frame,
|
||||
uint32_t error_code) {
|
||||
int lib_error_code) {
|
||||
int rv;
|
||||
rv = session_handle_invalid_stream(session, frame, error_code);
|
||||
rv = session_handle_invalid_stream(session, frame, lib_error_code);
|
||||
if (nghttp2_is_fatal(rv)) {
|
||||
return rv;
|
||||
}
|
||||
@@ -3047,24 +3099,25 @@ static int session_inflate_handle_invalid_stream(nghttp2_session *session,
|
||||
*/
|
||||
static int session_handle_invalid_connection(nghttp2_session *session,
|
||||
nghttp2_frame *frame,
|
||||
uint32_t error_code,
|
||||
int lib_error_code,
|
||||
const char *reason) {
|
||||
if (session->callbacks.on_invalid_frame_recv_callback) {
|
||||
if (session->callbacks.on_invalid_frame_recv_callback(
|
||||
session, frame, error_code, session->user_data) != 0) {
|
||||
session, frame, lib_error_code, session->user_data) != 0) {
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
}
|
||||
return nghttp2_session_terminate_session_with_reason(session, error_code,
|
||||
reason);
|
||||
return nghttp2_session_terminate_session_with_reason(
|
||||
session, get_error_code_from_lib_error_code(lib_error_code), reason);
|
||||
}
|
||||
|
||||
static int session_inflate_handle_invalid_connection(nghttp2_session *session,
|
||||
nghttp2_frame *frame,
|
||||
uint32_t error_code,
|
||||
int lib_error_code,
|
||||
const char *reason) {
|
||||
int rv;
|
||||
rv = session_handle_invalid_connection(session, frame, error_code, reason);
|
||||
rv =
|
||||
session_handle_invalid_connection(session, frame, lib_error_code, reason);
|
||||
if (nghttp2_is_fatal(rv)) {
|
||||
return rv;
|
||||
}
|
||||
@@ -3131,12 +3184,12 @@ static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame,
|
||||
}
|
||||
if (proclen < 0) {
|
||||
if (session->iframe.state == NGHTTP2_IB_READ_HEADER_BLOCK) {
|
||||
if (stream && stream->state != NGHTTP2_STREAM_CLOSING) {
|
||||
if (subject_stream && subject_stream->state != NGHTTP2_STREAM_CLOSING) {
|
||||
/* Adding RST_STREAM here is very important. It prevents
|
||||
from invoking subsequent callbacks for the same stream
|
||||
ID. */
|
||||
rv = nghttp2_session_add_rst_stream(session, frame->hd.stream_id,
|
||||
NGHTTP2_COMPRESSION_ERROR);
|
||||
rv = nghttp2_session_add_rst_stream(
|
||||
session, subject_stream->stream_id, NGHTTP2_COMPRESSION_ERROR);
|
||||
|
||||
if (nghttp2_is_fatal(rv)) {
|
||||
return rv;
|
||||
@@ -3170,7 +3223,7 @@ static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame,
|
||||
|
||||
rv =
|
||||
session_handle_invalid_stream2(session, subject_stream->stream_id,
|
||||
frame, NGHTTP2_PROTOCOL_ERROR);
|
||||
frame, NGHTTP2_ERR_HTTP_HEADER);
|
||||
if (nghttp2_is_fatal(rv)) {
|
||||
return rv;
|
||||
}
|
||||
@@ -3345,7 +3398,7 @@ static int session_after_header_block_received(nghttp2_session *session) {
|
||||
call_cb = 0;
|
||||
|
||||
rv = session_handle_invalid_stream2(session, stream_id, frame,
|
||||
NGHTTP2_PROTOCOL_ERROR);
|
||||
NGHTTP2_ERR_HTTP_MESSAGING);
|
||||
if (nghttp2_is_fatal(rv)) {
|
||||
return rv;
|
||||
}
|
||||
@@ -3384,8 +3437,7 @@ int nghttp2_session_on_request_headers_received(nghttp2_session *session,
|
||||
nghttp2_stream *stream;
|
||||
if (frame->hd.stream_id == 0) {
|
||||
return session_inflate_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR,
|
||||
"request HEADERS: stream_id == 0");
|
||||
session, frame, NGHTTP2_ERR_PROTO, "request HEADERS: stream_id == 0");
|
||||
}
|
||||
|
||||
/* If client recieves idle stream from server, it is invalid
|
||||
@@ -3394,7 +3446,7 @@ int nghttp2_session_on_request_headers_received(nghttp2_session *session,
|
||||
if (!session->server) {
|
||||
if (session_detect_idle_stream(session, frame->hd.stream_id)) {
|
||||
return session_inflate_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR,
|
||||
session, frame, NGHTTP2_ERR_PROTO,
|
||||
"request HEADERS: client received request");
|
||||
}
|
||||
|
||||
@@ -3411,7 +3463,7 @@ int nghttp2_session_on_request_headers_received(nghttp2_session *session,
|
||||
just ignore HEADERS for now. */
|
||||
if (session_detect_idle_stream(session, frame->hd.stream_id)) {
|
||||
return session_inflate_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR,
|
||||
session, frame, NGHTTP2_ERR_PROTO,
|
||||
"request HEADERS: invalid stream_id");
|
||||
}
|
||||
|
||||
@@ -3426,19 +3478,18 @@ int nghttp2_session_on_request_headers_received(nghttp2_session *session,
|
||||
|
||||
if (session_is_incoming_concurrent_streams_max(session)) {
|
||||
return session_inflate_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR,
|
||||
session, frame, NGHTTP2_ERR_PROTO,
|
||||
"request HEADERS: max concurrent streams exceeded");
|
||||
}
|
||||
|
||||
if (frame->headers.pri_spec.stream_id == frame->hd.stream_id) {
|
||||
return session_inflate_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR,
|
||||
"request HEADERS: depend on itself");
|
||||
session, frame, NGHTTP2_ERR_PROTO, "request HEADERS: depend on itself");
|
||||
}
|
||||
|
||||
if (session_is_incoming_concurrent_streams_pending_max(session)) {
|
||||
return session_inflate_handle_invalid_stream(session, frame,
|
||||
NGHTTP2_REFUSED_STREAM);
|
||||
NGHTTP2_ERR_REFUSED_STREAM);
|
||||
}
|
||||
|
||||
stream = nghttp2_session_open_stream(
|
||||
@@ -3465,8 +3516,7 @@ int nghttp2_session_on_response_headers_received(nghttp2_session *session,
|
||||
nghttp2_session_is_my_stream_id(session, frame->hd.stream_id));
|
||||
if (frame->hd.stream_id == 0) {
|
||||
return session_inflate_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR,
|
||||
"response HEADERS: stream_id == 0");
|
||||
session, frame, NGHTTP2_ERR_PROTO, "response HEADERS: stream_id == 0");
|
||||
}
|
||||
if (stream->shut_flags & NGHTTP2_SHUT_RD) {
|
||||
/* half closed (remote): from the spec:
|
||||
@@ -3476,7 +3526,7 @@ int nghttp2_session_on_response_headers_received(nghttp2_session *session,
|
||||
5.4.2) of type STREAM_CLOSED.
|
||||
*/
|
||||
return session_inflate_handle_invalid_stream(session, frame,
|
||||
NGHTTP2_STREAM_CLOSED);
|
||||
NGHTTP2_ERR_STREAM_CLOSED);
|
||||
}
|
||||
stream->state = NGHTTP2_STREAM_OPENED;
|
||||
rv = session_call_on_begin_headers(session, frame);
|
||||
@@ -3493,7 +3543,7 @@ int nghttp2_session_on_push_response_headers_received(nghttp2_session *session,
|
||||
assert(stream->state == NGHTTP2_STREAM_RESERVED);
|
||||
if (frame->hd.stream_id == 0) {
|
||||
return session_inflate_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR,
|
||||
session, frame, NGHTTP2_ERR_PROTO,
|
||||
"push response HEADERS: stream_id == 0");
|
||||
}
|
||||
if (session->goaway_flags) {
|
||||
@@ -3503,12 +3553,12 @@ int nghttp2_session_on_push_response_headers_received(nghttp2_session *session,
|
||||
|
||||
if (session_is_incoming_concurrent_streams_max(session)) {
|
||||
return session_inflate_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR,
|
||||
session, frame, NGHTTP2_ERR_PROTO,
|
||||
"push response HEADERS: max concurrent streams exceeded");
|
||||
}
|
||||
if (session_is_incoming_concurrent_streams_pending_max(session)) {
|
||||
return session_inflate_handle_invalid_stream(session, frame,
|
||||
NGHTTP2_REFUSED_STREAM);
|
||||
NGHTTP2_ERR_REFUSED_STREAM);
|
||||
}
|
||||
|
||||
nghttp2_stream_promise_fulfilled(stream);
|
||||
@@ -3526,7 +3576,7 @@ int nghttp2_session_on_headers_received(nghttp2_session *session,
|
||||
int rv = 0;
|
||||
if (frame->hd.stream_id == 0) {
|
||||
return session_inflate_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR, "HEADERS: stream_id == 0");
|
||||
session, frame, NGHTTP2_ERR_PROTO, "HEADERS: stream_id == 0");
|
||||
}
|
||||
if (stream->state == NGHTTP2_STREAM_RESERVED) {
|
||||
/* reserved. The valid push response HEADERS is processed by
|
||||
@@ -3534,7 +3584,7 @@ int nghttp2_session_on_headers_received(nghttp2_session *session,
|
||||
generic HEADERS is called invalid cases for HEADERS against
|
||||
reserved state. */
|
||||
return session_inflate_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR, "HEADERS: stream in reserved");
|
||||
session, frame, NGHTTP2_ERR_PROTO, "HEADERS: stream in reserved");
|
||||
}
|
||||
if ((stream->shut_flags & NGHTTP2_SHUT_RD)) {
|
||||
/* half closed (remote): from the spec:
|
||||
@@ -3544,7 +3594,7 @@ int nghttp2_session_on_headers_received(nghttp2_session *session,
|
||||
5.4.2) of type STREAM_CLOSED.
|
||||
*/
|
||||
return session_inflate_handle_invalid_stream(session, frame,
|
||||
NGHTTP2_STREAM_CLOSED);
|
||||
NGHTTP2_ERR_STREAM_CLOSED);
|
||||
}
|
||||
if (nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) {
|
||||
if (stream->state == NGHTTP2_STREAM_OPENED) {
|
||||
@@ -3560,7 +3610,7 @@ int nghttp2_session_on_headers_received(nghttp2_session *session,
|
||||
return NGHTTP2_ERR_IGN_HEADER_BLOCK;
|
||||
} else {
|
||||
return session_inflate_handle_invalid_stream(session, frame,
|
||||
NGHTTP2_PROTOCOL_ERROR);
|
||||
NGHTTP2_ERR_PROTO);
|
||||
}
|
||||
}
|
||||
/* If this is remote peer initiated stream, it is OK unless it
|
||||
@@ -3620,8 +3670,8 @@ int nghttp2_session_on_priority_received(nghttp2_session *session,
|
||||
nghttp2_stream *stream;
|
||||
|
||||
if (frame->hd.stream_id == 0) {
|
||||
return session_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR, "PRIORITY: stream_id == 0");
|
||||
return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
|
||||
"PRIORITY: stream_id == 0");
|
||||
}
|
||||
|
||||
if (!session->server) {
|
||||
@@ -3672,14 +3722,14 @@ int nghttp2_session_on_rst_stream_received(nghttp2_session *session,
|
||||
int rv;
|
||||
nghttp2_stream *stream;
|
||||
if (frame->hd.stream_id == 0) {
|
||||
return session_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR, "RST_STREAM: stream_id == 0");
|
||||
return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
|
||||
"RST_STREAM: stream_id == 0");
|
||||
}
|
||||
stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
|
||||
if (!stream) {
|
||||
if (session_detect_idle_stream(session, frame->hd.stream_id)) {
|
||||
return session_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR, "RST_STREAM: stream in idle");
|
||||
session, frame, NGHTTP2_ERR_PROTO, "RST_STREAM: stream in idle");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3899,18 +3949,18 @@ int nghttp2_session_on_settings_received(nghttp2_session *session,
|
||||
mem = &session->mem;
|
||||
|
||||
if (frame->hd.stream_id != 0) {
|
||||
return session_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR, "SETTINGS: stream_id != 0");
|
||||
return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
|
||||
"SETTINGS: stream_id != 0");
|
||||
}
|
||||
if (frame->hd.flags & NGHTTP2_FLAG_ACK) {
|
||||
if (frame->settings.niv != 0) {
|
||||
return session_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_FRAME_SIZE_ERROR,
|
||||
session, frame, NGHTTP2_ERR_FRAME_SIZE_ERROR,
|
||||
"SETTINGS: ACK and payload != 0");
|
||||
}
|
||||
if (session->inflight_niv == -1) {
|
||||
return session_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR, "SETTINGS: unexpected ACK");
|
||||
session, frame, NGHTTP2_ERR_PROTO, "SETTINGS: unexpected ACK");
|
||||
}
|
||||
rv = nghttp2_session_update_local_settings(session, session->inflight_iv,
|
||||
session->inflight_niv);
|
||||
@@ -3918,15 +3968,10 @@ int nghttp2_session_on_settings_received(nghttp2_session *session,
|
||||
session->inflight_iv = NULL;
|
||||
session->inflight_niv = -1;
|
||||
if (rv != 0) {
|
||||
uint32_t error_code = NGHTTP2_INTERNAL_ERROR;
|
||||
if (nghttp2_is_fatal(rv)) {
|
||||
return rv;
|
||||
}
|
||||
if (rv == NGHTTP2_ERR_HEADER_COMP) {
|
||||
error_code = NGHTTP2_COMPRESSION_ERROR;
|
||||
}
|
||||
return session_handle_invalid_connection(session, frame, error_code,
|
||||
NULL);
|
||||
return session_handle_invalid_connection(session, frame, rv, NULL);
|
||||
}
|
||||
return session_call_on_frame_received(session, frame);
|
||||
}
|
||||
@@ -3939,7 +3984,7 @@ int nghttp2_session_on_settings_received(nghttp2_session *session,
|
||||
|
||||
if (entry->value > NGHTTP2_MAX_HEADER_TABLE_SIZE) {
|
||||
return session_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_COMPRESSION_ERROR,
|
||||
session, frame, NGHTTP2_ERR_HEADER_COMP,
|
||||
"SETTINGS: too large SETTINGS_HEADER_TABLE_SIZE");
|
||||
}
|
||||
|
||||
@@ -3950,7 +3995,7 @@ int nghttp2_session_on_settings_received(nghttp2_session *session,
|
||||
return rv;
|
||||
} else {
|
||||
return session_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_COMPRESSION_ERROR, NULL);
|
||||
session, frame, NGHTTP2_ERR_HEADER_COMP, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3961,13 +4006,13 @@ int nghttp2_session_on_settings_received(nghttp2_session *session,
|
||||
|
||||
if (entry->value != 0 && entry->value != 1) {
|
||||
return session_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR,
|
||||
session, frame, NGHTTP2_ERR_PROTO,
|
||||
"SETTINGS: invalid SETTINGS_ENBLE_PUSH");
|
||||
}
|
||||
|
||||
if (!session->server && entry->value != 0) {
|
||||
return session_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR,
|
||||
session, frame, NGHTTP2_ERR_PROTO,
|
||||
"SETTINGS: server attempted to enable push");
|
||||
}
|
||||
|
||||
@@ -3985,7 +4030,7 @@ int nghttp2_session_on_settings_received(nghttp2_session *session,
|
||||
/* Check that initial_window_size < (1u << 31) */
|
||||
if (entry->value > NGHTTP2_MAX_WINDOW_SIZE) {
|
||||
return session_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_FLOW_CONTROL_ERROR,
|
||||
session, frame, NGHTTP2_ERR_FLOW_CONTROL,
|
||||
"SETTINGS: too large SETTINGS_INITIAL_WINDOW_SIZE");
|
||||
}
|
||||
|
||||
@@ -3997,7 +4042,7 @@ int nghttp2_session_on_settings_received(nghttp2_session *session,
|
||||
|
||||
if (rv != 0) {
|
||||
return session_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_FLOW_CONTROL_ERROR, NULL);
|
||||
session, frame, NGHTTP2_ERR_FLOW_CONTROL, NULL);
|
||||
}
|
||||
|
||||
session->remote_settings.initial_window_size = entry->value;
|
||||
@@ -4008,7 +4053,7 @@ int nghttp2_session_on_settings_received(nghttp2_session *session,
|
||||
if (entry->value < NGHTTP2_MAX_FRAME_SIZE_MIN ||
|
||||
entry->value > NGHTTP2_MAX_FRAME_SIZE_MAX) {
|
||||
return session_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR,
|
||||
session, frame, NGHTTP2_ERR_PROTO,
|
||||
"SETTINGS: invalid SETTINGS_MAX_FRAME_SIZE");
|
||||
}
|
||||
|
||||
@@ -4032,7 +4077,7 @@ int nghttp2_session_on_settings_received(nghttp2_session *session,
|
||||
}
|
||||
|
||||
return session_handle_invalid_connection(session, frame,
|
||||
NGHTTP2_INTERNAL_ERROR, NULL);
|
||||
NGHTTP2_ERR_INTERNAL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4085,11 +4130,11 @@ int nghttp2_session_on_push_promise_received(nghttp2_session *session,
|
||||
|
||||
if (frame->hd.stream_id == 0) {
|
||||
return session_inflate_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR, "PUSH_PROMISE: stream_id == 0");
|
||||
session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: stream_id == 0");
|
||||
}
|
||||
if (session->server || session->local_settings.enable_push == 0) {
|
||||
return session_inflate_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR, "PUSH_PROMISE: push disabled");
|
||||
session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: push disabled");
|
||||
}
|
||||
if (session->goaway_flags) {
|
||||
/* We just dicard PUSH_PROMISE after GOAWAY is sent or
|
||||
@@ -4099,8 +4144,7 @@ int nghttp2_session_on_push_promise_received(nghttp2_session *session,
|
||||
|
||||
if (!nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) {
|
||||
return session_inflate_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR,
|
||||
"PUSH_PROMISE: invalid stream_id");
|
||||
session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: invalid stream_id");
|
||||
}
|
||||
|
||||
if (!session_is_new_peer_stream_id(session,
|
||||
@@ -4109,7 +4153,7 @@ int nghttp2_session_on_push_promise_received(nghttp2_session *session,
|
||||
illegal stream ID is subject to a connection error of type
|
||||
PROTOCOL_ERROR. */
|
||||
return session_inflate_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR,
|
||||
session, frame, NGHTTP2_ERR_PROTO,
|
||||
"PUSH_PROMISE: invalid promised_stream_id");
|
||||
}
|
||||
session->last_recv_stream_id = frame->push_promise.promised_stream_id;
|
||||
@@ -4119,8 +4163,7 @@ int nghttp2_session_on_push_promise_received(nghttp2_session *session,
|
||||
if (!stream) {
|
||||
if (session_detect_idle_stream(session, frame->hd.stream_id)) {
|
||||
return session_inflate_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR,
|
||||
"PUSH_PROMISE: stream in idle");
|
||||
session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: stream in idle");
|
||||
}
|
||||
}
|
||||
rv = nghttp2_session_add_rst_stream(session,
|
||||
@@ -4189,8 +4232,8 @@ int nghttp2_session_on_ping_received(nghttp2_session *session,
|
||||
nghttp2_frame *frame) {
|
||||
int rv = 0;
|
||||
if (frame->hd.stream_id != 0) {
|
||||
return session_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR, "PING: stream_id != 0");
|
||||
return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
|
||||
"PING: stream_id != 0");
|
||||
}
|
||||
if ((frame->hd.flags & NGHTTP2_FLAG_ACK) == 0 &&
|
||||
!session_is_closing(session)) {
|
||||
@@ -4219,8 +4262,8 @@ int nghttp2_session_on_goaway_received(nghttp2_session *session,
|
||||
int rv;
|
||||
|
||||
if (frame->hd.stream_id != 0) {
|
||||
return session_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR, "GOAWAY: stream_id != 0");
|
||||
return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
|
||||
"GOAWAY: stream_id != 0");
|
||||
}
|
||||
/* Spec says Endpoints MUST NOT increase the value they send in the
|
||||
last stream identifier. */
|
||||
@@ -4228,8 +4271,7 @@ int nghttp2_session_on_goaway_received(nghttp2_session *session,
|
||||
!nghttp2_session_is_my_stream_id(session,
|
||||
frame->goaway.last_stream_id)) ||
|
||||
session->remote_last_stream_id < frame->goaway.last_stream_id) {
|
||||
return session_handle_invalid_connection(session, frame,
|
||||
NGHTTP2_PROTOCOL_ERROR,
|
||||
return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
|
||||
"GOAWAY: invalid last_stream_id");
|
||||
}
|
||||
|
||||
@@ -4265,14 +4307,14 @@ session_on_connection_window_update_received(nghttp2_session *session,
|
||||
nghttp2_frame *frame) {
|
||||
/* Handle connection-level flow control */
|
||||
if (frame->window_update.window_size_increment == 0) {
|
||||
return session_handle_invalid_connection(session, frame,
|
||||
NGHTTP2_PROTOCOL_ERROR, NULL);
|
||||
return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
|
||||
NULL);
|
||||
}
|
||||
|
||||
if (NGHTTP2_MAX_WINDOW_SIZE - frame->window_update.window_size_increment <
|
||||
session->remote_window_size) {
|
||||
return session_handle_invalid_connection(session, frame,
|
||||
NGHTTP2_FLOW_CONTROL_ERROR, NULL);
|
||||
NGHTTP2_ERR_FLOW_CONTROL, NULL);
|
||||
}
|
||||
session->remote_window_size += frame->window_update.window_size_increment;
|
||||
|
||||
@@ -4286,25 +4328,22 @@ static int session_on_stream_window_update_received(nghttp2_session *session,
|
||||
stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
|
||||
if (!stream) {
|
||||
if (session_detect_idle_stream(session, frame->hd.stream_id)) {
|
||||
return session_handle_invalid_connection(session, frame,
|
||||
NGHTTP2_PROTOCOL_ERROR,
|
||||
"WINDOW_UPDATE to idle stream");
|
||||
return session_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_ERR_PROTO, "WINDOW_UPDATE to idle stream");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (state_reserved_remote(session, stream)) {
|
||||
return session_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR,
|
||||
"WINDOW_UPADATE to reserved stream");
|
||||
session, frame, NGHTTP2_ERR_PROTO, "WINDOW_UPADATE to reserved stream");
|
||||
}
|
||||
if (frame->window_update.window_size_increment == 0) {
|
||||
return session_handle_invalid_stream(session, frame,
|
||||
NGHTTP2_PROTOCOL_ERROR);
|
||||
return session_handle_invalid_stream(session, frame, NGHTTP2_ERR_PROTO);
|
||||
}
|
||||
if (NGHTTP2_MAX_WINDOW_SIZE - frame->window_update.window_size_increment <
|
||||
stream->remote_window_size) {
|
||||
return session_handle_invalid_stream(session, frame,
|
||||
NGHTTP2_FLOW_CONTROL_ERROR);
|
||||
NGHTTP2_ERR_FLOW_CONTROL);
|
||||
}
|
||||
stream->remote_window_size += frame->window_update.window_size_increment;
|
||||
|
||||
@@ -4340,18 +4379,6 @@ static int session_process_window_update_frame(nghttp2_session *session) {
|
||||
return nghttp2_session_on_window_update_received(session, frame);
|
||||
}
|
||||
|
||||
/* static int get_error_code_from_lib_error_code(int lib_error_code) */
|
||||
/* { */
|
||||
/* switch(lib_error_code) { */
|
||||
/* case NGHTTP2_ERR_HEADER_COMP: */
|
||||
/* return NGHTTP2_COMPRESSION_ERROR; */
|
||||
/* case NGHTTP2_ERR_FRAME_SIZE_ERROR: */
|
||||
/* return NGHTTP2_FRAME_SIZE_ERROR; */
|
||||
/* default: */
|
||||
/* return NGHTTP2_PROTOCOL_ERROR; */
|
||||
/* } */
|
||||
/* } */
|
||||
|
||||
int nghttp2_session_on_data_received(nghttp2_session *session,
|
||||
nghttp2_frame *frame) {
|
||||
int rv = 0;
|
||||
@@ -4789,14 +4816,13 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
|
||||
|
||||
for (;;) {
|
||||
switch (iframe->state) {
|
||||
case NGHTTP2_IB_READ_CLIENT_PREFACE:
|
||||
case NGHTTP2_IB_READ_CLIENT_MAGIC:
|
||||
readlen = nghttp2_min(inlen, iframe->payloadleft);
|
||||
|
||||
if (memcmp(NGHTTP2_CLIENT_CONNECTION_PREFACE +
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN -
|
||||
if (memcmp(NGHTTP2_CLIENT_MAGIC + NGHTTP2_CLIENT_MAGIC_LEN -
|
||||
iframe->payloadleft,
|
||||
in, readlen) != 0) {
|
||||
return NGHTTP2_ERR_BAD_PREFACE;
|
||||
return NGHTTP2_ERR_BAD_CLIENT_MAGIC;
|
||||
}
|
||||
|
||||
iframe->payloadleft -= readlen;
|
||||
@@ -5734,51 +5760,60 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
|
||||
if (nghttp2_is_fatal(rv)) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
data_readlen = inbound_frame_effective_readlen(
|
||||
iframe, iframe->payloadleft, readlen);
|
||||
data_readlen = inbound_frame_effective_readlen(
|
||||
iframe, iframe->payloadleft, readlen);
|
||||
|
||||
padlen = readlen - data_readlen;
|
||||
padlen = readlen - data_readlen;
|
||||
|
||||
if (padlen > 0) {
|
||||
/* Padding is considered as "consumed" immediately */
|
||||
rv = nghttp2_session_consume(session, iframe->frame.hd.stream_id,
|
||||
padlen);
|
||||
if (padlen > 0) {
|
||||
/* Padding is considered as "consumed" immediately */
|
||||
rv = nghttp2_session_consume(session, iframe->frame.hd.stream_id,
|
||||
padlen);
|
||||
|
||||
if (nghttp2_is_fatal(rv)) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
DEBUGF(fprintf(stderr, "recv: data_readlen=%zd\n", data_readlen));
|
||||
|
||||
if (data_readlen > 0) {
|
||||
if (session_enforce_http_messaging(session)) {
|
||||
if (nghttp2_http_on_data_chunk(stream, data_readlen) != 0) {
|
||||
rv = nghttp2_session_add_rst_stream(session,
|
||||
iframe->frame.hd.stream_id,
|
||||
NGHTTP2_PROTOCOL_ERROR);
|
||||
if (nghttp2_is_fatal(rv)) {
|
||||
return rv;
|
||||
}
|
||||
busy = 1;
|
||||
iframe->state = NGHTTP2_IB_IGN_DATA;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (session->callbacks.on_data_chunk_recv_callback) {
|
||||
rv = session->callbacks.on_data_chunk_recv_callback(
|
||||
session, iframe->frame.hd.flags, iframe->frame.hd.stream_id,
|
||||
in - readlen, data_readlen, session->user_data);
|
||||
if (rv == NGHTTP2_ERR_PAUSE) {
|
||||
return in - first;
|
||||
}
|
||||
|
||||
if (nghttp2_is_fatal(rv)) {
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) {
|
||||
/* stream was closed or does not exist. Consume all data
|
||||
for connection immediately here */
|
||||
rv = session_update_connection_consumed_size(session, readlen);
|
||||
|
||||
if (nghttp2_is_fatal(rv)) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
DEBUGF(fprintf(stderr, "recv: data_readlen=%zd\n", data_readlen));
|
||||
|
||||
if (stream && data_readlen > 0) {
|
||||
if (session_enforce_http_messaging(session)) {
|
||||
if (nghttp2_http_on_data_chunk(stream, data_readlen) != 0) {
|
||||
rv = nghttp2_session_add_rst_stream(
|
||||
session, iframe->frame.hd.stream_id, NGHTTP2_PROTOCOL_ERROR);
|
||||
if (nghttp2_is_fatal(rv)) {
|
||||
return rv;
|
||||
}
|
||||
busy = 1;
|
||||
iframe->state = NGHTTP2_IB_IGN_DATA;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (session->callbacks.on_data_chunk_recv_callback) {
|
||||
rv = session->callbacks.on_data_chunk_recv_callback(
|
||||
session, iframe->frame.hd.flags, iframe->frame.hd.stream_id,
|
||||
in - readlen, data_readlen, session->user_data);
|
||||
if (rv == NGHTTP2_ERR_PAUSE) {
|
||||
return in - first;
|
||||
}
|
||||
|
||||
if (nghttp2_is_fatal(rv)) {
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (iframe->payloadleft) {
|
||||
|
||||
@@ -46,14 +46,15 @@
|
||||
*/
|
||||
typedef enum {
|
||||
NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE = 1 << 0,
|
||||
NGHTTP2_OPTMASK_RECV_CLIENT_PREFACE = 1 << 1,
|
||||
NGHTTP2_OPTMASK_NO_HTTP_MESSAGING = 1 << 2,
|
||||
NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC = 1 << 1,
|
||||
NGHTTP2_OPTMASK_NO_HTTP_MESSAGING = 1 << 2
|
||||
} nghttp2_optmask;
|
||||
|
||||
typedef enum {
|
||||
NGHTTP2_OB_POP_ITEM,
|
||||
NGHTTP2_OB_SEND_DATA,
|
||||
NGHTTP2_OB_SEND_NO_COPY
|
||||
NGHTTP2_OB_SEND_NO_COPY,
|
||||
NGHTTP2_OB_SEND_CLIENT_MAGIC
|
||||
} nghttp2_outbound_state;
|
||||
|
||||
typedef struct {
|
||||
@@ -69,7 +70,7 @@ typedef struct {
|
||||
/* Internal state when receiving incoming frame */
|
||||
typedef enum {
|
||||
/* Receiving frame header */
|
||||
NGHTTP2_IB_READ_CLIENT_PREFACE,
|
||||
NGHTTP2_IB_READ_CLIENT_MAGIC,
|
||||
NGHTTP2_IB_READ_FIRST_SETTINGS,
|
||||
NGHTTP2_IB_READ_HEAD,
|
||||
NGHTTP2_IB_READ_NBYTE,
|
||||
@@ -84,7 +85,7 @@ typedef enum {
|
||||
NGHTTP2_IB_READ_PAD_DATA,
|
||||
NGHTTP2_IB_READ_DATA,
|
||||
NGHTTP2_IB_IGN_DATA,
|
||||
NGHTTP2_IB_IGN_ALL,
|
||||
NGHTTP2_IB_IGN_ALL
|
||||
} nghttp2_inbound_state;
|
||||
|
||||
#define NGHTTP2_INBOUND_NUM_IV 7
|
||||
@@ -136,7 +137,7 @@ typedef enum {
|
||||
/* Flag means GOAWAY was sent */
|
||||
NGHTTP2_GOAWAY_SENT = 0x4,
|
||||
/* Flag means GOAWAY was received */
|
||||
NGHTTP2_GOAWAY_RECV = 0x8,
|
||||
NGHTTP2_GOAWAY_RECV = 0x8
|
||||
} nghttp2_goaway_flag;
|
||||
|
||||
struct nghttp2_session {
|
||||
|
||||
@@ -202,9 +202,9 @@ static void stream_update_dep_effective_weight(nghttp2_stream *stream) {
|
||||
if (si->dpri != NGHTTP2_STREAM_DPRI_REST) {
|
||||
si->effective_weight =
|
||||
nghttp2_stream_dep_distributed_effective_weight(stream, si->weight);
|
||||
}
|
||||
|
||||
stream_update_dep_effective_weight(si);
|
||||
stream_update_dep_effective_weight(si);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -311,7 +311,6 @@ static int stream_update_dep_queue_top(nghttp2_stream *stream,
|
||||
*/
|
||||
static int stream_update_dep_sum_norest_weight(nghttp2_stream *stream) {
|
||||
nghttp2_stream *si;
|
||||
int rv;
|
||||
|
||||
stream->sum_norest_weight = 0;
|
||||
|
||||
@@ -323,17 +322,14 @@ static int stream_update_dep_sum_norest_weight(nghttp2_stream *stream) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
rv = 0;
|
||||
|
||||
for (si = stream->dep_next; si; si = si->sib_next) {
|
||||
|
||||
if (stream_update_dep_sum_norest_weight(si)) {
|
||||
rv = 1;
|
||||
stream->sum_norest_weight += si->weight;
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
return stream->sum_norest_weight > 0;
|
||||
}
|
||||
|
||||
static int stream_update_dep_on_attach_item(nghttp2_stream *stream,
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
/*
|
||||
* Maximum number of streams in one dependency tree.
|
||||
*/
|
||||
#define NGHTTP2_MAX_DEP_TREE_LENGTH 100
|
||||
#define NGHTTP2_MAX_DEP_TREE_LENGTH 120
|
||||
|
||||
/*
|
||||
* If local peer is stream initiator:
|
||||
@@ -133,7 +133,7 @@ typedef enum {
|
||||
/* "http" or "https" scheme */
|
||||
NGHTTP2_HTTP_FLAG_SCHEME_HTTP = 1 << 12,
|
||||
/* set if final response is expected */
|
||||
NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE = 1 << 13,
|
||||
NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE = 1 << 13
|
||||
} nghttp2_http_flag;
|
||||
|
||||
typedef enum {
|
||||
|
||||
@@ -376,15 +376,6 @@ int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nghttp2_submit_altsvc(nghttp2_session *session _U_, uint8_t flags _U_,
|
||||
int32_t stream_id _U_, uint32_t max_age _U_,
|
||||
uint16_t port _U_, const uint8_t *protocol_id _U_,
|
||||
size_t protocol_id_len _U_, const uint8_t *host _U_,
|
||||
size_t host_len _U_, const uint8_t *origin _U_,
|
||||
size_t origin_len _U_) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t set_request_flags(const nghttp2_priority_spec *pri_spec,
|
||||
const nghttp2_data_provider *data_prd) {
|
||||
uint8_t flags = NGHTTP2_FLAG_NONE;
|
||||
|
||||
@@ -36,7 +36,8 @@ install-exec-local:
|
||||
$(PYTHON) setup.py install --prefix=$(DESTDIR)$(prefix)
|
||||
|
||||
uninstall-local:
|
||||
rm -rf $(DESTDIR)$(libdir)/python*/site-packages/*nghttp2*
|
||||
rm -f $(DESTDIR)$(libdir)/python*/site-packages/nghttp2.so
|
||||
rm -f $(DESTDIR)$(libdir)/python*/site-packages/python_nghttp2-*.egg-info
|
||||
|
||||
clean-local:
|
||||
$(PYTHON) setup.py clean --all
|
||||
|
||||
@@ -698,6 +698,7 @@ cdef class _HTTP2SessionCoreBase:
|
||||
handler.stream_id = stream_id
|
||||
handler.http2 = self
|
||||
handler.remote_address = self._get_remote_address()
|
||||
handler.client_certificate = self._get_client_certificate()
|
||||
self.handlers.add(handler)
|
||||
|
||||
def _rst_stream(self, stream_id,
|
||||
@@ -713,6 +714,13 @@ cdef class _HTTP2SessionCoreBase:
|
||||
def _get_remote_address(self):
|
||||
return self.transport.get_extra_info('peername')
|
||||
|
||||
def _get_client_certificate(self):
|
||||
sock = self.transport.get_extra_info('socket')
|
||||
try:
|
||||
return sock.getpeercert()
|
||||
except AttributeError:
|
||||
return None
|
||||
|
||||
def _start_settings_timer(self):
|
||||
loop = asyncio.get_event_loop()
|
||||
self.settings_timer = loop.call_later(self.SETTINGS_TIMEOUT,
|
||||
@@ -875,6 +883,11 @@ cdef class _HTTP2SessionCore(_HTTP2SessionCoreBase):
|
||||
|
||||
return promised_handler
|
||||
|
||||
def connection_lost(self):
|
||||
for handler in self.handlers:
|
||||
handler.on_close(cnghttp2.NGHTTP2_INTERNAL_ERROR)
|
||||
self.handlers = set()
|
||||
|
||||
cdef class _HTTP2ClientSessionCore(_HTTP2SessionCoreBase):
|
||||
def __cinit__(self, *args, **kwargs):
|
||||
cdef cnghttp2.nghttp2_session_callbacks *callbacks
|
||||
@@ -1030,6 +1043,9 @@ if asyncio:
|
||||
Contains a tuple of the form (host, port) referring to the client's
|
||||
address.
|
||||
|
||||
client_certificate
|
||||
May contain the client certifcate in its non-binary form
|
||||
|
||||
stream_id
|
||||
Stream ID of this stream
|
||||
|
||||
@@ -1058,6 +1074,8 @@ if asyncio:
|
||||
self.http2 = http2
|
||||
# address of the client
|
||||
self.remote_address = self.http2._get_remote_address()
|
||||
# certificate of the client
|
||||
self._client_certificate = self.http2._get_client_certificate()
|
||||
# :scheme header field in request
|
||||
self.scheme = None
|
||||
# :method header field in request
|
||||
@@ -1075,6 +1093,10 @@ if asyncio:
|
||||
def client_address(self):
|
||||
return self.remote_address
|
||||
|
||||
@property
|
||||
def client_certificate(self):
|
||||
return self._client_certificate
|
||||
|
||||
def on_headers(self):
|
||||
|
||||
'''Called when request HEADERS is arrived.
|
||||
@@ -1237,9 +1259,11 @@ if asyncio:
|
||||
logging.info('connection_made, address:%s, port:%s', address[0], address[1])
|
||||
|
||||
self.transport = transport
|
||||
self.connection_header = cnghttp2.NGHTTP2_CLIENT_CONNECTION_PREFACE
|
||||
sock = self.transport.get_extra_info('socket')
|
||||
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
||||
try:
|
||||
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
||||
except OSError as e:
|
||||
logging.info('failed to set tcp-nodelay: %s', str(e))
|
||||
ssl_ctx = self.transport.get_extra_info('sslcontext')
|
||||
if ssl_ctx:
|
||||
protocol = sock.selected_npn_protocol()
|
||||
@@ -1247,35 +1271,24 @@ if asyncio:
|
||||
if protocol.encode('utf-8') != \
|
||||
cnghttp2.NGHTTP2_PROTO_VERSION_ID:
|
||||
self.transport.abort()
|
||||
return
|
||||
try:
|
||||
self.http2 = _HTTP2SessionCore\
|
||||
(self.transport,
|
||||
self.RequestHandlerClass)
|
||||
except Exception as err:
|
||||
sys.stderr.write(traceback.format_exc())
|
||||
self.transport.abort()
|
||||
return
|
||||
|
||||
|
||||
def connection_lost(self, exc):
|
||||
logging.info('connection_lost')
|
||||
if self.http2:
|
||||
self.http2.connection_lost()
|
||||
self.http2 = None
|
||||
|
||||
def data_received(self, data):
|
||||
nread = min(len(data), len(self.connection_header))
|
||||
|
||||
if self.connection_header.startswith(data[:nread]):
|
||||
data = data[nread:]
|
||||
self.connection_header = self.connection_header[nread:]
|
||||
if len(self.connection_header) == 0:
|
||||
try:
|
||||
self.http2 = _HTTP2SessionCore\
|
||||
(self.transport,
|
||||
self.RequestHandlerClass)
|
||||
except Exception as err:
|
||||
sys.stderr.write(traceback.format_exc())
|
||||
self.transport.abort()
|
||||
return
|
||||
|
||||
self.data_received = self.data_received2
|
||||
self.resume_writing = self.resume_writing2
|
||||
self.data_received(data)
|
||||
else:
|
||||
self.transport.abort()
|
||||
|
||||
def data_received2(self, data):
|
||||
try:
|
||||
self.http2.data_received(data)
|
||||
except Exception as err:
|
||||
@@ -1283,7 +1296,7 @@ if asyncio:
|
||||
self.transport.close()
|
||||
return
|
||||
|
||||
def resume_writing2(self):
|
||||
def resume_writing(self):
|
||||
try:
|
||||
self.http2.send_data()
|
||||
except Exception as err:
|
||||
@@ -1350,7 +1363,8 @@ if asyncio:
|
||||
|
||||
When whole response is received, on_response_done() is invoked.
|
||||
|
||||
When stream is closed, on_close(error_code) is called.
|
||||
When stream is closed or underlying connection is lost,
|
||||
on_close(error_code) is called.
|
||||
|
||||
The application can send follow up requests using HTTP2Client.send_request() method.
|
||||
|
||||
@@ -1490,8 +1504,6 @@ if asyncio:
|
||||
cnghttp2.NGHTTP2_PROTO_VERSION_ID:
|
||||
self.transport.abort()
|
||||
|
||||
# Send preamble
|
||||
self.transport.write(cnghttp2.NGHTTP2_CLIENT_CONNECTION_PREFACE)
|
||||
self.http2 = _HTTP2ClientSessionCore(self.transport)
|
||||
|
||||
# Clear pending requests
|
||||
|
||||
25
script/Makefile.am
Normal file
25
script/Makefile.am
Normal file
@@ -0,0 +1,25 @@
|
||||
# nghttp2 - HTTP/2 C Library
|
||||
|
||||
# Copyright (c) 2015 Tatsuhiro Tsujikawa
|
||||
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
EXTRA_DIST = README.rst
|
||||
dist_pkgdata_SCRIPTS = fetch-ocsp-response
|
||||
10
script/README.rst
Normal file
10
script/README.rst
Normal file
@@ -0,0 +1,10 @@
|
||||
fetch-ocsp-response is a Python script which performs OCSP query and
|
||||
get response. It uses openssl command under the hood. nghttpx uses
|
||||
it to enable OCSP stapling feature.
|
||||
|
||||
fetch-ocsp-response is a translation from original fetch-ocsp-response
|
||||
written in Perl and which has been developed as part of h2o project
|
||||
(https://github.com/h2o/h2o).
|
||||
|
||||
fetch-ocsp-response is usually installed under $(pkgdatadir), which is
|
||||
$(prefix)/share/nghttp2.
|
||||
241
script/fetch-ocsp-response
Executable file
241
script/fetch-ocsp-response
Executable file
@@ -0,0 +1,241 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# nghttp2 - HTTP/2 C Library
|
||||
|
||||
# Copyright (c) 2015 Tatsuhiro Tsujikawa
|
||||
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
# This program was translated from the program originally developed by
|
||||
# h2o project (https://github.com/h2o/h2o), written in Perl. It had
|
||||
# the following copyright notice:
|
||||
|
||||
# Copyright (c) 2015 DeNA Co., Ltd.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to
|
||||
# deal in the Software without restriction, including without limitation the
|
||||
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
# sell copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
# IN THE SOFTWARE.
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import argparse
|
||||
import io
|
||||
import os
|
||||
import os.path
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
# make this program work for both Python 3 and Python 2.
|
||||
try:
|
||||
from urllib.parse import urlparse
|
||||
stdout_bwrite = sys.stdout.buffer.write
|
||||
except ImportError:
|
||||
from urlparse import urlparse
|
||||
stdout_bwrite = sys.stdout.write
|
||||
|
||||
|
||||
def die(msg):
|
||||
sys.stderr.write(msg)
|
||||
sys.stderr.write('\n')
|
||||
sys.exit(255)
|
||||
|
||||
|
||||
def tempfail(msg):
|
||||
sys.stderr.write(msg)
|
||||
sys.stderr.write('\n')
|
||||
sys.exit(os.EX_TEMPFAIL)
|
||||
|
||||
|
||||
def run_openssl(args, allow_tempfail=False):
|
||||
buf = io.BytesIO()
|
||||
try:
|
||||
p = subprocess.Popen(args, stdout=subprocess.PIPE)
|
||||
except Exception as e:
|
||||
die('failed to invoke {}:{}'.format(args, e))
|
||||
try:
|
||||
while True:
|
||||
data = p.stdout.read()
|
||||
if len(data) == 0:
|
||||
break
|
||||
buf.write(data)
|
||||
if p.wait() != 0:
|
||||
raise Exception('nonzero return code {}'.format(p.returncode))
|
||||
return buf.getvalue()
|
||||
except Exception as e:
|
||||
msg = 'OpenSSL exitted abnormally: {}:{}'.format(args, e)
|
||||
tempfail(msg) if allow_tempfail else die(msg)
|
||||
|
||||
|
||||
def read_file(path):
|
||||
with open(path, 'rb') as f:
|
||||
return f.read()
|
||||
|
||||
|
||||
def write_file(path, data):
|
||||
with open(path, 'wb') as f:
|
||||
f.write(data)
|
||||
|
||||
|
||||
def detect_openssl_version(cmd):
|
||||
return run_openssl([cmd, 'version']).decode('utf-8').strip()
|
||||
|
||||
|
||||
def extract_ocsp_uri(cmd, cert_fn):
|
||||
# obtain ocsp uri
|
||||
ocsp_uri = run_openssl(
|
||||
[cmd, 'x509', '-in', cert_fn, '-noout',
|
||||
'-ocsp_uri']).decode('utf-8').strip()
|
||||
|
||||
if not re.match(r'^https?://', ocsp_uri):
|
||||
die('failed to extract ocsp URI from {}'.format(cert_fn))
|
||||
|
||||
return ocsp_uri
|
||||
|
||||
|
||||
def save_issuer_certificate(issuer_fn, cert_fn):
|
||||
# save issuer certificate
|
||||
chain = read_file(cert_fn).decode('utf-8')
|
||||
m = re.match(
|
||||
r'.*?-----END CERTIFICATE-----.*?(-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----)',
|
||||
chain, re.DOTALL)
|
||||
if not m:
|
||||
die('--issuer option was not used, and failed to extract issuer certificate from the certificate')
|
||||
write_file(issuer_fn, (m.group(1) + '\n').encode('utf-8'))
|
||||
|
||||
|
||||
def send_and_receive_ocsp(respder_fn, cmd, cert_fn, issuer_fn, ocsp_uri,
|
||||
ocsp_host, openssl_version):
|
||||
# obtain response (without verification)
|
||||
sys.stderr.write('sending OCSP request to {}\n'.format(ocsp_uri))
|
||||
args = [
|
||||
cmd, 'ocsp', '-issuer', issuer_fn, '-cert', cert_fn, '-url', ocsp_uri
|
||||
]
|
||||
if openssl_version.lower().startswith('openssl 1.'):
|
||||
args.extend(['-header', 'Host', ocsp_host])
|
||||
args.extend(['-noverify', '-respout', respder_fn])
|
||||
resp = run_openssl(args, allow_tempfail=True)
|
||||
|
||||
return resp.decode('utf-8')
|
||||
|
||||
|
||||
def verify_response(cmd, tempdir, issuer_fn, respder_fn):
|
||||
# verify the response
|
||||
sys.stderr.write('verifying the response signature\n')
|
||||
|
||||
verify_fn = os.path.join(tempdir, 'verify.out')
|
||||
|
||||
# try from exotic options
|
||||
allextra = [
|
||||
# for comodo
|
||||
['-VAfile', issuer_fn],
|
||||
# these options are only available in OpenSSL >= 1.0.2
|
||||
['-partial_chain', '-trusted_first', '-CAfile', issuer_fn],
|
||||
# for OpenSSL <= 1.0.1
|
||||
['-CAfile', issuer_fn],
|
||||
]
|
||||
|
||||
for extra in allextra:
|
||||
with open(verify_fn, 'wb') as f:
|
||||
args = [cmd, 'ocsp', '-respin', respder_fn]
|
||||
args.extend(extra)
|
||||
p = subprocess.Popen(args, stdout=f, stderr=f)
|
||||
if p.wait() == 0:
|
||||
sys.stderr.write('verify OK (used: {})\n'.format(extra))
|
||||
return True
|
||||
|
||||
sys.stderr.write(read_file(verify_fn).decode('utf-8'))
|
||||
return False
|
||||
|
||||
|
||||
def fetch_ocsp_response(cmd, cert_fn, tempdir, issuer_fn=None):
|
||||
openssl_version = detect_openssl_version(cmd)
|
||||
|
||||
sys.stderr.write(
|
||||
'fetch-ocsp-response (using {})\n'.format(openssl_version))
|
||||
|
||||
ocsp_uri = extract_ocsp_uri(cmd, cert_fn)
|
||||
ocsp_host = urlparse(ocsp_uri).hostname
|
||||
|
||||
if not issuer_fn:
|
||||
issuer_fn = os.path.join(tempdir, 'issuer.crt')
|
||||
save_issuer_certificate(issuer_fn, cert_fn)
|
||||
|
||||
respder_fn = os.path.join(tempdir, 'resp.der')
|
||||
resp = send_and_receive_ocsp(
|
||||
respder_fn, cmd, cert_fn, issuer_fn, ocsp_uri, ocsp_host,
|
||||
openssl_version)
|
||||
|
||||
sys.stderr.write('{}\n'.format(resp))
|
||||
|
||||
if not verify_response(cmd, tempdir, issuer_fn, respder_fn):
|
||||
tempfail('failed to verify the response')
|
||||
|
||||
# success
|
||||
res = read_file(respder_fn)
|
||||
stdout_bwrite(res)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(
|
||||
description=
|
||||
'''The command issues an OCSP request for given server certificate, verifies the response and prints the resulting DER.''',
|
||||
epilog=
|
||||
'''The command exits 0 if successful, or 75 (EX_TEMPFAIL) on temporary error. Other exit codes may be returned in case of hard errors.''')
|
||||
parser.add_argument(
|
||||
'--issuer',
|
||||
metavar='FILE',
|
||||
help=
|
||||
'issuer certificate (if omitted, is extracted from the certificate chain)')
|
||||
parser.add_argument('--openssl',
|
||||
metavar='CMD',
|
||||
help='openssl command to use (default: "openssl")',
|
||||
default='openssl')
|
||||
parser.add_argument('certificate',
|
||||
help='path to certificate file to validate')
|
||||
args = parser.parse_args()
|
||||
|
||||
tempdir = None
|
||||
try:
|
||||
# Python3.2 has tempfile.TemporaryDirectory, which has nice
|
||||
# feature to delete its tree by cleanup() function. We have
|
||||
# to support Python2.7, so we have to do this manually.
|
||||
tempdir = tempfile.mkdtemp()
|
||||
fetch_ocsp_response(args.openssl, args.certificate, tempdir,
|
||||
args.issuer)
|
||||
finally:
|
||||
if tempdir:
|
||||
shutil.rmtree(tempdir)
|
||||
@@ -25,13 +25,25 @@
|
||||
#include "HttpServer.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#ifdef HAVE_SYS_SOCKET_H
|
||||
#include <sys/socket.h>
|
||||
#endif // HAVE_SYS_SOCKET_H
|
||||
#ifdef HAVE_NETDB_H
|
||||
#include <netdb.h>
|
||||
#endif // HAVE_NETDB_H
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif // HAVE_UNISTD_H
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif // HAVE_FCNTL_H
|
||||
#ifdef HAVE_NETINET_IN_H
|
||||
#include <netinet/in.h>
|
||||
#endif // HAVE_NETINET_IN_H
|
||||
#include <netinet/tcp.h>
|
||||
#ifdef HAVE_ARPA_INET_H
|
||||
#include <arpa/inet.h>
|
||||
#endif // HAVE_ARPA_INET_H
|
||||
|
||||
#include <cassert>
|
||||
#include <set>
|
||||
@@ -57,8 +69,9 @@
|
||||
namespace nghttp2 {
|
||||
|
||||
namespace {
|
||||
const std::string DEFAULT_HTML = "index.html";
|
||||
const std::string NGHTTPD_SERVER = "nghttpd nghttp2/" NGHTTP2_VERSION;
|
||||
// TODO could be constexpr
|
||||
constexpr char DEFAULT_HTML[] = "index.html";
|
||||
constexpr char NGHTTPD_SERVER[] = "nghttpd nghttp2/" NGHTTP2_VERSION;
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
@@ -87,16 +100,13 @@ template <typename Array> void append_nv(Stream *stream, const Array &nva) {
|
||||
} // namespace
|
||||
|
||||
Config::Config()
|
||||
: stream_read_timeout(60.), stream_write_timeout(60.),
|
||||
session_option(nullptr), data_ptr(nullptr), padding(0), num_worker(1),
|
||||
: stream_read_timeout(60.), stream_write_timeout(60.), data_ptr(nullptr),
|
||||
padding(0), num_worker(1), max_concurrent_streams(100),
|
||||
header_table_size(-1), port(0), verbose(false), daemon(false),
|
||||
verify_client(false), no_tls(false), error_gzip(false),
|
||||
early_response(false), hexdump(false) {
|
||||
nghttp2_option_new(&session_option);
|
||||
nghttp2_option_set_recv_client_preface(session_option, 1);
|
||||
}
|
||||
early_response(false), hexdump(false), echo_upload(false) {}
|
||||
|
||||
Config::~Config() { nghttp2_option_del(session_option); }
|
||||
Config::~Config() {}
|
||||
|
||||
namespace {
|
||||
void stream_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
|
||||
@@ -282,7 +292,7 @@ private:
|
||||
|
||||
Stream::Stream(Http2Handler *handler, int32_t stream_id)
|
||||
: handler(handler), file_ent(nullptr), body_length(0), body_offset(0),
|
||||
stream_id(stream_id) {
|
||||
stream_id(stream_id), echo_upload(false) {
|
||||
auto config = handler->get_config();
|
||||
ev_timer_init(&rtimer, stream_timeout_cb, 0., config->stream_read_timeout);
|
||||
ev_timer_init(&wtimer, stream_timeout_cb, 0., config->stream_write_timeout);
|
||||
@@ -317,9 +327,13 @@ void on_session_closed(Http2Handler *hd, int64_t session_id) {
|
||||
|
||||
namespace {
|
||||
void settings_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
|
||||
int rv;
|
||||
auto hd = static_cast<Http2Handler *>(w->data);
|
||||
hd->terminate_session(NGHTTP2_SETTINGS_TIMEOUT);
|
||||
hd->on_write();
|
||||
rv = hd->on_write();
|
||||
if (rv == -1) {
|
||||
delete_handler(hd);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -461,7 +475,7 @@ int Http2Handler::read_clear() {
|
||||
|
||||
rv = nghttp2_session_mem_recv(session_, buf.data(), nread);
|
||||
if (rv < 0) {
|
||||
if (rv != NGHTTP2_ERR_BAD_PREFACE) {
|
||||
if (rv != NGHTTP2_ERR_BAD_CLIENT_MAGIC) {
|
||||
std::cerr << "nghttp2_session_mem_recv() returned error: "
|
||||
<< nghttp2_strerror(rv) << std::endl;
|
||||
}
|
||||
@@ -588,7 +602,7 @@ int Http2Handler::read_tls() {
|
||||
|
||||
rv = nghttp2_session_mem_recv(session_, buf.data(), nread);
|
||||
if (rv < 0) {
|
||||
if (rv != NGHTTP2_ERR_BAD_PREFACE) {
|
||||
if (rv != NGHTTP2_ERR_BAD_CLIENT_MAGIC) {
|
||||
std::cerr << "nghttp2_session_mem_recv() returned error: "
|
||||
<< nghttp2_strerror(rv) << std::endl;
|
||||
}
|
||||
@@ -660,20 +674,22 @@ int Http2Handler::on_write() { return write_(*this); }
|
||||
int Http2Handler::connection_made() {
|
||||
int r;
|
||||
|
||||
r = nghttp2_session_server_new2(&session_, sessions_->get_callbacks(), this,
|
||||
sessions_->get_config()->session_option);
|
||||
r = nghttp2_session_server_new(&session_, sessions_->get_callbacks(), this);
|
||||
|
||||
if (r != 0) {
|
||||
return r;
|
||||
}
|
||||
|
||||
auto config = sessions_->get_config();
|
||||
std::array<nghttp2_settings_entry, 4> entry;
|
||||
size_t niv = 1;
|
||||
|
||||
entry[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
|
||||
entry[0].value = 100;
|
||||
entry[0].value = config->max_concurrent_streams;
|
||||
|
||||
if (sessions_->get_config()->header_table_size >= 0) {
|
||||
if (config->header_table_size >= 0) {
|
||||
entry[niv].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
|
||||
entry[niv].value = sessions_->get_config()->header_table_size;
|
||||
entry[niv].value = config->header_table_size;
|
||||
++niv;
|
||||
}
|
||||
r = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, entry.data(), niv);
|
||||
@@ -724,7 +740,7 @@ int Http2Handler::submit_file_response(const std::string &status,
|
||||
std::string content_length = util::utos(file_length);
|
||||
std::string last_modified_str;
|
||||
auto nva = make_array(http2::make_nv_ls(":status", status),
|
||||
http2::make_nv_ls("server", NGHTTPD_SERVER),
|
||||
http2::make_nv_ll("server", NGHTTPD_SERVER),
|
||||
http2::make_nv_ls("content-length", content_length),
|
||||
http2::make_nv_ll("cache-control", "max-age=3600"),
|
||||
http2::make_nv_ls("date", sessions_->get_cached_date()),
|
||||
@@ -754,7 +770,7 @@ int Http2Handler::submit_response(const std::string &status, int32_t stream_id,
|
||||
auto nva = std::vector<nghttp2_nv>();
|
||||
nva.reserve(3 + headers.size());
|
||||
nva.push_back(http2::make_nv_ls(":status", status));
|
||||
nva.push_back(http2::make_nv_ls("server", NGHTTPD_SERVER));
|
||||
nva.push_back(http2::make_nv_ll("server", NGHTTPD_SERVER));
|
||||
nva.push_back(http2::make_nv_ls("date", sessions_->get_cached_date()));
|
||||
for (auto &nv : headers) {
|
||||
nva.push_back(http2::make_nv(nv.name, nv.value, nv.no_index));
|
||||
@@ -767,7 +783,7 @@ int Http2Handler::submit_response(const std::string &status, int32_t stream_id,
|
||||
int Http2Handler::submit_response(const std::string &status, int32_t stream_id,
|
||||
nghttp2_data_provider *data_prd) {
|
||||
auto nva = make_array(http2::make_nv_ls(":status", status),
|
||||
http2::make_nv_ls("server", NGHTTPD_SERVER));
|
||||
http2::make_nv_ll("server", NGHTTPD_SERVER));
|
||||
return nghttp2_submit_response(session_, stream_id, nva.data(), nva.size(),
|
||||
data_prd);
|
||||
}
|
||||
@@ -916,6 +932,49 @@ void prepare_status_response(Stream *stream, Http2Handler *hd, int status) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void prepare_echo_response(Stream *stream, Http2Handler *hd) {
|
||||
auto length = lseek(stream->file_ent->fd, 0, SEEK_END);
|
||||
if (length == -1) {
|
||||
hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
stream->body_length = length;
|
||||
if (lseek(stream->file_ent->fd, 0, SEEK_SET) == -1) {
|
||||
hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
nghttp2_data_provider data_prd;
|
||||
data_prd.source.fd = stream->file_ent->fd;
|
||||
data_prd.read_callback = file_read_callback;
|
||||
|
||||
Headers headers;
|
||||
headers.emplace_back("nghttpd-response", "echo");
|
||||
headers.emplace_back("content-length", util::utos(length));
|
||||
|
||||
hd->submit_response("200", stream->stream_id, headers, &data_prd);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
bool prepare_upload_temp_store(Stream *stream, Http2Handler *hd) {
|
||||
auto sessions = hd->get_sessions();
|
||||
|
||||
char tempfn[] = "/tmp/nghttpd.temp.XXXXXX";
|
||||
auto fd = mkstemp(tempfn);
|
||||
if (fd == -1) {
|
||||
return false;
|
||||
}
|
||||
unlink(tempfn);
|
||||
// Ordinary request never start with "echo:". The length is 0 for
|
||||
// now. We will update it when we get whole request body.
|
||||
stream->file_ent = sessions->cache_fd(std::string("echo:") + tempfn,
|
||||
FileEntry(tempfn, 0, 0, fd));
|
||||
stream->echo_upload = true;
|
||||
return true;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void prepare_redirect_response(Stream *stream, Http2Handler *hd,
|
||||
const std::string &path, int status) {
|
||||
@@ -970,8 +1029,14 @@ void prepare_response(Stream *stream, Http2Handler *hd,
|
||||
url = reqpath;
|
||||
}
|
||||
|
||||
url = util::percentDecode(url.begin(), url.end());
|
||||
auto sessions = hd->get_sessions();
|
||||
|
||||
url = util::percentDecode(std::begin(url), std::end(url));
|
||||
if (!util::check_path(url)) {
|
||||
if (stream->file_ent) {
|
||||
sessions->release_fd(stream->file_ent->path);
|
||||
stream->file_ent = nullptr;
|
||||
}
|
||||
prepare_status_response(stream, hd, 404);
|
||||
return;
|
||||
}
|
||||
@@ -985,12 +1050,18 @@ void prepare_response(Stream *stream, Http2Handler *hd,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string path = hd->get_config()->htdocs + url;
|
||||
if (path[path.size() - 1] == '/') {
|
||||
path += DEFAULT_HTML;
|
||||
}
|
||||
|
||||
auto sessions = hd->get_sessions();
|
||||
if (stream->echo_upload) {
|
||||
assert(stream->file_ent);
|
||||
prepare_echo_response(stream, hd);
|
||||
return;
|
||||
}
|
||||
|
||||
auto file_ent = sessions->get_cached_fd(path);
|
||||
|
||||
if (file_ent == nullptr) {
|
||||
@@ -1024,7 +1095,7 @@ void prepare_response(Stream *stream, Http2Handler *hd,
|
||||
return;
|
||||
}
|
||||
|
||||
if (last_mod_found && buf.st_mtime <= last_mod) {
|
||||
if (last_mod_found && static_cast<time_t>(buf.st_mtime) <= last_mod) {
|
||||
close(file);
|
||||
prepare_status_response(stream, hd, 304);
|
||||
|
||||
@@ -1120,7 +1191,7 @@ int hd_on_frame_recv_callback(nghttp2_session *session,
|
||||
|
||||
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||
remove_stream_read_timeout(stream);
|
||||
if (!hd->get_config()->early_response) {
|
||||
if (stream->echo_upload || !hd->get_config()->early_response) {
|
||||
prepare_response(stream, hd);
|
||||
}
|
||||
} else {
|
||||
@@ -1144,14 +1215,22 @@ int hd_on_frame_recv_callback(nghttp2_session *session,
|
||||
hd->submit_non_final_response("100", frame->hd.stream_id);
|
||||
}
|
||||
|
||||
if (hd->get_config()->early_response) {
|
||||
auto &method = http2::get_header(stream->hdidx, http2::HD__METHOD,
|
||||
stream->headers)->value;
|
||||
if (hd->get_config()->echo_upload &&
|
||||
(method == "POST" || method == "PUT")) {
|
||||
if (!prepare_upload_temp_store(stream, hd)) {
|
||||
hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR);
|
||||
return 0;
|
||||
}
|
||||
} else if (hd->get_config()->early_response) {
|
||||
prepare_response(stream, hd);
|
||||
}
|
||||
}
|
||||
|
||||
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||
remove_stream_read_timeout(stream);
|
||||
if (!hd->get_config()->early_response) {
|
||||
if (stream->echo_upload || !hd->get_config()->early_response) {
|
||||
prepare_response(stream, hd);
|
||||
}
|
||||
} else {
|
||||
@@ -1297,6 +1376,21 @@ int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (stream->echo_upload) {
|
||||
assert(stream->file_ent);
|
||||
while (len) {
|
||||
ssize_t n;
|
||||
while ((n = write(stream->file_ent->fd, data, len)) == -1 &&
|
||||
errno == EINTR)
|
||||
;
|
||||
if (n == -1) {
|
||||
hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR);
|
||||
return 0;
|
||||
}
|
||||
len -= n;
|
||||
data += n;
|
||||
}
|
||||
}
|
||||
// TODO Handle POST
|
||||
|
||||
add_stream_read_timeout(stream);
|
||||
|
||||
@@ -27,9 +27,9 @@
|
||||
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <cinttypes>
|
||||
#include <cstdlib>
|
||||
|
||||
#include <string>
|
||||
@@ -59,10 +59,10 @@ struct Config {
|
||||
std::string address;
|
||||
ev_tstamp stream_read_timeout;
|
||||
ev_tstamp stream_write_timeout;
|
||||
nghttp2_option *session_option;
|
||||
void *data_ptr;
|
||||
size_t padding;
|
||||
size_t num_worker;
|
||||
size_t max_concurrent_streams;
|
||||
ssize_t header_table_size;
|
||||
uint16_t port;
|
||||
bool verbose;
|
||||
@@ -72,6 +72,7 @@ struct Config {
|
||||
bool error_gzip;
|
||||
bool early_response;
|
||||
bool hexdump;
|
||||
bool echo_upload;
|
||||
Config();
|
||||
~Config();
|
||||
};
|
||||
@@ -100,6 +101,7 @@ struct Stream {
|
||||
int64_t body_offset;
|
||||
int32_t stream_id;
|
||||
http2::HeaderIndex hdidx;
|
||||
bool echo_upload;
|
||||
Stream(Http2Handler *handler, int32_t stream_id);
|
||||
~Stream();
|
||||
};
|
||||
|
||||
@@ -23,11 +23,21 @@
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include <sys/types.h>
|
||||
#ifdef HAVE_SYS_SOCKET_H
|
||||
#include <sys/socket.h>
|
||||
#endif // HAVE_SYS_SOCKET_H
|
||||
#ifdef HAVE_NETDB_H
|
||||
#include <netdb.h>
|
||||
#endif // HAVE_NETDB_H
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif // HAVE_UNISTD_H
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif // HAVE_FCNTL_H
|
||||
#ifdef HAVE_NETINET_IN_H
|
||||
#include <netinet/in.h>
|
||||
#endif // HAVE_NETINET_IN_H
|
||||
#include <netinet/tcp.h>
|
||||
#include <poll.h>
|
||||
|
||||
@@ -130,8 +140,6 @@ const char *strframetype(uint8_t type) {
|
||||
return "GOAWAY";
|
||||
case NGHTTP2_WINDOW_UPDATE:
|
||||
return "WINDOW_UPDATE";
|
||||
case NGHTTP2_EXT_ALTSVC:
|
||||
return "ALTSVC";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
@@ -362,34 +370,6 @@ void print_frame(print_type ptype, const nghttp2_frame *frame) {
|
||||
fprintf(outfile, "(window_size_increment=%d)\n",
|
||||
frame->window_update.window_size_increment);
|
||||
break;
|
||||
case NGHTTP2_EXT_ALTSVC: {
|
||||
print_frame_attr_indent();
|
||||
|
||||
auto altsvc = static_cast<const nghttp2_ext_altsvc *>(frame->ext.payload);
|
||||
|
||||
fprintf(outfile, "(max-age=%u, port=%u, protocol_id=", altsvc->max_age,
|
||||
altsvc->port);
|
||||
|
||||
if (altsvc->protocol_id_len) {
|
||||
fwrite(altsvc->protocol_id, altsvc->protocol_id_len, 1, outfile);
|
||||
}
|
||||
|
||||
fprintf(outfile, ", host=");
|
||||
|
||||
if (altsvc->host_len) {
|
||||
fwrite(altsvc->host, altsvc->host_len, 1, outfile);
|
||||
}
|
||||
|
||||
fprintf(outfile, ", origin=");
|
||||
|
||||
if (altsvc->origin_len) {
|
||||
fwrite(altsvc->origin, altsvc->origin_len, 1, outfile);
|
||||
}
|
||||
|
||||
fprintf(outfile, ")\n");
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -429,10 +409,11 @@ int verbose_on_frame_recv_callback(nghttp2_session *session,
|
||||
|
||||
int verbose_on_invalid_frame_recv_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame,
|
||||
uint32_t error_code,
|
||||
int lib_error_code,
|
||||
void *user_data) {
|
||||
print_timer();
|
||||
fprintf(outfile, " [INVALID; status=%s] recv ", strstatus(error_code));
|
||||
fprintf(outfile, " [INVALID; error=%s] recv ",
|
||||
nghttp2_strerror(lib_error_code));
|
||||
print_frame(PRINT_RECV, frame);
|
||||
fflush(outfile);
|
||||
return 0;
|
||||
|
||||
@@ -27,9 +27,11 @@
|
||||
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <cinttypes>
|
||||
#include <cstdlib>
|
||||
#ifdef HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#endif // HAVE_SYS_TIME_H
|
||||
#include <poll.h>
|
||||
|
||||
#include <map>
|
||||
@@ -49,8 +51,7 @@ int verbose_on_frame_recv_callback(nghttp2_session *session,
|
||||
|
||||
int verbose_on_invalid_frame_recv_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame,
|
||||
uint32_t error_code,
|
||||
void *user_data);
|
||||
int lib_error_code, void *user_data);
|
||||
|
||||
int verbose_on_frame_send_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame, void *user_data);
|
||||
|
||||
@@ -75,10 +75,6 @@ void session_impl::connected(tcp::resolver::iterator endpoint_it) {
|
||||
|
||||
socket().set_option(boost::asio::ip::tcp::no_delay(true));
|
||||
|
||||
std::copy_n(NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN, std::begin(wb_));
|
||||
wblen_ = NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN;
|
||||
|
||||
do_write();
|
||||
do_read();
|
||||
|
||||
|
||||
@@ -268,17 +268,7 @@ int http2_handler::start() {
|
||||
nghttp2_session_callbacks_set_on_frame_not_send_callback(
|
||||
callbacks, on_frame_not_send_callback);
|
||||
|
||||
nghttp2_option *option;
|
||||
rv = nghttp2_option_new(&option);
|
||||
if (rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto opt_del = defer(nghttp2_option_del, option);
|
||||
|
||||
nghttp2_option_set_recv_client_preface(option, 1);
|
||||
|
||||
rv = nghttp2_session_server_new2(&session_, callbacks, this, option);
|
||||
rv = nghttp2_session_server_new(&session_, callbacks, this);
|
||||
if (rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,10 @@
|
||||
#ifndef BUFFER_TEST_H
|
||||
#define BUFFER_TEST_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif // HAVE_CONFIG_H
|
||||
|
||||
namespace nghttp2 {
|
||||
|
||||
void test_buffer_write(void);
|
||||
|
||||
@@ -26,7 +26,9 @@
|
||||
#include <config.h>
|
||||
#endif // HAVE_CONFIG_H
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif // HAVE_UNISTD_H
|
||||
#include <getopt.h>
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
219
src/h2load.cc
219
src/h2load.cc
@@ -26,10 +26,14 @@
|
||||
|
||||
#include <getopt.h>
|
||||
#include <signal.h>
|
||||
#ifdef HAVE_NETINET_IN_H
|
||||
#include <netinet/in.h>
|
||||
#endif // HAVE_NETINET_IN_H
|
||||
#include <netinet/tcp.h>
|
||||
#include <sys/stat.h>
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif // HAVE_FCNTL_H
|
||||
|
||||
#include <cstdio>
|
||||
#include <cassert>
|
||||
@@ -150,8 +154,8 @@ void readcb(struct ev_loop *loop, ev_io *w, int revents) {
|
||||
|
||||
Client::Client(Worker *worker, size_t req_todo)
|
||||
: worker(worker), ssl(nullptr), next_addr(config.addrs), reqidx(0),
|
||||
state(CLIENT_IDLE), req_todo(req_todo), req_started(0), req_done(0),
|
||||
fd(-1) {
|
||||
state(CLIENT_IDLE), first_byte_received(false), req_todo(req_todo),
|
||||
req_started(0), req_done(0), fd(-1) {
|
||||
ev_io_init(&wev, writecb, 0, EV_WRITE);
|
||||
ev_io_init(&rev, readcb, 0, EV_READ);
|
||||
|
||||
@@ -165,6 +169,8 @@ int Client::do_read() { return readfn(*this); }
|
||||
int Client::do_write() { return writefn(*this); }
|
||||
|
||||
int Client::connect() {
|
||||
record_start_time(&worker->stats);
|
||||
|
||||
while (next_addr) {
|
||||
auto addr = next_addr;
|
||||
next_addr = next_addr->ai_next;
|
||||
@@ -412,23 +418,21 @@ int Client::connection_made() {
|
||||
if (next_proto) {
|
||||
if (util::check_h2_is_selected(next_proto, next_proto_len)) {
|
||||
session = make_unique<Http2Session>(this);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
#ifdef HAVE_SPDYLAY
|
||||
else {
|
||||
auto spdy_version =
|
||||
spdylay_npn_get_version(next_proto, next_proto_len);
|
||||
if (spdy_version) {
|
||||
session = make_unique<SpdySession>(this, spdy_version);
|
||||
} else {
|
||||
debug_nextproto_error();
|
||||
fail();
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
#else // !HAVE_SPDYLAY
|
||||
debug_nextproto_error();
|
||||
fail();
|
||||
return -1;
|
||||
#endif // !HAVE_SPDYLAY
|
||||
}
|
||||
#endif // HAVE_SPDYLAY
|
||||
|
||||
next_proto = nullptr;
|
||||
break;
|
||||
}
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||
@@ -469,6 +473,8 @@ int Client::connection_made() {
|
||||
|
||||
session->on_connect();
|
||||
|
||||
record_connect_time(&worker->stats);
|
||||
|
||||
auto nreq =
|
||||
std::min(req_todo - req_started, (size_t)config.max_concurrent_streams);
|
||||
|
||||
@@ -519,6 +525,11 @@ int Client::read_clear() {
|
||||
if (on_read(buf, nread) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!first_byte_received) {
|
||||
first_byte_received = true;
|
||||
record_ttfb(&worker->stats);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -641,6 +652,11 @@ int Client::read_tls() {
|
||||
if (on_read(buf, rv) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!first_byte_received) {
|
||||
first_byte_received = true;
|
||||
record_ttfb(&worker->stats);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -691,6 +707,18 @@ void Client::record_request_time(RequestStat *req_stat) {
|
||||
req_stat->request_time = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
void Client::record_start_time(Stats *stat) {
|
||||
stat->start_times.push_back(std::chrono::steady_clock::now());
|
||||
}
|
||||
|
||||
void Client::record_connect_time(Stats *stat) {
|
||||
stat->connect_times.push_back(std::chrono::steady_clock::now());
|
||||
}
|
||||
|
||||
void Client::record_ttfb(Stats *stat) {
|
||||
stat->ttfbs.push_back(std::chrono::steady_clock::now());
|
||||
}
|
||||
|
||||
void Client::signal_write() { ev_io_start(worker->loop, &wev); }
|
||||
|
||||
Worker::Worker(uint32_t id, SSL_CTX *ssl_ctx, size_t req_todo, size_t nclients,
|
||||
@@ -731,70 +759,100 @@ void Worker::run() {
|
||||
}
|
||||
|
||||
namespace {
|
||||
double within_sd(const std::vector<std::unique_ptr<Worker>> &workers,
|
||||
const std::chrono::microseconds &mean,
|
||||
const std::chrono::microseconds &sd, size_t n) {
|
||||
auto upper = mean.count() + sd.count();
|
||||
auto lower = mean.count() - sd.count();
|
||||
size_t m = 0;
|
||||
for (const auto &w : workers) {
|
||||
for (const auto &req_stat : w->stats.req_stats) {
|
||||
if (!req_stat.completed) {
|
||||
continue;
|
||||
}
|
||||
auto t = std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
req_stat.stream_close_time - req_stat.request_time);
|
||||
if (lower <= t.count() && t.count() <= upper) {
|
||||
++m;
|
||||
}
|
||||
}
|
||||
// Returns percentage of number of samples within mean +/- sd.
|
||||
template <typename Duration>
|
||||
double within_sd(const std::vector<Duration> &samples, const Duration &mean,
|
||||
const Duration &sd) {
|
||||
if (samples.size() == 0) {
|
||||
return 0.0;
|
||||
}
|
||||
return (m / static_cast<double>(n)) * 100;
|
||||
auto lower = mean - sd;
|
||||
auto upper = mean + sd;
|
||||
auto m = std::count_if(
|
||||
std::begin(samples), std::end(samples),
|
||||
[&lower, &upper](const Duration &t) { return lower <= t && t <= upper; });
|
||||
return (m / static_cast<double>(samples.size())) * 100;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
// Computes statistics using |samples|. The min, max, mean, sd, and
|
||||
// percentage of number of samples within mean +/- sd are computed.
|
||||
template <typename Duration>
|
||||
TimeStat<Duration> compute_time_stat(const std::vector<Duration> &samples) {
|
||||
if (samples.empty()) {
|
||||
return {Duration::zero(), Duration::zero(), Duration::zero(),
|
||||
Duration::zero(), 0.0};
|
||||
}
|
||||
// standard deviation calculated using Rapid calculation method:
|
||||
// http://en.wikipedia.org/wiki/Standard_deviation#Rapid_calculation_methods
|
||||
double a = 0, q = 0;
|
||||
size_t n = 0;
|
||||
int64_t sum = 0;
|
||||
auto res = TimeStat<Duration>{Duration::max(), Duration::min()};
|
||||
for (const auto &t : samples) {
|
||||
++n;
|
||||
res.min = std::min(res.min, t);
|
||||
res.max = std::max(res.max, t);
|
||||
sum += t.count();
|
||||
|
||||
auto na = a + (t.count() - a) / n;
|
||||
q += (t.count() - a) * (t.count() - na);
|
||||
a = na;
|
||||
}
|
||||
|
||||
assert(n > 0);
|
||||
res.mean = Duration(sum / n);
|
||||
res.sd = Duration(static_cast<typename Duration::rep>(sqrt(q / n)));
|
||||
res.within_sd = within_sd(samples, res.mean, res.sd);
|
||||
|
||||
return res;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
TimeStats
|
||||
process_time_stats(const std::vector<std::unique_ptr<Worker>> &workers) {
|
||||
auto ts = TimeStats();
|
||||
int64_t sum = 0;
|
||||
size_t n = 0;
|
||||
size_t nrequest_times = 0, nttfb_times = 0;
|
||||
for (const auto &w : workers) {
|
||||
nrequest_times += w->stats.req_stats.size();
|
||||
nttfb_times += w->stats.ttfbs.size();
|
||||
}
|
||||
|
||||
ts.time_min = std::chrono::microseconds::max();
|
||||
ts.time_max = std::chrono::microseconds::min();
|
||||
ts.within_sd = 0.;
|
||||
std::vector<std::chrono::microseconds> request_times;
|
||||
request_times.reserve(nrequest_times);
|
||||
std::vector<std::chrono::microseconds> connect_times, ttfb_times;
|
||||
connect_times.reserve(nttfb_times);
|
||||
ttfb_times.reserve(nttfb_times);
|
||||
|
||||
// standard deviation calculated using Rapid calculation method:
|
||||
// http://en.wikipedia.org/wiki/Standard_deviation#Rapid_calculation_methods
|
||||
double a = 0, q = 0;
|
||||
for (const auto &w : workers) {
|
||||
for (const auto &req_stat : w->stats.req_stats) {
|
||||
if (!req_stat.completed) {
|
||||
continue;
|
||||
}
|
||||
++n;
|
||||
auto t = std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
req_stat.stream_close_time - req_stat.request_time);
|
||||
ts.time_min = std::min(ts.time_min, t);
|
||||
ts.time_max = std::max(ts.time_max, t);
|
||||
sum += t.count();
|
||||
request_times.push_back(
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
req_stat.stream_close_time - req_stat.request_time));
|
||||
}
|
||||
|
||||
auto na = a + (t.count() - a) / n;
|
||||
q = q + (t.count() - a) * (t.count() - na);
|
||||
a = na;
|
||||
const auto &stat = w->stats;
|
||||
// rule out cases where we started but didn't connect or get the
|
||||
// first byte (errors). We will get connect event before FFTB.
|
||||
assert(stat.start_times.size() >= stat.ttfbs.size());
|
||||
assert(stat.connect_times.size() >= stat.ttfbs.size());
|
||||
for (size_t i = 0; i < stat.ttfbs.size(); ++i) {
|
||||
connect_times.push_back(
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
stat.connect_times[i] - stat.start_times[i]));
|
||||
|
||||
ttfb_times.push_back(
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
stat.ttfbs[i] - stat.start_times[i]));
|
||||
}
|
||||
}
|
||||
if (n == 0) {
|
||||
ts.time_max = ts.time_min = std::chrono::microseconds::zero();
|
||||
return ts;
|
||||
}
|
||||
|
||||
ts.time_mean = std::chrono::microseconds(sum / n);
|
||||
ts.time_sd = std::chrono::microseconds(
|
||||
static_cast<std::chrono::microseconds::rep>(sqrt(q / n)));
|
||||
|
||||
ts.within_sd = within_sd(workers, ts.time_mean, ts.time_sd, n);
|
||||
return ts;
|
||||
return {compute_time_stat(request_times), compute_time_stat(connect_times),
|
||||
compute_time_stat(ttfb_times)};
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -959,7 +1017,7 @@ Options:
|
||||
Number of native threads.
|
||||
Default: )" << config.nthreads << R"(
|
||||
-i, --input-file=<FILE>
|
||||
Path of a file with multiple URIs are seperated by EOLs.
|
||||
Path of a file with multiple URIs are separated by EOLs.
|
||||
This option will disable URIs getting from command-line.
|
||||
If '-' is given as <FILE>, URIs will be read from stdin.
|
||||
URIs are used in this order for each client. All URIs
|
||||
@@ -1006,6 +1064,14 @@ Options:
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
#ifndef NOTHREADS
|
||||
ssl::LibsslGlobalLock lock;
|
||||
#endif // NOTHREADS
|
||||
SSL_load_error_strings();
|
||||
SSL_library_init();
|
||||
OpenSSL_add_all_algorithms();
|
||||
OPENSSL_config(nullptr);
|
||||
|
||||
std::string datafile;
|
||||
while (1) {
|
||||
static int flag = 0;
|
||||
@@ -1202,14 +1268,6 @@ int main(int argc, char **argv) {
|
||||
memset(&act, 0, sizeof(struct sigaction));
|
||||
act.sa_handler = SIG_IGN;
|
||||
sigaction(SIGPIPE, &act, nullptr);
|
||||
OPENSSL_config(nullptr);
|
||||
OpenSSL_add_all_algorithms();
|
||||
SSL_load_error_strings();
|
||||
SSL_library_init();
|
||||
|
||||
#ifndef NOTHREADS
|
||||
ssl::LibsslGlobalLock lock;
|
||||
#endif // NOTHREADS
|
||||
|
||||
auto ssl_ctx = SSL_CTX_new(SSLv23_client_method());
|
||||
if (!ssl_ctx) {
|
||||
@@ -1408,7 +1466,7 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
}
|
||||
|
||||
auto time_stats = process_time_stats(workers);
|
||||
auto ts = process_time_stats(workers);
|
||||
|
||||
// Requests which have not been issued due to connection errors, are
|
||||
// counted towards req_failed and req_error.
|
||||
@@ -1441,14 +1499,23 @@ status codes: )" << stats.status[2] << " 2xx, " << stats.status[3] << " 3xx, "
|
||||
traffic: )" << stats.bytes_total << " bytes total, " << stats.bytes_head
|
||||
<< " bytes headers, " << stats.bytes_body << R"( bytes data
|
||||
min max mean sd +/- sd
|
||||
time for request: )" << std::setw(10)
|
||||
<< util::format_duration(time_stats.time_min) << " "
|
||||
<< std::setw(10) << util::format_duration(time_stats.time_max)
|
||||
<< " " << std::setw(10)
|
||||
<< util::format_duration(time_stats.time_mean) << " "
|
||||
<< std::setw(10) << util::format_duration(time_stats.time_sd)
|
||||
<< std::setw(9) << util::dtos(time_stats.within_sd) << "%"
|
||||
<< std::endl;
|
||||
time for request: )" << std::setw(10) << util::format_duration(ts.request.min)
|
||||
<< " " << std::setw(10) << util::format_duration(ts.request.max)
|
||||
<< " " << std::setw(10) << util::format_duration(ts.request.mean)
|
||||
<< " " << std::setw(10) << util::format_duration(ts.request.sd)
|
||||
<< std::setw(9) << util::dtos(ts.request.within_sd) << "%"
|
||||
<< "\ntime for connect: " << std::setw(10)
|
||||
<< util::format_duration(ts.connect.min) << " " << std::setw(10)
|
||||
<< util::format_duration(ts.connect.max) << " " << std::setw(10)
|
||||
<< util::format_duration(ts.connect.mean) << " " << std::setw(10)
|
||||
<< util::format_duration(ts.connect.sd) << std::setw(9)
|
||||
<< util::dtos(ts.connect.within_sd) << "%"
|
||||
<< "\ntime to 1st byte: " << std::setw(10)
|
||||
<< util::format_duration(ts.ttfb.min) << " " << std::setw(10)
|
||||
<< util::format_duration(ts.ttfb.max) << " " << std::setw(10)
|
||||
<< util::format_duration(ts.ttfb.mean) << " " << std::setw(10)
|
||||
<< util::format_duration(ts.ttfb.sd) << std::setw(9)
|
||||
<< util::dtos(ts.ttfb.within_sd) << "%" << std::endl;
|
||||
|
||||
SSL_CTX_free(ssl_ctx);
|
||||
|
||||
|
||||
33
src/h2load.h
33
src/h2load.h
@@ -28,8 +28,12 @@
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#ifdef HAVE_SYS_SOCKET_H
|
||||
#include <sys/socket.h>
|
||||
#endif // HAVE_SYS_SOCKET_H
|
||||
#ifdef HAVE_NETDB_H
|
||||
#include <netdb.h>
|
||||
#endif // HAVE_NETDB_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
@@ -94,13 +98,24 @@ struct RequestStat {
|
||||
bool completed;
|
||||
};
|
||||
|
||||
struct TimeStats {
|
||||
// time for request: max, min, mean and sd (standard deviation)
|
||||
std::chrono::microseconds time_max, time_min, time_mean, time_sd;
|
||||
// percentage of number of requests inside mean -/+ sd
|
||||
template <typename Duration> struct TimeStat {
|
||||
// min, max, mean and sd (standard deviation)
|
||||
Duration min, max, mean, sd;
|
||||
// percentage of samples inside mean -/+ sd
|
||||
double within_sd;
|
||||
};
|
||||
|
||||
struct TimeStats {
|
||||
// time for request
|
||||
TimeStat<std::chrono::microseconds> request;
|
||||
// time for connect
|
||||
TimeStat<std::chrono::microseconds> connect;
|
||||
// time to first byte (TTFB)
|
||||
TimeStat<std::chrono::microseconds> ttfb;
|
||||
};
|
||||
|
||||
enum TimeStatType { STAT_REQUEST, STAT_CONNECT, STAT_FIRST_BYTE };
|
||||
|
||||
struct Stats {
|
||||
Stats(size_t req_todo);
|
||||
// The total number of requests
|
||||
@@ -132,6 +147,12 @@ struct Stats {
|
||||
std::array<size_t, 6> status;
|
||||
// The statistics per request
|
||||
std::vector<RequestStat> req_stats;
|
||||
// time connect starts
|
||||
std::vector<std::chrono::steady_clock::time_point> start_times;
|
||||
// time to connect
|
||||
std::vector<std::chrono::steady_clock::time_point> connect_times;
|
||||
// time to first byte (TTFB)
|
||||
std::vector<std::chrono::steady_clock::time_point> ttfbs;
|
||||
};
|
||||
|
||||
enum ClientState { CLIENT_IDLE, CLIENT_CONNECTED };
|
||||
@@ -171,6 +192,7 @@ struct Client {
|
||||
addrinfo *next_addr;
|
||||
size_t reqidx;
|
||||
ClientState state;
|
||||
bool first_byte_received;
|
||||
// The number of requests this client has to issue.
|
||||
size_t req_todo;
|
||||
// The number of requests this client has issued so far.
|
||||
@@ -215,6 +237,9 @@ struct Client {
|
||||
void on_stream_close(int32_t stream_id, bool success, RequestStat *req_stat);
|
||||
|
||||
void record_request_time(RequestStat *req_stat);
|
||||
void record_start_time(Stats *stat);
|
||||
void record_connect_time(Stats *stat);
|
||||
void record_ttfb(Stats *stat);
|
||||
|
||||
void signal_write();
|
||||
};
|
||||
|
||||
@@ -202,12 +202,6 @@ void Http2Session::on_connect() {
|
||||
extra_connection_window);
|
||||
}
|
||||
|
||||
auto &wb = client_->wb;
|
||||
assert(wb.wleft() >= NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
|
||||
|
||||
wb.write(NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
|
||||
|
||||
client_->signal_write();
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,8 @@
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <cinttypes>
|
||||
|
||||
#include "h2load.h"
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "h2load_spdy_session.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cerrno>
|
||||
|
||||
#include "h2load.h"
|
||||
#include "util.h"
|
||||
|
||||
190
src/http2.cc
190
src/http2.cc
@@ -832,7 +832,7 @@ parse_next_link_header_once(const char *first, const char *last) {
|
||||
static constexpr size_t PLLEN = sizeof(PL) - 1;
|
||||
if (first + PLLEN == last) {
|
||||
if (std::equal(PL, PL + PLLEN, first, util::CaseCmp())) {
|
||||
ok = true;
|
||||
// ok = true;
|
||||
// this is the end of sequence
|
||||
return {{{url_first, url_last}}, last};
|
||||
}
|
||||
@@ -842,7 +842,7 @@ parse_next_link_header_once(const char *first, const char *last) {
|
||||
if (!std::equal(PL, PL + PLLEN, first, util::CaseCmp())) {
|
||||
break;
|
||||
}
|
||||
ok = true;
|
||||
// ok = true;
|
||||
// skip including ','
|
||||
first += PLLEN + 1;
|
||||
return {{{url_first, url_last}}, first};
|
||||
@@ -1113,6 +1113,192 @@ bool expect_response_body(const std::string &method, int status_code) {
|
||||
return method != "HEAD" && expect_response_body(status_code);
|
||||
}
|
||||
|
||||
bool expect_response_body(int method_token, int status_code) {
|
||||
return method_token != HTTP_HEAD && expect_response_body(status_code);
|
||||
}
|
||||
|
||||
int lookup_method_token(const std::string &name) {
|
||||
return lookup_method_token(reinterpret_cast<const uint8_t *>(name.c_str()),
|
||||
name.size());
|
||||
}
|
||||
|
||||
// This function was generated by genmethodfunc.py.
|
||||
int lookup_method_token(const uint8_t *name, size_t namelen) {
|
||||
switch (namelen) {
|
||||
case 3:
|
||||
switch (name[2]) {
|
||||
case 'T':
|
||||
if (util::streq_l("GE", name, 2)) {
|
||||
return HTTP_GET;
|
||||
}
|
||||
if (util::streq_l("PU", name, 2)) {
|
||||
return HTTP_PUT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
switch (name[3]) {
|
||||
case 'D':
|
||||
if (util::streq_l("HEA", name, 3)) {
|
||||
return HTTP_HEAD;
|
||||
}
|
||||
break;
|
||||
case 'E':
|
||||
if (util::streq_l("MOV", name, 3)) {
|
||||
return HTTP_MOVE;
|
||||
}
|
||||
break;
|
||||
case 'K':
|
||||
if (util::streq_l("LOC", name, 3)) {
|
||||
return HTTP_LOCK;
|
||||
}
|
||||
break;
|
||||
case 'T':
|
||||
if (util::streq_l("POS", name, 3)) {
|
||||
return HTTP_POST;
|
||||
}
|
||||
break;
|
||||
case 'Y':
|
||||
if (util::streq_l("COP", name, 3)) {
|
||||
return HTTP_COPY;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
switch (name[4]) {
|
||||
case 'E':
|
||||
if (util::streq_l("MERG", name, 4)) {
|
||||
return HTTP_MERGE;
|
||||
}
|
||||
if (util::streq_l("PURG", name, 4)) {
|
||||
return HTTP_PURGE;
|
||||
}
|
||||
if (util::streq_l("TRAC", name, 4)) {
|
||||
return HTTP_TRACE;
|
||||
}
|
||||
break;
|
||||
case 'H':
|
||||
if (util::streq_l("PATC", name, 4)) {
|
||||
return HTTP_PATCH;
|
||||
}
|
||||
break;
|
||||
case 'L':
|
||||
if (util::streq_l("MKCO", name, 4)) {
|
||||
return HTTP_MKCOL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
switch (name[5]) {
|
||||
case 'E':
|
||||
if (util::streq_l("DELET", name, 5)) {
|
||||
return HTTP_DELETE;
|
||||
}
|
||||
break;
|
||||
case 'H':
|
||||
if (util::streq_l("SEARC", name, 5)) {
|
||||
return HTTP_SEARCH;
|
||||
}
|
||||
break;
|
||||
case 'K':
|
||||
if (util::streq_l("UNLOC", name, 5)) {
|
||||
return HTTP_UNLOCK;
|
||||
}
|
||||
break;
|
||||
case 'T':
|
||||
if (util::streq_l("REPOR", name, 5)) {
|
||||
return HTTP_REPORT;
|
||||
}
|
||||
break;
|
||||
case 'Y':
|
||||
if (util::streq_l("NOTIF", name, 5)) {
|
||||
return HTTP_NOTIFY;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
switch (name[6]) {
|
||||
case 'H':
|
||||
if (util::streq_l("MSEARC", name, 6)) {
|
||||
return HTTP_MSEARCH;
|
||||
}
|
||||
break;
|
||||
case 'S':
|
||||
if (util::streq_l("OPTION", name, 6)) {
|
||||
return HTTP_OPTIONS;
|
||||
}
|
||||
break;
|
||||
case 'T':
|
||||
if (util::streq_l("CONNEC", name, 6)) {
|
||||
return HTTP_CONNECT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
switch (name[7]) {
|
||||
case 'D':
|
||||
if (util::streq_l("PROPFIN", name, 7)) {
|
||||
return HTTP_PROPFIND;
|
||||
}
|
||||
break;
|
||||
case 'T':
|
||||
if (util::streq_l("CHECKOU", name, 7)) {
|
||||
return HTTP_CHECKOUT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 9:
|
||||
switch (name[8]) {
|
||||
case 'E':
|
||||
if (util::streq_l("SUBSCRIB", name, 8)) {
|
||||
return HTTP_SUBSCRIBE;
|
||||
}
|
||||
break;
|
||||
case 'H':
|
||||
if (util::streq_l("PROPPATC", name, 8)) {
|
||||
return HTTP_PROPPATCH;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 10:
|
||||
switch (name[9]) {
|
||||
case 'R':
|
||||
if (util::streq_l("MKCALENDA", name, 9)) {
|
||||
return HTTP_MKCALENDAR;
|
||||
}
|
||||
break;
|
||||
case 'Y':
|
||||
if (util::streq_l("MKACTIVIT", name, 9)) {
|
||||
return HTTP_MKACTIVITY;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 11:
|
||||
switch (name[10]) {
|
||||
case 'E':
|
||||
if (util::streq_l("UNSUBSCRIB", name, 10)) {
|
||||
return HTTP_UNSUBSCRIBE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *to_method_string(int method_token) {
|
||||
// we happened to use same value for method with http-parser.
|
||||
return http_method_str(static_cast<http_method>(method_token));
|
||||
}
|
||||
|
||||
} // namespace http2
|
||||
|
||||
} // namespace nghttp2
|
||||
|
||||
11
src/http2.h
11
src/http2.h
@@ -109,7 +109,7 @@ nghttp2_nv make_nv(const std::string &name, const std::string &value,
|
||||
|
||||
// Create nghttp2_nv from string literal |name| and |value|.
|
||||
template <size_t N, size_t M>
|
||||
nghttp2_nv make_nv_ll(const char (&name)[N], const char (&value)[M]) {
|
||||
constexpr nghttp2_nv make_nv_ll(const char (&name)[N], const char (&value)[M]) {
|
||||
return {(uint8_t *)name, (uint8_t *)value, N - 1, M - 1,
|
||||
NGHTTP2_NV_FLAG_NONE};
|
||||
}
|
||||
@@ -285,10 +285,19 @@ std::string path_join(const char *base_path, size_t base_pathlen,
|
||||
// true if response has body, taking into account the request method
|
||||
// and status code.
|
||||
bool expect_response_body(const std::string &method, int status_code);
|
||||
bool expect_response_body(int method_token, int status_code);
|
||||
|
||||
// true if response has body, taking into account status code only.
|
||||
bool expect_response_body(int status_code);
|
||||
|
||||
// Looks up method token for method name |name| of length |namelen|.
|
||||
// Only methods defined in http-parser/http-parser.h (http_method) are
|
||||
// tokenized. If method name cannot be tokenized, returns -1.
|
||||
int lookup_method_token(const uint8_t *name, size_t namelen);
|
||||
int lookup_method_token(const std::string &name);
|
||||
|
||||
const char *to_method_string(int method_token);
|
||||
|
||||
} // namespace http2
|
||||
|
||||
} // namespace nghttp2
|
||||
|
||||
@@ -380,6 +380,12 @@ void test_http2_parse_link_header(void) {
|
||||
auto res = http2::parse_link_header(s, sizeof(s) - 1);
|
||||
CU_ASSERT(0 == res.size());
|
||||
}
|
||||
{
|
||||
// Error if link header ends with ';'
|
||||
const char s[] = "<url>;rel=preload;, <url>";
|
||||
auto res = http2::parse_link_header(s, sizeof(s) - 1);
|
||||
CU_ASSERT(0 == res.size());
|
||||
}
|
||||
{
|
||||
// OK if input ends with ','
|
||||
const char s[] = "<url>;rel=preload,";
|
||||
@@ -396,13 +402,13 @@ void test_http2_parse_link_header(void) {
|
||||
}
|
||||
{
|
||||
// Error if url is not enclosed by <>
|
||||
const char s[] = "url>;rel=preload;";
|
||||
const char s[] = "url>;rel=preload";
|
||||
auto res = http2::parse_link_header(s, sizeof(s) - 1);
|
||||
CU_ASSERT(0 == res.size());
|
||||
}
|
||||
{
|
||||
// Error if url is not enclosed by <>
|
||||
const char s[] = "<url;rel=preload;";
|
||||
const char s[] = "<url;rel=preload";
|
||||
auto res = http2::parse_link_header(s, sizeof(s) - 1);
|
||||
CU_ASSERT(0 == res.size());
|
||||
}
|
||||
|
||||
@@ -25,6 +25,10 @@
|
||||
#ifndef SHRPX_HTTP2_TEST_H
|
||||
#define SHRPX_HTTP2_TEST_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif // HAVE_CONFIG_H
|
||||
|
||||
namespace shrpx {
|
||||
|
||||
void test_http2_add_header(void);
|
||||
|
||||
@@ -26,7 +26,9 @@
|
||||
#include <config.h>
|
||||
#endif // HAVE_CONFIG_H
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif // HAVE_UNISTD_H
|
||||
#include <getopt.h>
|
||||
|
||||
#include <cstdio>
|
||||
@@ -99,6 +101,11 @@ static int inflate_hd(json_t *obj, nghttp2_hd_inflater *inflater, int seq) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!json_is_string(wire)) {
|
||||
fprintf(stderr, "'wire' value is not string at %d\n", seq);
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto table_size = json_object_get(obj, "header_table_size");
|
||||
|
||||
if (table_size) {
|
||||
|
||||
@@ -25,6 +25,10 @@
|
||||
#ifndef MEMCHUNK_TEST_H
|
||||
#define MEMCHUNK_TEST_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif // HAVE_CONFIG_H
|
||||
|
||||
namespace nghttp2 {
|
||||
|
||||
void test_pool_recycle(void);
|
||||
|
||||
@@ -25,9 +25,15 @@
|
||||
#include "nghttp.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif // HAVE_UNISTD_H
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif // HAVE_FCNTL_H
|
||||
#ifdef HAVE_NETINET_IN_H
|
||||
#include <netinet/in.h>
|
||||
#endif // HAVE_NETINET_IN_H
|
||||
#include <netinet/tcp.h>
|
||||
#include <getopt.h>
|
||||
|
||||
@@ -83,7 +89,7 @@ enum {
|
||||
};
|
||||
|
||||
namespace {
|
||||
auto anchors = std::array<Anchor, 5>{{
|
||||
constexpr auto anchors = std::array<Anchor, 5>{{
|
||||
{3, 0, 201}, {5, 0, 101}, {7, 0, 1}, {9, 7, 1}, {11, 3, 1},
|
||||
}};
|
||||
} // namespace
|
||||
@@ -948,9 +954,6 @@ int HttpClient::connection_made() {
|
||||
request_done(stream_user_data);
|
||||
}
|
||||
}
|
||||
// Send connection header here
|
||||
wb.write(NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
|
||||
// If upgrade succeeds, the SETTINGS value sent with
|
||||
// HTTP2-Settings header field has already been submitted to
|
||||
// session object.
|
||||
@@ -2315,8 +2318,9 @@ Options:
|
||||
filename is dereived from URI. If URI ends with '/',
|
||||
'index.html' is used as a filename. Not implemented
|
||||
yet.
|
||||
-t, --timeout=<SEC>
|
||||
Timeout each request after <SEC> seconds.
|
||||
-t, --timeout=<DURATION>
|
||||
Timeout each request after <DURATION>. Set 0 to disable
|
||||
timeout.
|
||||
-w, --window-bits=<N>
|
||||
Sets the stream level initial window size to 2**<N>-1.
|
||||
-W, --connection-window-bits=<N>
|
||||
@@ -2386,11 +2390,21 @@ Options:
|
||||
--
|
||||
|
||||
The <SIZE> argument is an integer and an optional unit (e.g., 10K is
|
||||
10 * 1024). Units are K, M and G (powers of 1024).)" << std::endl;
|
||||
10 * 1024). Units are K, M and G (powers of 1024).
|
||||
|
||||
The <DURATION> argument is an integer and an optional unit (e.g., 1s
|
||||
is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms
|
||||
(hours, minutes, seconds and milliseconds, respectively). If a unit
|
||||
is omitted, a second is used as unit.)" << std::endl;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
SSL_load_error_strings();
|
||||
SSL_library_init();
|
||||
OpenSSL_add_all_algorithms();
|
||||
OPENSSL_config(nullptr);
|
||||
|
||||
bool color = false;
|
||||
while (1) {
|
||||
static int flag = 0;
|
||||
@@ -2472,7 +2486,11 @@ int main(int argc, char **argv) {
|
||||
++config.verbose;
|
||||
break;
|
||||
case 't':
|
||||
config.timeout = atoi(optarg);
|
||||
config.timeout = util::parse_duration_with_unit(optarg);
|
||||
if (config.timeout == std::numeric_limits<double>::infinity()) {
|
||||
std::cerr << "-t: bad timeout value: " << optarg << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
case 'u':
|
||||
config.upgrade = true;
|
||||
@@ -2626,10 +2644,6 @@ int main(int argc, char **argv) {
|
||||
memset(&act, 0, sizeof(struct sigaction));
|
||||
act.sa_handler = SIG_IGN;
|
||||
sigaction(SIGPIPE, &act, nullptr);
|
||||
OPENSSL_config(nullptr);
|
||||
OpenSSL_add_all_algorithms();
|
||||
SSL_load_error_strings();
|
||||
SSL_library_init();
|
||||
reset_timer();
|
||||
return run(argv + optind, argc - optind);
|
||||
}
|
||||
|
||||
@@ -28,8 +28,12 @@
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#ifdef HAVE_SYS_SOCKET_H
|
||||
#include <sys/socket.h>
|
||||
#endif // HAVE_SYS_SOCKET_H
|
||||
#ifdef HAVE_NETDB_H
|
||||
#include <netdb.h>
|
||||
#endif // HAVE_NETDB_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -25,6 +25,10 @@
|
||||
#ifndef NGHTTP2_GZIP_TEST_H
|
||||
#define NGHTTP2_GZIP_TEST_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
@@ -24,7 +24,9 @@
|
||||
*/
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif // HAVE_UNISTD_H
|
||||
#include <signal.h>
|
||||
#include <getopt.h>
|
||||
|
||||
@@ -88,6 +90,7 @@ void print_usage(std::ostream &out) {
|
||||
|
||||
namespace {
|
||||
void print_help(std::ostream &out) {
|
||||
Config config;
|
||||
print_usage(out);
|
||||
out << R"(
|
||||
<PORT> Specify listening port number.
|
||||
@@ -128,6 +131,10 @@ Options:
|
||||
-b, --padding=<N>
|
||||
Add at most <N> bytes to a frame payload as padding.
|
||||
Specify 0 to disable padding.
|
||||
-m, --max-concurrent-streams=<N>
|
||||
Set the maximum number of the concurrent streams in one
|
||||
HTTP/2 session.
|
||||
Default: )" << config.max_concurrent_streams << R"(
|
||||
-n, --workers=<N>
|
||||
Set the number of worker threads.
|
||||
Default: 1
|
||||
@@ -148,6 +155,8 @@ Options:
|
||||
--hexdump Display the incoming traffic in hexadecimal (Canonical
|
||||
hex+ASCII display). If SSL/TLS is used, decrypted data
|
||||
are used.
|
||||
--echo-upload
|
||||
Send back uploaded content if method is POST or PUT.
|
||||
--version Display version information and exit.
|
||||
-h, --help Display this help and exit.
|
||||
|
||||
@@ -159,6 +168,14 @@ Options:
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
#ifndef NOTHREADS
|
||||
ssl::LibsslGlobalLock lock;
|
||||
#endif // NOTHREADS
|
||||
SSL_load_error_strings();
|
||||
SSL_library_init();
|
||||
OpenSSL_add_all_algorithms();
|
||||
OPENSSL_config(nullptr);
|
||||
|
||||
Config config;
|
||||
bool color = false;
|
||||
while (1) {
|
||||
@@ -173,6 +190,7 @@ int main(int argc, char **argv) {
|
||||
{"header-table-size", required_argument, nullptr, 'c'},
|
||||
{"push", required_argument, nullptr, 'p'},
|
||||
{"padding", required_argument, nullptr, 'b'},
|
||||
{"max-concurrent-streams", required_argument, nullptr, 'm'},
|
||||
{"workers", required_argument, nullptr, 'n'},
|
||||
{"error-gzip", no_argument, nullptr, 'e'},
|
||||
{"no-tls", no_argument, &flag, 1},
|
||||
@@ -182,9 +200,10 @@ int main(int argc, char **argv) {
|
||||
{"early-response", no_argument, &flag, 5},
|
||||
{"trailer", required_argument, &flag, 6},
|
||||
{"hexdump", no_argument, &flag, 7},
|
||||
{"echo-upload", no_argument, &flag, 8},
|
||||
{nullptr, 0, nullptr, 0}};
|
||||
int option_index = 0;
|
||||
int c = getopt_long(argc, argv, "DVb:c:d:ehn:p:va:", long_options,
|
||||
int c = getopt_long(argc, argv, "DVb:c:d:ehm:n:p:va:", long_options,
|
||||
&option_index);
|
||||
char *end;
|
||||
if (c == -1) {
|
||||
@@ -209,6 +228,16 @@ int main(int argc, char **argv) {
|
||||
case 'e':
|
||||
config.error_gzip = true;
|
||||
break;
|
||||
case 'm': {
|
||||
// max-concurrent-streams option
|
||||
auto n = util::parse_uint(optarg);
|
||||
if (n == -1) {
|
||||
std::cerr << "-m: invalid argument: " << optarg << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
config.max_concurrent_streams = n;
|
||||
break;
|
||||
}
|
||||
case 'n':
|
||||
#ifdef NOTHREADS
|
||||
std::cerr << "-n: WARNING: Threading disabled at build time, "
|
||||
@@ -294,6 +323,10 @@ int main(int argc, char **argv) {
|
||||
// hexdump option
|
||||
config.hexdump = true;
|
||||
break;
|
||||
case 8:
|
||||
// echo-upload option
|
||||
config.echo_upload = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -334,13 +367,6 @@ int main(int argc, char **argv) {
|
||||
memset(&act, 0, sizeof(struct sigaction));
|
||||
act.sa_handler = SIG_IGN;
|
||||
sigaction(SIGPIPE, &act, nullptr);
|
||||
OPENSSL_config(nullptr);
|
||||
OpenSSL_add_all_algorithms();
|
||||
SSL_load_error_strings();
|
||||
SSL_library_init();
|
||||
#ifndef NOTHREADS
|
||||
ssl::LibsslGlobalLock lock;
|
||||
#endif // NOTHREADS
|
||||
|
||||
reset_timer();
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
#endif // HAVE_CONFIG_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
305
src/shrpx.cc
305
src/shrpx.cc
@@ -24,25 +24,41 @@
|
||||
*/
|
||||
#include "shrpx.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#ifdef HAVE_SYS_SOCKET_H
|
||||
#include <sys/socket.h>
|
||||
#endif // HAVE_SYS_SOCKET_H
|
||||
#include <sys/un.h>
|
||||
#ifdef HAVE_NETDB_H
|
||||
#include <netdb.h>
|
||||
#endif // HAVE_NETDB_H
|
||||
#include <signal.h>
|
||||
#ifdef HAVE_NETINET_IN_H
|
||||
#include <netinet/in.h>
|
||||
#endif // HAVE_NETINET_IN_H
|
||||
#include <netinet/tcp.h>
|
||||
#ifdef HAVE_ARPA_INET_H
|
||||
#include <arpa/inet.h>
|
||||
#endif // HAVE_ARPA_INET_H
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif // HAVE_UNISTD_H
|
||||
#include <getopt.h>
|
||||
#ifdef HAVE_SYSLOG_H
|
||||
#include <syslog.h>
|
||||
#endif // HAVE_SYSLOG_H
|
||||
#include <signal.h>
|
||||
#ifdef HAVE_LIMITS_H
|
||||
#include <limits.h>
|
||||
#endif // HAVE_LIMITS_H
|
||||
#ifdef HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#endif // HAVE_SYS_TIME_H
|
||||
#include <sys/resource.h>
|
||||
#include <grp.h>
|
||||
|
||||
#include <cinttypes>
|
||||
#include <limits>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
@@ -295,11 +311,15 @@ std::unique_ptr<AcceptHandler> create_acceptor(ConnectionHandler *handler,
|
||||
fd =
|
||||
socket(rp->ai_family, rp->ai_socktype | SOCK_NONBLOCK, rp->ai_protocol);
|
||||
if (fd == -1) {
|
||||
auto error = errno;
|
||||
LOG(WARN) << "socket() syscall failed, error=" << error;
|
||||
continue;
|
||||
}
|
||||
#else // !SOCK_NONBLOCK
|
||||
fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
|
||||
if (fd == -1) {
|
||||
auto error = errno;
|
||||
LOG(WARN) << "socket() syscall failed, error=" << error;
|
||||
continue;
|
||||
}
|
||||
util::make_socket_nonblocking(fd);
|
||||
@@ -307,6 +327,10 @@ std::unique_ptr<AcceptHandler> create_acceptor(ConnectionHandler *handler,
|
||||
int val = 1;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val,
|
||||
static_cast<socklen_t>(sizeof(val))) == -1) {
|
||||
auto error = errno;
|
||||
LOG(WARN)
|
||||
<< "Failed to set SO_REUSEADDR option to listener socket, error="
|
||||
<< error;
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
@@ -315,6 +339,10 @@ std::unique_ptr<AcceptHandler> create_acceptor(ConnectionHandler *handler,
|
||||
if (family == AF_INET6) {
|
||||
if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val,
|
||||
static_cast<socklen_t>(sizeof(val))) == -1) {
|
||||
auto error = errno;
|
||||
LOG(WARN)
|
||||
<< "Failed to set IPV6_V6ONLY option to listener socket, error="
|
||||
<< error;
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
@@ -329,11 +357,24 @@ std::unique_ptr<AcceptHandler> create_acceptor(ConnectionHandler *handler,
|
||||
}
|
||||
#endif // TCP_DEFER_ACCEPT
|
||||
|
||||
if (bind(fd, rp->ai_addr, rp->ai_addrlen) == 0 &&
|
||||
listen(fd, get_config()->backlog) == 0) {
|
||||
break;
|
||||
// When we are executing new binary, and the old binary did not
|
||||
// bind privileged port (< 1024) for some reason, binding to those
|
||||
// ports will fail with permission denied error.
|
||||
if (bind(fd, rp->ai_addr, rp->ai_addrlen) == -1) {
|
||||
auto error = errno;
|
||||
LOG(WARN) << "bind() syscall failed, error=" << error;
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
if (listen(fd, get_config()->backlog) == -1) {
|
||||
auto error = errno;
|
||||
LOG(WARN) << "listen() syscall failed, error=" << error;
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (!rp) {
|
||||
@@ -752,25 +793,26 @@ bool conf_exists(const char *path) {
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
const char *DEFAULT_NPN_LIST = "h2,h2-16," NGHTTP2_PROTO_VERSION_ID ","
|
||||
constexpr char DEFAULT_NPN_LIST[] = "h2,h2-16,h2-14,"
|
||||
#ifdef HAVE_SPDYLAY
|
||||
"spdy/3.1,"
|
||||
"spdy/3.1,"
|
||||
#endif // HAVE_SPDYLAY
|
||||
"http/1.1";
|
||||
"http/1.1";
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
const char *DEFAULT_TLS_PROTO_LIST = "TLSv1.2,TLSv1.1";
|
||||
constexpr char DEFAULT_TLS_PROTO_LIST[] = "TLSv1.2,TLSv1.1";
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
const char *DEFAULT_ACCESSLOG_FORMAT = "$remote_addr - - [$time_local] "
|
||||
"\"$request\" $status $body_bytes_sent "
|
||||
"\"$http_referer\" \"$http_user_agent\"";
|
||||
constexpr char DEFAULT_ACCESSLOG_FORMAT[] =
|
||||
R"($remote_addr - - [$time_local] )"
|
||||
R"("$request" $status $body_bytes_sent )"
|
||||
R"("$http_referer" "$http_user_agent")";
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
auto DEFAULT_DOWNSTREAM_HOST = "127.0.0.1";
|
||||
constexpr char DEFAULT_DOWNSTREAM_HOST[] = "127.0.0.1";
|
||||
int16_t DEFAULT_DOWNSTREAM_PORT = 80;
|
||||
} // namespace;
|
||||
|
||||
@@ -887,6 +929,7 @@ void fill_default_config() {
|
||||
|
||||
nghttp2_option_new(&mod_config()->http2_option);
|
||||
nghttp2_option_set_no_auto_window_update(get_config()->http2_option, 1);
|
||||
nghttp2_option_set_no_recv_client_magic(get_config()->http2_option, 1);
|
||||
|
||||
nghttp2_option_new(&mod_config()->http2_client_option);
|
||||
nghttp2_option_set_no_auto_window_update(get_config()->http2_client_option,
|
||||
@@ -912,6 +955,8 @@ void fill_default_config() {
|
||||
mod_config()->fetch_ocsp_response_file =
|
||||
strcopy(PKGDATADIR "/fetch-ocsp-response");
|
||||
mod_config()->no_ocsp = false;
|
||||
mod_config()->header_field_buffer = 64 * 1024;
|
||||
mod_config()->max_header_fields = 100;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -1326,16 +1371,32 @@ HTTP:
|
||||
--altsvc=<PROTOID,PORT[,HOST,[ORIGIN]]>
|
||||
Specify protocol ID, port, host and origin of
|
||||
alternative service. <HOST> and <ORIGIN> are optional.
|
||||
They are advertised in alt-svc header field or HTTP/2
|
||||
ALTSVC frame. This option can be used multiple times to
|
||||
specify multiple alternative services. Example:
|
||||
--altsvc=h2,443
|
||||
They are advertised in alt-svc header field only in
|
||||
HTTP/1.1 frontend. This option can be used multiple
|
||||
times to specify multiple alternative services.
|
||||
Example: --altsvc=h2,443
|
||||
--add-request-header=<HEADER>
|
||||
Specify additional header field to add to request header
|
||||
set. This option just appends header field and won't
|
||||
replace anything already set. This option can be used
|
||||
several times to specify multiple header fields.
|
||||
Example: --add-request-header="foo: bar"
|
||||
--add-response-header=<HEADER>
|
||||
Specify additional header field to add to response
|
||||
header set. This option just appends header field and
|
||||
won't replace anything already set. This option can be
|
||||
used several times to specify multiple header fields.
|
||||
Example: --add-response-header="foo: bar"
|
||||
--header-field-buffer=<SIZE>
|
||||
Set maximum buffer size for incoming HTTP header field
|
||||
list. This is the sum of header name and value in
|
||||
bytes.
|
||||
Default: )"
|
||||
<< util::utos_with_unit(get_config()->header_field_buffer) << R"(
|
||||
--max-header-fields=<N>
|
||||
Set maximum number of incoming HTTP header fields, which
|
||||
appear in one request or response header field list.
|
||||
Default: )" << get_config()->max_header_fields << R"(
|
||||
|
||||
Debug:
|
||||
--frontend-http2-dump-request-header=<PATH>
|
||||
@@ -1386,6 +1447,16 @@ Misc:
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
#ifndef NOTHREADS
|
||||
nghttp2::ssl::LibsslGlobalLock lock;
|
||||
#endif // NOTHREADS
|
||||
// Initialize OpenSSL before parsing options because we create
|
||||
// SSL_CTX there.
|
||||
SSL_load_error_strings();
|
||||
SSL_library_init();
|
||||
OpenSSL_add_all_algorithms();
|
||||
OPENSSL_config(nullptr);
|
||||
|
||||
Log::set_severity_level(NOTICE);
|
||||
create_config();
|
||||
fill_default_config();
|
||||
@@ -1409,93 +1480,102 @@ int main(int argc, char **argv) {
|
||||
while (1) {
|
||||
static int flag = 0;
|
||||
static option long_options[] = {
|
||||
{"daemon", no_argument, nullptr, 'D'},
|
||||
{"log-level", required_argument, nullptr, 'L'},
|
||||
{"backend", required_argument, nullptr, 'b'},
|
||||
{"http2-max-concurrent-streams", required_argument, nullptr, 'c'},
|
||||
{"frontend", required_argument, nullptr, 'f'},
|
||||
{SHRPX_OPT_DAEMON, no_argument, nullptr, 'D'},
|
||||
{SHRPX_OPT_LOG_LEVEL, required_argument, nullptr, 'L'},
|
||||
{SHRPX_OPT_BACKEND, required_argument, nullptr, 'b'},
|
||||
{SHRPX_OPT_HTTP2_MAX_CONCURRENT_STREAMS, required_argument, nullptr,
|
||||
'c'},
|
||||
{SHRPX_OPT_FRONTEND, required_argument, nullptr, 'f'},
|
||||
{"help", no_argument, nullptr, 'h'},
|
||||
{"insecure", no_argument, nullptr, 'k'},
|
||||
{"workers", required_argument, nullptr, 'n'},
|
||||
{"client-proxy", no_argument, nullptr, 'p'},
|
||||
{"http2-proxy", no_argument, nullptr, 's'},
|
||||
{SHRPX_OPT_INSECURE, no_argument, nullptr, 'k'},
|
||||
{SHRPX_OPT_WORKERS, required_argument, nullptr, 'n'},
|
||||
{SHRPX_OPT_CLIENT_PROXY, no_argument, nullptr, 'p'},
|
||||
{SHRPX_OPT_HTTP2_PROXY, no_argument, nullptr, 's'},
|
||||
{"version", no_argument, nullptr, 'v'},
|
||||
{"frontend-frame-debug", no_argument, nullptr, 'o'},
|
||||
{"add-x-forwarded-for", no_argument, &flag, 1},
|
||||
{"frontend-http2-read-timeout", required_argument, &flag, 2},
|
||||
{"frontend-read-timeout", required_argument, &flag, 3},
|
||||
{"frontend-write-timeout", required_argument, &flag, 4},
|
||||
{"backend-read-timeout", required_argument, &flag, 5},
|
||||
{"backend-write-timeout", required_argument, &flag, 6},
|
||||
{"accesslog-file", required_argument, &flag, 7},
|
||||
{"backend-keep-alive-timeout", required_argument, &flag, 8},
|
||||
{"frontend-http2-window-bits", required_argument, &flag, 9},
|
||||
{"pid-file", required_argument, &flag, 10},
|
||||
{"user", required_argument, &flag, 11},
|
||||
{SHRPX_OPT_FRONTEND_FRAME_DEBUG, no_argument, nullptr, 'o'},
|
||||
{SHRPX_OPT_ADD_X_FORWARDED_FOR, no_argument, &flag, 1},
|
||||
{SHRPX_OPT_FRONTEND_HTTP2_READ_TIMEOUT, required_argument, &flag, 2},
|
||||
{SHRPX_OPT_FRONTEND_READ_TIMEOUT, required_argument, &flag, 3},
|
||||
{SHRPX_OPT_FRONTEND_WRITE_TIMEOUT, required_argument, &flag, 4},
|
||||
{SHRPX_OPT_BACKEND_READ_TIMEOUT, required_argument, &flag, 5},
|
||||
{SHRPX_OPT_BACKEND_WRITE_TIMEOUT, required_argument, &flag, 6},
|
||||
{SHRPX_OPT_ACCESSLOG_FILE, required_argument, &flag, 7},
|
||||
{SHRPX_OPT_BACKEND_KEEP_ALIVE_TIMEOUT, required_argument, &flag, 8},
|
||||
{SHRPX_OPT_FRONTEND_HTTP2_WINDOW_BITS, required_argument, &flag, 9},
|
||||
{SHRPX_OPT_PID_FILE, required_argument, &flag, 10},
|
||||
{SHRPX_OPT_USER, required_argument, &flag, 11},
|
||||
{"conf", required_argument, &flag, 12},
|
||||
{"syslog-facility", required_argument, &flag, 14},
|
||||
{"backlog", required_argument, &flag, 15},
|
||||
{"ciphers", required_argument, &flag, 16},
|
||||
{"client", no_argument, &flag, 17},
|
||||
{"backend-http2-window-bits", required_argument, &flag, 18},
|
||||
{"cacert", required_argument, &flag, 19},
|
||||
{"backend-ipv4", no_argument, &flag, 20},
|
||||
{"backend-ipv6", no_argument, &flag, 21},
|
||||
{"private-key-passwd-file", required_argument, &flag, 22},
|
||||
{"no-via", no_argument, &flag, 23},
|
||||
{"subcert", required_argument, &flag, 24},
|
||||
{"http2-bridge", no_argument, &flag, 25},
|
||||
{"backend-http-proxy-uri", required_argument, &flag, 26},
|
||||
{"backend-no-tls", no_argument, &flag, 27},
|
||||
{"frontend-no-tls", no_argument, &flag, 29},
|
||||
{"backend-tls-sni-field", required_argument, &flag, 31},
|
||||
{"dh-param-file", required_argument, &flag, 33},
|
||||
{"read-rate", required_argument, &flag, 34},
|
||||
{"read-burst", required_argument, &flag, 35},
|
||||
{"write-rate", required_argument, &flag, 36},
|
||||
{"write-burst", required_argument, &flag, 37},
|
||||
{"npn-list", required_argument, &flag, 38},
|
||||
{"verify-client", no_argument, &flag, 39},
|
||||
{"verify-client-cacert", required_argument, &flag, 40},
|
||||
{"client-private-key-file", required_argument, &flag, 41},
|
||||
{"client-cert-file", required_argument, &flag, 42},
|
||||
{"frontend-http2-dump-request-header", required_argument, &flag, 43},
|
||||
{"frontend-http2-dump-response-header", required_argument, &flag, 44},
|
||||
{"http2-no-cookie-crumbling", no_argument, &flag, 45},
|
||||
{"frontend-http2-connection-window-bits", required_argument, &flag, 46},
|
||||
{"backend-http2-connection-window-bits", required_argument, &flag, 47},
|
||||
{"tls-proto-list", required_argument, &flag, 48},
|
||||
{"padding", required_argument, &flag, 49},
|
||||
{"worker-read-rate", required_argument, &flag, 50},
|
||||
{"worker-read-burst", required_argument, &flag, 51},
|
||||
{"worker-write-rate", required_argument, &flag, 52},
|
||||
{"worker-write-burst", required_argument, &flag, 53},
|
||||
{"altsvc", required_argument, &flag, 54},
|
||||
{"add-response-header", required_argument, &flag, 55},
|
||||
{"worker-frontend-connections", required_argument, &flag, 56},
|
||||
{"accesslog-syslog", no_argument, &flag, 57},
|
||||
{"errorlog-file", required_argument, &flag, 58},
|
||||
{"errorlog-syslog", no_argument, &flag, 59},
|
||||
{"stream-read-timeout", required_argument, &flag, 60},
|
||||
{"stream-write-timeout", required_argument, &flag, 61},
|
||||
{"no-location-rewrite", no_argument, &flag, 62},
|
||||
{"backend-http1-connections-per-host", required_argument, &flag, 63},
|
||||
{"listener-disable-timeout", required_argument, &flag, 64},
|
||||
{"strip-incoming-x-forwarded-for", no_argument, &flag, 65},
|
||||
{"accesslog-format", required_argument, &flag, 66},
|
||||
{"backend-http1-connections-per-frontend", required_argument, &flag,
|
||||
67},
|
||||
{"tls-ticket-key-file", required_argument, &flag, 68},
|
||||
{"rlimit-nofile", required_argument, &flag, 69},
|
||||
{"tls-ctx-per-worker", no_argument, &flag, 70},
|
||||
{"backend-response-buffer", required_argument, &flag, 71},
|
||||
{"backend-request-buffer", required_argument, &flag, 72},
|
||||
{"no-host-rewrite", no_argument, &flag, 73},
|
||||
{"no-server-push", no_argument, &flag, 74},
|
||||
{"backend-http2-connections-per-worker", required_argument, &flag, 76},
|
||||
{"fetch-ocsp-response-file", required_argument, &flag, 77},
|
||||
{"ocsp-update-interval", required_argument, &flag, 78},
|
||||
{"no-ocsp", no_argument, &flag, 79},
|
||||
{SHRPX_OPT_SYSLOG_FACILITY, required_argument, &flag, 14},
|
||||
{SHRPX_OPT_BACKLOG, required_argument, &flag, 15},
|
||||
{SHRPX_OPT_CIPHERS, required_argument, &flag, 16},
|
||||
{SHRPX_OPT_CLIENT, no_argument, &flag, 17},
|
||||
{SHRPX_OPT_BACKEND_HTTP2_WINDOW_BITS, required_argument, &flag, 18},
|
||||
{SHRPX_OPT_CACERT, required_argument, &flag, 19},
|
||||
{SHRPX_OPT_BACKEND_IPV4, no_argument, &flag, 20},
|
||||
{SHRPX_OPT_BACKEND_IPV6, no_argument, &flag, 21},
|
||||
{SHRPX_OPT_PRIVATE_KEY_PASSWD_FILE, required_argument, &flag, 22},
|
||||
{SHRPX_OPT_NO_VIA, no_argument, &flag, 23},
|
||||
{SHRPX_OPT_SUBCERT, required_argument, &flag, 24},
|
||||
{SHRPX_OPT_HTTP2_BRIDGE, no_argument, &flag, 25},
|
||||
{SHRPX_OPT_BACKEND_HTTP_PROXY_URI, required_argument, &flag, 26},
|
||||
{SHRPX_OPT_BACKEND_NO_TLS, no_argument, &flag, 27},
|
||||
{SHRPX_OPT_FRONTEND_NO_TLS, no_argument, &flag, 29},
|
||||
{SHRPX_OPT_BACKEND_TLS_SNI_FIELD, required_argument, &flag, 31},
|
||||
{SHRPX_OPT_DH_PARAM_FILE, required_argument, &flag, 33},
|
||||
{SHRPX_OPT_READ_RATE, required_argument, &flag, 34},
|
||||
{SHRPX_OPT_READ_BURST, required_argument, &flag, 35},
|
||||
{SHRPX_OPT_WRITE_RATE, required_argument, &flag, 36},
|
||||
{SHRPX_OPT_WRITE_BURST, required_argument, &flag, 37},
|
||||
{SHRPX_OPT_NPN_LIST, required_argument, &flag, 38},
|
||||
{SHRPX_OPT_VERIFY_CLIENT, no_argument, &flag, 39},
|
||||
{SHRPX_OPT_VERIFY_CLIENT_CACERT, required_argument, &flag, 40},
|
||||
{SHRPX_OPT_CLIENT_PRIVATE_KEY_FILE, required_argument, &flag, 41},
|
||||
{SHRPX_OPT_CLIENT_CERT_FILE, required_argument, &flag, 42},
|
||||
{SHRPX_OPT_FRONTEND_HTTP2_DUMP_REQUEST_HEADER, required_argument, &flag,
|
||||
43},
|
||||
{SHRPX_OPT_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER, required_argument,
|
||||
&flag, 44},
|
||||
{SHRPX_OPT_HTTP2_NO_COOKIE_CRUMBLING, no_argument, &flag, 45},
|
||||
{SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS, required_argument,
|
||||
&flag, 46},
|
||||
{SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_BITS, required_argument,
|
||||
&flag, 47},
|
||||
{SHRPX_OPT_TLS_PROTO_LIST, required_argument, &flag, 48},
|
||||
{SHRPX_OPT_PADDING, required_argument, &flag, 49},
|
||||
{SHRPX_OPT_WORKER_READ_RATE, required_argument, &flag, 50},
|
||||
{SHRPX_OPT_WORKER_READ_BURST, required_argument, &flag, 51},
|
||||
{SHRPX_OPT_WORKER_WRITE_RATE, required_argument, &flag, 52},
|
||||
{SHRPX_OPT_WORKER_WRITE_BURST, required_argument, &flag, 53},
|
||||
{SHRPX_OPT_ALTSVC, required_argument, &flag, 54},
|
||||
{SHRPX_OPT_ADD_RESPONSE_HEADER, required_argument, &flag, 55},
|
||||
{SHRPX_OPT_WORKER_FRONTEND_CONNECTIONS, required_argument, &flag, 56},
|
||||
{SHRPX_OPT_ACCESSLOG_SYSLOG, no_argument, &flag, 57},
|
||||
{SHRPX_OPT_ERRORLOG_FILE, required_argument, &flag, 58},
|
||||
{SHRPX_OPT_ERRORLOG_SYSLOG, no_argument, &flag, 59},
|
||||
{SHRPX_OPT_STREAM_READ_TIMEOUT, required_argument, &flag, 60},
|
||||
{SHRPX_OPT_STREAM_WRITE_TIMEOUT, required_argument, &flag, 61},
|
||||
{SHRPX_OPT_NO_LOCATION_REWRITE, no_argument, &flag, 62},
|
||||
{SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_HOST, required_argument, &flag,
|
||||
63},
|
||||
{SHRPX_OPT_LISTENER_DISABLE_TIMEOUT, required_argument, &flag, 64},
|
||||
{SHRPX_OPT_STRIP_INCOMING_X_FORWARDED_FOR, no_argument, &flag, 65},
|
||||
{SHRPX_OPT_ACCESSLOG_FORMAT, required_argument, &flag, 66},
|
||||
{SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND, required_argument,
|
||||
&flag, 67},
|
||||
{SHRPX_OPT_TLS_TICKET_KEY_FILE, required_argument, &flag, 68},
|
||||
{SHRPX_OPT_RLIMIT_NOFILE, required_argument, &flag, 69},
|
||||
{SHRPX_OPT_BACKEND_RESPONSE_BUFFER, required_argument, &flag, 71},
|
||||
{SHRPX_OPT_BACKEND_REQUEST_BUFFER, required_argument, &flag, 72},
|
||||
{SHRPX_OPT_NO_HOST_REWRITE, no_argument, &flag, 73},
|
||||
{SHRPX_OPT_NO_SERVER_PUSH, no_argument, &flag, 74},
|
||||
{SHRPX_OPT_BACKEND_HTTP2_CONNECTIONS_PER_WORKER, required_argument,
|
||||
&flag, 76},
|
||||
{SHRPX_OPT_FETCH_OCSP_RESPONSE_FILE, required_argument, &flag, 77},
|
||||
{SHRPX_OPT_OCSP_UPDATE_INTERVAL, required_argument, &flag, 78},
|
||||
{SHRPX_OPT_NO_OCSP, no_argument, &flag, 79},
|
||||
{SHRPX_OPT_HEADER_FIELD_BUFFER, required_argument, &flag, 80},
|
||||
{SHRPX_OPT_MAX_HEADER_FIELDS, required_argument, &flag, 81},
|
||||
{SHRPX_OPT_ADD_REQUEST_HEADER, required_argument, &flag, 82},
|
||||
{nullptr, 0, nullptr, 0}};
|
||||
|
||||
int option_index = 0;
|
||||
@@ -1846,6 +1926,18 @@ int main(int argc, char **argv) {
|
||||
// --no-ocsp
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_NO_OCSP, "yes");
|
||||
break;
|
||||
case 80:
|
||||
// --header-field-buffer
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_HEADER_FIELD_BUFFER, optarg);
|
||||
break;
|
||||
case 81:
|
||||
// --max-header-fields
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_MAX_HEADER_FIELDS, optarg);
|
||||
break;
|
||||
case 82:
|
||||
// --add-request-header
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_ADD_REQUEST_HEADER, optarg);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -1855,13 +1947,6 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize OpenSSL before parsing options because we create
|
||||
// SSL_CTX there.
|
||||
OPENSSL_config(nullptr);
|
||||
OpenSSL_add_all_algorithms();
|
||||
SSL_load_error_strings();
|
||||
SSL_library_init();
|
||||
|
||||
if (conf_exists(get_config()->conf_path.get())) {
|
||||
if (load_config(get_config()->conf_path.get()) == -1) {
|
||||
LOG(FATAL) << "Failed to load configuration from "
|
||||
@@ -1886,10 +1971,6 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef NOTHREADS
|
||||
auto lock = make_unique<nghttp2::ssl::LibsslGlobalLock>();
|
||||
#endif // NOTHREADS
|
||||
|
||||
if (get_config()->accesslog_syslog || get_config()->errorlog_syslog) {
|
||||
openlog("nghttpx", LOG_NDELAY | LOG_NOWAIT | LOG_PID,
|
||||
get_config()->syslog_facility);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user