Skip to content

Fix TLS/HTTP2 connection failure with IP addresses#28717

Open
robobun wants to merge 3 commits intomainfrom
farm/939d2987/fix-tls-ip-servername
Open

Fix TLS/HTTP2 connection failure with IP addresses#28717
robobun wants to merge 3 commits intomainfrom
farm/939d2987/fix-tls-ip-servername

Conversation

@robobun
Copy link
Copy Markdown
Collaborator

@robobun robobun commented Mar 31, 2026

Problem

http2.connect and tls.connect fail with UNABLE_TO_VERIFY_LEAF_SIGNATURE when connecting to an IP address with servername: '' or without an explicit servername. Node.js handles both cases correctly.

const http2 = require('http2');
const session = http2.connect('https://1.1.1.1', { servername: '' });
// Bun: UNABLE_TO_VERIFY_LEAF_SIGNATURE
// Node: connects successfully

Root Cause

Two bugs:

  1. tls.ts: The [buntls] method used || to compute serverName:

    serverName: this.servername || host || "localhost"

    || treats '' as falsy, so an explicit servername: '' falls through to the host IP. Per RFC 6066, IP addresses must not be sent as SNI. Changed to ?? with a net.isIP() guard so '' is preserved and IP hosts default to '' (no SNI).

  2. net.ts: lookupAndConnect returns early for IP hosts without setting self._host. A second [buntls] call in internalConnect then uses self._host (undefined), overriding the correct serverName with 'localhost'.

Fix

  • tls.ts: serverName: this.servername ?? (net.isIP(host) ? "" : (host || "localhost")) — preserves empty string, auto-detects IP hosts
  • net.ts: Set self._host = host before the early return for IP addresses

Fixes #28716

When servername is '' or unset with an IP host, the serverName sent to
the native TLS layer was incorrectly computed:

1. tls.ts used || which treats '' as falsy, falling through to the host
   IP. Per RFC 6066, IPs must not be sent as SNI. Changed to ?? with a
   net.isIP() guard so '' is preserved and IP hosts default to '' (no SNI).

2. net.ts lookupAndConnect returned early for IP hosts without setting
   self._host, causing a second buntls call in internalConnect to use
   undefined as host and override the correct serverName with 'localhost'.

Fixes #28716
Copy link
Copy Markdown
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

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

⚠️ Code review skipped — your organization's overage spend limit has been reached.

Code review is billed via overage credits. To resume reviews, an organization admin can raise the monthly limit at claude.ai/admin-settings/claude-code.

Once credits are available, push a new commit or reopen this pull request to trigger a review.

@robobun
Copy link
Copy Markdown
Collaborator Author

robobun commented Mar 31, 2026

Updated 7:20 AM PT - Mar 31st, 2026

@robobun, your commit 0d5daf1 has 3 failures in Build #43028 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 28717

That installs a local version of the PR into your bun-28717 executable, so you can run:

bun-28717 --bun

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 31, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4ac9ff1c-7bee-41a5-9ff2-868fd8134630

📥 Commits

Reviewing files that changed from the base of the PR and between f3d14e4 and 0d5daf1.

📒 Files selected for processing (1)
  • test/regression/issue/28716.test.ts

Walkthrough

Sets the socket's internal host for IP short-circuits and changes TLS serverName fallbacks to treat IP hosts as empty server names; adds regression tests validating TLS/HTTP2 connections to an IP with empty or omitted servername.

Changes

Cohort / File(s) Summary
Net module host assignment
src/js/node/net.ts
Sets self._host = host when options.host is an IP before calling internalConnect on next tick, ensuring _host is populated for IP connection flow.
TLS serverName fallback logic
src/js/node/tls.ts
Replaces previous fallback with `this.servername ?? (net.isIP(host) ? "" : (host
Regression tests
test/regression/issue/28716.test.ts
Adds three tests that spawn child processes to verify successful TLS and HTTP/2 connections to https://1.1.1.1 with servername: '' and with no servername, asserting no certificate verification errors and expected success outputs.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Fix TLS/HTTP2 connection failure with IP addresses' clearly and specifically describes the main change in the PR.
Description check ✅ Passed The description covers the problem, root cause, and fix sections comprehensively. It follows the template structure with detailed technical explanations and code examples.
Linked Issues check ✅ Passed The PR directly addresses issue #28716 by fixing both the tls.ts serverName computation and net.ts _host assignment, with tests verifying successful IP-address connections.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the linked issue: modifications to tls.ts and net.ts handle the IP/servername bug, and the regression test validates the fix.

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


Comment @coderabbitai help to get the list of available commands and usage tips.

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@test/regression/issue/28716.test.ts`:
- Around line 34-36: Move the exitCode assertion to the end of the first test so
stdout/stderr checks run first; specifically, in the block containing
expect(stderr).not.toContain("UNABLE_TO_VERIFY_LEAF_SIGNATURE"),
expect(exitCode).toBe(0), and const origins = JSON.parse(stdout.trim()),
relocate the expect(exitCode).toBe(0) after parsing and asserting stdout (i.e.,
after const origins = JSON.parse(stdout.trim()) and any related assertions) to
follow subprocess-test convention and improve failure diagnostics.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 22654877-be09-486b-87e6-18fe827af137

📥 Commits

Reviewing files that changed from the base of the PR and between 5c59842 and f3d14e4.

📒 Files selected for processing (3)
  • src/js/node/net.ts
  • src/js/node/tls.ts
  • test/regression/issue/28716.test.ts

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

http2.connect fails with IP address due to certificate verification (UNABLE_TO_VERIFY_LEAF_SIGNATURE)

1 participant