mirror of
https://github.com/coder/coder.git
synced 2026-06-04 05:28:20 +00:00
148a5a3593
fixes #14881 Our handlers for streaming logs don't read from the websocket. We don't allow the client to send us any data, but the websocket library we use requires reading from the websocket to properly handle pings and closing. Not doing so can [can cause the websocket to hang on write](https://github.com/coder/websocket/issues/405), leaking go routines which were noticed in #14881. This fixes the issue, and in process refactors our log streaming to a encoder/decoder package which provides generic types for sending JSON over websocket. I'd also like for us to upgrade to the latest https://github.com/coder/websocket but we should also upgrade our tailscale fork before doing so to avoid including two copies of the websocket library.
43 lines
1.1 KiB
Go
43 lines
1.1 KiB
Go
package wsjson
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
|
|
"golang.org/x/xerrors"
|
|
"nhooyr.io/websocket"
|
|
)
|
|
|
|
type Encoder[T any] struct {
|
|
conn *websocket.Conn
|
|
typ websocket.MessageType
|
|
}
|
|
|
|
func (e *Encoder[T]) Encode(v T) error {
|
|
w, err := e.conn.Writer(context.Background(), e.typ)
|
|
if err != nil {
|
|
return xerrors.Errorf("get websocket writer: %w", err)
|
|
}
|
|
defer w.Close()
|
|
j := json.NewEncoder(w)
|
|
err = j.Encode(v)
|
|
if err != nil {
|
|
return xerrors.Errorf("encode json: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (e *Encoder[T]) Close(c websocket.StatusCode) error {
|
|
return e.conn.Close(c, "")
|
|
}
|
|
|
|
// NewEncoder creates a JSON-over websocket encoder for the type T, which must be JSON-serializable.
|
|
// You may then call Encode() to send objects over the websocket. Creating an Encoder closes the
|
|
// websocket for reading, turning it into a unidirectional write stream of JSON-encoded objects.
|
|
func NewEncoder[T any](conn *websocket.Conn, typ websocket.MessageType) *Encoder[T] {
|
|
// Here we close the websocket for reading, so that the websocket library will handle pings and
|
|
// close frames.
|
|
_ = conn.CloseRead(context.Background())
|
|
return &Encoder[T]{conn: conn, typ: typ}
|
|
}
|