diff --git a/lib/plug/adapters/test/conn.ex b/lib/plug/adapters/test/conn.ex index e396ceb3..630f8542 100644 --- a/lib/plug/adapters/test/conn.ex +++ b/lib/plug/adapters/test/conn.ex @@ -33,7 +33,13 @@ defmodule Plug.Adapters.Test.Conn do address: {127, 0, 0, 1}, port: 111_317, ssl_cert: nil - }) + }), + sock_data: + get_from_adapter(conn, :get_sock_data, %{ + address: {127, 0, 0, 1}, + port: 111_318 + }), + ssl_data: get_from_adapter(conn, :get_ssl_data, nil) } conn_port = if conn.port != 0, do: conn.port, else: 80 @@ -140,6 +146,14 @@ defmodule Plug.Adapters.Test.Conn do Map.fetch!(payload, :peer_data) end + def get_sock_data(payload) do + Map.fetch!(payload, :sock_data) + end + + def get_ssl_data(payload) do + Map.fetch!(payload, :ssl_data) + end + def get_http_protocol(payload) do Map.fetch!(payload, :http_protocol) end diff --git a/lib/plug/conn.ex b/lib/plug/conn.ex index b163ed1a..141b4c6d 100644 --- a/lib/plug/conn.ex +++ b/lib/plug/conn.ex @@ -638,6 +638,36 @@ defmodule Plug.Conn do adapter.get_peer_data(payload) end + @doc """ + Returns the request sock (local) data. + + It raises if the adapter does not provide this metadata. + """ + @spec get_sock_data(t) :: Plug.Conn.Adapter.sock_data() + def get_sock_data(%Conn{adapter: {adapter, payload}}) do + if function_exported?(adapter, :get_sock_data, 1) do + adapter.get_sock_data(payload) + else + raise "get_sock_data not supported by #{inspect(adapter)}" + end + end + + @doc """ + Returns SSL data for the connection. + + If the connection is not SSL, returns nil. + + It raises if the adapter does not provide this metadata. + """ + @spec get_ssl_data(t) :: Plug.Conn.Adapter.ssl_data() + def get_ssl_data(%Conn{adapter: {adapter, payload}}) do + if function_exported?(adapter, :get_ssl_data, 1) do + adapter.get_ssl_data(payload) + else + raise "get_ssl_data not supported by #{inspect(adapter)}" + end + end + @doc """ Returns the HTTP protocol and version. diff --git a/lib/plug/conn/adapter.ex b/lib/plug/conn/adapter.ex index bf7780aa..c4e90369 100644 --- a/lib/plug/conn/adapter.ex +++ b/lib/plug/conn/adapter.ex @@ -11,6 +11,11 @@ defmodule Plug.Conn.Adapter do port: :inet.port_number(), ssl_cert: binary | nil } + @type sock_data :: %{ + address: :inet.ip_address(), + port: :inet.port_number() + } + @type ssl_data :: :ssl.connection_info() | nil @doc """ Function used by adapters to create a new connection. @@ -166,10 +171,21 @@ defmodule Plug.Conn.Adapter do """ @callback get_peer_data(payload) :: peer_data() + @doc """ + Returns sock (local-side) information such as the address and port. + """ + @callback get_sock_data(payload) :: sock_data() + + @doc """ + Returns details of the negotiated SSL connection, if present. If the connection is not SSL, + returns nil + """ + @callback get_ssl_data(payload) :: ssl_data() + @doc """ Returns the HTTP protocol and its version. """ @callback get_http_protocol(payload) :: http_protocol - @optional_callbacks push: 3 + @optional_callbacks push: 3, get_sock_data: 1, get_ssl_data: 1 end diff --git a/lib/plug/test.ex b/lib/plug/test.ex index 0abed751..7d8925c6 100644 --- a/lib/plug/test.ex +++ b/lib/plug/test.ex @@ -208,6 +208,24 @@ defmodule Plug.Test do end) end + @doc """ + Puts the sock data. + """ + def put_sock_data(conn, sock_data) do + update_in(conn.adapter, fn {adapter, payload} -> + {adapter, Map.put(payload, :sock_data, sock_data)} + end) + end + + @doc """ + Puts the ssl data. + """ + def put_ssl_data(conn, ssl_data) do + update_in(conn.adapter, fn {adapter, payload} -> + {adapter, Map.put(payload, :ssl_data, ssl_data)} + end) + end + @doc """ Puts a request cookie. """ diff --git a/test/plug/adapters/test/conn_test.exs b/test/plug/adapters/test/conn_test.exs index 84a80fe1..9c940ed1 100644 --- a/test/plug/adapters/test/conn_test.exs +++ b/test/plug/adapters/test/conn_test.exs @@ -185,6 +185,18 @@ defmodule Plug.Adapters.Test.ConnTest do assert peer_data == Plug.Conn.get_peer_data(conn) end + test "use custom sock data" do + sock_data = %{address: {127, 0, 0, 1}, port: 111_318} + conn = conn(:get, "/") |> put_sock_data(sock_data) + assert sock_data == Plug.Conn.get_sock_data(conn) + end + + test "use custom ssl data" do + ssl_data = %{address: {127, 0, 0, 1}, port: 111_317} + conn = conn(:get, "/") |> put_ssl_data(ssl_data) + assert ssl_data == Plug.Conn.get_ssl_data(conn) + end + test "push/3 sends message including path and headers" do ref = make_ref() diff --git a/test/plug/conn_test.exs b/test/plug/conn_test.exs index 371021ed..c89c1812 100644 --- a/test/plug/conn_test.exs +++ b/test/plug/conn_test.exs @@ -126,6 +126,13 @@ defmodule Plug.ConnTest do port: 111_317, ssl_cert: nil } + + assert get_sock_data(conn) == %{ + address: {127, 0, 0, 1}, + port: 111_318 + } + + assert is_nil(get_ssl_data(conn)) end test "path_info" do