Skip to content

Commit

Permalink
HTTP/2 WebSockets: adding throughput tests and test client improvements
Browse files Browse the repository at this point in the history
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1910649 13f79535-47bb-0310-9956-ffa450edef68
  • Loading branch information
icing committed Jun 28, 2023
1 parent 71269d0 commit 53cc13a
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 10 deletions.
16 changes: 15 additions & 1 deletion test/clients/h2ws.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -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:
Expand Down
25 changes: 24 additions & 1 deletion test/modules/http2/test_800_websockets.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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] ')])
Expand All @@ -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")
Expand All @@ -90,6 +92,7 @@ def _class_scope(self, env):
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()
Expand Down Expand Up @@ -308,3 +311,23 @@ def test_h2_800_16_ws_frame_delay(self, env: H2TestEnv, ws_server, frame_delay):
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')
20 changes: 12 additions & 8 deletions test/modules/http2/ws_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down

0 comments on commit 53cc13a

Please sign in to comment.