77 "net"
88 "net/http"
99 "os"
10+ "slices"
1011 "strings"
1112 "sync"
1213 "time"
@@ -19,6 +20,7 @@ const (
1920 readTimeout time.Duration = 10 * time .Second
2021 writeTimeout time.Duration = 10 * time .Second
2122 timeout time.Duration = 10 * time .Second
23+ flushTimeout time.Duration = 10 * time .Millisecond
2224 kbSize int64 = 1000
2325)
2426
@@ -84,12 +86,40 @@ func isLocalAddress(addr string) bool {
8486
8587type proxyApp struct {
8688 httpServer * http.Server
87- sockServer * http.Client
89+ sockClient * http.Client
8890 httpClient * http.Client
8991 sockDialer proxy.Dialer
9092 logger * zerolog.Logger
9193}
9294
95+ func (p * proxyApp ) doReq (w http.ResponseWriter , r * http.Request , socks bool ) * http.Response {
96+ var (
97+ resp * http.Response
98+ err error
99+ msg string
100+ client * http.Client
101+ )
102+ if socks {
103+ client = p .sockClient
104+ msg = "Connection to SOCKS5 server failed"
105+ } else {
106+ client = p .httpClient
107+ msg = "Connection failed"
108+ }
109+ resp , err = client .Do (r )
110+ if err != nil {
111+ p .logger .Error ().Err (err ).Msg (msg )
112+ w .WriteHeader (http .StatusServiceUnavailable )
113+ return nil
114+ }
115+ if resp == nil {
116+ p .logger .Error ().Msg (msg )
117+ w .WriteHeader (http .StatusServiceUnavailable )
118+ return nil
119+ }
120+ return resp
121+ }
122+
93123func (p * proxyApp ) handleForward (w http.ResponseWriter , r * http.Request ) {
94124
95125 req , err := http .NewRequest (r .Method , r .URL .String (), r .Body )
@@ -106,33 +136,69 @@ func (p *proxyApp) handleForward(w http.ResponseWriter, r *http.Request) {
106136 appendHostToXForwardHeader (req .Header , clientIP )
107137 }
108138 var resp * http.Response
139+ var chunked bool
140+ p .httpClient .Timeout = timeout
141+ p .sockClient .Timeout = timeout
109142 if isLocalAddress (r .Host ) {
110- resp , err = p .httpClient .Do (req )
111- if err != nil {
112- p .logger .Error ().Err (err ).Msg ("Connection failed" )
113- w .WriteHeader (http .StatusServiceUnavailable )
114- return
115- }
143+ resp = p .doReq (w , req , false )
116144 if resp == nil {
117- p .logger .Error ().Err (err ).Msg ("Connection failed" )
118- w .WriteHeader (http .StatusServiceUnavailable )
119145 return
120146 }
121- } else {
122- resp , err = p .sockServer .Do (req )
123- if err != nil {
124- p .logger .Error ().Err (err ).Msg ("Connection to SOCKS5 server failed" )
125- w .WriteHeader (http .StatusServiceUnavailable )
126- return
147+ if slices .Contains (resp .TransferEncoding , "chunked" ) {
148+ chunked = true
149+ p .httpClient .Timeout = 0
150+ p .sockClient .Timeout = 0
151+ resp .Body .Close ()
152+ resp = p .doReq (w , req , false )
153+ if resp == nil {
154+ return
155+ }
127156 }
157+ } else {
158+ resp = p .doReq (w , req , true )
128159 if resp == nil {
129- p .logger .Error ().Err (err ).Msg ("Connection to SOCKS5 server failed" )
130- w .WriteHeader (http .StatusServiceUnavailable )
131160 return
132161 }
162+ if slices .Contains (resp .TransferEncoding , "chunked" ) {
163+ chunked = true
164+ p .httpClient .Timeout = 0
165+ p .sockClient .Timeout = 0
166+ resp .Body .Close ()
167+ resp = p .doReq (w , req , true )
168+ if resp == nil {
169+ return
170+ }
171+ }
133172 }
134173 defer resp .Body .Close ()
135-
174+ done := make (chan struct {})
175+ if chunked {
176+ rc := http .NewResponseController (w )
177+ go func () {
178+ for {
179+ select {
180+ case <- time .Tick (flushTimeout ):
181+ err := rc .Flush ()
182+ if err != nil {
183+ p .logger .Error ().Err (err )
184+ return
185+ }
186+ err = rc .SetReadDeadline (time .Now ().Add (readTimeout ))
187+ if err != nil {
188+ p .logger .Error ().Err (err )
189+ return
190+ }
191+ err = rc .SetWriteDeadline (time .Now ().Add (writeTimeout ))
192+ if err != nil {
193+ p .logger .Error ().Err (err )
194+ return
195+ }
196+ case <- done :
197+ return
198+ }
199+ }
200+ }()
201+ }
136202 delConnectionHeaders (resp .Header )
137203 delHopHeaders (resp .Header )
138204 copyHeader (w .Header (), resp .Header )
@@ -149,7 +215,12 @@ func (p *proxyApp) handleForward(w http.ResponseWriter, r *http.Request) {
149215 } else {
150216 written = fmt .Sprintf ("%dKB" , n / kbSize )
151217 }
218+ if chunked {
219+ written = fmt .Sprintf ("%s - chunked" , written )
220+ }
152221 p .logger .Debug ().Msgf ("%s - %s - %s - %d - %s" , r .Proto , r .Method , r .Host , resp .StatusCode , written )
222+ done <- struct {}{}
223+ close (done )
153224}
154225
155226func (p * proxyApp ) handleTunnel (w http.ResponseWriter , r * http.Request ) {
@@ -268,7 +339,6 @@ func New(conf *Config) *proxyApp {
268339 CheckRedirect : func (req * http.Request , via []* http.Request ) error {
269340 return http .ErrUseLastResponse
270341 },
271- Timeout : timeout ,
272342 }
273343 hs := & http.Server {
274344 Addr : conf .AddrHTTP ,
@@ -286,9 +356,8 @@ func New(conf *Config) *proxyApp {
286356 CheckRedirect : func (req * http.Request , via []* http.Request ) error {
287357 return http .ErrUseLastResponse
288358 },
289- Timeout : timeout ,
290359 }
291360 logger .Info ().Msgf ("SOCKS5 Proxy: %s" , conf .AddrSOCKS )
292361 logger .Info ().Msgf ("HTTP Proxy: %s" , conf .AddrHTTP )
293- return & proxyApp {httpServer : hs , sockServer : socks , httpClient : hc , sockDialer : dialer , logger : & logger }
362+ return & proxyApp {httpServer : hs , sockClient : socks , httpClient : hc , sockDialer : dialer , logger : & logger }
294363}
0 commit comments