mirror of
https://github.com/nghttp2/nghttp2.git
synced 2025-12-07 02:28:53 +08:00
Compare commits
141 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
92a20c76e6 | ||
|
|
42ccea806c | ||
|
|
805f36d134 | ||
|
|
e2c0a3e43b | ||
|
|
3572e7c634 | ||
|
|
0479f833fc | ||
|
|
252aeb43e1 | ||
|
|
24fe24b37d | ||
|
|
32603d7eff | ||
|
|
53bfc70c9e | ||
|
|
c4068cd404 | ||
|
|
0aa17f64c1 | ||
|
|
38cfc5c47c | ||
|
|
91c8f085ef | ||
|
|
5da49989f8 | ||
|
|
260131966d | ||
|
|
6862f66c23 | ||
|
|
2f2a7ace81 | ||
|
|
132719f752 | ||
|
|
a8625e15f0 | ||
|
|
7f60de0c51 | ||
|
|
3a46a2c0a4 | ||
|
|
fbff101165 | ||
|
|
526d2c727d | ||
|
|
6c232da679 | ||
|
|
1241c951d6 | ||
|
|
73e79130d1 | ||
|
|
1a1902350b | ||
|
|
eec65826cf | ||
|
|
fc17c0a618 | ||
|
|
2620992003 | ||
|
|
5a2069b55c | ||
|
|
14adcb2d81 | ||
|
|
13660edef2 | ||
|
|
98034286ac | ||
|
|
2a37a28d72 | ||
|
|
7bb154f768 | ||
|
|
eb96aa261f | ||
|
|
232d359cbb | ||
|
|
2d5d9d5d04 | ||
|
|
7ecca39025 | ||
|
|
bc0190c19f | ||
|
|
9a162b81f0 | ||
|
|
a67a8fabff | ||
|
|
989d381aab | ||
|
|
1d65d82cb5 | ||
|
|
4be4d875f3 | ||
|
|
1ab707713f | ||
|
|
cc46d363c5 | ||
|
|
fa082cbdd0 | ||
|
|
016d40ea0f | ||
|
|
fe6d065bb4 | ||
|
|
b4e8bea4b5 | ||
|
|
555d5abac9 | ||
|
|
3137dc4a70 | ||
|
|
4bba4bf66c | ||
|
|
7b3a33a313 | ||
|
|
ee52290de7 | ||
|
|
8f0899a190 | ||
|
|
f6cfd082c7 | ||
|
|
9d81be4b35 | ||
|
|
a62778d6b0 | ||
|
|
ea612a2dce | ||
|
|
026521b097 | ||
|
|
9dc5259593 | ||
|
|
ea8a566d98 | ||
|
|
8c6f9e899f | ||
|
|
552f675466 | ||
|
|
f9a50333d2 | ||
|
|
f05a4830c5 | ||
|
|
9e1b068a4b | ||
|
|
54bff91762 | ||
|
|
63630690a8 | ||
|
|
dbc613e0d0 | ||
|
|
ee354ee6c8 | ||
|
|
df707df21b | ||
|
|
2436acbd23 | ||
|
|
b41835f19b | ||
|
|
42b2430fe1 | ||
|
|
c8f67788e0 | ||
|
|
bbdff112a3 | ||
|
|
02468b1ca1 | ||
|
|
c41f413978 | ||
|
|
e38dd37667 | ||
|
|
f2cf2b625c | ||
|
|
eb05777d88 | ||
|
|
6b0b8ea7d5 | ||
|
|
c925c32233 | ||
|
|
514558afc0 | ||
|
|
b9f602b9a2 | ||
|
|
1a99bcc860 | ||
|
|
6b8aa36c98 | ||
|
|
86ddda5c0e | ||
|
|
58b7f4a096 | ||
|
|
4069aab4f7 | ||
|
|
5595ba643e | ||
|
|
77c556901c | ||
|
|
4928959213 | ||
|
|
a200bb1084 | ||
|
|
92a1ca5917 | ||
|
|
97648d257f | ||
|
|
787d40129b | ||
|
|
91ad7e150e | ||
|
|
28bde2cef0 | ||
|
|
a9b54a1bfa | ||
|
|
80f0e99f00 | ||
|
|
102ea7c0bb | ||
|
|
85671a69bf | ||
|
|
a3fa257473 | ||
|
|
c4e994c97d | ||
|
|
0b41e20d54 | ||
|
|
cfabce6e70 | ||
|
|
b948c5457d | ||
|
|
3bdf78e8af | ||
|
|
436595df98 | ||
|
|
d3561a63b1 | ||
|
|
1a12a9b397 | ||
|
|
57644e0256 | ||
|
|
7323d4c639 | ||
|
|
e23225689f | ||
|
|
e6ad2eb14f | ||
|
|
d4a22edeb3 | ||
|
|
8f4e2d941f | ||
|
|
1a8da6caec | ||
|
|
dc335b9025 | ||
|
|
93afbc7d2f | ||
|
|
82e2c5bd22 | ||
|
|
53bcafb39f | ||
|
|
59f8397659 | ||
|
|
061732adf0 | ||
|
|
5c2ca28706 | ||
|
|
a8ea86cfe5 | ||
|
|
7451a73def | ||
|
|
889e705f35 | ||
|
|
14d4979c54 | ||
|
|
095bc178f3 | ||
|
|
308738025c | ||
|
|
97366bf55c | ||
|
|
87cadca3d8 | ||
|
|
9803f92e9c | ||
|
|
cbfa021095 |
46
.travis.yml
46
.travis.yml
@@ -1,31 +1,31 @@
|
||||
language: cpp
|
||||
compiler:
|
||||
- clang
|
||||
#Disable gcc build for the moment...
|
||||
# - gcc
|
||||
- gcc
|
||||
sudo: false
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-4.9
|
||||
- libstdc++-4.9-dev
|
||||
- autoconf
|
||||
- automake
|
||||
- autotools-dev
|
||||
- libtool
|
||||
- pkg-config
|
||||
- zlib1g-dev
|
||||
- libcunit1-dev
|
||||
- libssl-dev
|
||||
- libxml2-dev
|
||||
- libev-dev
|
||||
- libevent-dev
|
||||
- libjansson-dev
|
||||
- libjemalloc-dev
|
||||
before_install:
|
||||
- $CC --version
|
||||
- sudo add-apt-repository --yes ppa:ubuntu-toolchain-r/test
|
||||
- sudo apt-get update -qq
|
||||
#Install and use gcc-4.8 (don't build with gcc-4.6)
|
||||
#libstdc++-4.8 is needed by Clang to build too
|
||||
- sudo apt-get -qq install g++-4.8 libstdc++-4.8-dev
|
||||
- >
|
||||
sudo apt-get install --no-install-recommends -qq
|
||||
autoconf
|
||||
automake
|
||||
autotools-dev
|
||||
libtool
|
||||
pkg-config
|
||||
zlib1g-dev
|
||||
libcunit1-dev
|
||||
libssl-dev
|
||||
libxml2-dev
|
||||
libev-dev
|
||||
libevent-dev
|
||||
libjansson-dev
|
||||
libjemalloc-dev
|
||||
- if [ "$CXX" = "g++" ]; then export CXX="g++-4.8" CC="gcc-4.8"; fi
|
||||
- if [ "$CXX" = "g++" ]; then export CXX="g++-4.9" CC="gcc-4.9"; fi
|
||||
- $CC --version
|
||||
before_script:
|
||||
- autoreconf -i
|
||||
|
||||
10
README.rst
10
README.rst
@@ -13,7 +13,7 @@ An HPACK encoder and decoder are available as a public API.
|
||||
|
||||
An experimental high level C++ library is also available.
|
||||
|
||||
We have Python bindings of this libary, but we do not have full
|
||||
We have Python bindings of this library, but we do not have full
|
||||
code coverage yet.
|
||||
|
||||
Development Status
|
||||
@@ -187,7 +187,7 @@ https://nghttp2.org/documentation/
|
||||
Unit tests
|
||||
----------
|
||||
|
||||
Unit tests are done by simply running `make check`.
|
||||
Unit tests are done by simply running ``make check``.
|
||||
|
||||
Integration tests
|
||||
-----------------
|
||||
@@ -490,7 +490,7 @@ disabled in the frontend and incoming HTTP/1.1 connections can be
|
||||
upgraded to HTTP/2 through HTTP Upgrade.
|
||||
|
||||
The ``--http2-bridge``, ``--client`` and ``--client-proxy`` modes use
|
||||
SSL/TLS in the backend connection by deafult. To disable SSL/TLS, use
|
||||
SSL/TLS in the backend connection by default. To disable SSL/TLS, use
|
||||
the ``--backend-no-tls`` option.
|
||||
|
||||
``nghttpx`` supports a configuration file. See the ``--conf`` option and
|
||||
@@ -785,7 +785,7 @@ max_deflate_size
|
||||
The maximum header table size the encoder uses. This can be smaller
|
||||
than ``max_size``. In this case, the encoder only uses up to first
|
||||
``max_deflate_size`` buffer. Since the header table size is still
|
||||
``max_size``, the encoder has to keep track of entries ouside the
|
||||
``max_size``, the encoder has to keep track of entries outside the
|
||||
``max_deflate_size`` but inside the ``max_size`` and make sure
|
||||
that they are no longer referenced.
|
||||
|
||||
@@ -1273,7 +1273,7 @@ original creator(s) or those who have been assigned copyright by the
|
||||
original author(s).
|
||||
|
||||
By submitting a patch to the nghttp2 project, you (or your employer, as
|
||||
the case may be) agree to assign the copyright of your submission to us.
|
||||
the case may be) agree to assign the copyright of your submission to us.
|
||||
.. the above really needs to be reworded to pass legal muster.
|
||||
We will credit you for your
|
||||
changes as far as possible, to give credit but also to keep a trace
|
||||
|
||||
95
configure.ac
95
configure.ac
@@ -25,13 +25,30 @@ dnl Do not change user variables!
|
||||
dnl http://www.gnu.org/software/automake/manual/html_node/Flag-Variables-Ordering.html
|
||||
|
||||
AC_PREREQ(2.61)
|
||||
AC_INIT([nghttp2], [0.7.11], [t-tujikawa@users.sourceforge.net])
|
||||
AC_INIT([nghttp2], [0.7.15], [t-tujikawa@users.sourceforge.net])
|
||||
AC_USE_SYSTEM_EXTENSIONS
|
||||
|
||||
LT_PREREQ([2.2.6])
|
||||
LT_INIT()
|
||||
|
||||
AC_CANONICAL_BUILD
|
||||
AC_CANONICAL_HOST
|
||||
AC_CANONICAL_TARGET
|
||||
|
||||
AM_INIT_AUTOMAKE([subdir-objects])
|
||||
# comment out for now since this requires automake 1.13 or higher and
|
||||
# travis has older one.
|
||||
# AM_EXTRA_RECURSIVE_TARGETS([it])
|
||||
|
||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
|
||||
dnl See versioning rule:
|
||||
dnl http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
|
||||
AC_SUBST(LT_CURRENT, 13)
|
||||
AC_SUBST(LT_REVISION, 0)
|
||||
AC_SUBST(LT_REVISION, 4)
|
||||
AC_SUBST(LT_AGE, 8)
|
||||
|
||||
major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/[^0-9]//g"`
|
||||
@@ -42,21 +59,6 @@ PACKAGE_VERSION_NUM=`printf "0x%02x%02x%02x" "$major" "$minor" "$patch"`
|
||||
|
||||
AC_SUBST(PACKAGE_VERSION_NUM)
|
||||
|
||||
AC_CANONICAL_BUILD
|
||||
AC_CANONICAL_HOST
|
||||
AC_CANONICAL_TARGET
|
||||
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
|
||||
AM_INIT_AUTOMAKE([subdir-objects])
|
||||
# comment out for now since this requires automake 1.13 or higher and
|
||||
# travis has older one.
|
||||
# AM_EXTRA_RECURSIVE_TARGETS([it])
|
||||
|
||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
|
||||
dnl Checks for command-line options
|
||||
AC_ARG_ENABLE([werror],
|
||||
[AS_HELP_STRING([--enable-werror],
|
||||
@@ -129,10 +131,12 @@ AC_ARG_VAR([CYTHON], [the Cython executable])
|
||||
dnl Checks for programs
|
||||
AC_PROG_CC
|
||||
AC_PROG_CXX
|
||||
AC_PROG_CPP
|
||||
AC_PROG_INSTALL
|
||||
AC_PROG_LN_S
|
||||
AC_PROG_MAKE_SET
|
||||
AM_PROG_CC_C_O
|
||||
AC_PROG_MKDIR_P
|
||||
|
||||
PKG_PROG_PKG_CONFIG([0.20])
|
||||
|
||||
if [test "x$request_python_bindings" != "xno"]; then
|
||||
@@ -358,9 +362,23 @@ if test "x${request_jemalloc}" != "xno"; then
|
||||
AC_SEARCH_LIBS([malloc_stats_print], [jemalloc], [have_jemalloc=yes], [],
|
||||
[$PTHREAD_LDFLAGS])
|
||||
LIBS=$LIBS_OLD
|
||||
|
||||
if test "x${have_jemalloc}" = "xyes"; then
|
||||
jemalloc_libs=${ac_cv_search_malloc_stats_print}
|
||||
else
|
||||
# On Darwin, malloc_stats_print is je_malloc_stats_print
|
||||
AC_SEARCH_LIBS([je_malloc_stats_print], [jemalloc], [have_jemalloc=yes], [],
|
||||
[$PTHREAD_LDFLAGS])
|
||||
LIBS=$LIBS_OLD
|
||||
|
||||
if test "x${have_jemalloc}" = "xyes"; then
|
||||
jemalloc_libs=${ac_cv_search_je_malloc_stats_print}
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "x${have_jemalloc}" = "xyes" &&
|
||||
test "x${ac_cv_search_malloc_stats_print}" != "xnone required"; then
|
||||
JEMALLOC_LIBS=${ac_cv_search_malloc_stats_print}
|
||||
test "x${jemalloc_libs}" != "xnone required"; then
|
||||
JEMALLOC_LIBS=${jemalloc_libs}
|
||||
AC_SUBST([JEMALLOC_LIBS])
|
||||
fi
|
||||
fi
|
||||
@@ -498,12 +516,19 @@ AM_CONDITIONAL([ENABLE_FAILMALLOC], [ test "x${enable_failmalloc}" = "xyes" ])
|
||||
AC_HEADER_ASSERT
|
||||
AC_CHECK_HEADERS([ \
|
||||
arpa/inet.h \
|
||||
fcntl.h \
|
||||
inttypes.h \
|
||||
limits.h \
|
||||
netdb.h \
|
||||
netinet/in.h \
|
||||
pwd.h \
|
||||
stddef.h \
|
||||
stdint.h \
|
||||
stdlib.h \
|
||||
string.h \
|
||||
sys/socket.h \
|
||||
sys/time.h \
|
||||
syslog.h \
|
||||
time.h \
|
||||
unistd.h \
|
||||
])
|
||||
@@ -515,8 +540,16 @@ AC_TYPE_UINT8_T
|
||||
AC_TYPE_UINT16_T
|
||||
AC_TYPE_UINT32_T
|
||||
AC_TYPE_UINT64_T
|
||||
AC_TYPE_INT8_T
|
||||
AC_TYPE_INT16_T
|
||||
AC_TYPE_INT32_T
|
||||
AC_TYPE_INT64_T
|
||||
AC_TYPE_OFF_T
|
||||
AC_TYPE_PID_T
|
||||
AC_TYPE_UID_T
|
||||
AC_CHECK_TYPES([ptrdiff_t])
|
||||
AC_C_BIGENDIAN
|
||||
AC_C_INLINE
|
||||
AC_SYS_LARGEFILE
|
||||
|
||||
AC_CHECK_MEMBER([struct tm.tm_gmtoff], [have_struct_tm_tm_gmtoff=yes],
|
||||
@@ -535,12 +568,34 @@ AC_CHECK_SIZEOF([int *])
|
||||
if test "x$cross_compiling" != "xyes"; then
|
||||
AC_FUNC_MALLOC
|
||||
fi
|
||||
|
||||
AC_FUNC_CHOWN
|
||||
AC_FUNC_ERROR_AT_LINE
|
||||
AC_FUNC_FORK
|
||||
# Don't check realloc, since LeakSanitizer detects memory leak during check
|
||||
# AC_FUNC_REALLOC
|
||||
AC_FUNC_STRERROR_R
|
||||
AC_FUNC_STRNLEN
|
||||
|
||||
AC_CHECK_FUNCS([ \
|
||||
_Exit \
|
||||
accept4 \
|
||||
dup2 \
|
||||
getcwd \
|
||||
getpwnam \
|
||||
localtime_r \
|
||||
memchr \
|
||||
memmove \
|
||||
memset \
|
||||
socket \
|
||||
sqrt \
|
||||
strchr \
|
||||
strdup \
|
||||
strerror \
|
||||
strndup \
|
||||
strstr \
|
||||
strtol \
|
||||
strtoul \
|
||||
timegm \
|
||||
])
|
||||
|
||||
|
||||
2
contrib/.gitignore
vendored
2
contrib/.gitignore
vendored
@@ -1 +1,3 @@
|
||||
nghttpx-init
|
||||
nghttpx.service
|
||||
nghttpx-upstart.conf
|
||||
|
||||
@@ -21,19 +21,24 @@
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
EXTRA_DIST = nghttpx-init.in nghttpx-logrotate
|
||||
configfiles = nghttpx-init nghttpx.service nghttpx-upstart.conf
|
||||
|
||||
EXTRA_DIST = $(configfiles:%=%.in) nghttpx-logrotate
|
||||
|
||||
edit = sed -e 's|@bindir[@]|$(bindir)|g'
|
||||
|
||||
nghttpx-init: Makefile
|
||||
nghttpx-init: %: $(srcdir)/%.in
|
||||
rm -f $@ $@.tmp
|
||||
$(edit) $(srcdir)/$@.in > $@.tmp
|
||||
$(edit) $< > $@.tmp
|
||||
chmod +x $@.tmp
|
||||
mv $@.tmp $@
|
||||
|
||||
nghttpx-init: $(srcdir)/nghttpx-init.in
|
||||
nghttpx.service nghttpx-upstart.conf: %: $(srcdir)/%.in
|
||||
$(edit) $< > $@
|
||||
|
||||
all-local: nghttpx-init
|
||||
$(configfiles): Makefile
|
||||
|
||||
all-local: $(configfiles)
|
||||
|
||||
clean-local:
|
||||
-rm -f nghttpx-init nghttpx-init.tmp
|
||||
-rm -f nghttpx-init.tmp $(configfiles)
|
||||
|
||||
@@ -1,18 +1,11 @@
|
||||
/var/log/nghttpx/*.log {
|
||||
weekly
|
||||
missingok
|
||||
rotate 52
|
||||
compress
|
||||
delaycompress
|
||||
notifempty
|
||||
create 0640 www-data adm
|
||||
sharedscripts
|
||||
prerotate
|
||||
if [ -d /etc/logrotate.d/httpd-prerotate ]; then \
|
||||
run-parts /etc/logrotate.d/httpd-prerotate; \
|
||||
fi \
|
||||
endscript
|
||||
postrotate
|
||||
[ -s /run/nghttpx.pid ] && kill -USR1 `cat /run/nghttpx.pid`
|
||||
endscript
|
||||
weekly
|
||||
rotate 52
|
||||
missingok
|
||||
compress
|
||||
delaycompress
|
||||
notifempty
|
||||
postrotate
|
||||
killall -USR1 nghttpx 2> /dev/null || true
|
||||
endscript
|
||||
}
|
||||
|
||||
8
contrib/nghttpx-upstart.conf.in
Normal file
8
contrib/nghttpx-upstart.conf.in
Normal file
@@ -0,0 +1,8 @@
|
||||
# vim: ft=upstart:
|
||||
|
||||
description "HTTP/2 reverse proxy"
|
||||
|
||||
start on runlevel [2]
|
||||
stop on runlevel [016]
|
||||
|
||||
exec @bindir@/nghttpx
|
||||
10
contrib/nghttpx.service.in
Normal file
10
contrib/nghttpx.service.in
Normal file
@@ -0,0 +1,10 @@
|
||||
[Unit]
|
||||
Description=HTTP/2 experimental proxy
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=@bindir@/nghttpx --errorlog-syslog
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -201,11 +201,28 @@ help:
|
||||
@echo " linkcheck to check all external links for integrity"
|
||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
||||
|
||||
apiref.rst: $(top_builddir)/lib/includes/nghttp2/nghttp2ver.h \
|
||||
apiref.rst macros.rst enums.rst types.rst: \
|
||||
$(top_builddir)/lib/includes/nghttp2/nghttp2ver.h \
|
||||
$(top_builddir)/lib/includes/nghttp2/nghttp2.h
|
||||
$(PYTHON) $(top_srcdir)/doc/mkapiref.py \
|
||||
$@ macros.rst enums.rst types.rst . $^
|
||||
|
||||
# Inspired by
|
||||
# http://www.gnu.org/savannah-checkouts/gnu/automake/manual/html_node/Multiple-Outputs.html
|
||||
apidoc.stamp: $(top_builddir)/lib/includes/nghttp2/nghttp2ver.h \
|
||||
$(top_builddir)/lib/includes/nghttp2/nghttp2.h
|
||||
@rm -f apidoc.tmp
|
||||
@touch apidoc.tmp
|
||||
$(PYTHON) $(top_srcdir)/doc/mkapiref.py \
|
||||
$@ macros.rst enums.rst types.rst . $^
|
||||
@mv -f apidoc.tmp $@
|
||||
$(APIDOC): apidoc.stamp
|
||||
## Recover from the removal of $@
|
||||
@if test -f $@; then :; else \
|
||||
rm -f apidoc.stamp; \
|
||||
$(MAKE) $(AM_MAKEFLAGS) apidoc.stamp; \
|
||||
fi
|
||||
|
||||
clean-local:
|
||||
-rm $(APIDOCS)
|
||||
-rm -rf $(BUILDDIR)/*
|
||||
|
||||
2
doc/_themes/sphinx_rtd_theme/__init__.py
vendored
2
doc/_themes/sphinx_rtd_theme/__init__.py
vendored
@@ -5,7 +5,7 @@ From https://github.com/ryan-roemer/sphinx-bootstrap-theme.
|
||||
"""
|
||||
import os
|
||||
|
||||
VERSION = (0, 1, 7)
|
||||
VERSION = (0, 1, 8)
|
||||
|
||||
__version__ = ".".join(str(v) for v in VERSION)
|
||||
__version_full__ = __version__
|
||||
|
||||
18
doc/_themes/sphinx_rtd_theme/breadcrumbs.html
vendored
18
doc/_themes/sphinx_rtd_theme/breadcrumbs.html
vendored
@@ -6,14 +6,16 @@
|
||||
{% endfor %}
|
||||
<li>{{ title }}</li>
|
||||
<li class="wy-breadcrumbs-aside">
|
||||
{% if display_github %}
|
||||
<a href="https://{{ github_host|default("github.com") }}/{{ github_user }}/{{ github_repo }}/blob/{{ github_version }}{{ conf_py_path }}{{ pagename }}{{ source_suffix }}" class="fa fa-github"> Edit on GitHub</a>
|
||||
{% elif display_bitbucket %}
|
||||
<a href="https://bitbucket.org/{{ bitbucket_user }}/{{ bitbucket_repo }}/src/{{ bitbucket_version}}{{ conf_py_path }}{{ pagename }}{{ source_suffix }}" class="fa fa-bitbucket"> Edit on Bitbucket</a>
|
||||
{% elif show_source and source_url_prefix %}
|
||||
<a href="{{ source_url_prefix }}{{ pagename }}{{ source_suffix }}">View page source</a>
|
||||
{% elif show_source and has_source and sourcename %}
|
||||
<a href="{{ pathto('_sources/' + sourcename, true)|e }}" rel="nofollow"> View page source</a>
|
||||
{% if pagename != "search" %}
|
||||
{% if display_github %}
|
||||
<a href="https://{{ github_host|default("github.com") }}/{{ github_user }}/{{ github_repo }}/blob/{{ github_version }}{{ conf_py_path }}{{ pagename }}{{ source_suffix }}" class="fa fa-github"> Edit on GitHub</a>
|
||||
{% elif display_bitbucket %}
|
||||
<a href="https://bitbucket.org/{{ bitbucket_user }}/{{ bitbucket_repo }}/src/{{ bitbucket_version}}{{ conf_py_path }}{{ pagename }}{{ source_suffix }}" class="fa fa-bitbucket"> Edit on Bitbucket</a>
|
||||
{% elif show_source and source_url_prefix %}
|
||||
<a href="{{ source_url_prefix }}{{ pagename }}{{ source_suffix }}">View page source</a>
|
||||
{% elif show_source and has_source and sourcename %}
|
||||
<a href="{{ pathto('_sources/' + sourcename, true)|e }}" rel="nofollow"> View page source</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
4
doc/_themes/sphinx_rtd_theme/footer.html
vendored
4
doc/_themes/sphinx_rtd_theme/footer.html
vendored
@@ -2,10 +2,10 @@
|
||||
{% if next or prev %}
|
||||
<div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
|
||||
{% if next %}
|
||||
<a href="{{ next.link|e }}" class="btn btn-neutral float-right" title="{{ next.title|striptags|e }}">Next <span class="fa fa-arrow-circle-right"></span></a>
|
||||
<a href="{{ next.link|e }}" class="btn btn-neutral float-right" title="{{ next.title|striptags|e }}" accesskey="n">Next <span class="fa fa-arrow-circle-right"></span></a>
|
||||
{% endif %}
|
||||
{% if prev %}
|
||||
<a href="{{ prev.link|e }}" class="btn btn-neutral" title="{{ prev.title|striptags|e }}"><span class="fa fa-arrow-circle-left"></span> Previous</a>
|
||||
<a href="{{ prev.link|e }}" class="btn btn-neutral" title="{{ prev.title|striptags|e }}" accesskey="p"><span class="fa fa-arrow-circle-left"></span> Previous</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
2
doc/_themes/sphinx_rtd_theme/layout.html
vendored
2
doc/_themes/sphinx_rtd_theme/layout.html
vendored
@@ -107,7 +107,7 @@
|
||||
|
||||
<div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
|
||||
{% block menu %}
|
||||
{% set toctree = toctree(maxdepth=2, collapse=False, includehidden=True) %}
|
||||
{% set toctree = toctree(maxdepth=4, collapse=False, includehidden=True) %}
|
||||
{% if toctree %}
|
||||
{{ toctree }}
|
||||
{% else %}
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
101
doc/_themes/sphinx_rtd_theme/static/js/theme.js
vendored
101
doc/_themes/sphinx_rtd_theme/static/js/theme.js
vendored
@@ -1,50 +1,113 @@
|
||||
$( document ).ready(function() {
|
||||
function toggleCurrent (elem) {
|
||||
var parent_li = elem.closest('li');
|
||||
parent_li.siblings('li.current').removeClass('current');
|
||||
parent_li.siblings().find('li.current').removeClass('current');
|
||||
parent_li.find('> ul li.current').removeClass('current');
|
||||
parent_li.toggleClass('current');
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
// Shift nav in mobile when clicking the menu.
|
||||
$(document).on('click', "[data-toggle='wy-nav-top']", function() {
|
||||
$("[data-toggle='wy-nav-shift']").toggleClass("shift");
|
||||
$("[data-toggle='rst-versions']").toggleClass("shift");
|
||||
$("[data-toggle='wy-nav-shift']").toggleClass("shift");
|
||||
$("[data-toggle='rst-versions']").toggleClass("shift");
|
||||
});
|
||||
// Close menu when you click a link.
|
||||
// Nav menu link click operations
|
||||
$(document).on('click', ".wy-menu-vertical .current ul li a", function() {
|
||||
$("[data-toggle='wy-nav-shift']").removeClass("shift");
|
||||
$("[data-toggle='rst-versions']").toggleClass("shift");
|
||||
var target = $(this);
|
||||
// Close menu when you click a link.
|
||||
$("[data-toggle='wy-nav-shift']").removeClass("shift");
|
||||
$("[data-toggle='rst-versions']").toggleClass("shift");
|
||||
// Handle dynamic display of l3 and l4 nav lists
|
||||
toggleCurrent(target);
|
||||
if (typeof(window.SphinxRtdTheme) != 'undefined') {
|
||||
window.SphinxRtdTheme.StickyNav.hashChange();
|
||||
}
|
||||
});
|
||||
$(document).on('click', "[data-toggle='rst-current-version']", function() {
|
||||
$("[data-toggle='rst-versions']").toggleClass("shift-up");
|
||||
});
|
||||
$("[data-toggle='rst-versions']").toggleClass("shift-up");
|
||||
});
|
||||
// Make tables responsive
|
||||
$("table.docutils:not(.field-list)").wrap("<div class='wy-table-responsive'></div>");
|
||||
|
||||
// Add expand links to all parents of nested ul
|
||||
$('.wy-menu-vertical ul').siblings('a').each(function () {
|
||||
var link = $(this);
|
||||
expand = $('<span class="toctree-expand"></span>');
|
||||
expand.on('click', function (ev) {
|
||||
toggleCurrent(link);
|
||||
ev.stopPropagation();
|
||||
return false;
|
||||
});
|
||||
link.prepend(expand);
|
||||
});
|
||||
});
|
||||
|
||||
// Sphinx theme state
|
||||
window.SphinxRtdTheme = (function (jquery) {
|
||||
var stickyNav = (function () {
|
||||
var navBar,
|
||||
win,
|
||||
stickyNavCssClass = 'stickynav',
|
||||
winScroll = false,
|
||||
linkScroll = false,
|
||||
winPosition = 0,
|
||||
enable = function () {
|
||||
navBar.addClass(stickyNavCssClass);
|
||||
win.on('scroll', function() { // set flag on scroll event
|
||||
winScroll = true;
|
||||
init();
|
||||
reset();
|
||||
win.on('hashchange', reset);
|
||||
|
||||
// Set scrolling
|
||||
win.on('scroll', function () {
|
||||
if (!linkScroll) {
|
||||
winScroll = true;
|
||||
}
|
||||
});
|
||||
// use setInterval to only handle a subset of scroll events so we don't kill scroll performance
|
||||
setInterval(function() {
|
||||
setInterval(function () {
|
||||
if (winScroll) {
|
||||
winScroll = false;
|
||||
navBar.scrollTop(win.scrollTop());
|
||||
var newWinPosition = win.scrollTop(),
|
||||
navPosition = navBar.scrollTop(),
|
||||
newNavPosition = navPosition + (newWinPosition - winPosition);
|
||||
navBar.scrollTop(newNavPosition);
|
||||
winPosition = newWinPosition;
|
||||
}
|
||||
}, 100);
|
||||
}, 25);
|
||||
},
|
||||
init = function () {
|
||||
navBar = jquery('nav.wy-nav-side:first');
|
||||
win = jquery(window);
|
||||
win = jquery(window);
|
||||
},
|
||||
reset = function () {
|
||||
// Get anchor from URL and open up nested nav
|
||||
var anchor = encodeURI(window.location.hash);
|
||||
if (anchor) {
|
||||
try {
|
||||
var link = $('.wy-menu-vertical')
|
||||
.find('[href="' + anchor + '"]');
|
||||
$('.wy-menu-vertical li.toctree-l1 li.current')
|
||||
.removeClass('current');
|
||||
link.closest('li.toctree-l2').addClass('current');
|
||||
link.closest('li.toctree-l3').addClass('current');
|
||||
link.closest('li.toctree-l4').addClass('current');
|
||||
}
|
||||
catch (err) {
|
||||
console.log("Error expanding nav for anchor", err);
|
||||
}
|
||||
}
|
||||
},
|
||||
hashChange = function () {
|
||||
linkScroll = true;
|
||||
win.one('hashchange', function () {
|
||||
linkScroll = false;
|
||||
});
|
||||
};
|
||||
jquery(init);
|
||||
return {
|
||||
enable : enable
|
||||
enable: enable,
|
||||
hashChange: hashChange
|
||||
};
|
||||
}());
|
||||
return {
|
||||
StickyNav : stickyNav
|
||||
StickyNav: stickyNav
|
||||
};
|
||||
}($));
|
||||
|
||||
@@ -8,7 +8,7 @@ _nghttp()
|
||||
_get_comp_words_by_ref cur prev
|
||||
case $cur in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W '--verbose --no-dep --get-assets --har --header-table-size --multiply --padding --hexdump --dep-idle --continuation --connection-window-bits --peer-max-concurrent-streams --timeout --data --no-content-length --version --color --cert --upgrade --remote-name --trailer --weight --help --key --null-out --window-bits --stat --header ' -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W '--no-push --verbose --no-dep --get-assets --har --header-table-size --multiply --padding --hexdump --continuation --connection-window-bits --peer-max-concurrent-streams --timeout --data --no-content-length --version --color --cert --upgrade --remote-name --trailer --weight --help --key --null-out --window-bits --stat --header ' -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
_filedir
|
||||
|
||||
@@ -8,7 +8,7 @@ _nghttpd()
|
||||
_get_comp_words_by_ref cur prev
|
||||
case $cur in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W '--error-gzip --push --header-table-size --trailer --htdocs --address --padding --verbose --version --help --hexdump --daemon --verify-client --workers --no-tls --color --early-response --dh-param-file ' -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W '--error-gzip --push --header-table-size --trailer --htdocs --address --padding --verbose --version --help --hexdump --dh-param-file --daemon --verify-client --echo-upload --workers --no-tls --color --early-response --max-concurrent-streams ' -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
_filedir
|
||||
|
||||
@@ -8,7 +8,7 @@ _nghttpx()
|
||||
_get_comp_words_by_ref cur prev
|
||||
case $cur in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W '--frontend-http2-connection-window-bits --worker-read-rate --frontend-no-tls --frontend-http2-dump-request-header --daemon --write-rate --altsvc --frontend-http2-dump-response-header --backend-http1-connections-per-frontend --tls-ticket-key-file --ciphers --verify-client-cacert --backend-keep-alive-timeout --strip-incoming-x-forwarded-for --errorlog-file --private-key-passwd-file --version --backlog --backend-http-proxy-uri --add-response-header --backend-write-timeout --backend-request-buffer --add-x-forwarded-for --write-burst --backend-http2-connection-window-bits --insecure --rlimit-nofile --backend-http2-window-bits --tls-proto-list --no-location-rewrite --padding --conf --accesslog-syslog --backend-http2-connections-per-worker --http2-max-concurrent-streams --client-proxy --worker-frontend-connections --ocsp-update-interval --cacert --frontend-read-timeout --worker-write-burst --npn-list --syslog-facility --backend-http1-connections-per-host --no-server-push --client --http2-bridge --fetch-ocsp-response-file --no-via --user --stream-write-timeout --no-ocsp --backend-response-buffer --http2-no-cookie-crumbling --backend-read-timeout --stream-read-timeout --workers --worker-read-burst --dh-param-file --errorlog-syslog --frontend --accesslog-file --http2-proxy --frontend-http2-read-timeout --accesslog-format --frontend-http2-window-bits --backend-no-tls --client-private-key-file --pid-file --client-cert-file --no-host-rewrite --log-level --worker-write-rate --help --backend-tls-sni-field --subcert --frontend-frame-debug --frontend-write-timeout --verify-client --read-rate --read-burst --backend-ipv4 --listener-disable-timeout --backend-ipv6 --backend ' -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W '--worker-read-rate --frontend-no-tls --frontend-http2-dump-response-header --backend-http1-connections-per-frontend --tls-ticket-key-file --verify-client-cacert --backend-request-buffer --backend-http2-connection-window-bits --conf --worker-write-burst --npn-list --fetch-ocsp-response-file --stream-read-timeout --accesslog-syslog --frontend-http2-read-timeout --listener-disable-timeout --frontend-http2-connection-window-bits --ciphers --strip-incoming-x-forwarded-for --daemon --backend-keep-alive-timeout --backend-http-proxy-uri --backend-http1-connections-per-host --rlimit-nofile --no-via --ocsp-update-interval --backend-write-timeout --client --http2-no-cookie-crumbling --worker-read-burst --client-proxy --http2-bridge --accesslog-format --errorlog-syslog --errorlog-file --http2-max-concurrent-streams --frontend-write-timeout --read-burst --backend-ipv4 --backend-ipv6 --backend --insecure --log-level --tls-proto-list --backend-http2-connections-per-worker --dh-param-file --worker-frontend-connections --header-field-buffer --no-server-push --no-location-rewrite --no-ocsp --backend-response-buffer --workers --frontend-http2-window-bits --no-host-rewrite --worker-write-rate --backend-tls-sni-field --subcert --help --frontend-frame-debug --pid-file --frontend-http2-dump-request-header --private-key-passwd-file --write-rate --altsvc --user --add-x-forwarded-for --syslog-facility --frontend-read-timeout --backlog --write-burst --backend-http2-window-bits --padding --stream-write-timeout --cacert --version --verify-client --backend-read-timeout --frontend --accesslog-file --http2-proxy --max-header-fields --backend-no-tls --client-private-key-file --client-cert-file --add-response-header --read-rate ' -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
_filedir
|
||||
|
||||
60
doc/h2load.1
60
doc/h2load.1
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "H2LOAD" "1" "April 10, 2015" "0.7.11" "nghttp2"
|
||||
.TH "H2LOAD" "1" "May 15, 2015" "0.7.15" "nghttp2"
|
||||
.SH NAME
|
||||
h2load \- HTTP/2 benchmarking tool
|
||||
.
|
||||
@@ -71,7 +71,7 @@ Default: \fB1\fP
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-i, \-\-input\-file=<FILE>
|
||||
Path of a file with multiple URIs are seperated by EOLs.
|
||||
Path of a file with multiple URIs are separated by EOLs.
|
||||
This option will disable URIs getting from command\-line.
|
||||
If \(aq\-\(aq is given as <FILE>, URIs will be read from stdin.
|
||||
URIs are used in this order for each client. All URIs
|
||||
@@ -93,6 +93,8 @@ Default: \fBauto\fP
|
||||
.B \-w, \-\-window\-bits=<N>
|
||||
Sets the stream level initial window size to (2**<N>)\-1.
|
||||
For SPDY, 2**<N> is used instead.
|
||||
.sp
|
||||
Default: \fB30\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -101,6 +103,8 @@ Sets the connection level initial window size to
|
||||
(2**<N>)\-1. For SPDY, if <N> is strictly less than 16,
|
||||
this option is ignored. Otherwise 2**<N> is used for
|
||||
SPDY.
|
||||
.sp
|
||||
Default: \fB30\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -201,13 +205,63 @@ The maximum time taken for request and response.
|
||||
The mean time taken for request and response.
|
||||
.TP
|
||||
.B sd
|
||||
The standard deviation of the time for request and response.
|
||||
The standard deviation of the time taken for request and response.
|
||||
.TP
|
||||
.B +/\- sd
|
||||
The fraction of the number of requests within standard deviation
|
||||
range (mean +/\- sd) against total number of successful requests.
|
||||
.UNINDENT
|
||||
.TP
|
||||
.B time for connect
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B min
|
||||
The minimum time taken to connect to a server.
|
||||
.TP
|
||||
.B max
|
||||
The maximum time taken to connect to a server.
|
||||
.TP
|
||||
.B mean
|
||||
The mean time taken to connect to a server.
|
||||
.TP
|
||||
.B sd
|
||||
The standard deviation of the time taken to connect to a server.
|
||||
.TP
|
||||
.B +/\- sd
|
||||
The fraction of the number of connections within standard
|
||||
deviation range (mean +/\- sd) against total number of successful
|
||||
connections.
|
||||
.UNINDENT
|
||||
.TP
|
||||
.B time for 1st byte (of (decrypted in case of TLS) application data)
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B min
|
||||
The minimum time taken to get 1st byte from a server.
|
||||
.TP
|
||||
.B max
|
||||
The maximum time taken to get 1st byte from a server.
|
||||
.TP
|
||||
.B mean
|
||||
The mean time taken to get 1st byte from a server.
|
||||
.TP
|
||||
.B sd
|
||||
The standard deviation of the time taken to get 1st byte from a
|
||||
server.
|
||||
.TP
|
||||
.B +/\- sd
|
||||
The fraction of the number of connections within standard
|
||||
deviation range (mean +/\- sd) against total number of successful
|
||||
connections.
|
||||
.UNINDENT
|
||||
.UNINDENT
|
||||
.SH FLOW CONTROL
|
||||
.sp
|
||||
h2load sets large flow control window by default, and effectively
|
||||
disables flow control to avoid under utilization of server
|
||||
performance. To set smaller flow control window, use \fI\%\-w\fP and
|
||||
\fI\%\-W\fP options. For example, use \fB\-w16 \-W16\fP to set default
|
||||
window size described in HTTP/2 and SPDY protocol specification.
|
||||
.SH SEE ALSO
|
||||
.sp
|
||||
\fInghttp(1)\fP, \fInghttpd(1)\fP, \fInghttpx(1)\fP
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
|
||||
.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY.
|
||||
|
||||
.. program:: h2load
|
||||
|
||||
h2load(1)
|
||||
=========
|
||||
|
||||
@@ -44,7 +48,7 @@ OPTIONS
|
||||
|
||||
.. option:: -i, --input-file=<FILE>
|
||||
|
||||
Path of a file with multiple URIs are seperated by EOLs.
|
||||
Path of a file with multiple URIs are separated by EOLs.
|
||||
This option will disable URIs getting from command-line.
|
||||
If '-' is given as <FILE>, URIs will be read from stdin.
|
||||
URIs are used in this order for each client. All URIs
|
||||
@@ -65,6 +69,8 @@ OPTIONS
|
||||
Sets the stream level initial window size to (2\*\*<N>)-1.
|
||||
For SPDY, 2**<N> is used instead.
|
||||
|
||||
Default: ``30``
|
||||
|
||||
.. option:: -W, --connection-window-bits=<N>
|
||||
|
||||
Sets the connection level initial window size to
|
||||
@@ -72,6 +78,8 @@ OPTIONS
|
||||
this option is ignored. Otherwise 2\*\*<N> is used for
|
||||
SPDY.
|
||||
|
||||
Default: ``30``
|
||||
|
||||
.. option:: -H, --header=<HEADER>
|
||||
|
||||
Add/Override a header to the requests.
|
||||
@@ -147,11 +155,49 @@ time for request
|
||||
mean
|
||||
The mean time taken for request and response.
|
||||
sd
|
||||
The standard deviation of the time for request and response.
|
||||
The standard deviation of the time taken for request and response.
|
||||
+/- sd
|
||||
The fraction of the number of requests within standard deviation
|
||||
range (mean +/- sd) against total number of successful requests.
|
||||
|
||||
time for connect
|
||||
min
|
||||
The minimum time taken to connect to a server.
|
||||
max
|
||||
The maximum time taken to connect to a server.
|
||||
mean
|
||||
The mean time taken to connect to a server.
|
||||
sd
|
||||
The standard deviation of the time taken to connect to a server.
|
||||
+/- sd
|
||||
The fraction of the number of connections within standard
|
||||
deviation range (mean +/- sd) against total number of successful
|
||||
connections.
|
||||
|
||||
time for 1st byte (of (decrypted in case of TLS) application data)
|
||||
min
|
||||
The minimum time taken to get 1st byte from a server.
|
||||
max
|
||||
The maximum time taken to get 1st byte from a server.
|
||||
mean
|
||||
The mean time taken to get 1st byte from a server.
|
||||
sd
|
||||
The standard deviation of the time taken to get 1st byte from a
|
||||
server.
|
||||
+/- sd
|
||||
The fraction of the number of connections within standard
|
||||
deviation range (mean +/- sd) against total number of successful
|
||||
connections.
|
||||
|
||||
FLOW CONTROL
|
||||
------------
|
||||
|
||||
h2load sets large flow control window by default, and effectively
|
||||
disables flow control to avoid under utilization of server
|
||||
performance. To set smaller flow control window, use :option:`-w` and
|
||||
:option:`-W` options. For example, use ``-w16 -W16`` to set default
|
||||
window size described in HTTP/2 and SPDY protocol specification.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
|
||||
|
||||
@@ -44,11 +44,49 @@ time for request
|
||||
mean
|
||||
The mean time taken for request and response.
|
||||
sd
|
||||
The standard deviation of the time for request and response.
|
||||
The standard deviation of the time taken for request and response.
|
||||
+/- sd
|
||||
The fraction of the number of requests within standard deviation
|
||||
range (mean +/- sd) against total number of successful requests.
|
||||
|
||||
time for connect
|
||||
min
|
||||
The minimum time taken to connect to a server.
|
||||
max
|
||||
The maximum time taken to connect to a server.
|
||||
mean
|
||||
The mean time taken to connect to a server.
|
||||
sd
|
||||
The standard deviation of the time taken to connect to a server.
|
||||
+/- sd
|
||||
The fraction of the number of connections within standard
|
||||
deviation range (mean +/- sd) against total number of successful
|
||||
connections.
|
||||
|
||||
time for 1st byte (of (decrypted in case of TLS) application data)
|
||||
min
|
||||
The minimum time taken to get 1st byte from a server.
|
||||
max
|
||||
The maximum time taken to get 1st byte from a server.
|
||||
mean
|
||||
The mean time taken to get 1st byte from a server.
|
||||
sd
|
||||
The standard deviation of the time taken to get 1st byte from a
|
||||
server.
|
||||
+/- sd
|
||||
The fraction of the number of connections within standard
|
||||
deviation range (mean +/- sd) against total number of successful
|
||||
connections.
|
||||
|
||||
FLOW CONTROL
|
||||
------------
|
||||
|
||||
h2load sets large flow control window by default, and effectively
|
||||
disables flow control to avoid under utilization of server
|
||||
performance. To set smaller flow control window, use :option:`-w` and
|
||||
:option:`-W` options. For example, use ``-w16 -W16`` to set default
|
||||
window size described in HTTP/2 and SPDY protocol specification.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
|
||||
|
||||
@@ -222,6 +222,7 @@ def process_function(domain, infile):
|
||||
func_proto = ''.join(func_proto)
|
||||
func_proto = re.sub(r';\n$', '', func_proto)
|
||||
func_proto = re.sub(r'\s+', ' ', func_proto)
|
||||
func_proto = re.sub(r'NGHTTP2_EXTERN ', '', func_proto)
|
||||
return FunctionDoc(func_proto, content, domain)
|
||||
|
||||
def read_content(infile):
|
||||
|
||||
85
doc/nghttp.1
85
doc/nghttp.1
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTP" "1" "April 10, 2015" "0.7.11" "nghttp2"
|
||||
.TH "NGHTTP" "1" "May 15, 2015" "0.7.15" "nghttp2"
|
||||
.SH NAME
|
||||
nghttp \- HTTP/2 experimental client
|
||||
.
|
||||
@@ -64,8 +64,9 @@ yet.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-t, \-\-timeout=<SEC>
|
||||
Timeout each request after <SEC> seconds.
|
||||
.B \-t, \-\-timeout=<DURATION>
|
||||
Timeout each request after <DURATION>. Set 0 to disable
|
||||
timeout.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -104,8 +105,8 @@ Add a header to the requests. Example: \fI\%\-H\fP\(aq:method: PUT\(aq
|
||||
.B \-\-trailer=<HEADER>
|
||||
Add a trailer header to the requests. <HEADER> must not
|
||||
include pseudo header field (header field name starting
|
||||
with \(aq:\(aq). To send trailer, one must use \fI\-d\fP option to
|
||||
send request body. Example: \fI\-\-trailer\fP \(aqfoo: bar\(aq.
|
||||
with \(aq:\(aq). To send trailer, one must use \fI\%\-d\fP option to
|
||||
send request body. Example: \fI\%\-\-trailer\fP \(aqfoo: bar\(aq.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -135,7 +136,7 @@ requested twice. This option disables it too.
|
||||
.TP
|
||||
.B \-u, \-\-upgrade
|
||||
Perform HTTP Upgrade for HTTP/2. This option is ignored
|
||||
if the request URI has https scheme. If \fI\-d\fP is used, the
|
||||
if the request URI has https scheme. If \fI\%\-d\fP is used, the
|
||||
HTTP upgrade request is performed with OPTIONS method.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
@@ -192,11 +193,6 @@ Don\(aqt send dependency based priority hint to server.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-dep\-idle
|
||||
Use idle streams as anchor nodes to express priority.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-hexdump
|
||||
Display the incoming traffic in hexadecimal (Canonical
|
||||
hex+ASCII display). If SSL/TLS is used, decrypted data
|
||||
@@ -204,6 +200,11 @@ are used.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-no\-push
|
||||
Disable server push.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-version
|
||||
Display version information and exit.
|
||||
.UNINDENT
|
||||
@@ -215,6 +216,68 @@ Display this help and exit.
|
||||
.sp
|
||||
The <SIZE> argument is an integer and an optional unit (e.g., 10K is
|
||||
10 * 1024). Units are K, M and G (powers of 1024).
|
||||
.sp
|
||||
The <DURATION> argument is an integer and an optional unit (e.g., 1s
|
||||
is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms
|
||||
(hours, minutes, seconds and milliseconds, respectively). If a unit
|
||||
is omitted, a second is used as unit.
|
||||
.SH DEPENDENCY BASED PRIORITY
|
||||
.sp
|
||||
nghttp sends priority hints to server by default unless
|
||||
\fI\%\-\-no\-dep\fP is used. nghttp mimics the way Firefox employs to
|
||||
manages dependency using idle streams. We follows the behaviour of
|
||||
Firefox Nightly as of April, 2015, and nghttp\(aqs behaviour is very
|
||||
static and could be different from Firefox in detail. But reproducing
|
||||
the same behaviour of Firefox is not our goal. The goal is provide
|
||||
the easy way to test out the dependency priority in server
|
||||
implementation.
|
||||
.sp
|
||||
When connection is established, nghttp sends 5 PRIORITY frames to idle
|
||||
streams 3, 5, 7, 9 and 11 to create "anchor" nodes in dependency
|
||||
tree:
|
||||
.INDENT 0.0
|
||||
.INDENT 3.5
|
||||
.sp
|
||||
.nf
|
||||
.ft C
|
||||
+\-\-\-\-\-+
|
||||
|id=0 |
|
||||
+\-\-\-\-\-+
|
||||
^ ^ ^
|
||||
w=201 / | \e w=1
|
||||
/ | \e
|
||||
/ w=101| \e
|
||||
+\-\-\-\-\-+ +\-\-\-\-\-+ +\-\-\-\-\-+
|
||||
|id=3 | |id=5 | |id=7 |
|
||||
+\-\-\-\-\-+ +\-\-\-\-\-+ +\-\-\-\-\-+
|
||||
^ ^
|
||||
w=1 | w=1 |
|
||||
| |
|
||||
+\-\-\-\-\-+ +\-\-\-\-\-+
|
||||
|id=11| |id=9 |
|
||||
+\-\-\-\-\-+ +\-\-\-\-\-+
|
||||
.ft P
|
||||
.fi
|
||||
.UNINDENT
|
||||
.UNINDENT
|
||||
.sp
|
||||
In the above figure, \fBid\fP means stream ID, and \fBw\fP means weight.
|
||||
The stream 0 is non\-existence stream, and forms the root of the tree.
|
||||
The stream 7 and 9 are not used for now.
|
||||
.sp
|
||||
The URIs given in the command\-line depend on stream 11 with the weight
|
||||
given in \fI\%\-p\fP option, which defaults to 16.
|
||||
.sp
|
||||
If \fI\%\-a\fP option is used, nghttp parses the resource pointed by
|
||||
URI given in command\-line as html, and extracts resource links from
|
||||
it. When requesting those resources, nghttp uses dependency according
|
||||
to its resource type.
|
||||
.sp
|
||||
For CSS, and Javascript files inside "head" element, they depend on
|
||||
stream 3 with the weight 2. The Javascript files outside "head"
|
||||
element depend on stream 5 with the weight 2. The mages depend on
|
||||
stream 11 with the weight 12. The other resources (e.g., icon) depend
|
||||
on stream 11 with the weight 2.
|
||||
.SH SEE ALSO
|
||||
.sp
|
||||
\fInghttpd(1)\fP, \fInghttpx(1)\fP, \fIh2load(1)\fP
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
|
||||
.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY.
|
||||
|
||||
.. program:: nghttp
|
||||
|
||||
nghttp(1)
|
||||
=========
|
||||
|
||||
@@ -36,9 +40,10 @@ OPTIONS
|
||||
'index.html' is used as a filename. Not implemented
|
||||
yet.
|
||||
|
||||
.. option:: -t, --timeout=<SEC>
|
||||
.. option:: -t, --timeout=<DURATION>
|
||||
|
||||
Timeout each request after <SEC> seconds.
|
||||
Timeout each request after <DURATION>. Set 0 to disable
|
||||
timeout.
|
||||
|
||||
.. option:: -w, --window-bits=<N>
|
||||
|
||||
@@ -143,16 +148,16 @@ OPTIONS
|
||||
|
||||
Don't send dependency based priority hint to server.
|
||||
|
||||
.. option:: --dep-idle
|
||||
|
||||
Use idle streams as anchor nodes to express priority.
|
||||
|
||||
.. option:: --hexdump
|
||||
|
||||
Display the incoming traffic in hexadecimal (Canonical
|
||||
hex+ASCII display). If SSL/TLS is used, decrypted data
|
||||
are used.
|
||||
|
||||
.. option:: --no-push
|
||||
|
||||
Disable server push.
|
||||
|
||||
.. option:: --version
|
||||
|
||||
Display version information and exit.
|
||||
@@ -166,6 +171,62 @@ OPTIONS
|
||||
The <SIZE> argument is an integer and an optional unit (e.g., 10K is
|
||||
10 * 1024). Units are K, M and G (powers of 1024).
|
||||
|
||||
The <DURATION> argument is an integer and an optional unit (e.g., 1s
|
||||
is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms
|
||||
(hours, minutes, seconds and milliseconds, respectively). If a unit
|
||||
is omitted, a second is used as unit.
|
||||
|
||||
DEPENDENCY BASED PRIORITY
|
||||
-------------------------
|
||||
|
||||
nghttp sends priority hints to server by default unless
|
||||
:option:`--no-dep` is used. nghttp mimics the way Firefox employs to
|
||||
manages dependency using idle streams. We follows the behaviour of
|
||||
Firefox Nightly as of April, 2015, and nghttp's behaviour is very
|
||||
static and could be different from Firefox in detail. But reproducing
|
||||
the same behaviour of Firefox is not our goal. The goal is provide
|
||||
the easy way to test out the dependency priority in server
|
||||
implementation.
|
||||
|
||||
When connection is established, nghttp sends 5 PRIORITY frames to idle
|
||||
streams 3, 5, 7, 9 and 11 to create "anchor" nodes in dependency
|
||||
tree::
|
||||
|
||||
+-----+
|
||||
|id=0 |
|
||||
+-----+
|
||||
^ ^ ^
|
||||
w=201 / | \ w=1
|
||||
/ | \
|
||||
/ w=101| \
|
||||
+-----+ +-----+ +-----+
|
||||
|id=3 | |id=5 | |id=7 |
|
||||
+-----+ +-----+ +-----+
|
||||
^ ^
|
||||
w=1 | w=1 |
|
||||
| |
|
||||
+-----+ +-----+
|
||||
|id=11| |id=9 |
|
||||
+-----+ +-----+
|
||||
|
||||
In the above figure, ``id`` means stream ID, and ``w`` means weight.
|
||||
The stream 0 is non-existence stream, and forms the root of the tree.
|
||||
The stream 7 and 9 are not used for now.
|
||||
|
||||
The URIs given in the command-line depend on stream 11 with the weight
|
||||
given in :option:`-p` option, which defaults to 16.
|
||||
|
||||
If :option:`-a` option is used, nghttp parses the resource pointed by
|
||||
URI given in command-line as html, and extracts resource links from
|
||||
it. When requesting those resources, nghttp uses dependency according
|
||||
to its resource type.
|
||||
|
||||
For CSS, and Javascript files inside "head" element, they depend on
|
||||
stream 3 with the weight 2. The Javascript files outside "head"
|
||||
element depend on stream 5 with the weight 2. The mages depend on
|
||||
stream 11 with the weight 12. The other resources (e.g., icon) depend
|
||||
on stream 11 with the weight 2.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
|
||||
|
||||
@@ -1,3 +1,54 @@
|
||||
DEPENDENCY BASED PRIORITY
|
||||
-------------------------
|
||||
|
||||
nghttp sends priority hints to server by default unless
|
||||
:option:`--no-dep` is used. nghttp mimics the way Firefox employs to
|
||||
manages dependency using idle streams. We follows the behaviour of
|
||||
Firefox Nightly as of April, 2015, and nghttp's behaviour is very
|
||||
static and could be different from Firefox in detail. But reproducing
|
||||
the same behaviour of Firefox is not our goal. The goal is provide
|
||||
the easy way to test out the dependency priority in server
|
||||
implementation.
|
||||
|
||||
When connection is established, nghttp sends 5 PRIORITY frames to idle
|
||||
streams 3, 5, 7, 9 and 11 to create "anchor" nodes in dependency
|
||||
tree::
|
||||
|
||||
+-----+
|
||||
|id=0 |
|
||||
+-----+
|
||||
^ ^ ^
|
||||
w=201 / | \ w=1
|
||||
/ | \
|
||||
/ w=101| \
|
||||
+-----+ +-----+ +-----+
|
||||
|id=3 | |id=5 | |id=7 |
|
||||
+-----+ +-----+ +-----+
|
||||
^ ^
|
||||
w=1 | w=1 |
|
||||
| |
|
||||
+-----+ +-----+
|
||||
|id=11| |id=9 |
|
||||
+-----+ +-----+
|
||||
|
||||
In the above figure, ``id`` means stream ID, and ``w`` means weight.
|
||||
The stream 0 is non-existence stream, and forms the root of the tree.
|
||||
The stream 7 and 9 are not used for now.
|
||||
|
||||
The URIs given in the command-line depend on stream 11 with the weight
|
||||
given in :option:`-p` option, which defaults to 16.
|
||||
|
||||
If :option:`-a` option is used, nghttp parses the resource pointed by
|
||||
URI given in command-line as html, and extracts resource links from
|
||||
it. When requesting those resources, nghttp uses dependency according
|
||||
to its resource type.
|
||||
|
||||
For CSS, and Javascript files inside "head" element, they depend on
|
||||
stream 3 with the weight 2. The Javascript files outside "head"
|
||||
element depend on stream 5 with the weight 2. The mages depend on
|
||||
stream 11 with the weight 12. The other resources (e.g., icon) depend
|
||||
on stream 11 with the weight 2.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTPD" "1" "April 10, 2015" "0.7.11" "nghttp2"
|
||||
.TH "NGHTTPD" "1" "May 15, 2015" "0.7.15" "nghttp2"
|
||||
.SH NAME
|
||||
nghttpd \- HTTP/2 experimental server
|
||||
.
|
||||
@@ -63,7 +63,7 @@ address determined by getaddrinfo is used.
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-D, \-\-daemon
|
||||
Run in a background. If \fI\-D\fP is used, the current working
|
||||
Run in a background. If \fI\%\-D\fP is used, the current working
|
||||
directory is changed to \(aq\fI/\fP\(aq. Therefore if this option
|
||||
is used, \fI\%\-d\fP option must be specified.
|
||||
.UNINDENT
|
||||
@@ -109,7 +109,7 @@ Push resources <PUSH_PATH>s when <PATH> is requested.
|
||||
This option can be used repeatedly to specify multiple
|
||||
push configurations. <PATH> and <PUSH_PATH>s are
|
||||
relative to document root. See \fI\%\-\-htdocs\fP option.
|
||||
Example: \fI\-p\fP/=/foo.png \fI\-p\fP/doc=/bar.css
|
||||
Example: \fI\%\-p\fP/=/foo.png \fI\%\-p\fP/doc=/bar.css
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -119,6 +119,14 @@ Specify 0 to disable padding.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-m, \-\-max\-concurrent\-streams=<N>
|
||||
Set the maximum number of the concurrent streams in one
|
||||
HTTP/2 session.
|
||||
.sp
|
||||
Default: \fB100\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-n, \-\-workers=<N>
|
||||
Set the number of worker threads.
|
||||
.sp
|
||||
@@ -159,6 +167,11 @@ are used.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-echo\-upload
|
||||
Send back uploaded content if method is POST or PUT.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-version
|
||||
Display version information and exit.
|
||||
.UNINDENT
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
|
||||
.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY.
|
||||
|
||||
.. program:: nghttpd
|
||||
|
||||
nghttpd(1)
|
||||
==========
|
||||
|
||||
@@ -83,6 +87,13 @@ OPTIONS
|
||||
Add at most <N> bytes to a frame payload as padding.
|
||||
Specify 0 to disable padding.
|
||||
|
||||
.. option:: -m, --max-concurrent-streams=<N>
|
||||
|
||||
Set the maximum number of the concurrent streams in one
|
||||
HTTP/2 session.
|
||||
|
||||
Default: ``100``
|
||||
|
||||
.. option:: -n, --workers=<N>
|
||||
|
||||
Set the number of worker threads.
|
||||
@@ -117,6 +128,10 @@ OPTIONS
|
||||
hex+ASCII display). If SSL/TLS is used, decrypted data
|
||||
are used.
|
||||
|
||||
.. option:: --echo-upload
|
||||
|
||||
Send back uploaded content if method is POST or PUT.
|
||||
|
||||
.. option:: --version
|
||||
|
||||
Display version information and exit.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTPX" "1" "April 10, 2015" "0.7.11" "nghttp2"
|
||||
.TH "NGHTTPX" "1" "May 15, 2015" "0.7.15" "nghttp2"
|
||||
.SH NAME
|
||||
nghttpx \- HTTP/2 experimental proxy
|
||||
.
|
||||
@@ -704,6 +704,23 @@ won\(aqt replace anything already set. This option can be
|
||||
used several times to specify multiple header fields.
|
||||
Example: \fI\%\-\-add\-response\-header\fP="foo: bar"
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-header\-field\-buffer=<SIZE>
|
||||
Set maximum buffer size for incoming HTTP header field
|
||||
list. This is the sum of header name and value in
|
||||
bytes.
|
||||
.sp
|
||||
Default: \fB64K\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-max\-header\-fields=<N>
|
||||
Set maximum number of incoming HTTP header fields, which
|
||||
appear in one request or response header field list.
|
||||
.sp
|
||||
Default: \fB100\fP
|
||||
.UNINDENT
|
||||
.SS Debug
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -795,7 +812,7 @@ argument in the configuration file. Specify \fByes\fP as an argument
|
||||
ignored.
|
||||
.sp
|
||||
To specify private key and certificate file which are given as
|
||||
positional arguments in commnad\-line, use \fBprivate\-key\-file\fP and
|
||||
positional arguments in command\-line, use \fBprivate\-key\-file\fP and
|
||||
\fBcertificate\-file\fP\&.
|
||||
.sp
|
||||
\fI\%\-\-conf\fP option cannot be used in the configuration file and
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
|
||||
.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY.
|
||||
|
||||
.. program:: nghttpx
|
||||
|
||||
nghttpx(1)
|
||||
==========
|
||||
|
||||
@@ -617,6 +621,21 @@ HTTP
|
||||
used several times to specify multiple header fields.
|
||||
Example: :option:`--add-response-header`\="foo: bar"
|
||||
|
||||
.. option:: --header-field-buffer=<SIZE>
|
||||
|
||||
Set maximum buffer size for incoming HTTP header field
|
||||
list. This is the sum of header name and value in
|
||||
bytes.
|
||||
|
||||
Default: ``64K``
|
||||
|
||||
.. option:: --max-header-fields=<N>
|
||||
|
||||
Set maximum number of incoming HTTP header fields, which
|
||||
appear in one request or response header field list.
|
||||
|
||||
Default: ``100``
|
||||
|
||||
|
||||
Debug
|
||||
~~~~~
|
||||
@@ -710,7 +729,7 @@ FILES
|
||||
ignored.
|
||||
|
||||
To specify private key and certificate file which are given as
|
||||
positional arguments in commnad-line, use ``private-key-file`` and
|
||||
positional arguments in command-line, use ``private-key-file`` and
|
||||
``certificate-file``.
|
||||
|
||||
:option:`--conf` option cannot be used in the configuration file and
|
||||
|
||||
@@ -19,7 +19,7 @@ FILES
|
||||
ignored.
|
||||
|
||||
To specify private key and certificate file which are given as
|
||||
positional arguments in commnad-line, use ``private-key-file`` and
|
||||
positional arguments in command-line, use ``private-key-file`` and
|
||||
``certificate-file``.
|
||||
|
||||
:option:`--conf` option cannot be used in the configuration file and
|
||||
|
||||
@@ -50,8 +50,9 @@ Flow Control
|
||||
------------
|
||||
|
||||
HTTP/2 and SPDY/3 or later employ flow control and it may affect
|
||||
benchmarking results. To adjust receiver flow control window size,
|
||||
there is following options:
|
||||
benchmarking results. By default, h2load uses large enough flow
|
||||
control window, which effectively disables flow control. To adjust
|
||||
receiver flow control window size, there are following options:
|
||||
|
||||
``-w``
|
||||
Sets the stream level initial window size to
|
||||
@@ -86,5 +87,5 @@ If multiple URIs are specified, they are used in round robin manner.
|
||||
|
||||
.. note::
|
||||
|
||||
Please note that h2load uses sheme, host and port in the first URI
|
||||
Please note that h2load uses scheme, host and port in the first URI
|
||||
and ignores those parts in the rest of the URIs.
|
||||
|
||||
@@ -80,7 +80,7 @@ status code, in the above example, which is 200. The second argument,
|
||||
which is omitted in the above example, is additional header fields to
|
||||
send.
|
||||
|
||||
``nghttp2::asio_http2::server::response::end`` sends responde body.
|
||||
``nghttp2::asio_http2::server::response::end`` sends response body.
|
||||
In the above example, we send string "hello, world".
|
||||
|
||||
The life time of req and res object ends after the callback set by
|
||||
@@ -277,7 +277,7 @@ response header fields and response body to the console screen:
|
||||
``boost::asio::io_service`` object and remote server address. When
|
||||
connection is made, the callback function passed to
|
||||
``nghttp2::asio_http2::client::on_connect`` is invoked with connected
|
||||
address as its paramter. After this callback call, use
|
||||
address as its parameter. After this callback call, use
|
||||
``nghttp2::asio_http2::session::submit`` to send request to the
|
||||
server. You can submit multiple requests at once without waiting for
|
||||
the completion of previous request.
|
||||
|
||||
@@ -21,7 +21,7 @@ SSL/TLS, the frontend also supports SPDY protocol.
|
||||
By default, this mode's frontend connection is encrypted using
|
||||
SSL/TLS. So server's private key and certificate must be supplied to
|
||||
the command line (or through configuration file). In this case, the
|
||||
fontend protocol selection will is done via ALPN or NPN.
|
||||
frontend protocol selection will is done via ALPN or NPN.
|
||||
|
||||
With ``--frontend-no-tls`` option, user can turn off SSL/TLS in
|
||||
frontend connection. In this case, SPDY protocol is not available
|
||||
@@ -243,7 +243,7 @@ Read/write rate limit
|
||||
---------------------
|
||||
|
||||
nghttpx supports transfer rate limiting on frontend connections. You
|
||||
can do rate limit per frontend connection for reading and writeing
|
||||
can do rate limit per frontend connection for reading and writing
|
||||
individually.
|
||||
|
||||
To perform rate limit for reading, use ``--read-rate`` and
|
||||
|
||||
@@ -267,7 +267,7 @@ HTTP/2 servers
|
||||
(byte string could be ``None``), :py:data:`DATA_EOF` must be
|
||||
returned as flag. If there is no data available right now, but
|
||||
additional data are anticipated, return tuple (``None``,
|
||||
:py:data:`DATA_DEFERRD`). When data arrived, call
|
||||
:py:data:`DATA_DEFERRED`). When data arrived, call
|
||||
:py:meth:`resume()` and restart response body transmission.
|
||||
|
||||
Only the body generator can pause response body generation;
|
||||
|
||||
@@ -65,11 +65,11 @@ its stream specific data in ``http2_stream_data`` structure and the
|
||||
defined as follows::
|
||||
|
||||
typedef struct {
|
||||
/* The NULL-terminated URI string to retreive. */
|
||||
/* The NULL-terminated URI string to retrieve. */
|
||||
const char *uri;
|
||||
/* Parsed result of the |uri| */
|
||||
struct http_parser_url *u;
|
||||
/* The authroity portion of the |uri|, not NULL-terminated */
|
||||
/* The authority portion of the |uri|, not NULL-terminated */
|
||||
char *authority;
|
||||
/* The path portion of the |uri|, including query, not
|
||||
NULL-terminated */
|
||||
@@ -204,7 +204,7 @@ transmission of client connection header is done in
|
||||
}
|
||||
|
||||
Here we specify SETTINGS_MAX_CONCURRENT_STREAMS to 100, which is
|
||||
really not needed for this tiny example progoram, but we are
|
||||
really not needed for this tiny example program, but we are
|
||||
demonstrating the use of SETTINGS frame. To queue the SETTINGS frame
|
||||
for the transmission, we use `nghttp2_submit_settings()`. Note that
|
||||
`nghttp2_submit_settings()` function only queues the frame and not
|
||||
@@ -388,7 +388,7 @@ After all name/value pairs are emitted for a frame,
|
||||
}
|
||||
|
||||
In this tutorial, we are just interested in the HTTP response
|
||||
HEADERS. We check te frame type and its category (it should be
|
||||
HEADERS. We check the frame type and its category (it should be
|
||||
:macro:`NGHTTP2_HCAT_RESPONSE` for HTTP response HEADERS). Also check
|
||||
its stream ID.
|
||||
|
||||
@@ -407,7 +407,7 @@ of data is received from the remote peer::
|
||||
}
|
||||
|
||||
In our case, a chunk of data is response body. After checking stream
|
||||
ID, we just write the recieved data to the stdout. Note that the
|
||||
ID, we just write the received data to the stdout. Note that the
|
||||
output in the terminal may be corrupted if the response body contains
|
||||
some binary data.
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ bound of encoded result, use `nghttp2_hd_deflate_bound()` function::
|
||||
size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater,
|
||||
const nghttp2_nv *nva, size_t nvlen);
|
||||
|
||||
Pass this function with the same paramters *deflater*, *nva* and
|
||||
Pass this function with the same parameters *deflater*, *nva* and
|
||||
*nvlen* which will be passed to `nghttp2_hd_deflate_hd()`.
|
||||
|
||||
The subsequent call of `nghttp2_hd_deflate_hd()` will use current
|
||||
|
||||
@@ -35,8 +35,12 @@
|
||||
//
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif // HAVE_UNISTD_H
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif // HAVE_FCNTL_H
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
|
||||
@@ -28,16 +28,26 @@
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /* !HAVE_CONFIG_H */
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif /* HAVE_UNISTD_H */
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif /* HAVE_FCNTL_H */
|
||||
#include <sys/types.h>
|
||||
#ifdef HAVE_SYS_SOCKET_H
|
||||
#include <sys/socket.h>
|
||||
#endif /* HAVE_SYS_SOCKET_H */
|
||||
#ifdef HAVE_NETDB_H
|
||||
#include <netdb.h>
|
||||
#endif /* HAVE_NETDB_H */
|
||||
#ifdef HAVE_NETINET_IN_H
|
||||
#include <netinet/in.h>
|
||||
#endif /* HAVE_NETINET_IN_H */
|
||||
#include <netinet/tcp.h>
|
||||
#include <poll.h>
|
||||
#include <signal.h>
|
||||
|
||||
@@ -24,12 +24,18 @@
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /* !HAVE_CONFIG_H */
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <sys/types.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif /* HAVE_UNISTD_H */
|
||||
#ifdef HAVE_SYS_SOCKET_H
|
||||
#include <sys/socket.h>
|
||||
#endif /* HAVE_SYS_SOCKET_H */
|
||||
#ifdef HAVE_NETINET_IN_H
|
||||
#include <netinet/in.h>
|
||||
#endif /* HAVE_NETINET_IN_H */
|
||||
#include <netinet/tcp.h>
|
||||
#include <err.h>
|
||||
#include <signal.h>
|
||||
@@ -50,11 +56,11 @@
|
||||
#define ARRLEN(x) (sizeof(x) / sizeof(x[0]))
|
||||
|
||||
typedef struct {
|
||||
/* The NULL-terminated URI string to retreive. */
|
||||
/* The NULL-terminated URI string to retrieve. */
|
||||
const char *uri;
|
||||
/* Parsed result of the |uri| */
|
||||
struct http_parser_url *u;
|
||||
/* The authroity portion of the |uri|, not NULL-terminated */
|
||||
/* The authority portion of the |uri|, not NULL-terminated */
|
||||
char *authority;
|
||||
/* The path portion of the |uri|, including query, not
|
||||
NULL-terminated */
|
||||
@@ -258,8 +264,7 @@ static int on_data_chunk_recv_callback(nghttp2_session *session _U_,
|
||||
stream), if it is closed, we send GOAWAY and tear down the
|
||||
session */
|
||||
static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||
uint32_t error_code,
|
||||
void *user_data) {
|
||||
uint32_t error_code, void *user_data) {
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
int rv;
|
||||
|
||||
|
||||
@@ -24,17 +24,27 @@
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /* !HAVE_CONFIG_H */
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <sys/types.h>
|
||||
#ifdef HAVE_SYS_SOCKET_H
|
||||
#include <sys/socket.h>
|
||||
#endif /* HAVE_SYS_SOCKET_H */
|
||||
#ifdef HAVE_NETDB_H
|
||||
#include <netdb.h>
|
||||
#endif /* HAVE_NETDB_H */
|
||||
#include <signal.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif /* HAVE_UNISTD_H */
|
||||
#include <sys/stat.h>
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif /* HAVE_FCNTL_H */
|
||||
#include <ctype.h>
|
||||
#ifdef HAVE_NETINET_IN_H
|
||||
#include <netinet/in.h>
|
||||
#endif /* HAVE_NETINET_IN_H */
|
||||
#include <netinet/tcp.h>
|
||||
#include <err.h>
|
||||
|
||||
|
||||
@@ -30,18 +30,30 @@
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /* !HAVE_CONFIG_H */
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <sys/types.h>
|
||||
#ifdef HAVE_SYS_SOCKET_H
|
||||
#include <sys/socket.h>
|
||||
#endif /* HAVE_SYS_SOCKET_H */
|
||||
#include <sys/stat.h>
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif /* HAVE_FCNTL_H */
|
||||
#ifdef HAVE_NETDB_H
|
||||
#include <netdb.h>
|
||||
#endif /* HAVE_NETDB_H */
|
||||
#ifdef HAVE_NETINET_IN_H
|
||||
#include <netinet/in.h>
|
||||
#endif /* HAVE_NETINET_IN_H */
|
||||
#include <netinet/tcp.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif /* HAVE_UNISTD_H */
|
||||
#include <stdlib.h>
|
||||
#ifdef HAVE_TIME_H
|
||||
#include <time.h>
|
||||
#endif /* HAVE_TIME_H */
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
@@ -190,7 +202,7 @@ typedef enum {
|
||||
NGHTTP2_TOKEN__METHOD,
|
||||
NGHTTP2_TOKEN__PATH,
|
||||
NGHTTP2_TOKEN__SCHEME,
|
||||
NGHTTP2_TOKEN_HOST,
|
||||
NGHTTP2_TOKEN_HOST
|
||||
} nghttp2_token;
|
||||
|
||||
/* Inspired by h2o header lookup. https://github.com/h2o/h2o */
|
||||
@@ -699,7 +711,8 @@ static ssize_t fd_read_callback(nghttp2_session *session _U_,
|
||||
nghttp2_data_source *source,
|
||||
void *user_data _U_) {
|
||||
stream *strm = source->ptr;
|
||||
ssize_t nread = (int64_t)length < strm->fileleft ? length : strm->fileleft;
|
||||
ssize_t nread =
|
||||
(int64_t)length < strm->fileleft ? (int64_t)length : strm->fileleft;
|
||||
|
||||
*data_flags |= NGHTTP2_DATA_FLAG_NO_COPY;
|
||||
|
||||
|
||||
@@ -1,19 +1,72 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
HEADERS = [
|
||||
':authority',
|
||||
':method',
|
||||
':path',
|
||||
':scheme',
|
||||
':status',
|
||||
"content-length",
|
||||
"host",
|
||||
"te",
|
||||
'connection',
|
||||
'keep-alive',
|
||||
'proxy-connection',
|
||||
'transfer-encoding',
|
||||
'upgrade'
|
||||
(':authority', 0),
|
||||
(':method', 1),
|
||||
(':method', 2),
|
||||
(':path', 3),
|
||||
(':path', 4),
|
||||
(':scheme', 5),
|
||||
(':scheme', 6),
|
||||
(':status', 7),
|
||||
(':status', 8),
|
||||
(':status', 9),
|
||||
(':status', 10),
|
||||
(':status', 11),
|
||||
(':status', 12),
|
||||
(':status', 13),
|
||||
('accept-charset', 14),
|
||||
('accept-encoding', 15),
|
||||
('accept-language', 16),
|
||||
('accept-ranges', 17),
|
||||
('accept', 18),
|
||||
('access-control-allow-origin', 19),
|
||||
('age', 20),
|
||||
('allow', 21),
|
||||
('authorization', 22),
|
||||
('cache-control', 23),
|
||||
('content-disposition', 24),
|
||||
('content-encoding', 25),
|
||||
('content-language', 26),
|
||||
('content-length', 27),
|
||||
('content-location', 28),
|
||||
('content-range', 29),
|
||||
('content-type', 30),
|
||||
('cookie', 31),
|
||||
('date', 32),
|
||||
('etag', 33),
|
||||
('expect', 34),
|
||||
('expires', 35),
|
||||
('from', 36),
|
||||
('host', 37),
|
||||
('if-match', 38),
|
||||
('if-modified-since', 39),
|
||||
('if-none-match', 40),
|
||||
('if-range', 41),
|
||||
('if-unmodified-since', 42),
|
||||
('last-modified', 43),
|
||||
('link', 44),
|
||||
('location', 45),
|
||||
('max-forwards', 46),
|
||||
('proxy-authenticate', 47),
|
||||
('proxy-authorization', 48),
|
||||
('range', 49),
|
||||
('referer', 50),
|
||||
('refresh', 51),
|
||||
('retry-after', 52),
|
||||
('server', 53),
|
||||
('set-cookie', 54),
|
||||
('strict-transport-security', 55),
|
||||
('transfer-encoding', 56),
|
||||
('user-agent', 57),
|
||||
('vary', 58),
|
||||
('via', 59),
|
||||
('www-authenticate', 60),
|
||||
('te', None),
|
||||
('connection', None),
|
||||
('keep-alive',None),
|
||||
('proxy-connection', None),
|
||||
('upgrade', None),
|
||||
]
|
||||
|
||||
def to_enum_hd(k):
|
||||
@@ -27,7 +80,7 @@ def to_enum_hd(k):
|
||||
|
||||
def build_header(headers):
|
||||
res = {}
|
||||
for k in headers:
|
||||
for k, _ in headers:
|
||||
size = len(k)
|
||||
if size not in res:
|
||||
res[size] = {}
|
||||
@@ -40,18 +93,20 @@ def build_header(headers):
|
||||
return res
|
||||
|
||||
def gen_enum():
|
||||
print '''\
|
||||
typedef enum {'''
|
||||
for k in sorted(HEADERS):
|
||||
print '''\
|
||||
{},'''.format(to_enum_hd(k))
|
||||
print '''\
|
||||
NGHTTP2_TOKEN_MAXIDX,
|
||||
} nghttp2_token;'''
|
||||
name = ''
|
||||
print 'typedef enum {'
|
||||
for k, token in HEADERS:
|
||||
if token is None:
|
||||
print ' {},'.format(to_enum_hd(k))
|
||||
else:
|
||||
if name != k:
|
||||
name = k
|
||||
print ' {} = {},'.format(to_enum_hd(k), token)
|
||||
print '} nghttp2_token;'
|
||||
|
||||
def gen_index_header():
|
||||
print '''\
|
||||
static int lookup_token(const uint8_t *name, size_t namelen) {
|
||||
static inline int lookup_token(const uint8_t *name, size_t namelen) {
|
||||
switch (namelen) {'''
|
||||
b = build_header(HEADERS)
|
||||
for size in sorted(b.keys()):
|
||||
@@ -66,7 +121,7 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
|
||||
case '{}':'''.format(c)
|
||||
for k in headers:
|
||||
print '''\
|
||||
if (streq("{}", name, {})) {{
|
||||
if (lstreq("{}", name, {})) {{
|
||||
return {};
|
||||
}}'''.format(k[:-1], size - 1, to_enum_hd(k))
|
||||
print '''\
|
||||
|
||||
@@ -61,6 +61,10 @@ def help2man(infile):
|
||||
description.append(line)
|
||||
|
||||
print '''
|
||||
.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY.
|
||||
|
||||
.. program:: {cmdname}
|
||||
|
||||
{cmdname}(1)
|
||||
{cmdnameunderline}
|
||||
|
||||
|
||||
@@ -246,6 +246,72 @@ func TestH1H1RequestTrailer(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H1HeaderFieldBufferPath tests that request with request path
|
||||
// larger than configured buffer size is rejected.
|
||||
func TestH1H1HeaderFieldBufferPath(t *testing.T) {
|
||||
// The value 100 is chosen so that sum of header fields bytes
|
||||
// does not exceed it. We use > 100 bytes URI to exceed this
|
||||
// limit.
|
||||
st := newServerTester([]string{"--header-field-buffer=100"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Fatal("execution path should not be here")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http1(requestParam{
|
||||
name: "TestH1H1HeaderFieldBufferPath",
|
||||
path: "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http1() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 431; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H1HeaderFieldBuffer tests that request with header fields
|
||||
// larger than configured buffer size is rejected.
|
||||
func TestH1H1HeaderFieldBuffer(t *testing.T) {
|
||||
st := newServerTester([]string{"--header-field-buffer=10"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Fatal("execution path should not be here")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http1(requestParam{
|
||||
name: "TestH1H1HeaderFieldBuffer",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http1() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 431; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H1HeaderFields tests that request with header fields more
|
||||
// than configured number is rejected.
|
||||
func TestH1H1HeaderFields(t *testing.T) {
|
||||
st := newServerTester([]string{"--max-header-fields=1"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Fatal("execution path should not be here")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http1(requestParam{
|
||||
name: "TestH1H1HeaderFields",
|
||||
header: []hpack.HeaderField{
|
||||
// Add extra header field to ensure that
|
||||
// header field limit exceeds
|
||||
pair("Connection", "close"),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http1() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 431; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H2ConnectFailure tests that server handles the situation that
|
||||
// connection attempt to HTTP/2 backend failed.
|
||||
func TestH1H2ConnectFailure(t *testing.T) {
|
||||
|
||||
@@ -558,6 +558,79 @@ func TestH2H1RequestTrailer(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1HeaderFieldBuffer tests that request with header fields
|
||||
// larger than configured buffer size is rejected.
|
||||
func TestH2H1HeaderFieldBuffer(t *testing.T) {
|
||||
st := newServerTester([]string{"--header-field-buffer=10"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Fatal("execution path should not be here")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1HeaderFieldBuffer",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 431; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1HeaderFields tests that request with header fields more
|
||||
// than configured number is rejected.
|
||||
func TestH2H1HeaderFields(t *testing.T) {
|
||||
st := newServerTester([]string{"--max-header-fields=1"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Fatal("execution path should not be here")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1HeaderFields",
|
||||
// we have at least 4 pseudo-header fields sent, and
|
||||
// that ensures that buffer limit exceeds.
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 431; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1Upgrade tests HTTP Upgrade to HTTP/2
|
||||
func TestH2H1Upgrade(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http1(requestParam{
|
||||
name: "TestH2H1Upgrade",
|
||||
header: []hpack.HeaderField{
|
||||
pair("Connection", "Upgrade, HTTP2-Settings"),
|
||||
pair("Upgrade", "h2c-14"),
|
||||
pair("HTTP2-Settings", "AAMAAABkAAQAAP__"),
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http1() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := res.status, 101; got != want {
|
||||
t.Errorf("res.status: %v; want %v", got, want)
|
||||
}
|
||||
|
||||
res, err = st.http2(requestParam{
|
||||
httpUpgrade: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 200; got != want {
|
||||
t.Errorf("res.status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1GracefulShutdown tests graceful shutdown.
|
||||
func TestH2H1GracefulShutdown(t *testing.T) {
|
||||
st := newServerTester(nil, t, noopHandler)
|
||||
|
||||
@@ -170,6 +170,46 @@ func TestS3H1NoVia(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestS3H1HeaderFieldBuffer tests that request with header fields
|
||||
// larger than configured buffer size is rejected.
|
||||
func TestS3H1HeaderFieldBuffer(t *testing.T) {
|
||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--header-field-buffer=10"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Fatal("execution path should not be here")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.spdy(requestParam{
|
||||
name: "TestS3H1HeaderFieldBuffer",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.spdy() = %v", err)
|
||||
}
|
||||
if got, want := res.spdyRstErrCode, spdy.InternalError; got != want {
|
||||
t.Errorf("res.spdyRstErrCode: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestS3H1HeaderFields tests that request with header fields more
|
||||
// than configured number is rejected.
|
||||
func TestS3H1HeaderFields(t *testing.T) {
|
||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--max-header-fields=1"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Fatal("execution path should not be here")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.spdy(requestParam{
|
||||
name: "TestS3H1HeaderFields",
|
||||
// we have at least 5 pseudo-header fields sent, and
|
||||
// that ensures that buffer limit exceeds.
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.spdy() = %v", err)
|
||||
}
|
||||
if got, want := res.spdyRstErrCode, spdy.InternalError; got != want {
|
||||
t.Errorf("res.spdyRstErrCode: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestS3H2ConnectFailure tests that server handles the situation that
|
||||
// connection attempt to HTTP/2 backend failed.
|
||||
func TestS3H2ConnectFailure(t *testing.T) {
|
||||
|
||||
@@ -247,15 +247,16 @@ func (st *serverTester) readSpdyFrame() (spdy.Frame, error) {
|
||||
}
|
||||
|
||||
type requestParam struct {
|
||||
name string // name for this request to identify the request in log easily
|
||||
streamID uint32 // stream ID, automatically assigned if 0
|
||||
method string // method, defaults to GET
|
||||
scheme string // scheme, defaults to http
|
||||
authority string // authority, defaults to backend server address
|
||||
path string // path, defaults to /
|
||||
header []hpack.HeaderField // additional request header fields
|
||||
body []byte // request body
|
||||
trailer []hpack.HeaderField // trailer part
|
||||
name string // name for this request to identify the request in log easily
|
||||
streamID uint32 // stream ID, automatically assigned if 0
|
||||
method string // method, defaults to GET
|
||||
scheme string // scheme, defaults to http
|
||||
authority string // authority, defaults to backend server address
|
||||
path string // path, defaults to /
|
||||
header []hpack.HeaderField // additional request header fields
|
||||
body []byte // request body
|
||||
trailer []hpack.HeaderField // trailer part
|
||||
httpUpgrade bool // true if upgraded to HTTP/2 through HTTP Upgrade
|
||||
}
|
||||
|
||||
// wrapper for request body to set trailer part
|
||||
@@ -296,7 +297,19 @@ func (st *serverTester) http1(rp requestParam) (*serverResponse, error) {
|
||||
body = cbr
|
||||
}
|
||||
}
|
||||
req, err := http.NewRequest(method, st.url, body)
|
||||
|
||||
reqURL := st.url
|
||||
|
||||
if rp.path != "" {
|
||||
u, err := url.Parse(st.url)
|
||||
if err != nil {
|
||||
st.t.Fatalf("Error parsing URL from st.url %v: %v", st.url, err)
|
||||
}
|
||||
u.Path = rp.path
|
||||
reqURL = u.String()
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(method, reqURL, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -478,69 +491,70 @@ func (st *serverTester) http2(rp requestParam) (*serverResponse, error) {
|
||||
streams := make(map[uint32]*serverResponse)
|
||||
streams[id] = res
|
||||
|
||||
method := "GET"
|
||||
if rp.method != "" {
|
||||
method = rp.method
|
||||
}
|
||||
_ = st.enc.WriteField(pair(":method", method))
|
||||
|
||||
scheme := "http"
|
||||
if rp.scheme != "" {
|
||||
scheme = rp.scheme
|
||||
}
|
||||
_ = st.enc.WriteField(pair(":scheme", scheme))
|
||||
|
||||
authority := st.authority
|
||||
if rp.authority != "" {
|
||||
authority = rp.authority
|
||||
}
|
||||
_ = st.enc.WriteField(pair(":authority", authority))
|
||||
|
||||
path := "/"
|
||||
if rp.path != "" {
|
||||
path = rp.path
|
||||
}
|
||||
_ = st.enc.WriteField(pair(":path", path))
|
||||
|
||||
_ = st.enc.WriteField(pair("test-case", rp.name))
|
||||
|
||||
for _, h := range rp.header {
|
||||
_ = st.enc.WriteField(h)
|
||||
}
|
||||
|
||||
err := st.fr.WriteHeaders(http2.HeadersFrameParam{
|
||||
StreamID: id,
|
||||
EndStream: len(rp.body) == 0 && len(rp.trailer) == 0,
|
||||
EndHeaders: true,
|
||||
BlockFragment: st.headerBlkBuf.Bytes(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(rp.body) != 0 {
|
||||
// TODO we assume rp.body fits in 1 frame
|
||||
if err := st.fr.WriteData(id, len(rp.trailer) == 0, rp.body); err != nil {
|
||||
return nil, err
|
||||
if !rp.httpUpgrade {
|
||||
method := "GET"
|
||||
if rp.method != "" {
|
||||
method = rp.method
|
||||
}
|
||||
}
|
||||
_ = st.enc.WriteField(pair(":method", method))
|
||||
|
||||
if len(rp.trailer) != 0 {
|
||||
st.headerBlkBuf.Reset()
|
||||
for _, h := range rp.trailer {
|
||||
scheme := "http"
|
||||
if rp.scheme != "" {
|
||||
scheme = rp.scheme
|
||||
}
|
||||
_ = st.enc.WriteField(pair(":scheme", scheme))
|
||||
|
||||
authority := st.authority
|
||||
if rp.authority != "" {
|
||||
authority = rp.authority
|
||||
}
|
||||
_ = st.enc.WriteField(pair(":authority", authority))
|
||||
|
||||
path := "/"
|
||||
if rp.path != "" {
|
||||
path = rp.path
|
||||
}
|
||||
_ = st.enc.WriteField(pair(":path", path))
|
||||
|
||||
_ = st.enc.WriteField(pair("test-case", rp.name))
|
||||
|
||||
for _, h := range rp.header {
|
||||
_ = st.enc.WriteField(h)
|
||||
}
|
||||
|
||||
err := st.fr.WriteHeaders(http2.HeadersFrameParam{
|
||||
StreamID: id,
|
||||
EndStream: true,
|
||||
EndStream: len(rp.body) == 0 && len(rp.trailer) == 0,
|
||||
EndHeaders: true,
|
||||
BlockFragment: st.headerBlkBuf.Bytes(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if len(rp.body) != 0 {
|
||||
// TODO we assume rp.body fits in 1 frame
|
||||
if err := st.fr.WriteData(id, len(rp.trailer) == 0, rp.body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if len(rp.trailer) != 0 {
|
||||
st.headerBlkBuf.Reset()
|
||||
for _, h := range rp.trailer {
|
||||
_ = st.enc.WriteField(h)
|
||||
}
|
||||
err := st.fr.WriteHeaders(http2.HeadersFrameParam{
|
||||
StreamID: id,
|
||||
EndStream: true,
|
||||
EndHeaders: true,
|
||||
BlockFragment: st.headerBlkBuf.Bytes(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
loop:
|
||||
for {
|
||||
fr, err := st.readFrame()
|
||||
|
||||
@@ -36,7 +36,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <nghttp2/nghttp2ver.h>
|
||||
@@ -241,8 +241,9 @@ typedef enum {
|
||||
*/
|
||||
NGHTTP2_ERR_UNSUPPORTED_VERSION = -503,
|
||||
/**
|
||||
* Used as a return value from :type:`nghttp2_send_callback` and
|
||||
* :type:`nghttp2_recv_callback` to indicate that the operation
|
||||
* Used as a return value from :type:`nghttp2_send_callback`,
|
||||
* :type:`nghttp2_recv_callback` and
|
||||
* :type:`nghttp2_send_data_callback` to indicate that the operation
|
||||
* would block.
|
||||
*/
|
||||
NGHTTP2_ERR_WOULDBLOCK = -504,
|
||||
@@ -1275,10 +1276,10 @@ typedef ssize_t (*nghttp2_recv_callback)(nghttp2_session *session, uint8_t *buf,
|
||||
/**
|
||||
* @functypedef
|
||||
*
|
||||
* Callback function invoked by `nghttp2_session_recv()` when a frame
|
||||
* is received. The |user_data| pointer is the third argument passed
|
||||
* in to the call to `nghttp2_session_client_new()` or
|
||||
* `nghttp2_session_server_new()`.
|
||||
* Callback function invoked by `nghttp2_session_recv()` and
|
||||
* `nghttp2_session_mem_recv()` when a frame is received. The
|
||||
* |user_data| pointer is the third argument passed in to the call to
|
||||
* `nghttp2_session_client_new()` or `nghttp2_session_server_new()`.
|
||||
*
|
||||
* If frame is HEADERS or PUSH_PROMISE, the ``nva`` and ``nvlen``
|
||||
* member of their data structure are always ``NULL`` and 0
|
||||
@@ -1313,14 +1314,14 @@ typedef int (*nghttp2_on_frame_recv_callback)(nghttp2_session *session,
|
||||
/**
|
||||
* @functypedef
|
||||
*
|
||||
* Callback function invoked by `nghttp2_session_recv()` when an
|
||||
* invalid non-DATA frame is received. The |error_code| indicates the
|
||||
* error. It is usually one of the :enum:`nghttp2_error_code` but
|
||||
* that is not guaranteed. When this callback function is invoked,
|
||||
* the library automatically submits either RST_STREAM or GOAWAY
|
||||
* frame. The |user_data| pointer is the third argument passed in to
|
||||
* the call to `nghttp2_session_client_new()` or
|
||||
* `nghttp2_session_server_new()`.
|
||||
* Callback function invoked by `nghttp2_session_recv()` and
|
||||
* `nghttp2_session_mem_recv()` when an invalid non-DATA frame is
|
||||
* received. The |error_code| indicates the error. It is usually one
|
||||
* of the :enum:`nghttp2_error_code` but that is not guaranteed. When
|
||||
* this callback function is invoked, the library automatically
|
||||
* submits either RST_STREAM or GOAWAY frame. The |user_data| pointer
|
||||
* is the third argument passed in to the call to
|
||||
* `nghttp2_session_client_new()` or `nghttp2_session_server_new()`.
|
||||
*
|
||||
* If frame is HEADERS or PUSH_PROMISE, the ``nva`` and ``nvlen``
|
||||
* member of their data structure are always ``NULL`` and 0
|
||||
@@ -1328,7 +1329,7 @@ typedef int (*nghttp2_on_frame_recv_callback)(nghttp2_session *session,
|
||||
*
|
||||
* The implementation of this function must return 0 if it succeeds.
|
||||
* If nonzero is returned, it is treated as fatal error and
|
||||
* `nghttp2_session_recv()` and `nghttp2_session_send()` functions
|
||||
* `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions
|
||||
* immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
|
||||
*
|
||||
* To set this callback to :type:`nghttp2_session_callbacks`, use
|
||||
@@ -1361,7 +1362,7 @@ typedef int (*nghttp2_on_invalid_frame_recv_callback)(
|
||||
* region included in the input bytes.
|
||||
*
|
||||
* The implementation of this function must return 0 if it succeeds.
|
||||
* If nonzero is returned, it is treated as fatal error and
|
||||
* If nonzero is returned, it is treated as fatal error, and
|
||||
* `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions
|
||||
* immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
|
||||
*
|
||||
@@ -1384,7 +1385,7 @@ typedef int (*nghttp2_on_data_chunk_recv_callback)(nghttp2_session *session,
|
||||
*
|
||||
* The implementation of this function must return 0 if it succeeds.
|
||||
* If nonzero is returned, it is treated as fatal error and
|
||||
* `nghttp2_session_recv()` and `nghttp2_session_send()` functions
|
||||
* `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions
|
||||
* immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
|
||||
*
|
||||
* To set this callback to :type:`nghttp2_session_callbacks`, use
|
||||
@@ -1403,7 +1404,7 @@ typedef int (*nghttp2_before_frame_send_callback)(nghttp2_session *session,
|
||||
*
|
||||
* The implementation of this function must return 0 if it succeeds.
|
||||
* If nonzero is returned, it is treated as fatal error and
|
||||
* `nghttp2_session_recv()` and `nghttp2_session_send()` functions
|
||||
* `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions
|
||||
* immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
|
||||
*
|
||||
* To set this callback to :type:`nghttp2_session_callbacks`, use
|
||||
@@ -1425,7 +1426,7 @@ typedef int (*nghttp2_on_frame_send_callback)(nghttp2_session *session,
|
||||
*
|
||||
* The implementation of this function must return 0 if it succeeds.
|
||||
* If nonzero is returned, it is treated as fatal error and
|
||||
* `nghttp2_session_recv()` and `nghttp2_session_send()` functions
|
||||
* `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions
|
||||
* immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
|
||||
*
|
||||
* `nghttp2_session_get_stream_user_data()` can be used to get
|
||||
@@ -1455,8 +1456,9 @@ typedef int (*nghttp2_on_frame_not_send_callback)(nghttp2_session *session,
|
||||
*
|
||||
* The implementation of this function must return 0 if it succeeds.
|
||||
* If nonzero is returned, it is treated as fatal error and
|
||||
* `nghttp2_session_recv()` and `nghttp2_session_send()` functions
|
||||
* immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
|
||||
* `nghttp2_session_recv()`, `nghttp2_session_mem_recv()`,
|
||||
* `nghttp2_session_send()`, and `nghttp2_session_mem_send()`
|
||||
* functions immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
|
||||
*
|
||||
* To set this callback to :type:`nghttp2_session_callbacks`, use
|
||||
* `nghttp2_session_callbacks_set_on_stream_close_callback()`.
|
||||
@@ -1498,13 +1500,26 @@ typedef int (*nghttp2_on_stream_close_callback)(nghttp2_session *session,
|
||||
* frame with ``frame->headers.cat == NGHTTP2_HCAT_HEADERS``
|
||||
* containing final response headers (non-1xx status code). The
|
||||
* trailer headers also has ``frame->headers.cat ==
|
||||
* NGHTTP2_HCAT_HEADERS`` which does not containg any status code.
|
||||
* NGHTTP2_HCAT_HEADERS`` which does not contain any status code.
|
||||
*
|
||||
* The implementation of this function must return 0 if it succeeds or
|
||||
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. If nonzero value other than
|
||||
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned, it is treated as
|
||||
* if :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned. If
|
||||
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned,
|
||||
* Returning :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will close
|
||||
* the stream (promised stream if frame is PUSH_PROMISE) by issuing
|
||||
* RST_STREAM with :enum:`NGHTTP2_INTERNAL_ERROR`. In this case,
|
||||
* :type:`nghttp2_on_header_callback` and
|
||||
* :type:`nghttp2_on_frame_recv_callback` will not be invoked. If a
|
||||
* different error code is desirable, use
|
||||
* `nghttp2_submit_rst_stream()` with a desired error code and then
|
||||
* return :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. Again, use
|
||||
* ``frame->push_promise.promised_stream_id`` as stream_id parameter
|
||||
* in `nghttp2_submit_rst_stream()` if frame is PUSH_PROMISE.
|
||||
*
|
||||
* The implementation of this function must return 0 if it succeeds.
|
||||
* It can return :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` to
|
||||
* reset the stream (promised stream if frame is PUSH_PROMISE). For
|
||||
* critical errors, it must return
|
||||
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. If the other value is
|
||||
* returned, it is treated as if :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`
|
||||
* is returned. If :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned,
|
||||
* `nghttp2_session_mem_recv()` function will immediately return
|
||||
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
|
||||
*
|
||||
@@ -1559,12 +1574,15 @@ typedef int (*nghttp2_on_begin_headers_callback)(nghttp2_session *session,
|
||||
* included in the input bytes.
|
||||
*
|
||||
* Returning :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will close
|
||||
* the stream by issuing RST_STREAM with
|
||||
* :enum:`NGHTTP2_INTERNAL_ERROR`. In this case,
|
||||
* the stream (promised stream if frame is PUSH_PROMISE) by issuing
|
||||
* RST_STREAM with :enum:`NGHTTP2_INTERNAL_ERROR`. In this case,
|
||||
* :type:`nghttp2_on_header_callback` and
|
||||
* :type:`nghttp2_on_frame_recv_callback` will not be invoked. If a
|
||||
* different error code is desirable, use
|
||||
* `nghttp2_submit_rst_stream()` with a desired error code and then
|
||||
* return :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`.
|
||||
* return :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. Again, use
|
||||
* ``frame->push_promise.promised_stream_id`` as stream_id parameter
|
||||
* in `nghttp2_submit_rst_stream()` if frame is PUSH_PROMISE.
|
||||
*
|
||||
* The implementation of this function must return 0 if it succeeds.
|
||||
* It may return :enum:`NGHTTP2_ERR_PAUSE` or
|
||||
@@ -1596,8 +1614,8 @@ typedef int (*nghttp2_on_header_callback)(nghttp2_session *session,
|
||||
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. Returning
|
||||
* ``frame->hd.length`` means no padding is added. Returning
|
||||
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` will make
|
||||
* `nghttp2_session_send()` function immediately return
|
||||
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
|
||||
* `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions
|
||||
* immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
|
||||
*
|
||||
* To set this callback to :type:`nghttp2_session_callbacks`, use
|
||||
* `nghttp2_session_callbacks_set_select_padding_callback()`.
|
||||
@@ -1726,8 +1744,8 @@ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_recv_callback(
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* Sets callback function invoked by `nghttp2_session_recv()` when a
|
||||
* frame is received.
|
||||
* Sets callback function invoked by `nghttp2_session_recv()` and
|
||||
* `nghttp2_session_mem_recv()` when a frame is received.
|
||||
*/
|
||||
NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_frame_recv_callback(
|
||||
nghttp2_session_callbacks *cbs,
|
||||
@@ -1736,8 +1754,9 @@ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_frame_recv_callback(
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* Sets callback function invoked by `nghttp2_session_recv()` when an
|
||||
* invalid non-DATA frame is received.
|
||||
* Sets callback function invoked by `nghttp2_session_recv()` and
|
||||
* `nghttp2_session_mem_recv()` when an invalid non-DATA frame is
|
||||
* received.
|
||||
*/
|
||||
NGHTTP2_EXTERN void
|
||||
nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(
|
||||
@@ -2707,7 +2726,9 @@ NGHTTP2_EXTERN uint32_t
|
||||
*
|
||||
* :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
|
||||
* The |next_stream_id| is strictly less than the value
|
||||
* `nghttp2_session_get_next_stream_id()` returns.
|
||||
* `nghttp2_session_get_next_stream_id()` returns; or
|
||||
* |next_stream_id| is invalid (e.g., even integer for client, or
|
||||
* odd integer for server).
|
||||
*/
|
||||
NGHTTP2_EXTERN int nghttp2_session_set_next_stream_id(nghttp2_session *session,
|
||||
int32_t next_stream_id);
|
||||
@@ -2928,13 +2949,13 @@ nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec);
|
||||
* If |data_prd| is not ``NULL``, it provides data which will be sent
|
||||
* in subsequent DATA frames. In this case, a method that allows
|
||||
* request message bodies
|
||||
* (http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9) must
|
||||
* be specified with ``:method`` key in |nva| (e.g. ``POST``). This
|
||||
* function does not take ownership of the |data_prd|. The function
|
||||
* copies the members of the |data_prd|. If |data_prd| is ``NULL``,
|
||||
* HEADERS have END_STREAM set. The |stream_user_data| is data
|
||||
* associated to the stream opened by this request and can be an
|
||||
* arbitrary pointer, which can be retrieved later by
|
||||
* (https://tools.ietf.org/html/rfc7231#section-4) must be specified
|
||||
* with ``:method`` key in |nva| (e.g. ``POST``). This function does
|
||||
* not take ownership of the |data_prd|. The function copies the
|
||||
* members of the |data_prd|. If |data_prd| is ``NULL``, HEADERS have
|
||||
* END_STREAM set. The |stream_user_data| is data associated to the
|
||||
* stream opened by this request and can be an arbitrary pointer,
|
||||
* which can be retrieved later by
|
||||
* `nghttp2_session_get_stream_user_data()`.
|
||||
*
|
||||
* This function returns assigned stream ID if it succeeds, or one of
|
||||
@@ -3541,7 +3562,10 @@ NGHTTP2_EXTERN int nghttp2_nv_compare_name(const nghttp2_nv *lhs,
|
||||
* {
|
||||
* int rv;
|
||||
* rv = nghttp2_select_next_protocol(out, outlen, in, inlen);
|
||||
* if(rv == 1) {
|
||||
* if (rv == -1) {
|
||||
* return SSL_TLSEXT_ERR_NOACK;
|
||||
* }
|
||||
* if (rv == 1) {
|
||||
* ((MyType*)arg)->http2_selected = 1;
|
||||
* }
|
||||
* return SSL_TLSEXT_ERR_OK;
|
||||
|
||||
@@ -320,7 +320,7 @@ int nghttp2_bufs_add(nghttp2_bufs *bufs, const void *data, size_t len) {
|
||||
}
|
||||
|
||||
buf->last = nghttp2_cpymem(buf->last, p, nwrite);
|
||||
p += len;
|
||||
p += nwrite;
|
||||
len -= nwrite;
|
||||
}
|
||||
|
||||
@@ -432,6 +432,24 @@ ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out) {
|
||||
return (ssize_t)len;
|
||||
}
|
||||
|
||||
size_t nghttp2_bufs_remove_copy(nghttp2_bufs *bufs, uint8_t *out) {
|
||||
size_t len;
|
||||
nghttp2_buf_chain *chain;
|
||||
nghttp2_buf *buf;
|
||||
nghttp2_buf resbuf;
|
||||
|
||||
len = nghttp2_bufs_len(bufs);
|
||||
|
||||
nghttp2_buf_wrap_init(&resbuf, out, len);
|
||||
|
||||
for (chain = bufs->head; chain; chain = chain->next) {
|
||||
buf = &chain->buf;
|
||||
resbuf.last = nghttp2_cpymem(resbuf.last, buf->pos, nghttp2_buf_len(buf));
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
void nghttp2_bufs_reset(nghttp2_bufs *bufs) {
|
||||
nghttp2_buf_chain *chain, *ci;
|
||||
size_t k;
|
||||
|
||||
@@ -324,6 +324,17 @@ int nghttp2_bufs_orb_hold(nghttp2_bufs *bufs, uint8_t b);
|
||||
*/
|
||||
ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out);
|
||||
|
||||
/*
|
||||
* Copies all data stored in |bufs| to |out|. This function assumes
|
||||
* that the buffer space pointed by |out| has at least
|
||||
* nghttp2_bufs(bufs) bytes.
|
||||
*
|
||||
* The contents of |bufs| is left unchanged.
|
||||
*
|
||||
* This function returns the length of copied data.
|
||||
*/
|
||||
size_t nghttp2_bufs_remove_copy(nghttp2_bufs *bufs, uint8_t *out);
|
||||
|
||||
/*
|
||||
* Resets |bufs| and makes the buffers empty.
|
||||
*/
|
||||
|
||||
899
lib/nghttp2_hd.c
899
lib/nghttp2_hd.c
File diff suppressed because it is too large
Load Diff
103
lib/nghttp2_hd.h
103
lib/nghttp2_hd.h
@@ -49,7 +49,68 @@
|
||||
#define NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE (1 << 12)
|
||||
|
||||
/* Exported for unit test */
|
||||
extern const size_t NGHTTP2_STATIC_TABLE_LENGTH;
|
||||
#define NGHTTP2_STATIC_TABLE_LENGTH 61
|
||||
|
||||
/* Generated by genlibtokenlookup.py */
|
||||
typedef enum {
|
||||
NGHTTP2_TOKEN__AUTHORITY = 0,
|
||||
NGHTTP2_TOKEN__METHOD = 1,
|
||||
NGHTTP2_TOKEN__PATH = 3,
|
||||
NGHTTP2_TOKEN__SCHEME = 5,
|
||||
NGHTTP2_TOKEN__STATUS = 7,
|
||||
NGHTTP2_TOKEN_ACCEPT_CHARSET = 14,
|
||||
NGHTTP2_TOKEN_ACCEPT_ENCODING = 15,
|
||||
NGHTTP2_TOKEN_ACCEPT_LANGUAGE = 16,
|
||||
NGHTTP2_TOKEN_ACCEPT_RANGES = 17,
|
||||
NGHTTP2_TOKEN_ACCEPT = 18,
|
||||
NGHTTP2_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN = 19,
|
||||
NGHTTP2_TOKEN_AGE = 20,
|
||||
NGHTTP2_TOKEN_ALLOW = 21,
|
||||
NGHTTP2_TOKEN_AUTHORIZATION = 22,
|
||||
NGHTTP2_TOKEN_CACHE_CONTROL = 23,
|
||||
NGHTTP2_TOKEN_CONTENT_DISPOSITION = 24,
|
||||
NGHTTP2_TOKEN_CONTENT_ENCODING = 25,
|
||||
NGHTTP2_TOKEN_CONTENT_LANGUAGE = 26,
|
||||
NGHTTP2_TOKEN_CONTENT_LENGTH = 27,
|
||||
NGHTTP2_TOKEN_CONTENT_LOCATION = 28,
|
||||
NGHTTP2_TOKEN_CONTENT_RANGE = 29,
|
||||
NGHTTP2_TOKEN_CONTENT_TYPE = 30,
|
||||
NGHTTP2_TOKEN_COOKIE = 31,
|
||||
NGHTTP2_TOKEN_DATE = 32,
|
||||
NGHTTP2_TOKEN_ETAG = 33,
|
||||
NGHTTP2_TOKEN_EXPECT = 34,
|
||||
NGHTTP2_TOKEN_EXPIRES = 35,
|
||||
NGHTTP2_TOKEN_FROM = 36,
|
||||
NGHTTP2_TOKEN_HOST = 37,
|
||||
NGHTTP2_TOKEN_IF_MATCH = 38,
|
||||
NGHTTP2_TOKEN_IF_MODIFIED_SINCE = 39,
|
||||
NGHTTP2_TOKEN_IF_NONE_MATCH = 40,
|
||||
NGHTTP2_TOKEN_IF_RANGE = 41,
|
||||
NGHTTP2_TOKEN_IF_UNMODIFIED_SINCE = 42,
|
||||
NGHTTP2_TOKEN_LAST_MODIFIED = 43,
|
||||
NGHTTP2_TOKEN_LINK = 44,
|
||||
NGHTTP2_TOKEN_LOCATION = 45,
|
||||
NGHTTP2_TOKEN_MAX_FORWARDS = 46,
|
||||
NGHTTP2_TOKEN_PROXY_AUTHENTICATE = 47,
|
||||
NGHTTP2_TOKEN_PROXY_AUTHORIZATION = 48,
|
||||
NGHTTP2_TOKEN_RANGE = 49,
|
||||
NGHTTP2_TOKEN_REFERER = 50,
|
||||
NGHTTP2_TOKEN_REFRESH = 51,
|
||||
NGHTTP2_TOKEN_RETRY_AFTER = 52,
|
||||
NGHTTP2_TOKEN_SERVER = 53,
|
||||
NGHTTP2_TOKEN_SET_COOKIE = 54,
|
||||
NGHTTP2_TOKEN_STRICT_TRANSPORT_SECURITY = 55,
|
||||
NGHTTP2_TOKEN_TRANSFER_ENCODING = 56,
|
||||
NGHTTP2_TOKEN_USER_AGENT = 57,
|
||||
NGHTTP2_TOKEN_VARY = 58,
|
||||
NGHTTP2_TOKEN_VIA = 59,
|
||||
NGHTTP2_TOKEN_WWW_AUTHENTICATE = 60,
|
||||
NGHTTP2_TOKEN_TE,
|
||||
NGHTTP2_TOKEN_CONNECTION,
|
||||
NGHTTP2_TOKEN_KEEP_ALIVE,
|
||||
NGHTTP2_TOKEN_PROXY_CONNECTION,
|
||||
NGHTTP2_TOKEN_UPGRADE
|
||||
} nghttp2_token;
|
||||
|
||||
typedef enum {
|
||||
NGHTTP2_HD_FLAG_NONE = 0,
|
||||
@@ -67,18 +128,14 @@ typedef enum {
|
||||
|
||||
typedef struct {
|
||||
nghttp2_nv nv;
|
||||
uint32_t name_hash;
|
||||
uint32_t value_hash;
|
||||
/* nghttp2_token value for nv.name. It could be -1 if we have no
|
||||
token for that header field name. */
|
||||
int token;
|
||||
/* Reference count */
|
||||
uint8_t ref;
|
||||
uint8_t flags;
|
||||
} nghttp2_hd_entry;
|
||||
|
||||
typedef struct {
|
||||
nghttp2_hd_entry ent;
|
||||
size_t index;
|
||||
} nghttp2_hd_static_entry;
|
||||
|
||||
typedef struct {
|
||||
nghttp2_hd_entry **buffer;
|
||||
size_t mask;
|
||||
@@ -107,6 +164,12 @@ typedef enum {
|
||||
NGHTTP2_HD_STATE_READ_VALUE
|
||||
} nghttp2_hd_inflate_state;
|
||||
|
||||
typedef enum {
|
||||
NGHTTP2_HD_WITH_INDEXING,
|
||||
NGHTTP2_HD_WITHOUT_INDEXING,
|
||||
NGHTTP2_HD_NEVER_INDEXING
|
||||
} nghttp2_hd_indexing_mode;
|
||||
|
||||
typedef struct {
|
||||
/* dynamic header table */
|
||||
nghttp2_hd_ringbuf hd_table;
|
||||
@@ -176,9 +239,8 @@ struct nghttp2_hd_inflater {
|
||||
* set in the |flags|, the content pointed by the |name| with length
|
||||
* |namelen| is copied. Likewise, if NGHTTP2_HD_FLAG_VALUE_ALLOC bit
|
||||
* set in the |flags|, the content pointed by the |value| with length
|
||||
* |valuelen| is copied. The |name_hash| and |value_hash| are hash
|
||||
* value for |name| and |value| respectively. The hash function is
|
||||
* defined in nghttp2_hd.c.
|
||||
* |valuelen| is copied. The |token| is enum number looked up by
|
||||
* |name|. It could be -1 if we don't have that enum value.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
@@ -188,8 +250,7 @@ struct nghttp2_hd_inflater {
|
||||
*/
|
||||
int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t flags, uint8_t *name,
|
||||
size_t namelen, uint8_t *value, size_t valuelen,
|
||||
uint32_t name_hash, uint32_t value_hash,
|
||||
nghttp2_mem *mem);
|
||||
int token, nghttp2_mem *mem);
|
||||
|
||||
void nghttp2_hd_entry_free(nghttp2_hd_entry *ent, nghttp2_mem *mem);
|
||||
|
||||
@@ -271,13 +332,25 @@ int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater, nghttp2_mem *mem);
|
||||
*/
|
||||
void nghttp2_hd_inflate_free(nghttp2_hd_inflater *inflater);
|
||||
|
||||
/*
|
||||
* Similar to nghttp2_hd_inflate_hd(), but this takes additional
|
||||
* output parameter |token|. On successful header emission, it
|
||||
* contains nghttp2_token value for nv_out->name. It could be -1 if
|
||||
* we don't have enum value for the name. Other than that return
|
||||
* values and semantics are the same as nghttp2_hd_inflate_hd().
|
||||
*/
|
||||
ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater,
|
||||
nghttp2_nv *nv_out, int *inflate_flags,
|
||||
int *token, uint8_t *in, size_t inlen,
|
||||
int in_final);
|
||||
|
||||
/* For unittesting purpose */
|
||||
int nghttp2_hd_emit_indname_block(nghttp2_bufs *bufs, size_t index,
|
||||
nghttp2_nv *nv, int inc_indexing);
|
||||
nghttp2_nv *nv, int indexing_mode);
|
||||
|
||||
/* For unittesting purpose */
|
||||
int nghttp2_hd_emit_newname_block(nghttp2_bufs *bufs, nghttp2_nv *nv,
|
||||
int inc_indexing);
|
||||
int indexing_mode);
|
||||
|
||||
/* For unittesting purpose */
|
||||
int nghttp2_hd_emit_table_size(nghttp2_bufs *bufs, size_t table_size);
|
||||
|
||||
@@ -168,10 +168,27 @@ void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx) {
|
||||
ctx->accept = 1;
|
||||
}
|
||||
|
||||
/* Use macro to make the code simpler..., but error case is tricky.
|
||||
We spent most of the CPU in decoding, so we are doing this
|
||||
thing. */
|
||||
#define hd_huff_decode_sym_emit(bufs, sym, avail) \
|
||||
do { \
|
||||
if ((avail)) { \
|
||||
nghttp2_bufs_fast_addb((bufs), (sym)); \
|
||||
--(avail); \
|
||||
} else { \
|
||||
rv = nghttp2_bufs_addb((bufs), (sym)); \
|
||||
if (rv != 0) { \
|
||||
return rv; \
|
||||
} \
|
||||
(avail) = nghttp2_bufs_cur_avail((bufs)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx,
|
||||
nghttp2_bufs *bufs, const uint8_t *src,
|
||||
size_t srclen, int final) {
|
||||
size_t i, j;
|
||||
size_t i;
|
||||
int rv;
|
||||
size_t avail;
|
||||
|
||||
@@ -180,30 +197,28 @@ ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx,
|
||||
/* We use the decoding algorithm described in
|
||||
http://graphics.ics.uci.edu/pub/Prefix.pdf */
|
||||
for (i = 0; i < srclen; ++i) {
|
||||
uint8_t in = src[i] >> 4;
|
||||
for (j = 0; j < 2; ++j) {
|
||||
const nghttp2_huff_decode *t;
|
||||
const nghttp2_huff_decode *t;
|
||||
|
||||
t = &huff_decode_table[ctx->state][in];
|
||||
if (t->flags & NGHTTP2_HUFF_FAIL) {
|
||||
return NGHTTP2_ERR_HEADER_COMP;
|
||||
}
|
||||
if (t->flags & NGHTTP2_HUFF_SYM) {
|
||||
if (avail) {
|
||||
nghttp2_bufs_fast_addb(bufs, t->sym);
|
||||
--avail;
|
||||
} else {
|
||||
rv = nghttp2_bufs_addb(bufs, t->sym);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
avail = nghttp2_bufs_cur_avail(bufs);
|
||||
}
|
||||
}
|
||||
ctx->state = t->state;
|
||||
ctx->accept = (t->flags & NGHTTP2_HUFF_ACCEPTED) != 0;
|
||||
in = src[i] & 0xf;
|
||||
t = &huff_decode_table[ctx->state][src[i] >> 4];
|
||||
if (t->flags & NGHTTP2_HUFF_FAIL) {
|
||||
return NGHTTP2_ERR_HEADER_COMP;
|
||||
}
|
||||
if (t->flags & NGHTTP2_HUFF_SYM) {
|
||||
/* this is macro, and may return from this function on error */
|
||||
hd_huff_decode_sym_emit(bufs, t->sym, avail);
|
||||
}
|
||||
|
||||
t = &huff_decode_table[t->state][src[i] & 0xf];
|
||||
if (t->flags & NGHTTP2_HUFF_FAIL) {
|
||||
return NGHTTP2_ERR_HEADER_COMP;
|
||||
}
|
||||
if (t->flags & NGHTTP2_HUFF_SYM) {
|
||||
/* this is macro, and may return from this function on error */
|
||||
hd_huff_decode_sym_emit(bufs, t->sym, avail);
|
||||
}
|
||||
|
||||
ctx->state = t->state;
|
||||
ctx->accept = (t->flags & NGHTTP2_HUFF_ACCEPTED) != 0;
|
||||
}
|
||||
if (final && !ctx->accept) {
|
||||
return NGHTTP2_ERR_HEADER_COMP;
|
||||
|
||||
@@ -29,12 +29,16 @@
|
||||
#include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
#include "nghttp2_mem.h"
|
||||
|
||||
#define nghttp2_min(A, B) ((A) < (B) ? (A) : (B))
|
||||
#define nghttp2_max(A, B) ((A) > (B) ? (A) : (B))
|
||||
|
||||
#define lstreq(A, B, N) ((sizeof((A)) - 1) == (N) && memcmp((A), (B), (N)) == 0)
|
||||
|
||||
/*
|
||||
* Copies 2 byte unsigned integer |n| in host byte order to |buf| in
|
||||
* network byte order.
|
||||
|
||||
@@ -28,11 +28,8 @@
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static int memeq(const void *a, const void *b, size_t n) {
|
||||
return memcmp(a, b, n) == 0;
|
||||
}
|
||||
|
||||
#define streq(A, B, N) ((sizeof((A)) - 1) == (N) && memeq((A), (B), (N)))
|
||||
#include "nghttp2_hd.h"
|
||||
#include "nghttp2_helper.h"
|
||||
|
||||
static char downcase(char c) {
|
||||
return 'A' <= c && c <= 'Z' ? (c - 'A' + 'a') : c;
|
||||
@@ -50,129 +47,7 @@ static int memieq(const void *a, const void *b, size_t n) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define strieq(A, B, N) ((sizeof((A)) - 1) == (N) && memieq((A), (B), (N)))
|
||||
|
||||
typedef enum {
|
||||
NGHTTP2_TOKEN__AUTHORITY,
|
||||
NGHTTP2_TOKEN__METHOD,
|
||||
NGHTTP2_TOKEN__PATH,
|
||||
NGHTTP2_TOKEN__SCHEME,
|
||||
NGHTTP2_TOKEN__STATUS,
|
||||
NGHTTP2_TOKEN_CONNECTION,
|
||||
NGHTTP2_TOKEN_CONTENT_LENGTH,
|
||||
NGHTTP2_TOKEN_HOST,
|
||||
NGHTTP2_TOKEN_KEEP_ALIVE,
|
||||
NGHTTP2_TOKEN_PROXY_CONNECTION,
|
||||
NGHTTP2_TOKEN_TE,
|
||||
NGHTTP2_TOKEN_TRANSFER_ENCODING,
|
||||
NGHTTP2_TOKEN_UPGRADE,
|
||||
NGHTTP2_TOKEN_MAXIDX,
|
||||
} nghttp2_token;
|
||||
|
||||
/*
|
||||
* This function was generated by genlibtokenlookup.py. Inspired by
|
||||
* h2o header lookup. https://github.com/h2o/h2o
|
||||
*/
|
||||
static int lookup_token(const uint8_t *name, size_t namelen) {
|
||||
switch (namelen) {
|
||||
case 2:
|
||||
switch (name[1]) {
|
||||
case 'e':
|
||||
if (streq("t", name, 1)) {
|
||||
return NGHTTP2_TOKEN_TE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
switch (name[3]) {
|
||||
case 't':
|
||||
if (streq("hos", name, 3)) {
|
||||
return NGHTTP2_TOKEN_HOST;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
switch (name[4]) {
|
||||
case 'h':
|
||||
if (streq(":pat", name, 4)) {
|
||||
return NGHTTP2_TOKEN__PATH;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
switch (name[6]) {
|
||||
case 'd':
|
||||
if (streq(":metho", name, 6)) {
|
||||
return NGHTTP2_TOKEN__METHOD;
|
||||
}
|
||||
break;
|
||||
case 'e':
|
||||
if (streq(":schem", name, 6)) {
|
||||
return NGHTTP2_TOKEN__SCHEME;
|
||||
}
|
||||
if (streq("upgrad", name, 6)) {
|
||||
return NGHTTP2_TOKEN_UPGRADE;
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
if (streq(":statu", name, 6)) {
|
||||
return NGHTTP2_TOKEN__STATUS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 10:
|
||||
switch (name[9]) {
|
||||
case 'e':
|
||||
if (streq("keep-aliv", name, 9)) {
|
||||
return NGHTTP2_TOKEN_KEEP_ALIVE;
|
||||
}
|
||||
break;
|
||||
case 'n':
|
||||
if (streq("connectio", name, 9)) {
|
||||
return NGHTTP2_TOKEN_CONNECTION;
|
||||
}
|
||||
break;
|
||||
case 'y':
|
||||
if (streq(":authorit", name, 9)) {
|
||||
return NGHTTP2_TOKEN__AUTHORITY;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 14:
|
||||
switch (name[13]) {
|
||||
case 'h':
|
||||
if (streq("content-lengt", name, 13)) {
|
||||
return NGHTTP2_TOKEN_CONTENT_LENGTH;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 16:
|
||||
switch (name[15]) {
|
||||
case 'n':
|
||||
if (streq("proxy-connectio", name, 15)) {
|
||||
return NGHTTP2_TOKEN_PROXY_CONNECTION;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 17:
|
||||
switch (name[16]) {
|
||||
case 'g':
|
||||
if (streq("transfer-encodin", name, 16)) {
|
||||
return NGHTTP2_TOKEN_TRANSFER_ENCODING;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
#define lstrieq(A, B, N) ((sizeof((A)) - 1) == (N) && memieq((A), (B), (N)))
|
||||
|
||||
static int64_t parse_uint(const uint8_t *s, size_t len) {
|
||||
int64_t n = 0;
|
||||
@@ -238,9 +113,7 @@ static int check_path(nghttp2_stream *stream) {
|
||||
}
|
||||
|
||||
static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
|
||||
int trailer) {
|
||||
int token;
|
||||
|
||||
int token, int trailer) {
|
||||
if (nv->name[0] == ':') {
|
||||
if (trailer ||
|
||||
(stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
|
||||
@@ -248,8 +121,6 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
|
||||
}
|
||||
}
|
||||
|
||||
token = lookup_token(nv->name, nv->namelen);
|
||||
|
||||
switch (token) {
|
||||
case NGHTTP2_TOKEN__AUTHORITY:
|
||||
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__AUTHORITY)) {
|
||||
@@ -262,14 +133,14 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
|
||||
}
|
||||
switch (nv->valuelen) {
|
||||
case 4:
|
||||
if (streq("HEAD", nv->value, nv->valuelen)) {
|
||||
if (lstreq("HEAD", nv->value, nv->valuelen)) {
|
||||
stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
switch (nv->value[6]) {
|
||||
case 'T':
|
||||
if (streq("CONNECT", nv->value, nv->valuelen)) {
|
||||
if (lstreq("CONNECT", nv->value, nv->valuelen)) {
|
||||
if (stream->stream_id % 2 == 0) {
|
||||
/* we won't allow CONNECT for push */
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
@@ -282,7 +153,7 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
|
||||
}
|
||||
break;
|
||||
case 'S':
|
||||
if (streq("OPTIONS", nv->value, nv->valuelen)) {
|
||||
if (lstreq("OPTIONS", nv->value, nv->valuelen)) {
|
||||
stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_OPTIONS;
|
||||
}
|
||||
break;
|
||||
@@ -338,7 +209,7 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
|
||||
case NGHTTP2_TOKEN_UPGRADE:
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
case NGHTTP2_TOKEN_TE:
|
||||
if (!strieq("trailers", nv->value, nv->valuelen)) {
|
||||
if (!lstrieq("trailers", nv->value, nv->valuelen)) {
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
}
|
||||
break;
|
||||
@@ -356,9 +227,7 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
|
||||
}
|
||||
|
||||
static int http_response_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
|
||||
int trailer) {
|
||||
int token;
|
||||
|
||||
int token, int trailer) {
|
||||
if (nv->name[0] == ':') {
|
||||
if (trailer ||
|
||||
(stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
|
||||
@@ -366,8 +235,6 @@ static int http_response_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
|
||||
}
|
||||
}
|
||||
|
||||
token = lookup_token(nv->name, nv->namelen);
|
||||
|
||||
switch (token) {
|
||||
case NGHTTP2_TOKEN__STATUS: {
|
||||
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__STATUS)) {
|
||||
@@ -400,7 +267,7 @@ static int http_response_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
|
||||
case NGHTTP2_TOKEN_UPGRADE:
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
case NGHTTP2_TOKEN_TE:
|
||||
if (!strieq("trailers", nv->value, nv->valuelen)) {
|
||||
if (!lstrieq("trailers", nv->value, nv->valuelen)) {
|
||||
return NGHTTP2_ERR_HTTP_HEADER;
|
||||
}
|
||||
break;
|
||||
@@ -418,7 +285,8 @@ static int http_response_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
|
||||
}
|
||||
|
||||
int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
|
||||
nghttp2_frame *frame, nghttp2_nv *nv, int trailer) {
|
||||
nghttp2_frame *frame, nghttp2_nv *nv, int token,
|
||||
int trailer) {
|
||||
/* We are strict for pseudo header field. One bad character should
|
||||
lead to fail. OTOH, we should be a bit forgiving for regular
|
||||
headers, since existing public internet has so much illegal
|
||||
@@ -458,10 +326,10 @@ int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
|
||||
}
|
||||
|
||||
if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
|
||||
return http_request_on_header(stream, nv, trailer);
|
||||
return http_request_on_header(stream, nv, token, trailer);
|
||||
}
|
||||
|
||||
return http_response_on_header(stream, nv, trailer);
|
||||
return http_response_on_header(stream, nv, token, trailer);
|
||||
}
|
||||
|
||||
int nghttp2_http_on_request_headers(nghttp2_stream *stream,
|
||||
@@ -574,14 +442,15 @@ void nghttp2_http_record_request_method(nghttp2_stream *stream,
|
||||
/* TODO we should do this strictly. */
|
||||
for (i = 0; i < nvlen; ++i) {
|
||||
const nghttp2_nv *nv = &nva[i];
|
||||
if (lookup_token(nv->name, nv->namelen) != NGHTTP2_TOKEN__METHOD) {
|
||||
if (!(nv->namelen == 7 && nv->name[6] == 'd' &&
|
||||
memcmp(":metho", nv->name, nv->namelen - 1) == 0)) {
|
||||
continue;
|
||||
}
|
||||
if (streq("CONNECT", nv->value, nv->valuelen)) {
|
||||
if (lstreq("CONNECT", nv->value, nv->valuelen)) {
|
||||
stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
|
||||
return;
|
||||
}
|
||||
if (streq("HEAD", nv->value, nv->valuelen)) {
|
||||
if (lstreq("HEAD", nv->value, nv->valuelen)) {
|
||||
stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -36,7 +36,8 @@
|
||||
/*
|
||||
* This function is called when HTTP header field |nv| in |frame| is
|
||||
* received for |stream|. This function will validate |nv| against
|
||||
* the current state of stream.
|
||||
* the current state of stream. The |token| is nghttp2_token value
|
||||
* for nv->name, or -1 if we don't have enum value for the name.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
@@ -48,7 +49,8 @@
|
||||
* if it was not received because of compatibility reasons.
|
||||
*/
|
||||
int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
|
||||
nghttp2_frame *frame, nghttp2_nv *nv, int trailer);
|
||||
nghttp2_frame *frame, nghttp2_nv *nv, int token,
|
||||
int trailer);
|
||||
|
||||
/*
|
||||
* This function is called when request header is received. This
|
||||
|
||||
@@ -39,7 +39,8 @@
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
typedef int (*nghttp2_compar)(const void *lhs, const void *rhs);
|
||||
/* "less" function, return nonzero if |lhs| is less than |rhs|. */
|
||||
typedef int (*nghttp2_less)(const void *lhs, const void *rhs);
|
||||
|
||||
/* Internal error code. They must be in the range [-499, -100],
|
||||
inclusive. */
|
||||
|
||||
@@ -58,7 +58,7 @@ typedef enum {
|
||||
*/
|
||||
NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS = 1 << 1,
|
||||
NGHTTP2_OPT_RECV_CLIENT_PREFACE = 1 << 2,
|
||||
NGHTTP2_OPT_NO_HTTP_MESSAGING = 1 << 3,
|
||||
NGHTTP2_OPT_NO_HTTP_MESSAGING = 1 << 3
|
||||
} nghttp2_option_flag;
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,6 +25,15 @@
|
||||
#include "nghttp2_outbound_item.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
void nghttp2_outbound_item_init(nghttp2_outbound_item *item) {
|
||||
item->cycle = 0;
|
||||
item->qnext = NULL;
|
||||
item->queued = 0;
|
||||
|
||||
memset(&item->aux_data, 0, sizeof(nghttp2_aux_data));
|
||||
}
|
||||
|
||||
void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem) {
|
||||
nghttp2_frame *frame;
|
||||
@@ -65,3 +74,32 @@ void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void nghttp2_outbound_queue_init(nghttp2_outbound_queue *q) {
|
||||
q->head = q->tail = NULL;
|
||||
q->n = 0;
|
||||
}
|
||||
|
||||
void nghttp2_outbound_queue_push(nghttp2_outbound_queue *q,
|
||||
nghttp2_outbound_item *item) {
|
||||
if (q->tail) {
|
||||
q->tail = q->tail->qnext = item;
|
||||
} else {
|
||||
q->head = q->tail = item;
|
||||
}
|
||||
++q->n;
|
||||
}
|
||||
|
||||
void nghttp2_outbound_queue_pop(nghttp2_outbound_queue *q) {
|
||||
nghttp2_outbound_item *item;
|
||||
if (!q->head) {
|
||||
return;
|
||||
}
|
||||
item = q->head;
|
||||
q->head = q->head->qnext;
|
||||
item->qnext = NULL;
|
||||
if (!q->head) {
|
||||
q->tail = NULL;
|
||||
}
|
||||
--q->n;
|
||||
}
|
||||
|
||||
@@ -33,13 +33,6 @@
|
||||
#include "nghttp2_frame.h"
|
||||
#include "nghttp2_mem.h"
|
||||
|
||||
/* A bit higher weight for non-DATA frames */
|
||||
#define NGHTTP2_OB_EX_WEIGHT 300
|
||||
/* Higher weight for SETTINGS */
|
||||
#define NGHTTP2_OB_SETTINGS_WEIGHT 301
|
||||
/* Highest weight for PING */
|
||||
#define NGHTTP2_OB_PING_WEIGHT 302
|
||||
|
||||
/* struct used for HEADERS and PUSH_PROMISE frame */
|
||||
typedef struct {
|
||||
nghttp2_data_provider data_prd;
|
||||
@@ -88,7 +81,7 @@ typedef enum {
|
||||
/* indicates that this GOAWAY is just a notification for graceful
|
||||
shutdown. No nghttp2_session.goaway_flags should be updated on
|
||||
the reaction to this frame. */
|
||||
NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE = 0x2,
|
||||
NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE = 0x2
|
||||
} nghttp2_goaway_aux_flag;
|
||||
|
||||
/* struct used for GOAWAY frame */
|
||||
@@ -104,19 +97,31 @@ typedef union {
|
||||
nghttp2_goaway_aux_data goaway;
|
||||
} nghttp2_aux_data;
|
||||
|
||||
typedef struct {
|
||||
struct nghttp2_outbound_item;
|
||||
typedef struct nghttp2_outbound_item nghttp2_outbound_item;
|
||||
|
||||
struct nghttp2_outbound_item {
|
||||
nghttp2_frame frame;
|
||||
nghttp2_aux_data aux_data;
|
||||
int64_t seq;
|
||||
/* Reset count of weight. See comment for last_cycle in
|
||||
nghttp2_session.h */
|
||||
/* The priority used in priority comparion. Smaller is served
|
||||
ealier. For PING, SETTINGS and non-DATA frames (excluding
|
||||
response HEADERS frame) have dedicated cycle value defined above.
|
||||
For DATA frame, cycle is computed by taking into account of
|
||||
effective weight and frame payload length previously sent, so
|
||||
that the amount of transmission is distributed across streams
|
||||
proportional to effective weight (inside a tree). */
|
||||
uint64_t cycle;
|
||||
/* The priority used in priority comparion. Larger is served
|
||||
ealier. */
|
||||
int32_t weight;
|
||||
nghttp2_outbound_item *qnext;
|
||||
/* nonzero if this object is queued. */
|
||||
uint8_t queued;
|
||||
} nghttp2_outbound_item;
|
||||
};
|
||||
|
||||
/*
|
||||
* Initializes |item|. No memory allocation is done in this function.
|
||||
* Don't call nghttp2_outbound_item_free() until frame member is
|
||||
* initialized.
|
||||
*/
|
||||
void nghttp2_outbound_item_init(nghttp2_outbound_item *item);
|
||||
|
||||
/*
|
||||
* Deallocates resource for |item|. If |item| is NULL, this function
|
||||
@@ -124,4 +129,29 @@ typedef struct {
|
||||
*/
|
||||
void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem);
|
||||
|
||||
/*
|
||||
* queue for nghttp2_outbound_item.
|
||||
*/
|
||||
typedef struct {
|
||||
nghttp2_outbound_item *head, *tail;
|
||||
/* number of items in this queue. */
|
||||
size_t n;
|
||||
} nghttp2_outbound_queue;
|
||||
|
||||
void nghttp2_outbound_queue_init(nghttp2_outbound_queue *q);
|
||||
|
||||
/* Pushes |item| into |q| */
|
||||
void nghttp2_outbound_queue_push(nghttp2_outbound_queue *q,
|
||||
nghttp2_outbound_item *item);
|
||||
|
||||
/* Pops |item| at the top from |q|. If |q| is empty, nothing
|
||||
happens. */
|
||||
void nghttp2_outbound_queue_pop(nghttp2_outbound_queue *q);
|
||||
|
||||
/* Returns the top item. */
|
||||
#define nghttp2_outbound_queue_top(Q) ((Q)->head)
|
||||
|
||||
/* Returns the size of the queue */
|
||||
#define nghttp2_outbound_queue_size(Q) ((Q)->n)
|
||||
|
||||
#endif /* NGHTTP2_OUTBOUND_ITEM_H */
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
*/
|
||||
#include "nghttp2_pq.h"
|
||||
|
||||
int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_compar compar, nghttp2_mem *mem) {
|
||||
int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_less less, nghttp2_mem *mem) {
|
||||
pq->mem = mem;
|
||||
pq->capacity = 128;
|
||||
pq->q = nghttp2_mem_malloc(mem, pq->capacity * sizeof(void *));
|
||||
@@ -32,7 +32,7 @@ int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_compar compar, nghttp2_mem *mem) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
pq->length = 0;
|
||||
pq->compar = compar;
|
||||
pq->less = less;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ static void bubble_up(nghttp2_pq *pq, size_t index) {
|
||||
return;
|
||||
} else {
|
||||
size_t parent = (index - 1) / 2;
|
||||
if (pq->compar(pq->q[parent], pq->q[index]) > 0) {
|
||||
if (pq->less(pq->q[index], pq->q[parent])) {
|
||||
swap(pq, parent, index);
|
||||
bubble_up(pq, parent);
|
||||
}
|
||||
@@ -93,7 +93,7 @@ static void bubble_down(nghttp2_pq *pq, size_t index) {
|
||||
if (j >= pq->length) {
|
||||
break;
|
||||
}
|
||||
if (pq->compar(pq->q[minindex], pq->q[j]) > 0) {
|
||||
if (pq->less(pq->q[j], pq->q[minindex])) {
|
||||
minindex = j;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,8 +45,8 @@ typedef struct {
|
||||
/* The maximum number of items this pq can store. This is
|
||||
automatically extended when length is reached to this value. */
|
||||
size_t capacity;
|
||||
/* The compare function between items */
|
||||
nghttp2_compar compar;
|
||||
/* The less function between items */
|
||||
nghttp2_less less;
|
||||
} nghttp2_pq;
|
||||
|
||||
/*
|
||||
@@ -58,7 +58,7 @@ typedef struct {
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*/
|
||||
int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_compar cmp, nghttp2_mem *mem);
|
||||
int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_less less, nghttp2_mem *mem);
|
||||
|
||||
/*
|
||||
* Deallocates any resources allocated for |pq|. The stored items are
|
||||
|
||||
@@ -221,22 +221,13 @@ nghttp2_stream *nghttp2_session_get_stream_raw(nghttp2_session *session,
|
||||
return (nghttp2_stream *)nghttp2_map_find(&session->streams, stream_id);
|
||||
}
|
||||
|
||||
static int outbound_item_compar(const void *lhsx, const void *rhsx) {
|
||||
static int outbound_item_less(const void *lhsx, const void *rhsx) {
|
||||
const nghttp2_outbound_item *lhs, *rhs;
|
||||
|
||||
lhs = (const nghttp2_outbound_item *)lhsx;
|
||||
rhs = (const nghttp2_outbound_item *)rhsx;
|
||||
|
||||
if (lhs->cycle == rhs->cycle) {
|
||||
if (lhs->weight == rhs->weight) {
|
||||
return (lhs->seq < rhs->seq) ? -1 : ((lhs->seq > rhs->seq) ? 1 : 0);
|
||||
}
|
||||
|
||||
/* Larger weight has higher precedence */
|
||||
return rhs->weight - lhs->weight;
|
||||
}
|
||||
|
||||
return (lhs->cycle < rhs->cycle) ? -1 : 1;
|
||||
return (lhs->cycle < rhs->cycle) ? 1 : 0;
|
||||
}
|
||||
|
||||
static void session_inbound_frame_reset(nghttp2_session *session) {
|
||||
@@ -338,15 +329,7 @@ static int session_new(nghttp2_session **session_ptr,
|
||||
/* next_stream_id is initialized in either
|
||||
nghttp2_session_client_new2 or nghttp2_session_server_new2 */
|
||||
|
||||
rv = nghttp2_pq_init(&(*session_ptr)->ob_pq, outbound_item_compar, mem);
|
||||
if (rv != 0) {
|
||||
goto fail_ob_pq;
|
||||
}
|
||||
rv = nghttp2_pq_init(&(*session_ptr)->ob_ss_pq, outbound_item_compar, mem);
|
||||
if (rv != 0) {
|
||||
goto fail_ob_ss_pq;
|
||||
}
|
||||
rv = nghttp2_pq_init(&(*session_ptr)->ob_da_pq, outbound_item_compar, mem);
|
||||
rv = nghttp2_pq_init(&(*session_ptr)->ob_da_pq, outbound_item_less, mem);
|
||||
if (rv != 0) {
|
||||
goto fail_ob_da_pq;
|
||||
}
|
||||
@@ -366,9 +349,6 @@ static int session_new(nghttp2_session **session_ptr,
|
||||
|
||||
nghttp2_stream_roots_init(&(*session_ptr)->roots);
|
||||
|
||||
(*session_ptr)->next_seq = 0;
|
||||
(*session_ptr)->last_cycle = 1;
|
||||
|
||||
(*session_ptr)->remote_window_size = NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE;
|
||||
(*session_ptr)->recv_window_size = 0;
|
||||
(*session_ptr)->consumed_size = 0;
|
||||
@@ -457,10 +437,6 @@ fail_hd_inflater:
|
||||
fail_hd_deflater:
|
||||
nghttp2_pq_free(&(*session_ptr)->ob_da_pq);
|
||||
fail_ob_da_pq:
|
||||
nghttp2_pq_free(&(*session_ptr)->ob_ss_pq);
|
||||
fail_ob_ss_pq:
|
||||
nghttp2_pq_free(&(*session_ptr)->ob_pq);
|
||||
fail_ob_pq:
|
||||
nghttp2_mem_free(mem, *session_ptr);
|
||||
fail_session:
|
||||
return rv;
|
||||
@@ -566,6 +542,16 @@ static void ob_pq_free(nghttp2_pq *pq, nghttp2_mem *mem) {
|
||||
nghttp2_pq_free(pq);
|
||||
}
|
||||
|
||||
static void ob_q_free(nghttp2_outbound_queue *q, nghttp2_mem *mem) {
|
||||
nghttp2_outbound_item *item, *next;
|
||||
for (item = q->head; item;) {
|
||||
next = item->qnext;
|
||||
nghttp2_outbound_item_free(item, mem);
|
||||
nghttp2_mem_free(mem, item);
|
||||
item = next;
|
||||
}
|
||||
}
|
||||
|
||||
void nghttp2_session_del(nghttp2_session *session) {
|
||||
nghttp2_mem *mem;
|
||||
|
||||
@@ -584,8 +570,9 @@ void nghttp2_session_del(nghttp2_session *session) {
|
||||
nghttp2_map_each_free(&session->streams, free_streams, session);
|
||||
nghttp2_map_free(&session->streams);
|
||||
|
||||
ob_pq_free(&session->ob_pq, mem);
|
||||
ob_pq_free(&session->ob_ss_pq, mem);
|
||||
ob_q_free(&session->ob_urgent, mem);
|
||||
ob_q_free(&session->ob_reg, mem);
|
||||
ob_q_free(&session->ob_syn, mem);
|
||||
ob_pq_free(&session->ob_da_pq, mem);
|
||||
active_outbound_item_reset(&session->aob, mem);
|
||||
session_inbound_frame_reset(session);
|
||||
@@ -691,17 +678,6 @@ nghttp2_session_reprioritize_stream(nghttp2_session *session,
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nghttp2_session_outbound_item_init(nghttp2_session *session,
|
||||
nghttp2_outbound_item *item) {
|
||||
item->seq = session->next_seq++;
|
||||
/* We use cycle for DATA only */
|
||||
item->cycle = 0;
|
||||
item->weight = NGHTTP2_OB_EX_WEIGHT;
|
||||
item->queued = 0;
|
||||
|
||||
memset(&item->aux_data, 0, sizeof(nghttp2_aux_data));
|
||||
}
|
||||
|
||||
int nghttp2_session_add_item(nghttp2_session *session,
|
||||
nghttp2_outbound_item *item) {
|
||||
/* TODO Return error if stream is not found for the frame requiring
|
||||
@@ -716,66 +692,45 @@ int nghttp2_session_add_item(nghttp2_session *session,
|
||||
if (frame->hd.type != NGHTTP2_DATA) {
|
||||
|
||||
switch (frame->hd.type) {
|
||||
case NGHTTP2_RST_STREAM:
|
||||
if (stream) {
|
||||
stream->state = NGHTTP2_STREAM_CLOSING;
|
||||
}
|
||||
|
||||
break;
|
||||
case NGHTTP2_SETTINGS:
|
||||
item->weight = NGHTTP2_OB_SETTINGS_WEIGHT;
|
||||
|
||||
break;
|
||||
case NGHTTP2_PING:
|
||||
/* Ping has highest priority. */
|
||||
item->weight = NGHTTP2_OB_PING_WEIGHT;
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (frame->hd.type == NGHTTP2_HEADERS) {
|
||||
case NGHTTP2_HEADERS:
|
||||
/* We push request HEADERS and push response HEADERS to
|
||||
dedicated queue because their transmission is affected by
|
||||
SETTINGS_MAX_CONCURRENT_STREAMS */
|
||||
/* TODO If 2 HEADERS are submitted for reserved stream, then
|
||||
both of them are queued into ob_ss_pq, which is not
|
||||
both of them are queued into ob_syn, which is not
|
||||
desirable. */
|
||||
if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
|
||||
rv = nghttp2_pq_push(&session->ob_ss_pq, item);
|
||||
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nghttp2_outbound_queue_push(&session->ob_syn, item);
|
||||
item->queued = 1;
|
||||
} else if (stream && (stream->state == NGHTTP2_STREAM_RESERVED ||
|
||||
item->aux_data.headers.attach_stream)) {
|
||||
item->weight = stream->effective_weight;
|
||||
item->cycle = session->last_cycle;
|
||||
break;
|
||||
}
|
||||
|
||||
if (stream && (stream->state == NGHTTP2_STREAM_RESERVED ||
|
||||
item->aux_data.headers.attach_stream)) {
|
||||
rv = nghttp2_stream_attach_item(stream, item, session);
|
||||
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
} else {
|
||||
rv = nghttp2_pq_push(&session->ob_pq, item);
|
||||
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
item->queued = 1;
|
||||
}
|
||||
} else {
|
||||
rv = nghttp2_pq_push(&session->ob_pq, item);
|
||||
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
break;
|
||||
}
|
||||
|
||||
nghttp2_outbound_queue_push(&session->ob_reg, item);
|
||||
item->queued = 1;
|
||||
break;
|
||||
case NGHTTP2_SETTINGS:
|
||||
case NGHTTP2_PING:
|
||||
nghttp2_outbound_queue_push(&session->ob_urgent, item);
|
||||
item->queued = 1;
|
||||
break;
|
||||
case NGHTTP2_RST_STREAM:
|
||||
if (stream) {
|
||||
stream->state = NGHTTP2_STREAM_CLOSING;
|
||||
}
|
||||
/* fall through */
|
||||
default:
|
||||
nghttp2_outbound_queue_push(&session->ob_reg, item);
|
||||
item->queued = 1;
|
||||
}
|
||||
|
||||
@@ -790,9 +745,6 @@ int nghttp2_session_add_item(nghttp2_session *session,
|
||||
return NGHTTP2_ERR_DATA_EXIST;
|
||||
}
|
||||
|
||||
item->weight = stream->effective_weight;
|
||||
item->cycle = session->last_cycle;
|
||||
|
||||
rv = nghttp2_stream_attach_item(stream, item, session);
|
||||
|
||||
if (rv != 0) {
|
||||
@@ -802,30 +754,6 @@ int nghttp2_session_add_item(nghttp2_session *session,
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int32_t stream_id;
|
||||
uint32_t error_code;
|
||||
} nghttp2_rst_target;
|
||||
|
||||
static int cancel_pending_request(void *pq_item, void *arg) {
|
||||
nghttp2_outbound_item *item;
|
||||
nghttp2_rst_target *t;
|
||||
nghttp2_headers_aux_data *aux_data;
|
||||
|
||||
item = pq_item;
|
||||
t = arg;
|
||||
aux_data = &item->aux_data.headers;
|
||||
|
||||
if (item->frame.hd.stream_id != t->stream_id || aux_data->canceled) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
aux_data->error_code = t->error_code;
|
||||
aux_data->canceled = 1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id,
|
||||
uint32_t error_code) {
|
||||
int rv;
|
||||
@@ -833,7 +761,6 @@ int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id,
|
||||
nghttp2_frame *frame;
|
||||
nghttp2_stream *stream;
|
||||
nghttp2_mem *mem;
|
||||
nghttp2_rst_target t = {stream_id, error_code};
|
||||
|
||||
mem = &session->mem;
|
||||
stream = nghttp2_session_get_stream(session, stream_id);
|
||||
@@ -841,21 +768,35 @@ int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Cancel pending request HEADERS in ob_ss_pq if this RST_STREAM
|
||||
/* Cancel pending request HEADERS in ob_syn if this RST_STREAM
|
||||
refers to that stream. */
|
||||
if (!session->server && nghttp2_session_is_my_stream_id(session, stream_id) &&
|
||||
nghttp2_pq_top(&session->ob_ss_pq)) {
|
||||
nghttp2_outbound_item *top;
|
||||
nghttp2_outbound_queue_top(&session->ob_syn)) {
|
||||
nghttp2_headers_aux_data *aux_data;
|
||||
nghttp2_frame *headers_frame;
|
||||
|
||||
top = nghttp2_pq_top(&session->ob_ss_pq);
|
||||
headers_frame = &top->frame;
|
||||
|
||||
headers_frame = &nghttp2_outbound_queue_top(&session->ob_syn)->frame;
|
||||
assert(headers_frame->hd.type == NGHTTP2_HEADERS);
|
||||
|
||||
if (headers_frame->hd.stream_id <= stream_id &&
|
||||
(uint32_t)stream_id < session->next_stream_id) {
|
||||
if (nghttp2_pq_each(&session->ob_ss_pq, cancel_pending_request, &t)) {
|
||||
|
||||
for (item = session->ob_syn.head; item; item = item->qnext) {
|
||||
aux_data = &item->aux_data.headers;
|
||||
|
||||
if (item->frame.hd.stream_id < stream_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* stream_id in ob_syn queue must be strictly increasing. If
|
||||
we found larger ID, then we can break here. */
|
||||
if (item->frame.hd.stream_id > stream_id || aux_data->canceled) {
|
||||
break;
|
||||
}
|
||||
|
||||
aux_data->error_code = error_code;
|
||||
aux_data->canceled = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -866,7 +807,7 @@ int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id,
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
nghttp2_session_outbound_item_init(session, item);
|
||||
nghttp2_outbound_item_init(item);
|
||||
|
||||
frame = &item->frame;
|
||||
|
||||
@@ -1986,7 +1927,8 @@ static int session_prep_frame(nghttp2_session *session,
|
||||
}
|
||||
|
||||
rv = nghttp2_session_pack_data(session, &session->aob.framebufs,
|
||||
next_readmax, frame, &item->aux_data.data);
|
||||
next_readmax, frame, &item->aux_data.data,
|
||||
stream);
|
||||
if (rv == NGHTTP2_ERR_DEFERRED) {
|
||||
rv = nghttp2_stream_defer_item(stream, NGHTTP2_STREAM_FLAG_DEFERRED_USER,
|
||||
session);
|
||||
@@ -2028,125 +1970,66 @@ static int session_prep_frame(nghttp2_session *session,
|
||||
}
|
||||
}
|
||||
|
||||
/* Used only for tests */
|
||||
nghttp2_outbound_item *nghttp2_session_get_ob_pq_top(nghttp2_session *session) {
|
||||
return (nghttp2_outbound_item *)nghttp2_pq_top(&session->ob_pq);
|
||||
}
|
||||
|
||||
nghttp2_outbound_item *
|
||||
nghttp2_session_get_next_ob_item(nghttp2_session *session) {
|
||||
nghttp2_outbound_item *item, *headers_item;
|
||||
if (nghttp2_outbound_queue_top(&session->ob_urgent)) {
|
||||
return nghttp2_outbound_queue_top(&session->ob_urgent);
|
||||
}
|
||||
|
||||
if (nghttp2_pq_empty(&session->ob_pq)) {
|
||||
if (nghttp2_pq_empty(&session->ob_ss_pq)) {
|
||||
if (session->remote_window_size == 0 ||
|
||||
nghttp2_pq_empty(&session->ob_da_pq)) {
|
||||
return NULL;
|
||||
}
|
||||
if (nghttp2_outbound_queue_top(&session->ob_reg)) {
|
||||
return nghttp2_outbound_queue_top(&session->ob_reg);
|
||||
}
|
||||
|
||||
return nghttp2_pq_top(&session->ob_da_pq);
|
||||
if (!session_is_outgoing_concurrent_streams_max(session)) {
|
||||
if (nghttp2_outbound_queue_top(&session->ob_syn)) {
|
||||
return nghttp2_outbound_queue_top(&session->ob_syn);
|
||||
}
|
||||
|
||||
/* Return item only when concurrent connection limit is not
|
||||
reached */
|
||||
if (session_is_outgoing_concurrent_streams_max(session)) {
|
||||
if (session->remote_window_size == 0 ||
|
||||
nghttp2_pq_empty(&session->ob_da_pq)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return nghttp2_pq_top(&session->ob_da_pq);
|
||||
}
|
||||
|
||||
return nghttp2_pq_top(&session->ob_ss_pq);
|
||||
}
|
||||
|
||||
if (nghttp2_pq_empty(&session->ob_ss_pq)) {
|
||||
return nghttp2_pq_top(&session->ob_pq);
|
||||
if (session->remote_window_size > 0 &&
|
||||
!nghttp2_pq_empty(&session->ob_da_pq)) {
|
||||
return nghttp2_pq_top(&session->ob_da_pq);
|
||||
}
|
||||
|
||||
item = nghttp2_pq_top(&session->ob_pq);
|
||||
headers_item = nghttp2_pq_top(&session->ob_ss_pq);
|
||||
|
||||
if (session_is_outgoing_concurrent_streams_max(session) ||
|
||||
item->weight > headers_item->weight ||
|
||||
(item->weight == headers_item->weight && item->seq < headers_item->seq)) {
|
||||
return item;
|
||||
}
|
||||
|
||||
return headers_item;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
nghttp2_outbound_item *
|
||||
nghttp2_session_pop_next_ob_item(nghttp2_session *session) {
|
||||
nghttp2_outbound_item *item, *headers_item;
|
||||
nghttp2_outbound_item *item;
|
||||
|
||||
if (nghttp2_pq_empty(&session->ob_pq)) {
|
||||
if (nghttp2_pq_empty(&session->ob_ss_pq)) {
|
||||
if (session->remote_window_size == 0 ||
|
||||
nghttp2_pq_empty(&session->ob_da_pq)) {
|
||||
return NULL;
|
||||
}
|
||||
item = nghttp2_outbound_queue_top(&session->ob_urgent);
|
||||
if (item) {
|
||||
nghttp2_outbound_queue_pop(&session->ob_urgent);
|
||||
item->queued = 0;
|
||||
return item;
|
||||
}
|
||||
|
||||
item = nghttp2_pq_top(&session->ob_da_pq);
|
||||
nghttp2_pq_pop(&session->ob_da_pq);
|
||||
item = nghttp2_outbound_queue_top(&session->ob_reg);
|
||||
if (item) {
|
||||
nghttp2_outbound_queue_pop(&session->ob_reg);
|
||||
item->queued = 0;
|
||||
return item;
|
||||
}
|
||||
|
||||
if (!session_is_outgoing_concurrent_streams_max(session)) {
|
||||
item = nghttp2_outbound_queue_top(&session->ob_syn);
|
||||
if (item) {
|
||||
nghttp2_outbound_queue_pop(&session->ob_syn);
|
||||
item->queued = 0;
|
||||
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
/* Pop item only when concurrent connection limit is not
|
||||
reached */
|
||||
if (session_is_outgoing_concurrent_streams_max(session)) {
|
||||
if (session->remote_window_size == 0 ||
|
||||
nghttp2_pq_empty(&session->ob_da_pq)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
item = nghttp2_pq_top(&session->ob_da_pq);
|
||||
nghttp2_pq_pop(&session->ob_da_pq);
|
||||
|
||||
item->queued = 0;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
item = nghttp2_pq_top(&session->ob_ss_pq);
|
||||
nghttp2_pq_pop(&session->ob_ss_pq);
|
||||
|
||||
if (session->remote_window_size > 0 &&
|
||||
!nghttp2_pq_empty(&session->ob_da_pq)) {
|
||||
item = nghttp2_pq_top(&session->ob_da_pq);
|
||||
nghttp2_pq_pop(&session->ob_da_pq);
|
||||
item->queued = 0;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
if (nghttp2_pq_empty(&session->ob_ss_pq)) {
|
||||
item = nghttp2_pq_top(&session->ob_pq);
|
||||
nghttp2_pq_pop(&session->ob_pq);
|
||||
|
||||
item->queued = 0;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
item = nghttp2_pq_top(&session->ob_pq);
|
||||
headers_item = nghttp2_pq_top(&session->ob_ss_pq);
|
||||
|
||||
if (session_is_outgoing_concurrent_streams_max(session) ||
|
||||
item->weight > headers_item->weight ||
|
||||
(item->weight == headers_item->weight && item->seq < headers_item->seq)) {
|
||||
nghttp2_pq_pop(&session->ob_pq);
|
||||
|
||||
item->queued = 0;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
nghttp2_pq_pop(&session->ob_ss_pq);
|
||||
|
||||
headers_item->queued = 0;
|
||||
|
||||
return headers_item;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int session_call_before_frame_send(nghttp2_session *session,
|
||||
@@ -2249,21 +2132,21 @@ static int session_close_stream_on_goaway(nghttp2_session *session,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void session_outbound_item_cycle_weight(nghttp2_session *session,
|
||||
nghttp2_outbound_item *item,
|
||||
int32_t ini_weight) {
|
||||
if (item->weight == NGHTTP2_MIN_WEIGHT || item->weight > ini_weight) {
|
||||
static void session_outbound_item_schedule(nghttp2_session *session,
|
||||
nghttp2_outbound_item *item,
|
||||
int32_t weight) {
|
||||
/* Schedule next write. Offset proportional to the write size.
|
||||
Stream with heavier weight is scheduled earlier. */
|
||||
size_t delta = item->frame.hd.length * NGHTTP2_MAX_WEIGHT / weight;
|
||||
|
||||
item->weight = ini_weight;
|
||||
|
||||
if (item->cycle == session->last_cycle) {
|
||||
item->cycle = ++session->last_cycle;
|
||||
} else {
|
||||
item->cycle = session->last_cycle;
|
||||
}
|
||||
} else {
|
||||
--item->weight;
|
||||
if (session->last_cycle < item->cycle) {
|
||||
session->last_cycle = item->cycle;
|
||||
}
|
||||
|
||||
/* We pretend to ignore overflow given that the value range of
|
||||
item->cycle, which is uint64_t. nghttp2 won't explode even when
|
||||
overflow occurs, there might be some disturbance of priority. */
|
||||
item->cycle = session->last_cycle + delta;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2583,20 +2466,12 @@ static int session_after_frame_sent2(nghttp2_session *session) {
|
||||
assert(stream);
|
||||
next_item = nghttp2_session_get_next_ob_item(session);
|
||||
|
||||
/* Imagine we hit connection window size limit while sending DATA
|
||||
frame. If we decrement weight here, its stream might get
|
||||
inferior share because the other streams' weight is not
|
||||
decremented because of flow control. */
|
||||
if (session->remote_window_size > 0 || stream->remote_window_size <= 0) {
|
||||
session_outbound_item_cycle_weight(session, aob->item,
|
||||
stream->effective_weight);
|
||||
}
|
||||
|
||||
/* If priority of this stream is higher or equal to other stream
|
||||
waiting at the top of the queue, we continue to send this
|
||||
data. */
|
||||
if (stream->dpri == NGHTTP2_STREAM_DPRI_TOP &&
|
||||
(next_item == NULL || outbound_item_compar(item, next_item) < 0)) {
|
||||
(next_item == NULL || (next_item->frame.hd.type == NGHTTP2_DATA &&
|
||||
outbound_item_less(item, next_item)))) {
|
||||
size_t next_readmax;
|
||||
|
||||
next_readmax = nghttp2_session_next_data_read(session, stream);
|
||||
@@ -2634,7 +2509,7 @@ static int session_after_frame_sent2(nghttp2_session *session) {
|
||||
nghttp2_bufs_reset(framebufs);
|
||||
|
||||
rv = nghttp2_session_pack_data(session, framebufs, next_readmax, frame,
|
||||
aux_data);
|
||||
aux_data, stream);
|
||||
if (nghttp2_is_fatal(rv)) {
|
||||
return rv;
|
||||
}
|
||||
@@ -3086,6 +2961,9 @@ static int session_call_on_begin_headers(nghttp2_session *session,
|
||||
if (session->callbacks.on_begin_headers_callback) {
|
||||
rv = session->callbacks.on_begin_headers_callback(session, frame,
|
||||
session->user_data);
|
||||
if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
|
||||
return rv;
|
||||
}
|
||||
if (rv != 0) {
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
@@ -3230,6 +3108,7 @@ static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame,
|
||||
nghttp2_stream *stream;
|
||||
nghttp2_stream *subject_stream;
|
||||
int trailer = 0;
|
||||
int token;
|
||||
|
||||
*readlen_ptr = 0;
|
||||
stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
|
||||
@@ -3245,19 +3124,19 @@ static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame,
|
||||
DEBUGF(fprintf(stderr, "recv: decoding header block %zu bytes\n", inlen));
|
||||
for (;;) {
|
||||
inflate_flags = 0;
|
||||
proclen = nghttp2_hd_inflate_hd(&session->hd_inflater, &nv, &inflate_flags,
|
||||
in, inlen, final);
|
||||
proclen = nghttp2_hd_inflate_hd2(&session->hd_inflater, &nv, &inflate_flags,
|
||||
&token, in, inlen, final);
|
||||
if (nghttp2_is_fatal((int)proclen)) {
|
||||
return (int)proclen;
|
||||
}
|
||||
if (proclen < 0) {
|
||||
if (session->iframe.state == NGHTTP2_IB_READ_HEADER_BLOCK) {
|
||||
if (stream && stream->state != NGHTTP2_STREAM_CLOSING) {
|
||||
if (subject_stream && subject_stream->state != NGHTTP2_STREAM_CLOSING) {
|
||||
/* Adding RST_STREAM here is very important. It prevents
|
||||
from invoking subsequent callbacks for the same stream
|
||||
ID. */
|
||||
rv = nghttp2_session_add_rst_stream(session, frame->hd.stream_id,
|
||||
NGHTTP2_COMPRESSION_ERROR);
|
||||
rv = nghttp2_session_add_rst_stream(
|
||||
session, subject_stream->stream_id, NGHTTP2_COMPRESSION_ERROR);
|
||||
|
||||
if (nghttp2_is_fatal(rv)) {
|
||||
return rv;
|
||||
@@ -3281,7 +3160,7 @@ static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame,
|
||||
if (call_header_cb && (inflate_flags & NGHTTP2_HD_INFLATE_EMIT)) {
|
||||
rv = 0;
|
||||
if (subject_stream && session_enforce_http_messaging(session)) {
|
||||
rv = nghttp2_http_on_header(session, subject_stream, frame, &nv,
|
||||
rv = nghttp2_http_on_header(session, subject_stream, frame, &nv, token,
|
||||
trailer);
|
||||
if (rv == NGHTTP2_ERR_HTTP_HEADER) {
|
||||
DEBUGF(fprintf(
|
||||
@@ -5101,6 +4980,16 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
|
||||
|
||||
busy = 1;
|
||||
|
||||
if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
|
||||
rv = nghttp2_session_add_rst_stream(
|
||||
session, iframe->frame.hd.stream_id, NGHTTP2_INTERNAL_ERROR);
|
||||
if (nghttp2_is_fatal(rv)) {
|
||||
return rv;
|
||||
}
|
||||
iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
|
||||
break;
|
||||
}
|
||||
|
||||
if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) {
|
||||
iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
|
||||
break;
|
||||
@@ -5345,6 +5234,16 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
|
||||
|
||||
busy = 1;
|
||||
|
||||
if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
|
||||
rv = nghttp2_session_add_rst_stream(
|
||||
session, iframe->frame.hd.stream_id, NGHTTP2_INTERNAL_ERROR);
|
||||
if (nghttp2_is_fatal(rv)) {
|
||||
return rv;
|
||||
}
|
||||
iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
|
||||
break;
|
||||
}
|
||||
|
||||
if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) {
|
||||
iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
|
||||
break;
|
||||
@@ -5409,6 +5308,17 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
|
||||
|
||||
busy = 1;
|
||||
|
||||
if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
|
||||
rv = nghttp2_session_add_rst_stream(
|
||||
session, iframe->frame.push_promise.promised_stream_id,
|
||||
NGHTTP2_INTERNAL_ERROR);
|
||||
if (nghttp2_is_fatal(rv)) {
|
||||
return rv;
|
||||
}
|
||||
iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
|
||||
break;
|
||||
}
|
||||
|
||||
if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) {
|
||||
iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
|
||||
break;
|
||||
@@ -5515,8 +5425,12 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
|
||||
in += hd_proclen;
|
||||
iframe->payloadleft -= hd_proclen;
|
||||
|
||||
/* Use promised stream ID for PUSH_PROMISE */
|
||||
rv = nghttp2_session_add_rst_stream(
|
||||
session, iframe->frame.hd.stream_id, NGHTTP2_INTERNAL_ERROR);
|
||||
session, iframe->frame.hd.type == NGHTTP2_PUSH_PROMISE
|
||||
? iframe->frame.push_promise.promised_stream_id
|
||||
: iframe->frame.hd.stream_id,
|
||||
NGHTTP2_INTERNAL_ERROR);
|
||||
if (nghttp2_is_fatal(rv)) {
|
||||
return rv;
|
||||
}
|
||||
@@ -5999,10 +5913,12 @@ int nghttp2_session_want_write(nghttp2_session *session) {
|
||||
* want to write them.
|
||||
*/
|
||||
|
||||
if (session->aob.item == NULL && nghttp2_pq_empty(&session->ob_pq) &&
|
||||
if (session->aob.item == NULL &&
|
||||
nghttp2_outbound_queue_top(&session->ob_urgent) == NULL &&
|
||||
nghttp2_outbound_queue_top(&session->ob_reg) == NULL &&
|
||||
(nghttp2_pq_empty(&session->ob_da_pq) ||
|
||||
session->remote_window_size == 0) &&
|
||||
(nghttp2_pq_empty(&session->ob_ss_pq) ||
|
||||
(nghttp2_outbound_queue_top(&session->ob_syn) == NULL ||
|
||||
session_is_outgoing_concurrent_streams_max(session))) {
|
||||
return 0;
|
||||
}
|
||||
@@ -6026,7 +5942,7 @@ int nghttp2_session_add_ping(nghttp2_session *session, uint8_t flags,
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
nghttp2_session_outbound_item_init(session, item);
|
||||
nghttp2_outbound_item_init(item);
|
||||
|
||||
frame = &item->frame;
|
||||
|
||||
@@ -6075,7 +5991,7 @@ int nghttp2_session_add_goaway(nghttp2_session *session, int32_t last_stream_id,
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
nghttp2_session_outbound_item_init(session, item);
|
||||
nghttp2_outbound_item_init(item);
|
||||
|
||||
frame = &item->frame;
|
||||
|
||||
@@ -6112,7 +6028,7 @@ int nghttp2_session_add_window_update(nghttp2_session *session, uint8_t flags,
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
nghttp2_session_outbound_item_init(session, item);
|
||||
nghttp2_outbound_item_init(item);
|
||||
|
||||
frame = &item->frame;
|
||||
|
||||
@@ -6183,7 +6099,7 @@ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags,
|
||||
session->inflight_niv = niv;
|
||||
}
|
||||
|
||||
nghttp2_session_outbound_item_init(session, item);
|
||||
nghttp2_outbound_item_init(item);
|
||||
|
||||
frame = &item->frame;
|
||||
|
||||
@@ -6228,7 +6144,8 @@ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags,
|
||||
|
||||
int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs,
|
||||
size_t datamax, nghttp2_frame *frame,
|
||||
nghttp2_data_aux_data *aux_data) {
|
||||
nghttp2_data_aux_data *aux_data,
|
||||
nghttp2_stream *stream) {
|
||||
int rv;
|
||||
uint32_t data_flags;
|
||||
ssize_t payloadlen;
|
||||
@@ -6241,12 +6158,6 @@ int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs,
|
||||
buf = &bufs->cur->buf;
|
||||
|
||||
if (session->callbacks.read_length_callback) {
|
||||
nghttp2_stream *stream;
|
||||
|
||||
stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
|
||||
if (!stream) {
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
payloadlen = session->callbacks.read_length_callback(
|
||||
session, frame->hd.type, stream->stream_id, session->remote_window_size,
|
||||
@@ -6360,6 +6271,9 @@ int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs,
|
||||
return rv;
|
||||
}
|
||||
|
||||
session_outbound_item_schedule(session, stream->item,
|
||||
stream->effective_weight);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -6405,8 +6319,9 @@ int nghttp2_session_resume_data(nghttp2_session *session, int32_t stream_id) {
|
||||
}
|
||||
|
||||
size_t nghttp2_session_get_outbound_queue_size(nghttp2_session *session) {
|
||||
return nghttp2_pq_size(&session->ob_pq) +
|
||||
nghttp2_pq_size(&session->ob_ss_pq) +
|
||||
return nghttp2_outbound_queue_size(&session->ob_urgent) +
|
||||
nghttp2_outbound_queue_size(&session->ob_reg) +
|
||||
nghttp2_outbound_queue_size(&session->ob_syn) +
|
||||
nghttp2_pq_size(&session->ob_da_pq);
|
||||
}
|
||||
|
||||
@@ -6646,11 +6561,19 @@ int nghttp2_session_consume_stream(nghttp2_session *session, int32_t stream_id,
|
||||
|
||||
int nghttp2_session_set_next_stream_id(nghttp2_session *session,
|
||||
int32_t next_stream_id) {
|
||||
if (next_stream_id < 0 ||
|
||||
if (next_stream_id <= 0 ||
|
||||
session->next_stream_id > (uint32_t)next_stream_id) {
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (session->server) {
|
||||
if (next_stream_id % 2) {
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
} else if (next_stream_id % 2 == 0) {
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
session->next_stream_id = next_stream_id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
typedef enum {
|
||||
NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE = 1 << 0,
|
||||
NGHTTP2_OPTMASK_RECV_CLIENT_PREFACE = 1 << 1,
|
||||
NGHTTP2_OPTMASK_NO_HTTP_MESSAGING = 1 << 2,
|
||||
NGHTTP2_OPTMASK_NO_HTTP_MESSAGING = 1 << 2
|
||||
} nghttp2_optmask;
|
||||
|
||||
typedef enum {
|
||||
@@ -84,7 +84,7 @@ typedef enum {
|
||||
NGHTTP2_IB_READ_PAD_DATA,
|
||||
NGHTTP2_IB_READ_DATA,
|
||||
NGHTTP2_IB_IGN_DATA,
|
||||
NGHTTP2_IB_IGN_ALL,
|
||||
NGHTTP2_IB_IGN_ALL
|
||||
} nghttp2_inbound_state;
|
||||
|
||||
#define NGHTTP2_INBOUND_NUM_IV 7
|
||||
@@ -136,18 +136,21 @@ typedef enum {
|
||||
/* Flag means GOAWAY was sent */
|
||||
NGHTTP2_GOAWAY_SENT = 0x4,
|
||||
/* Flag means GOAWAY was received */
|
||||
NGHTTP2_GOAWAY_RECV = 0x8,
|
||||
NGHTTP2_GOAWAY_RECV = 0x8
|
||||
} nghttp2_goaway_flag;
|
||||
|
||||
struct nghttp2_session {
|
||||
nghttp2_map /* <nghttp2_stream*> */ streams;
|
||||
nghttp2_stream_roots roots;
|
||||
/* Queue for outbound frames other than stream-creating HEADERS and
|
||||
DATA */
|
||||
nghttp2_pq /* <nghttp2_outbound_item*> */ ob_pq;
|
||||
/* Queue for outbound stream-creating HEADERS frame */
|
||||
nghttp2_pq /* <nghttp2_outbound_item*> */ ob_ss_pq;
|
||||
/* QUeue for DATA frame */
|
||||
/* Queue for outbound urgent frames (PING and SETTINGS) */
|
||||
nghttp2_outbound_queue ob_urgent;
|
||||
/* Queue for non-DATA frames */
|
||||
nghttp2_outbound_queue ob_reg;
|
||||
/* Queue for outbound stream-creating HEADERS (request or push
|
||||
response) frame, which are subject to
|
||||
SETTINGS_MAX_CONCURRENT_STREAMS limit. */
|
||||
nghttp2_outbound_queue ob_syn;
|
||||
/* Queue for DATA frame */
|
||||
nghttp2_pq /* <nghttp2_outbound_item*> */ ob_da_pq;
|
||||
nghttp2_active_outbound_item aob;
|
||||
nghttp2_inbound_frame iframe;
|
||||
@@ -156,22 +159,8 @@ struct nghttp2_session {
|
||||
nghttp2_session_callbacks callbacks;
|
||||
/* Memory allocator */
|
||||
nghttp2_mem mem;
|
||||
/* Sequence number of outbound frame to maintain the order of
|
||||
enqueue if priority is equal. */
|
||||
int64_t next_seq;
|
||||
/* Reset count of nghttp2_outbound_item's weight. We decrements
|
||||
weight each time DATA is sent to simulate resource sharing. We
|
||||
use priority queue and larger weight has the precedence. If
|
||||
weight is reached to lowest weight, it resets to its initial
|
||||
weight. If this happens, other items which have the lower weight
|
||||
currently but same initial weight cannot send DATA until item
|
||||
having large weight is decreased. To avoid this, we use this
|
||||
cycle variable. Initally, this is set to 1. If weight gets
|
||||
lowest weight, and if item's cycle == last_cycle, we increments
|
||||
last_cycle and assigns it to item's cycle. Otherwise, just
|
||||
assign last_cycle. In priority queue comparator, we first
|
||||
compare items' cycle value. Lower cycle value has the
|
||||
precedence. */
|
||||
/* Base value when we schedule next DATA frame write. This is
|
||||
updated when one frame was written. */
|
||||
uint64_t last_cycle;
|
||||
void *user_data;
|
||||
/* Points to the latest closed stream. NULL if there is no closed
|
||||
@@ -291,14 +280,6 @@ typedef struct {
|
||||
int nghttp2_session_is_my_stream_id(nghttp2_session *session,
|
||||
int32_t stream_id);
|
||||
|
||||
/*
|
||||
* Initializes |item|. No memory allocation is done in this function.
|
||||
* Don't call nghttp2_outbound_item_free() until frame member is
|
||||
* initialized.
|
||||
*/
|
||||
void nghttp2_session_outbound_item_init(nghttp2_session *session,
|
||||
nghttp2_outbound_item *item);
|
||||
|
||||
/*
|
||||
* Adds |item| to the outbound queue in |session|. When this function
|
||||
* succeeds, it takes ownership of |item|. So caller must not free it
|
||||
@@ -704,13 +685,8 @@ nghttp2_stream *nghttp2_session_get_stream_raw(nghttp2_session *session,
|
||||
*/
|
||||
int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs,
|
||||
size_t datamax, nghttp2_frame *frame,
|
||||
nghttp2_data_aux_data *aux_data);
|
||||
|
||||
/*
|
||||
* Returns top of outbound frame queue. This function returns NULL if
|
||||
* queue is empty.
|
||||
*/
|
||||
nghttp2_outbound_item *nghttp2_session_get_ob_pq_top(nghttp2_session *session);
|
||||
nghttp2_data_aux_data *aux_data,
|
||||
nghttp2_stream *stream);
|
||||
|
||||
/*
|
||||
* Pops and returns next item to send. If there is no such item,
|
||||
|
||||
@@ -63,7 +63,6 @@ void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,
|
||||
stream->effective_weight = stream->weight;
|
||||
stream->sum_dep_weight = 0;
|
||||
stream->sum_norest_weight = 0;
|
||||
stream->sum_top_weight = 0;
|
||||
|
||||
stream->roots = roots;
|
||||
stream->root_prev = NULL;
|
||||
@@ -102,21 +101,26 @@ static int stream_push_item(nghttp2_stream *stream, nghttp2_session *session) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (item->weight > stream->effective_weight) {
|
||||
item->weight = stream->effective_weight;
|
||||
}
|
||||
|
||||
item->cycle = session->last_cycle;
|
||||
|
||||
switch (item->frame.hd.type) {
|
||||
case NGHTTP2_DATA:
|
||||
/* Penalize item by delaying scheduling according to effective
|
||||
weight. This will delay low priority stream, which is good.
|
||||
OTOH, this may incur delay for high priority item. Will
|
||||
see. */
|
||||
item->cycle =
|
||||
session->last_cycle +
|
||||
NGHTTP2_DATA_PAYLOADLEN * NGHTTP2_MAX_WEIGHT / stream->effective_weight;
|
||||
|
||||
rv = nghttp2_pq_push(&session->ob_da_pq, item);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
break;
|
||||
case NGHTTP2_HEADERS:
|
||||
if (stream->state == NGHTTP2_STREAM_RESERVED) {
|
||||
rv = nghttp2_pq_push(&session->ob_ss_pq, item);
|
||||
nghttp2_outbound_queue_push(&session->ob_syn, item);
|
||||
} else {
|
||||
rv = nghttp2_pq_push(&session->ob_pq, item);
|
||||
nghttp2_outbound_queue_push(&session->ob_reg, item);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -124,10 +128,6 @@ static int stream_push_item(nghttp2_stream *stream, nghttp2_session *session) {
|
||||
assert(0);
|
||||
}
|
||||
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
item->queued = 1;
|
||||
|
||||
return 0;
|
||||
@@ -178,18 +178,6 @@ int32_t nghttp2_stream_dep_distributed_effective_weight(nghttp2_stream *stream,
|
||||
return nghttp2_max(1, weight);
|
||||
}
|
||||
|
||||
static int32_t
|
||||
stream_dep_distributed_top_effective_weight(nghttp2_stream *stream,
|
||||
int32_t weight) {
|
||||
if (stream->sum_top_weight == 0) {
|
||||
return stream->effective_weight;
|
||||
}
|
||||
|
||||
weight = stream->effective_weight * weight / stream->sum_top_weight;
|
||||
|
||||
return nghttp2_max(1, weight);
|
||||
}
|
||||
|
||||
static void stream_update_dep_set_rest(nghttp2_stream *stream);
|
||||
|
||||
/* Updates effective_weight of descendant streams in subtree of
|
||||
@@ -199,10 +187,9 @@ static void stream_update_dep_effective_weight(nghttp2_stream *stream) {
|
||||
nghttp2_stream *si;
|
||||
|
||||
DEBUGF(fprintf(stderr, "stream: update_dep_effective_weight "
|
||||
"stream(%p)=%d, weight=%d, sum_norest_weight=%d, "
|
||||
"sum_top_weight=%d\n",
|
||||
"stream(%p)=%d, weight=%d, sum_norest_weight=%d\n",
|
||||
stream, stream->stream_id, stream->weight,
|
||||
stream->sum_norest_weight, stream->sum_top_weight));
|
||||
stream->sum_norest_weight));
|
||||
|
||||
/* stream->sum_norest_weight == 0 means there is no
|
||||
NGHTTP2_STREAM_DPRI_TOP under stream */
|
||||
@@ -211,47 +198,13 @@ static void stream_update_dep_effective_weight(nghttp2_stream *stream) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* If there is no direct descendant whose dpri is
|
||||
NGHTTP2_STREAM_DPRI_TOP, indirect descendants have the chance to
|
||||
send data, so recursively set weight for descendants. */
|
||||
if (stream->sum_top_weight == 0) {
|
||||
for (si = stream->dep_next; si; si = si->sib_next) {
|
||||
if (si->dpri != NGHTTP2_STREAM_DPRI_REST) {
|
||||
si->effective_weight =
|
||||
nghttp2_stream_dep_distributed_effective_weight(stream, si->weight);
|
||||
}
|
||||
|
||||
stream_update_dep_effective_weight(si);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* If there is at least one direct descendant whose dpri is
|
||||
NGHTTP2_STREAM_DPRI_TOP, we won't give a chance to indirect
|
||||
descendants, since closed or blocked stream's weight is
|
||||
distributed among its siblings */
|
||||
for (si = stream->dep_next; si; si = si->sib_next) {
|
||||
if (si->dpri == NGHTTP2_STREAM_DPRI_TOP) {
|
||||
if (si->dpri != NGHTTP2_STREAM_DPRI_REST) {
|
||||
si->effective_weight =
|
||||
stream_dep_distributed_top_effective_weight(stream, si->weight);
|
||||
|
||||
DEBUGF(fprintf(stderr, "stream: stream=%d top eweight=%d\n",
|
||||
si->stream_id, si->effective_weight));
|
||||
|
||||
continue;
|
||||
nghttp2_stream_dep_distributed_effective_weight(stream, si->weight);
|
||||
}
|
||||
|
||||
if (si->dpri == NGHTTP2_STREAM_DPRI_NO_ITEM) {
|
||||
DEBUGF(fprintf(stderr, "stream: stream=%d no_item, ignored\n",
|
||||
si->stream_id));
|
||||
|
||||
/* Since we marked NGHTTP2_STREAM_DPRI_TOP under si, we make
|
||||
them NGHTTP2_STREAM_DPRI_REST again. */
|
||||
stream_update_dep_set_rest(si->dep_next);
|
||||
} else {
|
||||
DEBUGF(
|
||||
fprintf(stderr, "stream: stream=%d rest, ignored\n", si->stream_id));
|
||||
}
|
||||
stream_update_dep_effective_weight(si);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,25 +300,20 @@ static int stream_update_dep_queue_top(nghttp2_stream *stream,
|
||||
}
|
||||
|
||||
/*
|
||||
* Updates stream->sum_norest_weight and stream->sum_top_weight
|
||||
* recursively. We have to gather effective sum of weight of
|
||||
* descendants. If stream->dpri == NGHTTP2_STREAM_DPRI_NO_ITEM, we
|
||||
* have to go deeper and check that any of its descendants has dpri
|
||||
* value of NGHTTP2_STREAM_DPRI_TOP. If so, we have to add weight of
|
||||
* its direct descendants to stream->sum_norest_weight. To make this
|
||||
* work, this function returns 1 if any of its descendants has dpri
|
||||
* value of NGHTTP2_STREAM_DPRI_TOP, otherwise 0.
|
||||
*
|
||||
* Calculating stream->sum_top-weight is very simple compared to
|
||||
* stream->sum_norest_weight. It just adds up the weight of direct
|
||||
* descendants whose dpri is NGHTTP2_STREAM_DPRI_TOP.
|
||||
* Updates stream->sum_norest_weight recursively. We have to gather
|
||||
* effective sum of weight of descendants. If stream->dpri ==
|
||||
* NGHTTP2_STREAM_DPRI_NO_ITEM, we have to go deeper and check that
|
||||
* any of its descendants has dpri value of NGHTTP2_STREAM_DPRI_TOP.
|
||||
* If so, we have to add weight of its direct descendants to
|
||||
* stream->sum_norest_weight. To make this work, this function
|
||||
* returns 1 if any of its descendants has dpri value of
|
||||
* NGHTTP2_STREAM_DPRI_TOP, otherwise 0.
|
||||
*/
|
||||
static int stream_update_dep_sum_norest_weight(nghttp2_stream *stream) {
|
||||
nghttp2_stream *si;
|
||||
int rv;
|
||||
|
||||
stream->sum_norest_weight = 0;
|
||||
stream->sum_top_weight = 0;
|
||||
|
||||
if (stream->dpri == NGHTTP2_STREAM_DPRI_TOP) {
|
||||
return 1;
|
||||
@@ -383,10 +331,6 @@ static int stream_update_dep_sum_norest_weight(nghttp2_stream *stream) {
|
||||
rv = 1;
|
||||
stream->sum_norest_weight += si->weight;
|
||||
}
|
||||
|
||||
if (si->dpri == NGHTTP2_STREAM_DPRI_TOP) {
|
||||
stream->sum_top_weight += si->weight;
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
|
||||
@@ -133,7 +133,7 @@ typedef enum {
|
||||
/* "http" or "https" scheme */
|
||||
NGHTTP2_HTTP_FLAG_SCHEME_HTTP = 1 << 12,
|
||||
/* set if final response is expected */
|
||||
NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE = 1 << 13,
|
||||
NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE = 1 << 13
|
||||
} nghttp2_http_flag;
|
||||
|
||||
typedef enum {
|
||||
@@ -217,9 +217,6 @@ struct nghttp2_stream {
|
||||
descendant with dpri == NGHTTP2_STREAM_DPRI_TOP. We use this
|
||||
value to calculate effective weight. */
|
||||
int32_t sum_norest_weight;
|
||||
/* sum of weight of direct descendants whose dpri value is
|
||||
NGHTTP2_STREAM_DPRI_TOP */
|
||||
int32_t sum_top_weight;
|
||||
nghttp2_stream_state state;
|
||||
/* status code from remote server */
|
||||
int16_t status_code;
|
||||
|
||||
@@ -62,7 +62,7 @@ static int32_t submit_headers_shared(nghttp2_session *session, uint8_t flags,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
nghttp2_session_outbound_item_init(session, item);
|
||||
nghttp2_outbound_item_init(item);
|
||||
|
||||
if (data_prd != NULL && data_prd->read_callback != NULL) {
|
||||
item->aux_data.headers.data_prd = *data_prd;
|
||||
@@ -212,7 +212,7 @@ int nghttp2_submit_priority(nghttp2_session *session, uint8_t flags _U_,
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
nghttp2_session_outbound_item_init(session, item);
|
||||
nghttp2_outbound_item_init(item);
|
||||
|
||||
frame = &item->frame;
|
||||
|
||||
@@ -299,7 +299,7 @@ int32_t nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags _U_,
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
nghttp2_session_outbound_item_init(session, item);
|
||||
nghttp2_outbound_item_init(item);
|
||||
|
||||
item->aux_data.headers.stream_user_data = promised_stream_user_data;
|
||||
|
||||
@@ -453,7 +453,7 @@ int nghttp2_submit_data(nghttp2_session *session, uint8_t flags,
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
nghttp2_session_outbound_item_init(session, item);
|
||||
nghttp2_outbound_item_init(item);
|
||||
|
||||
frame = &item->frame;
|
||||
aux_data = &item->aux_data.data;
|
||||
|
||||
432
mkhufftbl.py
432
mkhufftbl.py
@@ -10,8 +10,271 @@
|
||||
from __future__ import unicode_literals
|
||||
import re
|
||||
import sys
|
||||
import StringIO
|
||||
|
||||
# From [1]
|
||||
HUFFMAN_CODE_TABLE = """\
|
||||
( 0) |11111111|11000 1ff8 [13]
|
||||
( 1) |11111111|11111111|1011000 7fffd8 [23]
|
||||
( 2) |11111111|11111111|11111110|0010 fffffe2 [28]
|
||||
( 3) |11111111|11111111|11111110|0011 fffffe3 [28]
|
||||
( 4) |11111111|11111111|11111110|0100 fffffe4 [28]
|
||||
( 5) |11111111|11111111|11111110|0101 fffffe5 [28]
|
||||
( 6) |11111111|11111111|11111110|0110 fffffe6 [28]
|
||||
( 7) |11111111|11111111|11111110|0111 fffffe7 [28]
|
||||
( 8) |11111111|11111111|11111110|1000 fffffe8 [28]
|
||||
( 9) |11111111|11111111|11101010 ffffea [24]
|
||||
( 10) |11111111|11111111|11111111|111100 3ffffffc [30]
|
||||
( 11) |11111111|11111111|11111110|1001 fffffe9 [28]
|
||||
( 12) |11111111|11111111|11111110|1010 fffffea [28]
|
||||
( 13) |11111111|11111111|11111111|111101 3ffffffd [30]
|
||||
( 14) |11111111|11111111|11111110|1011 fffffeb [28]
|
||||
( 15) |11111111|11111111|11111110|1100 fffffec [28]
|
||||
( 16) |11111111|11111111|11111110|1101 fffffed [28]
|
||||
( 17) |11111111|11111111|11111110|1110 fffffee [28]
|
||||
( 18) |11111111|11111111|11111110|1111 fffffef [28]
|
||||
( 19) |11111111|11111111|11111111|0000 ffffff0 [28]
|
||||
( 20) |11111111|11111111|11111111|0001 ffffff1 [28]
|
||||
( 21) |11111111|11111111|11111111|0010 ffffff2 [28]
|
||||
( 22) |11111111|11111111|11111111|111110 3ffffffe [30]
|
||||
( 23) |11111111|11111111|11111111|0011 ffffff3 [28]
|
||||
( 24) |11111111|11111111|11111111|0100 ffffff4 [28]
|
||||
( 25) |11111111|11111111|11111111|0101 ffffff5 [28]
|
||||
( 26) |11111111|11111111|11111111|0110 ffffff6 [28]
|
||||
( 27) |11111111|11111111|11111111|0111 ffffff7 [28]
|
||||
( 28) |11111111|11111111|11111111|1000 ffffff8 [28]
|
||||
( 29) |11111111|11111111|11111111|1001 ffffff9 [28]
|
||||
( 30) |11111111|11111111|11111111|1010 ffffffa [28]
|
||||
( 31) |11111111|11111111|11111111|1011 ffffffb [28]
|
||||
' ' ( 32) |010100 14 [ 6]
|
||||
'!' ( 33) |11111110|00 3f8 [10]
|
||||
'"' ( 34) |11111110|01 3f9 [10]
|
||||
'#' ( 35) |11111111|1010 ffa [12]
|
||||
'$' ( 36) |11111111|11001 1ff9 [13]
|
||||
'%' ( 37) |010101 15 [ 6]
|
||||
'&' ( 38) |11111000 f8 [ 8]
|
||||
''' ( 39) |11111111|010 7fa [11]
|
||||
'(' ( 40) |11111110|10 3fa [10]
|
||||
')' ( 41) |11111110|11 3fb [10]
|
||||
'*' ( 42) |11111001 f9 [ 8]
|
||||
'+' ( 43) |11111111|011 7fb [11]
|
||||
',' ( 44) |11111010 fa [ 8]
|
||||
'-' ( 45) |010110 16 [ 6]
|
||||
'.' ( 46) |010111 17 [ 6]
|
||||
'/' ( 47) |011000 18 [ 6]
|
||||
'0' ( 48) |00000 0 [ 5]
|
||||
'1' ( 49) |00001 1 [ 5]
|
||||
'2' ( 50) |00010 2 [ 5]
|
||||
'3' ( 51) |011001 19 [ 6]
|
||||
'4' ( 52) |011010 1a [ 6]
|
||||
'5' ( 53) |011011 1b [ 6]
|
||||
'6' ( 54) |011100 1c [ 6]
|
||||
'7' ( 55) |011101 1d [ 6]
|
||||
'8' ( 56) |011110 1e [ 6]
|
||||
'9' ( 57) |011111 1f [ 6]
|
||||
':' ( 58) |1011100 5c [ 7]
|
||||
';' ( 59) |11111011 fb [ 8]
|
||||
'<' ( 60) |11111111|1111100 7ffc [15]
|
||||
'=' ( 61) |100000 20 [ 6]
|
||||
'>' ( 62) |11111111|1011 ffb [12]
|
||||
'?' ( 63) |11111111|00 3fc [10]
|
||||
'@' ( 64) |11111111|11010 1ffa [13]
|
||||
'A' ( 65) |100001 21 [ 6]
|
||||
'B' ( 66) |1011101 5d [ 7]
|
||||
'C' ( 67) |1011110 5e [ 7]
|
||||
'D' ( 68) |1011111 5f [ 7]
|
||||
'E' ( 69) |1100000 60 [ 7]
|
||||
'F' ( 70) |1100001 61 [ 7]
|
||||
'G' ( 71) |1100010 62 [ 7]
|
||||
'H' ( 72) |1100011 63 [ 7]
|
||||
'I' ( 73) |1100100 64 [ 7]
|
||||
'J' ( 74) |1100101 65 [ 7]
|
||||
'K' ( 75) |1100110 66 [ 7]
|
||||
'L' ( 76) |1100111 67 [ 7]
|
||||
'M' ( 77) |1101000 68 [ 7]
|
||||
'N' ( 78) |1101001 69 [ 7]
|
||||
'O' ( 79) |1101010 6a [ 7]
|
||||
'P' ( 80) |1101011 6b [ 7]
|
||||
'Q' ( 81) |1101100 6c [ 7]
|
||||
'R' ( 82) |1101101 6d [ 7]
|
||||
'S' ( 83) |1101110 6e [ 7]
|
||||
'T' ( 84) |1101111 6f [ 7]
|
||||
'U' ( 85) |1110000 70 [ 7]
|
||||
'V' ( 86) |1110001 71 [ 7]
|
||||
'W' ( 87) |1110010 72 [ 7]
|
||||
'X' ( 88) |11111100 fc [ 8]
|
||||
'Y' ( 89) |1110011 73 [ 7]
|
||||
'Z' ( 90) |11111101 fd [ 8]
|
||||
'[' ( 91) |11111111|11011 1ffb [13]
|
||||
'\' ( 92) |11111111|11111110|000 7fff0 [19]
|
||||
']' ( 93) |11111111|11100 1ffc [13]
|
||||
'^' ( 94) |11111111|111100 3ffc [14]
|
||||
'_' ( 95) |100010 22 [ 6]
|
||||
'`' ( 96) |11111111|1111101 7ffd [15]
|
||||
'a' ( 97) |00011 3 [ 5]
|
||||
'b' ( 98) |100011 23 [ 6]
|
||||
'c' ( 99) |00100 4 [ 5]
|
||||
'd' (100) |100100 24 [ 6]
|
||||
'e' (101) |00101 5 [ 5]
|
||||
'f' (102) |100101 25 [ 6]
|
||||
'g' (103) |100110 26 [ 6]
|
||||
'h' (104) |100111 27 [ 6]
|
||||
'i' (105) |00110 6 [ 5]
|
||||
'j' (106) |1110100 74 [ 7]
|
||||
'k' (107) |1110101 75 [ 7]
|
||||
'l' (108) |101000 28 [ 6]
|
||||
'm' (109) |101001 29 [ 6]
|
||||
'n' (110) |101010 2a [ 6]
|
||||
'o' (111) |00111 7 [ 5]
|
||||
'p' (112) |101011 2b [ 6]
|
||||
'q' (113) |1110110 76 [ 7]
|
||||
'r' (114) |101100 2c [ 6]
|
||||
's' (115) |01000 8 [ 5]
|
||||
't' (116) |01001 9 [ 5]
|
||||
'u' (117) |101101 2d [ 6]
|
||||
'v' (118) |1110111 77 [ 7]
|
||||
'w' (119) |1111000 78 [ 7]
|
||||
'x' (120) |1111001 79 [ 7]
|
||||
'y' (121) |1111010 7a [ 7]
|
||||
'z' (122) |1111011 7b [ 7]
|
||||
'{' (123) |11111111|1111110 7ffe [15]
|
||||
'|' (124) |11111111|100 7fc [11]
|
||||
'}' (125) |11111111|111101 3ffd [14]
|
||||
'~' (126) |11111111|11101 1ffd [13]
|
||||
(127) |11111111|11111111|11111111|1100 ffffffc [28]
|
||||
(128) |11111111|11111110|0110 fffe6 [20]
|
||||
(129) |11111111|11111111|010010 3fffd2 [22]
|
||||
(130) |11111111|11111110|0111 fffe7 [20]
|
||||
(131) |11111111|11111110|1000 fffe8 [20]
|
||||
(132) |11111111|11111111|010011 3fffd3 [22]
|
||||
(133) |11111111|11111111|010100 3fffd4 [22]
|
||||
(134) |11111111|11111111|010101 3fffd5 [22]
|
||||
(135) |11111111|11111111|1011001 7fffd9 [23]
|
||||
(136) |11111111|11111111|010110 3fffd6 [22]
|
||||
(137) |11111111|11111111|1011010 7fffda [23]
|
||||
(138) |11111111|11111111|1011011 7fffdb [23]
|
||||
(139) |11111111|11111111|1011100 7fffdc [23]
|
||||
(140) |11111111|11111111|1011101 7fffdd [23]
|
||||
(141) |11111111|11111111|1011110 7fffde [23]
|
||||
(142) |11111111|11111111|11101011 ffffeb [24]
|
||||
(143) |11111111|11111111|1011111 7fffdf [23]
|
||||
(144) |11111111|11111111|11101100 ffffec [24]
|
||||
(145) |11111111|11111111|11101101 ffffed [24]
|
||||
(146) |11111111|11111111|010111 3fffd7 [22]
|
||||
(147) |11111111|11111111|1100000 7fffe0 [23]
|
||||
(148) |11111111|11111111|11101110 ffffee [24]
|
||||
(149) |11111111|11111111|1100001 7fffe1 [23]
|
||||
(150) |11111111|11111111|1100010 7fffe2 [23]
|
||||
(151) |11111111|11111111|1100011 7fffe3 [23]
|
||||
(152) |11111111|11111111|1100100 7fffe4 [23]
|
||||
(153) |11111111|11111110|11100 1fffdc [21]
|
||||
(154) |11111111|11111111|011000 3fffd8 [22]
|
||||
(155) |11111111|11111111|1100101 7fffe5 [23]
|
||||
(156) |11111111|11111111|011001 3fffd9 [22]
|
||||
(157) |11111111|11111111|1100110 7fffe6 [23]
|
||||
(158) |11111111|11111111|1100111 7fffe7 [23]
|
||||
(159) |11111111|11111111|11101111 ffffef [24]
|
||||
(160) |11111111|11111111|011010 3fffda [22]
|
||||
(161) |11111111|11111110|11101 1fffdd [21]
|
||||
(162) |11111111|11111110|1001 fffe9 [20]
|
||||
(163) |11111111|11111111|011011 3fffdb [22]
|
||||
(164) |11111111|11111111|011100 3fffdc [22]
|
||||
(165) |11111111|11111111|1101000 7fffe8 [23]
|
||||
(166) |11111111|11111111|1101001 7fffe9 [23]
|
||||
(167) |11111111|11111110|11110 1fffde [21]
|
||||
(168) |11111111|11111111|1101010 7fffea [23]
|
||||
(169) |11111111|11111111|011101 3fffdd [22]
|
||||
(170) |11111111|11111111|011110 3fffde [22]
|
||||
(171) |11111111|11111111|11110000 fffff0 [24]
|
||||
(172) |11111111|11111110|11111 1fffdf [21]
|
||||
(173) |11111111|11111111|011111 3fffdf [22]
|
||||
(174) |11111111|11111111|1101011 7fffeb [23]
|
||||
(175) |11111111|11111111|1101100 7fffec [23]
|
||||
(176) |11111111|11111111|00000 1fffe0 [21]
|
||||
(177) |11111111|11111111|00001 1fffe1 [21]
|
||||
(178) |11111111|11111111|100000 3fffe0 [22]
|
||||
(179) |11111111|11111111|00010 1fffe2 [21]
|
||||
(180) |11111111|11111111|1101101 7fffed [23]
|
||||
(181) |11111111|11111111|100001 3fffe1 [22]
|
||||
(182) |11111111|11111111|1101110 7fffee [23]
|
||||
(183) |11111111|11111111|1101111 7fffef [23]
|
||||
(184) |11111111|11111110|1010 fffea [20]
|
||||
(185) |11111111|11111111|100010 3fffe2 [22]
|
||||
(186) |11111111|11111111|100011 3fffe3 [22]
|
||||
(187) |11111111|11111111|100100 3fffe4 [22]
|
||||
(188) |11111111|11111111|1110000 7ffff0 [23]
|
||||
(189) |11111111|11111111|100101 3fffe5 [22]
|
||||
(190) |11111111|11111111|100110 3fffe6 [22]
|
||||
(191) |11111111|11111111|1110001 7ffff1 [23]
|
||||
(192) |11111111|11111111|11111000|00 3ffffe0 [26]
|
||||
(193) |11111111|11111111|11111000|01 3ffffe1 [26]
|
||||
(194) |11111111|11111110|1011 fffeb [20]
|
||||
(195) |11111111|11111110|001 7fff1 [19]
|
||||
(196) |11111111|11111111|100111 3fffe7 [22]
|
||||
(197) |11111111|11111111|1110010 7ffff2 [23]
|
||||
(198) |11111111|11111111|101000 3fffe8 [22]
|
||||
(199) |11111111|11111111|11110110|0 1ffffec [25]
|
||||
(200) |11111111|11111111|11111000|10 3ffffe2 [26]
|
||||
(201) |11111111|11111111|11111000|11 3ffffe3 [26]
|
||||
(202) |11111111|11111111|11111001|00 3ffffe4 [26]
|
||||
(203) |11111111|11111111|11111011|110 7ffffde [27]
|
||||
(204) |11111111|11111111|11111011|111 7ffffdf [27]
|
||||
(205) |11111111|11111111|11111001|01 3ffffe5 [26]
|
||||
(206) |11111111|11111111|11110001 fffff1 [24]
|
||||
(207) |11111111|11111111|11110110|1 1ffffed [25]
|
||||
(208) |11111111|11111110|010 7fff2 [19]
|
||||
(209) |11111111|11111111|00011 1fffe3 [21]
|
||||
(210) |11111111|11111111|11111001|10 3ffffe6 [26]
|
||||
(211) |11111111|11111111|11111100|000 7ffffe0 [27]
|
||||
(212) |11111111|11111111|11111100|001 7ffffe1 [27]
|
||||
(213) |11111111|11111111|11111001|11 3ffffe7 [26]
|
||||
(214) |11111111|11111111|11111100|010 7ffffe2 [27]
|
||||
(215) |11111111|11111111|11110010 fffff2 [24]
|
||||
(216) |11111111|11111111|00100 1fffe4 [21]
|
||||
(217) |11111111|11111111|00101 1fffe5 [21]
|
||||
(218) |11111111|11111111|11111010|00 3ffffe8 [26]
|
||||
(219) |11111111|11111111|11111010|01 3ffffe9 [26]
|
||||
(220) |11111111|11111111|11111111|1101 ffffffd [28]
|
||||
(221) |11111111|11111111|11111100|011 7ffffe3 [27]
|
||||
(222) |11111111|11111111|11111100|100 7ffffe4 [27]
|
||||
(223) |11111111|11111111|11111100|101 7ffffe5 [27]
|
||||
(224) |11111111|11111110|1100 fffec [20]
|
||||
(225) |11111111|11111111|11110011 fffff3 [24]
|
||||
(226) |11111111|11111110|1101 fffed [20]
|
||||
(227) |11111111|11111111|00110 1fffe6 [21]
|
||||
(228) |11111111|11111111|101001 3fffe9 [22]
|
||||
(229) |11111111|11111111|00111 1fffe7 [21]
|
||||
(230) |11111111|11111111|01000 1fffe8 [21]
|
||||
(231) |11111111|11111111|1110011 7ffff3 [23]
|
||||
(232) |11111111|11111111|101010 3fffea [22]
|
||||
(233) |11111111|11111111|101011 3fffeb [22]
|
||||
(234) |11111111|11111111|11110111|0 1ffffee [25]
|
||||
(235) |11111111|11111111|11110111|1 1ffffef [25]
|
||||
(236) |11111111|11111111|11110100 fffff4 [24]
|
||||
(237) |11111111|11111111|11110101 fffff5 [24]
|
||||
(238) |11111111|11111111|11111010|10 3ffffea [26]
|
||||
(239) |11111111|11111111|1110100 7ffff4 [23]
|
||||
(240) |11111111|11111111|11111010|11 3ffffeb [26]
|
||||
(241) |11111111|11111111|11111100|110 7ffffe6 [27]
|
||||
(242) |11111111|11111111|11111011|00 3ffffec [26]
|
||||
(243) |11111111|11111111|11111011|01 3ffffed [26]
|
||||
(244) |11111111|11111111|11111100|111 7ffffe7 [27]
|
||||
(245) |11111111|11111111|11111101|000 7ffffe8 [27]
|
||||
(246) |11111111|11111111|11111101|001 7ffffe9 [27]
|
||||
(247) |11111111|11111111|11111101|010 7ffffea [27]
|
||||
(248) |11111111|11111111|11111101|011 7ffffeb [27]
|
||||
(249) |11111111|11111111|11111111|1110 ffffffe [28]
|
||||
(250) |11111111|11111111|11111101|100 7ffffec [27]
|
||||
(251) |11111111|11111111|11111101|101 7ffffed [27]
|
||||
(252) |11111111|11111111|11111101|110 7ffffee [27]
|
||||
(253) |11111111|11111111|11111101|111 7ffffef [27]
|
||||
(254) |11111111|11111111|11111110|000 7fffff0 [27]
|
||||
(255) |11111111|11111111|11111011|10 3ffffee [26]
|
||||
EOS (256) |11111111|11111111|11111111|111111 3fffffff [30]
|
||||
"""
|
||||
|
||||
class Node:
|
||||
|
||||
def __init__(self, term = None):
|
||||
self.term = term
|
||||
self.left = None
|
||||
@@ -20,21 +283,18 @@ class Node:
|
||||
self.id = None
|
||||
self.accept = False
|
||||
|
||||
def to_bin(s):
|
||||
res = []
|
||||
for i in range(0, len(s), 8):
|
||||
x = s[i:i+8]
|
||||
x += '0'*(8 - len(x))
|
||||
a = 0
|
||||
for j in range(8):
|
||||
a *= 2
|
||||
a += ord(x[j]) - ord('0')
|
||||
res.append(a) #chr(a))
|
||||
return res
|
||||
class Context:
|
||||
|
||||
nodes = []
|
||||
def __init__(self):
|
||||
self.next_id_ = 0
|
||||
self.root = Node()
|
||||
|
||||
def insert(node, sym, bits):
|
||||
def next_id(self):
|
||||
id = self.next_id_
|
||||
self.next_id_ += 1
|
||||
return id
|
||||
|
||||
def _add(node, sym, bits):
|
||||
if len(bits) == 0:
|
||||
node.term = sym
|
||||
return
|
||||
@@ -47,67 +307,71 @@ def insert(node, sym, bits):
|
||||
if node.right is None:
|
||||
node.right = Node()
|
||||
child = node.right
|
||||
insert(child, sym, bits[1:])
|
||||
_add(child, sym, bits[1:])
|
||||
|
||||
def traverse(node, bits, syms, start_node, root, depth):
|
||||
if depth == 4:
|
||||
if 256 in syms:
|
||||
syms = []
|
||||
def huffman_tree_add(ctx, sym, bits):
|
||||
_add(ctx.root, sym, bits)
|
||||
|
||||
def _set_node_id(ctx, node, prefix):
|
||||
if node.term is not None:
|
||||
return
|
||||
if len(prefix) <= 7 and [1] * len(prefix) == prefix:
|
||||
node.accept = True
|
||||
node.id = ctx.next_id()
|
||||
_set_node_id(ctx, node.left, prefix + [0])
|
||||
_set_node_id(ctx, node.right, prefix + [1])
|
||||
|
||||
def huffman_tree_set_node_id(ctx):
|
||||
_set_node_id(ctx, ctx.root, [])
|
||||
|
||||
def _traverse(node, sym, start_node, root, left):
|
||||
if left == 0:
|
||||
if sym == 256:
|
||||
sym = None
|
||||
node = None
|
||||
start_node.trans.append((node, bits, syms))
|
||||
start_node.trans.append((node, sym))
|
||||
return
|
||||
|
||||
if node.term is not None:
|
||||
node = root
|
||||
|
||||
def go(node, bit):
|
||||
nbits = list(bits)
|
||||
nbits.append(bit)
|
||||
nsyms = list(syms)
|
||||
def go(node):
|
||||
if node.term is not None:
|
||||
nsyms.append(node.term)
|
||||
traverse(node, nbits, nsyms, start_node, root, depth + 1)
|
||||
assert sym is None
|
||||
nsym = node.term
|
||||
else:
|
||||
nsym = sym
|
||||
|
||||
go(node.left, 0)
|
||||
go(node.right, 1)
|
||||
_traverse(node, nsym, start_node, root, left - 1)
|
||||
|
||||
idseed = 0
|
||||
go(node.left)
|
||||
go(node.right)
|
||||
|
||||
def dfs_setid(node, prefix):
|
||||
if node.term is not None:
|
||||
return
|
||||
if len(prefix) <= 7 and [1] * len(prefix) == prefix:
|
||||
node.accept = True
|
||||
global idseed
|
||||
node.id = idseed
|
||||
idseed += 1
|
||||
dfs_setid(node.left, prefix + [0])
|
||||
dfs_setid(node.right, prefix + [1])
|
||||
|
||||
def dfs(node, root):
|
||||
def _build_transition_table(ctx, node):
|
||||
if node is None:
|
||||
return
|
||||
traverse(node, [], [], node, root, 0)
|
||||
dfs(node.left, root)
|
||||
dfs(node.right, root)
|
||||
_traverse(node, None, node, ctx.root, 4)
|
||||
_build_transition_table(ctx, node.left)
|
||||
_build_transition_table(ctx, node.right)
|
||||
|
||||
def huffman_tree_build_transition_table(ctx):
|
||||
_build_transition_table(ctx, ctx.root)
|
||||
|
||||
NGHTTP2_HUFF_ACCEPTED = 1
|
||||
NGHTTP2_HUFF_SYM = 1 << 1
|
||||
NGHTTP2_HUFF_FAIL = 1 << 2
|
||||
|
||||
def dfs_print(node):
|
||||
def _print_transition_table(node):
|
||||
if node.term is not None:
|
||||
return
|
||||
print '/* {} */'.format(node.id)
|
||||
print '{'
|
||||
for nd, bits, syms in node.trans:
|
||||
outlen = len(syms)
|
||||
for nd, sym in node.trans:
|
||||
flags = 0
|
||||
if outlen == 0:
|
||||
if sym is None:
|
||||
out = 0
|
||||
else:
|
||||
assert(outlen == 1)
|
||||
out = syms[0]
|
||||
out = sym
|
||||
flags |= NGHTTP2_HUFF_SYM
|
||||
if nd is None:
|
||||
id = 0
|
||||
@@ -122,52 +386,50 @@ def dfs_print(node):
|
||||
flags |= NGHTTP2_HUFF_ACCEPTED
|
||||
print ' {{{}, 0x{:02x}, {}}},'.format(id, flags, out)
|
||||
print '},'
|
||||
dfs_print(node.left)
|
||||
dfs_print(node.right)
|
||||
_print_transition_table(node.left)
|
||||
_print_transition_table(node.right)
|
||||
|
||||
symbol_tbl = [(None, 0) for i in range(257)]
|
||||
tables = {}
|
||||
def huffman_tree_print_transition_table(ctx):
|
||||
_print_transition_table(ctx.root)
|
||||
|
||||
root = Node()
|
||||
if __name__ == '__main__':
|
||||
ctx = Context()
|
||||
symbol_tbl = [(None, 0) for i in range(257)]
|
||||
|
||||
for line in sys.stdin:
|
||||
m = re.match(r'.*\(\s*(\d+)\)\s+([|01]+)\s+(\S+)\s+\[\s*(\d+)\].*', line)
|
||||
if m:
|
||||
#print m.group(1), m.group(2), m.group(4)
|
||||
if len(m.group(3)) > 8:
|
||||
raise Error('Code is more than 4 bytes long')
|
||||
sym = int(m.group(1))
|
||||
bits = re.sub(r'\|', '', m.group(2))
|
||||
nbits = int(m.group(4))
|
||||
assert(len(bits) == nbits)
|
||||
binpat = to_bin(bits)
|
||||
assert(len(binpat) == (nbits+7)/8)
|
||||
symbol_tbl[sym] = (binpat, nbits, m.group(3))
|
||||
#print "Inserting", sym
|
||||
insert(root, sym, bits)
|
||||
for line in StringIO.StringIO(HUFFMAN_CODE_TABLE):
|
||||
m = re.match(
|
||||
r'.*\(\s*(\d+)\)\s+([|01]+)\s+(\S+)\s+\[\s*(\d+)\].*', line)
|
||||
if m:
|
||||
sym = int(m.group(1))
|
||||
bits = re.sub(r'\|', '', m.group(2))
|
||||
code = m.group(3)
|
||||
nbits = int(m.group(4))
|
||||
if len(code) > 8:
|
||||
raise Error('Code is more than 4 bytes long')
|
||||
assert(len(bits) == nbits)
|
||||
symbol_tbl[sym] = (nbits, code)
|
||||
huffman_tree_add(ctx, sym, bits)
|
||||
|
||||
dfs_setid(root, [])
|
||||
dfs(root, root)
|
||||
huffman_tree_set_node_id(ctx)
|
||||
huffman_tree_build_transition_table(ctx)
|
||||
|
||||
print '''\
|
||||
print '''\
|
||||
typedef struct {
|
||||
uint32_t nbits;
|
||||
uint32_t code;
|
||||
} nghttp2_huff_sym;
|
||||
'''
|
||||
|
||||
print '''\
|
||||
const nghttp2_huff_sym huff_sym_table[] = {'''
|
||||
for i in range(257):
|
||||
pat = list(symbol_tbl[i][0])
|
||||
pat += [0]*(4 - len(pat))
|
||||
print '''\
|
||||
const nghttp2_huff_sym huff_sym_table[] = {'''
|
||||
for i in range(257):
|
||||
print '''\
|
||||
{{ {}, 0x{}u }}{}\
|
||||
'''.format(symbol_tbl[i][1], symbol_tbl[i][2], ',' if i < 256 else '')
|
||||
print '};'
|
||||
print ''
|
||||
'''.format(symbol_tbl[i][0], symbol_tbl[i][1], ',' if i < 256 else '')
|
||||
print '};'
|
||||
print ''
|
||||
|
||||
print '''\
|
||||
print '''\
|
||||
enum {{
|
||||
NGHTTP2_HUFF_ACCEPTED = {},
|
||||
NGHTTP2_HUFF_SYM = {},
|
||||
@@ -175,7 +437,7 @@ enum {{
|
||||
}} nghttp2_huff_decode_flag;
|
||||
'''.format(NGHTTP2_HUFF_ACCEPTED, NGHTTP2_HUFF_SYM, NGHTTP2_HUFF_FAIL)
|
||||
|
||||
print '''\
|
||||
print '''\
|
||||
typedef struct {
|
||||
uint8_t state;
|
||||
uint8_t flags;
|
||||
@@ -183,7 +445,7 @@ typedef struct {
|
||||
} nghttp2_huff_decode;
|
||||
'''
|
||||
|
||||
print '''\
|
||||
print '''\
|
||||
const nghttp2_huff_decode huff_decode_table[][16] = {'''
|
||||
dfs_print(root)
|
||||
print '};'
|
||||
huffman_tree_print_transition_table(ctx)
|
||||
print '};'
|
||||
|
||||
@@ -10,39 +10,17 @@
|
||||
from __future__ import unicode_literals
|
||||
import re, sys
|
||||
|
||||
def hash(s):
|
||||
h = 0
|
||||
for c in s:
|
||||
h = h * 31 + ord(c)
|
||||
return h & ((1 << 32) - 1)
|
||||
|
||||
entries = []
|
||||
for line in sys.stdin:
|
||||
m = re.match(r'(\d+)\s+(\S+)\s+(\S.*)?', line)
|
||||
val = m.group(3).strip() if m.group(3) else ''
|
||||
entries.append((hash(m.group(2)), int(m.group(1)), m.group(2), val))
|
||||
|
||||
entries.sort()
|
||||
|
||||
print '/* Sorted by hash(name) and its table index */'
|
||||
print 'static nghttp2_hd_static_entry static_table[] = {'
|
||||
for ent in entries:
|
||||
print 'MAKE_STATIC_ENT({}, "{}", "{}", {}u, {}u),'\
|
||||
.format(ent[1] - 1, ent[2], ent[3], ent[0], hash(ent[3]))
|
||||
print '};'
|
||||
|
||||
print ''
|
||||
|
||||
print '/* Index to the position in static_table */'
|
||||
print 'const size_t static_table_index[] = {'
|
||||
for i in range(len(entries)):
|
||||
for j, ent in enumerate(entries):
|
||||
if ent[1] - 1 == i:
|
||||
sys.stdout.write('{: <2d},'.format(j))
|
||||
break
|
||||
if (i + 1) % 16 == 0:
|
||||
sys.stdout.write('\n')
|
||||
else:
|
||||
sys.stdout.write(' ')
|
||||
entries.append((int(m.group(1)), m.group(2), val))
|
||||
|
||||
print 'static nghttp2_hd_entry static_table[] = {'
|
||||
idx = 0
|
||||
for i, ent in enumerate(entries):
|
||||
if entries[idx][1] != ent[1]:
|
||||
idx = i
|
||||
print 'MAKE_STATIC_ENT("{}", "{}", {}),'\
|
||||
.format(ent[1], ent[2], entries[idx][0] - 1)
|
||||
print '};'
|
||||
|
||||
@@ -30,7 +30,8 @@
|
||||
|
||||
namespace nghttp2 {
|
||||
|
||||
ParserData::ParserData(const std::string &base_uri) : base_uri(base_uri) {}
|
||||
ParserData::ParserData(const std::string &base_uri)
|
||||
: base_uri(base_uri), inside_head(0) {}
|
||||
|
||||
HtmlParser::HtmlParser(const std::string &base_uri)
|
||||
: base_uri_(base_uri), parser_ctx_(nullptr), parser_data_(base_uri) {}
|
||||
@@ -52,13 +53,13 @@ const char *get_attr(const xmlChar **attrs, const char *name) {
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void add_link(ParserData *parser_data, const char *uri, RequestPriority pri) {
|
||||
void add_link(ParserData *parser_data, const char *uri, ResourceType res_type) {
|
||||
auto u = xmlBuildURI(
|
||||
reinterpret_cast<const xmlChar *>(uri),
|
||||
reinterpret_cast<const xmlChar *>(parser_data->base_uri.c_str()));
|
||||
if (u) {
|
||||
parser_data->links.push_back(
|
||||
std::make_pair(reinterpret_cast<char *>(u), pri));
|
||||
std::make_pair(reinterpret_cast<char *>(u), res_type));
|
||||
free(u);
|
||||
}
|
||||
}
|
||||
@@ -68,6 +69,9 @@ namespace {
|
||||
void start_element_func(void *user_data, const xmlChar *name,
|
||||
const xmlChar **attrs) {
|
||||
auto parser_data = static_cast<ParserData *>(user_data);
|
||||
if (util::strieq(reinterpret_cast<const char *>(name), "head")) {
|
||||
++parser_data->inside_head;
|
||||
}
|
||||
if (util::strieq(reinterpret_cast<const char *>(name), "link")) {
|
||||
auto rel_attr = get_attr(attrs, "rel");
|
||||
auto href_attr = get_attr(attrs, "href");
|
||||
@@ -75,22 +79,35 @@ void start_element_func(void *user_data, const xmlChar *name,
|
||||
return;
|
||||
}
|
||||
if (util::strieq(rel_attr, "shortcut icon")) {
|
||||
add_link(parser_data, href_attr, REQ_PRI_LOWEST);
|
||||
add_link(parser_data, href_attr, REQ_OTHERS);
|
||||
} else if (util::strieq(rel_attr, "stylesheet")) {
|
||||
add_link(parser_data, href_attr, REQ_PRI_MEDIUM);
|
||||
add_link(parser_data, href_attr, REQ_CSS);
|
||||
}
|
||||
} else if (util::strieq(reinterpret_cast<const char *>(name), "img")) {
|
||||
auto src_attr = get_attr(attrs, "src");
|
||||
if (!src_attr) {
|
||||
return;
|
||||
}
|
||||
add_link(parser_data, src_attr, REQ_PRI_LOWEST);
|
||||
add_link(parser_data, src_attr, REQ_IMG);
|
||||
} else if (util::strieq(reinterpret_cast<const char *>(name), "script")) {
|
||||
auto src_attr = get_attr(attrs, "src");
|
||||
if (!src_attr) {
|
||||
return;
|
||||
}
|
||||
add_link(parser_data, src_attr, REQ_PRI_LOW);
|
||||
if (parser_data->inside_head) {
|
||||
add_link(parser_data, src_attr, REQ_JS);
|
||||
} else {
|
||||
add_link(parser_data, src_attr, REQ_UNBLOCK_JS);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void end_element_func(void *user_data, const xmlChar *name) {
|
||||
auto parser_data = static_cast<ParserData *>(user_data);
|
||||
if (util::strieq(reinterpret_cast<const char *>(name), "head")) {
|
||||
--parser_data->inside_head;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
@@ -112,7 +129,7 @@ xmlSAXHandler saxHandler = {
|
||||
nullptr, // startDocumentSAXFunc
|
||||
nullptr, // endDocumentSAXFunc
|
||||
&start_element_func, // startElementSAXFunc
|
||||
nullptr, // endElementSAXFunc
|
||||
&end_element_func, // endElementSAXFunc
|
||||
nullptr, // referenceSAXFunc
|
||||
nullptr, // charactersSAXFunc
|
||||
nullptr, // ignorableWhitespaceSAXFunc
|
||||
@@ -160,7 +177,7 @@ int HtmlParser::parse_chunk_internal(const char *chunk, size_t size, int fin) {
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<std::pair<std::string, RequestPriority>> &
|
||||
const std::vector<std::pair<std::string, ResourceType>> &
|
||||
HtmlParser::get_links() const {
|
||||
return parser_data_.links;
|
||||
}
|
||||
|
||||
@@ -38,16 +38,19 @@
|
||||
|
||||
namespace nghttp2 {
|
||||
|
||||
enum RequestPriority {
|
||||
REQ_PRI_HIGH = 0,
|
||||
REQ_PRI_MEDIUM = 1,
|
||||
REQ_PRI_LOW = 2,
|
||||
REQ_PRI_LOWEST = 3
|
||||
enum ResourceType {
|
||||
REQ_CSS = 1,
|
||||
REQ_JS,
|
||||
REQ_UNBLOCK_JS,
|
||||
REQ_IMG,
|
||||
REQ_OTHERS,
|
||||
};
|
||||
|
||||
struct ParserData {
|
||||
std::string base_uri;
|
||||
std::vector<std::pair<std::string, RequestPriority>> links;
|
||||
std::vector<std::pair<std::string, ResourceType>> links;
|
||||
// > 0 if we are inside "head" element.
|
||||
int inside_head;
|
||||
ParserData(const std::string &base_uri);
|
||||
};
|
||||
|
||||
@@ -58,7 +61,7 @@ public:
|
||||
HtmlParser(const std::string &base_uri);
|
||||
~HtmlParser();
|
||||
int parse_chunk(const char *chunk, size_t size, int fin);
|
||||
const std::vector<std::pair<std::string, RequestPriority>> &get_links() const;
|
||||
const std::vector<std::pair<std::string, ResourceType>> &get_links() const;
|
||||
void clear_links();
|
||||
|
||||
private:
|
||||
@@ -75,14 +78,13 @@ class HtmlParser {
|
||||
public:
|
||||
HtmlParser(const std::string &base_uri) {}
|
||||
int parse_chunk(const char *chunk, size_t size, int fin) { return 0; }
|
||||
const std::vector<std::pair<std::string, RequestPriority>> &
|
||||
get_links() const {
|
||||
const std::vector<std::pair<std::string, ResourceType>> &get_links() const {
|
||||
return links_;
|
||||
}
|
||||
void clear_links() {}
|
||||
|
||||
private:
|
||||
std::vector<std::pair<std::string, RequestPriority>> links_;
|
||||
std::vector<std::pair<std::string, ResourceType>> links_;
|
||||
};
|
||||
|
||||
#endif // !HAVE_LIBXML2
|
||||
|
||||
@@ -25,13 +25,25 @@
|
||||
#include "HttpServer.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#ifdef HAVE_SYS_SOCKET_H
|
||||
#include <sys/socket.h>
|
||||
#endif // HAVE_SYS_SOCKET_H
|
||||
#ifdef HAVE_NETDB_H
|
||||
#include <netdb.h>
|
||||
#endif // HAVE_NETDB_H
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif // HAVE_UNISTD_H
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif // HAVE_FCNTL_H
|
||||
#ifdef HAVE_NETINET_IN_H
|
||||
#include <netinet/in.h>
|
||||
#endif // HAVE_NETINET_IN_H
|
||||
#include <netinet/tcp.h>
|
||||
#ifdef HAVE_ARPA_INET_H
|
||||
#include <arpa/inet.h>
|
||||
#endif // HAVE_ARPA_INET_H
|
||||
|
||||
#include <cassert>
|
||||
#include <set>
|
||||
@@ -57,11 +69,6 @@
|
||||
namespace nghttp2 {
|
||||
|
||||
namespace {
|
||||
const std::string STATUS_200 = "200";
|
||||
const std::string STATUS_301 = "301";
|
||||
const std::string STATUS_304 = "304";
|
||||
const std::string STATUS_400 = "400";
|
||||
const std::string STATUS_404 = "404";
|
||||
const std::string DEFAULT_HTML = "index.html";
|
||||
const std::string NGHTTPD_SERVER = "nghttpd nghttp2/" NGHTTP2_VERSION;
|
||||
} // namespace
|
||||
@@ -94,9 +101,10 @@ template <typename Array> void append_nv(Stream *stream, const Array &nva) {
|
||||
Config::Config()
|
||||
: stream_read_timeout(60.), stream_write_timeout(60.),
|
||||
session_option(nullptr), data_ptr(nullptr), padding(0), num_worker(1),
|
||||
header_table_size(-1), port(0), verbose(false), daemon(false),
|
||||
verify_client(false), no_tls(false), error_gzip(false),
|
||||
early_response(false), hexdump(false) {
|
||||
max_concurrent_streams(100), header_table_size(-1), port(0),
|
||||
verbose(false), daemon(false), verify_client(false), no_tls(false),
|
||||
error_gzip(false), early_response(false), hexdump(false),
|
||||
echo_upload(false) {
|
||||
nghttp2_option_new(&session_option);
|
||||
nghttp2_option_set_recv_client_preface(session_option, 1);
|
||||
}
|
||||
@@ -171,9 +179,10 @@ void fill_callback(nghttp2_session_callbacks *callbacks, const Config *config);
|
||||
|
||||
class Sessions {
|
||||
public:
|
||||
Sessions(struct ev_loop *loop, const Config *config, SSL_CTX *ssl_ctx)
|
||||
: loop_(loop), config_(config), ssl_ctx_(ssl_ctx), callbacks_(nullptr),
|
||||
next_session_id_(1), tstamp_cached_(ev_now(loop)),
|
||||
Sessions(HttpServer *sv, struct ev_loop *loop, const Config *config,
|
||||
SSL_CTX *ssl_ctx)
|
||||
: sv_(sv), loop_(loop), config_(config), ssl_ctx_(ssl_ctx),
|
||||
callbacks_(nullptr), next_session_id_(1), tstamp_cached_(ev_now(loop)),
|
||||
cached_date_(util::http_date(tstamp_cached_)) {
|
||||
nghttp2_session_callbacks_new(&callbacks_);
|
||||
|
||||
@@ -244,9 +253,37 @@ public:
|
||||
}
|
||||
return cached_date_;
|
||||
}
|
||||
FileEntry *get_cached_fd(const std::string &path) {
|
||||
auto i = fd_cache_.find(path);
|
||||
if (i == std::end(fd_cache_)) {
|
||||
return nullptr;
|
||||
}
|
||||
auto &ent = (*i).second;
|
||||
++ent.usecount;
|
||||
return &ent;
|
||||
}
|
||||
FileEntry *cache_fd(const std::string &path, const FileEntry &ent) {
|
||||
auto rv = fd_cache_.emplace(path, ent);
|
||||
return &(*rv.first).second;
|
||||
}
|
||||
void release_fd(const std::string &path) {
|
||||
auto i = fd_cache_.find(path);
|
||||
if (i == std::end(fd_cache_)) {
|
||||
return;
|
||||
}
|
||||
auto &ent = (*i).second;
|
||||
if (--ent.usecount == 0) {
|
||||
close(ent.fd);
|
||||
fd_cache_.erase(i);
|
||||
}
|
||||
}
|
||||
const HttpServer *get_server() const { return sv_; }
|
||||
|
||||
private:
|
||||
std::set<Http2Handler *> handlers_;
|
||||
// cache for file descriptors to read file.
|
||||
std::map<std::string, FileEntry> fd_cache_;
|
||||
HttpServer *sv_;
|
||||
struct ev_loop *loop_;
|
||||
const Config *config_;
|
||||
SSL_CTX *ssl_ctx_;
|
||||
@@ -257,7 +294,8 @@ private:
|
||||
};
|
||||
|
||||
Stream::Stream(Http2Handler *handler, int32_t stream_id)
|
||||
: handler(handler), body_left(0), stream_id(stream_id), file(-1) {
|
||||
: handler(handler), file_ent(nullptr), body_length(0), body_offset(0),
|
||||
stream_id(stream_id), echo_upload(false) {
|
||||
auto config = handler->get_config();
|
||||
ev_timer_init(&rtimer, stream_timeout_cb, 0., config->stream_read_timeout);
|
||||
ev_timer_init(&wtimer, stream_timeout_cb, 0., config->stream_write_timeout);
|
||||
@@ -270,8 +308,9 @@ Stream::Stream(Http2Handler *handler, int32_t stream_id)
|
||||
}
|
||||
|
||||
Stream::~Stream() {
|
||||
if (file != -1) {
|
||||
close(file);
|
||||
if (file_ent != nullptr) {
|
||||
auto sessions = handler->get_sessions();
|
||||
sessions->release_fd(file_ent->path);
|
||||
}
|
||||
|
||||
auto loop = handler->get_loop();
|
||||
@@ -634,8 +673,10 @@ int Http2Handler::on_write() { return write_(*this); }
|
||||
int Http2Handler::connection_made() {
|
||||
int r;
|
||||
|
||||
auto config = sessions_->get_config();
|
||||
|
||||
r = nghttp2_session_server_new2(&session_, sessions_->get_callbacks(), this,
|
||||
sessions_->get_config()->session_option);
|
||||
config->session_option);
|
||||
if (r != 0) {
|
||||
return r;
|
||||
}
|
||||
@@ -643,11 +684,11 @@ int Http2Handler::connection_made() {
|
||||
size_t niv = 1;
|
||||
|
||||
entry[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
|
||||
entry[0].value = 100;
|
||||
entry[0].value = config->max_concurrent_streams;
|
||||
|
||||
if (sessions_->get_config()->header_table_size >= 0) {
|
||||
if (config->header_table_size >= 0) {
|
||||
entry[niv].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
|
||||
entry[niv].value = sessions_->get_config()->header_table_size;
|
||||
entry[niv].value = config->header_table_size;
|
||||
++niv;
|
||||
}
|
||||
r = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, entry.data(), niv);
|
||||
@@ -835,12 +876,12 @@ ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
|
||||
auto hd = static_cast<Http2Handler *>(user_data);
|
||||
auto stream = hd->get_stream(stream_id);
|
||||
|
||||
size_t nread = std::min(stream->body_left, static_cast<int64_t>(length));
|
||||
auto nread = std::min(stream->body_length - stream->body_offset,
|
||||
static_cast<int64_t>(length));
|
||||
|
||||
*data_flags |= NGHTTP2_DATA_FLAG_NO_COPY;
|
||||
|
||||
stream->body_left -= nread;
|
||||
if (nread == 0 || stream->body_left <= 0) {
|
||||
if (nread == 0 || stream->body_length == stream->body_offset + nread) {
|
||||
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
|
||||
|
||||
auto config = hd->get_config();
|
||||
@@ -872,59 +913,70 @@ ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
|
||||
}
|
||||
|
||||
namespace {
|
||||
void prepare_status_response(Stream *stream, Http2Handler *hd,
|
||||
const std::string &status) {
|
||||
int pipefd[2];
|
||||
if (status == STATUS_304 || pipe(pipefd) == -1) {
|
||||
hd->submit_response(status, stream->stream_id, 0);
|
||||
return;
|
||||
}
|
||||
std::string body;
|
||||
body.reserve(256);
|
||||
body = "<html><head><title>";
|
||||
body += status;
|
||||
body += "</title></head><body><h1>";
|
||||
body += status;
|
||||
body += "</h1><hr><address>";
|
||||
body += NGHTTPD_SERVER;
|
||||
body += " at port ";
|
||||
body += util::utos(hd->get_config()->port);
|
||||
body += "</address>";
|
||||
body += "</body></html>";
|
||||
void prepare_status_response(Stream *stream, Http2Handler *hd, int status) {
|
||||
auto sessions = hd->get_sessions();
|
||||
auto status_page = sessions->get_server()->get_status_page(status);
|
||||
auto file_ent = &status_page->file_ent;
|
||||
|
||||
// we don't set stream->file_ent since we don't want to expire it.
|
||||
stream->body_length = file_ent->length;
|
||||
nghttp2_data_provider data_prd;
|
||||
data_prd.source.fd = file_ent->fd;
|
||||
data_prd.read_callback = file_read_callback;
|
||||
|
||||
Headers headers;
|
||||
if (hd->get_config()->error_gzip) {
|
||||
gzFile write_fd = gzdopen(pipefd[1], "w");
|
||||
gzwrite(write_fd, body.c_str(), body.size());
|
||||
gzclose(write_fd);
|
||||
headers.emplace_back("content-encoding", "gzip");
|
||||
} else {
|
||||
ssize_t rv;
|
||||
|
||||
while ((rv = write(pipefd[1], body.c_str(), body.size())) == -1 &&
|
||||
errno == EINTR)
|
||||
;
|
||||
|
||||
if (rv != static_cast<ssize_t>(body.size())) {
|
||||
std::cerr << "Could not write all response body: " << rv << std::endl;
|
||||
}
|
||||
}
|
||||
close(pipefd[1]);
|
||||
|
||||
stream->file = pipefd[0];
|
||||
stream->body_left = body.size();
|
||||
nghttp2_data_provider data_prd;
|
||||
data_prd.source.fd = pipefd[0];
|
||||
data_prd.read_callback = file_read_callback;
|
||||
headers.emplace_back("content-type", "text/html; charset=UTF-8");
|
||||
hd->submit_response(status, stream->stream_id, headers, &data_prd);
|
||||
hd->submit_response(status_page->status, stream->stream_id, headers,
|
||||
&data_prd);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void prepare_echo_response(Stream *stream, Http2Handler *hd) {
|
||||
auto length = lseek(stream->file_ent->fd, 0, SEEK_END);
|
||||
if (length == -1) {
|
||||
hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
stream->body_length = length;
|
||||
if (lseek(stream->file_ent->fd, 0, SEEK_SET) == -1) {
|
||||
hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
nghttp2_data_provider data_prd;
|
||||
data_prd.source.fd = stream->file_ent->fd;
|
||||
data_prd.read_callback = file_read_callback;
|
||||
|
||||
Headers headers;
|
||||
headers.emplace_back("nghttpd-response", "echo");
|
||||
headers.emplace_back("content-length", util::utos(length));
|
||||
|
||||
hd->submit_response("200", stream->stream_id, headers, &data_prd);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
bool prepare_upload_temp_store(Stream *stream, Http2Handler *hd) {
|
||||
auto sessions = hd->get_sessions();
|
||||
|
||||
char tempfn[] = "/tmp/nghttpd.temp.XXXXXX";
|
||||
auto fd = mkstemp(tempfn);
|
||||
if (fd == -1) {
|
||||
return false;
|
||||
}
|
||||
unlink(tempfn);
|
||||
// Ordinary request never start with "echo:". The length is 0 for
|
||||
// now. We will update it when we get whole request body.
|
||||
stream->file_ent = sessions->cache_fd(std::string("echo:") + tempfn,
|
||||
FileEntry(tempfn, 0, 0, fd));
|
||||
stream->echo_upload = true;
|
||||
return true;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void prepare_redirect_response(Stream *stream, Http2Handler *hd,
|
||||
const std::string &path,
|
||||
const std::string &status) {
|
||||
const std::string &path, int status) {
|
||||
auto scheme =
|
||||
http2::get_header(stream->hdidx, http2::HD__SCHEME, stream->headers);
|
||||
auto authority =
|
||||
@@ -941,7 +993,10 @@ void prepare_redirect_response(Stream *stream, Http2Handler *hd,
|
||||
|
||||
auto headers = Headers{{"location", redirect_url}};
|
||||
|
||||
hd->submit_response(status, stream->stream_id, headers, nullptr);
|
||||
auto sessions = hd->get_sessions();
|
||||
auto status_page = sessions->get_server()->get_status_page(status);
|
||||
|
||||
hd->submit_response(status_page->status, stream->stream_id, headers, nullptr);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -973,9 +1028,15 @@ void prepare_response(Stream *stream, Http2Handler *hd,
|
||||
url = reqpath;
|
||||
}
|
||||
|
||||
url = util::percentDecode(url.begin(), url.end());
|
||||
auto sessions = hd->get_sessions();
|
||||
|
||||
url = util::percentDecode(std::begin(url), std::end(url));
|
||||
if (!util::check_path(url)) {
|
||||
prepare_status_response(stream, hd, STATUS_404);
|
||||
if (stream->file_ent) {
|
||||
sessions->release_fd(stream->file_ent->path);
|
||||
stream->file_ent = nullptr;
|
||||
}
|
||||
prepare_status_response(stream, hd, 404);
|
||||
return;
|
||||
}
|
||||
auto push_itr = hd->get_config()->push.find(url);
|
||||
@@ -988,55 +1049,76 @@ void prepare_response(Stream *stream, Http2Handler *hd,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string path = hd->get_config()->htdocs + url;
|
||||
if (path[path.size() - 1] == '/') {
|
||||
path += DEFAULT_HTML;
|
||||
}
|
||||
int file = open(path.c_str(), O_RDONLY | O_BINARY);
|
||||
if (file == -1) {
|
||||
prepare_status_response(stream, hd, STATUS_404);
|
||||
|
||||
if (stream->echo_upload) {
|
||||
assert(stream->file_ent);
|
||||
prepare_echo_response(stream, hd);
|
||||
return;
|
||||
}
|
||||
|
||||
struct stat buf;
|
||||
auto file_ent = sessions->get_cached_fd(path);
|
||||
|
||||
if (fstat(file, &buf) == -1) {
|
||||
close(file);
|
||||
prepare_status_response(stream, hd, STATUS_404);
|
||||
if (file_ent == nullptr) {
|
||||
int file = open(path.c_str(), O_RDONLY | O_BINARY);
|
||||
if (file == -1) {
|
||||
prepare_status_response(stream, hd, 404);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (buf.st_mode & S_IFDIR) {
|
||||
close(file);
|
||||
|
||||
if (query_pos == std::string::npos) {
|
||||
reqpath += "/";
|
||||
} else {
|
||||
reqpath.insert(query_pos, "/");
|
||||
return;
|
||||
}
|
||||
|
||||
prepare_redirect_response(stream, hd, reqpath, STATUS_301);
|
||||
struct stat buf;
|
||||
|
||||
if (fstat(file, &buf) == -1) {
|
||||
close(file);
|
||||
prepare_status_response(stream, hd, 404);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (buf.st_mode & S_IFDIR) {
|
||||
close(file);
|
||||
|
||||
if (query_pos == std::string::npos) {
|
||||
reqpath += "/";
|
||||
} else {
|
||||
reqpath.insert(query_pos, "/");
|
||||
}
|
||||
|
||||
prepare_redirect_response(stream, hd, reqpath, 301);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (last_mod_found && buf.st_mtime <= last_mod) {
|
||||
close(file);
|
||||
prepare_status_response(stream, hd, 304);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
file_ent = sessions->cache_fd(
|
||||
path, FileEntry(path, buf.st_size, buf.st_mtime, file));
|
||||
} else if (last_mod_found && file_ent->mtime <= last_mod) {
|
||||
sessions->release_fd(file_ent->path);
|
||||
prepare_status_response(stream, hd, 304);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
stream->file = file;
|
||||
stream->body_left = buf.st_size;
|
||||
stream->file_ent = file_ent;
|
||||
stream->body_length = file_ent->length;
|
||||
|
||||
nghttp2_data_provider data_prd;
|
||||
|
||||
data_prd.source.fd = file;
|
||||
data_prd.source.fd = file_ent->fd;
|
||||
data_prd.read_callback = file_read_callback;
|
||||
|
||||
if (last_mod_found && buf.st_mtime <= last_mod) {
|
||||
prepare_status_response(stream, hd, STATUS_304);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
hd->submit_file_response(STATUS_200, stream, buf.st_mtime, buf.st_size,
|
||||
hd->submit_file_response("200", stream, file_ent->mtime, file_ent->length,
|
||||
&data_prd);
|
||||
}
|
||||
} // namespace
|
||||
@@ -1108,7 +1190,7 @@ int hd_on_frame_recv_callback(nghttp2_session *session,
|
||||
|
||||
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||
remove_stream_read_timeout(stream);
|
||||
if (!hd->get_config()->early_response) {
|
||||
if (stream->echo_upload || !hd->get_config()->early_response) {
|
||||
prepare_response(stream, hd);
|
||||
}
|
||||
} else {
|
||||
@@ -1132,14 +1214,22 @@ int hd_on_frame_recv_callback(nghttp2_session *session,
|
||||
hd->submit_non_final_response("100", frame->hd.stream_id);
|
||||
}
|
||||
|
||||
if (hd->get_config()->early_response) {
|
||||
auto &method = http2::get_header(stream->hdidx, http2::HD__METHOD,
|
||||
stream->headers)->value;
|
||||
if (hd->get_config()->echo_upload &&
|
||||
(method == "POST" || method == "PUT")) {
|
||||
if (!prepare_upload_temp_store(stream, hd)) {
|
||||
hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR);
|
||||
return 0;
|
||||
}
|
||||
} else if (hd->get_config()->early_response) {
|
||||
prepare_response(stream, hd);
|
||||
}
|
||||
}
|
||||
|
||||
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||
remove_stream_read_timeout(stream);
|
||||
if (!hd->get_config()->early_response) {
|
||||
if (stream->echo_upload || !hd->get_config()->early_response) {
|
||||
prepare_response(stream, hd);
|
||||
}
|
||||
} else {
|
||||
@@ -1220,6 +1310,7 @@ int send_data_callback(nghttp2_session *session, nghttp2_frame *frame,
|
||||
auto hd = static_cast<Http2Handler *>(user_data);
|
||||
auto wb = hd->get_wb();
|
||||
auto padlen = frame->data.padlen;
|
||||
auto stream = hd->get_stream(frame->hd.stream_id);
|
||||
|
||||
if (wb->wleft() < 9 + length + padlen) {
|
||||
return NGHTTP2_ERR_WOULDBLOCK;
|
||||
@@ -1237,17 +1328,18 @@ int send_data_callback(nghttp2_session *session, nghttp2_frame *frame,
|
||||
|
||||
while (length) {
|
||||
ssize_t nread;
|
||||
while ((nread = read(fd, p, length)) == -1 && errno == EINTR)
|
||||
while ((nread = pread(fd, p, length, stream->body_offset)) == -1 &&
|
||||
errno == EINTR)
|
||||
;
|
||||
|
||||
if (nread == -1) {
|
||||
auto stream = hd->get_stream(frame->hd.stream_id);
|
||||
remove_stream_read_timeout(stream);
|
||||
remove_stream_write_timeout(stream);
|
||||
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
stream->body_offset += nread;
|
||||
length -= nread;
|
||||
p += nread;
|
||||
}
|
||||
@@ -1283,6 +1375,21 @@ int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (stream->echo_upload) {
|
||||
assert(stream->file_ent);
|
||||
while (len) {
|
||||
ssize_t n;
|
||||
while ((n = write(stream->file_ent->fd, data, len)) == -1 &&
|
||||
errno == EINTR)
|
||||
;
|
||||
if (n == -1) {
|
||||
hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR);
|
||||
return 0;
|
||||
}
|
||||
len -= n;
|
||||
data += n;
|
||||
}
|
||||
}
|
||||
// TODO Handle POST
|
||||
|
||||
add_stream_read_timeout(stream);
|
||||
@@ -1380,7 +1487,7 @@ void run_worker(Worker *worker) {
|
||||
|
||||
class AcceptHandler {
|
||||
public:
|
||||
AcceptHandler(Sessions *sessions, const Config *config)
|
||||
AcceptHandler(HttpServer *sv, Sessions *sessions, const Config *config)
|
||||
: sessions_(sessions), config_(config), next_worker_(0) {
|
||||
if (config_->num_worker == 1) {
|
||||
return;
|
||||
@@ -1392,7 +1499,7 @@ public:
|
||||
auto worker = make_unique<Worker>();
|
||||
auto loop = ev_loop_new(0);
|
||||
worker->sessions =
|
||||
make_unique<Sessions>(loop, config_, sessions_->get_ssl_ctx());
|
||||
make_unique<Sessions>(sv, loop, config_, sessions_->get_ssl_ctx());
|
||||
ev_async_init(&worker->w, worker_acceptcb);
|
||||
worker->w.data = worker.get();
|
||||
ev_async_start(loop, &worker->w);
|
||||
@@ -1476,7 +1583,61 @@ void acceptcb(struct ev_loop *loop, ev_io *w, int revents) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
HttpServer::HttpServer(const Config *config) : config_(config) {}
|
||||
namespace {
|
||||
FileEntry make_status_body(int status, uint16_t port) {
|
||||
std::string body;
|
||||
body = "<html><head><title>";
|
||||
body += http2::get_status_string(status);
|
||||
body += "</title></head><body><h1>";
|
||||
body += http2::get_status_string(status);
|
||||
body += "</h1><hr><address>";
|
||||
body += NGHTTPD_SERVER;
|
||||
body += " at port ";
|
||||
body += util::utos(port);
|
||||
body += "</address>";
|
||||
body += "</body></html>";
|
||||
|
||||
char tempfn[] = "/tmp/nghttpd.temp.XXXXXX";
|
||||
int fd = mkstemp(tempfn);
|
||||
if (fd == -1) {
|
||||
auto error = errno;
|
||||
std::cerr << "Could not open status response body file: errno=" << error;
|
||||
assert(0);
|
||||
}
|
||||
unlink(tempfn);
|
||||
ssize_t nwrite;
|
||||
while ((nwrite = write(fd, body.c_str(), body.size())) == -1 &&
|
||||
errno == EINTR)
|
||||
;
|
||||
if (nwrite == -1) {
|
||||
auto error = errno;
|
||||
std::cerr << "Could not write status response body into file: errno="
|
||||
<< error;
|
||||
assert(0);
|
||||
}
|
||||
|
||||
return FileEntry(util::utos(status), nwrite, 0, fd);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// index into HttpServer::status_pages_
|
||||
enum {
|
||||
IDX_200,
|
||||
IDX_301,
|
||||
IDX_304,
|
||||
IDX_400,
|
||||
IDX_404,
|
||||
};
|
||||
|
||||
HttpServer::HttpServer(const Config *config) : config_(config) {
|
||||
status_pages_ = std::vector<StatusPage>{
|
||||
{"200", make_status_body(200, config_->port)},
|
||||
{"301", make_status_body(301, config_->port)},
|
||||
{"304", make_status_body(304, config_->port)},
|
||||
{"400", make_status_body(400, config_->port)},
|
||||
{"404", make_status_body(404, config_->port)},
|
||||
};
|
||||
}
|
||||
|
||||
namespace {
|
||||
int next_proto_cb(SSL *s, const unsigned char **data, unsigned int *len,
|
||||
@@ -1497,14 +1658,14 @@ int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) {
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int start_listen(struct ev_loop *loop, Sessions *sessions,
|
||||
int start_listen(HttpServer *sv, struct ev_loop *loop, Sessions *sessions,
|
||||
const Config *config) {
|
||||
addrinfo hints;
|
||||
int r;
|
||||
bool ok = false;
|
||||
const char *addr = nullptr;
|
||||
|
||||
auto acceptor = std::make_shared<AcceptHandler>(sessions, config);
|
||||
auto acceptor = std::make_shared<AcceptHandler>(sv, sessions, config);
|
||||
auto service = util::utos(config->port);
|
||||
|
||||
memset(&hints, 0, sizeof(addrinfo));
|
||||
@@ -1699,8 +1860,8 @@ int HttpServer::run() {
|
||||
|
||||
auto loop = EV_DEFAULT;
|
||||
|
||||
Sessions sessions(loop, config_, ssl_ctx);
|
||||
if (start_listen(loop, &sessions, config_) != 0) {
|
||||
Sessions sessions(this, loop, config_, ssl_ctx);
|
||||
if (start_listen(this, loop, &sessions, config_) != 0) {
|
||||
std::cerr << "Could not listen" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
@@ -1711,4 +1872,22 @@ int HttpServer::run() {
|
||||
|
||||
const Config *HttpServer::get_config() const { return config_; }
|
||||
|
||||
const StatusPage *HttpServer::get_status_page(int status) const {
|
||||
switch (status) {
|
||||
case 200:
|
||||
return &status_pages_[IDX_200];
|
||||
case 301:
|
||||
return &status_pages_[IDX_301];
|
||||
case 304:
|
||||
return &status_pages_[IDX_304];
|
||||
case 400:
|
||||
return &status_pages_[IDX_400];
|
||||
case 404:
|
||||
return &status_pages_[IDX_404];
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace nghttp2
|
||||
|
||||
@@ -27,9 +27,9 @@
|
||||
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <cinttypes>
|
||||
#include <cstdlib>
|
||||
|
||||
#include <string>
|
||||
@@ -63,6 +63,7 @@ struct Config {
|
||||
void *data_ptr;
|
||||
size_t padding;
|
||||
size_t num_worker;
|
||||
size_t max_concurrent_streams;
|
||||
ssize_t header_table_size;
|
||||
uint16_t port;
|
||||
bool verbose;
|
||||
@@ -72,21 +73,36 @@ struct Config {
|
||||
bool error_gzip;
|
||||
bool early_response;
|
||||
bool hexdump;
|
||||
bool echo_upload;
|
||||
Config();
|
||||
~Config();
|
||||
};
|
||||
|
||||
class Http2Handler;
|
||||
|
||||
struct FileEntry {
|
||||
FileEntry(std::string path, int64_t length, int64_t mtime, int fd)
|
||||
: path(std::move(path)), length(length), mtime(mtime), dlprev(nullptr),
|
||||
dlnext(nullptr), fd(fd), usecount(1) {}
|
||||
std::string path;
|
||||
int64_t length;
|
||||
int64_t mtime;
|
||||
FileEntry *dlprev, *dlnext;
|
||||
int fd;
|
||||
int usecount;
|
||||
};
|
||||
|
||||
struct Stream {
|
||||
Headers headers;
|
||||
Http2Handler *handler;
|
||||
FileEntry *file_ent;
|
||||
ev_timer rtimer;
|
||||
ev_timer wtimer;
|
||||
int64_t body_left;
|
||||
int64_t body_length;
|
||||
int64_t body_offset;
|
||||
int32_t stream_id;
|
||||
int file;
|
||||
http2::HeaderIndex hdidx;
|
||||
bool echo_upload;
|
||||
Stream(Http2Handler *handler, int32_t stream_id);
|
||||
~Stream();
|
||||
};
|
||||
@@ -160,14 +176,21 @@ private:
|
||||
int fd_;
|
||||
};
|
||||
|
||||
struct StatusPage {
|
||||
std::string status;
|
||||
FileEntry file_ent;
|
||||
};
|
||||
|
||||
class HttpServer {
|
||||
public:
|
||||
HttpServer(const Config *config);
|
||||
int listen();
|
||||
int run();
|
||||
const Config *get_config() const;
|
||||
const StatusPage *get_status_page(int status) const;
|
||||
|
||||
private:
|
||||
std::vector<StatusPage> status_pages_;
|
||||
const Config *config_;
|
||||
};
|
||||
|
||||
|
||||
@@ -23,11 +23,21 @@
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include <sys/types.h>
|
||||
#ifdef HAVE_SYS_SOCKET_H
|
||||
#include <sys/socket.h>
|
||||
#endif // HAVE_SYS_SOCKET_H
|
||||
#ifdef HAVE_NETDB_H
|
||||
#include <netdb.h>
|
||||
#endif // HAVE_NETDB_H
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif // HAVE_UNISTD_H
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif // HAVE_FCNTL_H
|
||||
#ifdef HAVE_NETINET_IN_H
|
||||
#include <netinet/in.h>
|
||||
#endif // HAVE_NETINET_IN_H
|
||||
#include <netinet/tcp.h>
|
||||
#include <poll.h>
|
||||
|
||||
|
||||
@@ -27,9 +27,11 @@
|
||||
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <cinttypes>
|
||||
#include <cstdlib>
|
||||
#ifdef HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#endif // HAVE_SYS_TIME_H
|
||||
#include <poll.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
@@ -89,10 +89,7 @@ void session_impl::connected(tcp::resolver::iterator endpoint_it) {
|
||||
}
|
||||
|
||||
void session_impl::not_connected(const boost::system::error_code &ec) {
|
||||
auto &error_cb = on_error();
|
||||
if (error_cb) {
|
||||
error_cb(ec);
|
||||
}
|
||||
call_error_cb(ec);
|
||||
}
|
||||
|
||||
void session_impl::on_connect(connect_cb cb) { connect_cb_ = std::move(cb); }
|
||||
@@ -103,6 +100,14 @@ const connect_cb &session_impl::on_connect() const { return connect_cb_; }
|
||||
|
||||
const error_cb &session_impl::on_error() const { return error_cb_; }
|
||||
|
||||
void session_impl::call_error_cb(const boost::system::error_code &ec) {
|
||||
auto &error_cb = on_error();
|
||||
if (!error_cb) {
|
||||
return;
|
||||
}
|
||||
error_cb(ec);
|
||||
}
|
||||
|
||||
namespace {
|
||||
int on_begin_headers_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame, void *user_data) {
|
||||
@@ -308,10 +313,7 @@ bool session_impl::setup_session() {
|
||||
|
||||
auto rv = nghttp2_session_client_new(&session_, callbacks, this);
|
||||
if (rv != 0) {
|
||||
auto &error_cb = on_error();
|
||||
if (error_cb) {
|
||||
error_cb(make_error_code(static_cast<nghttp2_error>(rv)));
|
||||
}
|
||||
call_error_cb(make_error_code(static_cast<nghttp2_error>(rv)));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -528,6 +530,7 @@ void session_impl::do_read() {
|
||||
std::size_t bytes_transferred) {
|
||||
if (ec) {
|
||||
if (ec.value() == boost::asio::error::operation_aborted) {
|
||||
call_error_cb(ec);
|
||||
shutdown_socket();
|
||||
}
|
||||
return;
|
||||
@@ -540,6 +543,8 @@ void session_impl::do_read() {
|
||||
nghttp2_session_mem_recv(session_, rb_.data(), bytes_transferred);
|
||||
|
||||
if (rv != static_cast<ssize_t>(bytes_transferred)) {
|
||||
call_error_cb(make_error_code(
|
||||
static_cast<nghttp2_error>(rv < 0 ? rv : NGHTTP2_ERR_PROTO)));
|
||||
shutdown_socket();
|
||||
return;
|
||||
}
|
||||
@@ -577,6 +582,7 @@ void session_impl::do_write() {
|
||||
const uint8_t *data;
|
||||
auto n = nghttp2_session_mem_send(session_, &data);
|
||||
if (n < 0) {
|
||||
call_error_cb(make_error_code(static_cast<nghttp2_error>(n)));
|
||||
shutdown_socket();
|
||||
return;
|
||||
}
|
||||
@@ -606,6 +612,8 @@ void session_impl::do_write() {
|
||||
|
||||
write_socket([this](const boost::system::error_code &ec, std::size_t n) {
|
||||
if (ec) {
|
||||
call_error_cb(ec);
|
||||
shutdown_socket();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -97,6 +97,7 @@ protected:
|
||||
private:
|
||||
bool should_stop() const;
|
||||
bool setup_session();
|
||||
void call_error_cb(const boost::system::error_code &ec);
|
||||
|
||||
boost::asio::io_service &io_service_;
|
||||
tcp::resolver resolver_;
|
||||
|
||||
@@ -35,8 +35,6 @@
|
||||
//
|
||||
#include "asio_io_service_pool.h"
|
||||
|
||||
#include <future>
|
||||
|
||||
namespace nghttp2 {
|
||||
|
||||
namespace asio_http2 {
|
||||
@@ -56,19 +54,23 @@ io_service_pool::io_service_pool(std::size_t pool_size) : next_io_service_(0) {
|
||||
}
|
||||
}
|
||||
|
||||
void io_service_pool::run() {
|
||||
void io_service_pool::run(bool asynchronous) {
|
||||
// Create a pool of threads to run all of the io_services.
|
||||
auto futs = std::vector<std::future<std::size_t>>();
|
||||
|
||||
for (std::size_t i = 0; i < io_services_.size(); ++i) {
|
||||
futs.push_back(std::async(std::launch::async,
|
||||
(size_t (boost::asio::io_service::*)(void)) &
|
||||
boost::asio::io_service::run,
|
||||
io_services_[i]));
|
||||
futures_.push_back(std::async(std::launch::async,
|
||||
(size_t (boost::asio::io_service::*)(void)) &
|
||||
boost::asio::io_service::run,
|
||||
io_services_[i]));
|
||||
}
|
||||
|
||||
if (!asynchronous) {
|
||||
join();
|
||||
}
|
||||
}
|
||||
|
||||
void io_service_pool::join() {
|
||||
// Wait for all threads in the pool to exit.
|
||||
for (auto &fut : futs) {
|
||||
for (auto &fut : futures_) {
|
||||
fut.get();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <future>
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
@@ -58,11 +59,14 @@ public:
|
||||
explicit io_service_pool(std::size_t pool_size);
|
||||
|
||||
/// Run all io_service objects in the pool.
|
||||
void run();
|
||||
void run(bool asynchronous = false);
|
||||
|
||||
/// Stop all io_service objects in the pool.
|
||||
void stop();
|
||||
|
||||
/// Join on all io_service objects in the pool.
|
||||
void join();
|
||||
|
||||
/// Get an io_service to use.
|
||||
boost::asio::io_service &get_io_service();
|
||||
|
||||
@@ -75,6 +79,9 @@ private:
|
||||
|
||||
/// The next io_service to use for a connection.
|
||||
std::size_t next_io_service_;
|
||||
|
||||
/// Futures to all the io_service objects
|
||||
std::vector<std::future<std::size_t>> futures_;
|
||||
};
|
||||
|
||||
} // namespace asio_http2
|
||||
|
||||
@@ -50,7 +50,7 @@ boost::system::error_code
|
||||
server::listen_and_serve(boost::system::error_code &ec,
|
||||
boost::asio::ssl::context *tls_context,
|
||||
const std::string &address, const std::string &port,
|
||||
int backlog, serve_mux &mux) {
|
||||
int backlog, serve_mux &mux, bool asynchronous) {
|
||||
ec.clear();
|
||||
|
||||
if (bind_and_listen(ec, address, port, backlog)) {
|
||||
@@ -65,7 +65,7 @@ server::listen_and_serve(boost::system::error_code &ec,
|
||||
}
|
||||
}
|
||||
|
||||
io_service_pool_.run();
|
||||
io_service_pool_.run(asynchronous);
|
||||
|
||||
return ec;
|
||||
}
|
||||
@@ -156,6 +156,10 @@ void server::start_accept(tcp::acceptor &acceptor, serve_mux &mux) {
|
||||
});
|
||||
}
|
||||
|
||||
void server::stop() { io_service_pool_.stop(); }
|
||||
|
||||
void server::join() { io_service_pool_.join(); }
|
||||
|
||||
} // namespace server
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp2
|
||||
|
||||
@@ -69,7 +69,9 @@ public:
|
||||
listen_and_serve(boost::system::error_code &ec,
|
||||
boost::asio::ssl::context *tls_context,
|
||||
const std::string &address, const std::string &port,
|
||||
int backlog, serve_mux &mux);
|
||||
int backlog, serve_mux &mux, bool asynchronous = false);
|
||||
void join();
|
||||
void stop();
|
||||
|
||||
private:
|
||||
/// Initiate an asynchronous accept operation.
|
||||
|
||||
@@ -54,15 +54,15 @@ http2 &http2::operator=(http2 &&other) noexcept {
|
||||
|
||||
boost::system::error_code http2::listen_and_serve(boost::system::error_code &ec,
|
||||
const std::string &address,
|
||||
const std::string &port) {
|
||||
return impl_->listen_and_serve(ec, nullptr, address, port);
|
||||
const std::string &port,
|
||||
bool asynchronous) {
|
||||
return impl_->listen_and_serve(ec, nullptr, address, port, asynchronous);
|
||||
}
|
||||
|
||||
boost::system::error_code
|
||||
http2::listen_and_serve(boost::system::error_code &ec,
|
||||
boost::asio::ssl::context &tls_context,
|
||||
const std::string &address, const std::string &port) {
|
||||
return impl_->listen_and_serve(ec, &tls_context, address, port);
|
||||
boost::system::error_code http2::listen_and_serve(
|
||||
boost::system::error_code &ec, boost::asio::ssl::context &tls_context,
|
||||
const std::string &address, const std::string &port, bool asynchronous) {
|
||||
return impl_->listen_and_serve(ec, &tls_context, address, port, asynchronous);
|
||||
}
|
||||
|
||||
void http2::num_threads(size_t num_threads) { impl_->num_threads(num_threads); }
|
||||
@@ -73,6 +73,10 @@ bool http2::handle(std::string pattern, request_cb cb) {
|
||||
return impl_->handle(std::move(pattern), std::move(cb));
|
||||
}
|
||||
|
||||
void http2::stop() { impl_->stop(); }
|
||||
|
||||
void http2::join() { return impl_->join(); }
|
||||
|
||||
} // namespace server
|
||||
|
||||
} // namespace asio_http2
|
||||
|
||||
@@ -41,9 +41,10 @@ http2_impl::http2_impl() : num_threads_(1), backlog_(-1) {}
|
||||
|
||||
boost::system::error_code http2_impl::listen_and_serve(
|
||||
boost::system::error_code &ec, boost::asio::ssl::context *tls_context,
|
||||
const std::string &address, const std::string &port) {
|
||||
return server(num_threads_)
|
||||
.listen_and_serve(ec, tls_context, address, port, backlog_, mux_);
|
||||
const std::string &address, const std::string &port, bool asynchronous) {
|
||||
server_.reset(new server(num_threads_));
|
||||
return server_->listen_and_serve(ec, tls_context, address, port, backlog_,
|
||||
mux_, asynchronous);
|
||||
}
|
||||
|
||||
void http2_impl::num_threads(size_t num_threads) { num_threads_ = num_threads; }
|
||||
@@ -54,6 +55,10 @@ bool http2_impl::handle(std::string pattern, request_cb cb) {
|
||||
return mux_.handle(std::move(pattern), std::move(cb));
|
||||
}
|
||||
|
||||
void http2_impl::stop() { return server_->stop(); }
|
||||
|
||||
void http2_impl::join() { return server_->join(); }
|
||||
|
||||
} // namespace server
|
||||
|
||||
} // namespace asio_http2
|
||||
|
||||
@@ -42,13 +42,14 @@ class server;
|
||||
class http2_impl {
|
||||
public:
|
||||
http2_impl();
|
||||
boost::system::error_code
|
||||
listen_and_serve(boost::system::error_code &ec,
|
||||
boost::asio::ssl::context *tls_context,
|
||||
const std::string &address, const std::string &port);
|
||||
boost::system::error_code listen_and_serve(
|
||||
boost::system::error_code &ec, boost::asio::ssl::context *tls_context,
|
||||
const std::string &address, const std::string &port, bool asynchronous);
|
||||
void num_threads(size_t num_threads);
|
||||
void backlog(int backlog);
|
||||
bool handle(std::string pattern, request_cb cb);
|
||||
void stop();
|
||||
void join();
|
||||
|
||||
private:
|
||||
std::unique_ptr<server> server_;
|
||||
|
||||
@@ -25,6 +25,10 @@
|
||||
#ifndef BUFFER_TEST_H
|
||||
#define BUFFER_TEST_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif // HAVE_CONFIG_H
|
||||
|
||||
namespace nghttp2 {
|
||||
|
||||
void test_buffer_write(void);
|
||||
|
||||
@@ -57,6 +57,9 @@ json_t *dump_header(const uint8_t *name, size_t namelen, const uint8_t *value,
|
||||
size_t valuelen) {
|
||||
json_t *nv_pair = json_object();
|
||||
char *cname = malloc(namelen + 1);
|
||||
if (cname == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
memcpy(cname, name, namelen);
|
||||
cname[namelen] = '\0';
|
||||
json_object_set_new(nv_pair, cname, json_pack("s#", value, valuelen));
|
||||
|
||||
@@ -26,7 +26,9 @@
|
||||
#include <config.h>
|
||||
#endif // HAVE_CONFIG_H
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif // HAVE_UNISTD_H
|
||||
#include <getopt.h>
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
188
src/h2load.cc
188
src/h2load.cc
@@ -26,10 +26,14 @@
|
||||
|
||||
#include <getopt.h>
|
||||
#include <signal.h>
|
||||
#ifdef HAVE_NETINET_IN_H
|
||||
#include <netinet/in.h>
|
||||
#endif // HAVE_NETINET_IN_H
|
||||
#include <netinet/tcp.h>
|
||||
#include <sys/stat.h>
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif // HAVE_FCNTL_H
|
||||
|
||||
#include <cstdio>
|
||||
#include <cassert>
|
||||
@@ -69,7 +73,7 @@ namespace h2load {
|
||||
|
||||
Config::Config()
|
||||
: data_length(-1), addrs(nullptr), nreqs(1), nclients(1), nthreads(1),
|
||||
max_concurrent_streams(-1), window_bits(16), connection_window_bits(16),
|
||||
max_concurrent_streams(-1), window_bits(30), connection_window_bits(30),
|
||||
no_tls_proto(PROTO_HTTP2), data_fd(-1), port(0), default_port(0),
|
||||
verbose(false) {}
|
||||
|
||||
@@ -150,8 +154,8 @@ void readcb(struct ev_loop *loop, ev_io *w, int revents) {
|
||||
|
||||
Client::Client(Worker *worker, size_t req_todo)
|
||||
: worker(worker), ssl(nullptr), next_addr(config.addrs), reqidx(0),
|
||||
state(CLIENT_IDLE), req_todo(req_todo), req_started(0), req_done(0),
|
||||
fd(-1) {
|
||||
state(CLIENT_IDLE), first_byte_received(false), req_todo(req_todo),
|
||||
req_started(0), req_done(0), fd(-1) {
|
||||
ev_io_init(&wev, writecb, 0, EV_WRITE);
|
||||
ev_io_init(&rev, readcb, 0, EV_READ);
|
||||
|
||||
@@ -165,6 +169,8 @@ int Client::do_read() { return readfn(*this); }
|
||||
int Client::do_write() { return writefn(*this); }
|
||||
|
||||
int Client::connect() {
|
||||
record_start_time(&worker->stats);
|
||||
|
||||
while (next_addr) {
|
||||
auto addr = next_addr;
|
||||
next_addr = next_addr->ai_next;
|
||||
@@ -469,6 +475,8 @@ int Client::connection_made() {
|
||||
|
||||
session->on_connect();
|
||||
|
||||
record_connect_time(&worker->stats);
|
||||
|
||||
auto nreq =
|
||||
std::min(req_todo - req_started, (size_t)config.max_concurrent_streams);
|
||||
|
||||
@@ -519,6 +527,11 @@ int Client::read_clear() {
|
||||
if (on_read(buf, nread) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!first_byte_received) {
|
||||
first_byte_received = true;
|
||||
record_ttfb(&worker->stats);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -641,6 +654,11 @@ int Client::read_tls() {
|
||||
if (on_read(buf, rv) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!first_byte_received) {
|
||||
first_byte_received = true;
|
||||
record_ttfb(&worker->stats);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -691,6 +709,18 @@ void Client::record_request_time(RequestStat *req_stat) {
|
||||
req_stat->request_time = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
void Client::record_start_time(Stats *stat) {
|
||||
stat->start_times.push_back(std::chrono::steady_clock::now());
|
||||
}
|
||||
|
||||
void Client::record_connect_time(Stats *stat) {
|
||||
stat->connect_times.push_back(std::chrono::steady_clock::now());
|
||||
}
|
||||
|
||||
void Client::record_ttfb(Stats *stat) {
|
||||
stat->ttfbs.push_back(std::chrono::steady_clock::now());
|
||||
}
|
||||
|
||||
void Client::signal_write() { ev_io_start(worker->loop, &wev); }
|
||||
|
||||
Worker::Worker(uint32_t id, SSL_CTX *ssl_ctx, size_t req_todo, size_t nclients,
|
||||
@@ -731,70 +761,99 @@ void Worker::run() {
|
||||
}
|
||||
|
||||
namespace {
|
||||
double within_sd(const std::vector<std::unique_ptr<Worker>> &workers,
|
||||
const std::chrono::microseconds &mean,
|
||||
const std::chrono::microseconds &sd, size_t n) {
|
||||
auto upper = mean.count() + sd.count();
|
||||
auto lower = mean.count() - sd.count();
|
||||
size_t m = 0;
|
||||
for (const auto &w : workers) {
|
||||
for (const auto &req_stat : w->stats.req_stats) {
|
||||
if (!req_stat.completed) {
|
||||
continue;
|
||||
}
|
||||
auto t = std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
req_stat.stream_close_time - req_stat.request_time);
|
||||
if (lower <= t.count() && t.count() <= upper) {
|
||||
++m;
|
||||
}
|
||||
}
|
||||
// Returns percentage of number of samples within mean +/- sd.
|
||||
template <typename Duration>
|
||||
double within_sd(const std::vector<Duration> &samples, const Duration &mean,
|
||||
const Duration &sd) {
|
||||
if (samples.size() == 0) {
|
||||
return 0.0;
|
||||
}
|
||||
return (m / static_cast<double>(n)) * 100;
|
||||
auto lower = mean - sd;
|
||||
auto upper = mean + sd;
|
||||
auto m = std::count_if(
|
||||
std::begin(samples), std::end(samples),
|
||||
[&lower, &upper](const Duration &t) { return lower <= t && t <= upper; });
|
||||
return (m / static_cast<double>(samples.size())) * 100;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
// Computes statistics using |samples|. The min, max, mean, sd, and
|
||||
// percentage of number of samples within mean +/- sd are computed.
|
||||
template <typename Duration>
|
||||
TimeStat<Duration> compute_time_stat(const std::vector<Duration> &samples) {
|
||||
if (samples.size() == 0) {
|
||||
return {Duration::zero(), Duration::zero(), Duration::zero(),
|
||||
Duration::zero(), 0.0};
|
||||
}
|
||||
// standard deviation calculated using Rapid calculation method:
|
||||
// http://en.wikipedia.org/wiki/Standard_deviation#Rapid_calculation_methods
|
||||
double a = 0, q = 0;
|
||||
size_t n = 0;
|
||||
int64_t sum = 0;
|
||||
auto res = TimeStat<Duration>{Duration::max(), Duration::min()};
|
||||
for (const auto &t : samples) {
|
||||
++n;
|
||||
res.min = std::min(res.min, t);
|
||||
res.max = std::max(res.max, t);
|
||||
sum += t.count();
|
||||
|
||||
auto na = a + (t.count() - a) / n;
|
||||
q += (t.count() - a) * (t.count() - na);
|
||||
a = na;
|
||||
}
|
||||
|
||||
res.mean = Duration(sum / n);
|
||||
res.sd = Duration(static_cast<typename Duration::rep>(sqrt(q / n)));
|
||||
res.within_sd = within_sd(samples, res.mean, res.sd);
|
||||
|
||||
return res;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
TimeStats
|
||||
process_time_stats(const std::vector<std::unique_ptr<Worker>> &workers) {
|
||||
auto ts = TimeStats();
|
||||
int64_t sum = 0;
|
||||
size_t n = 0;
|
||||
size_t nrequest_times = 0, nttfb_times = 0;
|
||||
for (const auto &w : workers) {
|
||||
nrequest_times += w->stats.req_stats.size();
|
||||
nttfb_times += w->stats.ttfbs.size();
|
||||
}
|
||||
|
||||
ts.time_min = std::chrono::microseconds::max();
|
||||
ts.time_max = std::chrono::microseconds::min();
|
||||
ts.within_sd = 0.;
|
||||
std::vector<std::chrono::microseconds> request_times;
|
||||
request_times.reserve(nrequest_times);
|
||||
std::vector<std::chrono::microseconds> connect_times, ttfb_times;
|
||||
connect_times.reserve(nttfb_times);
|
||||
ttfb_times.reserve(nttfb_times);
|
||||
|
||||
// standard deviation calculated using Rapid calculation method:
|
||||
// http://en.wikipedia.org/wiki/Standard_deviation#Rapid_calculation_methods
|
||||
double a = 0, q = 0;
|
||||
for (const auto &w : workers) {
|
||||
for (const auto &req_stat : w->stats.req_stats) {
|
||||
if (!req_stat.completed) {
|
||||
continue;
|
||||
}
|
||||
++n;
|
||||
auto t = std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
req_stat.stream_close_time - req_stat.request_time);
|
||||
ts.time_min = std::min(ts.time_min, t);
|
||||
ts.time_max = std::max(ts.time_max, t);
|
||||
sum += t.count();
|
||||
request_times.push_back(
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
req_stat.stream_close_time - req_stat.request_time));
|
||||
}
|
||||
|
||||
auto na = a + (t.count() - a) / n;
|
||||
q = q + (t.count() - a) * (t.count() - na);
|
||||
a = na;
|
||||
const auto &stat = w->stats;
|
||||
// rule out cases where we started but didn't connect or get the
|
||||
// first byte (errors). We will get connect event before FFTB.
|
||||
assert(stat.start_times.size() >= stat.ttfbs.size());
|
||||
assert(stat.connect_times.size() >= stat.ttfbs.size());
|
||||
for (size_t i = 0; i < stat.ttfbs.size(); ++i) {
|
||||
connect_times.push_back(
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
stat.connect_times[i] - stat.start_times[i]));
|
||||
|
||||
ttfb_times.push_back(
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
stat.ttfbs[i] - stat.start_times[i]));
|
||||
}
|
||||
}
|
||||
if (n == 0) {
|
||||
ts.time_max = ts.time_min = std::chrono::microseconds::zero();
|
||||
return ts;
|
||||
}
|
||||
|
||||
ts.time_mean = std::chrono::microseconds(sum / n);
|
||||
ts.time_sd = std::chrono::microseconds(
|
||||
static_cast<std::chrono::microseconds::rep>(sqrt(q / n)));
|
||||
|
||||
ts.within_sd = within_sd(workers, ts.time_mean, ts.time_sd, n);
|
||||
return ts;
|
||||
return {compute_time_stat(request_times), compute_time_stat(connect_times),
|
||||
compute_time_stat(ttfb_times)};
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -959,7 +1018,7 @@ Options:
|
||||
Number of native threads.
|
||||
Default: )" << config.nthreads << R"(
|
||||
-i, --input-file=<FILE>
|
||||
Path of a file with multiple URIs are seperated by EOLs.
|
||||
Path of a file with multiple URIs are separated by EOLs.
|
||||
This option will disable URIs getting from command-line.
|
||||
If '-' is given as <FILE>, URIs will be read from stdin.
|
||||
URIs are used in this order for each client. All URIs
|
||||
@@ -974,11 +1033,13 @@ Options:
|
||||
-w, --window-bits=<N>
|
||||
Sets the stream level initial window size to (2**<N>)-1.
|
||||
For SPDY, 2**<N> is used instead.
|
||||
Default: )" << config.window_bits << R"(
|
||||
-W, --connection-window-bits=<N>
|
||||
Sets the connection level initial window size to
|
||||
(2**<N>)-1. For SPDY, if <N> is strictly less than 16,
|
||||
this option is ignored. Otherwise 2**<N> is used for
|
||||
SPDY.
|
||||
Default: )" << config.connection_window_bits << R"(
|
||||
-H, --header=<HEADER>
|
||||
Add/Override a header to the requests.
|
||||
-p, --no-tls-proto=<PROTOID>
|
||||
@@ -1406,7 +1467,7 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
}
|
||||
|
||||
auto time_stats = process_time_stats(workers);
|
||||
auto ts = process_time_stats(workers);
|
||||
|
||||
// Requests which have not been issued due to connection errors, are
|
||||
// counted towards req_failed and req_error.
|
||||
@@ -1439,14 +1500,23 @@ status codes: )" << stats.status[2] << " 2xx, " << stats.status[3] << " 3xx, "
|
||||
traffic: )" << stats.bytes_total << " bytes total, " << stats.bytes_head
|
||||
<< " bytes headers, " << stats.bytes_body << R"( bytes data
|
||||
min max mean sd +/- sd
|
||||
time for request: )" << std::setw(10)
|
||||
<< util::format_duration(time_stats.time_min) << " "
|
||||
<< std::setw(10) << util::format_duration(time_stats.time_max)
|
||||
<< " " << std::setw(10)
|
||||
<< util::format_duration(time_stats.time_mean) << " "
|
||||
<< std::setw(10) << util::format_duration(time_stats.time_sd)
|
||||
<< std::setw(9) << util::dtos(time_stats.within_sd) << "%"
|
||||
<< std::endl;
|
||||
time for request: )" << std::setw(10) << util::format_duration(ts.request.min)
|
||||
<< " " << std::setw(10) << util::format_duration(ts.request.max)
|
||||
<< " " << std::setw(10) << util::format_duration(ts.request.mean)
|
||||
<< " " << std::setw(10) << util::format_duration(ts.request.sd)
|
||||
<< std::setw(9) << util::dtos(ts.request.within_sd) << "%"
|
||||
<< "\ntime for connect: " << std::setw(10)
|
||||
<< util::format_duration(ts.connect.min) << " " << std::setw(10)
|
||||
<< util::format_duration(ts.connect.max) << " " << std::setw(10)
|
||||
<< util::format_duration(ts.connect.mean) << " " << std::setw(10)
|
||||
<< util::format_duration(ts.connect.sd) << std::setw(9)
|
||||
<< util::dtos(ts.connect.within_sd) << "%"
|
||||
<< "\ntime to 1st byte: " << std::setw(10)
|
||||
<< util::format_duration(ts.ttfb.min) << " " << std::setw(10)
|
||||
<< util::format_duration(ts.ttfb.max) << " " << std::setw(10)
|
||||
<< util::format_duration(ts.ttfb.mean) << " " << std::setw(10)
|
||||
<< util::format_duration(ts.ttfb.sd) << std::setw(9)
|
||||
<< util::dtos(ts.ttfb.within_sd) << "%" << std::endl;
|
||||
|
||||
SSL_CTX_free(ssl_ctx);
|
||||
|
||||
|
||||
33
src/h2load.h
33
src/h2load.h
@@ -28,8 +28,12 @@
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#ifdef HAVE_SYS_SOCKET_H
|
||||
#include <sys/socket.h>
|
||||
#endif // HAVE_SYS_SOCKET_H
|
||||
#ifdef HAVE_NETDB_H
|
||||
#include <netdb.h>
|
||||
#endif // HAVE_NETDB_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
@@ -94,13 +98,24 @@ struct RequestStat {
|
||||
bool completed;
|
||||
};
|
||||
|
||||
struct TimeStats {
|
||||
// time for request: max, min, mean and sd (standard deviation)
|
||||
std::chrono::microseconds time_max, time_min, time_mean, time_sd;
|
||||
// percentage of number of requests inside mean -/+ sd
|
||||
template <typename Duration> struct TimeStat {
|
||||
// min, max, mean and sd (standard deviation)
|
||||
Duration min, max, mean, sd;
|
||||
// percentage of samples inside mean -/+ sd
|
||||
double within_sd;
|
||||
};
|
||||
|
||||
struct TimeStats {
|
||||
// time for request
|
||||
TimeStat<std::chrono::microseconds> request;
|
||||
// time for connect
|
||||
TimeStat<std::chrono::microseconds> connect;
|
||||
// time to first byte (TTFB)
|
||||
TimeStat<std::chrono::microseconds> ttfb;
|
||||
};
|
||||
|
||||
enum TimeStatType { STAT_REQUEST, STAT_CONNECT, STAT_FIRST_BYTE };
|
||||
|
||||
struct Stats {
|
||||
Stats(size_t req_todo);
|
||||
// The total number of requests
|
||||
@@ -132,6 +147,12 @@ struct Stats {
|
||||
std::array<size_t, 6> status;
|
||||
// The statistics per request
|
||||
std::vector<RequestStat> req_stats;
|
||||
// time connect starts
|
||||
std::vector<std::chrono::steady_clock::time_point> start_times;
|
||||
// time to connect
|
||||
std::vector<std::chrono::steady_clock::time_point> connect_times;
|
||||
// time to first byte (TTFB)
|
||||
std::vector<std::chrono::steady_clock::time_point> ttfbs;
|
||||
};
|
||||
|
||||
enum ClientState { CLIENT_IDLE, CLIENT_CONNECTED };
|
||||
@@ -171,6 +192,7 @@ struct Client {
|
||||
addrinfo *next_addr;
|
||||
size_t reqidx;
|
||||
ClientState state;
|
||||
bool first_byte_received;
|
||||
// The number of requests this client has to issue.
|
||||
size_t req_todo;
|
||||
// The number of requests this client has issued so far.
|
||||
@@ -215,6 +237,9 @@ struct Client {
|
||||
void on_stream_close(int32_t stream_id, bool success, RequestStat *req_stat);
|
||||
|
||||
void record_request_time(RequestStat *req_stat);
|
||||
void record_start_time(Stats *stat);
|
||||
void record_connect_time(Stats *stat);
|
||||
void record_ttfb(Stats *stat);
|
||||
|
||||
void signal_write();
|
||||
};
|
||||
|
||||
@@ -28,7 +28,8 @@
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <cinttypes>
|
||||
|
||||
#include "h2load.h"
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "h2load_spdy_session.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cerrno>
|
||||
|
||||
#include "h2load.h"
|
||||
#include "util.h"
|
||||
|
||||
63
src/http2.cc
63
src/http2.cc
@@ -718,6 +718,30 @@ InputIt skip_to_right_dquote(InputIt first, InputIt last) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
// Returns true if link-param does not match pattern |pat| of length
|
||||
// |patlen| or it has empty value (""). |pat| should be parmname
|
||||
// followed by "=".
|
||||
bool check_link_param_empty(const char *first, const char *last,
|
||||
const char *pat, size_t patlen) {
|
||||
if (first + patlen <= last) {
|
||||
if (std::equal(pat, pat + patlen, first, util::CaseCmp())) {
|
||||
// we only accept URI if pat is followd by "" (e.g.,
|
||||
// loadpolicy="") here.
|
||||
if (first + patlen + 2 <= last) {
|
||||
if (*(first + patlen) != '"' || *(first + patlen + 1) != '"') {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// here we got invalid production (anchor=") or anchor=?
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
std::pair<LinkHeader, const char *>
|
||||
parse_next_link_header_once(const char *first, const char *last) {
|
||||
@@ -755,11 +779,11 @@ parse_next_link_header_once(const char *first, const char *last) {
|
||||
// we expect link-param
|
||||
|
||||
// rel can take several relations using quoted form.
|
||||
static const char PLP[] = "rel=\"";
|
||||
static const size_t PLPLEN = sizeof(PLP) - 1;
|
||||
static constexpr char PLP[] = "rel=\"";
|
||||
static constexpr size_t PLPLEN = sizeof(PLP) - 1;
|
||||
|
||||
static const char PLT[] = "preload";
|
||||
static const size_t PLTLEN = sizeof(PLT) - 1;
|
||||
static constexpr char PLT[] = "preload";
|
||||
static constexpr size_t PLTLEN = sizeof(PLT) - 1;
|
||||
if (first + PLPLEN < last && *(first + PLPLEN - 1) == '"' &&
|
||||
std::equal(PLP, PLP + PLPLEN, first, util::CaseCmp())) {
|
||||
// we have to search preload in whitespace separated list:
|
||||
@@ -804,8 +828,8 @@ parse_next_link_header_once(const char *first, const char *last) {
|
||||
}
|
||||
// we are only interested in rel=preload parameter. Others are
|
||||
// simply skipped.
|
||||
static const char PL[] = "rel=preload";
|
||||
static const size_t PLLEN = sizeof(PL) - 1;
|
||||
static constexpr char PL[] = "rel=preload";
|
||||
static constexpr size_t PLLEN = sizeof(PL) - 1;
|
||||
if (first + PLLEN == last) {
|
||||
if (std::equal(PL, PL + PLLEN, first, util::CaseCmp())) {
|
||||
ok = true;
|
||||
@@ -834,20 +858,19 @@ parse_next_link_header_once(const char *first, const char *last) {
|
||||
}
|
||||
}
|
||||
// we have to reject URI if we have nonempty anchor parameter.
|
||||
static const char ANCHOR[] = "anchor=";
|
||||
static const size_t ANCHORLEN = sizeof(ANCHOR) - 1;
|
||||
if (!ign && first + ANCHORLEN <= last) {
|
||||
if (std::equal(ANCHOR, ANCHOR + ANCHORLEN, first, util::CaseCmp())) {
|
||||
// we only accept URI if anchor="" here.
|
||||
if (first + ANCHORLEN + 2 <= last) {
|
||||
if (*(first + ANCHORLEN) != '"' || *(first + ANCHORLEN + 1) != '"') {
|
||||
ign = true;
|
||||
}
|
||||
} else {
|
||||
// here we got invalid production (anchor=") or anchor=?
|
||||
ign = true;
|
||||
}
|
||||
}
|
||||
static constexpr char ANCHOR[] = "anchor=";
|
||||
static constexpr size_t ANCHORLEN = sizeof(ANCHOR) - 1;
|
||||
if (!ign && !check_link_param_empty(first, last, ANCHOR, ANCHORLEN)) {
|
||||
ign = true;
|
||||
}
|
||||
|
||||
// reject URI if we have non-empty loadpolicy. This could be
|
||||
// tightened up to just pick up "next" or "insert".
|
||||
static constexpr char LOADPOLICY[] = "loadpolicy=";
|
||||
static constexpr size_t LOADPOLICYLEN = sizeof(LOADPOLICY) - 1;
|
||||
if (!ign &&
|
||||
!check_link_param_empty(first, last, LOADPOLICY, LOADPOLICYLEN)) {
|
||||
ign = true;
|
||||
}
|
||||
|
||||
auto param_first = first;
|
||||
|
||||
@@ -627,6 +627,19 @@ void test_http2_parse_link_header(void) {
|
||||
CU_ASSERT(1 == res.size());
|
||||
CU_ASSERT(std::make_pair(&s[36], &s[39]) == res[0].uri);
|
||||
}
|
||||
{
|
||||
// With loadpolicy="next", url should be ignored
|
||||
const char s[] = R"(<url>; rel=preload; loadpolicy="next")";
|
||||
auto res = http2::parse_link_header(s, sizeof(s) - 1);
|
||||
CU_ASSERT(0 == res.size());
|
||||
}
|
||||
{
|
||||
// url should be picked up if empty loadpolicy is specified
|
||||
const char s[] = R"(<url>; rel=preload; loadpolicy="")";
|
||||
auto res = http2::parse_link_header(s, sizeof(s) - 1);
|
||||
CU_ASSERT(1 == res.size());
|
||||
CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri);
|
||||
}
|
||||
{
|
||||
// case-insensitive match
|
||||
const char s[] = R"(<url>; rel=preload; ANCHOR="#foo", <url>; )"
|
||||
|
||||
@@ -25,6 +25,10 @@
|
||||
#ifndef SHRPX_HTTP2_TEST_H
|
||||
#define SHRPX_HTTP2_TEST_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif // HAVE_CONFIG_H
|
||||
|
||||
namespace shrpx {
|
||||
|
||||
void test_http2_add_header(void);
|
||||
|
||||
@@ -20,5 +20,8 @@
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
if ENABLE_ASIO_LIB
|
||||
nobase_include_HEADERS = nghttp2/asio_http2.h nghttp2/asio_http2_client.h \
|
||||
nghttp2/asio_http2_server.h
|
||||
endif # ENABLE_ASIO_LIB
|
||||
|
||||
@@ -136,17 +136,25 @@ public:
|
||||
http2 &operator=(http2 &&other) noexcept;
|
||||
|
||||
// Starts listening connection on given address and port and serves
|
||||
// incoming requests in cleartext TCP connection.
|
||||
// incoming requests in cleartext TCP connection. If |asynchronous|
|
||||
// is false, this function blocks forever unless there is an error.
|
||||
// If it is true, after server has started, this function returns
|
||||
// immediately, and the caller should call join() to shutdown server
|
||||
// gracefully.
|
||||
boost::system::error_code listen_and_serve(boost::system::error_code &ec,
|
||||
const std::string &address,
|
||||
const std::string &port);
|
||||
const std::string &port,
|
||||
bool asynchronous = false);
|
||||
|
||||
// Starts listening connection on given address and port and serves
|
||||
// incoming requests in SSL/TLS encrypted connection.
|
||||
// incoming requests in SSL/TLS encrypted connection. For
|
||||
// |asynchronous| parameter, see cleartext version
|
||||
// |listen_and_serve|.
|
||||
boost::system::error_code
|
||||
listen_and_serve(boost::system::error_code &ec,
|
||||
boost::asio::ssl::context &tls_context,
|
||||
const std::string &address, const std::string &port);
|
||||
const std::string &address, const std::string &port,
|
||||
bool asynchronous = false);
|
||||
|
||||
// Registers request handler |cb| with path pattern |pattern|. This
|
||||
// function will fail and returns false if same pattern has been
|
||||
@@ -187,6 +195,12 @@ public:
|
||||
// connections.
|
||||
void backlog(int backlog);
|
||||
|
||||
// Gracefully stop http2 server
|
||||
void stop();
|
||||
|
||||
// Join on http2 server and wait for it to fully stop
|
||||
void join();
|
||||
|
||||
private:
|
||||
std::unique_ptr<http2_impl> impl_;
|
||||
};
|
||||
|
||||
@@ -26,7 +26,9 @@
|
||||
#include <config.h>
|
||||
#endif // HAVE_CONFIG_H
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif // HAVE_UNISTD_H
|
||||
#include <getopt.h>
|
||||
|
||||
#include <cstdio>
|
||||
@@ -99,6 +101,11 @@ static int inflate_hd(json_t *obj, nghttp2_hd_inflater *inflater, int seq) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!json_is_string(wire)) {
|
||||
fprintf(stderr, "'wire' value is not string at %d\n", seq);
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto table_size = json_object_get(obj, "header_table_size");
|
||||
|
||||
if (table_size) {
|
||||
|
||||
@@ -25,6 +25,10 @@
|
||||
#ifndef MEMCHUNK_TEST_H
|
||||
#define MEMCHUNK_TEST_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif // HAVE_CONFIG_H
|
||||
|
||||
namespace nghttp2 {
|
||||
|
||||
void test_pool_recycle(void);
|
||||
|
||||
262
src/nghttp.cc
262
src/nghttp.cc
@@ -25,9 +25,15 @@
|
||||
#include "nghttp.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif // HAVE_UNISTD_H
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif // HAVE_FCNTL_H
|
||||
#ifdef HAVE_NETINET_IN_H
|
||||
#include <netinet/in.h>
|
||||
#endif // HAVE_NETINET_IN_H
|
||||
#include <netinet/tcp.h>
|
||||
#include <getopt.h>
|
||||
|
||||
@@ -61,17 +67,33 @@
|
||||
|
||||
namespace nghttp2 {
|
||||
|
||||
// stream ID of anchor stream node when --dep-idle is enabled. These
|
||||
// * portion of ANCHOR_ID_* matches RequestPriority in HtmlParser.h.
|
||||
// The stream ID = 1 is excluded since it is used as first stream in
|
||||
// upgrade case.
|
||||
enum {
|
||||
ANCHOR_ID_HIGH = 3,
|
||||
ANCHOR_ID_MEDIUM = 5,
|
||||
ANCHOR_ID_LOW = 7,
|
||||
ANCHOR_ID_LOWEST = 9,
|
||||
// The anchor stream nodes when --no-dep is not used. The stream ID =
|
||||
// 1 is excluded since it is used as first stream in upgrade case. We
|
||||
// follows the same dependency anchor nodes as Firefox does.
|
||||
struct Anchor {
|
||||
int32_t stream_id;
|
||||
// stream ID this anchor depends on
|
||||
int32_t dep_stream_id;
|
||||
// .. with this weight.
|
||||
int32_t weight;
|
||||
};
|
||||
|
||||
// This is index into anchors. Firefox uses ANCHOR_FOLLOWERS for html
|
||||
// file.
|
||||
enum {
|
||||
ANCHOR_LEADERS,
|
||||
ANCHOR_UNBLOCKED,
|
||||
ANCHOR_BACKGROUND,
|
||||
ANCHOR_SPECULATIVE,
|
||||
ANCHOR_FOLLOWERS,
|
||||
};
|
||||
|
||||
namespace {
|
||||
auto anchors = std::array<Anchor, 5>{{
|
||||
{3, 0, 201}, {5, 0, 101}, {7, 0, 1}, {9, 7, 1}, {11, 3, 1},
|
||||
}};
|
||||
} // namespace
|
||||
|
||||
Config::Config()
|
||||
: output_upper_thres(1024 * 1024), padding(0),
|
||||
peer_max_concurrent_streams(NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS),
|
||||
@@ -79,7 +101,7 @@ Config::Config()
|
||||
timeout(0.), window_bits(-1), connection_window_bits(-1), verbose(0),
|
||||
null_out(false), remote_name(false), get_assets(false), stat(false),
|
||||
upgrade(false), continuation(false), no_content_length(false),
|
||||
no_dep(false), dep_idle(false), hexdump(false) {
|
||||
no_dep(false), hexdump(false), no_push(false) {
|
||||
nghttp2_option_new(&http2_option);
|
||||
nghttp2_option_set_peer_max_concurrent_streams(http2_option,
|
||||
peer_max_concurrent_streams);
|
||||
@@ -111,12 +133,10 @@ std::string strip_fragment(const char *raw_uri) {
|
||||
|
||||
Request::Request(const std::string &uri, const http_parser_url &u,
|
||||
const nghttp2_data_provider *data_prd, int64_t data_length,
|
||||
const nghttp2_priority_spec &pri_spec,
|
||||
std::shared_ptr<Dependency> dep, int pri, int level)
|
||||
: uri(uri), u(u), dep(std::move(dep)), pri_spec(pri_spec),
|
||||
data_length(data_length), data_offset(0), response_len(0),
|
||||
inflater(nullptr), html_parser(nullptr), data_prd(data_prd),
|
||||
stream_id(-1), status(0), level(level), pri(pri),
|
||||
const nghttp2_priority_spec &pri_spec, int level)
|
||||
: uri(uri), u(u), pri_spec(pri_spec), data_length(data_length),
|
||||
data_offset(0), response_len(0), inflater(nullptr), html_parser(nullptr),
|
||||
data_prd(data_prd), stream_id(-1), status(0), level(level),
|
||||
expect_final_response(false) {
|
||||
http2::init_hdidx(res_hdidx);
|
||||
http2::init_hdidx(req_hdidx);
|
||||
@@ -155,77 +175,41 @@ std::string Request::make_reqpath() const {
|
||||
return path;
|
||||
}
|
||||
|
||||
int32_t Request::find_dep_stream_id(int start) {
|
||||
for (auto i = start; i >= 0; --i) {
|
||||
for (auto req : dep->deps[i]) {
|
||||
return req->stream_id;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
nghttp2_priority_spec Request::resolve_dep(int32_t pri) {
|
||||
namespace {
|
||||
nghttp2_priority_spec resolve_dep(int res_type) {
|
||||
nghttp2_priority_spec pri_spec;
|
||||
int exclusive = 0;
|
||||
int32_t stream_id = -1;
|
||||
|
||||
nghttp2_priority_spec_default_init(&pri_spec);
|
||||
|
||||
if (config.no_dep) {
|
||||
nghttp2_priority_spec_default_init(&pri_spec);
|
||||
|
||||
return pri_spec;
|
||||
}
|
||||
|
||||
if (config.dep_idle) {
|
||||
int32_t anchor_id = 0;
|
||||
switch (pri) {
|
||||
case REQ_PRI_HIGH:
|
||||
anchor_id = ANCHOR_ID_HIGH;
|
||||
break;
|
||||
case REQ_PRI_MEDIUM:
|
||||
anchor_id = ANCHOR_ID_MEDIUM;
|
||||
break;
|
||||
case REQ_PRI_LOW:
|
||||
anchor_id = ANCHOR_ID_LOW;
|
||||
break;
|
||||
case REQ_PRI_LOWEST:
|
||||
anchor_id = ANCHOR_ID_LOWEST;
|
||||
break;
|
||||
}
|
||||
nghttp2_priority_spec_init(&pri_spec, anchor_id, NGHTTP2_DEFAULT_WEIGHT, 0);
|
||||
return pri_spec;
|
||||
int32_t anchor_id;
|
||||
int32_t weight;
|
||||
switch (res_type) {
|
||||
case REQ_CSS:
|
||||
case REQ_JS:
|
||||
anchor_id = anchors[ANCHOR_LEADERS].stream_id;
|
||||
weight = 2;
|
||||
break;
|
||||
case REQ_UNBLOCK_JS:
|
||||
anchor_id = anchors[ANCHOR_UNBLOCKED].stream_id;
|
||||
weight = 2;
|
||||
break;
|
||||
case REQ_IMG:
|
||||
anchor_id = anchors[ANCHOR_FOLLOWERS].stream_id;
|
||||
weight = 12;
|
||||
break;
|
||||
default:
|
||||
anchor_id = anchors[ANCHOR_FOLLOWERS].stream_id;
|
||||
weight = 2;
|
||||
}
|
||||
|
||||
if (pri == 0) {
|
||||
return pri_spec;
|
||||
}
|
||||
|
||||
auto start = std::min(pri, (int)dep->deps.size() - 1);
|
||||
|
||||
for (auto i = start; i >= 0; --i) {
|
||||
if (dep->deps[i][0]->pri < pri) {
|
||||
stream_id = find_dep_stream_id(i);
|
||||
|
||||
if (i != (int)dep->deps.size() - 1) {
|
||||
exclusive = 1;
|
||||
}
|
||||
|
||||
break;
|
||||
} else if (dep->deps[i][0]->pri == pri) {
|
||||
stream_id = find_dep_stream_id(i - 1);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (stream_id == -1) {
|
||||
return pri_spec;
|
||||
}
|
||||
|
||||
nghttp2_priority_spec_init(&pri_spec, stream_id, NGHTTP2_DEFAULT_WEIGHT,
|
||||
exclusive);
|
||||
|
||||
nghttp2_priority_spec_init(&pri_spec, anchor_id, weight, 0);
|
||||
return pri_spec;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool Request::is_ipv6_literal_addr() const {
|
||||
if (util::has_uri_field(u, UF_HOST)) {
|
||||
@@ -759,6 +743,13 @@ size_t populate_settings(nghttp2_settings_entry *iv) {
|
||||
iv[niv].value = config.header_table_size;
|
||||
++niv;
|
||||
}
|
||||
|
||||
if (config.no_push) {
|
||||
iv[niv].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
|
||||
iv[niv].value = 0;
|
||||
++niv;
|
||||
}
|
||||
|
||||
return niv;
|
||||
}
|
||||
} // namespace
|
||||
@@ -767,7 +758,7 @@ int HttpClient::on_upgrade_connect() {
|
||||
ssize_t rv;
|
||||
record_connect_end_time();
|
||||
assert(!reqvec.empty());
|
||||
std::array<nghttp2_settings_entry, 32> iv;
|
||||
std::array<nghttp2_settings_entry, 16> iv;
|
||||
size_t niv = populate_settings(iv.data());
|
||||
assert(settings_payload.size() >= 8 * niv);
|
||||
rv = nghttp2_pack_settings_payload(settings_payload.data(),
|
||||
@@ -977,26 +968,22 @@ int HttpClient::connection_made() {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (!config.no_dep && config.dep_idle) {
|
||||
if (!config.no_dep) {
|
||||
// Create anchor stream nodes
|
||||
nghttp2_priority_spec pri_spec;
|
||||
int32_t dep_stream_id = 0;
|
||||
|
||||
for (auto stream_id :
|
||||
{ANCHOR_ID_HIGH, ANCHOR_ID_MEDIUM, ANCHOR_ID_LOW, ANCHOR_ID_LOWEST}) {
|
||||
|
||||
nghttp2_priority_spec_init(&pri_spec, dep_stream_id,
|
||||
NGHTTP2_DEFAULT_WEIGHT, 0);
|
||||
rv = nghttp2_submit_priority(session, NGHTTP2_FLAG_NONE, stream_id,
|
||||
for (auto &anchor : anchors) {
|
||||
nghttp2_priority_spec_init(&pri_spec, anchor.dep_stream_id, anchor.weight,
|
||||
0);
|
||||
rv = nghttp2_submit_priority(session, NGHTTP2_FLAG_NONE, anchor.stream_id,
|
||||
&pri_spec);
|
||||
if (rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
dep_stream_id = stream_id;
|
||||
}
|
||||
|
||||
rv = nghttp2_session_set_next_stream_id(session, ANCHOR_ID_LOWEST + 2);
|
||||
rv = nghttp2_session_set_next_stream_id(
|
||||
session, anchors[ANCHOR_FOLLOWERS].stream_id + 2);
|
||||
if (rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
@@ -1004,7 +991,8 @@ int HttpClient::connection_made() {
|
||||
if (need_upgrade()) {
|
||||
// Amend the priority because we cannot send priority in
|
||||
// HTTP/1.1 Upgrade.
|
||||
nghttp2_priority_spec_init(&pri_spec, ANCHOR_ID_HIGH, config.weight, 0);
|
||||
auto &anchor = anchors[ANCHOR_FOLLOWERS];
|
||||
nghttp2_priority_spec_init(&pri_spec, anchor.stream_id, config.weight, 0);
|
||||
|
||||
rv = nghttp2_submit_priority(session, NGHTTP2_FLAG_NONE, 1, &pri_spec);
|
||||
if (rv != 0) {
|
||||
@@ -1237,9 +1225,7 @@ void HttpClient::update_hostport() {
|
||||
bool HttpClient::add_request(const std::string &uri,
|
||||
const nghttp2_data_provider *data_prd,
|
||||
int64_t data_length,
|
||||
const nghttp2_priority_spec &pri_spec,
|
||||
std::shared_ptr<Dependency> dep, int pri,
|
||||
int level) {
|
||||
const nghttp2_priority_spec &pri_spec, int level) {
|
||||
http_parser_url u;
|
||||
memset(&u, 0, sizeof(u));
|
||||
if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) {
|
||||
@@ -1253,8 +1239,8 @@ bool HttpClient::add_request(const std::string &uri,
|
||||
path_cache.insert(uri);
|
||||
}
|
||||
|
||||
reqvec.push_back(make_unique<Request>(uri, u, data_prd, data_length, pri_spec,
|
||||
std::move(dep), pri, level));
|
||||
reqvec.push_back(
|
||||
make_unique<Request>(uri, u, data_prd, data_length, pri_spec, level));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1272,37 +1258,9 @@ void HttpClient::record_connect_end_time() {
|
||||
}
|
||||
|
||||
void HttpClient::request_done(Request *req) {
|
||||
if (req->pri == 0 && req->dep) {
|
||||
assert(req->dep->deps.empty());
|
||||
|
||||
req->dep->deps.push_back(std::vector<Request *>{req});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (req->stream_id % 2 == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto itr = std::begin(req->dep->deps);
|
||||
for (; itr != std::end(req->dep->deps); ++itr) {
|
||||
if ((*itr)[0]->pri == req->pri) {
|
||||
(*itr).push_back(req);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if ((*itr)[0]->pri > req->pri) {
|
||||
auto v = std::vector<Request *>{req};
|
||||
req->dep->deps.insert(itr, std::move(v));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (itr == std::end(req->dep->deps)) {
|
||||
req->dep->deps.push_back(std::vector<Request *>{req});
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_JANSSON
|
||||
@@ -1485,7 +1443,7 @@ void update_html_parser(HttpClient *client, Request *req, const uint8_t *data,
|
||||
|
||||
for (auto &p : req->html_parser->get_links()) {
|
||||
auto uri = strip_fragment(p.first.c_str());
|
||||
auto pri = p.second;
|
||||
auto res_type = p.second;
|
||||
|
||||
http_parser_url u;
|
||||
memset(&u, 0, sizeof(u));
|
||||
@@ -1494,10 +1452,9 @@ void update_html_parser(HttpClient *client, Request *req, const uint8_t *data,
|
||||
util::fieldeq(uri.c_str(), u, req->uri.c_str(), req->u, UF_HOST) &&
|
||||
util::porteq(uri.c_str(), u, req->uri.c_str(), req->u)) {
|
||||
// No POST data for assets
|
||||
auto pri_spec = req->resolve_dep(pri);
|
||||
auto pri_spec = resolve_dep(res_type);
|
||||
|
||||
if (client->add_request(uri, nullptr, 0, pri_spec, req->dep, pri,
|
||||
req->level + 1)) {
|
||||
if (client->add_request(uri, nullptr, 0, pri_spec, req->level + 1)) {
|
||||
|
||||
submit_request(client, config.headers, client->reqvec.back().get());
|
||||
}
|
||||
@@ -1660,7 +1617,7 @@ int on_begin_headers_callback(nghttp2_session *session,
|
||||
|
||||
nghttp2_priority_spec_default_init(&pri_spec);
|
||||
|
||||
auto req = make_unique<Request>("", u, nullptr, 0, pri_spec, nullptr);
|
||||
auto req = make_unique<Request>("", u, nullptr, 0, pri_spec);
|
||||
req->stream_id = stream_id;
|
||||
|
||||
nghttp2_session_set_stream_user_data(session, stream_id, req.get());
|
||||
@@ -1932,7 +1889,7 @@ see http://www.w3.org/TR/resource-timing/#processing-model
|
||||
|
||||
sorted by 'complete'
|
||||
|
||||
responseEnd requestStart process code size request path)" << std::endl;
|
||||
id responseEnd requestStart process code size request path)" << std::endl;
|
||||
|
||||
const auto &base = client.timing.connect_end_time;
|
||||
for (const auto &req : reqs) {
|
||||
@@ -1944,8 +1901,9 @@ responseEnd requestStart process code size request path)" << std::endl;
|
||||
req->timing.response_end_time - req->timing.request_start_time);
|
||||
auto pushed = req->stream_id % 2 == 0;
|
||||
|
||||
std::cout << std::setw(11) << ("+" + util::format_duration(response_end))
|
||||
<< " " << (pushed ? "*" : " ") << std::setw(11)
|
||||
std::cout << std::setw(3) << req->stream_id << " " << std::setw(11)
|
||||
<< ("+" + util::format_duration(response_end)) << " "
|
||||
<< (pushed ? "*" : " ") << std::setw(11)
|
||||
<< ("+" + util::format_duration(request_start)) << " "
|
||||
<< std::setw(8) << util::format_duration(total) << " "
|
||||
<< std::setw(4) << req->status << " " << std::setw(4)
|
||||
@@ -2058,17 +2016,16 @@ int communicate(
|
||||
nghttp2_priority_spec pri_spec;
|
||||
int32_t dep_stream_id = 0;
|
||||
|
||||
if (!config.no_dep && config.dep_idle) {
|
||||
dep_stream_id = ANCHOR_ID_HIGH;
|
||||
if (!config.no_dep) {
|
||||
dep_stream_id = anchors[ANCHOR_FOLLOWERS].stream_id;
|
||||
}
|
||||
|
||||
nghttp2_priority_spec_init(&pri_spec, dep_stream_id, config.weight, 0);
|
||||
|
||||
for (auto req : requests) {
|
||||
for (int i = 0; i < config.multiply; ++i) {
|
||||
auto dep = std::make_shared<Dependency>();
|
||||
client.add_request(std::get<0>(req), std::get<1>(req), std::get<2>(req),
|
||||
pri_spec, std::move(dep));
|
||||
pri_spec);
|
||||
}
|
||||
}
|
||||
client.update_hostport();
|
||||
@@ -2364,8 +2321,9 @@ Options:
|
||||
filename is dereived from URI. If URI ends with '/',
|
||||
'index.html' is used as a filename. Not implemented
|
||||
yet.
|
||||
-t, --timeout=<SEC>
|
||||
Timeout each request after <SEC> seconds.
|
||||
-t, --timeout=<DURATION>
|
||||
Timeout each request after <DURATION>. Set 0 to disable
|
||||
timeout.
|
||||
-w, --window-bits=<N>
|
||||
Sets the stream level initial window size to 2**<N>-1.
|
||||
-W, --connection-window-bits=<N>
|
||||
@@ -2425,17 +2383,22 @@ Options:
|
||||
--no-content-length
|
||||
Don't send content-length header field.
|
||||
--no-dep Don't send dependency based priority hint to server.
|
||||
--dep-idle Use idle streams as anchor nodes to express priority.
|
||||
--hexdump Display the incoming traffic in hexadecimal (Canonical
|
||||
hex+ASCII display). If SSL/TLS is used, decrypted data
|
||||
are used.
|
||||
--no-push Disable server push.
|
||||
--version Display version information and exit.
|
||||
-h, --help Display this help and exit.
|
||||
|
||||
--
|
||||
|
||||
The <SIZE> argument is an integer and an optional unit (e.g., 10K is
|
||||
10 * 1024). Units are K, M and G (powers of 1024).)" << std::endl;
|
||||
10 * 1024). Units are K, M and G (powers of 1024).
|
||||
|
||||
The <DURATION> argument is an integer and an optional unit (e.g., 1s
|
||||
is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms
|
||||
(hours, minutes, seconds and milliseconds, respectively). If a unit
|
||||
is omitted, a second is used as unit.)" << std::endl;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -2469,9 +2432,9 @@ int main(int argc, char **argv) {
|
||||
{"version", no_argument, &flag, 5},
|
||||
{"no-content-length", no_argument, &flag, 6},
|
||||
{"no-dep", no_argument, &flag, 7},
|
||||
{"dep-idle", no_argument, &flag, 8},
|
||||
{"trailer", required_argument, &flag, 9},
|
||||
{"hexdump", no_argument, &flag, 10},
|
||||
{"no-push", no_argument, &flag, 11},
|
||||
{nullptr, 0, nullptr, 0}};
|
||||
int option_index = 0;
|
||||
int c = getopt_long(argc, argv, "M:Oab:c:d:gm:np:r:hH:vst:uw:W:",
|
||||
@@ -2521,7 +2484,11 @@ int main(int argc, char **argv) {
|
||||
++config.verbose;
|
||||
break;
|
||||
case 't':
|
||||
config.timeout = atoi(optarg);
|
||||
config.timeout = util::parse_duration_with_unit(optarg);
|
||||
if (config.timeout == std::numeric_limits<double>::infinity()) {
|
||||
std::cerr << "-t: bad timeout value: " << optarg << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
case 'u':
|
||||
config.upgrade = true;
|
||||
@@ -2565,10 +2532,7 @@ int main(int argc, char **argv) {
|
||||
<< std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
// To test "never index" repr, don't index authorization header
|
||||
// field unconditionally.
|
||||
auto no_index = util::strieq_l("authorization", header);
|
||||
config.headers.emplace_back(header, value, no_index);
|
||||
config.headers.emplace_back(header, value, false);
|
||||
util::inp_strlower(config.headers.back().name);
|
||||
break;
|
||||
}
|
||||
@@ -2630,10 +2594,6 @@ int main(int argc, char **argv) {
|
||||
// no-dep option
|
||||
config.no_dep = true;
|
||||
break;
|
||||
case 8:
|
||||
// dep-idle option
|
||||
config.dep_idle = true;
|
||||
break;
|
||||
case 9: {
|
||||
// trailer option
|
||||
auto header = optarg;
|
||||
@@ -2662,6 +2622,10 @@ int main(int argc, char **argv) {
|
||||
// hexdump option
|
||||
config.hexdump = true;
|
||||
break;
|
||||
case 11:
|
||||
// no-push option
|
||||
config.no_push = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user