fix: log connections that timeout, abort, or send unparsable data#783
fix: log connections that timeout, abort, or send unparsable data#783
Conversation
Pull Request Test Coverage Report for Build 19147127865Details
💛 - Coveralls |
src/http/plugins/log-request.ts
Outdated
| async (fastify) => { | ||
| // Watch for connections that timeout or disconnect before complete HTTP headers are received | ||
| // Log if socket closes before request is triggered | ||
| fastify.server.on('connection', (socket) => { |
There was a problem hiding this comment.
we would need to remove these event handlers at the end of the request otherwise we will create memory leaks
There was a problem hiding this comment.
Also we need to consider that we use keep-alive so connections might be reused hence the listeners would be removed in the current implementation
80b05e4 to
d0fa85c
Compare
d0fa85c to
14342a7
Compare
📝 WalkthroughSummary by CodeRabbit
WalkthroughThe changes introduce socket-level tracking for logging aborted connections that close before a complete HTTP request is parsed. A new partial HTTP parser module extracts method, URL, headers, and tenant information from buffered socket data. The log-request plugin registers an onConnection handler to buffer incoming data and a cleanup mechanism to log "ABORTED CONN" events when sockets close prematurely. A helper function constructs minimal request objects from partial data for logging consistency. The LogRequestOptions interface is updated to include the new 'ABORTED CONN' status value alongside existing statuses. Sequence DiagramsequenceDiagram
participant Client as Client/Socket
participant Plugin as Log-Request Plugin
participant Parser as Partial HTTP Parser
participant Logger as Logger
Client->>Plugin: Connects (onConnection)
Plugin->>Plugin: Register socket lifecycle cleanup
Client->>Plugin: Send partial request data (≤2048 bytes)
Plugin->>Plugin: Buffer incoming data chunks
Client->>Plugin: Close before full request parsed
Plugin->>Parser: parsePartialHttp(bufferedData)
Parser->>Parser: Parse method, URL, headers, tenantId
Parser-->>Plugin: Return PartialHttpData
Plugin->>Plugin: createPartialLogRequest(partial data)
Plugin->>Logger: Log event with status 'ABORTED CONN'
Logger-->>Plugin: Event logged
Plugin->>Plugin: Cleanup socket reference
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
src/http/plugins/log-request.ts (1)
43-48:⚠️ Potential issue | 🔴 CriticalThe per-socket cleanup becomes unusable after the first request.
socketCleanupMap.set()happens once per socket, butcleanupSocketListeners()deletes that entry the first time a request finishes. After that, later requests on the same connection never clearwaitingForRequest/currentRequestData, so a reused socket can false-positiveABORTED CONNor double-log afterABORTED REQ/RES.Also applies to: 61-67, 131-141, 188-189
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/http/plugins/log-request.ts` around lines 43 - 48, The socket-cleanup map entry is removed by cleanupSocketListeners(), making per-socket state (socketCleanupMap, waitingForRequest, currentRequestData) unavailable for subsequent requests on the same connection; instead of deleting the socket's map entry inside cleanupSocketListeners, only remove the listeners/timeouts it registered and preserve the map entry (or reinitialize/reset waitingForRequest/currentRequestData per new request). Update cleanupSocketListeners to avoid socketCleanupMap.delete(socket) and ensure code that begins a new request (where waitingForRequest/currentRequestData are set) either reassigns/clears those fields per-request or relies on a persistent per-socket entry; reference functions/variables to change: cleanupSocketListeners, socketCleanupMap, waitingForRequest, currentRequestData (and the places that currently call socketCleanupMap.set so they either set per-request or keep the persistent entry).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/http/plugins/log-request.ts`:
- Around line 53-59: The zero-byte connection timeout is missed because
waitingForRequest is only set in onData; update onConnection/onData handling so
that waitingForRequest is true immediately when a socket connects (in
onConnection) and ensure the socket close/timeout handler checks for zero-length
requests (currentRequestDataSize === 0) and !pendingRequestLogged to emit the
timeout/no-request log; adjust resetting logic for pendingRequestLogged and
currentRequestStart inside onData and after emitting the log so the zero-byte
path is covered even if onData never ran.
---
Duplicate comments:
In `@src/http/plugins/log-request.ts`:
- Around line 43-48: The socket-cleanup map entry is removed by
cleanupSocketListeners(), making per-socket state (socketCleanupMap,
waitingForRequest, currentRequestData) unavailable for subsequent requests on
the same connection; instead of deleting the socket's map entry inside
cleanupSocketListeners, only remove the listeners/timeouts it registered and
preserve the map entry (or reinitialize/reset
waitingForRequest/currentRequestData per new request). Update
cleanupSocketListeners to avoid socketCleanupMap.delete(socket) and ensure code
that begins a new request (where waitingForRequest/currentRequestData are set)
either reassigns/clears those fields per-request or relies on a persistent
per-socket entry; reference functions/variables to change:
cleanupSocketListeners, socketCleanupMap, waitingForRequest, currentRequestData
(and the places that currently call socketCleanupMap.set so they either set
per-request or keep the persistent entry).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Central YAML (base), Organization UI (inherited)
Review profile: CHILL
Plan: Pro
Run ID: ba8cb604-ec80-4e7b-b1cf-3d6228a2a370
📒 Files selected for processing (3)
src/http/plugins/log-request.tssrc/internal/http/index.tssrc/internal/http/partial-http-parser.ts
src/http/plugins/log-request.ts
Outdated
| const onConnection = (socket: Socket) => { | ||
| const captureByteLimit = 2048 | ||
| let currentRequestData: Buffer[] = [] | ||
| let currentRequestDataSize = 0 | ||
| let currentRequestStart = Date.now() | ||
| let waitingForRequest = false | ||
| let pendingRequestLogged = false |
There was a problem hiding this comment.
Zero-byte connection timeouts are still skipped.
If a client connects and idles until node:http closes the socket, onData never runs, so waitingForRequest stays false and this guard exits without emitting a log. That misses the exact timeout/no-request path this PR is meant to cover.
Also applies to: 98-100
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/http/plugins/log-request.ts` around lines 53 - 59, The zero-byte
connection timeout is missed because waitingForRequest is only set in onData;
update onConnection/onData handling so that waitingForRequest is true
immediately when a socket connects (in onConnection) and ensure the socket
close/timeout handler checks for zero-length requests (currentRequestDataSize
=== 0) and !pendingRequestLogged to emit the timeout/no-request log; adjust
resetting logic for pendingRequestLogged and currentRequestStart inside onData
and after emitting the log so the zero-byte path is covered even if onData never
ran.
14342a7 to
3b43514
Compare
3b43514 to
12e3e6f
Compare
What kind of change does this PR introduce?
Bug fix
What is the current behavior?
There is no log entry created for connections that don't trigger a request.
What is the new behavior?
Create a log entry for all connections that don't result in a request event
Example logs
Disconnect before data sent
Node http connection timeout
Send unparsable data
Send incomplete data
Send complete headers with no payload