a73x

197849ba

fix: support HTTP/1.1 keep-alive in MITM CONNECT tunnel

a73x   2026-03-29 16:41

Commit message
fix: support HTTP/1.1 keep-alive in MITM CONNECT tunnel

Loop over requests in the tunnel instead of handling only one,
so clients that reuse connections work correctly.

proxy/proxy.go
Old New
@@ -174,31 +174,6 @@ func (p *Proxy) handleConnectMITM(w http.ResponseWriter, r *http.Request) {
174 } 174 }
175 defer tlsConn.Close() 175 defer tlsConn.Close()
176 176
177 req, err := http.ReadRequest(bufio.NewReader(tlsConn))
178 if err != nil {
179 log.Printf("ERROR: failed to read request from TLS conn for %s: %v", host, err)
180 return
181 }
182
183 findings := p.scanRequest(req)
184 if len(findings) > 0 {
185 rules := make([]string, 0, len(findings))
186 for _, f := range findings {
187 rules = append(rules, f.Rule)
188 }
189 log.Printf("BLOCKED %s %s [%s]", req.Method, r.Host, strings.Join(rules, ", "))
190 resp := &http.Response{
191 StatusCode: http.StatusForbidden,
192 ProtoMajor: 1,
193 ProtoMinor: 1,
194 Header: make(http.Header),
195 Body: io.NopCloser(strings.NewReader(fmt.Sprintf("request blocked: contains sensitive data (%s)\n", strings.Join(rules, ", ")))),
196 }
197 resp.Header.Set("Content-Type", "text/plain")
198 resp.Write(tlsConn)
199 return
200 }
201
202 upstreamCfg := p.upstreamTLS 177 upstreamCfg := p.upstreamTLS
203 if upstreamCfg == nil { 178 if upstreamCfg == nil {
204 upstreamCfg = &tls.Config{} 179 upstreamCfg = &tls.Config{}
@@ -209,32 +184,61 @@ func (p *Proxy) handleConnectMITM(w http.ResponseWriter, r *http.Request) {
209 upstreamConn, err := tls.Dial("tcp", r.Host, cfg) 184 upstreamConn, err := tls.Dial("tcp", r.Host, cfg)
210 if err != nil { 185 if err != nil {
211 log.Printf("ERROR: failed to dial upstream %s: %v", r.Host, err) 186 log.Printf("ERROR: failed to dial upstream %s: %v", r.Host, err)
212 resp := &http.Response{
213 StatusCode: http.StatusBadGateway,
214 ProtoMajor: 1,
215 ProtoMinor: 1,
216 Header: make(http.Header),
217 Body: io.NopCloser(strings.NewReader(err.Error()+"\n")),
218 }
219 resp.Write(tlsConn)
220 return 187 return
221 } 188 }
222 defer upstreamConn.Close() 189 defer upstreamConn.Close()
223 190
224 req.RequestURI = "" 191 clientReader := bufio.NewReader(tlsConn)
225 if err := req.Write(upstreamConn); err != nil { 192 upstreamReader := bufio.NewReader(upstreamConn)
226 log.Printf("ERROR: failed to forward request to %s: %v", r.Host, err)
227 return
228 }
229 193
230 upstreamResp, err := http.ReadResponse(bufio.NewReader(upstreamConn), req) 194 for {
231 if err != nil { 195 req, err := http.ReadRequest(clientReader)
232 log.Printf("ERROR: failed to read response from %s: %v", r.Host, err) 196 if err != nil {
233 return 197 if err != io.EOF {
234 } 198 log.Printf("ERROR: failed to read request from TLS conn for %s: %v", host, err)
235 defer upstreamResp.Body.Close() 199 }
200 return
201 }
202
203 findings := p.scanRequest(req)
204 if len(findings) > 0 {
205 rules := make([]string, 0, len(findings))
206 for _, f := range findings {
207 rules = append(rules, f.Rule)
208 }
209 log.Printf("BLOCKED %s %s [%s]", req.Method, r.Host, strings.Join(rules, ", "))
210 resp := &http.Response{
211 StatusCode: http.StatusForbidden,
212 ProtoMajor: 1,
213 ProtoMinor: 1,
214 Header: make(http.Header),
215 Body: io.NopCloser(strings.NewReader(fmt.Sprintf("request blocked: contains sensitive data (%s)\n", strings.Join(rules, ", ")))),
216 }
217 resp.Header.Set("Content-Type", "text/plain")
218 resp.Header.Set("Connection", "close")
219 resp.Write(tlsConn)
220 return
221 }
236 222
237 upstreamResp.Write(tlsConn) 223 req.RequestURI = ""
224 if err := req.Write(upstreamConn); err != nil {
225 log.Printf("ERROR: failed to forward request to %s: %v", r.Host, err)
226 return
227 }
228
229 upstreamResp, err := http.ReadResponse(upstreamReader, req)
230 if err != nil {
231 log.Printf("ERROR: failed to read response from %s: %v", r.Host, err)
232 return
233 }
234
235 upstreamResp.Write(tlsConn)
236 upstreamResp.Body.Close()
237
238 if req.Close || upstreamResp.Close {
239 return
240 }
241 }
238 } 242 }
239 243
240 func (p *Proxy) getOrCreateLeaf(host string) (*tls.Certificate, error) { 244 func (p *Proxy) getOrCreateLeaf(host string) (*tls.Certificate, error) {