Skip to content

feat: http/2 listener support#102

Merged
pi0 merged 25 commits intounjs:mainfrom
SukkaW:http2-support
Mar 25, 2026
Merged

feat: http/2 listener support#102
pi0 merged 25 commits intounjs:mainfrom
SukkaW:http2-support

Conversation

@SukkaW
Copy link
Copy Markdown
Member

@SukkaW SukkaW commented Feb 20, 2026

First step toward #38 with HTTP/2 server/listener support.

Summary by CodeRabbit

  • New Features

    • Added optional HTTP/2 listener support (http2 option) with HTTP/1.x compatibility and improved header handling for HTTP/2 pseudo-headers.
  • Bug Fixes

    • More robust proxy stream handling (better cleanup on upstream close/error) and improved redirect/host rewrite and connection/status behavior for HTTP/2 requests.
  • Tests

    • Added comprehensive HTTP/2 integration tests and an SSE regression test.
  • Chores

    • Added undici as a dev dependency for HTTP/2 testing.

@codecov
Copy link
Copy Markdown

codecov Bot commented Feb 20, 2026

Codecov Report

❌ Patch coverage is 97.56098% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 96.14%. Comparing base (743098d) to head (2ed0dfd).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
src/server.ts 92.85% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #102      +/-   ##
==========================================
- Coverage   96.19%   96.14%   -0.05%     
==========================================
  Files           8        8              
  Lines         604      623      +19     
  Branches      225      233       +8     
==========================================
+ Hits          581      599      +18     
- Misses         23       24       +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment thread src/_utils.ts Outdated
@SukkaW SukkaW requested review from pi0 and sapphi-red and removed request for pi0 February 20, 2026 15:32
@SukkaW SukkaW marked this pull request as ready for review February 20, 2026 15:32
@SukkaW
Copy link
Copy Markdown
Member Author

SukkaW commented Feb 20, 2026

@pi0 I believe this PR is ready for review now. All tests passed on my machine locally

image

@SukkaW SukkaW changed the title feat: initial http/2 listener support feat: http/2 listener support Feb 20, 2026
@SukkaW SukkaW requested review from Copilot and pi0 February 23, 2026 09:57
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds initial HTTP/2 (h2) listener support to the proxy server as a step toward issue #38 (HTTP2/3 support), including middleware updates to handle HTTP/2-specific header semantics and new integration tests.

Changes:

  • Introduce http2?: boolean option and create an HTTP/2 secure listener (with allowHTTP1) when enabled.
  • Update incoming/outgoing middleware + core utils to handle HTTP/2 pseudo-headers (:authority) and remove HTTP/2-incompatible hop-by-hop headers.
  • Add HTTP/2 integration tests (via undici with h2-enabled agent) and extend stubs/tests for httpVersionMajor.

Reviewed changes

Copilot reviewed 11 out of 12 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
test/middleware/web-outgoing.test.ts Adds coverage for :authority redirect rewriting and HTTP/2 connection-header behavior.
test/https-proxy.test.ts Exports helper functions for listening (currently used by new HTTP/2 tests).
test/http2-proxy.test.ts New HTTP/2 listener integration tests (HTTP/2+HTTP/1.1 clients over TLS).
test/_stubs.ts Enhances IncomingMessage stubs with httpVersion/httpVersionMajor; tweaks ProxyServer stub typing.
src/types.ts Adds http2 option and documents SSL usage for HTTP/2 listener.
src/server.ts Implements HTTP/2 secure server creation path; broadens request/response typings to include HTTP/2 compat types.
src/middleware/web-outgoing.ts Removes HTTP/2-incompatible headers (e.g., connection), supports :authority for redirects/status handling.
src/middleware/web-incoming.ts Uses :authority as a fallback for x-forwarded-host.
src/middleware/_utils.ts Extends middleware typing to accept HTTP/2 request/response types (with one remaining generic constraint issue).
src/_utils.ts Filters HTTP/2 pseudo-headers from outgoing request options and uses :authority for host/port derivation.
package.json Adds undici dev dependency for HTTP/2-capable fetch in tests.
pnpm-lock.yaml Locks undici addition.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported
Comments suppressed due to low confidence (4)

src/server.ts:85

  • When http2 is enabled, the code always creates a TLS server via http2.createSecureServer(...), but ssl is still optional in ProxyServerOptions. If a user sets http2: true without providing TLS credentials, the server will fail at runtime; consider validating options.ssl (or requiring it in types) and throwing a clear error early.
    if (this.options.http2) {
      this._server = http2.createSecureServer({ ...this.options.ssl, allowHTTP1: true }, closure);
    } else if (this.options.ssl) {
      this._server = https.createServer(this.options.ssl, closure);

src/server.ts:43

  • _webPasses is still typed as ProxyMiddleware<http.ServerResponse>[], but when http2 is enabled the res passed through the passes can be http2.Http2ServerResponse. This mismatch is currently papered over with casts in _getPasses/listen, reducing type safety and making it easy for a pass to accidentally use HTTP/1-only APIs. Consider widening the _webPasses type (and the middleware list types) to include Http2ServerResponse so incompatibilities are caught by typecheck.
  _webPasses: ProxyMiddleware<http.ServerResponse>[] = [...webIncomingMiddleware];
  _wsPasses: ProxyMiddleware<net.Socket>[] = [...websocketIncomingMiddleware];

test/http2-proxy.test.ts:25

  • undici Agent instances keep connection pools open; without closing them, the test runner can hang or report open handles. Add a top-level afterAll to close()/destroy() both http1Agent and http2Agent once the suite completes.
const http1Agent = new Agent({
  allowH2: false,
  connect: {
    // Allow to use SSL self signed
    rejectUnauthorized: false,
  },
});
const http2Agent = new Agent({
  allowH2: true,
  connect: {
    // Allow to use SSL self signed
    rejectUnauthorized: false,
  },
});

src/middleware/_utils.ts:29

  • defineProxyMiddleware's generic constraint is still T extends ServerResponse | Socket, but ProxyMiddleware now supports Http2ServerResponse as well. This makes it impossible to declare a middleware explicitly typed as ProxyMiddleware<Http2ServerResponse> (it won't satisfy the constraint) and is inconsistent with the updated ProxyMiddleware type. Widen defineProxyMiddleware's constraint to include Http2ServerResponse.
export function defineProxyMiddleware<T extends ServerResponse | Socket = ServerResponse>(
  m: ProxyMiddleware<T>,
) {

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/server.ts Outdated
Comment thread test/https-proxy.test.ts Outdated
Comment thread test/http2-proxy.test.ts
Comment thread test/http2-proxy.test.ts
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 1, 2026

Warning

Rate limit exceeded

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

⌛ 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: 79ebd896-20ae-47f9-a9ff-5b06d3117248

📥 Commits

Reviewing files that changed from the base of the PR and between aabae92 and 2ed0dfd.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (6)
  • package.json
  • src/_utils.ts
  • src/middleware/web-outgoing.ts
  • src/server.ts
  • test/http2-proxy.test.ts
  • test/middleware/ws-incoming.test.ts
📝 Walkthrough

Walkthrough

Adds HTTP/2 support across the proxy: types, runtime handling of HTTP/2 pseudo-headers and connection/status behavior, conditional HTTP/2 server creation, and end-to-end tests including HTTP/2 integration and utilities for test listening.

Changes

Cohort / File(s) Summary
Dependencies
package.json
Added undici devDependency (^7.21.0) for HTTP/2-capable client testing.
Options & Types
src/types.ts
Added optional http2?: boolean to ProxyServerOptions.
Core Utilities
src/_utils.ts
Extended functions to accept Http2ServerRequest; handle :authorityhost, strip HTTP/2 pseudo-headers, derive port from :authority, and detect TLS via req.socket.encrypted.
Middleware Types
src/middleware/_utils.ts
Expanded union types to include Http2ServerRequest / Http2ServerResponse; updated ProxyMiddleware and outgoing middleware signatures to accept HTTP/2 request/response unions.
Web Middleware Logic
src/middleware/web-incoming.ts, src/middleware/web-outgoing.ts
Prefer :authority for x-forwarded-host and redirect rewrites; add HTTP/2-aware connection header and statusMessage handling; add lifecycle handlers for incomplete proxied streams.
Server Implementation
src/server.ts
Widened ProxyServer/event generics for HTTP/2 types; select http2.createSecureServer when options.http2 + SSL (error if http2 without ssl); fixed websocket upgrade args and adjusted proxy function typing to accept mixed HTTP/1/HTTP/2 Req/Res unions.
Test Helpers & Stubs
test/_stubs.ts, test/_utils.ts
Added httpVersion/httpVersionMajor to the IncomingMessage stub; generalized stubProxyServer return type; added listenOn and proxyListen helpers for ephemeral ports.
Tests — Consolidation & New Cases
test/http-proxy.test.ts, test/https-proxy.test.ts, test/middleware/web-incoming.test.ts
Replaced in-file listenOn/proxyListen with shared utils imports; adjusted tests to use new helpers.
HTTP/2 Integration Tests
test/http2-proxy.test.ts
New comprehensive HTTP/2 integration suite exercising proxy in HTTP2->HTTP and HTTP2->HTTPS scenarios using self-signed certs and undici clients.
HTTP/2 Behavior Tests
test/middleware/web-outgoing.test.ts
Added tests for authority-based redirect rewriting, HTTP/2 connection header deletion, and httpVersionMajor-specific behaviors (HTTP/1.1 vs HTTP/2).

Sequence Diagram

sequenceDiagram
    participant Client
    participant Proxy as HTTP/2 Proxy
    participant Target as Backend Server

    Client->>Proxy: HTTP/2 request (includes :authority)
    Note over Proxy: identify HTTP/2 via httpVersionMajor\nstrip pseudo-headers and copy :authority → host
    Proxy->>Target: Outgoing request (HTTP/1.x or HTTP/2 depending on target)
    Target-->>Proxy: Response (statusCode, headers)
    Note over Proxy: remove connection header for HTTP/2+\nset statusCode (skip statusMessage for HTTP/2)
    Proxy-->>Client: HTTP/2 response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~65 minutes

Poem

🐇 I nibble headers, hop through streams,
:authority guides my proxy dreams,
Pseudo-headers gone, connections light,
HTTP/2 leaps into the night,
A joyful rabbit routing bytes!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.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: http/2 listener support' directly and clearly summarizes the main change: adding HTTP/2 listener support to the proxy server.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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: 3

🧹 Nitpick comments (2)
test/_stubs.ts (1)

60-61: Minor type inconsistency in return cast.

The return type is ProxyServer<any, any> but the cast on line 61 uses the unparameterized ProxyServer. This works due to the as unknown intermediate cast but could be slightly cleaner.

Optional fix for type consistency
 export function stubProxyServer(overrides: Record<string, unknown> = {}): ProxyServer<any, any> {
-  return overrides as unknown as ProxyServer;
+  return overrides as unknown as ProxyServer<any, any>;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/_stubs.ts` around lines 60 - 61, The stubProxyServer function returns
ProxyServer<any, any> but currently casts to the raw ProxyServer type; update
the return cast to match the declared generic parameters—change the final cast
to "as unknown as ProxyServer<any, any>" in the stubProxyServer function so the
returned type is consistently ProxyServer<any, any>.
test/http2-proxy.test.ts (1)

87-91: Consider awaiting source.close() for proper cleanup.

source.close() (for http.Server / https.Server) is asynchronous and accepts an optional callback. While it often works without awaiting, for deterministic test cleanup, consider wrapping it in a Promise or using a callback to ensure the server is fully closed before the test suite ends.

♻️ Optional: Await source close for consistent cleanup
     afterAll(async () => {
       // cleans up
       await new Promise<void>((resolve) => proxy.close(resolve));
-      source.close();
+      await new Promise<void>((resolve, reject) => 
+        source.close((err) => err ? reject(err) : resolve())
+      );
     });

Also applies to: 161-165

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/http2-proxy.test.ts` around lines 87 - 91, The afterAll cleanup
currently calls source.close() without awaiting its completion; change it to
await the server close by wrapping source.close in a Promise (or using its
callback) so the test waits for full shutdown, similar to how
proxy.close(resolve) is awaited; update all occurrences (e.g., the afterAll
blocks around source.close at the shown locations) to await the
Promise-returning close to ensure deterministic teardown.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@package.json`:
- Line 56: The package entry for undici is fine but HTTP/2 is opt-in; when you
create or configure undici's Agent or Client (Agent, Client) for HTTP/2 tests
set the allowH2 option to true (e.g., pass allowH2: true to Agent/Client
constructor or options) so HTTP/2 is actually enabled, and optionally bump the
package version to 7.22.0 if you want the latest patch release.

In `@src/server.ts`:
- Around line 82-88: The server currently calls http2.createSecureServer when
this.options.http2 is true but does not validate that this.options.ssl is
provided; add a runtime check in the server initialization (before calling
http2.createSecureServer) that if this.options.http2 is true and
this.options.ssl is falsy, throw or reject with a clear error explaining that
HTTP/2 requires SSL (referencing ProxyServerOptions in the message), otherwise
proceed to call http2.createSecureServer; ensure the check is placed alongside
the existing branch that inspects this.options.http2 / this.options.ssl so the
error occurs early with a helpful message rather than a cryptic
createSecureServer failure.

In `@test/_utils.ts`:
- Around line 16-27: The proxyListen helper can miss synchronous errors because
proxy.listen(0, "127.0.0.1") is called before attaching the server error
listener; to fix, in proxyListen wrap the call to proxy.listen(...) in a
try/catch and immediately reject on synchronous throw, then grab (proxy as
any)._server and attach server.once("error", reject) and
server.once("listening", ...) as before; keep the existing resolve logic in the
"listening" handler for port resolution.

---

Nitpick comments:
In `@test/_stubs.ts`:
- Around line 60-61: The stubProxyServer function returns ProxyServer<any, any>
but currently casts to the raw ProxyServer type; update the return cast to match
the declared generic parameters—change the final cast to "as unknown as
ProxyServer<any, any>" in the stubProxyServer function so the returned type is
consistently ProxyServer<any, any>.

In `@test/http2-proxy.test.ts`:
- Around line 87-91: The afterAll cleanup currently calls source.close() without
awaiting its completion; change it to await the server close by wrapping
source.close in a Promise (or using its callback) so the test waits for full
shutdown, similar to how proxy.close(resolve) is awaited; update all occurrences
(e.g., the afterAll blocks around source.close at the shown locations) to await
the Promise-returning close to ensure deterministic teardown.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4b03756 and 91b63fb.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (14)
  • package.json
  • src/_utils.ts
  • src/middleware/_utils.ts
  • src/middleware/web-incoming.ts
  • src/middleware/web-outgoing.ts
  • src/server.ts
  • src/types.ts
  • test/_stubs.ts
  • test/_utils.ts
  • test/http-proxy.test.ts
  • test/http2-proxy.test.ts
  • test/https-proxy.test.ts
  • test/middleware/web-incoming.test.ts
  • test/middleware/web-outgoing.test.ts

Comment thread package.json Outdated
"sse": "^0.0.8",
"typescript": "^5.9.3",
"unbuild": "^3.6.1",
"undici": "^7.21.0",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if undici 7.21.0 exists and get latest version
echo "=== Checking undici versions ==="
curl -s https://registry.npmjs.org/undici | jq '.dist-tags | {latest, latest_v7: (.["7.21.0"] // "not found")}'

# Get detailed info about 7.21.0
curl -s https://registry.npmjs.org/undici/7.21.0 | jq '{version: .version, name: .name, deprecated: .deprecated}'

# Get latest version info
curl -s https://registry.npmjs.org/undici/latest | jq '{version: .version, name: .name, deprecated: .deprecated}'

Repository: unjs/httpxy

Length of output: 386


🏁 Script executed:

#!/bin/bash
# Check for known vulnerabilities in undici
echo "=== Checking for security advisories ==="
curl -s https://registry.npmjs.org/-/npm/v1/security/advisories?package=undici | jq '.advisories // empty'

# Alternative: check npm audit for this package
npm view [email protected] --json | jq '{version: .version, keywords: .keywords[0:5], description: .description}'

Repository: unjs/httpxy

Length of output: 282


🌐 Web query:

undici HTTP/2 support Node.js

💡 Result:

Undici (and therefore Node.js’s built-in fetch) is primarily an HTTP/1.1 client, but it does have HTTP/2 client support as an experimental, opt-in feature. [1] [2]

How to use HTTP/2 with undici

Enable it by using an Agent/Client with allowH2: true (HTTP/2 is negotiated via ALPN). The docs also note constraints like “TLS-only” and “h2c is not supported” (so in practice you typically need https:// + a server that negotiates h2). [2]

import { Agent, setGlobalDispatcher, fetch } from 'undici'

setGlobalDispatcher(new Agent({ allowH2: true }))

const res = await fetch('https://example.com/')
console.log(res.status)

Node.js built-in fetch and HTTP/2

Node’s built-in fetch() uses the undici version bundled with your Node release, and HTTP/2 is not enabled by default—there’s an open request to support HTTP/2 by default in fetch. [3] [4]

Sources: [1] undici repo README, [2] undici Client API docs (UNPKG) incl. allowH2 + HTTP/2 notes, [3] undici discussion mentioning enabling H2 via allowH2: true for fetch, [4] undici issue requesting HTTP/2-by-default for fetch.


LGTM - undici is suitable for HTTP/2 testing with proper configuration.

The undici 7.21.0 package is stable and has no known security vulnerabilities. However, HTTP/2 support is an experimental, opt-in feature that requires explicit configuration (allowH2: true on the Agent/Client). As a devDependency, it won't affect production bundles. Note that version 7.22.0 is available if you want to stay current with the latest patch.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@package.json` at line 56, The package entry for undici is fine but HTTP/2 is
opt-in; when you create or configure undici's Agent or Client (Agent, Client)
for HTTP/2 tests set the allowH2 option to true (e.g., pass allowH2: true to
Agent/Client constructor or options) so HTTP/2 is actually enabled, and
optionally bump the package version to 7.22.0 if you want the latest patch
release.

Comment thread src/server.ts
Comment thread test/_utils.ts
Comment on lines +16 to +27
export function proxyListen(
proxy: ReturnType<typeof httpProxy.createProxyServer>,
): Promise<number> {
return new Promise((resolve, reject) => {
proxy.listen(0, "127.0.0.1");
const server = (proxy as any)._server as net.Server;
server.once("error", reject);
server.once("listening", () => {
resolve((server.address() as AddressInfo).port);
});
});
}
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot Mar 1, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Potential race condition: error listener attached after listen() call.

The error listener on line 22 is attached after proxy.listen() is called on line 20. If the server emits an error synchronously or very quickly, the reject handler may not be registered in time.

Proposed fix to attach error listener before listen
 export function proxyListen(
   proxy: ReturnType<typeof httpProxy.createProxyServer>,
 ): Promise<number> {
   return new Promise((resolve, reject) => {
-    proxy.listen(0, "127.0.0.1");
     const server = (proxy as any)._server as net.Server;
+    proxy.listen(0, "127.0.0.1");
+    // Note: _server is created inside listen(), so we must access it after
     server.once("error", reject);
     server.once("listening", () => {
       resolve((server.address() as AddressInfo).port);
     });
   });
 }

Actually, since _server is created inside listen(), the current order is necessary. However, this means errors during initial binding could be missed. Consider wrapping in a try-catch or accepting this as a test utility limitation.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/_utils.ts` around lines 16 - 27, The proxyListen helper can miss
synchronous errors because proxy.listen(0, "127.0.0.1") is called before
attaching the server error listener; to fix, in proxyListen wrap the call to
proxy.listen(...) in a try/catch and immediately reject on synchronous throw,
then grab (proxy as any)._server and attach server.once("error", reject) and
server.once("listening", ...) as before; keep the existing resolve logic in the
"listening" handler for port resolution.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

very quickly

It is impossible. That's not how JavaScript or the event loop works.

synchronously

What's the point anyway? Throwing in new Promise() will result in an automatic rejection.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@SukkaW You're absolutely right on both points. I apologize for the confusion.

The event loop guarantees that all synchronous code within the Promise executor completes before any asynchronous events can be processed, so the error and listening listeners will always be attached before those events can fire. Additionally, any synchronous throw within the Promise executor is automatically converted to a rejection by the Promise constructor.

The code is correct as written. Thank you for the clarification!

pi0 added 6 commits March 25, 2026 20:15
The HTTP/2 PR accidentally removed proxyRes close and error handlers
from web-incoming stream middleware, breaking upstream abort propagation
to downstream clients. Restores the handlers and the SSE close test.
Throws a clear error instead of letting http2.createSecureServer
fail with a cryptic message when ssl is not provided.
The contributor branch was based on older versions. Restores versions
from main and adds undici as new dev dependency.
The comment still said "HTTP 1.0" but the logic now covers both
HTTP/1.0 and HTTP/2 (anything that isn't HTTP/1.1).
All supported Node.js versions (16+) have req.socket. The deprecated
req.connection and pair fallbacks were unreachable dead code.
Use proper generic type assertions (as Req/Res) instead of as any
for the listener callback closure.
pi0 added 3 commits March 25, 2026 20:21
Setup logic is already in beforeAll, async on describe is redundant.
Use `req.httpVersion === "1.0" || req.httpVersionMajor >= 2` instead of
`!== "1.1"` for clarity. Also restore `https.Server` in `_server` type union.
Copy link
Copy Markdown
Member

@pi0 pi0 left a comment

Choose a reason for hiding this comment

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

🚀

@pi0 pi0 merged commit 8ab2a3b into unjs:main Mar 25, 2026
5 checks passed
@pi0 pi0 mentioned this pull request Mar 25, 2026
1 task
@SukkaW SukkaW deleted the http2-support branch March 27, 2026 03:50
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.

3 participants