Skip to content

Fixes critical WebSocket configuration gaps and async route handler errors.#32

Merged
VikramAditya33 merged 1 commit into
mainfrom
doc
Apr 25, 2026
Merged

Fixes critical WebSocket configuration gaps and async route handler errors.#32
VikramAditya33 merged 1 commit into
mainfrom
doc

Conversation

@VikramAditya33
Copy link
Copy Markdown
Collaborator

@VikramAditya33 VikramAditya33 commented Apr 25, 2026

WebSocket Configuration (Fixes #31)

Exposed 4 missing WebSocket options that were used internally but not available in the interface:

  • maxBackpressure - Maximum buffered bytes per connection (default: 1MB)
  • maxLifetime - Automatically closes WebSocket connections after a specified number of minutes, regardless of activity
  • closeOnBackpressureLimit - Auto-close slow clients exceeding buffer limit
  • sendPingsAutomatically - Enable automatic ping frames for connection health

All options now properly documented with JSDoc and applied in UwsAdapter constructor.

Async Route Handler Fix (Fixes #30)

Root cause: uWebSockets.js requires either synchronous response OR abort handler attachment before async operations.

Solution: Attach res._onAbort() handler immediately at the start of route handlers in 3 locations:

Summary by CodeRabbit

  • New Features

    • WebSocket connections gain new configuration options: max lifetime, backpressure limits, close-on-backpressure, and automatic pings; compression behavior clarified.
  • Bug Fixes

    • JSON request parsing now trims whitespace and handles empty bodies: returns an empty object for safe methods and reports invalid JSON for body-bearing methods.
  • Refactor

    • Request routing now registers abort handling earlier to improve request lifecycle reliability.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 25, 2026

Warning

Rate limit exceeded

@VikramAditya33 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 43 minutes and 59 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 43 minutes and 59 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2e3fa325-30d6-4e4b-b4aa-741eeedede7e

📥 Commits

Reviewing files that changed from the base of the PR and between e133f86 and a816c4f.

📒 Files selected for processing (5)
  • src/http/core/request.ts
  • src/http/platform/uws-platform.adapter.ts
  • src/http/routing/route-registry.ts
  • src/websocket/adapter/uws.adapter.ts
  • src/websocket/interfaces/uws-options.interface.ts
📝 Walkthrough

Walkthrough

This PR trims request JSON bodies before parsing and treats empty bodies differently by method; it constructs and registers the UwsResponse/abort handler earlier in route handlers and passes the response into body-parser initialization; and it exposes/resolves additional uWebSockets adapter options (maxLifetime, maxBackpressure, closeOnBackpressureLimit, sendPingsAutomatically).

Changes

Cohort / File(s) Summary
HTTP Request Body Parsing
src/http/core/request.ts
json() now trims decoded body before JSON.parse; empty trimmed body returns frozen {} for GET/HEAD/DELETE, and throws Invalid JSON: Request body is empty (with a SyntaxError cause) for POST/PUT/PATCH.
Route Handler / Abort Registration
src/http/routing/route-registry.ts
Constructs UwsResponse earlier so _onAbort can be registered immediately; removes redundant per-branch wrappers; passes res into req._initBodyParser(...); adds immediate abort registration in simple-route and CORS OPTIONS handlers.
WebSocket Adapter Options
src/websocket/adapter/uws.adapter.ts, src/websocket/interfaces/uws-options.interface.ts, src/http/platform/uws-platform.adapter.ts
Adds adapter option fields (maxLifetime, maxBackpressure, closeOnBackpressureLimit, sendPingsAutomatically) with resolved defaults; bindClientConnect forwards these into app.ws(...); platform adapter defaults adjusted (adds maxLifetime, removes perMessageDeflate).

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant uWS
  participant RouteRegistry
  participant UwsResponse
  participant UwsRequest

  Client->>uWS: HTTP request
  uWS->>RouteRegistry: invoke shared handler
  RouteRegistry->>UwsResponse: construct wrapper (early)
  RouteRegistry->>UwsResponse: register _onAbort handler
  RouteRegistry->>UwsRequest: init body parser with res
  UwsRequest->>RouteRegistry: body parsed / json() (trim -> parse or handle empty)
  RouteRegistry->>UwsResponse: invoke matched route handler
  UwsResponse-->>Client: response
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰
I trimmed the crumbs of JSON stray,
I bound aborts at break of day,
I nudged the sockets, set their pace,
New options hum in wicker space,
Hop, code—let stable messages play! 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR includes an additional change to src/http/core/request.ts that trims whitespace from decoded request bodies and conditionally handles empty bodies based on HTTP method, which is not mentioned in the linked issues. Clarify whether the request.ts changes are intentional improvements or should be split into a separate PR, as they fall outside the stated scope of WebSocket configuration and abort handler fixes.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: exposing WebSocket configuration options and fixing async route handler errors with abort handlers.
Linked Issues check ✅ Passed The PR successfully addresses both linked issues: #31 adds all four WebSocket options (maxBackpressure, closeOnBackpressureLimit, sendPingsAutomatically, maxLifetime) to uws-options.interface.ts with defaults in UwsAdapter; #30 attaches abort handlers immediately at route handler start.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch doc

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
Copy Markdown

@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: 2

🧹 Nitpick comments (1)
src/http/core/request.ts (1)

1074-1085: Empty-body handling looks reasonable, with one subtle caveat.

The trim + method-based branching is sensible: it produces a clearer error than letting JSON.parse('') throw, and this.method is normalized to uppercase in the constructor (line 175), so the comparisons are reliable.

One small thing worth being aware of: this.cachedJson = {} caches a shared object reference. If a handler mutates the returned object, subsequent calls to json() (or req.body) on the same request will see the mutation. This matches the existing JSON cache semantics, so it's not a regression — but the empty-body case makes it slightly easier to hit accidentally. If you want belt-and-suspenders, freezing the object would prevent that:

🔒 Optional hardening
-      if (text === '') {
-        if (this.method === 'GET' || this.method === 'HEAD' || this.method === 'DELETE') {
-          this.cachedJson = {} as T;
-          return this.cachedJson as T;
-        }
+      if (text === '') {
+        if (this.method === 'GET' || this.method === 'HEAD' || this.method === 'DELETE') {
+          this.cachedJson = Object.freeze({}) as T;
+          return this.cachedJson as T;
+        }
         throw new Error('Invalid JSON: Request body is empty', {
           cause: new SyntaxError('Unexpected end of JSON input'),
         });
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/http/core/request.ts` around lines 1074 - 1085, The current empty-body
branch assigns this.cachedJson = {} as T, which stores a mutable shared object
that handlers could accidentally mutate; update the empty-body handling in the
json parsing path (where this.cachedJson is set for GET/HEAD/DELETE) to assign
an immutable object instead (e.g., create the empty object and Object.freeze it
before assigning to this.cachedJson) so callers receive a read-only empty object
while preserving the existing caching behavior in methods like json() / request
parsing.
🤖 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/websocket/adapter/uws.adapter.ts`:
- Around line 128-131: Remove the dead perMessageDeflate option: delete any
reference to perMessageDeflate (e.g., the this.options.perMessageDeflate
initialization at line with perMessageDeflate and any uses elsewhere) and from
the public interface/type so compression is controlled only via the existing
compression option; ensure this.app.ws(...) continues to use
this.options.compression (uWS.SHARED_COMPRESSOR by default), update any
default/docs to reflect that per-message DEFLATE is now determined solely by
compression, and remove or refactor any conditional logic that attempted to read
perMessageDeflate.

In `@src/websocket/interfaces/uws-options.interface.ts`:
- Around line 39-55: The perMessageDeflate option is invalid for uWebSockets.js
v20 and should be removed or mapped to the new compression API: delete the
perMessageDeflate property from the uws options interface (remove the
perMessageDeflate?: boolean declaration) and from ResolvedUwsAdapterOptions, and
instead rely on the compression: CompressOptions field
(WebSocketBehavior/compression) using uWS.DISABLED, uWS.SHARED_COMPRESSOR, or
uWS.DEDICATED_COMPRESSOR_*; if you need backward compatibility, implement logic
in the option resolution (e.g., in the function that produces
ResolvedUwsAdapterOptions) to translate perMessageDeflate === false into
compression = uWS.DISABLED and perMessageDeflate === true into a sensible
default compressor, then remove the docs/examples referencing perMessageDeflate.

---

Nitpick comments:
In `@src/http/core/request.ts`:
- Around line 1074-1085: The current empty-body branch assigns this.cachedJson =
{} as T, which stores a mutable shared object that handlers could accidentally
mutate; update the empty-body handling in the json parsing path (where
this.cachedJson is set for GET/HEAD/DELETE) to assign an immutable object
instead (e.g., create the empty object and Object.freeze it before assigning to
this.cachedJson) so callers receive a read-only empty object while preserving
the existing caching behavior in methods like json() / request parsing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 34731568-4373-491f-8c92-de8ec9de3f1c

📥 Commits

Reviewing files that changed from the base of the PR and between f40915c and 1edaa7a.

📒 Files selected for processing (4)
  • src/http/core/request.ts
  • src/http/routing/route-registry.ts
  • src/websocket/adapter/uws.adapter.ts
  • src/websocket/interfaces/uws-options.interface.ts

Comment thread src/websocket/adapter/uws.adapter.ts Outdated
Comment thread src/websocket/interfaces/uws-options.interface.ts Outdated
Copy link
Copy Markdown

@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

🧹 Nitpick comments (1)
src/websocket/interfaces/uws-options.interface.ts (1)

53-79: Remove the dangling duplicate JSDoc above compression.

Lines 53–56 contain a stray JSDoc block that is immediately followed by another JSDoc block (57–79) on the same compression property. Only the latter attaches to the field; the former is leftover from the doc expansion and shows up as orphaned commentary in source/IDE tooltips.

♻️ Suggested cleanup
-  /**
-   * Compression mode
-   * `@default` uWS.SHARED_COMPRESSOR
-   */
   /**
    * Compression mode
    *
    * Controls per-message deflate compression for WebSocket messages.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/websocket/interfaces/uws-options.interface.ts` around lines 53 - 79,
Remove the stray/duplicate JSDoc block that precedes the detailed one so the
`compression` property has only the comprehensive JSDoc; locate the
`compression` property comment blocks (the orphan short JSDoc immediately above
the longer multi-line JSDoc) and delete the shorter block, leaving the detailed
JSDoc attached to `compression`.
🤖 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/core/request.ts`:
- Around line 1076-1086: The inline comment above the empty-body branch in the
json() method is misleading and the code also freezes the shared empty object;
update the comment to say “throw for any method other than GET/HEAD/DELETE” to
reflect actual behavior, and either remove Object.freeze({}) when assigning
this.cachedJson to return a mutable empty object or add/update the json() JSDoc
to document that this.cachedJson is a shared frozen object (call site will get a
TypeError if they try to mutate it); changes should be made in the json()
function where this.cachedJson is assigned and where Object.freeze({}) is used.

---

Nitpick comments:
In `@src/websocket/interfaces/uws-options.interface.ts`:
- Around line 53-79: Remove the stray/duplicate JSDoc block that precedes the
detailed one so the `compression` property has only the comprehensive JSDoc;
locate the `compression` property comment blocks (the orphan short JSDoc
immediately above the longer multi-line JSDoc) and delete the shorter block,
leaving the detailed JSDoc attached to `compression`.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 340a6f7d-6527-4818-8754-861c77d4c91d

📥 Commits

Reviewing files that changed from the base of the PR and between 1edaa7a and e133f86.

📒 Files selected for processing (5)
  • src/http/core/request.ts
  • src/http/platform/uws-platform.adapter.ts
  • src/http/routing/route-registry.ts
  • src/websocket/adapter/uws.adapter.ts
  • src/websocket/interfaces/uws-options.interface.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/websocket/adapter/uws.adapter.ts

Comment thread src/http/core/request.ts Outdated
…nd Attach an abort handler at the beginning of the route handler #30
@VikramAditya33 VikramAditya33 merged commit 13085ef into main Apr 25, 2026
3 checks passed
@VikramAditya33 VikramAditya33 deleted the doc branch April 25, 2026 18:15
@VikramAditya33 VikramAditya33 restored the doc branch April 25, 2026 18:16
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.

Expose important (MISSING) WebSocket configuration options Attach an abort handler at the beginning of the route handler

1 participant