Skip to content

fix(transport): return null bodies for 204 and 205 on raw outbound transports - #5125

Merged
lidge-jun merged 3 commits into
devfrom
codex/raw-transport-null-body
Sep 19, 2026
Merged

lidge-jun merged 3 commits into
devfrom
codex/raw-transport-null-body

Conversation

@lidge-jun

@lidge-jun lidge-jun commented Sep 19, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • A 204 or 205 from a provider no longer fails or stalls on the two raw outbound transports. Both build a Response from a socket rather than going through fetch, so each applied the Fetch null-body rule itself and the two disagreed: src/lib/socks5-fetch.ts excluded 204 but not 205, and src/lib/pinned-http.ts attached a streaming body to every 2xx. On the pinned direct route a 204 raised a TypeError from new Response(stream, { status: 204 }) inside the response handler instead of resolving; on either route a 205 waited for body bytes that were never coming, so the call settled only when a keep-alive peer eventually closed.
  • src/lib/http-response-semantics.ts now holds the null-body status set (204, 205, 304) and both transports read it. 101 and 103 are null-body statuses too, but neither is a final Response status these helpers support, and the file says so rather than claiming an upgrade is refused earlier.
  • Representation headers are preserved exactly as they arrived. A 205 that advertises Content-Encoding: br is neither decoded nor refused, because there are no coded bytes to act on.
  • Closes [Bug]: Return null bodies for 204 and 205 in raw outbound transports #5109.

Scope map:

Path Change
src/lib/http-response-semantics.ts New. The shared null-body status predicate and the reason it is shared.
src/lib/pinned-http.ts Branch on a null-body status before constructing the success stream; release the response and request.
src/lib/socks5-fetch.ts bodylessResponse reads the shared predicate, which adds 205 and keeps HEAD, 304 and informational behavior.
tests/lib/transport-null-body.test.ts New regression file, registered in both layout inventories.
structure/transports/inventory.md Records the shared rule and its test owner.

No behavior outside the null-body statuses changes. Content coding on the pinned direct route is a separate residual tracked in #5111, and the SOCKS upload lifecycle is #5110.

Verification

  • Local checks: NOT RUN. This lane is prohibited from running local suites, focused tests, typecheck, builds, installs or any live ocx command, so no local result is claimed. Exact-head hosted CI on this PR is the executable evidence.
  • Static review performed instead: read both transports at the current dev head, traced every path that reaches a Response constructor, and confirmed responseBody already returns null for anything bodylessResponse accepts, so adding 205 there needs no second change. Confirmed 304 cannot reach the new pinned branch because the existing non-2xx branch answers it first, and that the branch is still correct if that ever changes.
  • New regressions in tests/lib/transport-null-body.test.ts, all against loopback fake peers with no credentials: pinned 204 resolves with a null body while the peer holds the connection open; pinned 205 carrying Content-Encoding: br resolves with a null body and keeps the header; an ordinary pinned 200 still streams its JSON; a 205 through a fake SOCKS tunnel resolves with no body. The keep-alive peer is what distinguishes "resolved with no body" from "resolved because the connection went away".
  • git diff --check is clean. New test file registered in scripts/test-layout/layout.json and tests/fixtures/test-layout-expected.json; no file-size cap was raised, and none of the touched source files are tracked in tests/fixtures/file-size-baseline.json.

Checklist

  • Scope stays focused and avoids unrelated cleanup.
  • Docs or release notes were updated when needed.
  • Security-sensitive changes were reviewed for secrets, auth, and unsafe defaults.

Summary by CodeRabbit

  • Bug Fixes

    • Improved handling of bodyless HTTP responses with status codes 204, 205, and 304.
    • Requests now resolve with a null response body and release connections promptly, including with keep-alive servers.
    • Response headers are preserved for bodyless responses, while normal response bodies continue to stream correctly across direct and SOCKS5 transports.
  • Tests

    • Added coverage for bodyless responses and connection release across direct and SOCKS5 transports.

…ansports

Both raw outbound transports build a Response from a socket, so each applied the
Fetch null-body rule on its own and the two disagreed. The SOCKS helper excluded
204 but not 205; the pinned direct helper attached a streaming body to every 2xx.
A 204 or 205 therefore either raised a TypeError inside the response handler or
waited for a peer that was entitled to keep the connection alive.

The status set now lives in one place and both owners read it. Representation
headers survive untouched: a 205 that advertises a coding it never sent is
neither decoded nor refused, because there are no coded bytes to act on.
@lidge-jun
lidge-jun requested a review from Ingwannu as a code owner September 19, 2026 07:34
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@github-actions

Copy link
Copy Markdown
Contributor

✅ Deterministic PR hygiene checks passed.

@github-actions github-actions Bot added the bug Something isn't working label Sep 19, 2026
@coderabbitai

coderabbitai Bot commented Sep 19, 2026 •

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: lidge-jun/opencodex/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 5acd0ff2-aea6-4839-b2f9-850945316580

📥 Commits

Reviewing files that changed from the base of the PR and between 4c7fe1e and 6557add.

📒 Files selected for processing (1)
  • tests/lib/transport-null-body.test.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

The raw outbound transports now share isNullBodyStatus, which identifies statuses 204, 205, and 304. The pinned transport returns null-body Response objects and releases connections. The SOCKS5 transport also handles 205 as bodyless. Tests cover keep-alive responses, header preservation, normal 200 streaming, and SOCKS5 routing.

Changes

Raw transport response handling

Layer / File(s) Summary
Shared null-body rule and transport integration
src/lib/http-response-semantics.ts, src/lib/pinned-http.ts, src/lib/socks5-fetch.ts
isNullBodyStatus classifies 204, 205, and 304. The pinned transport resolves null-body responses before stream construction and destroys the underlying request and response. The SOCKS5 transport applies the same rule.

Cross-transport regression coverage

Layer / File(s) Summary
Keep-alive transport tests and test registration
tests/lib/transport-null-body.test.ts, scripts/test-layout/layout.json, tests/fixtures/test-layout-expected.json
Tests cover keep-alive 204 and 205 responses, header preservation, normal 200 streaming, connection release through SOCKS5, and registration in the lib domain.

Transport inventory update

Layer / File(s) Summary
Transport inventory documentation
structure/transports/inventory.md
The inventory documents the shared null-body statuses, connection release behavior, header preservation, and cross-transport test coverage.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix · Severity of issue fixed: Medium

Sequence Diagram(s)

sequenceDiagram
  participant Upstream
  participant PinnedHTTP
  participant SOCKS5Fetch
  participant Response
  Upstream->>PinnedHTTP: Return 204, 205, or 304
  PinnedHTTP->>Response: Resolve with null body and headers
  Upstream->>SOCKS5Fetch: Return 204, 205, or 304
  SOCKS5Fetch->>Response: Resolve as bodyless response
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 55.56% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 9 functions across 4 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the primary change: raw outbound transports now return null bodies for HTTP 204 and 205 responses.
Linked Issues check ✅ Passed Issue #5109 coding requirements are met. src/lib/http-response-semantics.ts:26-28 defines isNullBodyStatus for 204, 205, and 304. src/lib/pinned-http.ts applies the predicate in onResponse bef…
Out of Scope Changes check ✅ Passed The changes stay within Issue #5109. The shared predicate supports the two raw transports. The pinned and SOCKS5 branches implement null-body settlement, header preservation, and connection cleanup. T…
  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

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.

The comment claimed an upgrade is refused before the shared predicate is
consulted. It is not: the SOCKS helper stops its interim-response loop on 101 and
carries that status into response construction. Describe 101 and 103 as statuses
outside the final Response set these helpers support, which is what is actually
true, without changing any runtime behavior.
The case was named for a release it did not check. It asserted status and body
and then force-closed every socket in teardown, so the name claimed more than the
assertions did. It now waits, under a bounded deadline rather than a fixed sleep,
for the keep-alive peer's connection to be observably destroyed, which is the
release the transport actually owes.
@lidge-jun

Copy link
Copy Markdown
Owner Author

리뷰 · 우선순위 77 / 80

raw outbound 두 경로(pinned direct·SOCKS5)는 fetch 없이 소켓으로 Response를 만듭니다. Fetch의 null-body 규칙(204·205·304)을 각자 적용하다 어긋났습니다. SOCKS는 204는 막고 205는 빠뜨렸고, pinned는 2xx마다 스트림을 붙여 new Response(stream, { status: 204 })가 TypeError를 냈습니다. 205는 keep-alive 피어가 바디를 안 보내면 연결이 닫힐 때까지 hang이 됐습니다.

이 PR은 src/lib/http-response-semantics.ts에 isNullBodyStatus를 두고 양쪽에 공유합니다. pinned는 null-body면 Response(null)로 풀고 연결을 즉시 놓습니다. SOCKS bodylessResponse도 같은 집합을 씁니다. 101·103은 이 helpers가 final Response로 만들지 않는다고 문서에 적어 두었습니다. tests/lib/transport-null-body.test.ts가 keep-alive 피어 위에서 204/205 null body·SOCKS 205 tunnel release·일반 200을 고정합니다.

호스티드 CI는 이 글을 쓰는 시점 기준 hygiene/gates/test 1–4/macos/npm-global/keyring/docker 등 핵심이 이미 그린입니다. #5126이 이 브랜치 위에 content-coding을 쌓고 있으므로, 이 PR이 스택의 바닥입니다. types/config 스플릿과 무관.

경로 src/lib/http-response-semantics.ts - 규칙 한곳에. #5126이 여기 coding 분류를 이어 붙인다
경로 src/lib/pinned-http.ts null-body 분기 - TypeError·keep-alive hang을 동시에 막는다
경로 socks5-fetch.ts bodylessResponse - 205를 204와 같은 이유로 넣은 수정이 맞다
경로 tests/lib/transport-null-body.test.ts - keep-alive + awaitDestroyed가 “연결을 스스로 놓는지”를 본다
문서 structure/transports/inventory.md - 계약 기록이 코드와 같다
스택 - #5126·(간접적으로 #5127 충돌면)의 선행 조건이다

메인테이너의 판단이 필요한 지점

  • 지금 CI 그린이면 바로 merge할지(권장)
  • merge 직후 #5126을 이어서 돌릴지

너의 추천
지금 merge한다. CI가 이미 그린이고 #5126의 베이스다. types/config 스플릿 때문에 닫을 PR이 아니다. merge 후 #5126 CI를 다시 확인한다.

이 댓글은 grok-bot이 작성했습니다

@lidge-jun

Copy link
Copy Markdown
Owner Author

Maintainer integration decision for dev: integrate reviewed head 6557add72b48e91f8fdb5805a82192777b7d3286 under MAINTAINERS.md, without representing this as self-approval or a second maintainer review.

The seven-file change was independently reviewed. The informational-status prose was corrected, and the SOCKS 205 regression now observes connection destruction before fixture teardown. Runtime scope remains null-body handling; existing regressions were preserved. No unresolved review threads or maintainer change requests were present at the final check.

Exact-head hosted run: https://github.com/lidge-jun/opencodex/actions/runs/35430059298. All four test shards, both macOS shards, storage/api/gates, keyring, docker, packaging/structure and aggregate ci succeeded. Run logs explicitly show the new transport-null-body cases passing in Linux test 4/4 and macOS 2/2. Event-inapplicable skipped jobs are not claimed as passing executions. Local tests/typecheck/build/install were NOT RUN per the owner's restriction.

The merge is bound to the reviewed SHA and current dev base. Parent branch is retained because #5126 depends on it. Issue #5109 will be closed only after actual dev landing is confirmed; the child requires retargeting and fresh evidence separately.

@lidge-jun
lidge-jun merged commit 4e7d713 into dev Sep 19, 2026
32 checks passed
@lidge-jun
lidge-jun deleted the codex/raw-transport-null-body branch September 19, 2026 08:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant