Ensure all network operations are cancellable, timeout-protected, and executed where the resource lives.
Motivation
Rules 1) Always use context and timeouts for outbound network calls and dials. Use context-aware APIs (DialContext, http.NewRequestWithContext) and set sensible connect/read timeouts. Example (HTTP): ctx, cancel := context.WithTimeout(parentCtx, 10*time.Second) defer cancel() req, _ := http.NewRequestWithContext(ctx, “POST”, url, body) resp, err := http.DefaultClient.Do(req)
Example (websocket dial): d := websocket.Dialer{HandshakeTimeout: 20 * time.Second} conn, _, err := d.DialContext(ctx, wsURL, nil)
2) Make long-running reads cancellable by closing the underlying connection from another goroutine when the context expires. Closing the socket unblocks ReadMessage/Read calls so goroutines don’t leak. Example: go func() { <-ctx.Done() conn.Close() }()
3) Prefer incremental transfers for streaming channels: send only new/changed messages instead of replaying entire history to reduce bandwidth and contention on shared websockets. If a timeout happens, send a clear partial/final packet (e.g., a timeout or summary packet) so the client retains what it already received.
4) Execute resource-heavy or resource-local operations on the side that owns the resource (server-side) when invoked remotely. For example, create zip archives of a server’s config on the server and then copy the resulting artifact to the client (via filestore/WFS or an explicit transfer), rather than trying to reconstruct the server’s environment on the client.
5) Preserve transport/URI semantics when parsing and serializing (e.g., trailing slashes) so network requests behave as expected.
When to revisit
References: discussions 0,1,2,3,4,5
Enter the URL of a public GitHub repository