Skip to content

Commit f2b1265

Browse files
authored
Harden server timeout and shutdown tests (#1348)
* test(server): accept idle timeout response The server may write a complete 408 response before transport shutdown wins the race. Validate that optional response instead of requiring an empty read. * test(websocket): cover TLS server shutdown Run the active-session shutdown test over both TCP and TLS. The blocked handler keeps each connection tracked until forceclose inspects it, which removes timing-dependent project coverage.
1 parent d18d3b4 commit f2b1265

2 files changed

Lines changed: 57 additions & 37 deletions

File tree

test/http_server_http1_tests.jl

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -977,7 +977,18 @@ end
977977
response = HT._read_response(HT._ConnReader(sock), request)
978978
@test response.status == 200
979979
@test String(_read_all_server_bytes(response.body)) == "ok"
980-
@test isempty(read(sock))
980+
# The timeout response is best-effort. Depending on whether its write
981+
# wins the race with transport shutdown, the peer observes either a
982+
# clean EOF or a complete 408 response followed by EOF.
983+
trailing = read(sock)
984+
if !isempty(trailing)
985+
timeout_io = IOBuffer(trailing)
986+
timeout_response = HT._read_response(timeout_io)
987+
@test timeout_response.status == 408
988+
@test timeout_response.close
989+
@test timeout_response.content_length == 0
990+
@test eof(timeout_io)
991+
end
981992
finally
982993
HT.@try_ignore begin
983994
NC.close(sock)

test/http_websocket_server_tests.jl

Lines changed: 45 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -317,45 +317,54 @@ end
317317
end
318318

319319
@testset "HTTP.WebSockets server close notifies active sessions" begin
320-
started = Channel{Nothing}(1)
321-
finished = Channel{Nothing}(1)
322-
release_handler = Channel{Nothing}(1)
323-
server = W.listen!("127.0.0.1", 0) do ws
324-
put!(started, nothing)
320+
for secure in (false, true)
321+
started = Channel{Nothing}(1)
322+
finished = Channel{Nothing}(1)
323+
release_handler = Channel{Nothing}(1)
324+
tls_config = secure ?
325+
TL.Config(verify_peer = false, cert_file = _TLS_CERT_PATH, key_file = _TLS_KEY_PATH) :
326+
nothing
327+
server = W.listen!("127.0.0.1", 0; tls_config = tls_config) do ws
328+
put!(started, nothing)
329+
try
330+
while true
331+
W.receive(ws)
332+
end
333+
catch
334+
finally
335+
put!(finished, nothing)
336+
take!(release_handler)
337+
end
338+
end
339+
ws = nothing
340+
forceclose_task = nothing
325341
try
326-
while true
327-
W.receive(ws)
342+
address = W.server_addr(server)
343+
scheme = secure ? "wss" : "ws"
344+
ws = W.open(
345+
"$scheme://$address/shutdown";
346+
require_ssl_verification = false,
347+
)
348+
take!(started)
349+
forceclose_task = errormonitor(Threads.@spawn W.forceclose(server))
350+
take!(finished)
351+
fetch(forceclose_task::Task)
352+
put!(release_handler, nothing)
353+
wait(server)
354+
err = try
355+
W.receive(ws::W.WebSocket)
356+
nothing
357+
catch err
358+
err
328359
end
329-
catch
360+
@test err isa W.WebSocketError
361+
@test (err::W.WebSocketError).message.code == 1001
330362
finally
331-
put!(finished, nothing)
332-
take!(release_handler)
363+
isready(release_handler) || HTTP.@try_ignore put!(release_handler, nothing)
364+
forceclose_task === nothing || HTTP.@try_ignore fetch(forceclose_task::Task)
365+
ws === nothing || HTTP.@try_ignore close(ws::W.WebSocket)
366+
HTTP.@try_ignore W.forceclose(server)
367+
HTTP.@try_ignore wait(server)
333368
end
334369
end
335-
ws = nothing
336-
forceclose_task = nothing
337-
try
338-
address = W.server_addr(server)
339-
ws = W.open("ws://$address/shutdown")
340-
take!(started)
341-
forceclose_task = errormonitor(Threads.@spawn W.forceclose(server))
342-
take!(finished)
343-
fetch(forceclose_task::Task)
344-
put!(release_handler, nothing)
345-
wait(server)
346-
err = try
347-
W.receive(ws::W.WebSocket)
348-
nothing
349-
catch err
350-
err
351-
end
352-
@test err isa W.WebSocketError
353-
@test (err::W.WebSocketError).message.code == 1001
354-
finally
355-
isready(release_handler) || HTTP.@try_ignore put!(release_handler, nothing)
356-
forceclose_task === nothing || HTTP.@try_ignore fetch(forceclose_task::Task)
357-
ws === nothing || HTTP.@try_ignore close(ws::W.WebSocket)
358-
HTTP.@try_ignore W.forceclose(server)
359-
HTTP.@try_ignore wait(server)
360-
end
361370
end

0 commit comments

Comments
 (0)