diff options
| -rw-r--r-- | CHANGELOG.adoc | 7 | ||||
| -rw-r--r-- | lib/http/sse_conn.go | 11 |
2 files changed, 16 insertions, 2 deletions
diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 7f41b747..33f515c9 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -77,6 +77,13 @@ then shutting down. This allow de-activating HTTP server when no connections received after specific duration to reduce the system resources. +=== 🌼 lib/http: fix possible data race in SSE connection + +When server's handler call Write or WriteRaw, there is possibility that +the worker for keeping the connection alive also call Write at the same +time, which cause the data race. + + //}}} [#v0_61_0] == pakakeh.go v0.61.0 (2026-02-09) diff --git a/lib/http/sse_conn.go b/lib/http/sse_conn.go index 055a5793..8d2f0b76 100644 --- a/lib/http/sse_conn.go +++ b/lib/http/sse_conn.go @@ -10,6 +10,7 @@ import ( "net" "net/http" "strings" + "sync" "time" ) @@ -28,6 +29,10 @@ type SSEConn struct { bufrw *bufio.ReadWriter conn net.Conn + + // bufrwMtx protects the concurrent write between client and + // workerKeepAlive. + bufrwMtx sync.Mutex } // WriteEvent write message with optional event type and id to client. @@ -61,21 +66,23 @@ func (ep *SSEConn) WriteEvent(event, data string, id *string) (err error) { ep.writeData(&buf, data, id) - _, err = ep.bufrw.Write(buf.Bytes()) + err = ep.WriteRaw(buf.Bytes()) if err != nil { return fmt.Errorf(`WriteEvent: %w`, err) } - ep.bufrw.Flush() return nil } // WriteRaw write raw event message directly, without any parsing. func (ep *SSEConn) WriteRaw(msg []byte) (err error) { + ep.bufrwMtx.Lock() _, err = ep.bufrw.Write(msg) if err != nil { + ep.bufrwMtx.Unlock() return fmt.Errorf(`WriteRaw: %w`, err) } ep.bufrw.Flush() + ep.bufrwMtx.Unlock() return nil } |
