From d6f720d0e6f9c94acc7fd5e212a92d59bcef2648 Mon Sep 17 00:00:00 2001 From: Thomas Date: Thu, 27 Aug 2026 22:00:23 +0200 Subject: [PATCH] Revert "feat(http): set custom User-Agent on outbound integration requests (#6606)" (#6722) This reverts commit ddbf05524b77d2c8b158755eaa36e9d9a5fab9fd. --- .../core/src/infrastructure/http/index.ts | 1 - .../core/src/infrastructure/http/request.ts | 29 ++--- .../test/infrastructure/http/request.spec.ts | 120 ------------------ 3 files changed, 10 insertions(+), 140 deletions(-) delete mode 100644 packages/core/src/test/infrastructure/http/request.spec.ts diff --git a/packages/core/src/infrastructure/http/index.ts b/packages/core/src/infrastructure/http/index.ts index 116cf5971..572388f2a 100644 --- a/packages/core/src/infrastructure/http/index.ts +++ b/packages/core/src/infrastructure/http/index.ts @@ -5,5 +5,4 @@ export { createCustomCheckServerIdentity, createHttpsAgentAsync, fetchWithTrustedCertificatesAsync, - mergeHeadersWithUserAgent, } from "./request"; diff --git a/packages/core/src/infrastructure/http/request.ts b/packages/core/src/infrastructure/http/request.ts index 0b06b2725..44e0aa629 100644 --- a/packages/core/src/infrastructure/http/request.ts +++ b/packages/core/src/infrastructure/http/request.ts @@ -13,19 +13,6 @@ import { UndiciHttpAgent } from "@homarr/core/infrastructure/http"; import type { TrustedCertificateHostname } from "../certificates/hostnames"; import { withTimeoutAsync } from "./timeout"; - -import packageJson from "../../../../../package.json"; - -export const getDefaultUserAgent = () => `Homarr/${packageJson.version} (+https://homarr.dev)`; - -export const mergeHeadersWithUserAgent = (headers: unknown): Headers => { - const mergedHeaders = new Headers(headers as HeadersInit); - if (!mergedHeaders.has("user-agent")) { - mergedHeaders.set("user-agent", getDefaultUserAgent()); - } - return mergedHeaders; -}; - export const createCustomCheckServerIdentity = ( trustedHostnames: TrustedCertificateHostname[], ): typeof checkServerIdentity => { @@ -85,18 +72,22 @@ export const fetchWithTrustedCertificatesAsync = async ( undefined, options?.bodyTimeout !== undefined ? { bodyTimeout: options.bodyTimeout } : undefined, )); - const buildFetchOptions = (fetchOptions: Omit) => ({ - ...fetchOptions, - headers: mergeHeadersWithUserAgent(fetchOptions.headers) as unknown as RequestInit["headers"], - }); if (options?.timeout) { const { bodyTimeout: _bodyTimeout, dispatcher: _dispatcher, ...fetchOptions } = options; return await withTimeoutAsync( - async (signal) => fetch(url, { ...buildFetchOptions(fetchOptions), signal, dispatcher: agent }), + async (signal) => + fetch(url, { + ...fetchOptions, + signal, + dispatcher: agent, + }), options.timeout, ); } const { bodyTimeout: _bodyTimeout, dispatcher: _dispatcher, ...fetchOptions } = options ?? {}; - return fetch(url, { ...buildFetchOptions(fetchOptions), dispatcher: agent }); + return fetch(url, { + ...fetchOptions, + dispatcher: agent, + }); }; diff --git a/packages/core/src/test/infrastructure/http/request.spec.ts b/packages/core/src/test/infrastructure/http/request.spec.ts deleted file mode 100644 index e1980862d..000000000 --- a/packages/core/src/test/infrastructure/http/request.spec.ts +++ /dev/null @@ -1,120 +0,0 @@ -// @vitest-environment node - -import { createServer } from "node:http"; -import type { Server } from "node:http"; -import type { AddressInfo } from "node:net"; -import type { RequestInit } from "undici"; - -import { describe, expect, test, vi } from "vitest"; - -import { fetchWithTrustedCertificatesAsync, mergeHeadersWithUserAgent } from "@homarr/core/infrastructure/http"; - -vi.mock("@homarr/core/infrastructure/certificates", () => ({ - getAllTrustedCertificatesAsync: vi.fn(async () => []), - getTrustedCertificateHostnamesAsync: vi.fn(async () => []), -})); - -describe("mergeHeadersWithUserAgent", () => { - test("should keep existing headers when given a Headers instance", () => { - // Arrange - const headers = new Headers({ "x-api-key": "some-api-key", Accept: "application/json" }); - - // Act - const merged = mergeHeadersWithUserAgent(headers); - - // Assert - expect(merged.get("x-api-key")).toBe("some-api-key"); - expect(merged.get("accept")).toBe("application/json"); - expect(merged.get("user-agent")).toContain("Homarr/"); - }); - - test("should keep existing headers when given a record", () => { - // Arrange - const headers = { "x-api-key": "some-api-key", Accept: "application/json" }; - - // Act - const merged = mergeHeadersWithUserAgent(headers); - - // Assert - expect(merged.get("x-api-key")).toBe("some-api-key"); - expect(merged.get("accept")).toBe("application/json"); - expect(merged.get("user-agent")).toContain("Homarr/"); - }); - - test("should keep existing headers when given a tuple array", () => { - // Arrange - const headers: [string, string][] = [ - ["x-api-key", "some-api-key"], - ["Accept", "application/json"], - ]; - - // Act - const merged = mergeHeadersWithUserAgent(headers); - - // Assert - expect(merged.get("x-api-key")).toBe("some-api-key"); - expect(merged.get("accept")).toBe("application/json"); - expect(merged.get("user-agent")).toContain("Homarr/"); - }); - - test("should not override an existing user-agent in any casing", () => { - // Arrange - const headers = new Headers({ "USER-AGENT": "custom-agent" }); - - // Act - const merged = mergeHeadersWithUserAgent(headers); - - // Assert - expect(merged.get("user-agent")).toBe("custom-agent"); - }); - - test("should fall back to the default user agent when no headers are provided", () => { - // Act - const merged = mergeHeadersWithUserAgent(undefined); - - // Assert - expect(merged.get("user-agent")).toContain("Homarr/"); - }); -}); - -const startCaptureServerAsync = async (): Promise<{ - server: Server; - getReceivedHeaders: () => Record; - port: number; -}> => { - let receivedHeaders: Record = {}; - const server = createServer((req, res) => { - receivedHeaders = req.headers; - res.writeHead(200, { "Content-Type": "application/json" }); - res.end("{}"); - }); - await new Promise((resolve) => server.listen(0, resolve)); - const port = (server.address() as AddressInfo).port; - return { - server, - port, - getReceivedHeaders: () => receivedHeaders, - }; -}; - -describe("fetchWithTrustedCertificatesAsync", () => { - test("should deliver a Headers instance with all headers to the remote server", async () => { - // Arrange - the @immich/sdk hands over a Headers instance after merging its defaults - const { server, port, getReceivedHeaders } = await startCaptureServerAsync(); - const headers = new Headers({ "x-api-key": "immich-api-key", Accept: "application/json" }); - - // Act - try { - const response = await fetchWithTrustedCertificatesAsync(`http://127.0.0.1:${port}/api/server/statistics`, { - headers: headers as unknown as RequestInit["headers"], - }); - - // Assert - expect(response.status).toBe(200); - expect(getReceivedHeaders()["x-api-key"]).toBe("immich-api-key"); - expect(getReceivedHeaders()["user-agent"]).toContain("Homarr/"); - } finally { - server.close(); - } - }); -});