197849ba
fix: support HTTP/1.1 keep-alive in MITM CONNECT tunnel
a73x 2026-03-29 16:41
Commit message
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) { |