aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShulhan <ms@kilabit.info>2026-02-15 12:44:24 +0700
committerShulhan <ms@kilabit.info>2026-02-15 12:45:13 +0700
commit1844b9e48d0f5f7f3e9c8de026181d04d74c92ab (patch)
tree967889384daa96e58fdefd6315e134ab8029a2e7
parentccc9926635c1702337ac6fd1038bd7b903afa7f9 (diff)
downloadpakakeh.go-1844b9e48d0f5f7f3e9c8de026181d04d74c92ab.tar.xz
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.
-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
}