Skip to content

Commit ca1195d

Browse files
committed
qir-hs-fixes
1 parent e9e64b1 commit ca1195d

4 files changed

Lines changed: 542 additions & 0 deletions

File tree

lib/roles/h3/ops-h3.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,29 @@ rops_perform_user_POLLOUT_h3(struct lws *wsi)
361361
lwsl_wsi_err(wsi, "lws_h3_client_handshake failed!");
362362
return -1;
363363
}
364+
365+
/*
366+
* The request headers (+FIN for GET) have been sent. Bound the
367+
* wait for the server's response the same way the H1 client does
368+
* (client-http.c lws_http_client_socket_service), so an H3 stream
369+
* whose reply is lost to packet corruption, a silent/blackholed
370+
* peer, or a key-phase desync fails cleanly here instead of
371+
* hanging on an otherwise-live QUIC connection forever.
372+
*
373+
* LRS_WAITING_SERVER_REPLY is chosen (rather than staying in
374+
* LRS_ESTABLISHED, which lws_h3_client_handshake() just set) so
375+
* that, if the timeout fires, lws_sul_wsitimeout_cb() delivers the
376+
* informative LWS_CALLBACK_CLIENT_CONNECTION_ERROR "Timed out
377+
* waiting server reply" that H1 clients get. On the first response
378+
* bytes the timeout is cleared and the state advanced to
379+
* LRS_ESTABLISHED by lws_client_interpret_server_handshake()
380+
* (client-http.c); the LRS_ESTABLISHED POLLOUT branch below is only
381+
* used for client body upload / writeable, which does not happen
382+
* while we are waiting for the reply to a request we just sent.
383+
*/
384+
lwsi_set_state(wsi, LRS_WAITING_SERVER_REPLY);
385+
lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE,
386+
(int)wsi->a.context->timeout_secs);
364387
#endif
365388
return 0;
366389
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
project(lws-minimal-http-client-timeout-h3 C)
2+
cmake_minimum_required(VERSION 3.10)
3+
find_package(libwebsockets CONFIG REQUIRED)
4+
list(APPEND CMAKE_MODULE_PATH ${LWS_CMAKE_DIR})
5+
include(CheckCSourceCompiles)
6+
include(LwsCheckRequirements)
7+
8+
set(SAMP lws-minimal-http-client-timeout-h3)
9+
set(SRCS minimal-http-client-timeout-h3.c)
10+
11+
set(requirements 1)
12+
require_lws_config(LWS_ROLE_QUIC 1 requirements)
13+
require_lws_config(LWS_WITH_HTTP3 1 requirements)
14+
require_lws_config(LWS_WITH_CLIENT 1 requirements)
15+
require_lws_config(LWS_WITH_SERVER 1 requirements)
16+
17+
if (requirements)
18+
add_executable(${SAMP} ${SRCS})
19+
20+
if (websockets_shared)
21+
target_link_libraries(${SAMP} websockets_shared ${LIBWEBSOCKETS_DEP_LIBS})
22+
add_dependencies(${SAMP} websockets_shared)
23+
else()
24+
target_link_libraries(${SAMP} websockets ${LIBWEBSOCKETS_DEP_LIBS})
25+
endif()
26+
27+
if (LWS_WITH_MINIMAL_EXAMPLES)
28+
#
29+
# The test starts the blackhole QUIC server (-s) on a free port,
30+
# gives it a moment to bind, runs the client against it, then
31+
# kills the server. The client's success condition is that it
32+
# times out awaiting the reply (the server never answers), which
33+
# exercises the H3 client reply timeout. We bundle server +
34+
# client into a single ctest command with a tiny inline shell
35+
# wrapper so the test is self-contained and does not depend on
36+
# netstat/lsof-based port detection helpers.
37+
#
38+
# Expected exit codes: server (-s) always returns 0; the client
39+
# returns 0 if it saw the timeout and non-zero otherwise, so the
40+
# client exit code is the test result.
41+
lws_get_free_port(PORT_H3TO)
42+
43+
add_test(NAME h3to COMMAND sh -c
44+
"$<TARGET_FILE:${SAMP}> -s -p ${PORT_H3TO} -d1039 2>/dev/null & SRV=$!; sleep 2; $<TARGET_FILE:${SAMP}> -p ${PORT_H3TO} -d1039; RC=$?; kill $SRV 2>/dev/null; wait $SRV 2>/dev/null; exit $RC")
45+
set_tests_properties(h3to PROPERTIES
46+
WORKING_DIRECTORY .
47+
TIMEOUT 40)
48+
endif()
49+
endif()
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# lws-minimal-http-client-timeout-h3
2+
3+
This tests the HTTP/3 client reply timeout.
4+
5+
It runs as a single binary in two modes:
6+
7+
- by default, as a client that issues a single `GET` over HTTP/3 against the
8+
blackhole server described below, and
9+
- with `-s`, as a "blackhole" QUIC/H3 server that completes the QUIC + TLS
10+
handshake but then deliberately never sends any H3 response.
11+
12+
The intended outcome is that the **client times out** waiting for the server's
13+
reply and receives `LWS_CALLBACK_CLIENT_CONNECTION_ERROR` ("Timed out waiting
14+
server reply") within the context timeout — rather than hanging on the live
15+
QUIC connection indefinitely, which was the historical gap this example pins
16+
down.
17+
18+
When run under ctest, the blackhole server is started with `-s` on a free port
19+
as a background fixture, and the client is then run against it. The client
20+
returns `0` if it timed out as expected, and non-zero otherwise.
21+
22+
## Build
23+
24+
```bash
25+
$ cmake . && make
26+
```
27+
28+
## Usage (manual)
29+
30+
In one terminal, start the blackhole server:
31+
32+
```bash
33+
$ ./lws-minimal-http-client-timeout-h3 -s -p 7681 -d 1039
34+
```
35+
36+
In another, run the client against it:
37+
38+
```bash
39+
$ ./lws-minimal-http-client-timeout-h3 -p 7681 -d 1039
40+
```
41+
42+
The client should report `Completed: OK (timed out as expected)` after the
43+
short reply timeout configured via `info.timeout_secs`.

0 commit comments

Comments
 (0)