You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Tinygo crashes on http.Client{}.Do() with Timeout #58
It seems that, at the moment, there is no way to cancel an in-flight request. Any http.Get or http.Post to an unreachable destination blocks indefinitely, effectively deadlocking the entire process and requiring a manual reset.
Could you please share your solution? I’m curious to know how you approached it.
ofc, it's nothing too amazing, i just grabbed Go net/http/client.go code and removed some part like http2 and legacy CancelRequest (Go 1.6)
//net/http/client.go// setRequestCancel sets req.Cancel and adds a deadline context to req// if deadline is non-zero. The RoundTripper's type is used to// determine whether the legacy CancelRequest behavior should be used.//// As background, there are three ways to cancel a request:// First was Transport.CancelRequest. (deprecated)// Second was Request.Cancel.// Third was Request.Context.// This function populates the second and third, and uses the first if it really needs to. TINYGO: Removed the third option as we dont support Go 1.6funcsetRequestCancel(req*Request, rtRoundTripper, deadline time.Time) (stopTimerfunc(), didTimeoutfunc() bool) {
ifdeadline.IsZero() {
returnnop, alwaysFalse
}
knownTransport:=knownRoundTripperImpl(rt, req) // switch that check if rt.(type) is *TransportoldCtx:=req.Context()
ifreq.Cancel==nil&&knownTransport {
// If they already had a Request.Context that's// expiring sooner, do nothing:if!timeBeforeContextDeadline(deadline, oldCtx) {
returnnop, alwaysFalse
}
varcancelCtxfunc()
req.ctx, cancelCtx=context.WithDeadline(oldCtx, deadline)
returncancelCtx, func() bool { returntime.Now().After(deadline) }
}
initialReqCancel:=req.Cancel// the user's original Request.Cancel, if anyvarcancelCtxfunc()
iftimeBeforeContextDeadline(deadline, oldCtx) { // timeBeforeContextDeadline checks if ctx.Deadline() is before deadlinereq.ctx, cancelCtx=context.WithDeadline(oldCtx, deadline)
}
cancel:=make(chanstruct{})
req.Cancel=canceldoCancel:=func() {
// The second way in the func comment above:close(cancel)
// TINYGO: Removed logic using CancelRequest (implementation for <= Go 1.6)
}
stopTimerCh:=make(chanstruct{})
stopTimer=sync.OnceFunc(func() {
close(stopTimerCh)
ifcancelCtx!=nil {
cancelCtx()
}
})
timer:=time.NewTimer(time.Until(deadline))
vartimedOut atomic.Boolgofunc() {
select {
case<-initialReqCancel:
doCancel()
timer.Stop()
case<-timer.C:
timedOut.Store(true)
doCancel()
case<-stopTimerCh:
timer.Stop()
}
}()
returnstopTimer, timedOut.Load
}
then on roundtrip i added this before writing to the connection
This is fixed on main by 70037cf ("net/http: fix nil didTimeout deref when a client dial fails"): send now returns an alwaysFalse func on the error path, so c.do's didTimeout() call no longer dereferences a nil func value when the dial fails with Client.Timeout set.
Relatedly, #83 (open) makes the nil-Transport client dispatch through DefaultTransport / the fetch API on js/wasm instead of always dialing through the netdev — the other half of what your POC runs into (#66). With main as of 70037cf your reproducer should report the dial error instead of panicking; worth a re-test and closing if it checks out.
Activity
Nico-Mayer commentedon Jun 27, 2026
It seems that, at the moment, there is no way to cancel an in-flight request. Any
http.Getorhttp.Postto an unreachable destination blocks indefinitely, effectively deadlocking the entire process and requiring a manual reset.Darckfast commentedon Jun 30, 2026
yeah, i gave a shot porting Go's timeout solution and it seems to work fine, might bring this here
Nico-Mayer commentedon Jun 30, 2026
Could you please share your solution? I’m curious to know how you approached it.
Darckfast commentedon Jun 30, 2026
ofc, it's nothing too amazing, i just grabbed Go
net/http/client.gocode and removed some part like http2 and legacy CancelRequest (Go 1.6)then on roundtrip i added this before writing to the connection
the
setRequestCancel()get called a line before calling theroundTrip()in thesend()functioni still need to test with tinygo own roundTrip(), but for JS roundTrip it works fine
there is more code, like the error types, extra helper functions on transport and client, but the part above is the gist of it
feat: port Go net/http timeout handling
0pcom commentedon Sep 5, 2026
This is fixed on main by 70037cf ("net/http: fix nil didTimeout deref when a client dial fails"):
sendnow returns analwaysFalsefunc on the error path, soc.do'sdidTimeout()call no longer dereferences a nil func value when the dial fails withClient.Timeoutset.Relatedly, #83 (open) makes the nil-Transport client dispatch through
DefaultTransport/ the fetch API on js/wasm instead of always dialing through the netdev — the other half of what your POC runs into (#66). With main as of 70037cf your reproducer should report the dial error instead of panicking; worth a re-test and closing if it checks out.