@@ -10,12 +10,15 @@ import (
10
10
"net/http"
11
11
"net/url"
12
12
"os"
13
+ "os/signal"
13
14
"path"
14
15
"strings"
15
16
"sync"
16
17
"sync/atomic"
18
+ "syscall"
17
19
"time"
18
20
21
+ "golang.org/x/net/http2"
19
22
"golang.org/x/net/proxy"
20
23
)
21
24
@@ -101,6 +104,7 @@ func createProxyClient(proxyURL string) (*http.Client, error) {
101
104
return nil , err
102
105
}
103
106
107
+ var transport * http.Transport
104
108
switch urlParsed .Scheme {
105
109
case "socks5" , "socks5h" :
106
110
auth := & proxy.Auth {
@@ -118,25 +122,33 @@ func createProxyClient(proxyURL string) (*http.Client, error) {
118
122
return nil , err
119
123
}
120
124
121
- transport : = & http.Transport {
125
+ transport = & http.Transport {
122
126
DialContext : func (ctx context.Context , network , addr string ) (net.Conn , error ) {
123
127
return dialer .Dial (network , addr )
124
128
},
129
+ MaxIdleConns : 100 ,
130
+ MaxIdleConnsPerHost : 20 ,
131
+ IdleConnTimeout : 90 * time .Second ,
125
132
}
126
- return & http.Client {Transport : transport }, nil
127
133
128
134
case "http" , "https" :
129
- transport : = & http.Transport {
135
+ transport = & http.Transport {
130
136
Proxy : http .ProxyURL (urlParsed ),
131
137
DialContext : (& net.Dialer {
132
138
Timeout : 30 * time .Second ,
133
139
KeepAlive : 30 * time .Second ,
134
140
}).DialContext ,
135
141
}
136
- return & http.Client {Transport : transport }, nil
142
+ default :
143
+ return nil , fmt .Errorf ("unsupported proxy scheme: %s" , urlParsed .Scheme )
137
144
}
138
145
139
- return nil , fmt .Errorf ("unsupported proxy scheme: %s" , urlParsed .Scheme )
146
+ // Enable HTTP/2 support for the transport
147
+ if err := http2 .ConfigureTransport (transport ); err != nil {
148
+ return nil , fmt .Errorf ("failed to configure HTTP/2: %v" , err )
149
+ }
150
+
151
+ return & http.Client {Transport : transport , Timeout : 30 * time .Second }, nil
140
152
}
141
153
142
154
func NewProxyServer (configPath string ) (* ProxyServer , error ) {
@@ -150,12 +162,33 @@ func NewProxyServer(configPath string) (*ProxyServer, error) {
150
162
return nil , fmt .Errorf ("failed to parse config: %v" , err )
151
163
}
152
164
165
+ // Validate site configurations
166
+ for siteName , siteConfig := range config .Sites {
167
+ allowedProxyTypes := map [string ]bool {"header" : true , "query" : true , "path" : true , "direct" : true }
168
+ if ! allowedProxyTypes [siteConfig .ProxyType ] {
169
+ return nil , fmt .Errorf ("invalid PROXY_TYPE %s for site %s" , siteConfig .ProxyType , siteName )
170
+ }
171
+
172
+ if siteConfig .ProxyType == "direct" {
173
+ for _ , keyLimit := range siteConfig .Values {
174
+ for key := range keyLimit {
175
+ if _ , err := url .Parse (key ); err != nil {
176
+ return nil , fmt .Errorf ("invalid URL in direct proxy VALUES for site %s: %v" , siteName , key )
177
+ }
178
+ }
179
+ }
180
+ } else if (siteConfig .ProxyType == "header" || siteConfig .ProxyType == "query" ) && siteConfig .Key == "" {
181
+ return nil , fmt .Errorf ("PROXY_TYPE %s requires KEY for site %s" , siteConfig .ProxyType , siteName )
182
+ }
183
+ }
184
+
153
185
server := & ProxyServer {
154
186
config : config ,
155
187
clients : make ([]ClientWrapper , 0 ),
156
188
sites : make (map [string ]* SiteHandler ),
157
189
}
158
190
191
+ // Initialize site handlers
159
192
for siteName , siteConfig := range config .Sites {
160
193
handler := & SiteHandler {
161
194
config : siteConfig ,
@@ -176,13 +209,19 @@ func NewProxyServer(configPath string) (*ProxyServer, error) {
176
209
server .sites [siteName ] = handler
177
210
}
178
211
212
+ // Initialize direct client if enabled
179
213
if config .GlobalSettings .DirectAccess {
214
+ transport := & http.Transport {}
215
+ if err := http2 .ConfigureTransport (transport ); err != nil {
216
+ log .Printf ("Failed to configure HTTP/2 for direct client: %v" , err )
217
+ }
180
218
server .clients = append (server .clients , ClientWrapper {
181
- Client : & http.Client {Timeout : 30 * time .Second },
219
+ Client : & http.Client {Transport : transport , Timeout : 30 * time .Second },
182
220
IsDirect : true ,
183
221
})
184
222
}
185
223
224
+ // Initialize proxy clients
186
225
for _ , proxyURL := range config .GlobalSettings .Proxies {
187
226
client , err := createProxyClient (proxyURL )
188
227
if err != nil {
@@ -195,6 +234,7 @@ func NewProxyServer(configPath string) (*ProxyServer, error) {
195
234
})
196
235
}
197
236
237
+ // Ensure at least one client is available
198
238
if len (server .clients ) == 0 {
199
239
return nil , fmt .Errorf ("no working clients available" )
200
240
}
@@ -237,7 +277,6 @@ func (s *ProxyServer) handleRequest(w http.ResponseWriter, r *http.Request) {
237
277
return
238
278
}
239
279
240
- // Handle base path
241
280
basePath := s .config .GlobalSettings .BasePath
242
281
if basePath == "" {
243
282
basePath = "/proxy"
@@ -249,7 +288,6 @@ func (s *ProxyServer) handleRequest(w http.ResponseWriter, r *http.Request) {
249
288
return
250
289
}
251
290
252
- // Process remaining path after base path
253
291
remainingPath := strings .TrimPrefix (r .URL .Path , basePath )
254
292
remainingPath = strings .Trim (remainingPath , "/" )
255
293
@@ -344,7 +382,6 @@ func (s *ProxyServer) handleRequest(w http.ResponseWriter, r *http.Request) {
344
382
q .Set (siteHandler .config .Key , apiKey )
345
383
outReq .URL .RawQuery = q .Encode ()
346
384
case "path" :
347
- // Already handled in path construction
348
385
default :
349
386
log .Printf ("Unknown PROXY_TYPE: %s" , siteHandler .config .ProxyType )
350
387
}
@@ -375,8 +412,8 @@ func (s *ProxyServer) handleRequest(w http.ResponseWriter, r *http.Request) {
375
412
outReq .Header .Del (header )
376
413
}
377
414
378
- outReq .Header .Set ("Accept" , "application/json" )
379
- outReq .Header .Set ("Content-Type" , "application/json" )
415
+ // outReq.Header.Set("Accept", "application/json")
416
+ // outReq.Header.Set("Content-Type", "application/json")
380
417
381
418
resp , err := client .Client .Do (outReq )
382
419
if err != nil {
@@ -397,15 +434,11 @@ func (s *ProxyServer) handleRequest(w http.ResponseWriter, r *http.Request) {
397
434
}
398
435
}
399
436
400
- bodyBytes , err := io .ReadAll (resp .Body )
437
+ w .WriteHeader (resp .StatusCode )
438
+ _ , err = io .Copy (w , resp .Body )
401
439
if err != nil {
402
- log .Printf ("Failed to read response body: %v" , err )
403
- http .Error (w , "Failed to read response" , http .StatusInternalServerError )
404
- return
440
+ log .Printf ("Failed to stream response: %v" , err )
405
441
}
406
-
407
- w .WriteHeader (resp .StatusCode )
408
- _ , _ = w .Write (bodyBytes )
409
442
}
410
443
411
444
func main () {
@@ -422,7 +455,25 @@ func main() {
422
455
http .HandleFunc ("/" , server .handleRequest )
423
456
424
457
log .Printf ("Starting proxy server on :3000" )
425
- if err := http .ListenAndServe (":3000" , nil ); err != nil {
426
- log .Fatalf ("Server error: %v" , err )
458
+
459
+ srv := & http.Server {
460
+ Addr : ":3000" ,
461
+ Handler : nil ,
462
+ }
463
+
464
+ go func () {
465
+ if err := srv .ListenAndServe (); err != nil {
466
+ log .Fatalf ("Server error: %v" , err )
467
+ }
468
+ }()
469
+
470
+ stop := make (chan os.Signal , 1 )
471
+ signal .Notify (stop , os .Interrupt , syscall .SIGTERM )
472
+ <- stop
473
+
474
+ ctx , cancel := context .WithTimeout (context .Background (), 5 * time .Second )
475
+ defer cancel ()
476
+ if err := srv .Shutdown (ctx ); err != nil {
477
+ log .Printf ("Server shutdown error: %v" , err )
427
478
}
428
479
}
0 commit comments