$ the-wire · showcase
HTTP/1 body deadlock fix, gopls fillstruct stack overflow, and importer fork removal
By RepoJournal · Filed · About Go · Composed from the cited sources · methodology
Go's net/http team fixed an HTTP/1 deadlock that occurs when a response body Close races a concurrent Read to EOF, while gopls fixed a stack overflow on recursive pointer types and the compiler dropped a fork of the type importer.
The net/http deadlock surfaced after automatic response body draining landed: closing a response body early while a concurrent read reaches EOF makes both calls wait on the same eofc channel, but the persistConn readLoop sends only one value [1]. Separately, Brad Fitzpatrick fixed two bugs introduced by CL 828504, which had stopped pinning bufio buffers on idle HTTP/1 keep-alive connections but broke keep-alive over net.Conn implementations whose deadline errors do not wrap os.ErrDeadlineExceeded, such as gVisor's gonet used by Tailscale's netstack [2].
In gopls, fillstruct no longer recurses on a pointer's element type when populating pointer fields. That recursion caused stack overflow on recursive pointer types such as `type P *P`, and for non-composite element types like `**int` or channels it emitted invalid Go such as `&new(int)`; the fix generates `&T{...}` literals only when the pointer element type is a struct, array, slice, or map [3]. gopls also updated its mvdan/gofumpt dependency [4], anchored hover doc links for package definitions and references [5], and began building a symbol reference graph in the MoveDeclaration path, recording top-level symbols and reference edges in two passes [6].
In golang/go and golang/tools, Alan Donovan removed the fork of the importer in cmd/compile/internal/testimporter, deleting the ureader and calling importer.ReadPackage directly [7], and restricted gcexportdata's transitive dependencies ahead of its planned vendoring into std for go/importer by breaking the dependency on typesinternal [8]. Austin Clements converged specgen's GOROOT finding logic with gentools', which the commit message describes as "robust to a broken Go tree" [9]. Brad Fitzpatrick also converted Framer.getReadBuf from a function field to a plain method, removing a per-Framer closure allocation with no behavior change [10].
golang/net dropped http2 benchmarks that no longer exist on go1.27+ and panic on go1.26 due to calling synctest.Wait outside a bubble [11]. golang/telemetry regenerated its generated upload config via go generate ./internal/configgen [12].
Action items
- → Audit HTTP/1 response body handling for concurrent Read and Close on the same body if you rely on automatic draining golang/go [plan]
- → Test keep-alive over net.Conn implementations with custom deadline errors (e.g. gVisor's gonet) against the CL 828504 fix golang/go [plan]
- → Re-run gopls fillstruct on code with recursive pointer types such as `type P *P` after updating golang/tools [monitor]
References
- [1] net/http: fix HTTP/1 deadlock on concurrent response body Read and Close ↗ golang/go
- [2] net/http: fix two bugs from CL 828504 ↗ golang/go
- [3] gopls/internal/analysis/fillstruct: fix stack overflow ↗ golang/tools
- [4] gopls: update mvdan/gofumpt dependency ↗ golang/tools
- [5] gopls/internal/golang: Add doc link support for pkg doc comment ↗ golang/tools
- [6] gopls/internal/golang: MoveDeclaration - build symbol reference graph ↗ golang/tools
- [7] cmd/compile/internal/testimporter: remove fork of importer ↗ golang/go
- [8] go/gcexportdata: restrict transitive dependencies ↗ golang/tools
- [9] simd/archsimd/_gen/specgen: use gentools GOROOT logic ↗ golang/go
- [10] net/http/internal/http2: turn Framer.getReadBuf func field into a method ↗ golang/go
- [11] http2: drop benchmarks ↗ golang/net
- [12] config: regenerate upload config ↗ golang/telemetry