Merge branch 'master' into v1.0.0
Conflicts: lib/nghttp2_option.h lib/nghttp2_session.h src/HttpServer.cc
This commit is contained in:
commit
e63d6e490a
|
@ -179,7 +179,7 @@ https://nghttp2.org/documentation/
|
|||
Unit tests
|
||||
----------
|
||||
|
||||
Unit tests are done by simply running `make check`.
|
||||
Unit tests are done by simply running ``make check``.
|
||||
|
||||
Integration tests
|
||||
-----------------
|
||||
|
@ -1364,7 +1364,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
|
||||
|
|
|
@ -25,13 +25,13 @@ 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.14-DEV], [t-tujikawa@users.sourceforge.net])
|
||||
AC_INIT([nghttp2], [0.7.15-DEV], [t-tujikawa@users.sourceforge.net])
|
||||
LT_PREREQ([2.2.6])
|
||||
LT_INIT()
|
||||
dnl See versioning rule:
|
||||
dnl http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
|
||||
AC_SUBST(LT_CURRENT, 13)
|
||||
AC_SUBST(LT_REVISION, 2)
|
||||
AC_SUBST(LT_REVISION, 3)
|
||||
AC_SUBST(LT_AGE, 8)
|
||||
|
||||
major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/[^0-9]//g"`
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
# vim: ft=upstart:
|
||||
|
||||
description "HTTP/2 reverse proxy"
|
||||
|
||||
start on runlevel [2]
|
||||
stop on runlevel [016]
|
||||
|
||||
exec @bindir@/nghttpx
|
|
@ -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
|
|
@ -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__
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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
|
@ -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 @@ _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 --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
|
||||
|
|
47
doc/h2load.1
47
doc/h2load.1
|
@ -1,6 +1,6 @@
|
|||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "H2LOAD" "1" "April 27, 2015" "0.7.13" "nghttp2"
|
||||
.TH "H2LOAD" "1" "May 08, 2015" "0.7.14" "nghttp2"
|
||||
.SH NAME
|
||||
h2load \- HTTP/2 benchmarking tool
|
||||
.
|
||||
|
@ -205,12 +205,55 @@ The maximum time taken for request and response.
|
|||
The mean time taken for request and response.
|
||||
.TP
|
||||
.B sd
|
||||
The standard deviation of the time for request and response.
|
||||
The standard deviation of the time taken for request and response.
|
||||
.TP
|
||||
.B +/\- sd
|
||||
The fraction of the number of requests within standard deviation
|
||||
range (mean +/\- sd) against total number of successful requests.
|
||||
.UNINDENT
|
||||
.TP
|
||||
.B time for connect
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B min
|
||||
The minimum time taken to connect to a server.
|
||||
.TP
|
||||
.B max
|
||||
The maximum time taken to connect to a server.
|
||||
.TP
|
||||
.B mean
|
||||
The mean time taken to connect to a server.
|
||||
.TP
|
||||
.B sd
|
||||
The standard deviation of the time taken to connect to a server.
|
||||
.TP
|
||||
.B +/\- sd
|
||||
The fraction of the number of connections within standard
|
||||
deviation range (mean +/\- sd) against total number of successful
|
||||
connections.
|
||||
.UNINDENT
|
||||
.TP
|
||||
.B time for 1st byte (of (decrypted in case of TLS) application data)
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B min
|
||||
The minimum time taken to get 1st byte from a server.
|
||||
.TP
|
||||
.B max
|
||||
The maximum time taken to get 1st byte from a server.
|
||||
.TP
|
||||
.B mean
|
||||
The mean time taken to get 1st byte from a server.
|
||||
.TP
|
||||
.B sd
|
||||
The standard deviation of the time taken to get 1st byte from a
|
||||
server.
|
||||
.TP
|
||||
.B +/\- sd
|
||||
The fraction of the number of connections within standard
|
||||
deviation range (mean +/\- sd) against total number of successful
|
||||
connections.
|
||||
.UNINDENT
|
||||
.UNINDENT
|
||||
.SH FLOW CONTROL
|
||||
.sp
|
||||
|
|
|
@ -153,11 +153,40 @@ time for request
|
|||
mean
|
||||
The mean time taken for request and response.
|
||||
sd
|
||||
The standard deviation of the time for request and response.
|
||||
The standard deviation of the time taken for request and response.
|
||||
+/- sd
|
||||
The fraction of the number of requests within standard deviation
|
||||
range (mean +/- sd) against total number of successful requests.
|
||||
|
||||
time for connect
|
||||
min
|
||||
The minimum time taken to connect to a server.
|
||||
max
|
||||
The maximum time taken to connect to a server.
|
||||
mean
|
||||
The mean time taken to connect to a server.
|
||||
sd
|
||||
The standard deviation of the time taken to connect to a server.
|
||||
+/- sd
|
||||
The fraction of the number of connections within standard
|
||||
deviation range (mean +/- sd) against total number of successful
|
||||
connections.
|
||||
|
||||
time for 1st byte (of (decrypted in case of TLS) application data)
|
||||
min
|
||||
The minimum time taken to get 1st byte from a server.
|
||||
max
|
||||
The maximum time taken to get 1st byte from a server.
|
||||
mean
|
||||
The mean time taken to get 1st byte from a server.
|
||||
sd
|
||||
The standard deviation of the time taken to get 1st byte from a
|
||||
server.
|
||||
+/- sd
|
||||
The fraction of the number of connections within standard
|
||||
deviation range (mean +/- sd) against total number of successful
|
||||
connections.
|
||||
|
||||
FLOW CONTROL
|
||||
------------
|
||||
|
||||
|
|
|
@ -44,11 +44,40 @@ time for request
|
|||
mean
|
||||
The mean time taken for request and response.
|
||||
sd
|
||||
The standard deviation of the time for request and response.
|
||||
The standard deviation of the time taken for request and response.
|
||||
+/- sd
|
||||
The fraction of the number of requests within standard deviation
|
||||
range (mean +/- sd) against total number of successful requests.
|
||||
|
||||
time for connect
|
||||
min
|
||||
The minimum time taken to connect to a server.
|
||||
max
|
||||
The maximum time taken to connect to a server.
|
||||
mean
|
||||
The mean time taken to connect to a server.
|
||||
sd
|
||||
The standard deviation of the time taken to connect to a server.
|
||||
+/- sd
|
||||
The fraction of the number of connections within standard
|
||||
deviation range (mean +/- sd) against total number of successful
|
||||
connections.
|
||||
|
||||
time for 1st byte (of (decrypted in case of TLS) application data)
|
||||
min
|
||||
The minimum time taken to get 1st byte from a server.
|
||||
max
|
||||
The maximum time taken to get 1st byte from a server.
|
||||
mean
|
||||
The mean time taken to get 1st byte from a server.
|
||||
sd
|
||||
The standard deviation of the time taken to get 1st byte from a
|
||||
server.
|
||||
+/- sd
|
||||
The fraction of the number of connections within standard
|
||||
deviation range (mean +/- sd) against total number of successful
|
||||
connections.
|
||||
|
||||
FLOW CONTROL
|
||||
------------
|
||||
|
||||
|
|
12
doc/nghttp.1
12
doc/nghttp.1
|
@ -1,6 +1,6 @@
|
|||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTP" "1" "April 27, 2015" "0.7.13" "nghttp2"
|
||||
.TH "NGHTTP" "1" "May 08, 2015" "0.7.14" "nghttp2"
|
||||
.SH NAME
|
||||
nghttp \- HTTP/2 experimental client
|
||||
.
|
||||
|
@ -64,8 +64,9 @@ yet.
|
|||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-t, \-\-timeout=<SEC>
|
||||
Timeout each request after <SEC> seconds.
|
||||
.B \-t, \-\-timeout=<DURATION>
|
||||
Timeout each request after <DURATION>. Set 0 to disable
|
||||
timeout.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
|
@ -215,6 +216,11 @@ Display this help and exit.
|
|||
.sp
|
||||
The <SIZE> argument is an integer and an optional unit (e.g., 10K is
|
||||
10 * 1024). Units are K, M and G (powers of 1024).
|
||||
.sp
|
||||
The <DURATION> argument is an integer and an optional unit (e.g., 1s
|
||||
is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms
|
||||
(hours, minutes, seconds and milliseconds, respectively). If a unit
|
||||
is omitted, a second is used as unit.
|
||||
.SH DEPENDENCY BASED PRIORITY
|
||||
.sp
|
||||
nghttp sends priority hints to server by default unless
|
||||
|
|
|
@ -38,9 +38,10 @@ OPTIONS
|
|||
'index.html' is used as a filename. Not implemented
|
||||
yet.
|
||||
|
||||
.. option:: -t, --timeout=<SEC>
|
||||
.. option:: -t, --timeout=<DURATION>
|
||||
|
||||
Timeout each request after <SEC> seconds.
|
||||
Timeout each request after <DURATION>. Set 0 to disable
|
||||
timeout.
|
||||
|
||||
.. option:: -w, --window-bits=<N>
|
||||
|
||||
|
@ -168,6 +169,11 @@ OPTIONS
|
|||
The <SIZE> argument is an integer and an optional unit (e.g., 10K is
|
||||
10 * 1024). Units are K, M and G (powers of 1024).
|
||||
|
||||
The <DURATION> argument is an integer and an optional unit (e.g., 1s
|
||||
is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms
|
||||
(hours, minutes, seconds and milliseconds, respectively). If a unit
|
||||
is omitted, a second is used as unit.
|
||||
|
||||
DEPENDENCY BASED PRIORITY
|
||||
-------------------------
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTPD" "1" "April 27, 2015" "0.7.13" "nghttp2"
|
||||
.TH "NGHTTPD" "1" "May 08, 2015" "0.7.14" "nghttp2"
|
||||
.SH NAME
|
||||
nghttpd \- HTTP/2 experimental server
|
||||
.
|
||||
|
@ -119,6 +119,14 @@ Specify 0 to disable padding.
|
|||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-m, \-\-max\-concurrent\-streams=<N>
|
||||
Set the maximum number of the concurrent streams in one
|
||||
HTTP/2 session.
|
||||
.sp
|
||||
Default: \fB100\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-n, \-\-workers=<N>
|
||||
Set the number of worker threads.
|
||||
.sp
|
||||
|
|
|
@ -85,6 +85,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.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTPX" "1" "April 27, 2015" "0.7.13" "nghttp2"
|
||||
.TH "NGHTTPX" "1" "May 08, 2015" "0.7.14" "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
|
||||
|
|
|
@ -619,6 +619,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
|
||||
~~~~~
|
||||
|
|
|
@ -189,7 +189,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 */
|
||||
|
|
|
@ -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,46 @@ func TestH2H1RequestTrailer(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestH2H1HeaderFieldBuffer tests that request with header fields
|
||||
// larger than configured buffer size is rejected.
|
||||
func TestH2H1HeaderFieldBuffer(t *testing.T) {
|
||||
st := newServerTester([]string{"--header-field-buffer=10"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Fatal("execution path should not be here")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1HeaderFieldBuffer",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 431; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1HeaderFields tests that request with header fields more
|
||||
// than configured number is rejected.
|
||||
func TestH2H1HeaderFields(t *testing.T) {
|
||||
st := newServerTester([]string{"--max-header-fields=1"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Fatal("execution path should not be here")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1HeaderFields",
|
||||
// we have at least 4 pseudo-header fields sent, and
|
||||
// that ensures that buffer limit exceeds.
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 431; got != want {
|
||||
t.Errorf("status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1Upgrade tests HTTP Upgrade to HTTP/2
|
||||
func TestH2H1Upgrade(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {})
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -297,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
|
||||
}
|
||||
|
|
|
@ -2895,13 +2895,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
|
||||
|
@ -3492,7 +3492,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;
|
||||
|
|
|
@ -1157,15 +1157,11 @@ static nghttp2_hd_entry *add_hd_table_incremental(nghttp2_hd_context *context,
|
|||
}
|
||||
|
||||
static int name_eq(const nghttp2_nv *a, const nghttp2_nv *b) {
|
||||
return a->namelen == b->namelen &&
|
||||
a->name[a->namelen - 1] == b->name[a->namelen - 1] &&
|
||||
memeq(a->name, b->name, a->namelen);
|
||||
return a->namelen == b->namelen && memeq(a->name, b->name, a->namelen);
|
||||
}
|
||||
|
||||
static int value_eq(const nghttp2_nv *a, const nghttp2_nv *b) {
|
||||
return a->valuelen == b->valuelen &&
|
||||
a->value[a->valuelen - 1] == b->value[a->valuelen - 1] &&
|
||||
memeq(a->value, b->value, a->valuelen);
|
||||
return a->valuelen == b->valuelen && memeq(a->value, b->value, a->valuelen);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
|
@ -1733,7 +1729,9 @@ static int hd_inflate_remove_bufs(nghttp2_hd_inflater *inflater, nghttp2_nv *nv,
|
|||
static int hd_inflate_remove_bufs_with_name(nghttp2_hd_inflater *inflater,
|
||||
nghttp2_nv *nv,
|
||||
nghttp2_hd_entry *ent_name) {
|
||||
#ifndef NDEBUG
|
||||
size_t rv;
|
||||
#endif
|
||||
size_t buflen;
|
||||
uint8_t *buf;
|
||||
nghttp2_mem *mem;
|
||||
|
@ -1751,8 +1749,11 @@ static int hd_inflate_remove_bufs_with_name(nghttp2_hd_inflater *inflater,
|
|||
|
||||
/* Copy including terminal NULL */
|
||||
memcpy(buf, ent_name->nv.name, ent_name->nv.namelen + 1);
|
||||
rv = nghttp2_bufs_remove_copy(&inflater->nvbufs,
|
||||
buf + ent_name->nv.namelen + 1);
|
||||
#ifndef NDEBUG
|
||||
rv =
|
||||
#endif
|
||||
nghttp2_bufs_remove_copy(&inflater->nvbufs,
|
||||
buf + ent_name->nv.namelen + 1);
|
||||
assert(ent_name->nv.namelen + 1 + rv == buflen);
|
||||
|
||||
nghttp2_bufs_reset(&inflater->nvbufs);
|
||||
|
|
|
@ -109,7 +109,7 @@ typedef enum {
|
|||
NGHTTP2_TOKEN_CONNECTION,
|
||||
NGHTTP2_TOKEN_KEEP_ALIVE,
|
||||
NGHTTP2_TOKEN_PROXY_CONNECTION,
|
||||
NGHTTP2_TOKEN_UPGRADE,
|
||||
NGHTTP2_TOKEN_UPGRADE
|
||||
} nghttp2_token;
|
||||
|
||||
typedef enum {
|
||||
|
|
|
@ -58,7 +58,7 @@ typedef enum {
|
|||
*/
|
||||
NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS = 1 << 1,
|
||||
NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC = 1 << 2,
|
||||
NGHTTP2_OPT_NO_HTTP_MESSAGING = 1 << 3,
|
||||
NGHTTP2_OPT_NO_HTTP_MESSAGING = 1 << 3
|
||||
} nghttp2_option_flag;
|
||||
|
||||
/**
|
||||
|
|
|
@ -81,7 +81,7 @@ typedef enum {
|
|||
/* indicates that this GOAWAY is just a notification for graceful
|
||||
shutdown. No nghttp2_session.goaway_flags should be updated on
|
||||
the reaction to this frame. */
|
||||
NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE = 0x2,
|
||||
NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE = 0x2
|
||||
} nghttp2_goaway_aux_flag;
|
||||
|
||||
/* struct used for GOAWAY frame */
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
typedef enum {
|
||||
NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE = 1 << 0,
|
||||
NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC = 1 << 1,
|
||||
NGHTTP2_OPTMASK_NO_HTTP_MESSAGING = 1 << 2,
|
||||
NGHTTP2_OPTMASK_NO_HTTP_MESSAGING = 1 << 2
|
||||
} nghttp2_optmask;
|
||||
|
||||
typedef enum {
|
||||
|
@ -85,7 +85,7 @@ typedef enum {
|
|||
NGHTTP2_IB_READ_PAD_DATA,
|
||||
NGHTTP2_IB_READ_DATA,
|
||||
NGHTTP2_IB_IGN_DATA,
|
||||
NGHTTP2_IB_IGN_ALL,
|
||||
NGHTTP2_IB_IGN_ALL
|
||||
} nghttp2_inbound_state;
|
||||
|
||||
#define NGHTTP2_INBOUND_NUM_IV 7
|
||||
|
@ -137,7 +137,7 @@ typedef enum {
|
|||
/* Flag means GOAWAY was sent */
|
||||
NGHTTP2_GOAWAY_SENT = 0x4,
|
||||
/* Flag means GOAWAY was received */
|
||||
NGHTTP2_GOAWAY_RECV = 0x8,
|
||||
NGHTTP2_GOAWAY_RECV = 0x8
|
||||
} nghttp2_goaway_flag;
|
||||
|
||||
struct nghttp2_session {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -88,8 +88,9 @@ template <typename Array> void append_nv(Stream *stream, const Array &nva) {
|
|||
|
||||
Config::Config()
|
||||
: stream_read_timeout(60.), stream_write_timeout(60.), 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),
|
||||
padding(0), num_worker(1), max_concurrent_streams(100),
|
||||
header_table_size(-1), port(0), verbose(false), daemon(false),
|
||||
verify_client(false), no_tls(false), error_gzip(false),
|
||||
early_response(false), hexdump(false) {}
|
||||
|
||||
Config::~Config() {}
|
||||
|
@ -657,18 +658,21 @@ int Http2Handler::connection_made() {
|
|||
int r;
|
||||
|
||||
r = nghttp2_session_server_new(&session_, sessions_->get_callbacks(), this);
|
||||
|
||||
if (r != 0) {
|
||||
return r;
|
||||
}
|
||||
|
||||
auto config = sessions_->get_config();
|
||||
std::array<nghttp2_settings_entry, 4> entry;
|
||||
size_t niv = 1;
|
||||
|
||||
entry[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
|
||||
entry[0].value = 100;
|
||||
entry[0].value = config->max_concurrent_streams;
|
||||
|
||||
if (sessions_->get_config()->header_table_size >= 0) {
|
||||
if (config->header_table_size >= 0) {
|
||||
entry[niv].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
|
||||
entry[niv].value = sessions_->get_config()->header_table_size;
|
||||
entry[niv].value = config->header_table_size;
|
||||
++niv;
|
||||
}
|
||||
r = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, entry.data(), niv);
|
||||
|
|
|
@ -62,6 +62,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;
|
||||
|
|
178
src/h2load.cc
178
src/h2load.cc
|
@ -150,8 +150,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 +165,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 +471,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 +523,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 +650,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 +705,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 +757,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
|
||||
|
||||
|
@ -1408,7 +1463,7 @@ int main(int argc, char **argv) {
|
|||
}
|
||||
}
|
||||
|
||||
auto time_stats = process_time_stats(workers);
|
||||
auto ts = process_time_stats(workers);
|
||||
|
||||
// Requests which have not been issued due to connection errors, are
|
||||
// counted towards req_failed and req_error.
|
||||
|
@ -1441,14 +1496,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);
|
||||
|
||||
|
|
30
src/h2load.h
30
src/h2load.h
|
@ -94,13 +94,25 @@ 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 +144,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 +189,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 +234,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();
|
||||
};
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "h2load_spdy_session.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cerrno>
|
||||
|
||||
#include "h2load.h"
|
||||
#include "util.h"
|
||||
|
|
|
@ -88,6 +88,7 @@ void print_usage(std::ostream &out) {
|
|||
|
||||
namespace {
|
||||
void print_help(std::ostream &out) {
|
||||
Config config;
|
||||
print_usage(out);
|
||||
out << R"(
|
||||
<PORT> Specify listening port number.
|
||||
|
@ -128,6 +129,10 @@ Options:
|
|||
-b, --padding=<N>
|
||||
Add at most <N> bytes to a frame payload as padding.
|
||||
Specify 0 to disable padding.
|
||||
-m, --max-concurrent-streams=<N>
|
||||
Set the maximum number of the concurrent streams in one
|
||||
HTTP/2 session.
|
||||
Default: )" << config.max_concurrent_streams << R"(
|
||||
-n, --workers=<N>
|
||||
Set the number of worker threads.
|
||||
Default: 1
|
||||
|
@ -173,6 +178,7 @@ int main(int argc, char **argv) {
|
|||
{"header-table-size", required_argument, nullptr, 'c'},
|
||||
{"push", required_argument, nullptr, 'p'},
|
||||
{"padding", required_argument, nullptr, 'b'},
|
||||
{"max-concurrent-streams", required_argument, nullptr, 'm'},
|
||||
{"workers", required_argument, nullptr, 'n'},
|
||||
{"error-gzip", no_argument, nullptr, 'e'},
|
||||
{"no-tls", no_argument, &flag, 1},
|
||||
|
@ -184,7 +190,7 @@ int main(int argc, char **argv) {
|
|||
{"hexdump", no_argument, &flag, 7},
|
||||
{nullptr, 0, nullptr, 0}};
|
||||
int option_index = 0;
|
||||
int c = getopt_long(argc, argv, "DVb:c:d:ehn:p:va:", long_options,
|
||||
int c = getopt_long(argc, argv, "DVb:c:d:ehm:n:p:va:", long_options,
|
||||
&option_index);
|
||||
char *end;
|
||||
if (c == -1) {
|
||||
|
@ -209,6 +215,16 @@ int main(int argc, char **argv) {
|
|||
case 'e':
|
||||
config.error_gzip = true;
|
||||
break;
|
||||
case 'm': {
|
||||
// max-concurrent-streams option
|
||||
auto n = util::parse_uint(optarg);
|
||||
if (n == -1) {
|
||||
std::cerr << "-m: invalid argument: " << optarg << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
config.max_concurrent_streams = n;
|
||||
break;
|
||||
}
|
||||
case 'n':
|
||||
#ifdef NOTHREADS
|
||||
std::cerr << "-n: WARNING: Threading disabled at build time, "
|
||||
|
|
22
src/shrpx.cc
22
src/shrpx.cc
|
@ -913,6 +913,8 @@ void fill_default_config() {
|
|||
mod_config()->fetch_ocsp_response_file =
|
||||
strcopy(PKGDATADIR "/fetch-ocsp-response");
|
||||
mod_config()->no_ocsp = false;
|
||||
mod_config()->header_field_buffer = 64 * 1024;
|
||||
mod_config()->max_header_fields = 100;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
|
@ -1337,6 +1339,16 @@ HTTP:
|
|||
won't replace anything already set. This option can be
|
||||
used several times to specify multiple header fields.
|
||||
Example: --add-response-header="foo: bar"
|
||||
--header-field-buffer=<SIZE>
|
||||
Set maximum buffer size for incoming HTTP header field
|
||||
list. This is the sum of header name and value in
|
||||
bytes.
|
||||
Default: )"
|
||||
<< util::utos_with_unit(get_config()->header_field_buffer) << R"(
|
||||
--max-header-fields=<N>
|
||||
Set maximum number of incoming HTTP header fields, which
|
||||
appear in one request or response header field list.
|
||||
Default: )" << get_config()->max_header_fields << R"(
|
||||
|
||||
Debug:
|
||||
--frontend-http2-dump-request-header=<PATH>
|
||||
|
@ -1497,6 +1509,8 @@ int main(int argc, char **argv) {
|
|||
{"fetch-ocsp-response-file", required_argument, &flag, 77},
|
||||
{"ocsp-update-interval", required_argument, &flag, 78},
|
||||
{"no-ocsp", no_argument, &flag, 79},
|
||||
{"header-field-buffer", required_argument, &flag, 80},
|
||||
{"max-header-fields", required_argument, &flag, 81},
|
||||
{nullptr, 0, nullptr, 0}};
|
||||
|
||||
int option_index = 0;
|
||||
|
@ -1847,6 +1861,14 @@ int main(int argc, char **argv) {
|
|||
// --no-ocsp
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_NO_OCSP, "yes");
|
||||
break;
|
||||
case 80:
|
||||
// --header-field-buffer
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_HEADER_FIELD_BUFFER, optarg);
|
||||
break;
|
||||
case 81:
|
||||
// --max-header-fields
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_MAX_HEADER_FIELDS, optarg);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -684,27 +684,75 @@ void ClientHandler::start_immediate_shutdown() {
|
|||
ev_timer_start(conn_.loop, &reneg_shutdown_timer_);
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Construct absolute request URI from |downstream|, mainly to log
|
||||
// request URI for proxy request (HTTP/2 proxy or client proxy). This
|
||||
// is mostly same routine found in
|
||||
// HttpDownstreamConnection::push_request_headers(), but vastly
|
||||
// simplified since we only care about absolute URI.
|
||||
std::string construct_absolute_request_uri(Downstream *downstream) {
|
||||
const char *authority = nullptr, *host = nullptr;
|
||||
if (!downstream->get_request_http2_authority().empty()) {
|
||||
authority = downstream->get_request_http2_authority().c_str();
|
||||
}
|
||||
auto h = downstream->get_request_header(http2::HD_HOST);
|
||||
if (h) {
|
||||
host = h->value.c_str();
|
||||
}
|
||||
if (!authority && !host) {
|
||||
return downstream->get_request_path();
|
||||
}
|
||||
std::string uri;
|
||||
if (downstream->get_request_http2_scheme().empty()) {
|
||||
// this comes from HTTP/1 upstream without scheme. Just use http.
|
||||
uri += "http://";
|
||||
} else {
|
||||
uri += downstream->get_request_http2_scheme();
|
||||
uri += "://";
|
||||
}
|
||||
if (authority) {
|
||||
uri += authority;
|
||||
} else {
|
||||
uri += host;
|
||||
}
|
||||
|
||||
// Server-wide OPTIONS takes following form in proxy request:
|
||||
//
|
||||
// OPTIONS http://example.org HTTP/1.1
|
||||
//
|
||||
// Notice that no slash after authority. See
|
||||
// http://tools.ietf.org/html/rfc7230#section-5.3.4
|
||||
if (downstream->get_request_path() != "*") {
|
||||
uri += downstream->get_request_path();
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void ClientHandler::write_accesslog(Downstream *downstream) {
|
||||
LogSpec lgsp = {
|
||||
downstream, ipaddr_.c_str(), downstream->get_request_method().c_str(),
|
||||
upstream_accesslog(
|
||||
get_config()->accesslog_format,
|
||||
LogSpec{
|
||||
downstream, ipaddr_.c_str(), downstream->get_request_method().c_str(),
|
||||
|
||||
downstream->get_request_path().empty()
|
||||
? downstream->get_request_http2_authority().c_str()
|
||||
: downstream->get_request_path().c_str(),
|
||||
(downstream->get_request_method() != "CONNECT" &&
|
||||
(get_config()->http2_proxy || get_config()->client_proxy))
|
||||
? construct_absolute_request_uri(downstream).c_str()
|
||||
: downstream->get_request_path().empty()
|
||||
? downstream->get_request_http2_authority().c_str()
|
||||
: downstream->get_request_path().c_str(),
|
||||
|
||||
alpn_.c_str(),
|
||||
alpn_.c_str(),
|
||||
|
||||
std::chrono::system_clock::now(), // time_now
|
||||
downstream->get_request_start_time(), // request_start_time
|
||||
std::chrono::high_resolution_clock::now(), // request_end_time
|
||||
std::chrono::system_clock::now(), // time_now
|
||||
downstream->get_request_start_time(), // request_start_time
|
||||
std::chrono::high_resolution_clock::now(), // request_end_time
|
||||
|
||||
downstream->get_request_major(), downstream->get_request_minor(),
|
||||
downstream->get_response_http_status(),
|
||||
downstream->get_response_sent_bodylen(), port_.c_str(),
|
||||
get_config()->port, get_config()->pid,
|
||||
};
|
||||
|
||||
upstream_accesslog(get_config()->accesslog_format, &lgsp);
|
||||
downstream->get_request_major(), downstream->get_request_minor(),
|
||||
downstream->get_response_http_status(),
|
||||
downstream->get_response_sent_bodylen(), port_.c_str(),
|
||||
get_config()->port, get_config()->pid,
|
||||
});
|
||||
}
|
||||
|
||||
void ClientHandler::write_accesslog(int major, int minor, unsigned int status,
|
||||
|
@ -712,20 +760,19 @@ void ClientHandler::write_accesslog(int major, int minor, unsigned int status,
|
|||
auto time_now = std::chrono::system_clock::now();
|
||||
auto highres_now = std::chrono::high_resolution_clock::now();
|
||||
|
||||
LogSpec lgsp = {
|
||||
nullptr, ipaddr_.c_str(),
|
||||
"-", // method
|
||||
"-", // path,
|
||||
alpn_.c_str(), time_now,
|
||||
highres_now, // request_start_time TODO is
|
||||
// there a better value?
|
||||
highres_now, // request_end_time
|
||||
major, minor, // major, minor
|
||||
status, body_bytes_sent, port_.c_str(),
|
||||
get_config()->port, get_config()->pid,
|
||||
};
|
||||
|
||||
upstream_accesslog(get_config()->accesslog_format, &lgsp);
|
||||
upstream_accesslog(get_config()->accesslog_format,
|
||||
LogSpec{
|
||||
nullptr, ipaddr_.c_str(),
|
||||
"-", // method
|
||||
"-", // path,
|
||||
alpn_.c_str(), time_now,
|
||||
highres_now, // request_start_time TODO is
|
||||
// there a better value?
|
||||
highres_now, // request_end_time
|
||||
major, minor, // major, minor
|
||||
status, body_bytes_sent, port_.c_str(),
|
||||
get_config()->port, get_config()->pid,
|
||||
});
|
||||
}
|
||||
|
||||
ClientHandler::WriteBuf *ClientHandler::get_wb() { return &wb_; }
|
||||
|
|
|
@ -150,6 +150,8 @@ const char SHRPX_OPT_BACKEND_HTTP2_CONNECTIONS_PER_WORKER[] =
|
|||
const char SHRPX_OPT_FETCH_OCSP_RESPONSE_FILE[] = "fetch-ocsp-response-file";
|
||||
const char SHRPX_OPT_OCSP_UPDATE_INTERVAL[] = "ocsp-update-interval";
|
||||
const char SHRPX_OPT_NO_OCSP[] = "no-ocsp";
|
||||
const char SHRPX_OPT_HEADER_FIELD_BUFFER[] = "header-field-buffer";
|
||||
const char SHRPX_OPT_MAX_HEADER_FIELDS[] = "max-header-fields";
|
||||
|
||||
namespace {
|
||||
Config *config = nullptr;
|
||||
|
@ -1212,6 +1214,15 @@ int parse_config(const char *opt, const char *optarg) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (util::strieq(opt, SHRPX_OPT_HEADER_FIELD_BUFFER)) {
|
||||
return parse_uint_with_unit(&mod_config()->header_field_buffer, opt,
|
||||
optarg);
|
||||
}
|
||||
|
||||
if (util::strieq(opt, SHRPX_OPT_MAX_HEADER_FIELDS)) {
|
||||
return parse_uint(&mod_config()->max_header_fields, opt, optarg);
|
||||
}
|
||||
|
||||
if (util::strieq(opt, "conf")) {
|
||||
LOG(WARN) << "conf: ignored";
|
||||
|
||||
|
|
|
@ -139,6 +139,8 @@ extern const char SHRPX_OPT_BACKEND_HTTP2_CONNECTIONS_PER_WORKER[];
|
|||
extern const char SHRPX_OPT_FETCH_OCSP_RESPONSE_FILE[];
|
||||
extern const char SHRPX_OPT_OCSP_UPDATE_INTERVAL[];
|
||||
extern const char SHRPX_OPT_NO_OCSP[];
|
||||
extern const char SHRPX_OPT_HEADER_FIELD_BUFFER[];
|
||||
extern const char SHRPX_OPT_MAX_HEADER_FIELDS[];
|
||||
|
||||
union sockaddr_union {
|
||||
sockaddr_storage storage;
|
||||
|
@ -282,6 +284,8 @@ struct Config {
|
|||
size_t rlimit_nofile;
|
||||
size_t downstream_request_buffer_size;
|
||||
size_t downstream_response_buffer_size;
|
||||
size_t header_field_buffer;
|
||||
size_t max_header_fields;
|
||||
// Bit mask to disable SSL/TLS protocol versions. This will be
|
||||
// passed to SSL_CTX_set_options().
|
||||
long int tls_proto_mask;
|
||||
|
|
|
@ -1211,4 +1211,8 @@ void Downstream::detach_blocked_link(BlockedLink *l) {
|
|||
blocked_link_ = nullptr;
|
||||
}
|
||||
|
||||
void Downstream::add_request_headers_sum(size_t amount) {
|
||||
request_headers_sum_ += amount;
|
||||
}
|
||||
|
||||
} // namespace shrpx
|
||||
|
|
|
@ -143,6 +143,7 @@ public:
|
|||
void set_request_method(std::string method);
|
||||
const std::string &get_request_method() const;
|
||||
void set_request_path(std::string path);
|
||||
void add_request_headers_sum(size_t amount);
|
||||
void
|
||||
set_request_start_time(std::chrono::high_resolution_clock::time_point time);
|
||||
const std::chrono::high_resolution_clock::time_point &
|
||||
|
@ -193,6 +194,10 @@ public:
|
|||
// header contains invalid header field. We can safely send error
|
||||
// response (502) to a client.
|
||||
MSG_BAD_HEADER,
|
||||
// header fields in HTTP/1 request exceed the configuration limit.
|
||||
// This state is only transitioned from INITIAL state, and solely
|
||||
// used to signal 431 status code to the client.
|
||||
HTTP1_REQUEST_HEADER_TOO_LARGE,
|
||||
};
|
||||
void set_request_state(int state);
|
||||
int get_request_state() const;
|
||||
|
@ -286,9 +291,6 @@ public:
|
|||
// Change the priority of downstream
|
||||
int change_priority(int32_t pri);
|
||||
|
||||
// Maximum buffer size for header name/value pairs.
|
||||
static constexpr size_t MAX_HEADERS_SUM = 128 * 1024;
|
||||
|
||||
bool get_rst_stream_after_end_stream() const;
|
||||
void set_rst_stream_after_end_stream(bool f);
|
||||
|
||||
|
|
|
@ -733,10 +733,15 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
|||
auto trailer = frame->headers.cat != NGHTTP2_HCAT_RESPONSE &&
|
||||
!downstream->get_expect_final_response();
|
||||
|
||||
if (downstream->get_response_headers_sum() > Downstream::MAX_HEADERS_SUM) {
|
||||
if (downstream->get_response_headers_sum() + namelen + valuelen >
|
||||
get_config()->header_field_buffer ||
|
||||
downstream->get_response_headers().size() >=
|
||||
get_config()->max_header_fields) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
DLOG(INFO, downstream) << "Too large header block size="
|
||||
<< downstream->get_response_headers_sum();
|
||||
DLOG(INFO, downstream)
|
||||
<< "Too large or many header field size="
|
||||
<< downstream->get_response_headers_sum() + namelen + valuelen
|
||||
<< ", num=" << downstream->get_response_headers().size() + 1;
|
||||
}
|
||||
|
||||
if (trailer) {
|
||||
|
|
|
@ -202,14 +202,19 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (downstream->get_request_headers_sum() > Downstream::MAX_HEADERS_SUM) {
|
||||
if (downstream->get_request_headers_sum() + namelen + valuelen >
|
||||
get_config()->header_field_buffer ||
|
||||
downstream->get_request_headers().size() >=
|
||||
get_config()->max_header_fields) {
|
||||
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
ULOG(INFO, upstream) << "Too large header block size="
|
||||
<< downstream->get_request_headers_sum();
|
||||
ULOG(INFO, upstream) << "Too large or many header field size="
|
||||
<< downstream->get_request_headers_sum() + namelen +
|
||||
valuelen << ", num="
|
||||
<< downstream->get_request_headers().size() + 1;
|
||||
}
|
||||
|
||||
// just ignore header fields if this is trailer part.
|
||||
|
|
|
@ -582,10 +582,29 @@ int htp_hdrs_completecb(http_parser *htp) {
|
|||
namespace {
|
||||
int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
|
||||
auto downstream = static_cast<Downstream *>(htp->data);
|
||||
|
||||
if (downstream->get_response_headers_sum() + len >
|
||||
get_config()->header_field_buffer) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
DLOG(INFO, downstream) << "Too large header block size="
|
||||
<< downstream->get_response_headers_sum() + len;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (downstream->get_response_state() == Downstream::INITIAL) {
|
||||
if (downstream->get_response_header_key_prev()) {
|
||||
downstream->append_last_response_header_key(data, len);
|
||||
} else {
|
||||
if (downstream->get_response_headers().size() >=
|
||||
get_config()->max_header_fields) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
DLOG(INFO, downstream)
|
||||
<< "Too many header field num="
|
||||
<< downstream->get_response_headers().size() + 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
downstream->add_response_header(std::string(data, len), "");
|
||||
}
|
||||
} else {
|
||||
|
@ -593,16 +612,18 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
|
|||
if (downstream->get_response_trailer_key_prev()) {
|
||||
downstream->append_last_response_trailer_key(data, len);
|
||||
} else {
|
||||
if (downstream->get_response_headers().size() >=
|
||||
get_config()->max_header_fields) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
DLOG(INFO, downstream)
|
||||
<< "Too many header field num="
|
||||
<< downstream->get_response_headers().size() + 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
downstream->add_response_trailer(std::string(data, len), "");
|
||||
}
|
||||
}
|
||||
if (downstream->get_response_headers_sum() > Downstream::MAX_HEADERS_SUM) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
DLOG(INFO, downstream) << "Too large header block size="
|
||||
<< downstream->get_response_headers_sum();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
@ -610,6 +631,14 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
|
|||
namespace {
|
||||
int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) {
|
||||
auto downstream = static_cast<Downstream *>(htp->data);
|
||||
if (downstream->get_response_headers_sum() + len >
|
||||
get_config()->header_field_buffer) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
DLOG(INFO, downstream) << "Too large header block size="
|
||||
<< downstream->get_response_headers_sum() + len;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
if (downstream->get_response_state() == Downstream::INITIAL) {
|
||||
if (downstream->get_response_header_key_prev()) {
|
||||
downstream->set_last_response_header_value(data, len);
|
||||
|
@ -623,13 +652,6 @@ int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) {
|
|||
downstream->append_last_response_trailer_value(data, len);
|
||||
}
|
||||
}
|
||||
if (downstream->get_response_headers_sum() > Downstream::MAX_HEADERS_SUM) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
DLOG(INFO, downstream) << "Too large header block size="
|
||||
<< downstream->get_response_headers_sum();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
|
|
@ -78,6 +78,17 @@ namespace {
|
|||
int htp_uricb(http_parser *htp, const char *data, size_t len) {
|
||||
auto upstream = static_cast<HttpsUpstream *>(htp->data);
|
||||
auto downstream = upstream->get_downstream();
|
||||
if (downstream->get_request_headers_sum() + len >
|
||||
get_config()->header_field_buffer) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
ULOG(INFO, upstream) << "Too large URI size="
|
||||
<< downstream->get_request_headers_sum() + len;
|
||||
}
|
||||
assert(downstream->get_request_state() == Downstream::INITIAL);
|
||||
downstream->set_request_state(Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE);
|
||||
return -1;
|
||||
}
|
||||
downstream->add_request_headers_sum(len);
|
||||
downstream->append_request_path(data, len);
|
||||
return 0;
|
||||
}
|
||||
|
@ -87,10 +98,31 @@ namespace {
|
|||
int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
|
||||
auto upstream = static_cast<HttpsUpstream *>(htp->data);
|
||||
auto downstream = upstream->get_downstream();
|
||||
if (downstream->get_request_headers_sum() + len >
|
||||
get_config()->header_field_buffer) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
ULOG(INFO, upstream) << "Too large header block size="
|
||||
<< downstream->get_request_headers_sum() + len;
|
||||
}
|
||||
if (downstream->get_request_state() == Downstream::INITIAL) {
|
||||
downstream->set_request_state(Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
if (downstream->get_request_state() == Downstream::INITIAL) {
|
||||
if (downstream->get_request_header_key_prev()) {
|
||||
downstream->append_last_request_header_key(data, len);
|
||||
} else {
|
||||
if (downstream->get_request_headers().size() >=
|
||||
get_config()->max_header_fields) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
ULOG(INFO, upstream) << "Too many header field num="
|
||||
<< downstream->get_request_headers().size() + 1;
|
||||
}
|
||||
downstream->set_request_state(
|
||||
Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE);
|
||||
return -1;
|
||||
}
|
||||
downstream->add_request_header(std::string(data, len), "");
|
||||
}
|
||||
} else {
|
||||
|
@ -98,16 +130,17 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
|
|||
if (downstream->get_request_trailer_key_prev()) {
|
||||
downstream->append_last_request_trailer_key(data, len);
|
||||
} else {
|
||||
if (downstream->get_request_headers().size() >=
|
||||
get_config()->max_header_fields) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
ULOG(INFO, upstream) << "Too many header field num="
|
||||
<< downstream->get_request_headers().size() + 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
downstream->add_request_trailer(std::string(data, len), "");
|
||||
}
|
||||
}
|
||||
if (downstream->get_request_headers_sum() > Downstream::MAX_HEADERS_SUM) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
ULOG(INFO, upstream) << "Too large header block size="
|
||||
<< downstream->get_request_headers_sum();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
@ -116,6 +149,17 @@ namespace {
|
|||
int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) {
|
||||
auto upstream = static_cast<HttpsUpstream *>(htp->data);
|
||||
auto downstream = upstream->get_downstream();
|
||||
if (downstream->get_request_headers_sum() + len >
|
||||
get_config()->header_field_buffer) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
ULOG(INFO, upstream) << "Too large header block size="
|
||||
<< downstream->get_request_headers_sum() + len;
|
||||
}
|
||||
if (downstream->get_request_state() == Downstream::INITIAL) {
|
||||
downstream->set_request_state(Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
if (downstream->get_request_state() == Downstream::INITIAL) {
|
||||
if (downstream->get_request_header_key_prev()) {
|
||||
downstream->set_last_request_header_value(data, len);
|
||||
|
@ -129,13 +173,6 @@ int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) {
|
|||
downstream->append_last_request_trailer_value(data, len);
|
||||
}
|
||||
}
|
||||
if (downstream->get_request_headers_sum() > Downstream::MAX_HEADERS_SUM) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
ULOG(INFO, upstream) << "Too large header block size="
|
||||
<< downstream->get_request_headers_sum();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
@ -181,7 +218,12 @@ void rewrite_request_host_path_from_uri(Downstream *downstream, const char *uri,
|
|||
path += '?';
|
||||
path.append(uri + fdata.off, fdata.len);
|
||||
}
|
||||
downstream->set_request_path(path);
|
||||
downstream->set_request_path(std::move(path));
|
||||
if (get_config()->http2_proxy || get_config()->client_proxy) {
|
||||
std::string scheme;
|
||||
http2::copy_url_component(scheme, &u, UF_SCHEMA, uri);
|
||||
downstream->set_request_http2_scheme(std::move(scheme));
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
|
@ -229,22 +271,23 @@ int htp_hdrs_completecb(http_parser *htp) {
|
|||
|
||||
if (downstream->get_request_method() != "CONNECT") {
|
||||
http_parser_url u{};
|
||||
auto uri = downstream->get_request_path().c_str();
|
||||
rv = http_parser_parse_url(uri, downstream->get_request_path().size(), 0,
|
||||
&u);
|
||||
// make a copy of request path, since we may set request path
|
||||
// while we are refering to original request path.
|
||||
auto uri = downstream->get_request_path();
|
||||
rv = http_parser_parse_url(uri.c_str(),
|
||||
downstream->get_request_path().size(), 0, &u);
|
||||
if (rv != 0) {
|
||||
// Expect to respond with 400 bad request
|
||||
return -1;
|
||||
}
|
||||
// checking UF_HOST could be redundant, but just in case ...
|
||||
if (!(u.field_set & (1 << UF_SCHEMA)) || !(u.field_set & (1 << UF_HOST))) {
|
||||
if (get_config()->client_proxy) {
|
||||
if (get_config()->http2_proxy || get_config()->client_proxy) {
|
||||
// Request URI should be absolute-form for client proxy mode
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
rewrite_request_host_path_from_uri(downstream, uri, u);
|
||||
// uri could be invalidated here
|
||||
rewrite_request_host_path_from_uri(downstream, uri.c_str(), u);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -407,9 +450,15 @@ int HttpsUpstream::on_read() {
|
|||
|
||||
unsigned int status_code;
|
||||
|
||||
if (downstream &&
|
||||
downstream->get_request_state() == Downstream::CONNECT_FAIL) {
|
||||
status_code = 503;
|
||||
if (downstream) {
|
||||
if (downstream->get_request_state() == Downstream::CONNECT_FAIL) {
|
||||
status_code = 503;
|
||||
} else if (downstream->get_request_state() ==
|
||||
Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE) {
|
||||
status_code = 431;
|
||||
} else {
|
||||
status_code = 400;
|
||||
}
|
||||
} else {
|
||||
status_code = 400;
|
||||
}
|
||||
|
|
|
@ -158,7 +158,8 @@ std::pair<OutputIterator, size_t> copy(const char *src, size_t avail,
|
|||
}
|
||||
} // namespace
|
||||
|
||||
void upstream_accesslog(const std::vector<LogFragment> &lfv, LogSpec *lgsp) {
|
||||
void upstream_accesslog(const std::vector<LogFragment> &lfv,
|
||||
const LogSpec &lgsp) {
|
||||
auto lgconf = log_config();
|
||||
|
||||
if (lgconf->accesslog_fd == -1 && !get_config()->accesslog_syslog) {
|
||||
|
@ -167,12 +168,12 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv, LogSpec *lgsp) {
|
|||
|
||||
char buf[4096];
|
||||
|
||||
auto downstream = lgsp->downstream;
|
||||
auto downstream = lgsp.downstream;
|
||||
|
||||
auto p = buf;
|
||||
auto avail = sizeof(buf) - 2;
|
||||
|
||||
lgconf->update_tstamp(lgsp->time_now);
|
||||
lgconf->update_tstamp(lgsp.time_now);
|
||||
auto &time_local = lgconf->time_local_str;
|
||||
auto &time_iso8601 = lgconf->time_iso8601_str;
|
||||
|
||||
|
@ -182,7 +183,7 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv, LogSpec *lgsp) {
|
|||
std::tie(p, avail) = copy(lf.value.get(), avail, p);
|
||||
break;
|
||||
case SHRPX_LOGF_REMOTE_ADDR:
|
||||
std::tie(p, avail) = copy(lgsp->remote_addr, avail, p);
|
||||
std::tie(p, avail) = copy(lgsp.remote_addr, avail, p);
|
||||
break;
|
||||
case SHRPX_LOGF_TIME_LOCAL:
|
||||
std::tie(p, avail) = copy(time_local.c_str(), avail, p);
|
||||
|
@ -191,22 +192,22 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv, LogSpec *lgsp) {
|
|||
std::tie(p, avail) = copy(time_iso8601.c_str(), avail, p);
|
||||
break;
|
||||
case SHRPX_LOGF_REQUEST:
|
||||
std::tie(p, avail) = copy(lgsp->method, avail, p);
|
||||
std::tie(p, avail) = copy(lgsp.method, avail, p);
|
||||
std::tie(p, avail) = copy(" ", avail, p);
|
||||
std::tie(p, avail) = copy(lgsp->path, avail, p);
|
||||
std::tie(p, avail) = copy(lgsp.path, avail, p);
|
||||
std::tie(p, avail) = copy(" HTTP/", avail, p);
|
||||
std::tie(p, avail) = copy(util::utos(lgsp->major).c_str(), avail, p);
|
||||
if (lgsp->major < 2) {
|
||||
std::tie(p, avail) = copy(util::utos(lgsp.major).c_str(), avail, p);
|
||||
if (lgsp.major < 2) {
|
||||
std::tie(p, avail) = copy(".", avail, p);
|
||||
std::tie(p, avail) = copy(util::utos(lgsp->minor).c_str(), avail, p);
|
||||
std::tie(p, avail) = copy(util::utos(lgsp.minor).c_str(), avail, p);
|
||||
}
|
||||
break;
|
||||
case SHRPX_LOGF_STATUS:
|
||||
std::tie(p, avail) = copy(util::utos(lgsp->status).c_str(), avail, p);
|
||||
std::tie(p, avail) = copy(util::utos(lgsp.status).c_str(), avail, p);
|
||||
break;
|
||||
case SHRPX_LOGF_BODY_BYTES_SENT:
|
||||
std::tie(p, avail) =
|
||||
copy(util::utos(lgsp->body_bytes_sent).c_str(), avail, p);
|
||||
copy(util::utos(lgsp.body_bytes_sent).c_str(), avail, p);
|
||||
break;
|
||||
case SHRPX_LOGF_HTTP:
|
||||
if (downstream) {
|
||||
|
@ -221,15 +222,14 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv, LogSpec *lgsp) {
|
|||
|
||||
break;
|
||||
case SHRPX_LOGF_REMOTE_PORT:
|
||||
std::tie(p, avail) = copy(lgsp->remote_port, avail, p);
|
||||
std::tie(p, avail) = copy(lgsp.remote_port, avail, p);
|
||||
break;
|
||||
case SHRPX_LOGF_SERVER_PORT:
|
||||
std::tie(p, avail) =
|
||||
copy(util::utos(lgsp->server_port).c_str(), avail, p);
|
||||
std::tie(p, avail) = copy(util::utos(lgsp.server_port).c_str(), avail, p);
|
||||
break;
|
||||
case SHRPX_LOGF_REQUEST_TIME: {
|
||||
auto t = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
lgsp->request_end_time - lgsp->request_start_time).count();
|
||||
lgsp.request_end_time - lgsp.request_start_time).count();
|
||||
|
||||
auto frac = util::utos(t % 1000);
|
||||
auto sec = util::utos(t / 1000);
|
||||
|
@ -242,10 +242,10 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv, LogSpec *lgsp) {
|
|||
std::tie(p, avail) = copy(sec.c_str(), avail, p);
|
||||
} break;
|
||||
case SHRPX_LOGF_PID:
|
||||
std::tie(p, avail) = copy(util::utos(lgsp->pid).c_str(), avail, p);
|
||||
std::tie(p, avail) = copy(util::utos(lgsp.pid).c_str(), avail, p);
|
||||
break;
|
||||
case SHRPX_LOGF_ALPN:
|
||||
std::tie(p, avail) = copy(lgsp->alpn, avail, p);
|
||||
std::tie(p, avail) = copy(lgsp.alpn, avail, p);
|
||||
break;
|
||||
case SHRPX_LOGF_NONE:
|
||||
break;
|
||||
|
|
|
@ -139,7 +139,8 @@ struct LogSpec {
|
|||
pid_t pid;
|
||||
};
|
||||
|
||||
void upstream_accesslog(const std::vector<LogFragment> &lf, LogSpec *lgsp);
|
||||
void upstream_accesslog(const std::vector<LogFragment> &lf,
|
||||
const LogSpec &lgsp);
|
||||
|
||||
int reopen_log_files();
|
||||
|
||||
|
|
|
@ -163,6 +163,19 @@ void on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type,
|
|||
<< downstream->get_stream_id() << "\n" << ss.str();
|
||||
}
|
||||
|
||||
size_t num_headers = 0;
|
||||
size_t header_buffer = 0;
|
||||
for (size_t i = 0; nv[i]; i += 2) {
|
||||
++num_headers;
|
||||
header_buffer += strlen(nv[i]) + strlen(nv[i + 1]);
|
||||
}
|
||||
|
||||
if (header_buffer > get_config()->header_field_buffer ||
|
||||
num_headers > get_config()->max_header_fields) {
|
||||
upstream->rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; nv[i]; i += 2) {
|
||||
downstream->add_request_header(nv[i], nv[i + 1]);
|
||||
}
|
||||
|
@ -428,6 +441,12 @@ SpdyUpstream::SpdyUpstream(uint16_t version, ClientHandler *handler)
|
|||
rv = spdylay_session_server_new(&session_, version, &callbacks, this);
|
||||
assert(rv == 0);
|
||||
|
||||
uint32_t max_buffer = 65536;
|
||||
rv = spdylay_session_set_option(session_,
|
||||
SPDYLAY_OPT_MAX_RECV_CTRL_FRAME_BUFFER,
|
||||
&max_buffer, sizeof(max_buffer));
|
||||
assert(rv == 0);
|
||||
|
||||
if (version >= SPDYLAY_PROTO_SPDY3) {
|
||||
int val = 1;
|
||||
flow_control_ = true;
|
||||
|
|
Loading…
Reference in New Issue