-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathproxy.go
More file actions
175 lines (162 loc) · 5.48 KB
/
Copy pathproxy.go
File metadata and controls
175 lines (162 loc) · 5.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
package main
import (
"fmt"
"net/http"
"net/url"
"strings"
)
type proxyPurpose string
const (
proxyPurposeRelay proxyPurpose = "relay"
proxyPurposeRemoteControl proxyPurpose = "remote_control"
proxyPurposeOfficialAuth proxyPurpose = "official_auth"
proxyPurposeRealtime proxyPurpose = "realtime"
proxyPurposeModelCatalog proxyPurpose = "model_catalog"
proxyPurposeAudio proxyPurpose = "audio"
proxyPurposeVLM proxyPurpose = "vlm"
proxyPurposeStepwise proxyPurpose = "stepwise"
)
const defaultProxyNoProxy = "127.0.0.1,localhost,[::1]"
func parseConfiguredProxyURL(raw string) (*url.URL, error) {
raw = strings.TrimSpace(raw)
if raw == "" {
return nil, nil
}
parsed, err := url.Parse(raw)
if err != nil || parsed.Scheme == "" || parsed.Host == "" {
return nil, fmt.Errorf("代理地址无效:请输入带 http:// 或 https:// 的代理地址")
}
if parsed.Scheme != "http" && parsed.Scheme != "https" {
return nil, fmt.Errorf("代理地址无效:仅支持 http:// 或 https:// 代理 URL")
}
if parsed.User != nil && strings.TrimSpace(parsed.User.Username()) == "" {
return nil, fmt.Errorf("代理地址无效:代理用户名不能为空")
}
return parsed, nil
}
func proxyPurposeEnabled(settings backendSettings, purpose proxyPurpose) bool {
if !settings.ProxyEnabled || strings.TrimSpace(settings.ProxyURL) == "" {
return false
}
switch purpose {
case proxyPurposeRelay:
return settings.ProxyRelayEnabled
case proxyPurposeRemoteControl:
return settings.ProxyRemoteControlEnabled
case proxyPurposeOfficialAuth:
return settings.ProxyOfficialAuthEnabled
case proxyPurposeRealtime:
return settings.ProxyRealtimeEnabled
case proxyPurposeModelCatalog:
return settings.ProxyModelCatalogEnabled
case proxyPurposeAudio:
return settings.ProxyAudioEnabled
case proxyPurposeVLM:
return settings.ProxyVLMEnabled
case proxyPurposeStepwise:
return settings.ProxyStepwiseEnabled
default:
return false
}
}
func nativeDesktopProxyEnabled(settings backendSettings) bool {
return proxyPurposeEnabled(settings, proxyPurposeRemoteControl) ||
proxyPurposeEnabled(settings, proxyPurposeOfficialAuth) ||
proxyPurposeEnabled(settings, proxyPurposeRealtime)
}
func nativeDesktopProxyURL(settings backendSettings) (*url.URL, error) {
for _, purpose := range []proxyPurpose{proxyPurposeRemoteControl, proxyPurposeOfficialAuth, proxyPurposeRealtime} {
if !proxyPurposeEnabled(settings, purpose) {
continue
}
return globalProxyURL(settings, purpose)
}
return nil, nil
}
func globalProxyURL(settings backendSettings, purpose proxyPurpose) (*url.URL, error) {
if !proxyPurposeEnabled(settings, purpose) {
return nil, nil
}
return parseConfiguredProxyURL(settings.ProxyURL)
}
func effectiveProxyURL(settings backendSettings, profile relayProfile, purpose proxyPurpose) (*url.URL, error) {
// Native ChatGPT traffic must use the explicit global policy. Relay/VLM
// requests preserve the profile proxy as the first choice for compatibility.
if purpose == proxyPurposeRemoteControl || purpose == proxyPurposeOfficialAuth {
return globalProxyURL(settings, purpose)
}
if purpose == proxyPurposeRealtime {
if proxyURL, err := globalProxyURL(settings, purpose); err != nil || proxyURL != nil {
return proxyURL, err
}
return relayProfileProxyURL(profile)
}
if profileURL, err := relayProfileProxyURL(profile); err != nil || profileURL != nil {
return profileURL, err
}
return globalProxyURL(settings, purpose)
}
func proxyHTTPClient(settings backendSettings, profile relayProfile, purpose proxyPurpose) (*http.Client, error) {
proxyURL, err := effectiveProxyURL(settings, profile, purpose)
if err != nil {
return nil, err
}
if proxyURL == nil {
return http.DefaultClient, nil
}
baseTransport, ok := http.DefaultTransport.(*http.Transport)
if !ok {
return &http.Client{Transport: &http.Transport{Proxy: http.ProxyURL(proxyURL)}}, nil
}
transport := baseTransport.Clone()
transport.Proxy = http.ProxyURL(proxyURL)
return &http.Client{Transport: transport}, nil
}
func proxyEnvironmentForSettings(settings backendSettings) []string {
if !settings.ProxyEnabled || strings.TrimSpace(settings.ProxyURL) == "" {
return nil
}
if _, err := parseConfiguredProxyURL(settings.ProxyURL); err != nil {
appendDiagnosticLog("proxy.invalid_configuration", map[string]any{"error": err.Error()})
return nil
}
noProxy := strings.TrimSpace(settings.ProxyNoProxy)
if noProxy == "" {
noProxy = defaultProxyNoProxy
}
value := strings.TrimSpace(settings.ProxyURL)
return []string{
"HTTP_PROXY=" + value,
"HTTPS_PROXY=" + value,
"ALL_PROXY=" + value,
"http_proxy=" + value,
"https_proxy=" + value,
"all_proxy=" + value,
"NO_PROXY=" + noProxy,
"no_proxy=" + noProxy,
}
}
func proxyChromiumArguments(settings backendSettings) []string {
if !nativeDesktopProxyEnabled(settings) {
return nil
}
proxyURL, err := nativeDesktopProxyURL(settings)
if err != nil || proxyURL == nil {
return nil
}
bypass := strings.TrimSpace(settings.ProxyNoProxy)
if bypass == "" {
bypass = defaultProxyNoProxy
}
bypass = strings.NewReplacer(",", ";", " ", "").Replace(bypass)
args := []string{
"--proxy-server=" + proxyURL.String(),
"--proxy-bypass-list=" + bypass,
}
if proxyPurposeEnabled(settings, proxyPurposeRealtime) {
// Advanced Voice uses WebRTC. Without this flag Chromium may send
// media candidates over direct UDP even when signaling uses the proxy.
args = append(args, "--force-webrtc-ip-handling-policy=disable_non_proxied_udp")
}
return args
}