Skip to content

Conversation

@gmegidish
Copy link
Member

@gmegidish gmegidish commented Jan 25, 2026

Summary by CodeRabbit

  • New Features

    • Added a WebSocket server endpoint providing JSON-RPC 2.0-style bidirectional calls for real-time interactions, with optional CORS support and improved connection stability (ping/pong, keepalive, and message validation).
  • Chores

    • Added a WebSocket client/server dependency to the project.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 25, 2026

Walkthrough

Adds WebSocket support: new github.com/gorilla/websocket dependency, registers a /ws HTTP route, and implements a WebSocket JSON-RPC 2.0 handler with origin/CORS checks, message parsing/validation, method dispatch, and threaded-safe response sending.

Changes

Cohort / File(s) Summary
Dependency Management
go.mod
Added require github.com/gorilla/websocket v1.5.3.
WebSocket Implementation
server/websocket.go
New file implementing NewWebSocketHandler(enableCORS bool) http.HandlerFunc, connection upgrade and origin checks, read/write configuration, ping/pong routine, JSON-RPC 2.0 request validation (including screencapture blocking), method dispatch via the method registry, and thread-safe response/error sending (wsConnection methods).
Routing Configuration
server/server.go
Registered /ws route to NewWebSocketHandler(enableCORS) in server startup.
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: websocket support through /ws' directly and clearly summarizes the main change: adding WebSocket support accessible via the /ws endpoint, which is confirmed by the actual changes (new websocket.go implementation and /ws route registration).

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@server/websocket.go`:
- Around line 36-61: handleWebSocket currently upgrades the HTTP connection but
does not limit inbound frame sizes or idle time; immediately after conn :=
newUpgrader(enableCORS).Upgrade(...) returns, call conn.SetReadLimit with a
sensible maxMessageSize (e.g., 1MB or a configurable constant), set read
deadlines via conn.SetReadDeadline and install a pong handler with
conn.SetPongHandler to extend deadlines on activity; update
newUpgrader/handleWebSocket to use these methods on the returned websocket.Conn
(referencing handleWebSocket, newUpgrader, conn, SetReadLimit, SetReadDeadline,
SetPongHandler, and wsConnection) so oversized frames and idle connections are
bounded.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@server/websocket.go`:
- Around line 108-123: In readMessages, the return value from
wsConnection.sendError is ignored; modify the code to capture the error returned
by sendError (call on wsConn.sendError) and log it (e.g., via utils.Verbose or a
logger) so failures to send error frames are observable; ensure you still
continue the loop after logging so behavior is unchanged but errors are recorded
for wsConnection/sendError failures.
- Around line 194-209: The function handleWSMessage should check and handle the
return values from wsConn.sendError calls (called in the json.Unmarshal error
branch and the validateJSONRPCRequest error branch) instead of ignoring
them—capture the error return and log/handle it appropriately; also avoid
logging potentially sensitive request payloads with utils.Info: change the log
to utils.Verbose (or call a redact function on req.Params before logging) so
JSONRPCRequest params are not emitted at Info level; keep the rest of the flow
(validateJSONRPCRequest, handleWSMethodCall) intact.
- Around line 211-227: The calls to wsConn.sendError and wsConn.sendResponse in
handleWSMethodCall ignore returned errors and static analysis flags these;
update handleWSMethodCall to capture and log any error returned from
wsConn.sendError (both the "Method not found" and server error paths) and from
wsConn.sendResponse after successful execution, e.g., call the methods, check
the error value, and log it with context (mentioning req.Method and req.ID) so
failures in sendError/sendResponse are observable; locate these calls inside
handleWSMethodCall and add error handling/logging around wsConn.sendError and
wsConn.sendResponse.
🧹 Nitpick comments (2)
server/websocket.go (2)

125-142: Consider handling conn.Close() error for completeness.

Static analysis flags the unchecked conn.Close() error on line 133. While ignoring close errors in defers is common practice, logging can help with debugging connection issues.

♻️ Optional fix
-		defer conn.Close()
+		defer func() {
+			if err := conn.Close(); err != nil {
+				utils.Verbose("error closing WebSocket connection: %v", err)
+			}
+		}()

251-255: Consider setting write deadline in sendJSON.

Unlike pingLoop, the sendJSON method doesn't set a write deadline before writing. If the peer becomes unresponsive during a response write, this could block indefinitely.

♻️ Suggested improvement
 func (wsc *wsConnection) sendJSON(v interface{}) error {
 	wsc.writeMu.Lock()
 	defer wsc.writeMu.Unlock()
+	if err := wsc.conn.SetWriteDeadline(time.Now().Add(wsWriteWait)); err != nil {
+		return err
+	}
 	return wsc.conn.WriteJSON(v)
 }

Comment on lines +108 to +123
func readMessages(wsConn *wsConnection) {
for {
messageType, message, err := wsConn.conn.ReadMessage()
if err != nil {
utils.Verbose("WebSocket connection closed: %v", err)
break
}

if messageType != websocket.TextMessage {
wsConn.sendError(nil, ErrCodeInvalidRequest, errTitleInvalidReq, errMsgTextOnly)
continue
}

handleWSMessage(wsConn, message)
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Handle or acknowledge the error return from sendError.

The static analysis flags that sendError return value is unchecked on line 117. While the loop continuing regardless is reasonable behavior, the error should be logged for observability.

🔧 Suggested fix
 		if messageType != websocket.TextMessage {
-			wsConn.sendError(nil, ErrCodeInvalidRequest, errTitleInvalidReq, errMsgTextOnly)
+			if err := wsConn.sendError(nil, ErrCodeInvalidRequest, errTitleInvalidReq, errMsgTextOnly); err != nil {
+				utils.Verbose("failed to send error response: %v", err)
+			}
 			continue
 		}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func readMessages(wsConn *wsConnection) {
for {
messageType, message, err := wsConn.conn.ReadMessage()
if err != nil {
utils.Verbose("WebSocket connection closed: %v", err)
break
}
if messageType != websocket.TextMessage {
wsConn.sendError(nil, ErrCodeInvalidRequest, errTitleInvalidReq, errMsgTextOnly)
continue
}
handleWSMessage(wsConn, message)
}
}
func readMessages(wsConn *wsConnection) {
for {
messageType, message, err := wsConn.conn.ReadMessage()
if err != nil {
utils.Verbose("WebSocket connection closed: %v", err)
break
}
if messageType != websocket.TextMessage {
if err := wsConn.sendError(nil, ErrCodeInvalidRequest, errTitleInvalidReq, errMsgTextOnly); err != nil {
utils.Verbose("failed to send error response: %v", err)
}
continue
}
handleWSMessage(wsConn, message)
}
}
🧰 Tools
🪛 GitHub Check: lint

[failure] 117-117:
Error return value of wsConn.sendError is not checked (errcheck)

🤖 Prompt for AI Agents
In `@server/websocket.go` around lines 108 - 123, In readMessages, the return
value from wsConnection.sendError is ignored; modify the code to capture the
error returned by sendError (call on wsConn.sendError) and log it (e.g., via
utils.Verbose or a logger) so failures to send error frames are observable;
ensure you still continue the loop after logging so behavior is unchanged but
errors are recorded for wsConnection/sendError failures.

Comment on lines +194 to +209
func handleWSMessage(wsConn *wsConnection, message []byte) {
var req JSONRPCRequest
if err := json.Unmarshal(message, &req); err != nil {
wsConn.sendError(nil, ErrCodeParseError, errTitleParseError, errMsgParseError)
return
}

if validationErr := validateJSONRPCRequest(req); validationErr != nil {
wsConn.sendError(req.ID, validationErr.code, validationErr.message, validationErr.data)
return
}

utils.Info("WebSocket Request ID: %v, Method: %s, Params: %s", req.ID, req.Method, string(req.Params))

handleWSMethodCall(wsConn, req)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Address unchecked error returns and potential sensitive data logging.

  1. Static analysis flags unchecked sendError returns on lines 197 and 202.
  2. Line 206 logs req.Params at Info level, which may contain sensitive user data. Consider using Verbose level or redacting params.
🔧 Suggested fix
 func handleWSMessage(wsConn *wsConnection, message []byte) {
 	var req JSONRPCRequest
 	if err := json.Unmarshal(message, &req); err != nil {
-		wsConn.sendError(nil, ErrCodeParseError, errTitleParseError, errMsgParseError)
+		if sendErr := wsConn.sendError(nil, ErrCodeParseError, errTitleParseError, errMsgParseError); sendErr != nil {
+			utils.Verbose("failed to send parse error: %v", sendErr)
+		}
 		return
 	}
 
 	if validationErr := validateJSONRPCRequest(req); validationErr != nil {
-		wsConn.sendError(req.ID, validationErr.code, validationErr.message, validationErr.data)
+		if sendErr := wsConn.sendError(req.ID, validationErr.code, validationErr.message, validationErr.data); sendErr != nil {
+			utils.Verbose("failed to send validation error: %v", sendErr)
+		}
 		return
 	}
 
-	utils.Info("WebSocket Request ID: %v, Method: %s, Params: %s", req.ID, req.Method, string(req.Params))
+	utils.Verbose("WebSocket Request ID: %v, Method: %s, Params: %s", req.ID, req.Method, string(req.Params))
 
 	handleWSMethodCall(wsConn, req)
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func handleWSMessage(wsConn *wsConnection, message []byte) {
var req JSONRPCRequest
if err := json.Unmarshal(message, &req); err != nil {
wsConn.sendError(nil, ErrCodeParseError, errTitleParseError, errMsgParseError)
return
}
if validationErr := validateJSONRPCRequest(req); validationErr != nil {
wsConn.sendError(req.ID, validationErr.code, validationErr.message, validationErr.data)
return
}
utils.Info("WebSocket Request ID: %v, Method: %s, Params: %s", req.ID, req.Method, string(req.Params))
handleWSMethodCall(wsConn, req)
}
func handleWSMessage(wsConn *wsConnection, message []byte) {
var req JSONRPCRequest
if err := json.Unmarshal(message, &req); err != nil {
if sendErr := wsConn.sendError(nil, ErrCodeParseError, errTitleParseError, errMsgParseError); sendErr != nil {
utils.Verbose("failed to send parse error: %v", sendErr)
}
return
}
if validationErr := validateJSONRPCRequest(req); validationErr != nil {
if sendErr := wsConn.sendError(req.ID, validationErr.code, validationErr.message, validationErr.data); sendErr != nil {
utils.Verbose("failed to send validation error: %v", sendErr)
}
return
}
utils.Verbose("WebSocket Request ID: %v, Method: %s, Params: %s", req.ID, req.Method, string(req.Params))
handleWSMethodCall(wsConn, req)
}
🧰 Tools
🪛 GitHub Check: lint

[failure] 202-202:
Error return value of wsConn.sendError is not checked (errcheck)


[failure] 197-197:
Error return value of wsConn.sendError is not checked (errcheck)

🤖 Prompt for AI Agents
In `@server/websocket.go` around lines 194 - 209, The function handleWSMessage
should check and handle the return values from wsConn.sendError calls (called in
the json.Unmarshal error branch and the validateJSONRPCRequest error branch)
instead of ignoring them—capture the error return and log/handle it
appropriately; also avoid logging potentially sensitive request payloads with
utils.Info: change the log to utils.Verbose (or call a redact function on
req.Params before logging) so JSONRPCRequest params are not emitted at Info
level; keep the rest of the flow (validateJSONRPCRequest, handleWSMethodCall)
intact.

Comment on lines +211 to +227
func handleWSMethodCall(wsConn *wsConnection, req JSONRPCRequest) {
registry := GetMethodRegistry()
handler, exists := registry[req.Method]
if !exists {
wsConn.sendError(req.ID, ErrCodeMethodNotFound, "Method not found", req.Method+" not found")
return
}

result, err := handler(req.Params)
if err != nil {
log.Printf("Error executing method %s: %v", req.Method, err)
wsConn.sendError(req.ID, ErrCodeServerError, "Server error", err.Error())
return
}

wsConn.sendResponse(req.ID, result)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Handle error returns from sendError and sendResponse.

Static analysis flags unchecked errors on lines 215, 222, and 226. These should be logged for observability.

🔧 Suggested fix
 func handleWSMethodCall(wsConn *wsConnection, req JSONRPCRequest) {
 	registry := GetMethodRegistry()
 	handler, exists := registry[req.Method]
 	if !exists {
-		wsConn.sendError(req.ID, ErrCodeMethodNotFound, "Method not found", req.Method+" not found")
+		if err := wsConn.sendError(req.ID, ErrCodeMethodNotFound, "Method not found", req.Method+" not found"); err != nil {
+			utils.Verbose("failed to send method not found error: %v", err)
+		}
 		return
 	}
 
 	result, err := handler(req.Params)
 	if err != nil {
 		log.Printf("Error executing method %s: %v", req.Method, err)
-		wsConn.sendError(req.ID, ErrCodeServerError, "Server error", err.Error())
+		if sendErr := wsConn.sendError(req.ID, ErrCodeServerError, "Server error", err.Error()); sendErr != nil {
+			utils.Verbose("failed to send server error: %v", sendErr)
+		}
 		return
 	}
 
-	wsConn.sendResponse(req.ID, result)
+	if err := wsConn.sendResponse(req.ID, result); err != nil {
+		utils.Verbose("failed to send response: %v", err)
+	}
 }
🧰 Tools
🪛 GitHub Check: lint

[failure] 226-226:
Error return value of wsConn.sendResponse is not checked (errcheck)


[failure] 222-222:
Error return value of wsConn.sendError is not checked (errcheck)


[failure] 215-215:
Error return value of wsConn.sendError is not checked (errcheck)

🤖 Prompt for AI Agents
In `@server/websocket.go` around lines 211 - 227, The calls to wsConn.sendError
and wsConn.sendResponse in handleWSMethodCall ignore returned errors and static
analysis flags these; update handleWSMethodCall to capture and log any error
returned from wsConn.sendError (both the "Method not found" and server error
paths) and from wsConn.sendResponse after successful execution, e.g., call the
methods, check the error value, and log it with context (mentioning req.Method
and req.ID) so failures in sendError/sendResponse are observable; locate these
calls inside handleWSMethodCall and add error handling/logging around
wsConn.sendError and wsConn.sendResponse.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants