OneWayWebSocketEventSender establishes a new WebSocket connection that enforces one-way communication from the server to the client. The function returned allows you to send a single message to the client, while the channel lets you listen for when the connection closes. We must use an approach li
(log slog.Logger)
| 420 | // WebSockets have no such limitation, no matter what HTTP protocol was used to |
| 421 | // establish the connection. |
| 422 | func OneWayWebSocketEventSender(log slog.Logger) func(rw http.ResponseWriter, r *http.Request) ( |
| 423 | func(event codersdk.ServerSentEvent) error, |
| 424 | <-chan struct{}, |
| 425 | error, |
| 426 | ) { |
| 427 | return func(rw http.ResponseWriter, r *http.Request) ( |
| 428 | func(event codersdk.ServerSentEvent) error, |
| 429 | <-chan struct{}, |
| 430 | error, |
| 431 | ) { |
| 432 | ctx, cancel := context.WithCancel(r.Context()) |
| 433 | r = r.WithContext(ctx) |
| 434 | socket, err := websocket.Accept(rw, r, nil) |
| 435 | if err != nil { |
| 436 | cancel() |
| 437 | return nil, nil, xerrors.Errorf("cannot establish connection: %w", err) |
| 438 | } |
| 439 | go HeartbeatClose(ctx, log, cancel, socket) |
| 440 | |
| 441 | eventC := make(chan codersdk.ServerSentEvent, 64) |
| 442 | socketErrC := make(chan websocket.CloseError, 1) |
| 443 | closed := make(chan struct{}) |
| 444 | go func() { |
| 445 | defer cancel() |
| 446 | defer close(closed) |
| 447 | |
| 448 | for { |
| 449 | select { |
| 450 | case event := <-eventC: |
| 451 | writeCtx, cancel := context.WithTimeout(ctx, 10*time.Second) |
| 452 | err := wsjson.Write(writeCtx, socket, event) |
| 453 | cancel() |
| 454 | if err == nil { |
| 455 | continue |
| 456 | } |
| 457 | _ = socket.Close(websocket.StatusInternalError, "Unable to send newest message") |
| 458 | case err := <-socketErrC: |
| 459 | _ = socket.Close(err.Code, err.Reason) |
| 460 | case <-ctx.Done(): |
| 461 | _ = socket.Close(websocket.StatusNormalClosure, "Connection closed") |
| 462 | } |
| 463 | return |
| 464 | } |
| 465 | }() |
| 466 | |
| 467 | // We have some tools in the UI code to help enforce one-way WebSocket |
| 468 | // connections, but there's still the possibility that the client could send |
| 469 | // a message when it's not supposed to. If that happens, the client likely |
| 470 | // forgot to use those tools, and communication probably can't be trusted. |
| 471 | // Better to just close the socket and force the UI to fix its mess |
| 472 | go func() { |
| 473 | _, _, err := socket.Read(ctx) |
| 474 | if errors.Is(err, context.Canceled) { |
| 475 | return |
| 476 | } |
| 477 | if err != nil { |
| 478 | socketErrC <- websocket.CloseError{ |
| 479 | Code: websocket.StatusInternalError, |