{"record":{"id":"d49d49a64ddd04c5","repo":"siyuan-note/siyuan","slug":"proxy-returned-unexpected-tunnel-data","errorCode":null,"errorMessage":"proxy returned unexpected tunnel data","messagePattern":"proxy returned unexpected tunnel data","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/util/httprequest.go","lineNumber":109,"sourceCode":"\t\treturn nil, err\n\t}\n\tif proxyURL == nil {\n\t\treturn t.directTransport.RoundTrip(req)\n\t}\n\n\ttargetAddr, err := t.resolvePublicTarget(req.Context(), req.URL)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tconn, reader, err := dialProxyTunnel(req.Context(), proxyURL, targetAddr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif req.URL.Scheme == \"https\" {\n\t\tif reader.Buffered() != 0 {\n\t\t\tconn.Close()\n\t\t\treturn nil, errors.New(\"proxy returned unexpected tunnel data\")\n\t\t}\n\t\ttlsConn := tls.Client(conn, &tls.Config{ServerName: req.URL.Hostname(), NextProtos: []string{\"http/1.1\"}})\n\t\tif err = tlsConn.HandshakeContext(req.Context()); err != nil {\n\t\t\tconn.Close()\n\t\t\treturn nil, err\n\t\t}\n\t\tconn = tlsConn\n\t\treader = bufio.NewReader(conn)\n\t}\n\n\ttargetReq := req.Clone(req.Context())\n\ttargetReq.URL = cloneURL(req.URL)\n\ttargetReq.URL.Scheme = \"\"\n\ttargetReq.URL.Host = \"\"\n\ttargetReq.RequestURI = \"\"\n\ttargetReq.Header.Del(\"Proxy-Authorization\")\n\tif targetReq.Host == \"\" {\n\t\ttargetReq.Host = req.URL.Host","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/util/httprequest.go#L91-L127","documentation":"In this SSRF-safe transport's RoundTrip, after the proxy answers CONNECT for an https target, the code checks that the proxy sent exactly the tunnel establishment bytes: any buffered residue means the proxy injected or pipelined data into the tunnel before TLS began. The connection is closed because proceeding could leak or corrupt the TLS stream (e.g. a MITM proxy that already spoke plaintext).","triggerScenarios":"RoundTrip routes an https request through ssrfSafeClient's proxy path; after reading the CONNECT response, bufio reader has leftover bytes (proxy sent 200 followed by extra data, or responded non-protocol data).","commonSituations":"Corporate/intercepting proxies (Zscaler, squid with injected headers) that append data after the CONNECT response; misbehaving custom forward proxies; a proxy answering CONNECT with an error page plus keep-alive residue.","solutions":["Inspect/replace the HTTP proxy configured for the agent client — use a standards-compliant proxy that sends only the CONNECT response with no residual bytes","Test the proxy with 'curl -x proxy https://target' to see if it injects data; update or bypass the intercepting proxy for this traffic","If the target can be reached directly, remove the proxy setting so RoundTrip uses the direct SSRF-validated connection"],"exampleFix":"// before: intercepting proxy injects bytes after CONNECT\nHTTPS_PROXY=http://corporate-mitm:8080\n// after: allowlist the target on the proxy or connect directly\nHTTPS_PROXY=","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := client.Do(req); err != nil {\n    if strings.Contains(err.Error(), \"proxy returned unexpected tunnel data\") {\n        return directFallback(req) // retry without the proxy\n    }\n    return err\n}","preventionTips":["Use standards-compliant proxies; test that CONNECT responses carry no extra bytes","Keep an allowlist so trusted targets can bypass intercepting middleboxes","Monitor for this error after corporate proxy upgrades"],"tags":["proxy","https","network","tls"],"backgroundTag":"proxy-connection-failed","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}