Skip to content

Commit 8abdd30

Browse files
authored
Implement a more comprehensive way to get connection information (#1269)
Define optional get_sock_data/1 and get_ssl_data/1 callbacks
1 parent 91de005 commit 8abdd30

File tree

6 files changed

+99
-2
lines changed

6 files changed

+99
-2
lines changed

lib/plug/adapters/test/conn.ex

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,13 @@ defmodule Plug.Adapters.Test.Conn do
3333
address: {127, 0, 0, 1},
3434
port: 111_317,
3535
ssl_cert: nil
36-
})
36+
}),
37+
sock_data:
38+
get_from_adapter(conn, :get_sock_data, %{
39+
address: {127, 0, 0, 1},
40+
port: 111_318
41+
}),
42+
ssl_data: get_from_adapter(conn, :get_ssl_data, nil)
3743
}
3844

3945
conn_port = if conn.port != 0, do: conn.port, else: 80
@@ -140,6 +146,14 @@ defmodule Plug.Adapters.Test.Conn do
140146
Map.fetch!(payload, :peer_data)
141147
end
142148

149+
def get_sock_data(payload) do
150+
Map.fetch!(payload, :sock_data)
151+
end
152+
153+
def get_ssl_data(payload) do
154+
Map.fetch!(payload, :ssl_data)
155+
end
156+
143157
def get_http_protocol(payload) do
144158
Map.fetch!(payload, :http_protocol)
145159
end

lib/plug/conn.ex

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,36 @@ defmodule Plug.Conn do
638638
adapter.get_peer_data(payload)
639639
end
640640

641+
@doc """
642+
Returns the request sock (local) data.
643+
644+
It raises if the adapter does not provide this metadata.
645+
"""
646+
@spec get_sock_data(t) :: Plug.Conn.Adapter.sock_data()
647+
def get_sock_data(%Conn{adapter: {adapter, payload}}) do
648+
if function_exported?(adapter, :get_sock_data, 1) do
649+
adapter.get_sock_data(payload)
650+
else
651+
raise "get_sock_data not supported by #{inspect(adapter)}"
652+
end
653+
end
654+
655+
@doc """
656+
Returns SSL data for the connection.
657+
658+
If the connection is not SSL, returns nil.
659+
660+
It raises if the adapter does not provide this metadata.
661+
"""
662+
@spec get_ssl_data(t) :: Plug.Conn.Adapter.ssl_data()
663+
def get_ssl_data(%Conn{adapter: {adapter, payload}}) do
664+
if function_exported?(adapter, :get_ssl_data, 1) do
665+
adapter.get_ssl_data(payload)
666+
else
667+
raise "get_ssl_data not supported by #{inspect(adapter)}"
668+
end
669+
end
670+
641671
@doc """
642672
Returns the HTTP protocol and version.
643673

lib/plug/conn/adapter.ex

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ defmodule Plug.Conn.Adapter do
1111
port: :inet.port_number(),
1212
ssl_cert: binary | nil
1313
}
14+
@type sock_data :: %{
15+
address: :inet.ip_address(),
16+
port: :inet.port_number()
17+
}
18+
@type ssl_data :: :ssl.connection_info() | nil
1419

1520
@doc """
1621
Function used by adapters to create a new connection.
@@ -166,10 +171,21 @@ defmodule Plug.Conn.Adapter do
166171
"""
167172
@callback get_peer_data(payload) :: peer_data()
168173

174+
@doc """
175+
Returns sock (local-side) information such as the address and port.
176+
"""
177+
@callback get_sock_data(payload) :: sock_data()
178+
179+
@doc """
180+
Returns details of the negotiated SSL connection, if present. If the connection is not SSL,
181+
returns nil
182+
"""
183+
@callback get_ssl_data(payload) :: ssl_data()
184+
169185
@doc """
170186
Returns the HTTP protocol and its version.
171187
"""
172188
@callback get_http_protocol(payload) :: http_protocol
173189

174-
@optional_callbacks push: 3
190+
@optional_callbacks push: 3, get_sock_data: 1, get_ssl_data: 1
175191
end

lib/plug/test.ex

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,24 @@ defmodule Plug.Test do
208208
end)
209209
end
210210

211+
@doc """
212+
Puts the sock data.
213+
"""
214+
def put_sock_data(conn, sock_data) do
215+
update_in(conn.adapter, fn {adapter, payload} ->
216+
{adapter, Map.put(payload, :sock_data, sock_data)}
217+
end)
218+
end
219+
220+
@doc """
221+
Puts the ssl data.
222+
"""
223+
def put_ssl_data(conn, ssl_data) do
224+
update_in(conn.adapter, fn {adapter, payload} ->
225+
{adapter, Map.put(payload, :ssl_data, ssl_data)}
226+
end)
227+
end
228+
211229
@doc """
212230
Puts a request cookie.
213231
"""

test/plug/adapters/test/conn_test.exs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,18 @@ defmodule Plug.Adapters.Test.ConnTest do
185185
assert peer_data == Plug.Conn.get_peer_data(conn)
186186
end
187187

188+
test "use custom sock data" do
189+
sock_data = %{address: {127, 0, 0, 1}, port: 111_318}
190+
conn = conn(:get, "/") |> put_sock_data(sock_data)
191+
assert sock_data == Plug.Conn.get_sock_data(conn)
192+
end
193+
194+
test "use custom ssl data" do
195+
ssl_data = %{address: {127, 0, 0, 1}, port: 111_317}
196+
conn = conn(:get, "/") |> put_ssl_data(ssl_data)
197+
assert ssl_data == Plug.Conn.get_ssl_data(conn)
198+
end
199+
188200
test "push/3 sends message including path and headers" do
189201
ref = make_ref()
190202

test/plug/conn_test.exs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,13 @@ defmodule Plug.ConnTest do
126126
port: 111_317,
127127
ssl_cert: nil
128128
}
129+
130+
assert get_sock_data(conn) == %{
131+
address: {127, 0, 0, 1},
132+
port: 111_318
133+
}
134+
135+
assert is_nil(get_ssl_data(conn))
129136
end
130137

131138
test "path_info" do

0 commit comments

Comments
 (0)