From 0f7796703078948923fe3ff3d0a07208950c4c93 Mon Sep 17 00:00:00 2001 From: Jinkyou Son Date: Sat, 5 Sep 2026 19:54:41 +0900 Subject: [PATCH] Keep HTTP/1.0 CONNECT tunnel sockets open --- lib/mint/http1.ex | 10 ++++++++++ test/mint/http1/conn_test.exs | 12 ++++++++++++ test/mint/tunnel_proxy_connect_test.exs | 20 ++++++++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/lib/mint/http1.ex b/lib/mint/http1.ex index 9235454a..8a49dc4f 100644 --- a/lib/mint/http1.ex +++ b/lib/mint/http1.ex @@ -1126,6 +1126,16 @@ defmodule Mint.HTTP1 do {:ok, request} end + # A successful CONNECT switches the connection to tunnel mode, so the + # response's HTTP version and Connection headers no longer determine the + # lifetime of the underlying socket. In particular, HTTP/1.0 responses are + # otherwise treated as non-persistent and would close the newly-established + # tunnel before the caller can use it. + defp request_done(%{request: %{method: "CONNECT", status: status}} = conn) + when status in 200..299 do + pop_request(conn) + end + defp request_done(%{request: request} = conn) do conn = pop_request(conn) diff --git a/test/mint/http1/conn_test.exs b/test/mint/http1/conn_test.exs index 7f07432f..556bd61c 100644 --- a/test/mint/http1/conn_test.exs +++ b/test/mint/http1/conn_test.exs @@ -165,6 +165,18 @@ defmodule Mint.HTTP1Test do assert conn.buffer == "XXX" end + test "HTTP/1.0 2xx response to CONNECT leaves the tunnel socket open", %{conn: conn} do + {:ok, conn, ref} = HTTP1.request(conn, "CONNECT", "example.com:443", [], nil) + + assert {:ok, conn, [_status, _headers, {:done, ^ref}]} = + HTTP1.stream( + conn, + {:tcp, conn.socket, "HTTP/1.0 200 Connection established\r\n\r\n"} + ) + + assert HTTP1.open?(conn) + end + test "content-length is ignored in 2xx response to CONNECT request", %{conn: conn} do {:ok, conn, ref} = HTTP1.request(conn, "CONNECT", "example.com:443", [], nil) response = "HTTP/1.1 200 OK\r\ncontent-length: 0\r\n\r\n" diff --git a/test/mint/tunnel_proxy_connect_test.exs b/test/mint/tunnel_proxy_connect_test.exs index 053b2da3..f674e689 100644 --- a/test/mint/tunnel_proxy_connect_test.exs +++ b/test/mint/tunnel_proxy_connect_test.exs @@ -37,6 +37,26 @@ defmodule Mint.TunnelProxyConnectTest do assert merge_body(rest, request) == "hello" end + test "tunnels through a proxy that sends an HTTP/1.0 CONNECT response" do + {origin_port, _origin_ref} = start_tls_origin() + + {proxy_port, _proxy_ref} = + start_connect_proxy( + "HTTP/1.0 200 Connection established\r\nProxy-agent: tinyproxy/1.11.1\r\n\r\n" + ) + + assert {:ok, conn} = + HTTP.connect(:https, "localhost", origin_port, + proxy: {:http, "localhost", proxy_port, []}, + transport_opts: [verify: :verify_none] + ) + + assert {:ok, conn, request} = HTTP.request(conn, "GET", "/", [], nil) + assert {:ok, _conn, responses} = receive_stream(conn) + assert [{:status, ^request, 200}, {:headers, ^request, _headers} | rest] = responses + assert merge_body(rest, request) == "hello" + end + test "a proxy that stalls the CONNECT response yields a tunnel timeout error" do proxy_port = start_silent_proxy()