Current section

Files

Jump to
honeybadger lib honeybadger plug_data.ex
Raw

lib/honeybadger/plug_data.ex

if Code.ensure_loaded?(Plug) do
defmodule Honeybadger.PlugData do
@moduledoc false
alias Plug.Conn
@type plug_env :: %{
action: binary(),
cgi_data: map(),
component: binary(),
params: map(),
session: map(),
url: binary()
}
@type metadata :: %{
context: any(),
plug_env: plug_env()
}
@doc """
Generate notification metadata from a `Plug.Conn`.
The map that is returned contains the current context and environmental
data.
"""
@spec metadata(Conn.t(), module()) :: metadata()
def metadata(conn, module) do
%{context: Honeybadger.context(), plug_env: build_plug_env(conn, module)}
end
@doc false
@spec build_plug_env(Conn.t(), module()) :: plug_env()
def build_plug_env(%Conn{} = conn, module) do
conn = Conn.fetch_query_params(conn)
%{
params: conn.params,
session: %{},
url: conn.request_path,
action: action(conn),
component: component(conn, module),
cgi_data: build_cgi_data(conn)
}
end
@doc false
@spec build_cgi_data(Conn.t()) :: map()
def build_cgi_data(%Conn{} = conn) do
cgi_data = %{
"REQUEST_METHOD" => conn.method,
"PATH_INFO" => Enum.join(conn.path_info, "/"),
"QUERY_STRING" => conn.query_string,
"SCRIPT_NAME" => Enum.join(conn.script_name, "/"),
"REMOTE_ADDR" => remote_addr(conn),
"REMOTE_PORT" => remote_port(conn),
"SERVER_ADDR" => "127.0.0.1",
"SERVER_NAME" => Honeybadger.get_env(:hostname),
"SERVER_PORT" => conn.port,
"CONTENT_LENGTH" => Conn.get_req_header(conn, "content-length"),
"ORIGINAL_FULLPATH" => conn.request_path
}
headers = rack_format_headers(conn)
Map.merge(cgi_data, headers)
end
# Helpers
defp component(%Conn{private: private}, module) do
import Honeybadger.Utils, only: [module_to_string: 1]
case private do
%{phoenix_controller: controller} -> module_to_string(controller)
_ -> module_to_string(module)
end
end
defp action(%Conn{private: private}) do
case private do
%{phoenix_action: action_name} -> to_string(action_name)
_ -> ""
end
end
defp rack_format_headers(%Conn{req_headers: req_headers}) do
Enum.reduce(req_headers, %{}, fn {header, value}, acc ->
header = ("HTTP_" <> String.upcase(header)) |> String.replace("-", "_")
Map.put(acc, header, value)
end)
end
defp remote_addr(%Conn{remote_ip: remote_ip}) do
case :inet.ntoa(remote_ip) do
{:error, :einval} ->
to_string(remote_ip)
charlist ->
List.to_string(charlist)
end
end
defp remote_port(conn) do
cond do
function_exported?(Conn, :get_peer_data, 1) ->
conn
|> Conn.get_peer_data()
|> Map.get(:port)
Map.has_key?(conn, :peer) ->
conn
|> Map.get(:peer)
|> elem(1)
true ->
nil
end
end
end
end