{"record":{"id":"671db890a5a0b35f","repo":"windmill-labs/windmill","slug":"http-proxy-closed-connection-before-sending-connec","errorCode":null,"errorMessage":"HTTP proxy closed connection before sending CONNECT response","messagePattern":"HTTP proxy closed connection before sending CONNECT response","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/windmill-trigger-websocket/src/proxy.rs","lineNumber":272,"sourceCode":"    let mut stream = TcpStream::connect((proxy.host.as_str(), proxy.port)).await?;\n\n    let host_header = format!(\"{}:{}\", target_host, target_port);\n    let mut req = format!(\"CONNECT {h} HTTP/1.1\\r\\nHost: {h}\\r\\n\", h = host_header,);\n    if let Some(ref auth) = proxy.basic_auth {\n        req.push_str(\"Proxy-Authorization: Basic \");\n        req.push_str(auth);\n        req.push_str(\"\\r\\n\");\n    }\n    req.push_str(\"Proxy-Connection: keep-alive\\r\\n\\r\\n\");\n\n    stream.write_all(req.as_bytes()).await?;\n    stream.flush().await?;\n\n    let mut reader = BufReader::new(stream);\n    let mut status_line = String::new();\n    let n = reader.read_line(&mut status_line).await?;\n    if n == 0 {\n        return Err(io::Error::new(\n            io::ErrorKind::UnexpectedEof,\n            \"HTTP proxy closed connection before sending CONNECT response\",\n        ));\n    }\n\n    let status_ok = status_line\n        .split_whitespace()\n        .nth(1)\n        .map(|s| s == \"200\")\n        .unwrap_or(false);\n\n    loop {\n        let mut line = String::new();\n        let n = reader.read_line(&mut line).await?;\n        if n == 0 || line == \"\\r\\n\" || line == \"\\n\" {\n            break;\n        }\n    }","sourceCodeStart":254,"sourceCodeEnd":290,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/backend/windmill-trigger-websocket/src/proxy.rs#L254-L290","documentation":"`http_connect_tunnel` opens a plain TCP connection to the HTTP proxy, sends a `CONNECT host:port` request, then reads the status line of the proxy's response. If `read_line` returns `n == 0`, the proxy closed the TCP connection before answering the CONNECT request, so no tunnel can be established and the function converts this into `io::ErrorKind::UnexpectedEof`.","triggerScenarios":"Calling `connect_async_with_proxy` (via `test_connection` or `get_consumer`) where the configured HTTP proxy accepts the TCP connection but closes it immediately upon (or before) responding to the CONNECT request — e.g. proxy rejects the target, times out, or requires TLS on the proxy link.","commonSituations":"Proxy expects HTTPS (TLS) on the client link but gets plaintext CONNECT; proxy software (squid, mitmproxy, Istio sidecar) has an ACL denying the destination; idle/connection limits; proxy crashed between accept and respond.","solutions":["Confirm the proxy speaks plain HTTP CONNECT on that port (e.g. `curl -x http://proxy:port https://target -v`); if it needs TLS, fix the scheme","Check proxy ACLs/logs for denied CONNECT to the target host:port","Verify the target host:port is allowed by the proxy's whitelist/allowlist config","Test connectivity to the proxy itself (`nc -vz proxy port`) and check the proxy service logs for crashes or timeouts"],"exampleFix":"// before\n// client config: proxy = \"http://proxy.corp:3128\" but proxy requires TLS\n// after\n// proxy = \"https://proxy.corp:3128\" (or configure the proxy to accept plain CONNECT)","handlingStrategy":"try-catch","validationCode":"// sanity-check the proxy speaks plain CONNECT before use\nlet mut s = tokio::net::TcpStream::connect((proxy_host, proxy_port)).await?;\ns.write_all(b\"CONNECT example.com:443 HTTP/1.1\\r\\nHost: example.com:443\\r\\n\\r\\n\").await?;\nlet mut buf = vec![0u8; 16];\nlet n = s.read(&mut buf).await?;\nif n == 0 { return Err(anyhow!(\"proxy closes connection on CONNECT - check proxy ACLs/TLS mode\")); }","typeGuard":null,"tryCatchPattern":"match connect_async_with_proxy(&url, &proxy).await {\n    Err(WsError::Io(e)) if e.kind() == std::io::ErrorKind::UnexpectedEof => {\n        eprintln!(\"proxy dropped the CONNECT request: {}\", e);\n        // surface a config hint: wrong scheme (TLS vs plain), ACL denial, proxy down\n    }\n    Err(e) => return Err(e.into()),\n    Ok(_) => {}\n}","preventionTips":["Verify the proxy URL scheme matches the listener (http:// for plain CONNECT, https:// for TLS proxies)","Smoke-test the proxy with `curl -x http://proxy:port https://target -v` after config changes","Keep proxy ACLs/allowlists in sync with the websocket targets your workers need","Alert on proxy-side connection resets via proxy metrics/logs"],"tags":["network","proxy","http-connect","eof"],"backgroundTag":"proxy-closed-connection","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}