Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions lib/mint/http.ex
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,15 @@ defmodule Mint.HTTP do
uses TLS and the TLS session to the host is nested inside it. *HTTPS proxies
for HTTPS connections are available since v1.10.0*.

The `opts` in the `:proxy` tuple are the options of the connection to the proxy
itself and support the same options as `connect/4`. Tunnel proxies additionally
support:

* `:tunnel_timeout` - the maximum time (in milliseconds) to wait for the proxy
to reply to the `CONNECT` request. If the tunnel is not established within
this time, `connect/4` returns a `Mint.HTTPError` with reason
`{:proxy, :tunnel_timeout}`. Defaults to `30_000` (30 seconds).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is missing the *Available since vX.X.X* notice.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This option has been available since 0.1.0, it was just not documented.


## Transport options

The options specified in `:transport_opts` are passed to the module that
Expand Down
2 changes: 1 addition & 1 deletion lib/mint/tunnel_proxy.ex
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ defmodule Mint.TunnelProxy do
end

defp receive_response(conn, ref, timeout_deadline) do
timeout = timeout_deadline - System.monotonic_time(:millisecond)
timeout = max(timeout_deadline - System.monotonic_time(:millisecond), 0)
socket = HTTP1.get_socket(conn)

receive do
Expand Down
37 changes: 37 additions & 0 deletions test/mint/tunnel_proxy_connect_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,24 @@ defmodule Mint.TunnelProxyConnectTest do
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()

assert {:error, %Mint.HTTPError{reason: {:proxy, :tunnel_timeout}}} =
HTTP.connect(:https, "localhost", 443,
proxy: {:http, "localhost", proxy_port, [tunnel_timeout: 50]}
)
end

test "an already-elapsed tunnel deadline yields a tunnel timeout error instead of crashing" do
proxy_port = start_silent_proxy()

assert {:error, %Mint.HTTPError{reason: {:proxy, :tunnel_timeout}}} =
HTTP.connect(:https, "localhost", 443,
proxy: {:http, "localhost", proxy_port, [tunnel_timeout: -1]}
)
end

test "IPv6 address targets produce a bracketed CONNECT authority" do
{proxy_port, proxy_ref} = start_capturing_proxy()

Expand Down Expand Up @@ -82,6 +100,25 @@ defmodule Mint.TunnelProxyConnectTest do
{port, ref}
end

# Starts a one-shot proxy that accepts a single connection and never
# responds.
defp start_silent_proxy do
{:ok, listen_socket} =
:gen_tcp.listen(0, mode: :binary, packet: :raw, active: false, reuseaddr: true)

{:ok, port} = :inet.port(listen_socket)

spawn_link(fn ->
{:ok, socket} = :gen_tcp.accept(listen_socket)

receive do
:stop -> :gen_tcp.close(socket)
end
end)

port
end

# Starts a one-shot server that accepts a single connection, reports the raw
# request head to the test process, and closes the connection.
defp start_capturing_proxy do
Expand Down
Loading