Mailing List Archive

svn commit: r1902714 - in /httpd/httpd/branches/2.4.x/test: modules/http2/mod_h2test/mod_h2test.c modules/http2/test_202_trailer.py pyhttpd/nghttp.py
Author: icing
Date: Thu Jul 14 09:37:43 2022
New Revision: 1902714

URL: http://svn.apache.org/viewvc?rev=1902714&view=rev
Log:
*) test: backporting improved nghttp output parsing.
test/modules/http2 adding trailer tests.


Modified:
httpd/httpd/branches/2.4.x/test/modules/http2/mod_h2test/mod_h2test.c
httpd/httpd/branches/2.4.x/test/modules/http2/test_202_trailer.py
httpd/httpd/branches/2.4.x/test/pyhttpd/nghttp.py

Modified: httpd/httpd/branches/2.4.x/test/modules/http2/mod_h2test/mod_h2test.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/test/modules/http2/mod_h2test/mod_h2test.c?rev=1902714&r1=1902713&r2=1902714&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/test/modules/http2/mod_h2test/mod_h2test.c (original)
+++ httpd/httpd/branches/2.4.x/test/modules/http2/mod_h2test/mod_h2test.c Thu Jul 14 09:37:43 2022
@@ -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_
}

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,77 @@ 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, tclen;
+
+ if (strcmp(r->handler, "h2test-trailer")) {
+ return DECLINED;
+ }
+ if (r->method_number != M_GET && r->method_number != M_POST) {
+ return DECLINED;
+ }
+
+ if (r->args) {
+ tclen = 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");
+ 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);
+ }
+ /* flush it */
+ b = apr_bucket_flush_create(c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(bb, b);
+ rv = ap_pass_brigade(r->output_filters, bb);
+ apr_brigade_cleanup(bb);
+ apr_sleep(apr_time_from_msec(500));
+ /* set trailers */
+ apr_table_mergen(r->headers_out, "Trailer", "trailer-content-length");
+ apr_table_set(r->trailers_out, "trailer-content-length",
+ apr_psprintf(r->pool, "%d", tclen));
+ /* 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 +380,6 @@ static void h2test_hooks(apr_pool_t *poo
/* 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);
}


Modified: httpd/httpd/branches/2.4.x/test/modules/http2/test_202_trailer.py
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/test/modules/http2/test_202_trailer.py?rev=1902714&r1=1902713&r2=1902714&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/test/modules/http2/test_202_trailer.py (original)
+++ httpd/httpd/branches/2.4.x/test/modules/http2/test_202_trailer.py Thu Jul 14 09:37:43 2022
@@ -18,7 +18,15 @@ 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
@@ -26,40 +34,59 @@ class TestTrailers:
url = env.mkurl("https", "cgi", "/echo.py")
fpath = os.path.join(env.gen_dir, "data-1k")
r = env.nghttp().upload(url, fpath, options=["--trailer", "test: 1"])
- assert 300 > r.response["status"]
- assert 1000 == len(r.response["body"])
+ assert r.response["status"] < 300
+ assert len(r.response["body"]) == 1000

r = env.nghttp().upload(url, fpath, options=["--trailer", "test: 1b", "--trailer", "XXX: test"])
- assert 300 > r.response["status"]
- assert 1000 == len(r.response["body"])
+ assert r.response["status"] < 300
+ assert len(r.response["body"]) == 1000

# check if the server survives a trailer without content-length
def test_h2_202_02(self, env):
url = env.mkurl("https", "cgi", "/echo.py")
fpath = os.path.join(env.gen_dir, "data-1k")
r = env.nghttp().upload(url, fpath, options=["--trailer", "test: 2", "--no-content-length"])
- assert 300 > r.response["status"]
- assert 1000 == len(r.response["body"])
+ assert r.response["status"] < 300
+ assert len(r.response["body"]) == 1000

# check if echoing request headers in response from GET works
def test_h2_202_03(self, env):
url = env.mkurl("https", "cgi", "/echohd.py?name=X")
r = env.nghttp().get(url, options=["--header", "X: 3"])
- assert 300 > r.response["status"]
- assert b"X: 3\n" == r.response["body"]
+ assert r.response["status"] < 300
+ assert r.response["body"] == b"X: 3\n"

# check if echoing request headers in response from POST works
def test_h2_202_03b(self, env):
url = env.mkurl("https", "cgi", "/echohd.py?name=X")
r = env.nghttp().post_name(url, "Y", options=["--header", "X: 3b"])
- assert 300 > r.response["status"]
- assert b"X: 3b\n" == r.response["body"]
+ assert r.response["status"] < 300
+ assert r.response["body"] == b"X: 3b\n"

# check if echoing request headers in response from POST works, but trailers are not seen
# This is the way CGI invocation works.
def test_h2_202_04(self, env):
url = env.mkurl("https", "cgi", "/echohd.py?name=X")
r = env.nghttp().post_name(url, "Y", options=["--header", "X: 4a", "--trailer", "X: 4b"])
- assert 300 > r.response["status"]
- assert b"X: 4a\n" == r.response["body"]
+ 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'

Modified: httpd/httpd/branches/2.4.x/test/pyhttpd/nghttp.py
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/test/pyhttpd/nghttp.py?rev=1902714&r1=1902713&r2=1902714&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/test/pyhttpd/nghttp.py (original)
+++ httpd/httpd/branches/2.4.x/test/pyhttpd/nghttp.py Thu Jul 14 09:37:43 2022
@@ -80,6 +80,7 @@ class Nghttp:
# chunk output into lines. nghttp mixes text
# meta output with bytes from the response body.
lines = [l.decode() for l in btext.split(b'\n')]
+
for lidx, l in enumerate(lines):
if len(l) == 0:
body += '\n'
@@ -96,7 +97,6 @@ class Nghttp:
header[hname] += ", %s" % hval
else:
header[hname] = hval
- body = ''
continue

m = re.match(r'(.*)\[.*] recv HEADERS frame <.* stream_id=(\d+)>', l)
@@ -120,7 +120,6 @@ class Nghttp:
response["previous"] = prev
response[hkey] = s["header"]
s["header"] = {}
- body = ''
continue

m = re.match(r'(.*)\[.*] recv DATA frame <length=(\d+), .*stream_id=(\d+)>', l)
@@ -179,8 +178,8 @@ class Nghttp:

if '[' != l[0]:
skip_indents = None
- body += l + '\n'
-
+ body += l + '\n'
+
# the main request is done on the lowest odd numbered id
main_stream = 99999999999
for sid in streams: