Hi, @zach,
I have a websocket server protected by pritunl-zero. The websocket server will send out a message as soon as a connection is established. It seems pritunl-zero has chance to lose the first message.
The issue can be easily reproduced with this example websocket server implementation.
As you can see the server sends server says: hello! you're connected
after connection is established.
By clicking the “open” button then “close”, we can check the message received by client. If everything works fine, we should expect the “hello! you’re connected” message is received everytime after clicking “open” button. If we repeat the test multiple times, we can observe the initial message is lost.
After checking the pritunl-zero source code, I think there is a race condition. After we created the backConn, and before we start to copy the bytes received from backConn to frontConn, the data sent by backConn should be buffered by the websocket.Conn, and with current implementation the data won’t be forwarded to frontConn.
I have another piece of golang code to simulate pritunl-zero. By changing the forwarding logic from
_, _ = io.Copy(w.front.UnderlyingConn(), w.back.UnderlyingConn())
to
for {
msgType, msg, err := w.back.ReadMessage()
if err != nil {
m := websocket.FormatCloseMessage(websocket.CloseNormalClosure, fmt.Sprintf("%v", err))
if e, ok := err.(*websocket.CloseError); ok {
if e.Code != websocket.CloseNoStatusReceived {
m = websocket.FormatCloseMessage(e.Code, e.Text)
}
}
slog.Error("WebSocket back read error", slog.Any("error", err))
_ = w.front.WriteMessage(websocket.CloseMessage, m)
break
}
err = w.front.WriteMessage(msgType, msg)
if err != nil {
slog.Error("WebSocket front write error", slog.Any("error", err))
break
}
}
the issue is fixed.
Could you take a look? If it makes sense, could you provide a fix?