Add lightweight write deadlines to Conn

Open
#572 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
5/5
Estimated time
Over a week
Newbie friendliness
45/100
Issue type
Feature
Clarity
Mostly clear
Activity status
Quiet
Tech stack
go

Research direction

Inspect the write path on *websocket.Conn and the deadline handling in websocket.NetConn first, then read related issue #252 for the read-deadline direction. Done means a write-only deadline mechanism that bounds lock acquisition and writes, preserves timeout-close behavior, remains safe with concurrent operations, avoids read-side state, and clears on a zero time.

Written by the indexing model from the issue text.

Description

Problem

Long-lived WebSocket servers often need every outbound write to be bounded. With v1.8.15, the main API requires creating a deadline context for every message:

package main

import (
	"context"
	"log"
	"net/http"
	"time"

	"github.com/coder/websocket"
)

func main() {
	http.HandleFunc("/", serve)
	log.Fatal(http.ListenAndServe(":8080", nil))
}

func serve(w http.ResponseWriter, r *http.Request) {
	c, err := websocket.Accept(w, r, nil)
	if err != nil {
		return
	}
	defer c.CloseNow()

	connCtx := c.CloseRead(r.Context())
	ticker := time.NewTicker(10 * time.Millisecond)
	defer ticker.Stop()

	for {
		select {
		case <-connCtx.Done():
			return
		case <-ticker.C:
			ctx, cancel := context.WithTimeout(context.Background(), time.Second)
			err := c.Write(ctx, websocket.MessageText, []byte(`{"type":"event"}`))
			cancel()
			if err != nil {
				return
			}
		}
	}
}

Using context.Background() avoids the timeout-related allocations added around each frame, but also removes the bounded-write guarantee.

websocket.NetConn exposes SetWriteDeadline, but it is a full bidirectional adapter. It creates read and write contexts, timers, and locks, and calls SetReadLimit(-1) even when only its write side is needed.

A loopback benchmark with v1.8.15 on Go 1.27rc2 produced:

Strategy Write path Connection construction
context.WithTimeout 1304 B/op, 12 allocs/op no extra adapter
NetConn.SetWriteDeadline 664 B/op, 5 allocs/op 848 B, 13 allocs

The write measurements use the same client reader, so the allocation difference is the relevant result. At high connection counts, the fixed NetConn cost is material.

Request

Could *websocket.Conn provide a lightweight write-only deadline mechanism, for example SetWriteDeadline(time.Time) or an equivalent API?

Ideally it would:

  • reuse connection-owned deadline state instead of allocating a timer context per write;
  • bound both write-lock acquisition and the underlying write;
  • preserve the current behavior where a timed-out write closes the WebSocket;
  • remain safe with concurrent Ping, Close, and data writes;
  • avoid creating read-side state or changing the configured read limit;
  • allow zero time to clear the deadline.

Related: #252 discusses the same API direction for read deadlines.

Dominant language
Go
Stars
5.5k
Forks
377
PR merge metrics
No merged PRs in 30d

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from coder/websocket

All issues in coder/websocket

Similar issues

More Go issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.