Merge branch 'master' into simple-extensions
This commit is contained in:
commit
eb4e402aae
|
@ -0,0 +1,16 @@
|
||||||
|
#include <tunables/global>
|
||||||
|
|
||||||
|
/usr/sbin/nghttpx {
|
||||||
|
#include <abstractions/base>
|
||||||
|
#include <abstractions/nameservice>
|
||||||
|
#include <abstractions/openssl>
|
||||||
|
|
||||||
|
capability setgid,
|
||||||
|
capability setuid,
|
||||||
|
|
||||||
|
/usr/sbin/nghttpx rmix, # allow to run itself
|
||||||
|
/etc/nghttpx/nghttpx.conf r, # allow to read the config file
|
||||||
|
/etc/ssl/** r, # give access to ssl keys
|
||||||
|
|
||||||
|
/{,var/}run/nghttpx.pid lw, # allow to store a pid file
|
||||||
|
}
|
|
@ -108,7 +108,7 @@ APIDOCS= \
|
||||||
nghttp2_session_mem_recv.rst \
|
nghttp2_session_mem_recv.rst \
|
||||||
nghttp2_session_mem_send.rst \
|
nghttp2_session_mem_send.rst \
|
||||||
nghttp2_session_recv.rst \
|
nghttp2_session_recv.rst \
|
||||||
nghttp2_session_request_allowed.rst \
|
nghttp2_session_check_request_allowed.rst \
|
||||||
nghttp2_session_resume_data.rst \
|
nghttp2_session_resume_data.rst \
|
||||||
nghttp2_session_send.rst \
|
nghttp2_session_send.rst \
|
||||||
nghttp2_session_server_new.rst \
|
nghttp2_session_server_new.rst \
|
||||||
|
@ -119,6 +119,7 @@ APIDOCS= \
|
||||||
nghttp2_session_terminate_session.rst \
|
nghttp2_session_terminate_session.rst \
|
||||||
nghttp2_session_terminate_session2.rst \
|
nghttp2_session_terminate_session2.rst \
|
||||||
nghttp2_session_upgrade.rst \
|
nghttp2_session_upgrade.rst \
|
||||||
|
nghttp2_session_upgrade2.rst \
|
||||||
nghttp2_session_want_read.rst \
|
nghttp2_session_want_read.rst \
|
||||||
nghttp2_session_want_write.rst \
|
nghttp2_session_want_write.rst \
|
||||||
nghttp2_stream_get_first_child.rst \
|
nghttp2_stream_get_first_child.rst \
|
||||||
|
|
|
@ -22,7 +22,7 @@ starts with ``/**``. In other words, it only processes the comment
|
||||||
block starting ``/**``. The comment block must end with ``*/``. The
|
block starting ``/**``. The comment block must end with ``*/``. The
|
||||||
``mkapiref.py`` requires that which type of the object this comment
|
``mkapiref.py`` requires that which type of the object this comment
|
||||||
block refers to. To specify the type of the object, the next line
|
block refers to. To specify the type of the object, the next line
|
||||||
must contain the so-caled action keyword. Currently, the following
|
must contain the so-called action keyword. Currently, the following
|
||||||
action keywords are supported: ``@function``, ``@functypedef``,
|
action keywords are supported: ``@function``, ``@functypedef``,
|
||||||
``@enum``, ``@struct`` and ``@union``. The following sections
|
``@enum``, ``@struct`` and ``@union``. The following sections
|
||||||
describes each action keyword.
|
describes each action keyword.
|
||||||
|
|
14
doc/h2load.1
14
doc/h2load.1
|
@ -1,6 +1,6 @@
|
||||||
.\" Man page generated from reStructuredText.
|
.\" Man page generated from reStructuredText.
|
||||||
.
|
.
|
||||||
.TH "H2LOAD" "1" "October 25, 2015" "1.4.0" "nghttp2"
|
.TH "H2LOAD" "1" "November 12, 2015" "1.4.1-DEV" "nghttp2"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
h2load \- HTTP/2 benchmarking tool
|
h2load \- HTTP/2 benchmarking tool
|
||||||
.
|
.
|
||||||
|
@ -288,14 +288,18 @@ The number of status code h2load received.
|
||||||
.TP
|
.TP
|
||||||
.B total
|
.B total
|
||||||
The number of bytes received from the server "on the wire". If
|
The number of bytes received from the server "on the wire". If
|
||||||
requests were made via TLS, this value is the number of decrpyted
|
requests were made via TLS, this value is the number of decrypted
|
||||||
bytes.
|
bytes.
|
||||||
.TP
|
.TP
|
||||||
.B headers
|
.B headers
|
||||||
The number of response header bytes from the server without
|
The number of response header bytes from the server without
|
||||||
decompression. For HTTP/2, this is the sum of the payload of
|
decompression. The \fBspace savings\fP shows efficiency of header
|
||||||
HEADERS frame. For SPDY, this is the sum of the payload of
|
compression. Let \fBdecompressed(headers)\fP to the number of bytes
|
||||||
SYN_REPLY frame.
|
used for header fields after decompression. The \fBspace savings\fP
|
||||||
|
is calculated by (1 \- \fBheaders\fP / \fBdecompressed(headers)\fP) *
|
||||||
|
100. For HTTP/1.1, this is usually 0.00%, since it does not have
|
||||||
|
header compression. For HTTP/2 and SPDY, it shows some insightful
|
||||||
|
numbers.
|
||||||
.TP
|
.TP
|
||||||
.B data
|
.B data
|
||||||
The number of response body bytes received from the server.
|
The number of response body bytes received from the server.
|
||||||
|
|
|
@ -239,13 +239,17 @@ status codes
|
||||||
traffic
|
traffic
|
||||||
total
|
total
|
||||||
The number of bytes received from the server "on the wire". If
|
The number of bytes received from the server "on the wire". If
|
||||||
requests were made via TLS, this value is the number of decrpyted
|
requests were made via TLS, this value is the number of decrypted
|
||||||
bytes.
|
bytes.
|
||||||
headers
|
headers
|
||||||
The number of response header bytes from the server without
|
The number of response header bytes from the server without
|
||||||
decompression. For HTTP/2, this is the sum of the payload of
|
decompression. The ``space savings`` shows efficiency of header
|
||||||
HEADERS frame. For SPDY, this is the sum of the payload of
|
compression. Let ``decompressed(headers)`` to the number of bytes
|
||||||
SYN_REPLY frame.
|
used for header fields after decompression. The ``space savings``
|
||||||
|
is calculated by (1 - ``headers`` / ``decompressed(headers)``) *
|
||||||
|
100. For HTTP/1.1, this is usually 0.00%, since it does not have
|
||||||
|
header compression. For HTTP/2 and SPDY, it shows some insightful
|
||||||
|
numbers.
|
||||||
data
|
data
|
||||||
The number of response body bytes received from the server.
|
The number of response body bytes received from the server.
|
||||||
|
|
||||||
|
|
|
@ -30,13 +30,17 @@ status codes
|
||||||
traffic
|
traffic
|
||||||
total
|
total
|
||||||
The number of bytes received from the server "on the wire". If
|
The number of bytes received from the server "on the wire". If
|
||||||
requests were made via TLS, this value is the number of decrpyted
|
requests were made via TLS, this value is the number of decrypted
|
||||||
bytes.
|
bytes.
|
||||||
headers
|
headers
|
||||||
The number of response header bytes from the server without
|
The number of response header bytes from the server without
|
||||||
decompression. For HTTP/2, this is the sum of the payload of
|
decompression. The ``space savings`` shows efficiency of header
|
||||||
HEADERS frame. For SPDY, this is the sum of the payload of
|
compression. Let ``decompressed(headers)`` to the number of bytes
|
||||||
SYN_REPLY frame.
|
used for header fields after decompression. The ``space savings``
|
||||||
|
is calculated by (1 - ``headers`` / ``decompressed(headers)``) *
|
||||||
|
100. For HTTP/1.1, this is usually 0.00%, since it does not have
|
||||||
|
header compression. For HTTP/2 and SPDY, it shows some insightful
|
||||||
|
numbers.
|
||||||
data
|
data
|
||||||
The number of response body bytes received from the server.
|
The number of response body bytes received from the server.
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
.\" Man page generated from reStructuredText.
|
.\" Man page generated from reStructuredText.
|
||||||
.
|
.
|
||||||
.TH "NGHTTP" "1" "October 25, 2015" "1.4.0" "nghttp2"
|
.TH "NGHTTP" "1" "November 12, 2015" "1.4.1-DEV" "nghttp2"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
nghttp \- HTTP/2 client
|
nghttp \- HTTP/2 client
|
||||||
.
|
.
|
||||||
|
@ -58,7 +58,7 @@ Discard downloaded data.
|
||||||
.TP
|
.TP
|
||||||
.B \-O, \-\-remote\-name
|
.B \-O, \-\-remote\-name
|
||||||
Save download data in the current directory. The
|
Save download data in the current directory. The
|
||||||
filename is dereived from URI. If URI ends with \(aq\fI/\fP\(aq,
|
filename is derived from URI. If URI ends with \(aq\fI/\fP\(aq,
|
||||||
\(aqindex.html\(aq is used as a filename. Not implemented
|
\(aqindex.html\(aq is used as a filename. Not implemented
|
||||||
yet.
|
yet.
|
||||||
.UNINDENT
|
.UNINDENT
|
||||||
|
|
|
@ -36,7 +36,7 @@ OPTIONS
|
||||||
.. option:: -O, --remote-name
|
.. option:: -O, --remote-name
|
||||||
|
|
||||||
Save download data in the current directory. The
|
Save download data in the current directory. The
|
||||||
filename is dereived from URI. If URI ends with '*/*',
|
filename is derived from URI. If URI ends with '*/*',
|
||||||
'index.html' is used as a filename. Not implemented
|
'index.html' is used as a filename. Not implemented
|
||||||
yet.
|
yet.
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
.\" Man page generated from reStructuredText.
|
.\" Man page generated from reStructuredText.
|
||||||
.
|
.
|
||||||
.TH "NGHTTPD" "1" "October 25, 2015" "1.4.0" "nghttp2"
|
.TH "NGHTTPD" "1" "November 12, 2015" "1.4.1-DEV" "nghttp2"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
nghttpd \- HTTP/2 server
|
nghttpd \- HTTP/2 server
|
||||||
.
|
.
|
||||||
|
@ -172,6 +172,14 @@ Send back uploaded content if method is POST or PUT.
|
||||||
.UNINDENT
|
.UNINDENT
|
||||||
.INDENT 0.0
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
|
.B \-\-mime\-types\-file=<PATH>
|
||||||
|
Path to file that contains MIME media types and the
|
||||||
|
extensions that represent them.
|
||||||
|
.sp
|
||||||
|
Default: \fB/etc/mime.types\fP
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
|
.TP
|
||||||
.B \-\-version
|
.B \-\-version
|
||||||
Display version information and exit.
|
Display version information and exit.
|
||||||
.UNINDENT
|
.UNINDENT
|
||||||
|
|
|
@ -132,6 +132,13 @@ OPTIONS
|
||||||
|
|
||||||
Send back uploaded content if method is POST or PUT.
|
Send back uploaded content if method is POST or PUT.
|
||||||
|
|
||||||
|
.. option:: --mime-types-file=<PATH>
|
||||||
|
|
||||||
|
Path to file that contains MIME media types and the
|
||||||
|
extensions that represent them.
|
||||||
|
|
||||||
|
Default: ``/etc/mime.types``
|
||||||
|
|
||||||
.. option:: --version
|
.. option:: --version
|
||||||
|
|
||||||
Display version information and exit.
|
Display version information and exit.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
.\" Man page generated from reStructuredText.
|
.\" Man page generated from reStructuredText.
|
||||||
.
|
.
|
||||||
.TH "NGHTTPX" "1" "October 25, 2015" "1.4.0" "nghttp2"
|
.TH "NGHTTPX" "1" "November 12, 2015" "1.4.1-DEV" "nghttp2"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
nghttpx \- HTTP/2 proxy
|
nghttpx \- HTTP/2 proxy
|
||||||
.
|
.
|
||||||
|
@ -267,7 +267,7 @@ is done for each pattern group.
|
||||||
Set maximum number of backend concurrent HTTP/1
|
Set maximum number of backend concurrent HTTP/1
|
||||||
connections per origin host. This option is meaningful
|
connections per origin host. This option is meaningful
|
||||||
when \fI\%\-s\fP option is used. The origin host is determined
|
when \fI\%\-s\fP option is used. The origin host is determined
|
||||||
by authority portion of requset URI (or :authority
|
by authority portion of request URI (or :authority
|
||||||
header field for HTTP/2). To limit the number of
|
header field for HTTP/2). To limit the number of
|
||||||
connections per frontend for default mode, use
|
connections per frontend for default mode, use
|
||||||
\fI\%\-\-backend\-http1\-connections\-per\-frontend\fP\&.
|
\fI\%\-\-backend\-http1\-connections\-per\-frontend\fP\&.
|
||||||
|
@ -1377,7 +1377,7 @@ Clear all existing response header fields.
|
||||||
Return custom response \fIbody\fP to a client. When this method
|
Return custom response \fIbody\fP to a client. When this method
|
||||||
is called in request phase hook, the request is not forwarded
|
is called in request phase hook, the request is not forwarded
|
||||||
to the backend, and response phase hook for this request will
|
to the backend, and response phase hook for this request will
|
||||||
not be invoked. When this method is called in resonse phase
|
not be invoked. When this method is called in response phase
|
||||||
hook, response from backend server is canceled and discarded.
|
hook, response from backend server is canceled and discarded.
|
||||||
The status code and response header fields should be set
|
The status code and response header fields should be set
|
||||||
before using this method. To set status code, use :rb:meth To
|
before using this method. To set status code, use :rb:meth To
|
||||||
|
@ -1395,7 +1395,7 @@ It is an error to call this method twice for a given request.
|
||||||
.UNINDENT
|
.UNINDENT
|
||||||
.SS MRUBY EXAMPLES
|
.SS MRUBY EXAMPLES
|
||||||
.sp
|
.sp
|
||||||
Modify requet path:
|
Modify request path:
|
||||||
.INDENT 0.0
|
.INDENT 0.0
|
||||||
.INDENT 3.5
|
.INDENT 3.5
|
||||||
.sp
|
.sp
|
||||||
|
|
|
@ -236,7 +236,7 @@ Performance
|
||||||
Set maximum number of backend concurrent HTTP/1
|
Set maximum number of backend concurrent HTTP/1
|
||||||
connections per origin host. This option is meaningful
|
connections per origin host. This option is meaningful
|
||||||
when :option:`-s` option is used. The origin host is determined
|
when :option:`-s` option is used. The origin host is determined
|
||||||
by authority portion of requset URI (or :authority
|
by authority portion of request URI (or :authority
|
||||||
header field for HTTP/2). To limit the number of
|
header field for HTTP/2). To limit the number of
|
||||||
connections per frontend for default mode, use
|
connections per frontend for default mode, use
|
||||||
:option:`--backend-http1-connections-per-frontend`\.
|
:option:`--backend-http1-connections-per-frontend`\.
|
||||||
|
@ -1248,7 +1248,7 @@ respectively.
|
||||||
Return custom response *body* to a client. When this method
|
Return custom response *body* to a client. When this method
|
||||||
is called in request phase hook, the request is not forwarded
|
is called in request phase hook, the request is not forwarded
|
||||||
to the backend, and response phase hook for this request will
|
to the backend, and response phase hook for this request will
|
||||||
not be invoked. When this method is called in resonse phase
|
not be invoked. When this method is called in response phase
|
||||||
hook, response from backend server is canceled and discarded.
|
hook, response from backend server is canceled and discarded.
|
||||||
The status code and response header fields should be set
|
The status code and response header fields should be set
|
||||||
before using this method. To set status code, use :rb:meth To
|
before using this method. To set status code, use :rb:meth To
|
||||||
|
@ -1266,7 +1266,7 @@ respectively.
|
||||||
MRUBY EXAMPLES
|
MRUBY EXAMPLES
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Modify requet path:
|
Modify request path:
|
||||||
|
|
||||||
.. code-block:: ruby
|
.. code-block:: ruby
|
||||||
|
|
||||||
|
|
|
@ -354,7 +354,7 @@ respectively.
|
||||||
Return custom response *body* to a client. When this method
|
Return custom response *body* to a client. When this method
|
||||||
is called in request phase hook, the request is not forwarded
|
is called in request phase hook, the request is not forwarded
|
||||||
to the backend, and response phase hook for this request will
|
to the backend, and response phase hook for this request will
|
||||||
not be invoked. When this method is called in resonse phase
|
not be invoked. When this method is called in response phase
|
||||||
hook, response from backend server is canceled and discarded.
|
hook, response from backend server is canceled and discarded.
|
||||||
The status code and response header fields should be set
|
The status code and response header fields should be set
|
||||||
before using this method. To set status code, use :rb:meth To
|
before using this method. To set status code, use :rb:meth To
|
||||||
|
@ -372,7 +372,7 @@ respectively.
|
||||||
MRUBY EXAMPLES
|
MRUBY EXAMPLES
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Modify requet path:
|
Modify request path:
|
||||||
|
|
||||||
.. code-block:: ruby
|
.. code-block:: ruby
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ We use clang-format to format source code consistently. The
|
||||||
clang-format configuration file .clang-format is located at the root
|
clang-format configuration file .clang-format is located at the root
|
||||||
directory. Since clang-format produces slightly different results
|
directory. Since clang-format produces slightly different results
|
||||||
between versions, we currently use clang-format which comes with
|
between versions, we currently use clang-format which comes with
|
||||||
clang-3.5.
|
clang-3.6.
|
||||||
|
|
||||||
To detect any violation to the coding style, we recommend to setup git
|
To detect any violation to the coding style, we recommend to setup git
|
||||||
pre-commit hook to check coding style of the changes you introduced.
|
pre-commit hook to check coding style of the changes you introduced.
|
||||||
|
@ -35,7 +35,7 @@ The pre-commit file is located at the root directory. Copy it under
|
||||||
.git/hooks and make sure that it is executable. The pre-commit script
|
.git/hooks and make sure that it is executable. The pre-commit script
|
||||||
uses clang-format-diff.py to detect any style errors. If it is not in
|
uses clang-format-diff.py to detect any style errors. If it is not in
|
||||||
your PATH or it exists under different name (e.g.,
|
your PATH or it exists under different name (e.g.,
|
||||||
clang-format-diff-3.5 in debian), either add it to PATH variable or
|
clang-format-diff-3.6 in debian), either add it to PATH variable or
|
||||||
add git option ``clangformatdiff.binary`` to point to the script.
|
add git option ``clangformatdiff.binary`` to point to the script.
|
||||||
|
|
||||||
For emacs users, integrating clang-format to emacs is very easy.
|
For emacs users, integrating clang-format to emacs is very easy.
|
||||||
|
|
|
@ -459,10 +459,9 @@ static void ctl_poll(struct pollfd *pollfd, struct Connection *connection) {
|
||||||
static void submit_request(struct Connection *connection, struct Request *req) {
|
static void submit_request(struct Connection *connection, struct Request *req) {
|
||||||
int32_t stream_id;
|
int32_t stream_id;
|
||||||
/* Make sure that the last item is NULL */
|
/* Make sure that the last item is NULL */
|
||||||
const nghttp2_nv nva[] = {MAKE_NV(":method", "GET"),
|
const nghttp2_nv nva[] = {
|
||||||
MAKE_NV_CS(":path", req->path),
|
MAKE_NV(":method", "GET"), MAKE_NV_CS(":path", req->path),
|
||||||
MAKE_NV(":scheme", "https"),
|
MAKE_NV(":scheme", "https"), MAKE_NV_CS(":authority", req->hostport),
|
||||||
MAKE_NV_CS(":authority", req->hostport),
|
|
||||||
MAKE_NV("accept", "*/*"),
|
MAKE_NV("accept", "*/*"),
|
||||||
MAKE_NV("user-agent", "nghttp2/" NGHTTP2_VERSION)};
|
MAKE_NV("user-agent", "nghttp2/" NGHTTP2_VERSION)};
|
||||||
|
|
||||||
|
|
|
@ -434,7 +434,19 @@ typedef enum {
|
||||||
* Header Field never Indexed" representation must be used in HPACK
|
* Header Field never Indexed" representation must be used in HPACK
|
||||||
* encoding). Other implementation calls this bit as "sensitive".
|
* encoding). Other implementation calls this bit as "sensitive".
|
||||||
*/
|
*/
|
||||||
NGHTTP2_NV_FLAG_NO_INDEX = 0x01
|
NGHTTP2_NV_FLAG_NO_INDEX = 0x01,
|
||||||
|
/**
|
||||||
|
* This flag is set solely by application. If this flag is set, the
|
||||||
|
* library does not make a copy of header field name. This could
|
||||||
|
* improve performance.
|
||||||
|
*/
|
||||||
|
NGHTTP2_NV_FLAG_NO_COPY_NAME = 0x02,
|
||||||
|
/**
|
||||||
|
* This flag is set solely by application. If this flag is set, the
|
||||||
|
* library does not make a copy of header field value. This could
|
||||||
|
* improve performance.
|
||||||
|
*/
|
||||||
|
NGHTTP2_NV_FLAG_NO_COPY_VALUE = 0x04
|
||||||
} nghttp2_nv_flag;
|
} nghttp2_nv_flag;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -446,17 +458,27 @@ typedef struct {
|
||||||
/**
|
/**
|
||||||
* The |name| byte string. If this struct is presented from library
|
* The |name| byte string. If this struct is presented from library
|
||||||
* (e.g., :type:`nghttp2_on_frame_recv_callback`), |name| is
|
* (e.g., :type:`nghttp2_on_frame_recv_callback`), |name| is
|
||||||
* guaranteed to be NULL-terminated. When application is
|
* guaranteed to be NULL-terminated. For some callbacks
|
||||||
* constructing this struct, |name| is not required to be
|
* (:type:`nghttp2_before_frame_send_callback`,
|
||||||
|
* :type:`nghttp2_on_frame_send_callback`, and
|
||||||
|
* :type:`nghttp2_on_frame_not_send_callback`), it may not be
|
||||||
|
* NULL-terminated if header field is passed from application with
|
||||||
|
* the flag :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME`). When application
|
||||||
|
* is constructing this struct, |name| is not required to be
|
||||||
* NULL-terminated.
|
* NULL-terminated.
|
||||||
*/
|
*/
|
||||||
uint8_t *name;
|
uint8_t *name;
|
||||||
/**
|
/**
|
||||||
* The |value| byte string. If this struct is presented from
|
* The |value| byte string. If this struct is presented from
|
||||||
* library (e.g., :type:`nghttp2_on_frame_recv_callback`), |value|
|
* library (e.g., :type:`nghttp2_on_frame_recv_callback`), |value|
|
||||||
* is guaranteed to be NULL-terminated. When application is
|
* is guaranteed to be NULL-terminated. For some callbacks
|
||||||
* constructing this struct, |value| is not required to be
|
* (:type:`nghttp2_before_frame_send_callback`,
|
||||||
* NULL-terminated.
|
* :type:`nghttp2_on_frame_send_callback`, and
|
||||||
|
* :type:`nghttp2_on_frame_not_send_callback`), it may not be
|
||||||
|
* NULL-terminated if header field is passed from application with
|
||||||
|
* the flag :enum:`NGHTTP2_NV_FLAG_NO_COPY_VALUE`). When
|
||||||
|
* application is constructing this struct, |value| is not required
|
||||||
|
* to be NULL-terminated.
|
||||||
*/
|
*/
|
||||||
uint8_t *value;
|
uint8_t *value;
|
||||||
/**
|
/**
|
||||||
|
@ -2455,8 +2477,8 @@ NGHTTP2_EXTERN int nghttp2_session_send(nghttp2_session *session);
|
||||||
* buffer up small chunks of data as necessary to avoid this
|
* buffer up small chunks of data as necessary to avoid this
|
||||||
* situation.
|
* situation.
|
||||||
*/
|
*/
|
||||||
NGHTTP2_EXTERN ssize_t nghttp2_session_mem_send(nghttp2_session *session,
|
NGHTTP2_EXTERN ssize_t
|
||||||
const uint8_t **data_ptr);
|
nghttp2_session_mem_send(nghttp2_session *session, const uint8_t **data_ptr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @function
|
* @function
|
||||||
|
@ -2654,7 +2676,7 @@ nghttp2_session_set_stream_user_data(nghttp2_session *session,
|
||||||
* include the deferred DATA frames.
|
* include the deferred DATA frames.
|
||||||
*/
|
*/
|
||||||
NGHTTP2_EXTERN size_t
|
NGHTTP2_EXTERN size_t
|
||||||
nghttp2_session_get_outbound_queue_size(nghttp2_session *session);
|
nghttp2_session_get_outbound_queue_size(nghttp2_session *session);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @function
|
* @function
|
||||||
|
@ -2670,8 +2692,9 @@ NGHTTP2_EXTERN size_t
|
||||||
*
|
*
|
||||||
* This function returns -1 if it fails.
|
* This function returns -1 if it fails.
|
||||||
*/
|
*/
|
||||||
NGHTTP2_EXTERN int32_t nghttp2_session_get_stream_effective_recv_data_length(
|
NGHTTP2_EXTERN int32_t
|
||||||
nghttp2_session *session, int32_t stream_id);
|
nghttp2_session_get_stream_effective_recv_data_length(nghttp2_session *session,
|
||||||
|
int32_t stream_id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @function
|
* @function
|
||||||
|
@ -2683,8 +2706,9 @@ NGHTTP2_EXTERN int32_t nghttp2_session_get_stream_effective_recv_data_length(
|
||||||
*
|
*
|
||||||
* This function returns -1 if it fails.
|
* This function returns -1 if it fails.
|
||||||
*/
|
*/
|
||||||
NGHTTP2_EXTERN int32_t nghttp2_session_get_stream_effective_local_window_size(
|
NGHTTP2_EXTERN int32_t
|
||||||
nghttp2_session *session, int32_t stream_id);
|
nghttp2_session_get_stream_effective_local_window_size(nghttp2_session *session,
|
||||||
|
int32_t stream_id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @function
|
* @function
|
||||||
|
@ -2701,7 +2725,7 @@ NGHTTP2_EXTERN int32_t nghttp2_session_get_stream_effective_local_window_size(
|
||||||
* This function returns -1 if it fails.
|
* This function returns -1 if it fails.
|
||||||
*/
|
*/
|
||||||
NGHTTP2_EXTERN int32_t
|
NGHTTP2_EXTERN int32_t
|
||||||
nghttp2_session_get_effective_recv_data_length(nghttp2_session *session);
|
nghttp2_session_get_effective_recv_data_length(nghttp2_session *session);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @function
|
* @function
|
||||||
|
@ -2714,7 +2738,7 @@ NGHTTP2_EXTERN int32_t
|
||||||
* This function returns -1 if it fails.
|
* This function returns -1 if it fails.
|
||||||
*/
|
*/
|
||||||
NGHTTP2_EXTERN int32_t
|
NGHTTP2_EXTERN int32_t
|
||||||
nghttp2_session_get_effective_local_window_size(nghttp2_session *session);
|
nghttp2_session_get_effective_local_window_size(nghttp2_session *session);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @function
|
* @function
|
||||||
|
@ -2731,7 +2755,7 @@ NGHTTP2_EXTERN int32_t
|
||||||
* This function returns -1 if it fails.
|
* This function returns -1 if it fails.
|
||||||
*/
|
*/
|
||||||
NGHTTP2_EXTERN int32_t
|
NGHTTP2_EXTERN int32_t
|
||||||
nghttp2_session_get_stream_remote_window_size(nghttp2_session *session,
|
nghttp2_session_get_stream_remote_window_size(nghttp2_session *session,
|
||||||
int32_t stream_id);
|
int32_t stream_id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2742,7 +2766,7 @@ NGHTTP2_EXTERN int32_t
|
||||||
* This function always succeeds.
|
* This function always succeeds.
|
||||||
*/
|
*/
|
||||||
NGHTTP2_EXTERN int32_t
|
NGHTTP2_EXTERN int32_t
|
||||||
nghttp2_session_get_remote_window_size(nghttp2_session *session);
|
nghttp2_session_get_remote_window_size(nghttp2_session *session);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @function
|
* @function
|
||||||
|
@ -2868,7 +2892,7 @@ NGHTTP2_EXTERN int nghttp2_submit_shutdown_notice(nghttp2_session *session);
|
||||||
* :enum:`nghttp2_settings_id`.
|
* :enum:`nghttp2_settings_id`.
|
||||||
*/
|
*/
|
||||||
NGHTTP2_EXTERN uint32_t
|
NGHTTP2_EXTERN uint32_t
|
||||||
nghttp2_session_get_remote_settings(nghttp2_session *session,
|
nghttp2_session_get_remote_settings(nghttp2_session *session,
|
||||||
nghttp2_settings_id id);
|
nghttp2_settings_id id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2898,7 +2922,7 @@ NGHTTP2_EXTERN int nghttp2_session_set_next_stream_id(nghttp2_session *session,
|
||||||
* function returns 1 << 31.
|
* function returns 1 << 31.
|
||||||
*/
|
*/
|
||||||
NGHTTP2_EXTERN uint32_t
|
NGHTTP2_EXTERN uint32_t
|
||||||
nghttp2_session_get_next_stream_id(nghttp2_session *session);
|
nghttp2_session_get_next_stream_id(nghttp2_session *session);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @function
|
* @function
|
||||||
|
@ -2974,6 +2998,17 @@ NGHTTP2_EXTERN int nghttp2_session_consume_stream(nghttp2_session *session,
|
||||||
* be called from both client and server, but the behavior is very
|
* be called from both client and server, but the behavior is very
|
||||||
* different in each other.
|
* different in each other.
|
||||||
*
|
*
|
||||||
|
* .. warning::
|
||||||
|
*
|
||||||
|
* This function is deprecated in favor of
|
||||||
|
* `nghttp2_session_upgrade2()`, because this function lacks the
|
||||||
|
* parameter to tell the library the request method used in the
|
||||||
|
* original HTTP request. This information is required for client
|
||||||
|
* to validate actual response body length against content-length
|
||||||
|
* header field (see `nghttp2_option_set_no_http_messaging()`). If
|
||||||
|
* HEAD is used in request, the length of response body must be 0
|
||||||
|
* regardless of value included in content-length header field.
|
||||||
|
*
|
||||||
* If called from client side, the |settings_payload| must be the
|
* If called from client side, the |settings_payload| must be the
|
||||||
* value sent in ``HTTP2-Settings`` header field and must be decoded
|
* value sent in ``HTTP2-Settings`` header field and must be decoded
|
||||||
* by base64url decoder. The |settings_payloadlen| is the length of
|
* by base64url decoder. The |settings_payloadlen| is the length of
|
||||||
|
@ -3008,6 +3043,51 @@ NGHTTP2_EXTERN int nghttp2_session_upgrade(nghttp2_session *session,
|
||||||
size_t settings_payloadlen,
|
size_t settings_payloadlen,
|
||||||
void *stream_user_data);
|
void *stream_user_data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @function
|
||||||
|
*
|
||||||
|
* Performs post-process of HTTP Upgrade request. This function can
|
||||||
|
* be called from both client and server, but the behavior is very
|
||||||
|
* different in each other.
|
||||||
|
*
|
||||||
|
* If called from client side, the |settings_payload| must be the
|
||||||
|
* value sent in ``HTTP2-Settings`` header field and must be decoded
|
||||||
|
* by base64url decoder. The |settings_payloadlen| is the length of
|
||||||
|
* |settings_payload|. The |settings_payload| is unpacked and its
|
||||||
|
* setting values will be submitted using `nghttp2_submit_settings()`.
|
||||||
|
* This means that the client application code does not need to submit
|
||||||
|
* SETTINGS by itself. The stream with stream ID=1 is opened and the
|
||||||
|
* |stream_user_data| is used for its stream_user_data. The opened
|
||||||
|
* stream becomes half-closed (local) state.
|
||||||
|
*
|
||||||
|
* If called from server side, the |settings_payload| must be the
|
||||||
|
* value received in ``HTTP2-Settings`` header field and must be
|
||||||
|
* decoded by base64url decoder. The |settings_payloadlen| is the
|
||||||
|
* length of |settings_payload|. It is treated as if the SETTINGS
|
||||||
|
* frame with that payload is received. Thus, callback functions for
|
||||||
|
* the reception of SETTINGS frame will be invoked. The stream with
|
||||||
|
* stream ID=1 is opened. The |stream_user_data| is ignored. The
|
||||||
|
* opened stream becomes half-closed (remote).
|
||||||
|
*
|
||||||
|
* If the request method is HEAD, pass nonzero value to
|
||||||
|
* |head_request|. Otherwise, pass 0.
|
||||||
|
*
|
||||||
|
* This function returns 0 if it succeeds, or one of the following
|
||||||
|
* negative error codes:
|
||||||
|
*
|
||||||
|
* :enum:`NGHTTP2_ERR_NOMEM`
|
||||||
|
* Out of memory.
|
||||||
|
* :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
|
||||||
|
* The |settings_payload| is badly formed.
|
||||||
|
* :enum:`NGHTTP2_ERR_PROTO`
|
||||||
|
* The stream ID 1 is already used or closed; or is not available.
|
||||||
|
*/
|
||||||
|
NGHTTP2_EXTERN int nghttp2_session_upgrade2(nghttp2_session *session,
|
||||||
|
const uint8_t *settings_payload,
|
||||||
|
size_t settings_payloadlen,
|
||||||
|
int head_request,
|
||||||
|
void *stream_user_data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @function
|
* @function
|
||||||
*
|
*
|
||||||
|
@ -3030,7 +3110,7 @@ NGHTTP2_EXTERN int nghttp2_session_upgrade(nghttp2_session *session,
|
||||||
* The provided |buflen| size is too small to hold the output.
|
* The provided |buflen| size is too small to hold the output.
|
||||||
*/
|
*/
|
||||||
NGHTTP2_EXTERN ssize_t
|
NGHTTP2_EXTERN ssize_t
|
||||||
nghttp2_pack_settings_payload(uint8_t *buf, size_t buflen,
|
nghttp2_pack_settings_payload(uint8_t *buf, size_t buflen,
|
||||||
const nghttp2_settings_entry *iv, size_t niv);
|
const nghttp2_settings_entry *iv, size_t niv);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -3098,7 +3178,15 @@ nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec);
|
||||||
*
|
*
|
||||||
* This function creates copies of all name/value pairs in |nva|. It
|
* This function creates copies of all name/value pairs in |nva|. It
|
||||||
* also lower-cases all names in |nva|. The order of elements in
|
* also lower-cases all names in |nva|. The order of elements in
|
||||||
* |nva| is preserved.
|
* |nva| is preserved. For header fields with
|
||||||
|
* :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME` and
|
||||||
|
* :enum:`NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, header field name
|
||||||
|
* and value are not copied respectively. With
|
||||||
|
* :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME`, application is responsible to
|
||||||
|
* pass header field name in lowercase. The application should
|
||||||
|
* maintain the references to them until
|
||||||
|
* :type:`nghttp2_on_frame_send_callback` or
|
||||||
|
* :type:`nghttp2_on_frame_not_send_callback` is called.
|
||||||
*
|
*
|
||||||
* HTTP/2 specification has requirement about header fields in the
|
* HTTP/2 specification has requirement about header fields in the
|
||||||
* request HEADERS. See the specification for more details.
|
* request HEADERS. See the specification for more details.
|
||||||
|
@ -3134,7 +3222,7 @@ nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec);
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
NGHTTP2_EXTERN int32_t
|
NGHTTP2_EXTERN int32_t
|
||||||
nghttp2_submit_request(nghttp2_session *session,
|
nghttp2_submit_request(nghttp2_session *session,
|
||||||
const nghttp2_priority_spec *pri_spec,
|
const nghttp2_priority_spec *pri_spec,
|
||||||
const nghttp2_nv *nva, size_t nvlen,
|
const nghttp2_nv *nva, size_t nvlen,
|
||||||
const nghttp2_data_provider *data_prd,
|
const nghttp2_data_provider *data_prd,
|
||||||
|
@ -3154,7 +3242,15 @@ NGHTTP2_EXTERN int32_t
|
||||||
*
|
*
|
||||||
* This function creates copies of all name/value pairs in |nva|. It
|
* This function creates copies of all name/value pairs in |nva|. It
|
||||||
* also lower-cases all names in |nva|. The order of elements in
|
* also lower-cases all names in |nva|. The order of elements in
|
||||||
* |nva| is preserved.
|
* |nva| is preserved. For header fields with
|
||||||
|
* :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME` and
|
||||||
|
* :enum:`NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, header field name
|
||||||
|
* and value are not copied respectively. With
|
||||||
|
* :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME`, application is responsible to
|
||||||
|
* pass header field name in lowercase. The application should
|
||||||
|
* maintain the references to them until
|
||||||
|
* :type:`nghttp2_on_frame_send_callback` or
|
||||||
|
* :type:`nghttp2_on_frame_not_send_callback` is called.
|
||||||
*
|
*
|
||||||
* HTTP/2 specification has requirement about header fields in the
|
* HTTP/2 specification has requirement about header fields in the
|
||||||
* response HEADERS. See the specification for more details.
|
* response HEADERS. See the specification for more details.
|
||||||
|
@ -3211,7 +3307,15 @@ nghttp2_submit_response(nghttp2_session *session, int32_t stream_id,
|
||||||
*
|
*
|
||||||
* This function creates copies of all name/value pairs in |nva|. It
|
* This function creates copies of all name/value pairs in |nva|. It
|
||||||
* also lower-cases all names in |nva|. The order of elements in
|
* also lower-cases all names in |nva|. The order of elements in
|
||||||
* |nva| is preserved.
|
* |nva| is preserved. For header fields with
|
||||||
|
* :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME` and
|
||||||
|
* :enum:`NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, header field name
|
||||||
|
* and value are not copied respectively. With
|
||||||
|
* :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME`, application is responsible to
|
||||||
|
* pass header field name in lowercase. The application should
|
||||||
|
* maintain the references to them until
|
||||||
|
* :type:`nghttp2_on_frame_send_callback` or
|
||||||
|
* :type:`nghttp2_on_frame_not_send_callback` is called.
|
||||||
*
|
*
|
||||||
* For server, trailer must be followed by response HEADERS or
|
* For server, trailer must be followed by response HEADERS or
|
||||||
* response DATA. The library does not check that response HEADERS
|
* response DATA. The library does not check that response HEADERS
|
||||||
|
@ -3287,7 +3391,15 @@ NGHTTP2_EXTERN int nghttp2_submit_trailer(nghttp2_session *session,
|
||||||
*
|
*
|
||||||
* This function creates copies of all name/value pairs in |nva|. It
|
* This function creates copies of all name/value pairs in |nva|. It
|
||||||
* also lower-cases all names in |nva|. The order of elements in
|
* also lower-cases all names in |nva|. The order of elements in
|
||||||
* |nva| is preserved.
|
* |nva| is preserved. For header fields with
|
||||||
|
* :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME` and
|
||||||
|
* :enum:`NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, header field name
|
||||||
|
* and value are not copied respectively. With
|
||||||
|
* :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME`, application is responsible to
|
||||||
|
* pass header field name in lowercase. The application should
|
||||||
|
* maintain the references to them until
|
||||||
|
* :type:`nghttp2_on_frame_send_callback` or
|
||||||
|
* :type:`nghttp2_on_frame_not_send_callback` is called.
|
||||||
*
|
*
|
||||||
* The |stream_user_data| is a pointer to an arbitrary data which is
|
* The |stream_user_data| is a pointer to an arbitrary data which is
|
||||||
* associated to the stream this frame will open. Therefore it is
|
* associated to the stream this frame will open. Therefore it is
|
||||||
|
@ -3325,9 +3437,8 @@ NGHTTP2_EXTERN int nghttp2_submit_trailer(nghttp2_session *session,
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
NGHTTP2_EXTERN int32_t
|
NGHTTP2_EXTERN int32_t
|
||||||
nghttp2_submit_headers(nghttp2_session *session, uint8_t flags,
|
nghttp2_submit_headers(nghttp2_session *session, uint8_t flags,
|
||||||
int32_t stream_id,
|
int32_t stream_id, const nghttp2_priority_spec *pri_spec,
|
||||||
const nghttp2_priority_spec *pri_spec,
|
|
||||||
const nghttp2_nv *nva, size_t nvlen,
|
const nghttp2_nv *nva, size_t nvlen,
|
||||||
void *stream_user_data);
|
void *stream_user_data);
|
||||||
|
|
||||||
|
@ -3485,7 +3596,15 @@ NGHTTP2_EXTERN int nghttp2_submit_settings(nghttp2_session *session,
|
||||||
*
|
*
|
||||||
* This function creates copies of all name/value pairs in |nva|. It
|
* This function creates copies of all name/value pairs in |nva|. It
|
||||||
* also lower-cases all names in |nva|. The order of elements in
|
* also lower-cases all names in |nva|. The order of elements in
|
||||||
* |nva| is preserved.
|
* |nva| is preserved. For header fields with
|
||||||
|
* :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME` and
|
||||||
|
* :enum:`NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, header field name
|
||||||
|
* and value are not copied respectively. With
|
||||||
|
* :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME`, application is responsible to
|
||||||
|
* pass header field name in lowercase. The application should
|
||||||
|
* maintain the references to them until
|
||||||
|
* :type:`nghttp2_on_frame_send_callback` or
|
||||||
|
* :type:`nghttp2_on_frame_not_send_callback` is called.
|
||||||
*
|
*
|
||||||
* The |promised_stream_user_data| is a pointer to an arbitrary data
|
* The |promised_stream_user_data| is a pointer to an arbitrary data
|
||||||
* which is associated to the promised stream this frame will open and
|
* which is associated to the promised stream this frame will open and
|
||||||
|
@ -3524,7 +3643,7 @@ NGHTTP2_EXTERN int nghttp2_submit_settings(nghttp2_session *session,
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
NGHTTP2_EXTERN int32_t
|
NGHTTP2_EXTERN int32_t
|
||||||
nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags,
|
nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags,
|
||||||
int32_t stream_id, const nghttp2_nv *nva,
|
int32_t stream_id, const nghttp2_nv *nva,
|
||||||
size_t nvlen, void *promised_stream_user_data);
|
size_t nvlen, void *promised_stream_user_data);
|
||||||
|
|
||||||
|
@ -3615,7 +3734,7 @@ NGHTTP2_EXTERN int nghttp2_submit_goaway(nghttp2_session *session,
|
||||||
* This function always succeeds.
|
* This function always succeeds.
|
||||||
*/
|
*/
|
||||||
NGHTTP2_EXTERN int32_t
|
NGHTTP2_EXTERN int32_t
|
||||||
nghttp2_session_get_last_proc_stream_id(nghttp2_session *session);
|
nghttp2_session_get_last_proc_stream_id(nghttp2_session *session);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @function
|
* @function
|
||||||
|
@ -3632,7 +3751,8 @@ NGHTTP2_EXTERN int32_t
|
||||||
* may return error. Or, request is failed to sent, and
|
* may return error. Or, request is failed to sent, and
|
||||||
* :type:`nghttp2_on_stream_close_callback` is called.
|
* :type:`nghttp2_on_stream_close_callback` is called.
|
||||||
*/
|
*/
|
||||||
NGHTTP2_EXTERN int nghttp2_session_request_allowed(nghttp2_session *session);
|
NGHTTP2_EXTERN int
|
||||||
|
nghttp2_session_check_request_allowed(nghttp2_session *session);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @function
|
* @function
|
||||||
|
@ -3930,7 +4050,7 @@ nghttp2_hd_deflate_change_table_size(nghttp2_hd_deflater *deflater,
|
||||||
* The provided |buflen| size is too small to hold the output.
|
* The provided |buflen| size is too small to hold the output.
|
||||||
*/
|
*/
|
||||||
NGHTTP2_EXTERN ssize_t
|
NGHTTP2_EXTERN ssize_t
|
||||||
nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, uint8_t *buf,
|
nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, uint8_t *buf,
|
||||||
size_t buflen, const nghttp2_nv *nva, size_t nvlen);
|
size_t buflen, const nghttp2_nv *nva, size_t nvlen);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -4289,7 +4409,7 @@ typedef enum {
|
||||||
* :enum:`NGHTTP2_STREAM_STATE_IDLE`.
|
* :enum:`NGHTTP2_STREAM_STATE_IDLE`.
|
||||||
*/
|
*/
|
||||||
NGHTTP2_EXTERN nghttp2_stream_proto_state
|
NGHTTP2_EXTERN nghttp2_stream_proto_state
|
||||||
nghttp2_stream_get_state(nghttp2_stream *stream);
|
nghttp2_stream_get_state(nghttp2_stream *stream);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @function
|
* @function
|
||||||
|
@ -4352,7 +4472,7 @@ NGHTTP2_EXTERN int32_t nghttp2_stream_get_weight(nghttp2_stream *stream);
|
||||||
* Returns the sum of the weight for |stream|'s children.
|
* Returns the sum of the weight for |stream|'s children.
|
||||||
*/
|
*/
|
||||||
NGHTTP2_EXTERN int32_t
|
NGHTTP2_EXTERN int32_t
|
||||||
nghttp2_stream_get_sum_dependency_weight(nghttp2_stream *stream);
|
nghttp2_stream_get_sum_dependency_weight(nghttp2_stream *stream);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
@ -750,21 +750,26 @@ void nghttp2_nv_array_sort(nghttp2_nv *nva, size_t nvlen) {
|
||||||
int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr, const nghttp2_nv *nva,
|
int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr, const nghttp2_nv *nva,
|
||||||
size_t nvlen, nghttp2_mem *mem) {
|
size_t nvlen, nghttp2_mem *mem) {
|
||||||
size_t i;
|
size_t i;
|
||||||
uint8_t *data;
|
uint8_t *data = NULL;
|
||||||
size_t buflen = 0;
|
size_t buflen = 0;
|
||||||
nghttp2_nv *p;
|
nghttp2_nv *p;
|
||||||
|
|
||||||
for (i = 0; i < nvlen; ++i) {
|
|
||||||
/* + 2 for null-termination */
|
|
||||||
buflen += nva[i].namelen + nva[i].valuelen + 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nvlen == 0) {
|
if (nvlen == 0) {
|
||||||
*nva_ptr = NULL;
|
*nva_ptr = NULL;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < nvlen; ++i) {
|
||||||
|
/* + 1 for null-termination */
|
||||||
|
if ((nva[i].flags & NGHTTP2_NV_FLAG_NO_COPY_NAME) == 0) {
|
||||||
|
buflen += nva[i].namelen + 1;
|
||||||
|
}
|
||||||
|
if ((nva[i].flags & NGHTTP2_NV_FLAG_NO_COPY_VALUE) == 0) {
|
||||||
|
buflen += nva[i].valuelen + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
buflen += sizeof(nghttp2_nv) * nvlen;
|
buflen += sizeof(nghttp2_nv) * nvlen;
|
||||||
|
|
||||||
*nva_ptr = nghttp2_mem_malloc(mem, buflen);
|
*nva_ptr = nghttp2_mem_malloc(mem, buflen);
|
||||||
|
@ -779,17 +784,29 @@ int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr, const nghttp2_nv *nva,
|
||||||
for (i = 0; i < nvlen; ++i) {
|
for (i = 0; i < nvlen; ++i) {
|
||||||
p->flags = nva[i].flags;
|
p->flags = nva[i].flags;
|
||||||
|
|
||||||
|
if (nva[i].flags & NGHTTP2_NV_FLAG_NO_COPY_NAME) {
|
||||||
|
p->name = nva[i].name;
|
||||||
|
p->namelen = nva[i].namelen;
|
||||||
|
} else {
|
||||||
memcpy(data, nva[i].name, nva[i].namelen);
|
memcpy(data, nva[i].name, nva[i].namelen);
|
||||||
p->name = data;
|
p->name = data;
|
||||||
p->namelen = nva[i].namelen;
|
p->namelen = nva[i].namelen;
|
||||||
data[p->namelen] = '\0';
|
data[p->namelen] = '\0';
|
||||||
nghttp2_downcase(p->name, p->namelen);
|
nghttp2_downcase(p->name, p->namelen);
|
||||||
data += nva[i].namelen + 1;
|
data += nva[i].namelen + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nva[i].flags & NGHTTP2_NV_FLAG_NO_COPY_VALUE) {
|
||||||
|
p->value = nva[i].value;
|
||||||
|
p->valuelen = nva[i].valuelen;
|
||||||
|
} else {
|
||||||
memcpy(data, nva[i].value, nva[i].valuelen);
|
memcpy(data, nva[i].value, nva[i].valuelen);
|
||||||
p->value = data;
|
p->value = data;
|
||||||
p->valuelen = nva[i].valuelen;
|
p->valuelen = nva[i].valuelen;
|
||||||
data[p->valuelen] = '\0';
|
data[p->valuelen] = '\0';
|
||||||
data += nva[i].valuelen + 1;
|
data += nva[i].valuelen + 1;
|
||||||
|
}
|
||||||
|
|
||||||
++p;
|
++p;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -41,7 +41,7 @@
|
||||||
#define lstreq(A, B, N) ((sizeof((A)) - 1) == (N) && memcmp((A), (B), (N)) == 0)
|
#define lstreq(A, B, N) ((sizeof((A)) - 1) == (N) && memcmp((A), (B), (N)) == 0)
|
||||||
|
|
||||||
#define nghttp2_struct_of(ptr, type, member) \
|
#define nghttp2_struct_of(ptr, type, member) \
|
||||||
((type *)(void *)((char *)(ptr) - offsetof(type, member)))
|
((type *)(void *)((char *)(ptr)-offsetof(type, member)))
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copies 2 byte unsigned integer |n| in host byte order to |buf| in
|
* Copies 2 byte unsigned integer |n| in host byte order to |buf| in
|
||||||
|
|
|
@ -381,7 +381,8 @@ int nghttp2_http_on_response_headers(nghttp2_stream *stream) {
|
||||||
|
|
||||||
if (!expect_response_body(stream)) {
|
if (!expect_response_body(stream)) {
|
||||||
stream->content_length = 0;
|
stream->content_length = 0;
|
||||||
} else if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) {
|
} else if (stream->http_flags & (NGHTTP2_HTTP_FLAG_METH_CONNECT |
|
||||||
|
NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND)) {
|
||||||
stream->content_length = -1;
|
stream->content_length = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -615,9 +615,8 @@ void nghttp2_session_del(nghttp2_session *session) {
|
||||||
nghttp2_mem_free(mem, session);
|
nghttp2_mem_free(mem, session);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int nghttp2_session_reprioritize_stream(
|
||||||
nghttp2_session_reprioritize_stream(nghttp2_session *session,
|
nghttp2_session *session, nghttp2_stream *stream,
|
||||||
nghttp2_stream *stream,
|
|
||||||
const nghttp2_priority_spec *pri_spec_in) {
|
const nghttp2_priority_spec *pri_spec_in) {
|
||||||
int rv;
|
int rv;
|
||||||
nghttp2_stream *dep_stream = NULL;
|
nghttp2_stream *dep_stream = NULL;
|
||||||
|
@ -1327,7 +1326,7 @@ static int session_predicate_for_stream_send(nghttp2_session *session,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nghttp2_session_request_allowed(nghttp2_session *session) {
|
int nghttp2_session_check_request_allowed(nghttp2_session *session) {
|
||||||
return !session->server && session->next_stream_id <= INT32_MAX &&
|
return !session->server && session->next_stream_id <= INT32_MAX &&
|
||||||
(session->goaway_flags &
|
(session->goaway_flags &
|
||||||
(NGHTTP2_GOAWAY_TERM_ON_SEND | NGHTTP2_GOAWAY_RECV)) == 0;
|
(NGHTTP2_GOAWAY_TERM_ON_SEND | NGHTTP2_GOAWAY_RECV)) == 0;
|
||||||
|
@ -6662,7 +6661,7 @@ uint32_t nghttp2_session_get_remote_settings(nghttp2_session *session,
|
||||||
assert(0);
|
assert(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int nghttp2_session_upgrade(nghttp2_session *session,
|
static int nghttp2_session_upgrade_internal(nghttp2_session *session,
|
||||||
const uint8_t *settings_payload,
|
const uint8_t *settings_payload,
|
||||||
size_t settings_payloadlen,
|
size_t settings_payloadlen,
|
||||||
void *stream_user_data) {
|
void *stream_user_data) {
|
||||||
|
@ -6722,6 +6721,58 @@ int nghttp2_session_upgrade(nghttp2_session *session,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int nghttp2_session_upgrade(nghttp2_session *session,
|
||||||
|
const uint8_t *settings_payload,
|
||||||
|
size_t settings_payloadlen,
|
||||||
|
void *stream_user_data) {
|
||||||
|
int rv;
|
||||||
|
nghttp2_stream *stream;
|
||||||
|
|
||||||
|
rv = nghttp2_session_upgrade_internal(session, settings_payload,
|
||||||
|
settings_payloadlen, stream_user_data);
|
||||||
|
if (rv != 0) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream = nghttp2_session_get_stream(session, 1);
|
||||||
|
assert(stream);
|
||||||
|
|
||||||
|
/* We have no information about request header fields when Upgrade
|
||||||
|
was happened. So we don't know the request method here. If
|
||||||
|
request method is HEAD, we have a trouble because we may have
|
||||||
|
nonzero content-length header field in response headers, and we
|
||||||
|
will going to check it against the actual DATA frames, but we may
|
||||||
|
get mismatch because HEAD response body must be empty. Because
|
||||||
|
of this reason, nghttp2_session_upgrade() was deprecated in favor
|
||||||
|
of nghttp2_session_upgrade2(), which has |head_request| parameter
|
||||||
|
to indicate that request method is HEAD or not. */
|
||||||
|
stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nghttp2_session_upgrade2(nghttp2_session *session,
|
||||||
|
const uint8_t *settings_payload,
|
||||||
|
size_t settings_payloadlen, int head_request,
|
||||||
|
void *stream_user_data) {
|
||||||
|
int rv;
|
||||||
|
nghttp2_stream *stream;
|
||||||
|
|
||||||
|
rv = nghttp2_session_upgrade_internal(session, settings_payload,
|
||||||
|
settings_payloadlen, stream_user_data);
|
||||||
|
if (rv != 0) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream = nghttp2_session_get_stream(session, 1);
|
||||||
|
assert(stream);
|
||||||
|
|
||||||
|
if (head_request) {
|
||||||
|
stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int nghttp2_session_get_stream_local_close(nghttp2_session *session,
|
int nghttp2_session_get_stream_local_close(nghttp2_session *session,
|
||||||
int32_t stream_id) {
|
int32_t stream_id) {
|
||||||
nghttp2_stream *stream;
|
nghttp2_stream *stream;
|
||||||
|
|
|
@ -114,8 +114,7 @@ static int stream_subtree_active(nghttp2_stream *stream) {
|
||||||
*/
|
*/
|
||||||
static uint64_t stream_next_cycle(nghttp2_stream *stream, uint64_t last_cycle) {
|
static uint64_t stream_next_cycle(nghttp2_stream *stream, uint64_t last_cycle) {
|
||||||
return last_cycle +
|
return last_cycle +
|
||||||
(stream->last_writelen + 1) * NGHTTP2_MAX_WEIGHT /
|
stream->last_writelen * NGHTTP2_MAX_WEIGHT / (uint32_t)stream->weight;
|
||||||
(uint32_t)stream->weight;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int stream_obq_push(nghttp2_stream *dep_stream, nghttp2_stream *stream) {
|
static int stream_obq_push(nghttp2_stream *dep_stream, nghttp2_stream *stream) {
|
||||||
|
|
|
@ -116,19 +116,21 @@ typedef enum {
|
||||||
NGHTTP2_HTTP_FLAG_METH_CONNECT = 1 << 7,
|
NGHTTP2_HTTP_FLAG_METH_CONNECT = 1 << 7,
|
||||||
NGHTTP2_HTTP_FLAG_METH_HEAD = 1 << 8,
|
NGHTTP2_HTTP_FLAG_METH_HEAD = 1 << 8,
|
||||||
NGHTTP2_HTTP_FLAG_METH_OPTIONS = 1 << 9,
|
NGHTTP2_HTTP_FLAG_METH_OPTIONS = 1 << 9,
|
||||||
|
NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND = 1 << 10,
|
||||||
NGHTTP2_HTTP_FLAG_METH_ALL = NGHTTP2_HTTP_FLAG_METH_CONNECT |
|
NGHTTP2_HTTP_FLAG_METH_ALL = NGHTTP2_HTTP_FLAG_METH_CONNECT |
|
||||||
NGHTTP2_HTTP_FLAG_METH_HEAD |
|
NGHTTP2_HTTP_FLAG_METH_HEAD |
|
||||||
NGHTTP2_HTTP_FLAG_METH_OPTIONS,
|
NGHTTP2_HTTP_FLAG_METH_OPTIONS |
|
||||||
|
NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND,
|
||||||
/* :path category */
|
/* :path category */
|
||||||
/* path starts with "/" */
|
/* path starts with "/" */
|
||||||
NGHTTP2_HTTP_FLAG_PATH_REGULAR = 1 << 10,
|
NGHTTP2_HTTP_FLAG_PATH_REGULAR = 1 << 11,
|
||||||
/* path "*" */
|
/* path "*" */
|
||||||
NGHTTP2_HTTP_FLAG_PATH_ASTERISK = 1 << 11,
|
NGHTTP2_HTTP_FLAG_PATH_ASTERISK = 1 << 12,
|
||||||
/* scheme */
|
/* scheme */
|
||||||
/* "http" or "https" scheme */
|
/* "http" or "https" scheme */
|
||||||
NGHTTP2_HTTP_FLAG_SCHEME_HTTP = 1 << 12,
|
NGHTTP2_HTTP_FLAG_SCHEME_HTTP = 1 << 13,
|
||||||
/* set if final response is expected */
|
/* set if final response is expected */
|
||||||
NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE = 1 << 13
|
NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE = 1 << 14
|
||||||
} nghttp2_http_flag;
|
} nghttp2_http_flag;
|
||||||
|
|
||||||
struct nghttp2_stream {
|
struct nghttp2_stream {
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
#include "asio_client_session_tls_impl.h"
|
#include "asio_client_session_tls_impl.h"
|
||||||
|
#include "asio_common.h"
|
||||||
|
|
||||||
namespace nghttp2 {
|
namespace nghttp2 {
|
||||||
namespace asio_http2 {
|
namespace asio_http2 {
|
||||||
|
@ -44,8 +45,8 @@ session_tls_impl::session_tls_impl(boost::asio::io_service &io_service,
|
||||||
session_tls_impl::~session_tls_impl() {}
|
session_tls_impl::~session_tls_impl() {}
|
||||||
|
|
||||||
void session_tls_impl::start_connect(tcp::resolver::iterator endpoint_it) {
|
void session_tls_impl::start_connect(tcp::resolver::iterator endpoint_it) {
|
||||||
boost::asio::async_connect(socket(), endpoint_it,
|
boost::asio::async_connect(
|
||||||
[this](const boost::system::error_code &ec,
|
socket(), endpoint_it, [this](const boost::system::error_code &ec,
|
||||||
tcp::resolver::iterator endpoint_it) {
|
tcp::resolver::iterator endpoint_it) {
|
||||||
if (ec) {
|
if (ec) {
|
||||||
not_connected(ec);
|
not_connected(ec);
|
||||||
|
@ -59,6 +60,13 @@ void session_tls_impl::start_connect(tcp::resolver::iterator endpoint_it) {
|
||||||
not_connected(ec);
|
not_connected(ec);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!tls_h2_negotiated(socket_)) {
|
||||||
|
not_connected(make_error_code(
|
||||||
|
NGHTTP2_ASIO_ERR_TLS_NO_APP_PROTO_NEGOTIATED));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
connected(endpoint_it);
|
connected(endpoint_it);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -56,6 +56,12 @@ configure_tls_context(boost::system::error_code &ec,
|
||||||
|
|
||||||
SSL_CTX_set_next_proto_select_cb(ctx, client_select_next_proto_cb, nullptr);
|
SSL_CTX_set_next_proto_select_cb(ctx, client_select_next_proto_cb, nullptr);
|
||||||
|
|
||||||
|
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
auto proto_list = util::get_default_alpn();
|
||||||
|
|
||||||
|
SSL_CTX_set_alpn_protos(ctx, proto_list.data(), proto_list.size());
|
||||||
|
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
|
||||||
return ec;
|
return ec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,31 @@ boost::system::error_code make_error_code(nghttp2_error ev) {
|
||||||
return boost::system::error_code(static_cast<int>(ev), nghttp2_category());
|
return boost::system::error_code(static_cast<int>(ev), nghttp2_category());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class nghttp2_asio_category_impl : public boost::system::error_category {
|
||||||
|
public:
|
||||||
|
const char *name() const noexcept { return "nghttp2_asio"; }
|
||||||
|
std::string message(int ev) const {
|
||||||
|
switch (ev) {
|
||||||
|
case NGHTTP2_ASIO_ERR_NO_ERROR:
|
||||||
|
return "no error";
|
||||||
|
case NGHTTP2_ASIO_ERR_TLS_NO_APP_PROTO_NEGOTIATED:
|
||||||
|
return "tls: no application protocol negotiated";
|
||||||
|
default:
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const boost::system::error_category &nghttp2_asio_category() noexcept {
|
||||||
|
static nghttp2_asio_category_impl cat;
|
||||||
|
return cat;
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::system::error_code make_error_code(nghttp2_asio_error ev) {
|
||||||
|
return boost::system::error_code(static_cast<int>(ev),
|
||||||
|
nghttp2_asio_category());
|
||||||
|
}
|
||||||
|
|
||||||
generator_cb string_generator(std::string data) {
|
generator_cb string_generator(std::string data) {
|
||||||
auto strio = std::make_shared<std::pair<std::string, size_t>>(std::move(data),
|
auto strio = std::make_shared<std::pair<std::string, size_t>>(std::move(data),
|
||||||
data.size());
|
data.size());
|
||||||
|
@ -65,8 +90,9 @@ generator_cb string_generator(std::string data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
generator_cb deferred_generator() {
|
generator_cb deferred_generator() {
|
||||||
return [](uint8_t *buf, size_t len,
|
return [](uint8_t *buf, size_t len, uint32_t *data_flags) {
|
||||||
uint32_t *data_flags) { return NGHTTP2_ERR_DEFERRED; };
|
return NGHTTP2_ERR_DEFERRED;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename F, typename... T>
|
template <typename F, typename... T>
|
||||||
|
@ -144,5 +170,23 @@ boost::system::error_code host_service_from_uri(boost::system::error_code &ec,
|
||||||
return ec;
|
return ec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool tls_h2_negotiated(ssl_socket &socket) {
|
||||||
|
auto ssl = socket.native_handle();
|
||||||
|
|
||||||
|
const unsigned char *next_proto = nullptr;
|
||||||
|
unsigned int next_proto_len = 0;
|
||||||
|
|
||||||
|
SSL_get0_next_proto_negotiated(ssl, &next_proto, &next_proto_len);
|
||||||
|
if (next_proto == nullptr) {
|
||||||
|
SSL_get0_alpn_selected(ssl, &next_proto, &next_proto_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (next_proto == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return util::check_h2_is_selected(next_proto, next_proto_len);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace asio_http2
|
} // namespace asio_http2
|
||||||
} // namespace nghttp2
|
} // namespace nghttp2
|
||||||
|
|
|
@ -39,6 +39,8 @@ namespace asio_http2 {
|
||||||
|
|
||||||
boost::system::error_code make_error_code(nghttp2_error ev);
|
boost::system::error_code make_error_code(nghttp2_error ev);
|
||||||
|
|
||||||
|
boost::system::error_code make_error_code(nghttp2_asio_error ev);
|
||||||
|
|
||||||
generator_cb string_generator(std::string data);
|
generator_cb string_generator(std::string data);
|
||||||
|
|
||||||
// Returns generator_cb, which just returns NGHTTP2_ERR_DEFERRED
|
// Returns generator_cb, which just returns NGHTTP2_ERR_DEFERRED
|
||||||
|
@ -58,6 +60,12 @@ void split_path(uri_ref &dst, InputIt first, InputIt last) {
|
||||||
dst.raw_query.assign(query_first, last);
|
dst.raw_query.assign(query_first, last);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using boost::asio::ip::tcp;
|
||||||
|
|
||||||
|
using ssl_socket = boost::asio::ssl::stream<tcp::socket>;
|
||||||
|
|
||||||
|
bool tls_h2_negotiated(ssl_socket &socket);
|
||||||
|
|
||||||
} // namespace asio_http2
|
} // namespace asio_http2
|
||||||
|
|
||||||
} // namespace nghttp2
|
} // namespace nghttp2
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
#include "asio_server.h"
|
#include "asio_server.h"
|
||||||
|
|
||||||
#include "asio_server_connection.h"
|
#include "asio_server_connection.h"
|
||||||
|
#include "asio_common.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
namespace nghttp2 {
|
namespace nghttp2 {
|
||||||
|
@ -122,17 +123,25 @@ void server::start_accept(boost::asio::ssl::context &tls_context,
|
||||||
auto new_connection = std::make_shared<connection<ssl_socket>>(
|
auto new_connection = std::make_shared<connection<ssl_socket>>(
|
||||||
mux, io_service_pool_.get_io_service(), tls_context);
|
mux, io_service_pool_.get_io_service(), tls_context);
|
||||||
|
|
||||||
acceptor.async_accept(new_connection->socket().lowest_layer(),
|
acceptor.async_accept(
|
||||||
|
new_connection->socket().lowest_layer(),
|
||||||
[this, &tls_context, &acceptor, &mux, new_connection](
|
[this, &tls_context, &acceptor, &mux, new_connection](
|
||||||
const boost::system::error_code &e) {
|
const boost::system::error_code &e) {
|
||||||
if (!e) {
|
if (!e) {
|
||||||
new_connection->socket().lowest_layer().set_option(tcp::no_delay(true));
|
new_connection->socket().lowest_layer().set_option(
|
||||||
|
tcp::no_delay(true));
|
||||||
new_connection->socket().async_handshake(
|
new_connection->socket().async_handshake(
|
||||||
boost::asio::ssl::stream_base::server,
|
boost::asio::ssl::stream_base::server,
|
||||||
[new_connection](const boost::system::error_code &e) {
|
[new_connection](const boost::system::error_code &e) {
|
||||||
if (!e) {
|
if (e) {
|
||||||
new_connection->start();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!tls_h2_negotiated(new_connection->socket())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_connection->start();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,8 +153,8 @@ void server::start_accept(tcp::acceptor &acceptor, serve_mux &mux) {
|
||||||
auto new_connection = std::make_shared<connection<tcp::socket>>(
|
auto new_connection = std::make_shared<connection<tcp::socket>>(
|
||||||
mux, io_service_pool_.get_io_service());
|
mux, io_service_pool_.get_io_service());
|
||||||
|
|
||||||
acceptor.async_accept(new_connection->socket(),
|
acceptor.async_accept(
|
||||||
[this, &acceptor, &mux, new_connection](
|
new_connection->socket(), [this, &acceptor, &mux, new_connection](
|
||||||
const boost::system::error_code &e) {
|
const boost::system::error_code &e) {
|
||||||
if (!e) {
|
if (!e) {
|
||||||
new_connection->socket().set_option(tcp::no_delay(true));
|
new_connection->socket().set_option(tcp::no_delay(true));
|
||||||
|
|
|
@ -83,7 +83,8 @@ public:
|
||||||
void do_read() {
|
void do_read() {
|
||||||
auto self = this->shared_from_this();
|
auto self = this->shared_from_this();
|
||||||
|
|
||||||
socket_.async_read_some(boost::asio::buffer(buffer_),
|
socket_.async_read_some(
|
||||||
|
boost::asio::buffer(buffer_),
|
||||||
[this, self](const boost::system::error_code &e,
|
[this, self](const boost::system::error_code &e,
|
||||||
std::size_t bytes_transferred) {
|
std::size_t bytes_transferred) {
|
||||||
if (!e) {
|
if (!e) {
|
||||||
|
|
|
@ -42,6 +42,19 @@ std::vector<unsigned char> &get_alpn_token() {
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
namespace {
|
||||||
|
int alpn_select_proto_cb(SSL *ssl, const unsigned char **out,
|
||||||
|
unsigned char *outlen, const unsigned char *in,
|
||||||
|
unsigned int inlen, void *arg) {
|
||||||
|
if (!util::select_h2(out, outlen, in, inlen)) {
|
||||||
|
return SSL_TLSEXT_ERR_NOACK;
|
||||||
|
}
|
||||||
|
return SSL_TLSEXT_ERR_OK;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
|
||||||
boost::system::error_code
|
boost::system::error_code
|
||||||
configure_tls_context_easy(boost::system::error_code &ec,
|
configure_tls_context_easy(boost::system::error_code &ec,
|
||||||
boost::asio::ssl::context &tls_context) {
|
boost::asio::ssl::context &tls_context) {
|
||||||
|
@ -81,6 +94,11 @@ configure_tls_context_easy(boost::system::error_code &ec,
|
||||||
},
|
},
|
||||||
nullptr);
|
nullptr);
|
||||||
|
|
||||||
|
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
// ALPN selection callback
|
||||||
|
SSL_CTX_set_alpn_select_cb(ctx, alpn_select_proto_cb, nullptr);
|
||||||
|
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
|
||||||
return ec;
|
return ec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -95,8 +95,8 @@ RequestStat::RequestStat() : data_offset(0), completed(false) {}
|
||||||
Stats::Stats(size_t req_todo)
|
Stats::Stats(size_t req_todo)
|
||||||
: req_todo(0), req_started(0), req_done(0), req_success(0),
|
: req_todo(0), req_started(0), req_done(0), req_success(0),
|
||||||
req_status_success(0), req_failed(0), req_error(0), req_timedout(0),
|
req_status_success(0), req_failed(0), req_error(0), req_timedout(0),
|
||||||
bytes_total(0), bytes_head(0), bytes_body(0), status(),
|
bytes_total(0), bytes_head(0), bytes_head_decomp(0), bytes_body(0),
|
||||||
req_stats(req_todo) {}
|
status(), req_stats(req_todo) {}
|
||||||
|
|
||||||
Stream::Stream() : status_success(-1) {}
|
Stream::Stream() : status_success(-1) {}
|
||||||
|
|
||||||
|
@ -1171,12 +1171,11 @@ int client_select_next_proto_cb(SSL *ssl, unsigned char **out,
|
||||||
if (util::select_protocol(const_cast<const unsigned char **>(out), outlen, in,
|
if (util::select_protocol(const_cast<const unsigned char **>(out), outlen, in,
|
||||||
inlen, config.npn_list)) {
|
inlen, config.npn_list)) {
|
||||||
return SSL_TLSEXT_ERR_OK;
|
return SSL_TLSEXT_ERR_OK;
|
||||||
} else if (inlen == 0) {
|
|
||||||
std::cout
|
|
||||||
<< "Server does not support NPN. Fallback behaviour may be activated."
|
|
||||||
<< std::endl;
|
|
||||||
}
|
}
|
||||||
return SSL_TLSEXT_ERR_OK;
|
|
||||||
|
// OpenSSL will terminate handshake with fatal alert if we return
|
||||||
|
// NOACK. So there is no way to fallback.
|
||||||
|
return SSL_TLSEXT_ERR_NOACK;
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
@ -2073,6 +2072,7 @@ int main(int argc, char **argv) {
|
||||||
stats.req_error += s.req_error;
|
stats.req_error += s.req_error;
|
||||||
stats.bytes_total += s.bytes_total;
|
stats.bytes_total += s.bytes_total;
|
||||||
stats.bytes_head += s.bytes_head;
|
stats.bytes_head += s.bytes_head;
|
||||||
|
stats.bytes_head_decomp += s.bytes_head_decomp;
|
||||||
stats.bytes_body += s.bytes_body;
|
stats.bytes_body += s.bytes_body;
|
||||||
|
|
||||||
for (size_t i = 0; i < stats.status.size(); ++i) {
|
for (size_t i = 0; i < stats.status.size(); ++i) {
|
||||||
|
@ -2102,7 +2102,13 @@ int main(int argc, char **argv) {
|
||||||
bps = stats.bytes_total / secd.count();
|
bps = stats.bytes_total / secd.count();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << R"(
|
double header_space_savings = 0.;
|
||||||
|
if (stats.bytes_head_decomp > 0) {
|
||||||
|
header_space_savings =
|
||||||
|
1. - static_cast<double>(stats.bytes_head) / stats.bytes_head_decomp;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << std::fixed << std::setprecision(2) << R"(
|
||||||
finished in )" << util::format_duration(duration) << ", " << rps << " req/s, "
|
finished in )" << util::format_duration(duration) << ", " << rps << " req/s, "
|
||||||
<< util::utos_with_funit(bps) << R"(B/s
|
<< util::utos_with_funit(bps) << R"(B/s
|
||||||
requests: )" << stats.req_todo << " total, " << stats.req_started
|
requests: )" << stats.req_todo << " total, " << stats.req_started
|
||||||
|
@ -2113,7 +2119,8 @@ requests: )" << stats.req_todo << " total, " << stats.req_started
|
||||||
status codes: )" << stats.status[2] << " 2xx, " << stats.status[3] << " 3xx, "
|
status codes: )" << stats.status[2] << " 2xx, " << stats.status[3] << " 3xx, "
|
||||||
<< stats.status[4] << " 4xx, " << stats.status[5] << R"( 5xx
|
<< stats.status[4] << " 4xx, " << stats.status[5] << R"( 5xx
|
||||||
traffic: )" << stats.bytes_total << " bytes total, " << stats.bytes_head
|
traffic: )" << stats.bytes_total << " bytes total, " << stats.bytes_head
|
||||||
<< " bytes headers, " << stats.bytes_body << R"( bytes data
|
<< " bytes headers (space savings " << header_space_savings * 100
|
||||||
|
<< "%), " << stats.bytes_body << R"( bytes data
|
||||||
min max mean sd +/- sd
|
min max mean sd +/- sd
|
||||||
time for request: )" << std::setw(10) << util::format_duration(ts.request.min)
|
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.max)
|
||||||
|
|
|
@ -166,8 +166,12 @@ struct Stats {
|
||||||
// The number of bytes received on the "wire". If SSL/TLS is used,
|
// The number of bytes received on the "wire". If SSL/TLS is used,
|
||||||
// this is the number of decrypted bytes the application received.
|
// this is the number of decrypted bytes the application received.
|
||||||
int64_t bytes_total;
|
int64_t bytes_total;
|
||||||
// The number of bytes received in HEADERS frame payload.
|
// The number of bytes received for header fields. This is
|
||||||
|
// compressed version.
|
||||||
int64_t bytes_head;
|
int64_t bytes_head;
|
||||||
|
// The number of bytes received for header fields after they are
|
||||||
|
// decompressed.
|
||||||
|
int64_t bytes_head_decomp;
|
||||||
// The number of bytes received in DATA frame.
|
// The number of bytes received in DATA frame.
|
||||||
int64_t bytes_body;
|
int64_t bytes_body;
|
||||||
// The number of each HTTP status category, status[i] is status code
|
// The number of each HTTP status category, status[i] is status code
|
||||||
|
|
|
@ -99,6 +99,7 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
|
||||||
auto client = session->get_client();
|
auto client = session->get_client();
|
||||||
|
|
||||||
client->worker->stats.bytes_head += len;
|
client->worker->stats.bytes_head += len;
|
||||||
|
client->worker->stats.bytes_head_decomp += len;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -109,6 +110,7 @@ int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) {
|
||||||
auto client = session->get_client();
|
auto client = session->get_client();
|
||||||
|
|
||||||
client->worker->stats.bytes_head += len;
|
client->worker->stats.bytes_head += len;
|
||||||
|
client->worker->stats.bytes_head_decomp += len;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -51,6 +51,7 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
client->on_header(frame->hd.stream_id, name, namelen, value, valuelen);
|
client->on_header(frame->hd.stream_id, name, namelen, value, valuelen);
|
||||||
|
client->worker->stats.bytes_head_decomp += namelen + valuelen;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -63,7 +64,9 @@ int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
||||||
frame->headers.cat != NGHTTP2_HCAT_RESPONSE) {
|
frame->headers.cat != NGHTTP2_HCAT_RESPONSE) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
client->worker->stats.bytes_head += frame->hd.length;
|
client->worker->stats.bytes_head +=
|
||||||
|
frame->hd.length - frame->headers.padlen -
|
||||||
|
((frame->hd.flags & NGHTTP2_FLAG_PRIORITY) ? 5 : 0);
|
||||||
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||||
client->record_ttfb();
|
client->record_ttfb();
|
||||||
}
|
}
|
||||||
|
@ -210,7 +213,7 @@ void Http2Session::on_connect() {
|
||||||
}
|
}
|
||||||
|
|
||||||
int Http2Session::submit_request(RequestStat *req_stat) {
|
int Http2Session::submit_request(RequestStat *req_stat) {
|
||||||
if (nghttp2_session_request_allowed(session_) == 0) {
|
if (nghttp2_session_check_request_allowed(session_) == 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -65,11 +65,18 @@ void on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type,
|
||||||
for (auto p = frame->syn_reply.nv; *p; p += 2) {
|
for (auto p = frame->syn_reply.nv; *p; p += 2) {
|
||||||
auto name = *p;
|
auto name = *p;
|
||||||
auto value = *(p + 1);
|
auto value = *(p + 1);
|
||||||
|
auto namelen = strlen(name);
|
||||||
|
auto valuelen = strlen(value);
|
||||||
client->on_header(frame->syn_reply.stream_id,
|
client->on_header(frame->syn_reply.stream_id,
|
||||||
reinterpret_cast<const uint8_t *>(name), strlen(name),
|
reinterpret_cast<const uint8_t *>(name), namelen,
|
||||||
reinterpret_cast<const uint8_t *>(value), strlen(value));
|
reinterpret_cast<const uint8_t *>(value), valuelen);
|
||||||
|
client->worker->stats.bytes_head_decomp += namelen + valuelen;
|
||||||
}
|
}
|
||||||
client->worker->stats.bytes_head += frame->syn_reply.hd.length;
|
|
||||||
|
// Strictly speaking, we have to subtract 2 (unused field) if SPDY
|
||||||
|
// version is 2. But it is already deprecated, and we don't do
|
||||||
|
// extra work for it.
|
||||||
|
client->worker->stats.bytes_head += frame->syn_reply.hd.length - 4;
|
||||||
|
|
||||||
if (frame->syn_stream.hd.flags & SPDYLAY_CTRL_FLAG_FIN) {
|
if (frame->syn_stream.hd.flags & SPDYLAY_CTRL_FLAG_FIN) {
|
||||||
client->record_ttfb();
|
client->record_ttfb();
|
||||||
|
|
140
src/http2.cc
140
src/http2.cc
|
@ -132,6 +132,108 @@ std::string get_status_string(unsigned int status_code) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *stringify_status(unsigned int status_code) {
|
||||||
|
switch (status_code) {
|
||||||
|
case 100:
|
||||||
|
return "100";
|
||||||
|
case 101:
|
||||||
|
return "101";
|
||||||
|
case 200:
|
||||||
|
return "200";
|
||||||
|
case 201:
|
||||||
|
return "201";
|
||||||
|
case 202:
|
||||||
|
return "202";
|
||||||
|
case 203:
|
||||||
|
return "203";
|
||||||
|
case 204:
|
||||||
|
return "204";
|
||||||
|
case 205:
|
||||||
|
return "205";
|
||||||
|
case 206:
|
||||||
|
return "206";
|
||||||
|
case 300:
|
||||||
|
return "300";
|
||||||
|
case 301:
|
||||||
|
return "301";
|
||||||
|
case 302:
|
||||||
|
return "302";
|
||||||
|
case 303:
|
||||||
|
return "303";
|
||||||
|
case 304:
|
||||||
|
return "304";
|
||||||
|
case 305:
|
||||||
|
return "305";
|
||||||
|
// case 306: return "306";
|
||||||
|
case 307:
|
||||||
|
return "307";
|
||||||
|
case 308:
|
||||||
|
return "308";
|
||||||
|
case 400:
|
||||||
|
return "400";
|
||||||
|
case 401:
|
||||||
|
return "401";
|
||||||
|
case 402:
|
||||||
|
return "402";
|
||||||
|
case 403:
|
||||||
|
return "403";
|
||||||
|
case 404:
|
||||||
|
return "404";
|
||||||
|
case 405:
|
||||||
|
return "405";
|
||||||
|
case 406:
|
||||||
|
return "406";
|
||||||
|
case 407:
|
||||||
|
return "407";
|
||||||
|
case 408:
|
||||||
|
return "408";
|
||||||
|
case 409:
|
||||||
|
return "409";
|
||||||
|
case 410:
|
||||||
|
return "410";
|
||||||
|
case 411:
|
||||||
|
return "411";
|
||||||
|
case 412:
|
||||||
|
return "412";
|
||||||
|
case 413:
|
||||||
|
return "413";
|
||||||
|
case 414:
|
||||||
|
return "414";
|
||||||
|
case 415:
|
||||||
|
return "415";
|
||||||
|
case 416:
|
||||||
|
return "416";
|
||||||
|
case 417:
|
||||||
|
return "417";
|
||||||
|
case 421:
|
||||||
|
return "421";
|
||||||
|
case 426:
|
||||||
|
return "426";
|
||||||
|
case 428:
|
||||||
|
return "428";
|
||||||
|
case 429:
|
||||||
|
return "429";
|
||||||
|
case 431:
|
||||||
|
return "431";
|
||||||
|
case 500:
|
||||||
|
return "500";
|
||||||
|
case 501:
|
||||||
|
return "501";
|
||||||
|
case 502:
|
||||||
|
return "502";
|
||||||
|
case 503:
|
||||||
|
return "503";
|
||||||
|
case 504:
|
||||||
|
return "504";
|
||||||
|
case 505:
|
||||||
|
return "505";
|
||||||
|
case 511:
|
||||||
|
return "511";
|
||||||
|
default:
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void capitalize(DefaultMemchunks *buf, const std::string &s) {
|
void capitalize(DefaultMemchunks *buf, const std::string &s) {
|
||||||
buf->append(util::upcase(s[0]));
|
buf->append(util::upcase(s[0]));
|
||||||
for (size_t i = 1; i < s.size(); ++i) {
|
for (size_t i = 1; i < s.size(); ++i) {
|
||||||
|
@ -207,17 +309,34 @@ bool non_empty_value(const Headers::value_type *nv) {
|
||||||
return nv && !nv->value.empty();
|
return nv && !nv->value.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
nghttp2_nv make_nv(const std::string &name, const std::string &value,
|
namespace {
|
||||||
bool no_index) {
|
nghttp2_nv make_nv_internal(const std::string &name, const std::string &value,
|
||||||
|
bool no_index, uint8_t nv_flags) {
|
||||||
uint8_t flags;
|
uint8_t flags;
|
||||||
|
|
||||||
flags = no_index ? NGHTTP2_NV_FLAG_NO_INDEX : NGHTTP2_NV_FLAG_NONE;
|
flags =
|
||||||
|
nv_flags | (no_index ? NGHTTP2_NV_FLAG_NO_INDEX : NGHTTP2_NV_FLAG_NONE);
|
||||||
|
|
||||||
return {(uint8_t *)name.c_str(), (uint8_t *)value.c_str(), name.size(),
|
return {(uint8_t *)name.c_str(), (uint8_t *)value.c_str(), name.size(),
|
||||||
value.size(), flags};
|
value.size(), flags};
|
||||||
}
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
void copy_headers_to_nva(std::vector<nghttp2_nv> &nva, const Headers &headers) {
|
nghttp2_nv make_nv(const std::string &name, const std::string &value,
|
||||||
|
bool no_index) {
|
||||||
|
return make_nv_internal(name, value, no_index, NGHTTP2_NV_FLAG_NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
nghttp2_nv make_nv_nocopy(const std::string &name, const std::string &value,
|
||||||
|
bool no_index) {
|
||||||
|
return make_nv_internal(name, value, no_index,
|
||||||
|
NGHTTP2_NV_FLAG_NO_COPY_NAME |
|
||||||
|
NGHTTP2_NV_FLAG_NO_COPY_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
void copy_headers_to_nva_internal(std::vector<nghttp2_nv> &nva,
|
||||||
|
const Headers &headers, uint8_t nv_flags) {
|
||||||
for (auto &kv : headers) {
|
for (auto &kv : headers) {
|
||||||
if (kv.name.empty() || kv.name[0] == ':') {
|
if (kv.name.empty() || kv.name[0] == ':') {
|
||||||
continue;
|
continue;
|
||||||
|
@ -238,9 +357,20 @@ void copy_headers_to_nva(std::vector<nghttp2_nv> &nva, const Headers &headers) {
|
||||||
case HD_X_FORWARDED_PROTO:
|
case HD_X_FORWARDED_PROTO:
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
nva.push_back(make_nv(kv.name, kv.value, kv.no_index));
|
nva.push_back(make_nv_internal(kv.name, kv.value, kv.no_index, nv_flags));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void copy_headers_to_nva(std::vector<nghttp2_nv> &nva, const Headers &headers) {
|
||||||
|
copy_headers_to_nva_internal(nva, headers, NGHTTP2_NV_FLAG_NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void copy_headers_to_nva_nocopy(std::vector<nghttp2_nv> &nva,
|
||||||
|
const Headers &headers) {
|
||||||
|
copy_headers_to_nva_internal(nva, headers, NGHTTP2_NV_FLAG_NO_COPY_NAME |
|
||||||
|
NGHTTP2_NV_FLAG_NO_COPY_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
void build_http1_headers_from_headers(DefaultMemchunks *buf,
|
void build_http1_headers_from_headers(DefaultMemchunks *buf,
|
||||||
const Headers &headers) {
|
const Headers &headers) {
|
||||||
|
|
36
src/http2.h
36
src/http2.h
|
@ -70,6 +70,10 @@ namespace http2 {
|
||||||
|
|
||||||
std::string get_status_string(unsigned int status_code);
|
std::string get_status_string(unsigned int status_code);
|
||||||
|
|
||||||
|
// Returns string version of |status_code|. This function can handle
|
||||||
|
// only predefined status code. Otherwise, returns nullptr.
|
||||||
|
const char *stringify_status(unsigned int status_code);
|
||||||
|
|
||||||
void capitalize(DefaultMemchunks *buf, const std::string &s);
|
void capitalize(DefaultMemchunks *buf, const std::string &s);
|
||||||
|
|
||||||
// Returns true if |value| is LWS
|
// Returns true if |value| is LWS
|
||||||
|
@ -110,26 +114,41 @@ bool non_empty_value(const Headers::value_type *nv);
|
||||||
nghttp2_nv make_nv(const std::string &name, const std::string &value,
|
nghttp2_nv make_nv(const std::string &name, const std::string &value,
|
||||||
bool no_index = false);
|
bool no_index = false);
|
||||||
|
|
||||||
|
nghttp2_nv make_nv_nocopy(const std::string &name, const std::string &value,
|
||||||
|
bool no_index = false);
|
||||||
|
|
||||||
// Create nghttp2_nv from string literal |name| and |value|.
|
// Create nghttp2_nv from string literal |name| and |value|.
|
||||||
template <size_t N, size_t M>
|
template <size_t N, size_t M>
|
||||||
constexpr 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,
|
return {(uint8_t *)name, (uint8_t *)value, N - 1, M - 1,
|
||||||
NGHTTP2_NV_FLAG_NONE};
|
NGHTTP2_NV_FLAG_NO_COPY_NAME | NGHTTP2_NV_FLAG_NO_COPY_VALUE};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create nghttp2_nv from string literal |name| and c-string |value|.
|
// Create nghttp2_nv from string literal |name| and c-string |value|.
|
||||||
template <size_t N>
|
template <size_t N>
|
||||||
nghttp2_nv make_nv_lc(const char (&name)[N], const char *value) {
|
nghttp2_nv make_nv_lc(const char(&name)[N], const char *value) {
|
||||||
return {(uint8_t *)name, (uint8_t *)value, N - 1, strlen(value),
|
return {(uint8_t *)name, (uint8_t *)value, N - 1, strlen(value),
|
||||||
NGHTTP2_NV_FLAG_NONE};
|
NGHTTP2_NV_FLAG_NO_COPY_NAME};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t N>
|
||||||
|
nghttp2_nv make_nv_lc_nocopy(const char(&name)[N], const char *value) {
|
||||||
|
return {(uint8_t *)name, (uint8_t *)value, N - 1, strlen(value),
|
||||||
|
NGHTTP2_NV_FLAG_NO_COPY_NAME | NGHTTP2_NV_FLAG_NO_COPY_VALUE};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create nghttp2_nv from string literal |name| and std::string
|
// Create nghttp2_nv from string literal |name| and std::string
|
||||||
// |value|.
|
// |value|.
|
||||||
template <size_t N>
|
template <size_t N>
|
||||||
nghttp2_nv make_nv_ls(const char (&name)[N], const std::string &value) {
|
nghttp2_nv make_nv_ls(const char(&name)[N], const std::string &value) {
|
||||||
return {(uint8_t *)name, (uint8_t *)value.c_str(), N - 1, value.size(),
|
return {(uint8_t *)name, (uint8_t *)value.c_str(), N - 1, value.size(),
|
||||||
NGHTTP2_NV_FLAG_NONE};
|
NGHTTP2_NV_FLAG_NO_COPY_NAME};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t N>
|
||||||
|
nghttp2_nv make_nv_ls_nocopy(const char(&name)[N], const std::string &value) {
|
||||||
|
return {(uint8_t *)name, (uint8_t *)value.c_str(), N - 1, value.size(),
|
||||||
|
NGHTTP2_NV_FLAG_NO_COPY_NAME | NGHTTP2_NV_FLAG_NO_COPY_VALUE};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Appends headers in |headers| to |nv|. |headers| must be indexed
|
// Appends headers in |headers| to |nv|. |headers| must be indexed
|
||||||
|
@ -138,6 +157,11 @@ nghttp2_nv make_nv_ls(const char (&name)[N], const std::string &value) {
|
||||||
// which require special handling (i.e. via), are not copied.
|
// which require special handling (i.e. via), are not copied.
|
||||||
void copy_headers_to_nva(std::vector<nghttp2_nv> &nva, const Headers &headers);
|
void copy_headers_to_nva(std::vector<nghttp2_nv> &nva, const Headers &headers);
|
||||||
|
|
||||||
|
// Just like copy_headers_to_nva(), but this adds
|
||||||
|
// NGHTTP2_NV_FLAG_NO_COPY_NAME and NGHTTP2_NV_FLAG_NO_COPY_VALUE.
|
||||||
|
void copy_headers_to_nva_nocopy(std::vector<nghttp2_nv> &nva,
|
||||||
|
const Headers &headers);
|
||||||
|
|
||||||
// Appends HTTP/1.1 style header lines to |buf| from headers in
|
// Appends HTTP/1.1 style header lines to |buf| from headers in
|
||||||
// |headers|. |headers| must be indexed before this call (its
|
// |headers|. |headers| must be indexed before this call (its
|
||||||
// element's token field is assigned). Certain headers, which
|
// element's token field is assigned). Certain headers, which
|
||||||
|
|
|
@ -151,10 +151,26 @@ auto headers =
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void test_http2_copy_headers_to_nva(void) {
|
void test_http2_copy_headers_to_nva(void) {
|
||||||
|
auto ans = std::vector<int>{0, 1, 4, 5, 6, 7, 12};
|
||||||
std::vector<nghttp2_nv> nva;
|
std::vector<nghttp2_nv> nva;
|
||||||
|
|
||||||
|
http2::copy_headers_to_nva_nocopy(nva, headers);
|
||||||
|
CU_ASSERT(7 == nva.size());
|
||||||
|
for (size_t i = 0; i < ans.size(); ++i) {
|
||||||
|
check_nv(headers[ans[i]], &nva[i]);
|
||||||
|
|
||||||
|
if (ans[i] == 0) {
|
||||||
|
CU_ASSERT((NGHTTP2_NV_FLAG_NO_COPY_NAME | NGHTTP2_NV_FLAG_NO_COPY_VALUE |
|
||||||
|
NGHTTP2_NV_FLAG_NO_INDEX) == nva[i].flags);
|
||||||
|
} else {
|
||||||
|
CU_ASSERT((NGHTTP2_NV_FLAG_NO_COPY_NAME |
|
||||||
|
NGHTTP2_NV_FLAG_NO_COPY_VALUE) == nva[i].flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nva.clear();
|
||||||
http2::copy_headers_to_nva(nva, headers);
|
http2::copy_headers_to_nva(nva, headers);
|
||||||
CU_ASSERT(7 == nva.size());
|
CU_ASSERT(7 == nva.size());
|
||||||
auto ans = std::vector<int>{0, 1, 4, 5, 6, 7, 12};
|
|
||||||
for (size_t i = 0; i < ans.size(); ++i) {
|
for (size_t i = 0; i < ans.size(); ++i) {
|
||||||
check_nv(headers[ans[i]], &nva[i]);
|
check_nv(headers[ans[i]], &nva[i]);
|
||||||
|
|
||||||
|
|
|
@ -38,16 +38,6 @@
|
||||||
|
|
||||||
#include <nghttp2/nghttp2.h>
|
#include <nghttp2/nghttp2.h>
|
||||||
|
|
||||||
namespace boost {
|
|
||||||
namespace system {
|
|
||||||
|
|
||||||
template <> struct is_error_code_enum<nghttp2_error> {
|
|
||||||
BOOST_STATIC_CONSTANT(bool, value = true);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace system
|
|
||||||
} // namespace boost
|
|
||||||
|
|
||||||
namespace nghttp2 {
|
namespace nghttp2 {
|
||||||
|
|
||||||
namespace asio_http2 {
|
namespace asio_http2 {
|
||||||
|
@ -99,8 +89,8 @@ typedef std::function<void(uint32_t)> close_cb;
|
||||||
// are not available right now, return NGHTTP2_ERR_DEFERRED. In case
|
// are not available right now, return NGHTTP2_ERR_DEFERRED. In case
|
||||||
// of the error and request/response must be closed, return
|
// of the error and request/response must be closed, return
|
||||||
// NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE.
|
// NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE.
|
||||||
typedef std::function<
|
typedef std::function<ssize_t(uint8_t *buf, std::size_t len,
|
||||||
ssize_t(uint8_t *buf, std::size_t len, uint32_t *data_flags)> generator_cb;
|
uint32_t *data_flags)> generator_cb;
|
||||||
|
|
||||||
// Convenient function to create function to read file denoted by
|
// Convenient function to create function to read file denoted by
|
||||||
// |path|. This can be passed to response::end().
|
// |path|. This can be passed to response::end().
|
||||||
|
@ -132,8 +122,29 @@ boost::system::error_code host_service_from_uri(boost::system::error_code &ec,
|
||||||
std::string &service,
|
std::string &service,
|
||||||
const std::string &uri);
|
const std::string &uri);
|
||||||
|
|
||||||
|
enum nghttp2_asio_error {
|
||||||
|
NGHTTP2_ASIO_ERR_NO_ERROR = 0,
|
||||||
|
NGHTTP2_ASIO_ERR_TLS_NO_APP_PROTO_NEGOTIATED = 1,
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace asio_http2
|
} // namespace asio_http2
|
||||||
|
|
||||||
} // namespace nghttp2
|
} // namespace nghttp2
|
||||||
|
|
||||||
|
namespace boost {
|
||||||
|
|
||||||
|
namespace system {
|
||||||
|
|
||||||
|
template <> struct is_error_code_enum<nghttp2_error> {
|
||||||
|
BOOST_STATIC_CONSTANT(bool, value = true);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <> struct is_error_code_enum<nghttp2::asio_http2::nghttp2_asio_error> {
|
||||||
|
BOOST_STATIC_CONSTANT(bool, value = true);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace system
|
||||||
|
|
||||||
|
} // namespace boost
|
||||||
|
|
||||||
#endif // ASIO_HTTP2_H
|
#endif // ASIO_HTTP2_H
|
||||||
|
|
|
@ -161,7 +161,7 @@ template <typename Memchunk> struct Memchunks {
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
template <size_t N> size_t append(const char (&s)[N]) {
|
template <size_t N> size_t append(const char(&s)[N]) {
|
||||||
return append(s, N - 1);
|
return append(s, N - 1);
|
||||||
}
|
}
|
||||||
size_t append(const std::string &s) { return append(s.c_str(), s.size()); }
|
size_t append(const std::string &s) { return append(s.c_str(), s.size()); }
|
||||||
|
|
|
@ -226,8 +226,7 @@ void test_peek_memchunks_append(void) {
|
||||||
|
|
||||||
std::array<uint8_t, 32> b{{
|
std::array<uint8_t, 32> b{{
|
||||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4',
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4',
|
||||||
'5', '6', '7', '8', '9', '0', '1',
|
'5', '6', '7', '8', '9', '0', '1', }},
|
||||||
}},
|
|
||||||
d;
|
d;
|
||||||
|
|
||||||
pchunks.append(b.data(), b.size());
|
pchunks.append(b.data(), b.size());
|
||||||
|
@ -261,8 +260,7 @@ void test_peek_memchunks_disable_peek_drain(void) {
|
||||||
|
|
||||||
std::array<uint8_t, 32> b{{
|
std::array<uint8_t, 32> b{{
|
||||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4',
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4',
|
||||||
'5', '6', '7', '8', '9', '0', '1',
|
'5', '6', '7', '8', '9', '0', '1', }},
|
||||||
}},
|
|
||||||
d;
|
d;
|
||||||
|
|
||||||
pchunks.append(b.data(), b.size());
|
pchunks.append(b.data(), b.size());
|
||||||
|
@ -289,8 +287,7 @@ void test_peek_memchunks_disable_peek_no_drain(void) {
|
||||||
|
|
||||||
std::array<uint8_t, 32> b{{
|
std::array<uint8_t, 32> b{{
|
||||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4',
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4',
|
||||||
'5', '6', '7', '8', '9', '0', '1',
|
'5', '6', '7', '8', '9', '0', '1', }},
|
||||||
}},
|
|
||||||
d;
|
d;
|
||||||
|
|
||||||
pchunks.append(b.data(), b.size());
|
pchunks.append(b.data(), b.size());
|
||||||
|
@ -317,8 +314,7 @@ void test_peek_memchunks_reset(void) {
|
||||||
|
|
||||||
std::array<uint8_t, 32> b{{
|
std::array<uint8_t, 32> b{{
|
||||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4',
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4',
|
||||||
'5', '6', '7', '8', '9', '0', '1',
|
'5', '6', '7', '8', '9', '0', '1', }},
|
||||||
}},
|
|
||||||
d;
|
d;
|
||||||
|
|
||||||
pchunks.append(b.data(), b.size());
|
pchunks.append(b.data(), b.size());
|
||||||
|
|
|
@ -89,8 +89,7 @@ enum {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
constexpr 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},
|
{3, 0, 201}, {5, 0, 101}, {7, 0, 1}, {9, 7, 1}, {11, 3, 1}, }};
|
||||||
}};
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
Config::Config()
|
Config::Config()
|
||||||
|
@ -392,6 +391,11 @@ int submit_request(HttpClient *client, const Headers &headers, Request *req) {
|
||||||
nva.push_back(http2::make_nv(kv.name, kv.value, kv.no_index));
|
nva.push_back(http2::make_nv(kv.name, kv.value, kv.no_index));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto method = http2::get_header(build_headers, ":method");
|
||||||
|
assert(method);
|
||||||
|
|
||||||
|
req->method = method->value;
|
||||||
|
|
||||||
std::string trailer_names;
|
std::string trailer_names;
|
||||||
if (!config.trailer.empty()) {
|
if (!config.trailer.empty()) {
|
||||||
trailer_names = config.trailer[0].name;
|
trailer_names = config.trailer[0].name;
|
||||||
|
@ -549,8 +553,9 @@ int HttpClient::initiate_connection() {
|
||||||
// If the user overrode the :authority or host header, use that
|
// If the user overrode the :authority or host header, use that
|
||||||
// value for the SNI extension
|
// value for the SNI extension
|
||||||
const char *host_string = nullptr;
|
const char *host_string = nullptr;
|
||||||
auto i = std::find_if(std::begin(config.headers),
|
auto i =
|
||||||
std::end(config.headers), [](const Header &nv) {
|
std::find_if(std::begin(config.headers), std::end(config.headers),
|
||||||
|
[](const Header &nv) {
|
||||||
return ":authority" == nv.name || "host" == nv.name;
|
return ":authority" == nv.name || "host" == nv.name;
|
||||||
});
|
});
|
||||||
if (i != std::end(config.headers)) {
|
if (i != std::end(config.headers)) {
|
||||||
|
@ -808,27 +813,42 @@ int HttpClient::on_upgrade_connect() {
|
||||||
base64::encode(std::begin(settings_payload),
|
base64::encode(std::begin(settings_payload),
|
||||||
std::begin(settings_payload) + settings_payloadlen);
|
std::begin(settings_payload) + settings_payloadlen);
|
||||||
util::to_token68(token68);
|
util::to_token68(token68);
|
||||||
|
|
||||||
std::string req;
|
std::string req;
|
||||||
if (reqvec[0]->data_prd) {
|
if (reqvec[0]->data_prd) {
|
||||||
// If the request contains upload data, use OPTIONS * to upgrade
|
// If the request contains upload data, use OPTIONS * to upgrade
|
||||||
req = "OPTIONS *";
|
req = "OPTIONS *";
|
||||||
} else {
|
} else {
|
||||||
|
auto meth = std::find_if(
|
||||||
|
std::begin(config.headers), std::end(config.headers),
|
||||||
|
[](const Header &kv) { return util::streq_l(":method", kv.name); });
|
||||||
|
|
||||||
|
if (meth == std::end(config.headers)) {
|
||||||
req = "GET ";
|
req = "GET ";
|
||||||
|
reqvec[0]->method = "GET";
|
||||||
|
} else {
|
||||||
|
req = (*meth).value;
|
||||||
|
req += " ";
|
||||||
|
reqvec[0]->method = (*meth).value;
|
||||||
|
}
|
||||||
req += reqvec[0]->make_reqpath();
|
req += reqvec[0]->make_reqpath();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto headers = Headers{{"Host", hostport},
|
auto headers = Headers{{"host", hostport},
|
||||||
{"Connection", "Upgrade, HTTP2-Settings"},
|
{"connection", "Upgrade, HTTP2-Settings"},
|
||||||
{"Upgrade", NGHTTP2_CLEARTEXT_PROTO_VERSION_ID},
|
{"upgrade", NGHTTP2_CLEARTEXT_PROTO_VERSION_ID},
|
||||||
{"HTTP2-Settings", token68},
|
{"http2-settings", token68},
|
||||||
{"Accept", "*/*"},
|
{"accept", "*/*"},
|
||||||
{"User-Agent", "nghttp2/" NGHTTP2_VERSION}};
|
{"user-agent", "nghttp2/" NGHTTP2_VERSION}};
|
||||||
auto initial_headerslen = headers.size();
|
auto initial_headerslen = headers.size();
|
||||||
|
|
||||||
for (auto &kv : config.headers) {
|
for (auto &kv : config.headers) {
|
||||||
size_t i;
|
size_t i;
|
||||||
|
if (kv.name.empty() || kv.name[0] == ':') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
for (i = 0; i < initial_headerslen; ++i) {
|
for (i = 0; i < initial_headerslen; ++i) {
|
||||||
if (util::strieq(kv.name, headers[i].name)) {
|
if (kv.name == headers[i].name) {
|
||||||
headers[i].value = kv.value;
|
headers[i].value = kv.value;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -836,10 +856,8 @@ int HttpClient::on_upgrade_connect() {
|
||||||
if (i < initial_headerslen) {
|
if (i < initial_headerslen) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (kv.name.size() != 0 && kv.name[0] != ':') {
|
|
||||||
headers.emplace_back(kv.name, kv.value, kv.no_index);
|
headers.emplace_back(kv.name, kv.value, kv.no_index);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
req += " HTTP/1.1\r\n";
|
req += " HTTP/1.1\r\n";
|
||||||
|
|
||||||
|
@ -858,9 +876,10 @@ int HttpClient::on_upgrade_connect() {
|
||||||
std::cout << " HTTP Upgrade request\n" << req << std::endl;
|
std::cout << " HTTP Upgrade request\n" << req << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
// record request time if this is GET request
|
|
||||||
if (!reqvec[0]->data_prd) {
|
if (!reqvec[0]->data_prd) {
|
||||||
|
// record request time if this is a part of real request.
|
||||||
reqvec[0]->record_request_start_time();
|
reqvec[0]->record_request_start_time();
|
||||||
|
reqvec[0]->req_nva = std::move(headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
on_writefn = &HttpClient::noop;
|
on_writefn = &HttpClient::noop;
|
||||||
|
@ -979,8 +998,12 @@ int HttpClient::connection_made() {
|
||||||
if (!reqvec[0]->data_prd) {
|
if (!reqvec[0]->data_prd) {
|
||||||
stream_user_data = reqvec[0].get();
|
stream_user_data = reqvec[0].get();
|
||||||
}
|
}
|
||||||
rv = nghttp2_session_upgrade(session, settings_payload.data(),
|
// If HEAD is used, that is only when user specified it with -H
|
||||||
settings_payloadlen, stream_user_data);
|
// option.
|
||||||
|
auto head_request = stream_user_data && stream_user_data->method == "HEAD";
|
||||||
|
rv = nghttp2_session_upgrade2(session, settings_payload.data(),
|
||||||
|
settings_payloadlen, head_request,
|
||||||
|
stream_user_data);
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
std::cerr << "[ERROR] nghttp2_session_upgrade() returned error: "
|
std::cerr << "[ERROR] nghttp2_session_upgrade() returned error: "
|
||||||
<< nghttp2_strerror(rv) << std::endl;
|
<< nghttp2_strerror(rv) << std::endl;
|
||||||
|
@ -1378,13 +1401,6 @@ void HttpClient::output_har(FILE *outfile) {
|
||||||
auto request = json_object();
|
auto request = json_object();
|
||||||
json_object_set_new(entry, "request", request);
|
json_object_set_new(entry, "request", request);
|
||||||
|
|
||||||
auto method_ptr = http2::get_header(req->req_nva, ":method");
|
|
||||||
|
|
||||||
const char *method = "GET";
|
|
||||||
if (method_ptr) {
|
|
||||||
method = (*method_ptr).value.c_str();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto req_headers = json_array();
|
auto req_headers = json_array();
|
||||||
json_object_set_new(request, "headers", req_headers);
|
json_object_set_new(request, "headers", req_headers);
|
||||||
|
|
||||||
|
@ -1396,7 +1412,7 @@ void HttpClient::output_har(FILE *outfile) {
|
||||||
json_object_set_new(hd, "value", json_string(nv.value.c_str()));
|
json_object_set_new(hd, "value", json_string(nv.value.c_str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
json_object_set_new(request, "method", json_string(method));
|
json_object_set_new(request, "method", json_string(req->method.c_str()));
|
||||||
json_object_set_new(request, "url", json_string(req->uri.c_str()));
|
json_object_set_new(request, "url", json_string(req->uri.c_str()));
|
||||||
json_object_set_new(request, "httpVersion", json_string("HTTP/2.0"));
|
json_object_set_new(request, "httpVersion", json_string("HTTP/2.0"));
|
||||||
json_object_set_new(request, "cookies", json_array());
|
json_object_set_new(request, "cookies", json_array());
|
||||||
|
@ -2011,7 +2027,8 @@ namespace {
|
||||||
int communicate(
|
int communicate(
|
||||||
const std::string &scheme, const std::string &host, uint16_t port,
|
const std::string &scheme, const std::string &host, uint16_t port,
|
||||||
std::vector<std::tuple<std::string, nghttp2_data_provider *, int64_t>>
|
std::vector<std::tuple<std::string, nghttp2_data_provider *, int64_t>>
|
||||||
requests, const nghttp2_session_callbacks *callbacks) {
|
requests,
|
||||||
|
const nghttp2_session_callbacks *callbacks) {
|
||||||
int result = 0;
|
int result = 0;
|
||||||
auto loop = EV_DEFAULT;
|
auto loop = EV_DEFAULT;
|
||||||
SSL_CTX *ssl_ctx = nullptr;
|
SSL_CTX *ssl_ctx = nullptr;
|
||||||
|
@ -2382,7 +2399,7 @@ Options:
|
||||||
Discard downloaded data.
|
Discard downloaded data.
|
||||||
-O, --remote-name
|
-O, --remote-name
|
||||||
Save download data in the current directory. The
|
Save download data in the current directory. The
|
||||||
filename is dereived from URI. If URI ends with '/',
|
filename is derived from URI. If URI ends with '/',
|
||||||
'index.html' is used as a filename. Not implemented
|
'index.html' is used as a filename. Not implemented
|
||||||
yet.
|
yet.
|
||||||
-t, --timeout=<DURATION>
|
-t, --timeout=<DURATION>
|
||||||
|
|
|
@ -137,6 +137,7 @@ struct Request {
|
||||||
|
|
||||||
Headers res_nva;
|
Headers res_nva;
|
||||||
Headers req_nva;
|
Headers req_nva;
|
||||||
|
std::string method;
|
||||||
// URI without fragment
|
// URI without fragment
|
||||||
std::string uri;
|
std::string uri;
|
||||||
http_parser_url u;
|
http_parser_url u;
|
||||||
|
|
|
@ -1213,7 +1213,7 @@ Performance:
|
||||||
Set maximum number of backend concurrent HTTP/1
|
Set maximum number of backend concurrent HTTP/1
|
||||||
connections per origin host. This option is meaningful
|
connections per origin host. This option is meaningful
|
||||||
when -s option is used. The origin host is determined
|
when -s option is used. The origin host is determined
|
||||||
by authority portion of requset URI (or :authority
|
by authority portion of request URI (or :authority
|
||||||
header field for HTTP/2). To limit the number of
|
header field for HTTP/2). To limit the number of
|
||||||
connections per frontend for default mode, use
|
connections per frontend for default mode, use
|
||||||
--backend-http1-connections-per-frontend.
|
--backend-http1-connections-per-frontend.
|
||||||
|
|
|
@ -288,7 +288,12 @@ std::pair<std::string, std::string> parse_header(const char *optarg) {
|
||||||
for (; *value == '\t' || *value == ' '; ++value)
|
for (; *value == '\t' || *value == ' '; ++value)
|
||||||
;
|
;
|
||||||
|
|
||||||
return {std::string(optarg, colon), std::string(value, strlen(value))};
|
auto p = std::make_pair(std::string(optarg, colon),
|
||||||
|
std::string(value, strlen(value)));
|
||||||
|
util::inp_strlower(p.first);
|
||||||
|
util::inp_strlower(p.second);
|
||||||
|
|
||||||
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
|
|
@ -401,8 +401,8 @@ void ConnectionHandler::accept_pending_connection() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void ConnectionHandler::set_ticket_keys(
|
||||||
ConnectionHandler::set_ticket_keys(std::shared_ptr<TicketKeys> ticket_keys) {
|
std::shared_ptr<TicketKeys> ticket_keys) {
|
||||||
ticket_keys_ = std::move(ticket_keys);
|
ticket_keys_ = std::move(ticket_keys);
|
||||||
if (single_worker_) {
|
if (single_worker_) {
|
||||||
single_worker_->set_ticket_keys(ticket_keys_);
|
single_worker_->set_ticket_keys(ticket_keys_);
|
||||||
|
@ -740,8 +740,8 @@ void ConnectionHandler::on_tls_ticket_key_get_success(
|
||||||
set_ticket_keys_to_worker(ticket_keys);
|
set_ticket_keys_to_worker(ticket_keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void ConnectionHandler::schedule_next_tls_ticket_key_memcached_get(
|
||||||
ConnectionHandler::schedule_next_tls_ticket_key_memcached_get(ev_timer *w) {
|
ev_timer *w) {
|
||||||
ev_timer_set(w, get_config()->tls_ticket_key_memcached_interval, 0.);
|
ev_timer_set(w, get_config()->tls_ticket_key_memcached_interval, 0.);
|
||||||
ev_timer_start(loop_, w);
|
ev_timer_start(loop_, w);
|
||||||
}
|
}
|
||||||
|
|
|
@ -150,6 +150,9 @@ Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool,
|
||||||
|
|
||||||
http2::init_hdidx(request_hdidx_);
|
http2::init_hdidx(request_hdidx_);
|
||||||
http2::init_hdidx(response_hdidx_);
|
http2::init_hdidx(response_hdidx_);
|
||||||
|
|
||||||
|
request_headers_.reserve(16);
|
||||||
|
response_headers_.reserve(32);
|
||||||
}
|
}
|
||||||
|
|
||||||
Downstream::~Downstream() {
|
Downstream::~Downstream() {
|
||||||
|
@ -277,8 +280,33 @@ void Downstream::assemble_request_cookie() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Headers Downstream::crumble_request_cookie() {
|
size_t Downstream::count_crumble_request_cookie() {
|
||||||
Headers cookie_hdrs;
|
size_t n = 0;
|
||||||
|
for (auto &kv : request_headers_) {
|
||||||
|
if (kv.name.size() != 6 || kv.name[5] != 'e' ||
|
||||||
|
!util::streq_l("cooki", kv.name.c_str(), 5)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
size_t last = kv.value.size();
|
||||||
|
|
||||||
|
for (size_t j = 0; j < last;) {
|
||||||
|
j = kv.value.find_first_not_of("\t ;", j);
|
||||||
|
if (j == std::string::npos) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
j = kv.value.find(';', j);
|
||||||
|
if (j == std::string::npos) {
|
||||||
|
j = last;
|
||||||
|
}
|
||||||
|
|
||||||
|
++n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Downstream::crumble_request_cookie(std::vector<nghttp2_nv> &nva) {
|
||||||
for (auto &kv : request_headers_) {
|
for (auto &kv : request_headers_) {
|
||||||
if (kv.name.size() != 6 || kv.name[5] != 'e' ||
|
if (kv.name.size() != 6 || kv.name[5] != 'e' ||
|
||||||
!util::streq_l("cooki", kv.name.c_str(), 5)) {
|
!util::streq_l("cooki", kv.name.c_str(), 5)) {
|
||||||
|
@ -298,11 +326,13 @@ Headers Downstream::crumble_request_cookie() {
|
||||||
j = last;
|
j = last;
|
||||||
}
|
}
|
||||||
|
|
||||||
cookie_hdrs.push_back(
|
nva.push_back({(uint8_t *)"cookie", (uint8_t *)kv.value.c_str() + first,
|
||||||
Header("cookie", kv.value.substr(first, j - first), kv.no_index));
|
str_size("cookie"), j - first,
|
||||||
|
(uint8_t)(NGHTTP2_NV_FLAG_NO_COPY_NAME |
|
||||||
|
NGHTTP2_NV_FLAG_NO_COPY_VALUE |
|
||||||
|
(kv.no_index ? NGHTTP2_NV_FLAG_NO_INDEX : 0))});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return cookie_hdrs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string &Downstream::get_assembled_request_cookie() const {
|
const std::string &Downstream::get_assembled_request_cookie() const {
|
||||||
|
|
|
@ -97,9 +97,11 @@ public:
|
||||||
// downstream request API
|
// downstream request API
|
||||||
const Headers &get_request_headers() const;
|
const Headers &get_request_headers() const;
|
||||||
Headers &get_request_headers();
|
Headers &get_request_headers();
|
||||||
// Crumbles (split cookie by ";") in request_headers_ and returns
|
// Count number of crumbled cookies
|
||||||
// them. Headers::no_index is inherited.
|
size_t count_crumble_request_cookie();
|
||||||
Headers crumble_request_cookie();
|
// Crumbles (split cookie by ";") in request_headers_ and adds them
|
||||||
|
// in |nva|. Headers::no_index is inherited.
|
||||||
|
void crumble_request_cookie(std::vector<nghttp2_nv> &nva);
|
||||||
void assemble_request_cookie();
|
void assemble_request_cookie();
|
||||||
const std::string &get_assembled_request_cookie() const;
|
const std::string &get_assembled_request_cookie() const;
|
||||||
// Lower the request header field names and indexes request headers.
|
// Lower the request header field names and indexes request headers.
|
||||||
|
|
|
@ -108,13 +108,29 @@ void test_downstream_crumble_request_cookie(void) {
|
||||||
reinterpret_cast<const uint8_t *>(val), strlen(val), true, -1);
|
reinterpret_cast<const uint8_t *>(val), strlen(val), true, -1);
|
||||||
d.add_request_header("cookie", ";delta");
|
d.add_request_header("cookie", ";delta");
|
||||||
d.add_request_header("cookie", "echo");
|
d.add_request_header("cookie", "echo");
|
||||||
auto cookies = d.crumble_request_cookie();
|
|
||||||
|
std::vector<nghttp2_nv> nva;
|
||||||
|
d.crumble_request_cookie(nva);
|
||||||
|
|
||||||
|
auto num_cookies = d.count_crumble_request_cookie();
|
||||||
|
|
||||||
|
CU_ASSERT(5 == nva.size());
|
||||||
|
CU_ASSERT(5 == num_cookies);
|
||||||
|
|
||||||
|
Headers cookies;
|
||||||
|
std::transform(std::begin(nva), std::end(nva), std::back_inserter(cookies),
|
||||||
|
[](const nghttp2_nv &nv) {
|
||||||
|
return Header(std::string(nv.name, nv.name + nv.namelen),
|
||||||
|
std::string(nv.value, nv.value + nv.valuelen),
|
||||||
|
nv.flags & NGHTTP2_NV_FLAG_NO_INDEX);
|
||||||
|
});
|
||||||
|
|
||||||
Headers ans = {{"cookie", "alpha"},
|
Headers ans = {{"cookie", "alpha"},
|
||||||
{"cookie", "bravo"},
|
{"cookie", "bravo"},
|
||||||
{"cookie", "charlie"},
|
{"cookie", "charlie"},
|
||||||
{"cookie", "delta"},
|
{"cookie", "delta"},
|
||||||
{"cookie", "echo"}};
|
{"cookie", "echo"}};
|
||||||
|
|
||||||
CU_ASSERT(ans == cookies);
|
CU_ASSERT(ans == cookies);
|
||||||
CU_ASSERT(cookies[0].no_index);
|
CU_ASSERT(cookies[0].no_index);
|
||||||
CU_ASSERT(cookies[1].no_index);
|
CU_ASSERT(cookies[1].no_index);
|
||||||
|
|
|
@ -205,6 +205,8 @@ ssize_t http2_data_read_callback(nghttp2_session *session, int32_t stream_id,
|
||||||
if (!trailers.empty()) {
|
if (!trailers.empty()) {
|
||||||
std::vector<nghttp2_nv> nva;
|
std::vector<nghttp2_nv> nva;
|
||||||
nva.reserve(trailers.size());
|
nva.reserve(trailers.size());
|
||||||
|
// We cannot use nocopy version, since nva may be touched after
|
||||||
|
// Downstream object is deleted.
|
||||||
http2::copy_headers_to_nva(nva, trailers);
|
http2::copy_headers_to_nva(nva, trailers);
|
||||||
if (!nva.empty()) {
|
if (!nva.empty()) {
|
||||||
rv = nghttp2_submit_trailer(session, stream_id, nva.data(), nva.size());
|
rv = nghttp2_submit_trailer(session, stream_id, nva.data(), nva.size());
|
||||||
|
@ -273,17 +275,13 @@ int Http2DownstreamConnection::push_request_headers() {
|
||||||
authority = req_authority.c_str();
|
authority = req_authority.c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!authority) {
|
|
||||||
authority = downstream_hostport;
|
|
||||||
}
|
|
||||||
|
|
||||||
downstream_->set_request_downstream_host(authority);
|
downstream_->set_request_downstream_host(authority);
|
||||||
|
|
||||||
auto nheader = downstream_->get_request_headers().size();
|
auto nheader = downstream_->get_request_headers().size();
|
||||||
|
|
||||||
Headers cookies;
|
size_t num_cookies = 0;
|
||||||
if (!get_config()->http2_no_cookie_crumbling) {
|
if (!get_config()->http2_no_cookie_crumbling) {
|
||||||
cookies = downstream_->crumble_request_cookie();
|
num_cookies = downstream_->count_crumble_request_cookie();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 8 means:
|
// 8 means:
|
||||||
|
@ -296,29 +294,30 @@ int Http2DownstreamConnection::push_request_headers() {
|
||||||
// 7. x-forwarded-proto (optional)
|
// 7. x-forwarded-proto (optional)
|
||||||
// 8. te (optional)
|
// 8. te (optional)
|
||||||
auto nva = std::vector<nghttp2_nv>();
|
auto nva = std::vector<nghttp2_nv>();
|
||||||
nva.reserve(nheader + 8 + cookies.size() +
|
nva.reserve(nheader + 8 + num_cookies +
|
||||||
get_config()->add_request_headers.size());
|
get_config()->add_request_headers.size());
|
||||||
|
|
||||||
nva.push_back(http2::make_nv_lc(":method", http2::to_method_string(method)));
|
nva.push_back(
|
||||||
|
http2::make_nv_lc_nocopy(":method", http2::to_method_string(method)));
|
||||||
|
|
||||||
auto &scheme = downstream_->get_request_http2_scheme();
|
auto &scheme = downstream_->get_request_http2_scheme();
|
||||||
|
|
||||||
nva.push_back(http2::make_nv_lc(":authority", authority));
|
nva.push_back(http2::make_nv_lc_nocopy(":authority", authority));
|
||||||
|
|
||||||
if (method != HTTP_CONNECT) {
|
if (method != HTTP_CONNECT) {
|
||||||
assert(!scheme.empty());
|
assert(!scheme.empty());
|
||||||
|
|
||||||
nva.push_back(http2::make_nv_ls(":scheme", scheme));
|
nva.push_back(http2::make_nv_ls_nocopy(":scheme", scheme));
|
||||||
|
|
||||||
auto &path = downstream_->get_request_path();
|
auto &path = downstream_->get_request_path();
|
||||||
if (method == HTTP_OPTIONS && path.empty()) {
|
if (method == HTTP_OPTIONS && path.empty()) {
|
||||||
nva.push_back(http2::make_nv_ll(":path", "*"));
|
nva.push_back(http2::make_nv_ll(":path", "*"));
|
||||||
} else {
|
} else {
|
||||||
nva.push_back(http2::make_nv_ls(":path", path));
|
nva.push_back(http2::make_nv_ls_nocopy(":path", path));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
http2::copy_headers_to_nva(nva, downstream_->get_request_headers());
|
http2::copy_headers_to_nva_nocopy(nva, downstream_->get_request_headers());
|
||||||
|
|
||||||
bool chunked_encoding = false;
|
bool chunked_encoding = false;
|
||||||
auto transfer_encoding =
|
auto transfer_encoding =
|
||||||
|
@ -328,8 +327,8 @@ int Http2DownstreamConnection::push_request_headers() {
|
||||||
chunked_encoding = true;
|
chunked_encoding = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &nv : cookies) {
|
if (!get_config()->http2_no_cookie_crumbling) {
|
||||||
nva.push_back(http2::make_nv(nv.name, nv.value, nv.no_index));
|
downstream_->crumble_request_cookie(nva);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string xff_value;
|
std::string xff_value;
|
||||||
|
@ -343,20 +342,20 @@ int Http2DownstreamConnection::push_request_headers() {
|
||||||
downstream_->get_upstream()->get_client_handler()->get_ipaddr();
|
downstream_->get_upstream()->get_client_handler()->get_ipaddr();
|
||||||
nva.push_back(http2::make_nv_ls("x-forwarded-for", xff_value));
|
nva.push_back(http2::make_nv_ls("x-forwarded-for", xff_value));
|
||||||
} else if (xff && !get_config()->strip_incoming_x_forwarded_for) {
|
} else if (xff && !get_config()->strip_incoming_x_forwarded_for) {
|
||||||
nva.push_back(http2::make_nv_ls("x-forwarded-for", (*xff).value));
|
nva.push_back(http2::make_nv_ls_nocopy("x-forwarded-for", (*xff).value));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!get_config()->http2_proxy && !get_config()->client_proxy &&
|
if (!get_config()->http2_proxy && !get_config()->client_proxy &&
|
||||||
downstream_->get_request_method() != HTTP_CONNECT) {
|
downstream_->get_request_method() != HTTP_CONNECT) {
|
||||||
// We use same protocol with :scheme header field
|
// We use same protocol with :scheme header field
|
||||||
nva.push_back(http2::make_nv_ls("x-forwarded-proto", scheme));
|
nva.push_back(http2::make_nv_ls_nocopy("x-forwarded-proto", scheme));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string via_value;
|
std::string via_value;
|
||||||
auto via = downstream_->get_request_header(http2::HD_VIA);
|
auto via = downstream_->get_request_header(http2::HD_VIA);
|
||||||
if (get_config()->no_via) {
|
if (get_config()->no_via) {
|
||||||
if (via) {
|
if (via) {
|
||||||
nva.push_back(http2::make_nv_ls("via", (*via).value));
|
nva.push_back(http2::make_nv_ls_nocopy("via", (*via).value));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (via) {
|
if (via) {
|
||||||
|
@ -377,7 +376,7 @@ int Http2DownstreamConnection::push_request_headers() {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &p : get_config()->add_request_headers) {
|
for (auto &p : get_config()->add_request_headers) {
|
||||||
nva.push_back(http2::make_nv(p.first, p.second));
|
nva.push_back(http2::make_nv_nocopy(p.first, p.second));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LOG_ENABLED(INFO)) {
|
if (LOG_ENABLED(INFO)) {
|
||||||
|
|
|
@ -553,8 +553,8 @@ void Http2Session::add_downstream_connection(Http2DownstreamConnection *dconn) {
|
||||||
dconns_.append(dconn);
|
dconns_.append(dconn);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void Http2Session::remove_downstream_connection(
|
||||||
Http2Session::remove_downstream_connection(Http2DownstreamConnection *dconn) {
|
Http2DownstreamConnection *dconn) {
|
||||||
dconns_.remove(dconn);
|
dconns_.remove(dconn);
|
||||||
dconn->detach_stream_data();
|
dconn->detach_stream_data();
|
||||||
}
|
}
|
||||||
|
@ -1569,13 +1569,6 @@ int Http2Session::connected() {
|
||||||
|
|
||||||
conn_.rlimit.startw();
|
conn_.rlimit.startw();
|
||||||
|
|
||||||
if (conn_.tls.ssl) {
|
|
||||||
read_ = &Http2Session::tls_handshake;
|
|
||||||
write_ = &Http2Session::tls_handshake;
|
|
||||||
|
|
||||||
return do_write();
|
|
||||||
}
|
|
||||||
|
|
||||||
read_ = &Http2Session::read_clear;
|
read_ = &Http2Session::read_clear;
|
||||||
write_ = &Http2Session::write_clear;
|
write_ = &Http2Session::write_clear;
|
||||||
|
|
||||||
|
@ -1583,6 +1576,13 @@ int Http2Session::connected() {
|
||||||
return do_write();
|
return do_write();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (conn_.tls.ssl) {
|
||||||
|
read_ = &Http2Session::tls_handshake;
|
||||||
|
write_ = &Http2Session::tls_handshake;
|
||||||
|
|
||||||
|
return do_write();
|
||||||
|
}
|
||||||
|
|
||||||
if (connection_made() != 0) {
|
if (connection_made() != 0) {
|
||||||
state_ = CONNECT_FAILING;
|
state_ = CONNECT_FAILING;
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
@ -108,9 +108,10 @@ int Http2Upstream::upgrade_upstream(HttpsUpstream *http) {
|
||||||
auto settings_payload =
|
auto settings_payload =
|
||||||
base64::decode(std::begin(http2_settings), std::end(http2_settings));
|
base64::decode(std::begin(http2_settings), std::end(http2_settings));
|
||||||
|
|
||||||
rv = nghttp2_session_upgrade(
|
rv = nghttp2_session_upgrade2(
|
||||||
session_, reinterpret_cast<const uint8_t *>(settings_payload.c_str()),
|
session_, reinterpret_cast<const uint8_t *>(settings_payload.c_str()),
|
||||||
settings_payload.size(), nullptr);
|
settings_payload.size(),
|
||||||
|
http->get_downstream()->get_request_method() == HTTP_HEAD, nullptr);
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
if (LOG_ENABLED(INFO)) {
|
if (LOG_ENABLED(INFO)) {
|
||||||
ULOG(INFO, this) << "nghttp2_session_upgrade() returned error: "
|
ULOG(INFO, this) << "nghttp2_session_upgrade() returned error: "
|
||||||
|
@ -1259,7 +1260,7 @@ ssize_t downstream_data_read_callback(nghttp2_session *session,
|
||||||
if (!trailers.empty()) {
|
if (!trailers.empty()) {
|
||||||
std::vector<nghttp2_nv> nva;
|
std::vector<nghttp2_nv> nva;
|
||||||
nva.reserve(trailers.size());
|
nva.reserve(trailers.size());
|
||||||
http2::copy_headers_to_nva(nva, trailers);
|
http2::copy_headers_to_nva_nocopy(nva, trailers);
|
||||||
if (!nva.empty()) {
|
if (!nva.empty()) {
|
||||||
rv = nghttp2_submit_trailer(session, stream_id, nva.data(),
|
rv = nghttp2_submit_trailer(session, stream_id, nva.data(),
|
||||||
nva.size());
|
nva.size());
|
||||||
|
@ -1295,13 +1296,20 @@ int Http2Upstream::send_reply(Downstream *downstream, const uint8_t *body,
|
||||||
data_prd_ptr = &data_prd;
|
data_prd_ptr = &data_prd;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto status_code_str = util::utos(downstream->get_response_http_status());
|
|
||||||
auto &headers = downstream->get_response_headers();
|
auto &headers = downstream->get_response_headers();
|
||||||
auto nva = std::vector<nghttp2_nv>();
|
auto nva = std::vector<nghttp2_nv>();
|
||||||
// 2 for :status and server
|
// 2 for :status and server
|
||||||
nva.reserve(2 + headers.size());
|
nva.reserve(2 + headers.size());
|
||||||
|
|
||||||
|
std::string status_code_str;
|
||||||
|
auto response_status_const =
|
||||||
|
http2::stringify_status(downstream->get_response_http_status());
|
||||||
|
if (response_status_const) {
|
||||||
|
nva.push_back(http2::make_nv_lc_nocopy(":status", response_status_const));
|
||||||
|
} else {
|
||||||
|
status_code_str = util::utos(downstream->get_response_http_status());
|
||||||
nva.push_back(http2::make_nv_ls(":status", status_code_str));
|
nva.push_back(http2::make_nv_ls(":status", status_code_str));
|
||||||
|
}
|
||||||
|
|
||||||
for (auto &kv : headers) {
|
for (auto &kv : headers) {
|
||||||
if (kv.name.empty() || kv.name[0] == ':') {
|
if (kv.name.empty() || kv.name[0] == ':') {
|
||||||
|
@ -1316,11 +1324,12 @@ int Http2Upstream::send_reply(Downstream *downstream, const uint8_t *body,
|
||||||
case http2::HD_UPGRADE:
|
case http2::HD_UPGRADE:
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
nva.push_back(http2::make_nv(kv.name, kv.value, kv.no_index));
|
nva.push_back(http2::make_nv_nocopy(kv.name, kv.value, kv.no_index));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!downstream->get_response_header(http2::HD_SERVER)) {
|
if (!downstream->get_response_header(http2::HD_SERVER)) {
|
||||||
nva.push_back(http2::make_nv_lc("server", get_config()->server_name));
|
nva.push_back(
|
||||||
|
http2::make_nv_lc_nocopy("server", get_config()->server_name));
|
||||||
}
|
}
|
||||||
|
|
||||||
rv = nghttp2_submit_response(session_, downstream->get_stream_id(),
|
rv = nghttp2_submit_response(session_, downstream->get_stream_id(),
|
||||||
|
@ -1356,12 +1365,18 @@ int Http2Upstream::error_reply(Downstream *downstream,
|
||||||
auto lgconf = log_config();
|
auto lgconf = log_config();
|
||||||
lgconf->update_tstamp(std::chrono::system_clock::now());
|
lgconf->update_tstamp(std::chrono::system_clock::now());
|
||||||
|
|
||||||
|
auto response_status_const = http2::stringify_status(status_code);
|
||||||
auto content_length = util::utos(html.size());
|
auto content_length = util::utos(html.size());
|
||||||
auto status_code_str = util::utos(status_code);
|
|
||||||
auto nva =
|
std::string status_code_str;
|
||||||
make_array(http2::make_nv_ls(":status", status_code_str),
|
|
||||||
|
auto nva = make_array(
|
||||||
|
response_status_const
|
||||||
|
? http2::make_nv_lc_nocopy(":status", response_status_const)
|
||||||
|
: http2::make_nv_ls(":status",
|
||||||
|
(status_code_str = util::utos(status_code))),
|
||||||
http2::make_nv_ll("content-type", "text/html; charset=UTF-8"),
|
http2::make_nv_ll("content-type", "text/html; charset=UTF-8"),
|
||||||
http2::make_nv_lc("server", get_config()->server_name),
|
http2::make_nv_lc_nocopy("server", get_config()->server_name),
|
||||||
http2::make_nv_ls("content-length", content_length),
|
http2::make_nv_ls("content-length", content_length),
|
||||||
http2::make_nv_ls("date", lgconf->time_http_str));
|
http2::make_nv_ls("date", lgconf->time_http_str));
|
||||||
|
|
||||||
|
@ -1376,8 +1391,8 @@ int Http2Upstream::error_reply(Downstream *downstream,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void Http2Upstream::add_pending_downstream(
|
||||||
Http2Upstream::add_pending_downstream(std::unique_ptr<Downstream> downstream) {
|
std::unique_ptr<Downstream> downstream) {
|
||||||
downstream_queue_.add_pending(std::move(downstream));
|
downstream_queue_.add_pending(std::move(downstream));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1445,12 +1460,20 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
|
||||||
// field.
|
// field.
|
||||||
nva.reserve(nheader + 4 + get_config()->add_response_headers.size());
|
nva.reserve(nheader + 4 + get_config()->add_response_headers.size());
|
||||||
std::string via_value;
|
std::string via_value;
|
||||||
auto response_status = util::utos(downstream->get_response_http_status());
|
std::string response_status;
|
||||||
nva.push_back(http2::make_nv_ls(":status", response_status));
|
|
||||||
|
|
||||||
http2::copy_headers_to_nva(nva, downstream->get_response_headers());
|
auto response_status_const =
|
||||||
|
http2::stringify_status(downstream->get_response_http_status());
|
||||||
|
if (response_status_const) {
|
||||||
|
nva.push_back(http2::make_nv_lc_nocopy(":status", response_status_const));
|
||||||
|
} else {
|
||||||
|
response_status = util::utos(downstream->get_response_http_status());
|
||||||
|
nva.push_back(http2::make_nv_ls(":status", response_status));
|
||||||
|
}
|
||||||
|
|
||||||
if (downstream->get_non_final_response()) {
|
if (downstream->get_non_final_response()) {
|
||||||
|
http2::copy_headers_to_nva(nva, downstream->get_response_headers());
|
||||||
|
|
||||||
if (LOG_ENABLED(INFO)) {
|
if (LOG_ENABLED(INFO)) {
|
||||||
log_response_headers(downstream, nva);
|
log_response_headers(downstream, nva);
|
||||||
}
|
}
|
||||||
|
@ -1469,19 +1492,22 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
http2::copy_headers_to_nva_nocopy(nva, downstream->get_response_headers());
|
||||||
|
|
||||||
if (!get_config()->http2_proxy && !get_config()->client_proxy) {
|
if (!get_config()->http2_proxy && !get_config()->client_proxy) {
|
||||||
nva.push_back(http2::make_nv_lc("server", get_config()->server_name));
|
nva.push_back(
|
||||||
|
http2::make_nv_lc_nocopy("server", get_config()->server_name));
|
||||||
} else {
|
} else {
|
||||||
auto server = downstream->get_response_header(http2::HD_SERVER);
|
auto server = downstream->get_response_header(http2::HD_SERVER);
|
||||||
if (server) {
|
if (server) {
|
||||||
nva.push_back(http2::make_nv_ls("server", (*server).value));
|
nva.push_back(http2::make_nv_ls_nocopy("server", (*server).value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto via = downstream->get_response_header(http2::HD_VIA);
|
auto via = downstream->get_response_header(http2::HD_VIA);
|
||||||
if (get_config()->no_via) {
|
if (get_config()->no_via) {
|
||||||
if (via) {
|
if (via) {
|
||||||
nva.push_back(http2::make_nv_ls("via", (*via).value));
|
nva.push_back(http2::make_nv_ls_nocopy("via", (*via).value));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (via) {
|
if (via) {
|
||||||
|
@ -1494,7 +1520,7 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &p : get_config()->add_response_headers) {
|
for (auto &p : get_config()->add_response_headers) {
|
||||||
nva.push_back(http2::make_nv(p.first, p.second));
|
nva.push_back(http2::make_nv_nocopy(p.first, p.second));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (downstream->get_stream_id() % 2 == 0) {
|
if (downstream->get_stream_id() % 2 == 0) {
|
||||||
|
@ -1646,9 +1672,8 @@ int Http2Upstream::consume(int32_t stream_id, size_t len) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void Http2Upstream::log_response_headers(
|
||||||
Http2Upstream::log_response_headers(Downstream *downstream,
|
Downstream *downstream, const std::vector<nghttp2_nv> &nva) const {
|
||||||
const std::vector<nghttp2_nv> &nva) const {
|
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
for (auto &nv : nva) {
|
for (auto &nv : nva) {
|
||||||
ss << TTY_HTTP_HD << nv.name << TTY_RST << ": " << nv.value << "\n";
|
ss << TTY_HTTP_HD << nv.name << TTY_RST << ": " << nv.value << "\n";
|
||||||
|
@ -1797,7 +1822,7 @@ int Http2Upstream::submit_push_promise(const std::string &scheme,
|
||||||
case http2::HD_CACHE_CONTROL:
|
case http2::HD_CACHE_CONTROL:
|
||||||
case http2::HD_HOST:
|
case http2::HD_HOST:
|
||||||
case http2::HD_USER_AGENT:
|
case http2::HD_USER_AGENT:
|
||||||
nva.push_back(http2::make_nv(kv.name, kv.value, kv.no_index));
|
nva.push_back(http2::make_nv_nocopy(kv.name, kv.value, kv.no_index));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -184,7 +184,7 @@ std::pair<OutputIterator, size_t> copy(const std::string &src, size_t avail,
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
template <size_t N, typename OutputIterator>
|
template <size_t N, typename OutputIterator>
|
||||||
std::pair<OutputIterator, size_t> copy_l(const char (&src)[N], size_t avail,
|
std::pair<OutputIterator, size_t> copy_l(const char(&src)[N], size_t avail,
|
||||||
OutputIterator oitr) {
|
OutputIterator oitr) {
|
||||||
return copy(src, N - 1, avail, oitr);
|
return copy(src, N - 1, avail, oitr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,8 +52,8 @@ static LogConfig *config = new LogConfig();
|
||||||
LogConfig *log_config(void) { return config; }
|
LogConfig *log_config(void) { return config; }
|
||||||
#endif // NOTHREADS
|
#endif // NOTHREADS
|
||||||
|
|
||||||
void
|
void LogConfig::update_tstamp(
|
||||||
LogConfig::update_tstamp(const std::chrono::system_clock::time_point &now) {
|
const std::chrono::system_clock::time_point &now) {
|
||||||
auto t0 = std::chrono::system_clock::to_time_t(time_str_updated_);
|
auto t0 = std::chrono::system_clock::to_time_t(time_str_updated_);
|
||||||
auto t1 = std::chrono::system_clock::to_time_t(now);
|
auto t1 = std::chrono::system_clock::to_time_t(now);
|
||||||
if (t0 == t1) {
|
if (t0 == t1) {
|
||||||
|
|
|
@ -888,13 +888,11 @@ int SpdyUpstream::error_reply(Downstream *downstream,
|
||||||
|
|
||||||
std::string content_length = util::utos(html.size());
|
std::string content_length = util::utos(html.size());
|
||||||
std::string status_string = http2::get_status_string(status_code);
|
std::string status_string = http2::get_status_string(status_code);
|
||||||
const char *nv[] = {":status", status_string.c_str(),
|
const char *nv[] = {":status", status_string.c_str(), ":version", "http/1.1",
|
||||||
":version", "http/1.1",
|
"content-type", "text/html; charset=UTF-8", "server",
|
||||||
"content-type", "text/html; charset=UTF-8",
|
get_config()->server_name, "content-length",
|
||||||
"server", get_config()->server_name,
|
content_length.c_str(), "date",
|
||||||
"content-length", content_length.c_str(),
|
lgconf->time_http_str.c_str(), nullptr};
|
||||||
"date", lgconf->time_http_str.c_str(),
|
|
||||||
nullptr};
|
|
||||||
|
|
||||||
rv = spdylay_submit_response(session_, downstream->get_stream_id(), nv,
|
rv = spdylay_submit_response(session_, downstream->get_stream_id(), nv,
|
||||||
&data_prd);
|
&data_prd);
|
||||||
|
|
|
@ -930,7 +930,10 @@ int check_cert(SSL *ssl, const DownstreamAddr *addr) {
|
||||||
std::vector<std::string> dns_names;
|
std::vector<std::string> dns_names;
|
||||||
std::vector<std::string> ip_addrs;
|
std::vector<std::string> ip_addrs;
|
||||||
get_altnames(cert, dns_names, ip_addrs, common_name);
|
get_altnames(cert, dns_names, ip_addrs, common_name);
|
||||||
if (verify_hostname(addr->host.get(), &addr->addr, dns_names, ip_addrs,
|
auto hostname = get_config()->backend_tls_sni_name
|
||||||
|
? get_config()->backend_tls_sni_name.get()
|
||||||
|
: addr->host.get();
|
||||||
|
if (verify_hostname(hostname, &addr->addr, dns_names, ip_addrs,
|
||||||
common_name) != 0) {
|
common_name) != 0) {
|
||||||
LOG(ERROR) << "Certificate verification failed: hostname does not match";
|
LOG(ERROR) << "Certificate verification failed: hostname does not match";
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
@ -43,10 +43,9 @@ void test_shrpx_ssl_create_lookup_tree(void) {
|
||||||
SSL_CTX_new(SSLv23_method()), SSL_CTX_new(SSLv23_method()),
|
SSL_CTX_new(SSLv23_method()), SSL_CTX_new(SSLv23_method()),
|
||||||
SSL_CTX_new(SSLv23_method()), SSL_CTX_new(SSLv23_method())};
|
SSL_CTX_new(SSLv23_method()), SSL_CTX_new(SSLv23_method())};
|
||||||
|
|
||||||
const char *hostnames[] = {"example.com", "www.example.org",
|
const char *hostnames[] = {
|
||||||
"*www.example.org", "x*.host.domain",
|
"example.com", "www.example.org", "*www.example.org", "x*.host.domain",
|
||||||
"*yy.host.domain", "nghttp2.sourceforge.net",
|
"*yy.host.domain", "nghttp2.sourceforge.net", "sourceforge.net",
|
||||||
"sourceforge.net",
|
|
||||||
"sourceforge.net", // duplicate
|
"sourceforge.net", // duplicate
|
||||||
"*.foo.bar", // oo.bar is suffix of *.foo.bar
|
"*.foo.bar", // oo.bar is suffix of *.foo.bar
|
||||||
"oo.bar"};
|
"oo.bar"};
|
||||||
|
|
|
@ -60,11 +60,11 @@ make_array(T &&... t) {
|
||||||
sizeof...(T)>{{std::forward<T>(t)...}};
|
sizeof...(T)>{{std::forward<T>(t)...}};
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, size_t N> constexpr size_t array_size(T (&)[N]) {
|
template <typename T, size_t N> constexpr size_t array_size(T(&)[N]) {
|
||||||
return N;
|
return N;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, size_t N> constexpr size_t str_size(T (&)[N]) {
|
template <typename T, size_t N> constexpr size_t str_size(T(&)[N]) {
|
||||||
return N - 1;
|
return N - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,8 +76,8 @@ template <typename F, typename... T> struct Defer {
|
||||||
Defer(Defer &&o) : f(std::move(o.f)) {}
|
Defer(Defer &&o) : f(std::move(o.f)) {}
|
||||||
~Defer() { f(); }
|
~Defer() { f(); }
|
||||||
|
|
||||||
using ResultType = typename std::result_of<
|
using ResultType = typename std::result_of<typename std::decay<F>::type(
|
||||||
typename std::decay<F>::type(typename std::decay<T>::type...)>::type;
|
typename std::decay<T>::type...)>::type;
|
||||||
std::function<ResultType()> f;
|
std::function<ResultType()> f;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
12
src/util.h
12
src/util.h
|
@ -220,7 +220,7 @@ std::string quote_string(const std::string &target);
|
||||||
|
|
||||||
std::string format_hex(const unsigned char *s, size_t len);
|
std::string format_hex(const unsigned char *s, size_t len);
|
||||||
|
|
||||||
template <size_t N> std::string format_hex(const unsigned char (&s)[N]) {
|
template <size_t N> std::string format_hex(const unsigned char(&s)[N]) {
|
||||||
return format_hex(s, N);
|
return format_hex(s, N);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -366,11 +366,11 @@ inline bool strieq(const char *a, const std::string &b) {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename InputIt, size_t N>
|
template <typename InputIt, size_t N>
|
||||||
bool strieq_l(const char (&a)[N], InputIt b, size_t blen) {
|
bool strieq_l(const char(&a)[N], InputIt b, size_t blen) {
|
||||||
return strieq(a, N - 1, b, blen);
|
return strieq(a, N - 1, b, blen);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <size_t N> bool strieq_l(const char (&a)[N], const std::string &b) {
|
template <size_t N> bool strieq_l(const char(&a)[N], const std::string &b) {
|
||||||
return strieq(a, N - 1, std::begin(b), b.size());
|
return strieq(a, N - 1, std::begin(b), b.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -400,10 +400,14 @@ inline bool streq(const char *a, const char *b) {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename InputIt, size_t N>
|
template <typename InputIt, size_t N>
|
||||||
bool streq_l(const char (&a)[N], InputIt b, size_t blen) {
|
bool streq_l(const char(&a)[N], InputIt b, size_t blen) {
|
||||||
return streq(a, N - 1, b, blen);
|
return streq(a, N - 1, b, blen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <size_t N> bool streq_l(const char(&a)[N], const std::string &b) {
|
||||||
|
return streq(a, N - 1, std::begin(b), b.size());
|
||||||
|
}
|
||||||
|
|
||||||
bool strifind(const char *a, const char *b);
|
bool strifind(const char *a, const char *b);
|
||||||
|
|
||||||
template <typename InputIt> void inp_strlower(InputIt first, InputIt last) {
|
template <typename InputIt> void inp_strlower(InputIt first, InputIt last) {
|
||||||
|
|
|
@ -140,7 +140,7 @@ int main(int argc _U_, char *argv[] _U_) {
|
||||||
test_nghttp2_session_send_push_promise) ||
|
test_nghttp2_session_send_push_promise) ||
|
||||||
!CU_add_test(pSuite, "session_is_my_stream_id",
|
!CU_add_test(pSuite, "session_is_my_stream_id",
|
||||||
test_nghttp2_session_is_my_stream_id) ||
|
test_nghttp2_session_is_my_stream_id) ||
|
||||||
!CU_add_test(pSuite, "session_upgrade", test_nghttp2_session_upgrade) ||
|
!CU_add_test(pSuite, "session_upgrade2", test_nghttp2_session_upgrade2) ||
|
||||||
!CU_add_test(pSuite, "session_reprioritize_stream",
|
!CU_add_test(pSuite, "session_reprioritize_stream",
|
||||||
test_nghttp2_session_reprioritize_stream) ||
|
test_nghttp2_session_reprioritize_stream) ||
|
||||||
!CU_add_test(
|
!CU_add_test(
|
||||||
|
@ -301,6 +301,8 @@ int main(int argc _U_, char *argv[] _U_) {
|
||||||
test_nghttp2_http_record_request_method) ||
|
test_nghttp2_http_record_request_method) ||
|
||||||
!CU_add_test(pSuite, "http_push_promise",
|
!CU_add_test(pSuite, "http_push_promise",
|
||||||
test_nghttp2_http_push_promise) ||
|
test_nghttp2_http_push_promise) ||
|
||||||
|
!CU_add_test(pSuite, "http_head_method_upgrade_workaround",
|
||||||
|
test_nghttp2_http_head_method_upgrade_workaround) ||
|
||||||
!CU_add_test(pSuite, "frame_pack_headers",
|
!CU_add_test(pSuite, "frame_pack_headers",
|
||||||
test_nghttp2_frame_pack_headers) ||
|
test_nghttp2_frame_pack_headers) ||
|
||||||
!CU_add_test(pSuite, "frame_pack_headers_frame_too_large",
|
!CU_add_test(pSuite, "frame_pack_headers_frame_too_large",
|
||||||
|
|
|
@ -1007,25 +1007,20 @@ void test_nghttp2_hd_deflate_inflate(void) {
|
||||||
nghttp2_hd_deflater deflater;
|
nghttp2_hd_deflater deflater;
|
||||||
nghttp2_hd_inflater inflater;
|
nghttp2_hd_inflater inflater;
|
||||||
nghttp2_nv nv1[] = {
|
nghttp2_nv nv1[] = {
|
||||||
MAKE_NV(":status", "200 OK"),
|
MAKE_NV(":status", "200 OK"), MAKE_NV("access-control-allow-origin", "*"),
|
||||||
MAKE_NV("access-control-allow-origin", "*"),
|
|
||||||
MAKE_NV("cache-control", "private, max-age=0, must-revalidate"),
|
MAKE_NV("cache-control", "private, max-age=0, must-revalidate"),
|
||||||
MAKE_NV("content-length", "76073"),
|
MAKE_NV("content-length", "76073"), MAKE_NV("content-type", "text/html"),
|
||||||
MAKE_NV("content-type", "text/html"),
|
|
||||||
MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"),
|
MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"),
|
||||||
MAKE_NV("expires", "Sat, 27 Jul 2013 06:22:12 GMT"),
|
MAKE_NV("expires", "Sat, 27 Jul 2013 06:22:12 GMT"),
|
||||||
MAKE_NV("server", "Apache"),
|
MAKE_NV("server", "Apache"), MAKE_NV("vary", "foobar"),
|
||||||
MAKE_NV("vary", "foobar"),
|
|
||||||
MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"),
|
MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"),
|
||||||
MAKE_NV("x-cache", "MISS from alphabravo"),
|
MAKE_NV("x-cache", "MISS from alphabravo"),
|
||||||
MAKE_NV("x-cache-action", "MISS"),
|
MAKE_NV("x-cache-action", "MISS"), MAKE_NV("x-cache-age", "0"),
|
||||||
MAKE_NV("x-cache-age", "0"),
|
|
||||||
MAKE_NV("x-cache-lookup", "MISS from alphabravo:3128"),
|
MAKE_NV("x-cache-lookup", "MISS from alphabravo:3128"),
|
||||||
MAKE_NV("x-lb-nocache", "true"),
|
MAKE_NV("x-lb-nocache", "true"),
|
||||||
};
|
};
|
||||||
nghttp2_nv nv2[] = {
|
nghttp2_nv nv2[] = {
|
||||||
MAKE_NV(":status", "304 Not Modified"),
|
MAKE_NV(":status", "304 Not Modified"), MAKE_NV("age", "0"),
|
||||||
MAKE_NV("age", "0"),
|
|
||||||
MAKE_NV("cache-control", "max-age=56682045"),
|
MAKE_NV("cache-control", "max-age=56682045"),
|
||||||
MAKE_NV("content-type", "text/css"),
|
MAKE_NV("content-type", "text/css"),
|
||||||
MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"),
|
MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"),
|
||||||
|
@ -1036,8 +1031,7 @@ void test_nghttp2_hd_deflate_inflate(void) {
|
||||||
MAKE_NV("x-cache", "HIT from alphabravo"),
|
MAKE_NV("x-cache", "HIT from alphabravo"),
|
||||||
MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128")};
|
MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128")};
|
||||||
nghttp2_nv nv3[] = {
|
nghttp2_nv nv3[] = {
|
||||||
MAKE_NV(":status", "304 Not Modified"),
|
MAKE_NV(":status", "304 Not Modified"), MAKE_NV("age", "0"),
|
||||||
MAKE_NV("age", "0"),
|
|
||||||
MAKE_NV("cache-control", "max-age=56682072"),
|
MAKE_NV("cache-control", "max-age=56682072"),
|
||||||
MAKE_NV("content-type", "text/css"),
|
MAKE_NV("content-type", "text/css"),
|
||||||
MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"),
|
MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"),
|
||||||
|
@ -1049,8 +1043,7 @@ void test_nghttp2_hd_deflate_inflate(void) {
|
||||||
MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"),
|
MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"),
|
||||||
};
|
};
|
||||||
nghttp2_nv nv4[] = {
|
nghttp2_nv nv4[] = {
|
||||||
MAKE_NV(":status", "304 Not Modified"),
|
MAKE_NV(":status", "304 Not Modified"), MAKE_NV("age", "0"),
|
||||||
MAKE_NV("age", "0"),
|
|
||||||
MAKE_NV("cache-control", "max-age=56682022"),
|
MAKE_NV("cache-control", "max-age=56682022"),
|
||||||
MAKE_NV("content-type", "text/css"),
|
MAKE_NV("content-type", "text/css"),
|
||||||
MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"),
|
MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"),
|
||||||
|
@ -1062,8 +1055,7 @@ void test_nghttp2_hd_deflate_inflate(void) {
|
||||||
MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"),
|
MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"),
|
||||||
};
|
};
|
||||||
nghttp2_nv nv5[] = {
|
nghttp2_nv nv5[] = {
|
||||||
MAKE_NV(":status", "304 Not Modified"),
|
MAKE_NV(":status", "304 Not Modified"), MAKE_NV("age", "0"),
|
||||||
MAKE_NV("age", "0"),
|
|
||||||
MAKE_NV("cache-control", "max-age=4461139"),
|
MAKE_NV("cache-control", "max-age=4461139"),
|
||||||
MAKE_NV("content-type", "application/x-javascript"),
|
MAKE_NV("content-type", "application/x-javascript"),
|
||||||
MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"),
|
MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"),
|
||||||
|
@ -1075,8 +1067,7 @@ void test_nghttp2_hd_deflate_inflate(void) {
|
||||||
MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"),
|
MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"),
|
||||||
};
|
};
|
||||||
nghttp2_nv nv6[] = {
|
nghttp2_nv nv6[] = {
|
||||||
MAKE_NV(":status", "304 Not Modified"),
|
MAKE_NV(":status", "304 Not Modified"), MAKE_NV("age", "0"),
|
||||||
MAKE_NV("age", "0"),
|
|
||||||
MAKE_NV("cache-control", "max-age=18645951"),
|
MAKE_NV("cache-control", "max-age=18645951"),
|
||||||
MAKE_NV("content-type", "application/x-javascript"),
|
MAKE_NV("content-type", "application/x-javascript"),
|
||||||
MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"),
|
MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"),
|
||||||
|
@ -1088,8 +1079,7 @@ void test_nghttp2_hd_deflate_inflate(void) {
|
||||||
MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"),
|
MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"),
|
||||||
};
|
};
|
||||||
nghttp2_nv nv7[] = {
|
nghttp2_nv nv7[] = {
|
||||||
MAKE_NV(":status", "304 Not Modified"),
|
MAKE_NV(":status", "304 Not Modified"), MAKE_NV("age", "0"),
|
||||||
MAKE_NV("age", "0"),
|
|
||||||
MAKE_NV("cache-control", "max-age=31536000"),
|
MAKE_NV("cache-control", "max-age=31536000"),
|
||||||
MAKE_NV("content-type", "application/javascript"),
|
MAKE_NV("content-type", "application/javascript"),
|
||||||
MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"),
|
MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"),
|
||||||
|
@ -1101,8 +1091,7 @@ void test_nghttp2_hd_deflate_inflate(void) {
|
||||||
MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"),
|
MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"),
|
||||||
};
|
};
|
||||||
nghttp2_nv nv8[] = {
|
nghttp2_nv nv8[] = {
|
||||||
MAKE_NV(":status", "304 Not Modified"),
|
MAKE_NV(":status", "304 Not Modified"), MAKE_NV("age", "0"),
|
||||||
MAKE_NV("age", "0"),
|
|
||||||
MAKE_NV("cache-control", "max-age=31536000"),
|
MAKE_NV("cache-control", "max-age=31536000"),
|
||||||
MAKE_NV("content-type", "application/javascript"),
|
MAKE_NV("content-type", "application/javascript"),
|
||||||
MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"),
|
MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"),
|
||||||
|
@ -1114,8 +1103,7 @@ void test_nghttp2_hd_deflate_inflate(void) {
|
||||||
MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"),
|
MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"),
|
||||||
};
|
};
|
||||||
nghttp2_nv nv9[] = {
|
nghttp2_nv nv9[] = {
|
||||||
MAKE_NV(":status", "304 Not Modified"),
|
MAKE_NV(":status", "304 Not Modified"), MAKE_NV("age", "0"),
|
||||||
MAKE_NV("age", "0"),
|
|
||||||
MAKE_NV("cache-control", "max-age=31536000"),
|
MAKE_NV("cache-control", "max-age=31536000"),
|
||||||
MAKE_NV("content-type", "application/javascript"),
|
MAKE_NV("content-type", "application/javascript"),
|
||||||
MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"),
|
MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"),
|
||||||
|
@ -1127,8 +1115,7 @@ void test_nghttp2_hd_deflate_inflate(void) {
|
||||||
MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"),
|
MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"),
|
||||||
};
|
};
|
||||||
nghttp2_nv nv10[] = {
|
nghttp2_nv nv10[] = {
|
||||||
MAKE_NV(":status", "304 Not Modified"),
|
MAKE_NV(":status", "304 Not Modified"), MAKE_NV("age", "0"),
|
||||||
MAKE_NV("age", "0"),
|
|
||||||
MAKE_NV("cache-control", "max-age=56682045"),
|
MAKE_NV("cache-control", "max-age=56682045"),
|
||||||
MAKE_NV("content-type", "text/css"),
|
MAKE_NV("content-type", "text/css"),
|
||||||
MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"),
|
MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"),
|
||||||
|
|
|
@ -45,9 +45,7 @@ static string_entry *string_entry_new(const char *s) {
|
||||||
return ent;
|
return ent;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void string_entry_del(string_entry *ent) {
|
static void string_entry_del(string_entry *ent) { free(ent); }
|
||||||
free(ent);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int pq_less(const void *lhs, const void *rhs) {
|
static int pq_less(const void *lhs, const void *rhs) {
|
||||||
return strcmp(((string_entry *)lhs)->s, ((string_entry *)rhs)->s) < 0;
|
return strcmp(((string_entry *)lhs)->s, ((string_entry *)rhs)->s) < 0;
|
||||||
|
|
|
@ -3465,7 +3465,7 @@ void test_nghttp2_session_is_my_stream_id(void) {
|
||||||
nghttp2_session_del(session);
|
nghttp2_session_del(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_nghttp2_session_upgrade(void) {
|
void test_nghttp2_session_upgrade2(void) {
|
||||||
nghttp2_session *session;
|
nghttp2_session *session;
|
||||||
nghttp2_session_callbacks callbacks;
|
nghttp2_session_callbacks callbacks;
|
||||||
uint8_t settings_payload[128];
|
uint8_t settings_payload[128];
|
||||||
|
@ -3485,8 +3485,8 @@ void test_nghttp2_session_upgrade(void) {
|
||||||
|
|
||||||
/* Check client side */
|
/* Check client side */
|
||||||
nghttp2_session_client_new(&session, &callbacks, NULL);
|
nghttp2_session_client_new(&session, &callbacks, NULL);
|
||||||
CU_ASSERT(0 == nghttp2_session_upgrade(session, settings_payload,
|
CU_ASSERT(0 == nghttp2_session_upgrade2(session, settings_payload,
|
||||||
settings_payloadlen, &callbacks));
|
settings_payloadlen, 0, &callbacks));
|
||||||
stream = nghttp2_session_get_stream(session, 1);
|
stream = nghttp2_session_get_stream(session, 1);
|
||||||
CU_ASSERT(stream != NULL);
|
CU_ASSERT(stream != NULL);
|
||||||
CU_ASSERT(&callbacks == stream->stream_user_data);
|
CU_ASSERT(&callbacks == stream->stream_user_data);
|
||||||
|
@ -3501,16 +3501,16 @@ void test_nghttp2_session_upgrade(void) {
|
||||||
item->frame.settings.iv[1].settings_id);
|
item->frame.settings.iv[1].settings_id);
|
||||||
CU_ASSERT(4095 == item->frame.settings.iv[1].value);
|
CU_ASSERT(4095 == item->frame.settings.iv[1].value);
|
||||||
|
|
||||||
/* Call nghttp2_session_upgrade() again is error */
|
/* Call nghttp2_session_upgrade2() again is error */
|
||||||
CU_ASSERT(NGHTTP2_ERR_PROTO ==
|
CU_ASSERT(NGHTTP2_ERR_PROTO ==
|
||||||
nghttp2_session_upgrade(session, settings_payload,
|
nghttp2_session_upgrade2(session, settings_payload,
|
||||||
settings_payloadlen, &callbacks));
|
settings_payloadlen, 0, &callbacks));
|
||||||
nghttp2_session_del(session);
|
nghttp2_session_del(session);
|
||||||
|
|
||||||
/* Check server side */
|
/* Check server side */
|
||||||
nghttp2_session_server_new(&session, &callbacks, NULL);
|
nghttp2_session_server_new(&session, &callbacks, NULL);
|
||||||
CU_ASSERT(0 == nghttp2_session_upgrade(session, settings_payload,
|
CU_ASSERT(0 == nghttp2_session_upgrade2(session, settings_payload,
|
||||||
settings_payloadlen, &callbacks));
|
settings_payloadlen, 0, &callbacks));
|
||||||
stream = nghttp2_session_get_stream(session, 1);
|
stream = nghttp2_session_get_stream(session, 1);
|
||||||
CU_ASSERT(stream != NULL);
|
CU_ASSERT(stream != NULL);
|
||||||
CU_ASSERT(NULL == stream->stream_user_data);
|
CU_ASSERT(NULL == stream->stream_user_data);
|
||||||
|
@ -3518,10 +3518,10 @@ void test_nghttp2_session_upgrade(void) {
|
||||||
CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session));
|
CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session));
|
||||||
CU_ASSERT(1 == session->remote_settings.max_concurrent_streams);
|
CU_ASSERT(1 == session->remote_settings.max_concurrent_streams);
|
||||||
CU_ASSERT(4095 == session->remote_settings.initial_window_size);
|
CU_ASSERT(4095 == session->remote_settings.initial_window_size);
|
||||||
/* Call nghttp2_session_upgrade() again is error */
|
/* Call nghttp2_session_upgrade2() again is error */
|
||||||
CU_ASSERT(NGHTTP2_ERR_PROTO ==
|
CU_ASSERT(NGHTTP2_ERR_PROTO ==
|
||||||
nghttp2_session_upgrade(session, settings_payload,
|
nghttp2_session_upgrade2(session, settings_payload,
|
||||||
settings_payloadlen, &callbacks));
|
settings_payloadlen, 0, &callbacks));
|
||||||
nghttp2_session_del(session);
|
nghttp2_session_del(session);
|
||||||
|
|
||||||
/* Empty SETTINGS is OK */
|
/* Empty SETTINGS is OK */
|
||||||
|
@ -3529,8 +3529,8 @@ void test_nghttp2_session_upgrade(void) {
|
||||||
settings_payload, sizeof(settings_payload), NULL, 0);
|
settings_payload, sizeof(settings_payload), NULL, 0);
|
||||||
|
|
||||||
nghttp2_session_client_new(&session, &callbacks, NULL);
|
nghttp2_session_client_new(&session, &callbacks, NULL);
|
||||||
CU_ASSERT(0 == nghttp2_session_upgrade(session, settings_payload,
|
CU_ASSERT(0 == nghttp2_session_upgrade2(session, settings_payload,
|
||||||
settings_payloadlen, NULL));
|
settings_payloadlen, 0, NULL));
|
||||||
nghttp2_session_del(session);
|
nghttp2_session_del(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9441,3 +9441,45 @@ void test_nghttp2_http_push_promise(void) {
|
||||||
nghttp2_session_del(session);
|
nghttp2_session_del(session);
|
||||||
nghttp2_bufs_free(&bufs);
|
nghttp2_bufs_free(&bufs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_nghttp2_http_head_method_upgrade_workaround(void) {
|
||||||
|
nghttp2_session *session;
|
||||||
|
nghttp2_session_callbacks callbacks;
|
||||||
|
const nghttp2_nv cl_resnv[] = {MAKE_NV(":status", "200"),
|
||||||
|
MAKE_NV("content-length", "1000000007")};
|
||||||
|
nghttp2_bufs bufs;
|
||||||
|
nghttp2_hd_deflater deflater;
|
||||||
|
nghttp2_mem *mem;
|
||||||
|
ssize_t rv;
|
||||||
|
nghttp2_stream *stream;
|
||||||
|
|
||||||
|
mem = nghttp2_mem_default();
|
||||||
|
frame_pack_bufs_init(&bufs);
|
||||||
|
|
||||||
|
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
|
||||||
|
callbacks.send_callback = null_send_callback;
|
||||||
|
|
||||||
|
nghttp2_session_client_new(&session, &callbacks, NULL);
|
||||||
|
|
||||||
|
nghttp2_hd_deflate_init(&deflater, mem);
|
||||||
|
|
||||||
|
nghttp2_session_upgrade(session, NULL, 0, NULL);
|
||||||
|
|
||||||
|
rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, cl_resnv,
|
||||||
|
ARRLEN(cl_resnv), mem);
|
||||||
|
|
||||||
|
CU_ASSERT(0 == rv);
|
||||||
|
|
||||||
|
rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
|
||||||
|
nghttp2_buf_len(&bufs.head->buf));
|
||||||
|
|
||||||
|
CU_ASSERT((ssize_t)nghttp2_buf_len(&bufs.head->buf) == rv);
|
||||||
|
|
||||||
|
stream = nghttp2_session_get_stream(session, 1);
|
||||||
|
|
||||||
|
CU_ASSERT(-1 == stream->content_length);
|
||||||
|
|
||||||
|
nghttp2_hd_deflate_free(&deflater);
|
||||||
|
nghttp2_session_del(session);
|
||||||
|
nghttp2_bufs_free(&bufs);
|
||||||
|
}
|
||||||
|
|
|
@ -60,7 +60,7 @@ void test_nghttp2_session_send_headers_push_reply(void);
|
||||||
void test_nghttp2_session_send_rst_stream(void);
|
void test_nghttp2_session_send_rst_stream(void);
|
||||||
void test_nghttp2_session_send_push_promise(void);
|
void test_nghttp2_session_send_push_promise(void);
|
||||||
void test_nghttp2_session_is_my_stream_id(void);
|
void test_nghttp2_session_is_my_stream_id(void);
|
||||||
void test_nghttp2_session_upgrade(void);
|
void test_nghttp2_session_upgrade2(void);
|
||||||
void test_nghttp2_session_reprioritize_stream(void);
|
void test_nghttp2_session_reprioritize_stream(void);
|
||||||
void test_nghttp2_session_reprioritize_stream_with_idle_stream_dep(void);
|
void test_nghttp2_session_reprioritize_stream_with_idle_stream_dep(void);
|
||||||
void test_nghttp2_submit_data(void);
|
void test_nghttp2_submit_data(void);
|
||||||
|
@ -143,5 +143,6 @@ void test_nghttp2_http_ignore_regular_header(void);
|
||||||
void test_nghttp2_http_ignore_content_length(void);
|
void test_nghttp2_http_ignore_content_length(void);
|
||||||
void test_nghttp2_http_record_request_method(void);
|
void test_nghttp2_http_record_request_method(void);
|
||||||
void test_nghttp2_http_push_promise(void);
|
void test_nghttp2_http_push_promise(void);
|
||||||
|
void test_nghttp2_http_head_method_upgrade_workaround(void);
|
||||||
|
|
||||||
#endif /* NGHTTP2_SESSION_TEST_H */
|
#endif /* NGHTTP2_SESSION_TEST_H */
|
||||||
|
|
Loading…
Reference in New Issue