diff options
author | Stefan Eissing <icing@apache.org> | 2022-07-02 11:11:31 +0200 |
---|---|---|
committer | Stefan Eissing <icing@apache.org> | 2022-07-02 11:11:31 +0200 |
commit | 76c7f4a33a3c36ffe114d7816318b03147ff881d (patch) | |
tree | a5bebad846e0907763f63d00df95a4277ef2f4b4 /test | |
parent | *) test: improved nghttp client output parsing. (diff) | |
download | apache2-76c7f4a33a3c36ffe114d7816318b03147ff881d.tar.xz apache2-76c7f4a33a3c36ffe114d7816318b03147ff881d.zip |
*) test/modules/http2: adding tests for response trailers with
or without a body. This reproduces a bug reported in
<https://github.com/icing/mod_h2/issues/233>
where trailers are not sent on an empty response
body. This is used in gRPC.
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1902408 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'test')
-rw-r--r-- | test/modules/http2/mod_h2test/mod_h2test.c | 132 | ||||
-rw-r--r-- | test/modules/http2/test_105_timeout.py | 40 | ||||
-rw-r--r-- | test/modules/http2/test_202_trailer.py | 32 |
3 files changed, 178 insertions, 26 deletions
diff --git a/test/modules/http2/mod_h2test/mod_h2test.c b/test/modules/http2/mod_h2test/mod_h2test.c index 63dc28aac4..0b0f057e4a 100644 --- a/test/modules/http2/mod_h2test/mod_h2test.c +++ b/test/modules/http2/mod_h2test/mod_h2test.c @@ -43,6 +43,65 @@ AP_DECLARE_MODULE(h2test) = { #endif }; +#define SECS_PER_HOUR (60*60) +#define SECS_PER_DAY (24*SECS_PER_HOUR) + +static apr_status_t duration_parse(apr_interval_time_t *ptimeout, const char *value, + const char *def_unit) +{ + char *endp; + apr_int64_t n; + + n = apr_strtoi64(value, &endp, 10); + if (errno) { + return errno; + } + if (!endp || !*endp) { + if (!def_unit) def_unit = "s"; + } + else if (endp == value) { + return APR_EINVAL; + } + else { + def_unit = endp; + } + + switch (*def_unit) { + case 'D': + case 'd': + *ptimeout = apr_time_from_sec(n * SECS_PER_DAY); + break; + case 's': + case 'S': + *ptimeout = (apr_interval_time_t) apr_time_from_sec(n); + break; + case 'h': + case 'H': + /* Time is in hours */ + *ptimeout = (apr_interval_time_t) apr_time_from_sec(n * SECS_PER_HOUR); + break; + case 'm': + case 'M': + switch (*(++def_unit)) { + /* Time is in milliseconds */ + case 's': + case 'S': + *ptimeout = (apr_interval_time_t) n * 1000; + break; + /* Time is in minutes */ + case 'i': + case 'I': + *ptimeout = (apr_interval_time_t) apr_time_from_sec(n * 60); + break; + default: + return APR_EGENERAL; + } + break; + default: + return APR_EGENERAL; + } + return APR_SUCCESS; +} static int h2test_post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) @@ -163,9 +222,10 @@ static int h2test_delay_handler(request_rec *r) } if (r->args) { - rv = apr_cstr_atoi(&i, r->args); - if (APR_SUCCESS == rv) { - delay = apr_time_from_sec(i); + rv = duration_parse(&delay, r->args, "s"); + if (APR_SUCCESS != rv) { + ap_die(HTTP_BAD_REQUEST, r); + return OK; } } @@ -230,6 +290,71 @@ cleanup: return AP_FILTER_ERROR; } +static int h2test_trailer_handler(request_rec *r) +{ + conn_rec *c = r->connection; + apr_bucket_brigade *bb; + apr_bucket *b; + apr_status_t rv; + char buffer[8192]; + int i, chunks = 3; + long l; + int body_len = 0; + + if (strcmp(r->handler, "h2test-trailer")) { + return DECLINED; + } + if (r->method_number != M_GET && r->method_number != M_POST) { + return DECLINED; + } + + if (r->args) { + body_len = (int)apr_atoi64(r->args); + if (body_len < 0) body_len = 0; + } + + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "trailer_handler: processing request, %d body length", + body_len); + r->status = 200; + r->clength = body_len; + ap_set_content_length(r, body_len); + + ap_set_content_type(r, "application/octet-stream"); + apr_table_mergen(r->headers_out, "Trailer", "trailer-content-length"); + apr_table_set(r->trailers_out, "trailer-content-length", + apr_psprintf(r->pool, "%d", body_len)); + + bb = apr_brigade_create(r->pool, c->bucket_alloc); + memset(buffer, 0, sizeof(buffer)); + while (body_len > 0) { + l = (sizeof(buffer) > body_len)? body_len : sizeof(buffer); + body_len -= l; + rv = apr_brigade_write(bb, NULL, NULL, buffer, l); + if (APR_SUCCESS != rv) goto cleanup; + rv = ap_pass_brigade(r->output_filters, bb); + if (APR_SUCCESS != rv) goto cleanup; + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, + "trailer_handler: passed %ld bytes as response body", l); + } + /* we are done */ + b = apr_bucket_eos_create(c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb, b); + rv = ap_pass_brigade(r->output_filters, bb); + apr_brigade_cleanup(bb); + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, "trailer_handler: response passed"); + +cleanup: + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, + "trailer_handler: request cleanup, r->status=%d, aborted=%d", + r->status, c->aborted); + if (rv == APR_SUCCESS + || r->status != HTTP_OK + || c->aborted) { + return OK; + } + return AP_FILTER_ERROR; +} + /* Install this module into the apache2 infrastructure. */ static void h2test_hooks(apr_pool_t *pool) @@ -249,5 +374,6 @@ static void h2test_hooks(apr_pool_t *pool) /* test h2 handlers */ ap_hook_handler(h2test_echo_handler, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_handler(h2test_delay_handler, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_handler(h2test_trailer_handler, NULL, NULL, APR_HOOK_MIDDLE); } diff --git a/test/modules/http2/test_105_timeout.py b/test/modules/http2/test_105_timeout.py index d8dada90c4..e107a78fca 100644 --- a/test/modules/http2/test_105_timeout.py +++ b/test/modules/http2/test_105_timeout.py @@ -3,11 +3,10 @@ import time import pytest -from .env import H2Conf, H2TestEnv +from .env import H2Conf from pyhttpd.curl import CurlPiper -@pytest.mark.skipif(condition=H2TestEnv.is_unsupported, reason="mod_http2 not supported here") class TestTimeout: # Check that base servers 'Timeout' setting is observed on SSL handshake @@ -129,21 +128,22 @@ class TestTimeout: def test_h2_105_12(self, env): # long connection timeout, short stream timeout # sending a slow POST - conf = H2Conf(env) - conf.add_vhost_cgi() - conf.add("Timeout 10") - conf.add("H2StreamTimeout 1") - conf.install() - assert env.apache_restart() == 0 - url = env.mkurl("https", "cgi", "/h2test/delay?5") - piper = CurlPiper(env=env, url=url) - piper.start() - for _ in range(3): - time.sleep(2) - try: - piper.send("0123456789\n") - except BrokenPipeError: - break - piper.close() - assert piper.response - assert piper.response['status'] == 408 + if env.httpd_is_at_least("2.5.0"): + conf = H2Conf(env) + conf.add_vhost_cgi() + conf.add("Timeout 10") + conf.add("H2StreamTimeout 1") + conf.install() + assert env.apache_restart() == 0 + url = env.mkurl("https", "cgi", "/h2test/delay?5") + piper = CurlPiper(env=env, url=url) + piper.start() + for _ in range(3): + time.sleep(2) + try: + piper.send("0123456789\n") + except BrokenPipeError: + break + piper.close() + assert piper.response + assert piper.response['status'] == 408 diff --git a/test/modules/http2/test_202_trailer.py b/test/modules/http2/test_202_trailer.py index fce46c1574..8571955dd7 100644 --- a/test/modules/http2/test_202_trailer.py +++ b/test/modules/http2/test_202_trailer.py @@ -1,7 +1,7 @@ import os import pytest -from .env import H2Conf, H2TestEnv +from .env import H2Conf def setup_data(env): @@ -13,13 +13,20 @@ def setup_data(env): # The trailer tests depend on "nghttp" as no other client seems to be able to send those # rare things. -@pytest.mark.skipif(condition=H2TestEnv.is_unsupported, reason="mod_http2 not supported here") class TestTrailers: @pytest.fixture(autouse=True, scope='class') def _class_scope(self, env): setup_data(env) - H2Conf(env).add_vhost_cgi(h2proxy_self=True).install() + conf = H2Conf(env, extras={ + f"cgi.{env.http_tld}": [ + "<Location \"/h2test/trailer\">", + " SetHandler h2test-trailer", + "</Location>" + ], + }) + conf.add_vhost_cgi(h2proxy_self=True) + conf.install() assert env.apache_restart() == 0 # check if the server survives a trailer or two @@ -64,3 +71,22 @@ class TestTrailers: assert r.response["status"] < 300 assert r.response["body"] == b"X: 4a\n" + # check that our h2test-trailer handler works + def test_h2_202_10(self, env): + url = env.mkurl("https", "cgi", "/h2test/trailer?1024") + r = env.nghttp().get(url) + assert r.response["status"] == 200 + assert len(r.response["body"]) == 1024 + assert 'trailer' in r.response + assert 'trailer-content-length' in r.response['trailer'] + assert r.response['trailer']['trailer-content-length'] == '1024' + + # check that trailers also for with empty bodies + def test_h2_202_11(self, env): + url = env.mkurl("https", "cgi", "/h2test/trailer?0") + r = env.nghttp().get(url) + assert r.response["status"] == 200 + assert len(r.response["body"]) == 0 + assert 'trailer' in r.response + assert 'trailer-content-length' in r.response['trailer'] + assert r.response['trailer']['trailer-content-length'] == '0' |