Skip to content

Commit a352df8

Browse files
blink-so[bot]f0ssel
andcommitted
Implement direct connection handling to fix premature completion
- Replace HTTP server framework with direct connection handling - Parse HTTP requests manually using http.ReadRequest - Write HTTP responses directly to connection - Eliminates race condition where HTTP server completed before handler finished - Should fix 'Empty reply from server' issue completely - Maintains all logging and error handling Co-authored-by: f0ssel <[email protected]>
1 parent d38fde1 commit a352df8

File tree

1 file changed

+121
-23
lines changed

1 file changed

+121
-23
lines changed

proxy/proxy.go

Lines changed: 121 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -525,10 +525,10 @@ func (p *Server) handleConnectionWithTLSDetection(conn net.Conn) {
525525
// TLS handshake starts with 0x16 (TLS Content Type: Handshake)
526526
if len(firstByte) > 0 && firstByte[0] == 0x16 {
527527
p.logger.Debug("Detected TLS handshake, performing TLS termination")
528-
p.handleTLSTerminationBuffered(bufReader, conn)
528+
p.handleTLSTerminationDirect(bufReader, conn)
529529
} else {
530530
p.logger.Debug("Detected HTTP request, handling normally")
531-
p.handleHTTPConnectionBuffered(bufReader, conn)
531+
p.handleHTTPConnectionDirect(bufReader, conn)
532532
}
533533
}
534534

@@ -655,36 +655,134 @@ func (l *singleConnListener) Addr() net.Addr {
655655
return l.conn.LocalAddr()
656656
}
657657

658-
// handleHTTPConnectionBuffered handles regular HTTP connections using buffered reader
659-
func (p *Server) handleHTTPConnectionBuffered(bufReader *bufio.Reader, conn net.Conn) {
660-
p.logger.Debug("Starting buffered HTTP connection handling")
658+
// handleHTTPConnectionDirect handles HTTP connections directly without HTTP server framework
659+
func (p *Server) handleHTTPConnectionDirect(bufReader *bufio.Reader, conn net.Conn) {
660+
p.logger.Debug("Starting direct HTTP connection handling")
661661

662-
// Create a connection that uses the buffered reader
663-
bufferedConn := &bufferedConnection{
664-
Conn: conn,
665-
reader: bufReader,
662+
// Parse the HTTP request manually
663+
req, err := http.ReadRequest(bufReader)
664+
if err != nil {
665+
p.logger.Debug("Failed to parse HTTP request", "error", err)
666+
return
667+
}
668+
669+
p.logger.Debug("Parsed HTTP request", "method", req.Method, "url", req.URL.String(), "host", req.Host)
670+
671+
// Check if request should be allowed
672+
result := p.ruleEngine.Evaluate(req.Method, req.URL.String())
673+
674+
// Audit the request
675+
p.auditor.AuditRequest(audit.Request{
676+
Method: req.Method,
677+
URL: req.URL.String(),
678+
Allowed: result.Allowed,
679+
Rule: result.Rule,
680+
})
681+
682+
if !result.Allowed {
683+
// Send blocked response directly
684+
blockedResponse := "HTTP/1.1 403 Forbidden\r\n" +
685+
"Content-Type: text/html\r\n" +
686+
"Connection: close\r\n" +
687+
"\r\n" +
688+
"<html><body><h1>403 Forbidden</h1><p>Request blocked by jail</p></body></html>"
689+
conn.Write([]byte(blockedResponse))
690+
return
666691
}
667692

668-
p.logger.Debug("Created buffered connection, starting HTTP server")
693+
// Forward the request directly
694+
p.forwardHTTPRequestDirect(req, conn)
695+
}
696+
697+
// forwardHTTPRequestDirect forwards HTTP request and writes response directly to connection
698+
func (p *Server) forwardHTTPRequestDirect(req *http.Request, conn net.Conn) {
699+
p.logger.Debug("forwardHTTPRequestDirect called", "method", req.Method, "url", req.URL.String(), "host", req.Host)
669700

670-
// Create HTTP server to handle this connection
671-
server := &http.Server{
672-
Handler: http.HandlerFunc(p.handleHTTP),
701+
// Create target URL
702+
targetURL := &url.URL{
703+
Scheme: "http",
704+
Host: req.Host,
705+
Path: req.URL.Path,
706+
RawQuery: req.URL.RawQuery,
707+
}
708+
709+
p.logger.Debug("Target URL constructed", "target", targetURL.String())
710+
711+
// Create HTTP client
712+
client := &http.Client{
713+
Timeout: 5 * time.Second,
714+
CheckRedirect: func(req *http.Request, via []*http.Request) error {
715+
return http.ErrUseLastResponse
716+
},
717+
}
718+
719+
// Create new request
720+
outReq, err := http.NewRequest(req.Method, targetURL.String(), req.Body)
721+
if err != nil {
722+
p.logger.Error("Failed to create forward request", "error", err)
723+
errorResponse := "HTTP/1.1 500 Internal Server Error\r\n\r\n"
724+
conn.Write([]byte(errorResponse))
725+
return
726+
}
727+
728+
// Copy headers
729+
for name, values := range req.Header {
730+
if strings.ToLower(name) == "connection" || strings.ToLower(name) == "proxy-connection" {
731+
continue
732+
}
733+
for _, value := range values {
734+
outReq.Header.Add(name, value)
735+
}
736+
}
737+
738+
p.logger.Debug("About to make HTTP request", "target", targetURL.String())
739+
p.logger.Debug("Calling client.Do() now...")
740+
start := time.Now()
741+
resp, err := client.Do(outReq)
742+
duration := time.Since(start)
743+
p.logger.Debug("client.Do() completed", "duration", duration, "error", err)
744+
745+
if err != nil {
746+
p.logger.Error("Failed to make forward request", "error", err, "target", targetURL.String())
747+
errorResponse := "HTTP/1.1 502 Bad Gateway\r\n\r\n"
748+
conn.Write([]byte(errorResponse))
749+
return
673750
}
751+
defer resp.Body.Close()
674752

675-
p.logger.Debug("About to serve buffered HTTP connection")
753+
p.logger.Debug("Received response", "status", resp.StatusCode, "target", targetURL.String())
754+
755+
// Write status line
756+
statusLine := fmt.Sprintf("HTTP/1.1 %d %s\r\n", resp.StatusCode, resp.Status)
757+
conn.Write([]byte(statusLine))
758+
759+
// Write headers
760+
for name, values := range resp.Header {
761+
if strings.ToLower(name) == "connection" || strings.ToLower(name) == "transfer-encoding" {
762+
continue
763+
}
764+
for _, value := range values {
765+
headerLine := fmt.Sprintf("%s: %s\r\n", name, value)
766+
conn.Write([]byte(headerLine))
767+
}
768+
}
676769

677-
// Serve the HTTP request
678-
err := server.Serve(&singleConnListener{conn: bufferedConn})
679-
if err != nil && err != io.EOF && !isConnectionClosed(err) {
680-
p.logger.Debug("Buffered HTTP connection error", "error", err)
770+
// End headers
771+
conn.Write([]byte("\r\n"))
772+
773+
// Copy response body
774+
bytesWritten, copyErr := io.Copy(conn, resp.Body)
775+
if copyErr != nil {
776+
p.logger.Error("Error copying response body", "error", copyErr, "bytes_written", bytesWritten)
681777
} else {
682-
p.logger.Debug("Buffered HTTP connection completed successfully")
778+
p.logger.Debug("Successfully forwarded HTTP response", "bytes_written", bytesWritten, "status", resp.StatusCode)
683779
}
780+
781+
p.logger.Debug("forwardHTTPRequestDirect completed")
684782
}
685783

686-
// handleTLSTerminationBuffered performs TLS termination using buffered reader
687-
func (p *Server) handleTLSTerminationBuffered(bufReader *bufio.Reader, conn net.Conn) {
784+
// handleTLSTerminationDirect handles TLS termination directly
785+
func (p *Server) handleTLSTerminationDirect(bufReader *bufio.Reader, conn net.Conn) {
688786
// Create a connection that uses the buffered reader
689787
bufferedConn := &bufferedConnection{
690788
Conn: conn,
@@ -698,11 +796,11 @@ func (p *Server) handleTLSTerminationBuffered(bufReader *bufio.Reader, conn net.
698796
tlsConn := tls.Server(bufferedConn, p.tlsConfig)
699797
err := tlsConn.Handshake()
700798
if err != nil {
701-
p.logger.Debug("Buffered TLS handshake failed", "error", err)
799+
p.logger.Debug("TLS handshake failed", "error", err)
702800
return
703801
}
704802

705-
p.logger.Debug("Buffered TLS handshake successful, processing decrypted HTTPS traffic")
803+
p.logger.Debug("TLS handshake successful, processing decrypted HTTPS traffic")
706804

707805
// Handle the decrypted HTTPS requests
708806
p.handleTLSConnection(tlsConn, hostname)

0 commit comments

Comments
 (0)