mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-24 12:44:18 +08:00
feat(proxy): 添加本地代理请求覆盖功能,支持自定义请求头和请求体 (#4589)
* feat(proxy): 添加本地代理请求覆盖功能,支持自定义请求头和请求体 * fix(proxy): harden local request overrides validation * feat(proxy): 添加受保护的本地代理请求头名称验证功能 * fix(i18n): 更新本地代理请求覆盖的错误提示信息格式 --------- Co-authored-by: jason.mei <jason.mei@ucloud.cn>
This commit is contained in:
@@ -95,6 +95,10 @@ const renderCopilotForm = (overrides: Partial<ClaudeFormFieldsProps> = {}) => {
|
||||
onFullUrlChange: vi.fn(),
|
||||
customUserAgent: "",
|
||||
onCustomUserAgentChange: vi.fn(),
|
||||
localProxyHeadersOverride: "",
|
||||
onLocalProxyHeadersOverrideChange: vi.fn(),
|
||||
localProxyBodyOverride: "",
|
||||
onLocalProxyBodyOverrideChange: vi.fn(),
|
||||
...overrides,
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
buildLocalProxyRequestOverrides,
|
||||
isProtectedLocalProxyHeaderName,
|
||||
isValidHttpHeaderName,
|
||||
isValidHttpHeaderValue,
|
||||
parseBodyOverrideJson,
|
||||
parseHeaderOverrideJson,
|
||||
parseRequestOverrideJson,
|
||||
} from "@/lib/requestOverrides";
|
||||
|
||||
describe("requestOverrides", () => {
|
||||
it("treats empty JSON fields as unset", () => {
|
||||
expect(buildLocalProxyRequestOverrides("", " ")).toEqual({});
|
||||
});
|
||||
|
||||
it("parses header and body override objects", () => {
|
||||
expect(
|
||||
buildLocalProxyRequestOverrides(
|
||||
'{ "X-Test": "ok" }',
|
||||
'{ "temperature": 0.2 }',
|
||||
),
|
||||
).toEqual({
|
||||
overrides: {
|
||||
headers: { "x-test": "ok" },
|
||||
body: { temperature: 0.2 },
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects non-object body overrides", () => {
|
||||
expect(parseRequestOverrideJson("[]").error).toBeTruthy();
|
||||
});
|
||||
|
||||
it("rejects non-string header values", () => {
|
||||
expect(parseHeaderOverrideJson('{ "X-Test": 1 }').error).toBeTruthy();
|
||||
});
|
||||
|
||||
it("rejects invalid header names", () => {
|
||||
expect(isValidHttpHeaderName("X-Test")).toBe(true);
|
||||
expect(isValidHttpHeaderName("X Foo")).toBe(false);
|
||||
expect(isValidHttpHeaderName("Authorization:")).toBe(false);
|
||||
expect(parseHeaderOverrideJson('{ "X Foo": "bar" }').error).toBeTruthy();
|
||||
});
|
||||
|
||||
it("rejects duplicate header names after case normalization", () => {
|
||||
expect(
|
||||
parseHeaderOverrideJson('{ "X-Foo": "a", "x-foo": "b" }').error,
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it("rejects protected proxy-managed header names", () => {
|
||||
expect(isProtectedLocalProxyHeaderName("Content-Type")).toBe(true);
|
||||
expect(isProtectedLocalProxyHeaderName("authorization")).toBe(true);
|
||||
expect(isProtectedLocalProxyHeaderName("X-Test")).toBe(false);
|
||||
expect(
|
||||
parseHeaderOverrideJson('{ "content-type": "text/plain" }').error,
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
parseHeaderOverrideJson('{ "Authorization": "Bearer x" }').error,
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it("matches backend header value control-character rule", () => {
|
||||
expect(isValidHttpHeaderValue("hello\tworld")).toBe(true);
|
||||
expect(isValidHttpHeaderValue("hello\nworld")).toBe(false);
|
||||
});
|
||||
|
||||
it("rejects stream in body overrides", () => {
|
||||
expect(parseBodyOverrideJson('{ "stream": true }').error).toBeTruthy();
|
||||
expect(
|
||||
buildLocalProxyRequestOverrides("", '{ "stream": false }').error,
|
||||
).toBeTruthy();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user