Skip to content

Commit b6c88d9

Browse files
committed
Add HTTP 1252 loopback diagnostics
1 parent 6fc4295 commit b6c88d9

3 files changed

Lines changed: 229 additions & 6 deletions

File tree

.github/workflows/http-1252-haswell-probe.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@ name: HTTP 1252 haswell precompile probe
22

33
on:
44
workflow_dispatch:
5-
push:
6-
branches:
7-
- codex/http-1252-precompile-probe
85

96
jobs:
107
precompile:
Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
name: HTTP 1252 loopback probe
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
branches:
7+
- codex/http-1252-precompile-probe
8+
9+
jobs:
10+
loopback:
11+
name: Loopback / Julia ${{ matrix.julia-version }} / ${{ matrix.os }}
12+
runs-on: ${{ matrix.os }}
13+
timeout-minutes: 15
14+
strategy:
15+
fail-fast: false
16+
matrix:
17+
os:
18+
- ubuntu-22.04
19+
- ubuntu-24.04
20+
- windows-latest
21+
julia-version:
22+
- '1.10.11'
23+
- '1.11.9'
24+
- '1.12.6'
25+
26+
env:
27+
JULIA_NUM_THREADS: "1"
28+
JULIA_PKG_PRECOMPILE_AUTO: "0"
29+
30+
steps:
31+
- uses: actions/checkout@v4
32+
33+
- uses: julia-actions/setup-julia@v2
34+
with:
35+
version: ${{ matrix.julia-version }}
36+
37+
- name: Run same-process loopback probes
38+
shell: bash
39+
run: |
40+
set -euxo pipefail
41+
mkdir repro
42+
cd repro
43+
cat > loopback_probe.jl <<'JL'
44+
using Pkg
45+
using InteractiveUtils
46+
47+
Pkg.activate(".")
48+
Pkg.add([
49+
Pkg.PackageSpec(name="HTTP", version="2.0.0"),
50+
Pkg.PackageSpec(name="Reseau", version="1.1.4"),
51+
Pkg.PackageSpec(name="Preferences"),
52+
])
53+
54+
using Preferences
55+
set_preferences!("HTTP", "precompile_workload" => false; force=true)
56+
Pkg.precompile()
57+
58+
using HTTP
59+
using Reseau
60+
using Reseau: TCP
61+
using Reseau.IOPoll
62+
63+
versioninfo()
64+
println("Sys.CPU_NAME=", Sys.CPU_NAME)
65+
println("Threads.nthreads()=", Threads.nthreads())
66+
Pkg.status(["HTTP", "Reseau"])
67+
68+
deadline_ns(seconds) = Int64(time_ns()) + Int64(round(Int, seconds * 1.0e9))
69+
70+
function wait_task_done!(task::Task, label::AbstractString, seconds::Real)
71+
status = Base.timedwait(() -> istaskdone(task), seconds; pollint=0.01)
72+
println(label, " task wait status=", status, " task=", task)
73+
status == :ok || error(label * " task did not finish")
74+
wait(task)
75+
return nothing
76+
end
77+
78+
function raw_tcp_probe()
79+
println("BEGIN raw_tcp_probe")
80+
listener = TCP.listen("tcp", "127.0.0.1:0")
81+
TCP.set_deadline!(listener, deadline_ns(5))
82+
addr = string(TCP.addr(listener))
83+
println("raw listener addr=", addr)
84+
server_task = Threads.@spawn begin
85+
conn = TCP.accept(listener)
86+
try
87+
TCP.set_deadline!(conn, deadline_ns(5))
88+
buf = Vector{UInt8}(undef, 4)
89+
n = readbytes!(conn, buf, 4)
90+
println("raw server read n=", n, " data=", String(buf[1:n]))
91+
write(conn, collect(codeunits("pong")))
92+
finally
93+
try
94+
close(conn)
95+
catch err
96+
println("raw server close error=", repr(err))
97+
end
98+
end
99+
end
100+
client = TCP.connect("tcp", addr; timeout_ns=Int64(2_000_000_000))
101+
try
102+
TCP.set_deadline!(client, deadline_ns(5))
103+
write(client, collect(codeunits("ping")))
104+
buf = Vector{UInt8}(undef, 4)
105+
n = readbytes!(client, buf, 4)
106+
println("raw client read n=", n, " data=", String(buf[1:n]))
107+
@assert String(buf[1:n]) == "pong"
108+
finally
109+
try
110+
close(client)
111+
catch err
112+
println("raw client close error=", repr(err))
113+
end
114+
try
115+
close(listener)
116+
catch err
117+
println("raw listener close error=", repr(err))
118+
end
119+
end
120+
wait_task_done!(server_task, "raw server", 6)
121+
println("END raw_tcp_probe")
122+
return nothing
123+
end
124+
125+
function close_http_server!(server, label)
126+
close_task = Threads.@spawn HTTP.forceclose(server)
127+
status = Base.timedwait(() -> istaskdone(close_task), 6.0; pollint=0.01)
128+
println(label, " forceclose status=", status, " task=", close_task)
129+
status == :ok || error(label * " forceclose did not finish")
130+
wait(close_task)
131+
return nothing
132+
end
133+
134+
function http_probe(label::AbstractString; protocol::Symbol=:auto, pause::Bool=false)
135+
println("BEGIN http_probe label=", label, " protocol=", protocol, " pause=", pause)
136+
hits = Threads.Atomic{Int}(0)
137+
server = HTTP.serve!("127.0.0.1", 0; listenany=true) do req
138+
Threads.atomic_add!(hits, 1)
139+
println("handler label=", label, " target=", req.target, " task=", current_task())
140+
return HTTP.Response(200, "pong")
141+
end
142+
try
143+
addr = HTTP.server_addr(server)
144+
println("http server label=", label, " addr=", addr, " serve_task=", server.serve_task,
145+
" done=", istaskdone(server.serve_task))
146+
pause && sleep(0.05)
147+
t0 = time()
148+
response = HTTP.request(
149+
"GET",
150+
"http://$(addr)/ping";
151+
connect_timeout=2.0,
152+
request_timeout=4.0,
153+
response_header_timeout=4.0,
154+
read_idle_timeout=4.0,
155+
retry=false,
156+
protocol=protocol,
157+
)
158+
elapsed = time() - t0
159+
body = String(response.body)
160+
println("http response label=", label, " elapsed=", elapsed, " status=", response.status,
161+
" body=", body, " hits=", hits[])
162+
@assert response.status == 200
163+
@assert body == "pong"
164+
@assert hits[] == 1
165+
catch err
166+
println("http probe failed label=", label, " hits=", hits[],
167+
" serve_task=", server.serve_task,
168+
" done=", istaskdone(server.serve_task),
169+
" failed=", istaskfailed(server.serve_task))
170+
showerror(stdout, err, catch_backtrace())
171+
println()
172+
rethrow()
173+
finally
174+
close_http_server!(server, label)
175+
end
176+
println("END http_probe label=", label)
177+
return nothing
178+
end
179+
180+
raw_tcp_probe()
181+
http_probe("auto-immediate"; protocol=:auto)
182+
http_probe("h1-immediate"; protocol=:h1)
183+
http_probe("h1-paused"; protocol=:h1, pause=true)
184+
185+
IOPoll.shutdown!()
186+
println("loopback probes complete")
187+
JL
188+
julia --project=. loopback_probe.jl
189+
190+
precompile:
191+
name: Precompile / Julia ${{ matrix.julia-version }} / ${{ matrix.os }}
192+
runs-on: ${{ matrix.os }}
193+
timeout-minutes: 10
194+
strategy:
195+
fail-fast: false
196+
matrix:
197+
os:
198+
- ubuntu-22.04
199+
- windows-latest
200+
julia-version:
201+
- '1.11.9'
202+
- '1.12.6'
203+
204+
env:
205+
JULIA_NUM_THREADS: "1"
206+
JULIA_PKG_PRECOMPILE_AUTO: "0"
207+
208+
steps:
209+
- uses: actions/checkout@v4
210+
211+
- uses: julia-actions/setup-julia@v2
212+
with:
213+
version: ${{ matrix.julia-version }}
214+
215+
- name: Run full HTTP precompile workload
216+
shell: bash
217+
run: |
218+
set -euxo pipefail
219+
mkdir repro
220+
cd repro
221+
julia --project=. -e '
222+
using Pkg
223+
Pkg.add([
224+
Pkg.PackageSpec(name="HTTP", version="2.0.0"),
225+
Pkg.PackageSpec(name="Reseau", version="1.1.4"),
226+
])
227+
Pkg.status(["HTTP", "Reseau"])
228+
'
229+
julia --project=. -e 'using Pkg; Pkg.precompile()'

.github/workflows/http-1252-precompile-probe.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@ name: HTTP 1252 precompile probe
22

33
on:
44
workflow_dispatch:
5-
push:
6-
branches:
7-
- codex/http-1252-precompile-probe
85

96
jobs:
107
precompile:

0 commit comments

Comments
 (0)