Skip to content

Commit 7d7f485

Browse files
committed
fix: un-jank the OWWS implementation
1 parent 31cdb9f commit 7d7f485

File tree

1 file changed

+30
-32
lines changed

1 file changed

+30
-32
lines changed

coderd/httpapi/httpapi.go

Lines changed: 30 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/coder/coder/v2/coderd/tracing"
2121
"github.com/coder/coder/v2/codersdk"
2222
"github.com/coder/websocket"
23+
"github.com/coder/websocket/wsjson"
2324
)
2425

2526
var Validate *validator.Validate
@@ -285,7 +286,7 @@ type WebSocketEvent[T any] struct {
285286
}
286287

287288
func OneWayWebSocket[T any](rw http.ResponseWriter, r *http.Request) (
288-
sendEvent func(ctx context.Context, wsEvent T) error,
289+
sendEvent func(wsEvent T) error,
289290
closed chan struct{},
290291
err error,
291292
) {
@@ -302,17 +303,29 @@ func OneWayWebSocket[T any](rw http.ResponseWriter, r *http.Request) (
302303
Code websocket.StatusCode
303304
Reason string
304305
}
306+
307+
eventC := make(chan T)
305308
socketErrC := make(chan SocketError, 1)
306-
closed = make(chan struct{}, 1)
309+
closed = make(chan struct{})
307310
go func() {
308-
select {
309-
case err := <-socketErrC:
310-
_ = socket.Close(err.Code, err.Reason)
311-
case <-ctx.Done():
312-
_ = socket.Close(websocket.StatusNormalClosure, "Connection closed")
311+
defer cancel()
312+
defer close(closed)
313+
314+
for {
315+
select {
316+
case event := <-eventC:
317+
err := wsjson.Write(ctx, socket, event)
318+
if err == nil {
319+
continue
320+
}
321+
_ = socket.Close(websocket.StatusInternalError, "Unable to send newest message")
322+
case err := <-socketErrC:
323+
_ = socket.Close(err.Code, err.Reason)
324+
case <-ctx.Done():
325+
_ = socket.Close(websocket.StatusNormalClosure, "Connection closed")
326+
}
327+
return
313328
}
314-
cancel()
315-
close(closed)
316329
}()
317330

318331
// We have some tools in the UI code to help enforce one-way WebSocket
@@ -321,7 +334,7 @@ func OneWayWebSocket[T any](rw http.ResponseWriter, r *http.Request) (
321334
// forgot to use those tools, and communication probably can't be trusted.
322335
// Better to just close the socket and force the UI to fix its mess
323336
go func() {
324-
msgType, _, err := socket.Read(ctx)
337+
_, _, err := socket.Read(ctx)
325338
if errors.Is(err, context.Canceled) {
326339
return
327340
}
@@ -332,33 +345,18 @@ func OneWayWebSocket[T any](rw http.ResponseWriter, r *http.Request) (
332345
}
333346
return
334347
}
335-
switch msgType {
336-
case websocket.MessageBinary, websocket.MessageText:
337-
socketErrC <- SocketError{
338-
Code: websocket.StatusProtocolError,
339-
Reason: "Clients cannot send messages for one-way WebSockets",
340-
}
348+
socketErrC <- SocketError{
349+
Code: websocket.StatusProtocolError,
350+
Reason: "Clients cannot send messages for one-way WebSockets",
341351
}
342352
}()
343353

344-
sendEvent = func(context.Context, T) error {
345-
// Using multiple selects because we want possible errors to be
346-
// processed deterministically
347-
select {
348-
case _, open := <-ctx.Done():
349-
if !open {
350-
return xerrors.New("connection closed from client")
351-
}
352-
default:
353-
}
354+
sendEvent = func(event T) error {
354355
select {
355-
case _, open := <-closed:
356-
if !open {
357-
return xerrors.New("connection closed internally")
358-
}
359-
default:
356+
case eventC <- event:
357+
case <-ctx.Done():
358+
return ctx.Err()
360359
}
361-
362360
return nil
363361
}
364362

0 commit comments

Comments
 (0)