|
|
@@ -59,6 +59,15 @@ function createCodexProvider(): Provider {
|
|
|
} as unknown as Provider;
|
|
|
}
|
|
|
|
|
|
+function createGeminiProvider(providerType: "gemini" | "gemini-cli"): Provider {
|
|
|
+ return {
|
|
|
+ providerType,
|
|
|
+ url: "https://generativelanguage.googleapis.com/v1beta",
|
|
|
+ key: "test-outbound-key",
|
|
|
+ preserveClientIp: false,
|
|
|
+ } as unknown as Provider;
|
|
|
+}
|
|
|
+
|
|
|
describe("ProxyForwarder - buildHeaders User-Agent resolution", () => {
|
|
|
it("应该优先使用过滤器修改的 user-agent(Codex provider)", () => {
|
|
|
const session = createSession({
|
|
|
@@ -147,3 +156,157 @@ describe("ProxyForwarder - buildHeaders User-Agent resolution", () => {
|
|
|
expect(resultHeaders.get("user-agent")).toBe("");
|
|
|
});
|
|
|
});
|
|
|
+
|
|
|
+describe("ProxyForwarder - buildGeminiHeaders headers passthrough", () => {
|
|
|
+ it("应该透传 user-agent,并覆盖上游 x-goog-api-key(API key 模式)", () => {
|
|
|
+ const session = createSession({
|
|
|
+ userAgent: "Original-UA/1.0",
|
|
|
+ headers: new Headers([
|
|
|
+ ["user-agent", "Original-UA/1.0"],
|
|
|
+ ["x-api-key", "proxy-user-key-should-not-leak"],
|
|
|
+ ["x-goog-api-key", "proxy-user-key-google-should-not-leak"],
|
|
|
+ ["authorization", "Bearer proxy-user-bearer-should-not-leak"],
|
|
|
+ ]),
|
|
|
+ });
|
|
|
+
|
|
|
+ const provider = createGeminiProvider("gemini");
|
|
|
+ const { buildGeminiHeaders } = ProxyForwarder as unknown as {
|
|
|
+ buildGeminiHeaders: (
|
|
|
+ session: ProxySession,
|
|
|
+ provider: Provider,
|
|
|
+ baseUrl: string,
|
|
|
+ accessToken: string,
|
|
|
+ isApiKey: boolean
|
|
|
+ ) => Headers;
|
|
|
+ };
|
|
|
+ const resultHeaders = buildGeminiHeaders(
|
|
|
+ session,
|
|
|
+ provider,
|
|
|
+ "https://generativelanguage.googleapis.com/v1beta",
|
|
|
+ "upstream-api-key",
|
|
|
+ true
|
|
|
+ );
|
|
|
+
|
|
|
+ expect(resultHeaders.get("user-agent")).toBe("Original-UA/1.0");
|
|
|
+ expect(resultHeaders.get("x-api-key")).toBeNull();
|
|
|
+ expect(resultHeaders.get("authorization")).toBeNull();
|
|
|
+ expect(resultHeaders.get("x-goog-api-key")).toBe("upstream-api-key");
|
|
|
+ expect(resultHeaders.get("accept-encoding")).toBe("identity");
|
|
|
+ expect(resultHeaders.get("content-type")).toBe("application/json");
|
|
|
+ expect(resultHeaders.get("host")).toBe("generativelanguage.googleapis.com");
|
|
|
+ });
|
|
|
+
|
|
|
+ it("应该允许过滤器修改 user-agent(Gemini provider)", () => {
|
|
|
+ const session = createSession({
|
|
|
+ userAgent: "Original-UA/1.0",
|
|
|
+ headers: new Headers([["user-agent", "Filtered-UA/2.0"]]),
|
|
|
+ });
|
|
|
+ (session as any).originalHeaders = new Headers([["user-agent", "Original-UA/1.0"]]);
|
|
|
+
|
|
|
+ const provider = createGeminiProvider("gemini");
|
|
|
+ const { buildGeminiHeaders } = ProxyForwarder as unknown as {
|
|
|
+ buildGeminiHeaders: (
|
|
|
+ session: ProxySession,
|
|
|
+ provider: Provider,
|
|
|
+ baseUrl: string,
|
|
|
+ accessToken: string,
|
|
|
+ isApiKey: boolean
|
|
|
+ ) => Headers;
|
|
|
+ };
|
|
|
+ const resultHeaders = buildGeminiHeaders(
|
|
|
+ session,
|
|
|
+ provider,
|
|
|
+ "https://generativelanguage.googleapis.com/v1beta",
|
|
|
+ "upstream-api-key",
|
|
|
+ true
|
|
|
+ );
|
|
|
+
|
|
|
+ expect(resultHeaders.get("user-agent")).toBe("Filtered-UA/2.0");
|
|
|
+ });
|
|
|
+
|
|
|
+ it("应该在过滤器删除 user-agent 时回退到原始 userAgent(Gemini provider)", () => {
|
|
|
+ const session = createSession({
|
|
|
+ userAgent: "Original-UA/1.0",
|
|
|
+ headers: new Headers(), // user-agent 被删除
|
|
|
+ });
|
|
|
+ (session as any).originalHeaders = new Headers([["user-agent", "Original-UA/1.0"]]);
|
|
|
+
|
|
|
+ const provider = createGeminiProvider("gemini");
|
|
|
+ const { buildGeminiHeaders } = ProxyForwarder as unknown as {
|
|
|
+ buildGeminiHeaders: (
|
|
|
+ session: ProxySession,
|
|
|
+ provider: Provider,
|
|
|
+ baseUrl: string,
|
|
|
+ accessToken: string,
|
|
|
+ isApiKey: boolean
|
|
|
+ ) => Headers;
|
|
|
+ };
|
|
|
+ const resultHeaders = buildGeminiHeaders(
|
|
|
+ session,
|
|
|
+ provider,
|
|
|
+ "https://generativelanguage.googleapis.com/v1beta",
|
|
|
+ "upstream-api-key",
|
|
|
+ true
|
|
|
+ );
|
|
|
+
|
|
|
+ expect(resultHeaders.get("user-agent")).toBe("Original-UA/1.0");
|
|
|
+ });
|
|
|
+
|
|
|
+ it("应该使用 Authorization Bearer(OAuth 模式)并移除 x-goog-api-key", () => {
|
|
|
+ const session = createSession({
|
|
|
+ userAgent: "Original-UA/1.0",
|
|
|
+ headers: new Headers([
|
|
|
+ ["user-agent", "Original-UA/1.0"],
|
|
|
+ ["x-goog-api-key", "proxy-user-key-google-should-not-leak"],
|
|
|
+ ]),
|
|
|
+ });
|
|
|
+
|
|
|
+ const provider = createGeminiProvider("gemini");
|
|
|
+ const { buildGeminiHeaders } = ProxyForwarder as unknown as {
|
|
|
+ buildGeminiHeaders: (
|
|
|
+ session: ProxySession,
|
|
|
+ provider: Provider,
|
|
|
+ baseUrl: string,
|
|
|
+ accessToken: string,
|
|
|
+ isApiKey: boolean
|
|
|
+ ) => Headers;
|
|
|
+ };
|
|
|
+ const resultHeaders = buildGeminiHeaders(
|
|
|
+ session,
|
|
|
+ provider,
|
|
|
+ "https://generativelanguage.googleapis.com/v1beta",
|
|
|
+ "upstream-oauth-token",
|
|
|
+ false
|
|
|
+ );
|
|
|
+
|
|
|
+ expect(resultHeaders.get("authorization")).toBe("Bearer upstream-oauth-token");
|
|
|
+ expect(resultHeaders.get("x-goog-api-key")).toBeNull();
|
|
|
+ });
|
|
|
+
|
|
|
+ it("gemini-cli 应该注入 x-goog-api-client 头", () => {
|
|
|
+ const session = createSession({
|
|
|
+ userAgent: "Original-UA/1.0",
|
|
|
+ headers: new Headers([["user-agent", "Original-UA/1.0"]]),
|
|
|
+ });
|
|
|
+
|
|
|
+ const provider = createGeminiProvider("gemini-cli");
|
|
|
+ const { buildGeminiHeaders } = ProxyForwarder as unknown as {
|
|
|
+ buildGeminiHeaders: (
|
|
|
+ session: ProxySession,
|
|
|
+ provider: Provider,
|
|
|
+ baseUrl: string,
|
|
|
+ accessToken: string,
|
|
|
+ isApiKey: boolean
|
|
|
+ ) => Headers;
|
|
|
+ };
|
|
|
+ const resultHeaders = buildGeminiHeaders(
|
|
|
+ session,
|
|
|
+ provider,
|
|
|
+ "https://cloudcode-pa.googleapis.com/v1internal",
|
|
|
+ "upstream-oauth-token",
|
|
|
+ false
|
|
|
+ );
|
|
|
+
|
|
|
+ expect(resultHeaders.get("x-goog-api-client")).toBe("GeminiCLI/1.0");
|
|
|
+ });
|
|
|
+});
|