snail007/goproxy · warning
http decoder read err:%s
Error message
http decoder read err:%s
What it means
NewHTTPRequest reads the first chunk of the client connection into a buffer to parse the HTTP request head. If the Read() call fails with any error other than io.EOF, it wraps it as "http decoder read err:%s", closes the connection, and returns. This is a transport-level read failure before any HTTP parsing occurs.
Source
Thrown at utils/structs.go:249
conn *net.Conn
Host string
Method string
URL string
hostOrURL string
isBasicAuth bool
basicAuth *BasicAuth
}
func NewHTTPRequest(inConn *net.Conn, bufSize int, isBasicAuth bool, basicAuth *BasicAuth) (req HTTPRequest, err error) {
buf := make([]byte, bufSize)
len := 0
req = HTTPRequest{
conn: inConn,
}
len, err = (*inConn).Read(buf[:])
if err != nil {
if err != io.EOF {
err = fmt.Errorf("http decoder read err:%s", err)
}
CloseConn(inConn)
return
}
req.HeadBuf = buf[:len]
index := bytes.IndexByte(req.HeadBuf, '\n')
if index == -1 {
err = fmt.Errorf("http decoder data line err:%s", string(req.HeadBuf)[:50])
CloseConn(inConn)
return
}
fmt.Sscanf(string(req.HeadBuf[:index]), "%s%s", &req.Method, &req.hostOrURL)
if req.Method == "" || req.hostOrURL == "" {
err = fmt.Errorf("http decoder data err:%s", string(req.HeadBuf)[:50])
CloseConn(inConn)
return
}
req.Method = strings.ToUpper(req.Method)View on GitHub (pinned to e6d6a821db)
Solutions
- Treat this as an expected client-side disconnect: log it at debug level and skip — the connection is already closed by the library, just return without retrying on the same conn.
- If it happens for every request, check what the client is actually sending: a client speaking HTTPS directly to the HTTP proxy port will cause handshake bytes to break reads — point the client at the correct proxy port or enable the CONNECT/TLS path.
- Set/extend socket read deadlines if timeouts are the cause (review any SetReadDeadline on the accepted conn).
- Check for intermediary devices (NAT, LB) dropping idle connections and add TCP keepalives on the listener.
Example fix
len, err = (*inConn).Read(buf[:])
if err != nil {
if err != io.EOF {
// caller-side handling
var ne net.Error
if errors.As(err, &ne) && ne.Timeout() {
log.Println("client read timeout, closing conn")
} else {
log.Printf("client dropped connection: %v", err)
}
}
} Defensive patterns
Strategy: try-catch
Try / catch
_, err := utils.NewHTTPRequest(conn, bufSize, isAuth, auth)
if err != nil {
var ne net.Error
if errors.As(err, &ne) && ne.Timeout() {
log.Printf("client read timed out: %v", err)
} else {
log.Printf("client aborted before sending request (normal for connection pooling): %v", err)
}
// connection already closed by NewHTTPRequest; do not reuse
return
} Prevention
- Accept this as routine with keep-alive clients (speculative pre-connects get closed).
- Never route TLS traffic to the plain-HTTP proxy port — configure https_proxy to a TLS-enabled endpoint or the CONNECT path.
- Enable TCP keepalives on the listener to detect dead peers earlier.
- Log at debug level to avoid noise; alert only if it occurs on 100% of requests.
When it happens
Trigger: Calling NewHTTPRequest (via the callback path) when the underlying TCP conn.Read fails: the client abruptly closed/resets the connection (ECONNRESET), a read deadline expired, or the socket was torn down while the server was waiting for the request bytes.
Common situations: Browsers pre-opening connections and closing them before sending a request (very common with Chrome/keep-alive pooling); clients timing out behind slow proxies; the client sending a TLS handshake or other binary data that the raw TCP reader chokes on; idle keep-alive connections reaped by an intermediate NAT/firewall sending RST.
Related errors
- no address Found, net.InterfaceAddrs: %v
- http decoder data line err:%s
- http decoder data err:%s
- authorization data error,ERR:%s
- authorization data parse error,ERR:%s
AI-assisted analysis of snail007/goproxy@e6d6a821db (2026-09-03).
Data as JSON: /api/errors/008666e48b104336.
Report an issue: GitHub.