From: Stefan Eissing Date: Wed, 28 Jun 2023 08:55:19 +0000 (+0000) Subject: HTTP/2 WebSockets: adding throughput tests and test client improvements X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=53cc13a35346a07c83a37d5fa40db596885edbea;p=thirdparty%2Fapache%2Fhttpd.git HTTP/2 WebSockets: adding throughput tests and test client improvements git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1910649 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/test/clients/h2ws.c b/test/clients/h2ws.c index a090ac7b09b..1de38760dd5 100644 --- a/test/clients/h2ws.c +++ b/test/clients/h2ws.c @@ -605,6 +605,7 @@ static int h2_session_open(struct h2_session *session, const char *server_name, const char *host, uint16_t port) { nghttp2_session_callbacks *cbs = NULL; + nghttp2_settings_entry settings[2]; int rv = -1; memset(session, 0, sizeof(*session)); @@ -654,13 +655,26 @@ static int h2_session_open(struct h2_session *session, const char *server_name, goto leave; } /* submit initial settings */ - rv = nghttp2_submit_settings(session->ngh2, NGHTTP2_FLAG_NONE, NULL, 0); + settings[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; + settings[0].value = 100; + settings[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + settings[1].value = 10 * 1024 * 1024; + + rv = nghttp2_submit_settings(session->ngh2, NGHTTP2_FLAG_NONE, settings, 2); if (rv) { log_errf("submit settings", "error_code=%d, msg=%s\n", rv, nghttp2_strerror(rv)); rv = -1; goto leave; } + rv = nghttp2_session_set_local_window_size(session->ngh2, NGHTTP2_FLAG_NONE, + 0, 10 * 1024 * 1024); + if (rv) { + log_errf("set connection window size", "error_code=%d, msg=%s\n", rv, + nghttp2_strerror(rv)); + rv = -1; + goto leave; + } rv = 0; leave: diff --git a/test/modules/http2/test_800_websockets.py b/test/modules/http2/test_800_websockets.py index 76f9fe9475a..2afa45edc3b 100644 --- a/test/modules/http2/test_800_websockets.py +++ b/test/modules/http2/test_800_websockets.py @@ -45,6 +45,7 @@ def ws_run(env: H2TestEnv, path, authority=None, do_input=None, inbytes=None, # we write all output to files, because we manipulate input timings # and would run in deadlock situations with h2ws blocking operations # because its output is not consumed + start = datetime.now() with open(f'{env.gen_dir}/h2ws.stdout', 'w') as fdout: with open(f'{env.gen_dir}/h2ws.stderr', 'w') as fderr: proc = subprocess.Popen(args=args, stdin=subprocess.PIPE, @@ -64,6 +65,7 @@ def ws_run(env: H2TestEnv, path, authority=None, do_input=None, inbytes=None, log.error(f'ws_run: timeout expired') proc.kill() proc.communicate(timeout=timeout) + end = datetime.now() lines = open(f'{env.gen_dir}/h2ws.stdout').read().splitlines() infos = [line for line in lines if line.startswith('[1] ')] hex_content = ' '.join([line for line in lines if not line.startswith('[1] ')]) @@ -72,7 +74,7 @@ def ws_run(env: H2TestEnv, path, authority=None, do_input=None, inbytes=None, else: frames = bytearray.fromhex(hex_content) return ExecResult(args=args, exit_code=proc.returncode, - stdout=b'', stderr=b''), infos, frames + stdout=b'', stderr=b'', duration=end - start), infos, frames @pytest.mark.skipif(condition=H2TestEnv.is_unsupported, reason="mod_http2 not supported here") @@ -90,6 +92,7 @@ class TestWebSockets: f' H2WebSockets on', f' ProxyPass /ws/ http://127.0.0.1:{env.ws_port}/ \\', f' upgrade=websocket timeout=10', + f' ReadBufferSize 65535' ] }) conf.add_vhost_cgi(proxy_self=True, h2proxy_self=True).install() @@ -308,3 +311,23 @@ class TestWebSockets: assert len(frames) > 0 total_len = sum([f.data_len for f in frames if f.opcode == WsFrame.BINARY]) assert total_len == flen, f'{frames}\n{r}' + + # CONNECT to path with 1MB file and trigger delays between BINARY frame writes + @pytest.mark.parametrize("frame_len", [ + 64 * 1024, + 16 * 1024, + 1 * 1024, + ]) + def test_h2_800_17_ws_throughput(self, env: H2TestEnv, ws_server, frame_len): + fname = "data-1m" + flen = 1000*1000 + ncount = 5 + r, infos, frames = ws_run(env, path=f'/ws/file/{fname}/{frame_len}/0/{ncount}', + wait_close=0.1, send_close=False, timeout=30) + assert r.exit_code == 0, f'{r}' + assert infos == ['[1] :status: 200', '[1] EOF'], f'{r}' + assert len(frames) > 0 + total_len = sum([f.data_len for f in frames if f.opcode == WsFrame.BINARY]) + assert total_len == ncount * flen, f'{frames}\n{r}' + # to see these logged, invoke: `pytest -o log_cli=true` + log.info(f'throughput (frame-len={frame_len}): {(total_len / (1024*1024)) / r.duration.total_seconds():0.2f} MB/s') diff --git a/test/modules/http2/ws_server.py b/test/modules/http2/ws_server.py index bcb8d3b094b..99fb9cfd14e 100644 --- a/test/modules/http2/ws_server.py +++ b/test/modules/http2/ws_server.py @@ -57,14 +57,18 @@ async def on_async_conn(conn): delay_ms = 0 if len(pcomps) > 3: delay_ms = int(pcomps[3]) - with open(fpath, 'r+b') as fd: - while True: - buf = fd.read(bufsize) - if buf is None or len(buf) == 0: - break - await conn.send(buf) - if delay_ms > 0: - time.sleep(delay_ms/1000) + n = 1 + if len(pcomps) > 4: + n = int(pcomps[4]) + for _ in range(n): + with open(fpath, 'r+b') as fd: + while True: + buf = fd.read(bufsize) + if buf is None or len(buf) == 0: + break + await conn.send(buf) + if delay_ms > 0: + time.sleep(delay_ms/1000) else: log.info(f'unknown endpoint: {rpath}') await conn.close(code=4999, reason='path unknown')