{"record":{"id":"008666e48b104336","repo":"snail007/goproxy","slug":"http-decoder-read-err-s","errorCode":null,"errorMessage":"http decoder read err:%s","messagePattern":"http decoder read err:(.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"utils/structs.go","lineNumber":249,"sourceCode":"\tconn        *net.Conn\n\tHost        string\n\tMethod      string\n\tURL         string\n\thostOrURL   string\n\tisBasicAuth bool\n\tbasicAuth   *BasicAuth\n}\n\nfunc NewHTTPRequest(inConn *net.Conn, bufSize int, isBasicAuth bool, basicAuth *BasicAuth) (req HTTPRequest, err error) {\n\tbuf := make([]byte, bufSize)\n\tlen := 0\n\treq = HTTPRequest{\n\t\tconn: inConn,\n\t}\n\tlen, err = (*inConn).Read(buf[:])\n\tif err != nil {\n\t\tif err != io.EOF {\n\t\t\terr = fmt.Errorf(\"http decoder read err:%s\", err)\n\t\t}\n\t\tCloseConn(inConn)\n\t\treturn\n\t}\n\treq.HeadBuf = buf[:len]\n\tindex := bytes.IndexByte(req.HeadBuf, '\\n')\n\tif index == -1 {\n\t\terr = fmt.Errorf(\"http decoder data line err:%s\", string(req.HeadBuf)[:50])\n\t\tCloseConn(inConn)\n\t\treturn\n\t}\n\tfmt.Sscanf(string(req.HeadBuf[:index]), \"%s%s\", &req.Method, &req.hostOrURL)\n\tif req.Method == \"\" || req.hostOrURL == \"\" {\n\t\terr = fmt.Errorf(\"http decoder data err:%s\", string(req.HeadBuf)[:50])\n\t\tCloseConn(inConn)\n\t\treturn\n\t}\n\treq.Method = strings.ToUpper(req.Method)","sourceCodeStart":231,"sourceCodeEnd":267,"githubUrl":"https://github.com/snail007/goproxy/blob/e6d6a821db80e7f47ee6e981a144984e1d4ddb3d/utils/structs.go#L231-L267","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"len, err = (*inConn).Read(buf[:])\nif err != nil {\n\tif err != io.EOF {\n\t\t// caller-side handling\n\t\tvar ne net.Error\n\t\tif errors.As(err, &ne) && ne.Timeout() {\n\t\t\tlog.Println(\"client read timeout, closing conn\")\n\t\t} else {\n\t\t\tlog.Printf(\"client dropped connection: %v\", err)\n\t\t}\n\t}\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"_, err := utils.NewHTTPRequest(conn, bufSize, isAuth, auth)\nif err != nil {\n\tvar ne net.Error\n\tif errors.As(err, &ne) && ne.Timeout() {\n\t\tlog.Printf(\"client read timed out: %v\", err)\n\t} else {\n\t\tlog.Printf(\"client aborted before sending request (normal for connection pooling): %v\", err)\n\t}\n\t// connection already closed by NewHTTPRequest; do not reuse\n\treturn\n}","preventionTips":["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."],"tags":["network","go","http","connection-reset"],"backgroundTag":"connection-reset-by-peer","analyzedSha":"e6d6a821db80e7f47ee6e981a144984e1d4ddb3d","analyzedAt":"2026-09-03T15:32:42.750Z","contentChangedAt":"2026-09-03T15:32:42.750Z","schemaVersion":2},"datasetVersion":"2026-09-11T00:17:11.886Z"}