mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-10-05 23:31:05 +08:00
- Add context helpers to manage execution-scoped proxy overrides - Prioritize request proxy over credential and global proxy settings across HTTP and uTLS clients - Propagate request proxy in conductor execution and plugin host adapters - Isolate Codex WebSocket connection reuse by proxy endpoint Closes: #6013
121 lines
4.0 KiB
Go
121 lines
4.0 KiB
Go
package helps
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"testing"
|
|
|
|
"github.com/router-for-me/CLIProxyAPI/v7/internal/config"
|
|
cliproxyauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth"
|
|
coreexecutor "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/executor"
|
|
sdkconfig "github.com/router-for-me/CLIProxyAPI/v7/sdk/config"
|
|
)
|
|
|
|
func TestNewProxyAwareHTTPClientRequestProxyOverridesAuthAndGlobal(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx := coreexecutor.WithRequestProxyURL(context.Background(), "http://request-proxy.example:8081")
|
|
client := NewProxyAwareHTTPClient(
|
|
ctx,
|
|
&config.Config{SDKConfig: sdkconfig.SDKConfig{ProxyURL: "http://global-proxy.example.com:8080"}},
|
|
&cliproxyauth.Auth{ProxyURL: "http://auth-proxy.example:8080"},
|
|
0,
|
|
)
|
|
transport, ok := client.Transport.(*http.Transport)
|
|
if !ok || transport.Proxy == nil {
|
|
t.Fatalf("transport = %#v, want request proxy", client.Transport)
|
|
}
|
|
req, errReq := http.NewRequest(http.MethodGet, "https://upstream.example/v1", nil)
|
|
if errReq != nil {
|
|
t.Fatalf("request: %v", errReq)
|
|
}
|
|
proxyURL, errProxy := transport.Proxy(req)
|
|
if errProxy != nil {
|
|
t.Fatalf("proxy: %v", errProxy)
|
|
}
|
|
if proxyURL == nil || proxyURL.String() != "http://request-proxy.example:8081" {
|
|
t.Fatalf("proxy URL = %v, want request proxy", proxyURL)
|
|
}
|
|
|
|
refreshCtx := coreexecutor.WithoutRequestProxyURL(ctx)
|
|
refreshClient := NewProxyAwareHTTPClient(
|
|
refreshCtx,
|
|
&config.Config{SDKConfig: sdkconfig.SDKConfig{ProxyURL: "http://global-proxy.example.com:8080"}},
|
|
&cliproxyauth.Auth{ProxyURL: "http://auth-proxy.example:8080"},
|
|
0,
|
|
)
|
|
refreshTransport, ok := refreshClient.Transport.(*http.Transport)
|
|
if !ok || refreshTransport.Proxy == nil {
|
|
t.Fatalf("refresh transport = %#v, want auth proxy", refreshClient.Transport)
|
|
}
|
|
refreshProxy, errRefresh := refreshTransport.Proxy(req)
|
|
if errRefresh != nil {
|
|
t.Fatalf("refresh proxy: %v", errRefresh)
|
|
}
|
|
if refreshProxy == nil || refreshProxy.String() != "http://auth-proxy.example:8080" {
|
|
t.Fatalf("refresh proxy URL = %v, want auth proxy", refreshProxy)
|
|
}
|
|
}
|
|
|
|
func TestNewProxyAwareHTTPClientDirectBypassesGlobalProxy(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
client := NewProxyAwareHTTPClient(
|
|
context.Background(),
|
|
&config.Config{SDKConfig: sdkconfig.SDKConfig{ProxyURL: "http://global-proxy.example.com:8080"}},
|
|
&cliproxyauth.Auth{ProxyURL: "direct"},
|
|
0,
|
|
)
|
|
|
|
transport, ok := client.Transport.(*http.Transport)
|
|
if !ok {
|
|
t.Fatalf("transport type = %T, want *http.Transport", client.Transport)
|
|
}
|
|
if transport.Proxy != nil {
|
|
t.Fatal("expected direct transport to disable proxy function")
|
|
}
|
|
}
|
|
|
|
func TestNewDevinHTTPClient_ReusesTransportFromContext(t *testing.T) {
|
|
baseTransport := &http.Transport{}
|
|
ctx := context.WithValue(context.Background(), "cliproxy.roundtripper", baseTransport)
|
|
|
|
c1 := NewDevinHTTPClient(ctx, nil, nil, 0)
|
|
c2 := NewDevinHTTPClient(ctx, nil, nil, 0)
|
|
|
|
if c1.Transport != c2.Transport {
|
|
t.Errorf("expected c1.Transport == c2.Transport across requests, got different pointers %p vs %p", c1.Transport, c2.Transport)
|
|
}
|
|
|
|
tr, ok := c1.Transport.(*http.Transport)
|
|
if !ok {
|
|
t.Fatalf("expected *http.Transport, got %T", c1.Transport)
|
|
}
|
|
if !tr.DisableCompression {
|
|
t.Error("expected DisableCompression = true")
|
|
}
|
|
}
|
|
|
|
func TestNewDevinHTTPClient_NonStandardRoundTripperDisablesGzip(t *testing.T) {
|
|
var seenEncoding string
|
|
customRT := roundTripperFunc(func(req *http.Request) (*http.Response, error) {
|
|
seenEncoding = req.Header.Get("Accept-Encoding")
|
|
return &http.Response{StatusCode: 200}, nil
|
|
})
|
|
ctx := context.WithValue(context.Background(), "cliproxy.roundtripper", customRT)
|
|
|
|
c := NewDevinHTTPClient(ctx, nil, nil, 0)
|
|
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, "http://example.invalid", nil)
|
|
_, _ = c.Transport.RoundTrip(req)
|
|
|
|
if seenEncoding != "identity" {
|
|
t.Errorf("expected Accept-Encoding: identity, got %q", seenEncoding)
|
|
}
|
|
}
|
|
|
|
type roundTripperFunc func(req *http.Request) (*http.Response, error)
|
|
|
|
func (f roundTripperFunc) RoundTrip(req *http.Request) (*http.Response, error) {
|
|
return f(req)
|
|
}
|