]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
HTTP/2 WebSockets: adding throughput tests and test client improvements
authorStefan Eissing <icing@apache.org>
Wed, 28 Jun 2023 08:55:19 +0000 (08:55 +0000)
committerStefan Eissing <icing@apache.org>
Wed, 28 Jun 2023 08:55:19 +0000 (08:55 +0000)
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1910649 13f79535-47bb-0310-9956-ffa450edef68

test/clients/h2ws.c
test/modules/http2/test_800_websockets.py
test/modules/http2/ws_server.py

index a090ac7b09bbc414319da6000cfe027e853b25ee..1de38760dd5228b923c133c4b40002e1bde6b9aa 100644 (file)
@@ -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:
index 76f9fe9475aa13e4b33fbc28308831cfd06161b4..2afa45edc3b0012eb2b0b546c391874bf4af246f 100644 (file)
@@ -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')
index bcb8d3b094bad5e46908cf0689778de868978aa0..99fb9cfd14e580a2a257ef934d0129c232fe968f 100644 (file)
@@ -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')