Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion apps/server/src/binCli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { browserCommand } from "./cli/browser.ts";
import { connectCommand } from "./cli/connect.ts";
import { pairCommand } from "./cli/pair.ts";
import { hasCloudPublicConfig } from "./cloud/publicConfig.ts";
import { keepOutboundFetchOnHttp1 } from "./outboundHttp.ts";
import { extendConnectionAttemptTimeout, keepOutboundFetchOnHttp1 } from "./outboundHttp.ts";
import { sharedServerCommandFlags } from "./cli/config.ts";
import { projectCommand } from "./cli/project.ts";
import { runDefaultServerCommand, serveCommand, startCommand } from "./cli/server.ts";
Expand Down Expand Up @@ -95,6 +95,7 @@ export const makeCli = ({ cloudEnabled = hasCloudPublicConfig } = {}) =>
export const cli = makeCli();

export function runCli() {
extendConnectionAttemptTimeout();
keepOutboundFetchOnHttp1();
Command.run(cli, { version: packageJson.version }).pipe(
Effect.scoped,
Expand Down
22 changes: 21 additions & 1 deletion apps/server/src/outboundHttp.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ import { FetchHttpClient, HttpClient } from "effect/http";
import * as Undici from "undici";
import { afterAll, afterEach, beforeAll, describe, expect } from "vite-plus/test";

import { builtinFetchUsesHttp2, installHttp1FetchDispatcher } from "./outboundHttp.ts";
import {
builtinFetchUsesHttp2,
extendConnectionAttemptTimeout,
installHttp1FetchDispatcher,
} from "./outboundHttp.ts";

// Test-only self-signed P-256 certificate for 127.0.0.1, valid until 2126; not a
// secret. Node cannot mint X.509 certificates without a library, and a local
Expand Down Expand Up @@ -41,6 +45,7 @@ const server = NodeHttp2.createSecureServer(
let origin = "";
const defaultCaCertificates = NodeTls.getCACertificates("default");
const defaultDispatcher = Undici.getGlobalDispatcher();
const defaultAttemptTimeout = NodeNet.getDefaultAutoSelectFamilyAttemptTimeout();

beforeAll(async () => {
NodeTls.setDefaultCACertificates([...defaultCaCertificates, TEST_CERT]);
Expand All @@ -50,6 +55,7 @@ beforeAll(async () => {

afterEach(() => {
Undici.setGlobalDispatcher(defaultDispatcher);
NodeNet.setDefaultAutoSelectFamilyAttemptTimeout(defaultAttemptTimeout);
});

afterAll(async () => {
Expand Down Expand Up @@ -90,3 +96,17 @@ describe("installHttp1FetchDispatcher", () => {
}),
);
});

describe("extendConnectionAttemptTimeout", () => {
it("raises a default below the floor to 1000 ms", () => {
NodeNet.setDefaultAutoSelectFamilyAttemptTimeout(50);
extendConnectionAttemptTimeout();
expect(NodeNet.getDefaultAutoSelectFamilyAttemptTimeout()).toBe(1_000);
});

it("keeps a larger configured timeout", () => {
NodeNet.setDefaultAutoSelectFamilyAttemptTimeout(5_000);
extendConnectionAttemptTimeout();
expect(NodeNet.getDefaultAutoSelectFamilyAttemptTimeout()).toBe(5_000);
});
});
12 changes: 12 additions & 0 deletions apps/server/src/outboundHttp.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as NodeModule from "node:module";
import * as NodeNet from "node:net";

import type * as Undici from "undici";

Expand Down Expand Up @@ -41,3 +42,14 @@ export function installHttp1FetchDispatcher(): void {
export function keepOutboundFetchOnHttp1(): void {
if (builtinFetchUsesHttp2(process.versions.undici)) installHttp1FetchDispatcher();
}

/**
* Node's 250 ms Happy Eyeballs attempt timeout abandons a working-but-slow IPv4
* connect when IPv6 is advertised but unroutable, which broke the ACP registry
* fetch (#15039). A larger `--network-family-autoselection-attempt-timeout` still wins.
*/
export function extendConnectionAttemptTimeout(): void {
NodeNet.setDefaultAutoSelectFamilyAttemptTimeout(
Math.max(NodeNet.getDefaultAutoSelectFamilyAttemptTimeout(), 1_000),
);
}
Loading