Skip to content

internal/pipeproxy: named pipe to engine transport - #18

Merged
zcsizmadia merged 1 commit into
mainfrom
pipeproxy-transport
Sep 1, 2026
Merged

zcsizmadia merged 1 commit into
mainfrom
pipeproxy-transport

Conversation

@zcsizmadia

Copy link
Copy Markdown
Collaborator

Toward #6 — the transport layer of the load-bearing component. HTTP-level bind-path rewriting (using the winpath package from #17) layers on top via OnAccept in a follow-up PR; keeping them separate because their tests are entirely different in character, and this half is what everything else depends on.

Deliberately HTTP-oblivious. Byte-for-byte fidelity is what makes hijacked connections (exec -it, attach) work at all, so nothing here parses a request.

  • Relay — bidirectional copy with half-close propagated in both directions, so a client can send a body, half-close, then read the response. That''s the docker build / docker save shape, and the thing naive relays truncate.
  • Server.Serve — concurrent connections, graceful drain, and shutdown that closes live connections instead of waiting on them.
  • Listen (Windows) — go-winio pipe with an explicit SDDL: SYSTEM, administrators, interactive users. Explicit because a service running as SYSTEM would otherwise own the pipe and lock the logged-in user out. Byte mode, not message mode — message framing truncates large payloads, which is how image pulls break. TODO(#8) marks where the local Hawser Users group SID replaces IU.
  • WSLDialer — one wsl.exe+socat per connection, the v0.1 design Spike A validated. Closing stdin is a genuine half-close, and Close reaps the child so a busy client can''t leave zombie wsl.exe processes behind.

A real bug surfaced while writing the real-pipe test. Serve waited for in-flight connections to finish — so any streaming client (docker logs -f, docker events) would have pinned service shutdown forever. The test hung for 5s and failed. Serve now tracks live connections and closes them on cancellation; that test is kept as TestServeShutsDownWithStreamingClient and now passes in 0.01s. Worth calling out because it would have shown up as "stopping the Hawser service hangs" long after the fact.

14 tests, 70% coverage. The Dialer seam verifies transport behavior with no WSL2 (half-close, streaming, 256 KB binary round-trip, 25 concurrent connections, dial failure), while the Windows tests drive a real named pipe through go-winio — the same transport stock docker.exe uses.

🤖 Generated with Claude Code

The load-bearing component, transport layer only. Byte-for-byte fidelity
is what makes hijacked connections work, so this layer knows nothing about
HTTP; bind-path rewriting layers on via OnAccept in a follow-up.

- Relay: bidirectional copy with half-close propagation in both
  directions, so a client can send a body, half-close, and still read the
  response (the docker build / docker save shape)
- Server.Serve: concurrent connections, graceful drain, and shutdown that
  closes live connections rather than waiting on them
- Listen (windows): go-winio pipe with an explicit SDDL - SYSTEM,
  administrators, interactive users. Byte mode, not message mode: message
  framing truncates large payloads, which is how image pulls break.
- WSLDialer: one wsl.exe+socat per connection, the v0.1 design Spike A
  validated; closing stdin is a true half-close, and Close reaps the child
  so a busy client cannot leave zombie processes

A shutdown bug surfaced while writing the real-pipe test: Serve waited
for in-flight connections, so any streaming client (docker logs -f) would
have pinned service shutdown forever. Serve now tracks live connections
and closes them on cancellation; the test that caught it went from a 5s
hang to 0.01s and is kept as a regression guard.

14 tests, 70% coverage. The Dialer seam means the transport is verified
without WSL2, while the pipe tests exercise the real Windows transport
stock docker.exe uses.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@zcsizmadia
zcsizmadia merged commit 24bd2e5 into main Sep 1, 2026
1 check passed
@zcsizmadia
zcsizmadia deleted the pipeproxy-transport branch September 1, 2026 13:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant