aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.adoc7
-rw-r--r--lib/http/sse_conn.go11
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
}