Skip to content

Conversation

@rhyek
Copy link

@rhyek rhyek commented Oct 19, 2025

Summary

This PR fixes a critical bug in setResponseHeaders() where no headers were being set at all.

The Bug

The previous code used Object.entries(headers) to iterate over a Headers object:

for (const [name, value] of Object.entries(headers)) {
  event.res.headers.set(name, value)
}

Problem: Object.entries() doesn't work on Headers objects (Web API interface). It returns an empty array, so no headers were being set.

The Fix

Changed to use the correct Web API method headers.entries():

for (const [name, value] of headers.entries()) {
  ...
}

Benefits:

  • ✅ Headers are now actually set (fixes the critical bug)
  • ✅ First occurrence uses .set() to replace existing headers
  • ✅ Subsequent occurrences use .append() to preserve multiple headers with the same name (e.g., Set-Cookie)

Test Coverage

Added comprehensive tests in packages/start-server-core/tests/request-response.test.ts:

  • Test for setting single header
  • Test for setting multiple headers
  • Test for iterating through Headers using .entries()
  • Test for handling empty Headers object
  • Test for multiple headers with the same name (e.g., multiple Set-Cookie headers)

All tests pass ✓

Summary by CodeRabbit

  • Bug Fixes

    • Improved header handling to support multiple identical header names in a single response.
    • Ensures the first occurrence replaces existing values while subsequent duplicates are appended, preventing unintended overwrites.
  • Tests

    • Added tests covering single/multiple headers, header replacement, empty headers, and multiple Set-Cookie entries.

…ectly

The previous code used Object.entries(headers) which doesn't work on Headers
objects from the Web API, resulting in no headers being set at all.

Changed to use headers.entries() which correctly iterates through the Headers
object and properly handles multiple headers with the same name.

- Fixed setResponseHeaders to use headers.entries() instead of Object.entries()
- Added logic to use .set() for first occurrence, .append() for duplicates
- Added test for multiple headers with the same name
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 19, 2025

Walkthrough

Refactors header application to iterate Headers.entries() and track seen header names per invocation; first occurrence calls set, later duplicates call append. Adds tests verifying single/multiple headers, replacement, empty headers, and multiple Set-Cookie entries.

Changes

Cohort / File(s) Summary
Header handling refactor
packages/start-server-core/src/request-response.ts
Rewrote setResponseHeaders to iterate headers.entries() and use a local addedHeaderNames set to deduplicate within an invocation: use set on first occurrence of a header name, append for subsequent occurrences.
Request-response test suite
packages/start-server-core/tests/request-response.test.ts
Added tests covering: single header set/get, multiple headers, empty Headers, replacing an existing header by re-setting, and multiple Set-Cookie entries appended and validated.

Sequence Diagram(s)

sequenceDiagram
  participant Handler as requestHandler
  participant Setter as setResponseHeaders
  participant Resp as H3 Response

  Handler->>Setter: provide Headers object
  note right of Setter#lightblue: iterate headers.entries()\ntrack addedHeaderNames
  loop for each header entry
    alt header name not seen
      Setter->>Resp: set(headerName, value)
      Setter->>Setter: addedHeaderNames.add(headerName)
    else header name seen
      Setter->>Resp: append(headerName, value)
    end
  end
  Setter-->>Handler: headers applied
  Handler->>Resp: return Response('OK')
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped through headers, one by one,
First I set, then I appended for fun,
Cookies lined up like carrots in a row,
Tests gave a nod—now the flow can grow. 🥕

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "fix(start-server-core): use headers.entries() to iterate headers correctly" is fully related to the main change in the changeset. It accurately captures the core bug fix: replacing the incorrect Object.entries() iteration with the proper headers.entries() Web API method for the Headers object. The title is specific to the package and clearly conveys the nature of the fix, making it immediately understandable to developers scanning the project history.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 13cf4ff and 2d6e600.

📒 Files selected for processing (1)
  • packages/start-server-core/tests/request-response.test.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/start-server-core/tests/request-response.test.ts

Warning

Tools execution failed with the following error:

Failed to run tools: 13 INTERNAL: Received RST_STREAM with code 2 (Internal server error)


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
Contributor

@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)
packages/start-server-core/src/request-response.ts (1)

139-149: Excellent fix for the Headers iteration bug!

The change from Object.entries(headers) to headers.entries() correctly addresses the issue where no headers were being set. The deduplication logic properly handles multiple headers with the same name (e.g., multiple Set-Cookie headers) by using .set() for the first occurrence and .append() for subsequent ones.

For stricter type safety in TypeScript strict mode, consider making the method selection more explicit:

  const addedHeaderNames: Record<string, true> = {}
  for (const [name, value] of headers.entries()) {
-   const found = addedHeaderNames[name] ?? false
-   if (!found) {
-     addedHeaderNames[name] = true
-   }
-   // If header already existed in h3 event headers, it will be replaced.
-   // However, headers object in this invocation might have multiple instances of the same header name (.append() was used), let's allow the duplicates.
-   const method = found ? 'append' : 'set'
-   event.res.headers[method](name, value)
+   // If header already existed in h3 event headers, it will be replaced.
+   // However, headers object in this invocation might have multiple instances of the same header name (.append() was used), let's allow the duplicates.
+   if (addedHeaderNames[name]) {
+     event.res.headers.append(name, value)
+   } else {
+     addedHeaderNames[name] = true
+     event.res.headers.set(name, value)
+   }
  }

This eliminates the bracket notation call and makes the intent clearer.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4693e0e and 13cf4ff.

📒 Files selected for processing (2)
  • packages/start-server-core/src/request-response.ts (1 hunks)
  • packages/start-server-core/tests/request-response.test.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript in strict mode with extensive type safety across the codebase

Files:

  • packages/start-server-core/tests/request-response.test.ts
  • packages/start-server-core/src/request-response.ts
packages/{*-start,start-*}/**

📄 CodeRabbit inference engine (AGENTS.md)

Name and place Start framework packages under packages/-start/ or packages/start-/

Files:

  • packages/start-server-core/tests/request-response.test.ts
  • packages/start-server-core/src/request-response.ts
🧬 Code graph analysis (1)
packages/start-server-core/tests/request-response.test.ts (1)
packages/start-server-core/src/request-response.ts (4)
  • requestHandler (49-60)
  • setResponseHeaders (135-150)
  • getResponseHeaders (152-155)
  • getResponseHeader (157-162)
🔇 Additional comments (2)
packages/start-server-core/tests/request-response.test.ts (2)

10-81: Excellent test coverage for basic header operations!

These tests thoroughly validate single headers, multiple different headers, empty headers, and header replacement scenarios. The test structure is clear and assertions are appropriate.


96-101: Test assertions are appropriately flexible.

The test correctly validates that both cookie values are present without making assumptions about the exact format returned by getResponseHeader(). This approach ensures the test remains valid regardless of whether the H3 library returns comma-separated values or handles Set-Cookie specially.

@rhyek rhyek changed the title fix(start-server-core): use Headers.entries() to iterate headers correctly fix(start-server-core): use headers.entries() to iterate headers correctly Oct 19, 2025
@schiller-manuel
Copy link
Contributor

thanks for the PR, we are currently reworking header handling, so either we will merge this PR or apply similar changes. let's keep this open until we are ready

@rhyek
Copy link
Author

rhyek commented Oct 19, 2025

thanks for the PR, we are currently reworking header handling, so either we will merge this PR or apply similar changes. let's keep this open until we are ready

Ah, that makes sense. Was kind of hoping to get my contributor badge, but np! 😆

@rhyek
Copy link
Author

rhyek commented Oct 19, 2025

FYI, just getting the current headers object using getResponseHeaders() and updating headers through that works and you have the full Headers Fetch API available.

Maybe tanstack should just rely on the user doing that.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants