Subiendo api v2

This commit is contained in:
2025-04-16 10:03:13 -03:00
commit 226933fda7
7537 changed files with 576844 additions and 0 deletions

472
deps/plug_cowboy/lib/plug/cowboy.ex vendored Normal file
View File

@ -0,0 +1,472 @@
defmodule Plug.Cowboy do
@moduledoc """
Adapter interface to the [Cowboy webserver](https://github.com/ninenines/cowboy).
## Options
* `:net` - if using `:inet` (IPv4 only, the default) or `:inet6` (IPv6).
* `:ip` - the IP to bind the server to. Must be one of:
* a tuple in the format `{a, b, c, d}` with each value in `0..255` for IPv4,
* a tuple in the format `{a, b, c, d, e, f, g, h}` with each value in `0..65_535` for IPv6,
* or a tuple in the format `{:local, path}` for a Unix socket at the given `path`.
If you set an IPv6, the `:net` option will be automatically set to `:inet6`.
If both `:net` and `:ip` options are given, make sure they are compatible
(that is, give a IPv4 for `:inet` and IPv6 for `:inet6`).
Also, see the [*Loopback vs Public IP Addresses*
section](#module-loopback-vs-public-ip-addresses).
* `:port` - the port to run the server.
Defaults to `4000` (HTTP) and `4040` (HTTPS).
Must be `0` when `:ip` is a `{:local, path}` tuple.
* `:dispatch` - manually configure Cowboy's dispatch.
If this option is used, the given plug won't be initialized
nor dispatched to (and doing so becomes the user's responsibility).
* `:ref` - the reference name to be used.
Defaults to `plug.HTTP` (HTTP) and `plug.HTTPS` (HTTPS).
The default reference name does not contain the port, so in order
to serve the same plug on multiple ports you need to set the `:ref` accordingly.
For example, `ref: MyPlug_HTTP_4000`, `ref: MyPlug_HTTP_4001`, and so on.
This is the value that needs to be given on shutdown.
* `:compress` - if `true`, Cowboy will attempt to compress the response body.
Defaults to `false`.
* `:stream_handlers` - List of Cowboy `stream_handlers`,
see [Cowboy docs](https://ninenines.eu/docs/en/cowboy/2.12/manual/cowboy_http/).
* `:protocol_options` - Specifies remaining protocol options,
see the [Cowboy docs](https://ninenines.eu/docs/en/cowboy/2.12/manual/cowboy_http/).
* `:transport_options` - A keyword list specifying transport options,
see [Ranch docs](https://ninenines.eu/docs/en/ranch/1.7/manual/ranch/).
By default `:num_acceptors` will be set to `100` and `:max_connections`
to `16_384`.
All other options given at the top level must configure the underlying
socket. For HTTP connections, those options are listed under
[`ranch_tcp`](https://ninenines.eu/docs/en/ranch/1.7/manual/ranch_tcp/).
For example, you can set `:ipv6_v6only` to true if you want to bind only
on IPv6 addresses.
For HTTPS (SSL) connections, those options are described in
[`ranch_ssl`](https://ninenines.eu/docs/en/ranch/1.7/manual/ranch_ssl/).
See `https/3` for an example and read `Plug.SSL.configure/1` to
understand about our SSL defaults.
When using a Unix socket, OTP 21+ is required for `Plug.Static` and
`Plug.Conn.send_file/3` to behave correctly.
## Safety Limits
Cowboy sets different limits on URL size, header length, number of
headers, and so on to protect your application from attacks. For example,
the request line length defaults to 10k, which means Cowboy will return
`414` if a larger URL is given. You can change this under `:protocol_options`:
protocol_options: [max_request_line_length: 50_000]
Keep in mind that increasing those limits can pose a security risk.
Other times, browsers and proxies along the way may have equally strict
limits, which means the request will still fail or the URL will be
pruned. You can [consult all limits here](https://ninenines.eu/docs/en/cowboy/2.12/manual/cowboy_http/).
## Loopback vs Public IP Addresses
Should your application bind to a loopback address, such as `::1` (IPv6) or
`127.0.0.1` (IPv4), or a public one, such as `::0` (IPv6) or `0.0.0.0`
(IPv4)? It depends on how (and whether) you want it to be reachable from
other machines.
Loopback addresses are only reachable from the same host (`localhost` is
usually configured to resolve to a loopback address). You may wish to use one if:
* Your app is running in a development environment (such as your laptop) and
you don't want others on the same network to access it.
* Your app is running in production, but behind a reverse proxy. For
example, you might have [nginx](https://nginx.org/en/) bound to a public
address and serving HTTPS, but forwarding the traffic to your application
running on the same host. In that case, having your app bind to the
loopback address means that nginx can reach it, but outside traffic can
only reach it via nginx.
Public addresses are reachable from other hosts. You may wish to use one if:
* Your app is running in a container. In this case, its loopback address is
reachable only from within the container; to be accessible from outside the
container, it needs to bind to a public IP address.
* Your app is running in production without a reverse proxy, using Cowboy's
SSL support.
## Logging
You can configure which exceptions are logged via `:log_exceptions_with_status_code`
application environment variable. If the status code returned by `Plug.Exception.status/1`
for the exception falls into any of the configured ranges, the exception is logged.
By default it's set to `[500..599]`.
config :plug_cowboy,
log_exceptions_with_status_code: [400..599]
By default, `Plug.Cowboy` includes the entire `conn` to the log metadata for exceptions.
However, this metadata may contain sensitive information such as security headers or
cookies, which may be logged in plain text by certain logging backends. To prevent this,
you can configure the `:conn_in_exception_metadata` option to not include the `conn` in the metadata.
config :plug_cowboy,
conn_in_exception_metadata: false
## Instrumentation
`Plug.Cowboy` uses the [`telemetry` library](https://github.com/beam-telemetry/telemetry)
for instrumentation. The following span events are published during each request:
* `[:cowboy, :request, :start]` - dispatched at the beginning of the request
* `[:cowboy, :request, :stop]` - dispatched at the end of the request
* `[:cowboy, :request, :exception]` - dispatched at the end of a request that exits
A single event is published when the request ends with an early error:
* `[:cowboy, :request, :early_error]` - dispatched for requests terminated early by Cowboy
See [`cowboy_telemetry`](https://github.com/beam-telemetry/cowboy_telemetry#telemetry-events)
for more details on the events and their measurements and metadata.
To opt-out of this default instrumentation, you can manually configure
Cowboy with the option:
stream_handlers: [:cowboy_stream_h]
## WebSocket support
`Plug.Cowboy` supports upgrading HTTP requests to WebSocket connections via
the use of the `Plug.Conn.upgrade_adapter/3` function, called with `:websocket` as the second
argument. Applications should validate that the connection represents a valid WebSocket request
before calling this function (Cowboy will validate the connection as part of the upgrade
process, but does not provide any capacity for an application to be notified if the upgrade is
not successful). If an application wishes to negotiate WebSocket subprotocols or otherwise set
any response headers, it should do so before calling `Plug.Conn.upgrade_adapter/3`.
The third argument to `Plug.Conn.upgrade_adapter/3` defines the details of how Plug.Cowboy
should handle the WebSocket connection, and must take the form `{handler, handler_opts,
connection_opts}`, where values are as follows:
* `handler` is a module which implements the
[`:cowboy_websocket`](https://ninenines.eu/docs/en/cowboy/2.6/manual/cowboy_websocket/)
behaviour. Note that this module will NOT have its `c:cowboy_websocket.init/2` callback
called; only the 'later' parts of the `:cowboy_websocket` lifecycle are supported
* `handler_opts` is an arbitrary term which will be passed as the argument to
`c:cowboy_websocket.websocket_init/1`
* `connection_opts` is a map with any of [Cowboy's websockets options](https://ninenines.eu/docs/en/cowboy/2.6/manual/cowboy_websocket/#_opts)
"""
require Logger
@doc false
def start(_type, _args) do
Logger.add_translator({Plug.Cowboy.Translator, :translate})
Supervisor.start_link([], strategy: :one_for_one)
end
# Made public with @doc false for testing.
@doc false
def args(scheme, plug, plug_opts, cowboy_options) do
{cowboy_options, non_keyword_options} = Enum.split_with(cowboy_options, &match?({_, _}, &1))
cowboy_options
|> normalize_cowboy_options(scheme)
|> to_args(scheme, plug, plug_opts, non_keyword_options)
end
@doc """
Runs cowboy under HTTP.
## Example
# Starts a new interface:
Plug.Cowboy.http(MyPlug, [], port: 80)
# The interface above can be shut down with:
Plug.Cowboy.shutdown(MyPlug.HTTP)
"""
@spec http(module(), Keyword.t(), Keyword.t()) ::
{:ok, pid} | {:error, :eaddrinuse} | {:error, term}
def http(plug, opts, cowboy_options \\ []) do
run(:http, plug, opts, cowboy_options)
end
@doc """
Runs cowboy under HTTPS.
Besides the options described in the module documentation,
this function sets defaults and accepts all options defined
in `Plug.SSL.configure/1`.
## Example
# Starts a new interface:
Plug.Cowboy.https(
MyPlug,
[],
port: 443,
password: "SECRET",
otp_app: :my_app,
keyfile: "priv/ssl/key.pem",
certfile: "priv/ssl/cert.pem",
dhfile: "priv/ssl/dhparam.pem"
)
# The interface above can be shut down with:
Plug.Cowboy.shutdown(MyPlug.HTTPS)
"""
@spec https(module(), Keyword.t(), Keyword.t()) ::
{:ok, pid} | {:error, :eaddrinuse} | {:error, term}
def https(plug, opts, cowboy_options \\ []) do
Application.ensure_all_started(:ssl)
run(:https, plug, opts, cowboy_options)
end
@doc """
Shutdowns the given reference.
"""
@spec shutdown(:ranch.ref()) :: :ok | {:error, :not_found}
def shutdown(ref) do
:cowboy.stop_listener(ref)
end
@doc """
Returns a supervisor child spec to start Cowboy under a supervisor.
It supports all options as specified in the module documentation plus it
requires the following two options:
* `:scheme` - either `:http` or `:https`
* `:plug` - such as `MyPlug` or `{MyPlug, plug_opts}`
## Examples
Assuming your Plug module is named `MyApp` you can add it to your
supervision tree by using this function:
children = [
{Plug.Cowboy, scheme: :http, plug: MyApp, options: [port: 4040]}
]
Supervisor.start_link(children, strategy: :one_for_one)
"""
@spec child_spec(keyword()) :: Supervisor.child_spec()
def child_spec(opts) do
scheme = Keyword.fetch!(opts, :scheme)
{plug, plug_opts} =
case Keyword.fetch!(opts, :plug) do
{_, _} = tuple -> tuple
plug -> {plug, []}
end
# We support :options for backwards compatibility.
cowboy_opts =
opts
|> Keyword.drop([:scheme, :plug, :options])
|> Kernel.++(Keyword.get(opts, :options, []))
cowboy_args = args(scheme, plug, plug_opts, cowboy_opts)
[ref, transport_opts, proto_opts] = cowboy_args
{ranch_module, cowboy_protocol, transport_opts} =
case scheme do
:http ->
{:ranch_tcp, :cowboy_clear, transport_opts}
:https ->
%{socket_opts: socket_opts} = transport_opts
socket_opts =
socket_opts
|> Keyword.put_new(:next_protocols_advertised, ["h2", "http/1.1"])
|> Keyword.put_new(:alpn_preferred_protocols, ["h2", "http/1.1"])
{:ranch_ssl, :cowboy_tls, %{transport_opts | socket_opts: socket_opts}}
end
case :ranch.child_spec(ref, ranch_module, transport_opts, cowboy_protocol, proto_opts) do
{id, start, restart, shutdown, type, modules} ->
%{
id: id,
start: start,
restart: restart,
shutdown: shutdown,
type: type,
modules: modules
}
child_spec when is_map(child_spec) ->
child_spec
end
end
## Helpers
@protocol_options [:compress, :stream_handlers]
defp run(scheme, plug, opts, cowboy_options) do
case Application.ensure_all_started(:cowboy) do
{:ok, _} ->
nil
{:error, {:cowboy, _}} ->
raise "could not start the Cowboy application. Please ensure it is listed as a dependency in your mix.exs"
end
start =
case scheme do
:http -> :start_clear
:https -> :start_tls
other -> :erlang.error({:badarg, [other]})
end
:telemetry.attach(
:plug_cowboy,
[:cowboy, :request, :early_error],
&__MODULE__.handle_event/4,
nil
)
apply(:cowboy, start, args(scheme, plug, opts, cowboy_options))
end
defp normalize_cowboy_options(cowboy_options, :http) do
Keyword.put_new(cowboy_options, :port, 4000)
end
defp normalize_cowboy_options(cowboy_options, :https) do
cowboy_options
|> Keyword.put_new(:port, 4040)
|> Plug.SSL.configure()
|> case do
{:ok, options} -> options
{:error, message} -> fail(message)
end
end
defp to_args(opts, scheme, plug, plug_opts, non_keyword_opts) do
{timeout, opts} = Keyword.pop(opts, :timeout)
if timeout do
Logger.warning("the :timeout option for Cowboy webserver has no effect and must be removed")
end
opts = Keyword.delete(opts, :otp_app)
{ref, opts} = Keyword.pop(opts, :ref)
{dispatch, opts} = Keyword.pop(opts, :dispatch)
{protocol_options, opts} = Keyword.pop(opts, :protocol_options, [])
dispatch = :cowboy_router.compile(dispatch || dispatch_for(plug, plug_opts))
{extra_options, opts} = Keyword.split(opts, @protocol_options)
extra_options = set_stream_handlers(extra_options)
protocol_and_extra_options = :maps.from_list(protocol_options ++ extra_options)
protocol_options = Map.merge(%{env: %{dispatch: dispatch}}, protocol_and_extra_options)
{transport_options, socket_options} = Keyword.pop(opts, :transport_options, [])
{net, socket_options} = Keyword.pop(socket_options, :net)
socket_options = List.wrap(net) ++ non_keyword_opts ++ socket_options
transport_options =
transport_options
|> Keyword.put_new(:num_acceptors, 100)
|> Keyword.put_new(:max_connections, 16_384)
|> Keyword.update(
:socket_opts,
socket_options,
&(&1 ++ socket_options)
)
|> Map.new()
[ref || build_ref(plug, scheme), transport_options, protocol_options]
end
@default_stream_handlers [:cowboy_telemetry_h, :cowboy_stream_h]
defp set_stream_handlers(opts) do
compress = Keyword.get(opts, :compress)
stream_handlers = Keyword.get(opts, :stream_handlers)
case {compress, stream_handlers} do
{true, nil} ->
Keyword.put_new(opts, :stream_handlers, [:cowboy_compress_h | @default_stream_handlers])
{true, _} ->
raise "cannot set both compress and stream_handlers at once. " <>
"If you wish to set compress, please add `:cowboy_compress_h` to your stream handlers."
{_, nil} ->
Keyword.put_new(opts, :stream_handlers, @default_stream_handlers)
{_, _} ->
opts
end
end
defp build_ref(plug, scheme) do
Module.concat(plug, scheme |> to_string |> String.upcase())
end
defp dispatch_for(plug, opts) do
opts = plug.init(opts)
[{:_, [{:_, Plug.Cowboy.Handler, {plug, opts}}]}]
end
defp fail(message) do
raise ArgumentError, "could not start Cowboy2 adapter, " <> message
end
@doc false
def handle_event(
[:cowboy, :request, :early_error],
_,
%{reason: {:connection_error, :limit_reached, specific_reason}, partial_req: partial_req},
_
) do
Logger.error("""
Cowboy returned 431 because it was unable to parse the request headers.
This may happen because there are no headers, or there are too many headers
or the header name or value are too large (such as a large cookie).
More specific reason is:
#{inspect(specific_reason)}
You can customize those limits when configuring your http/https
server. The configuration option and default values are shown below:
protocol_options: [
max_header_name_length: 64,
max_header_value_length: 4096,
max_headers: 100
]
Request info:
peer: #{format_peer(partial_req.peer)}
method: #{partial_req.method || "<unable to parse>"}
path: #{partial_req.path || "<unable to parse>"}
""")
end
def handle_event(_, _, _, _) do
:ok
end
defp format_peer({addr, port}) do
"#{:inet_parse.ntoa(addr)}:#{port}"
end
end

174
deps/plug_cowboy/lib/plug/cowboy/conn.ex vendored Normal file
View File

@ -0,0 +1,174 @@
defmodule Plug.Cowboy.Conn do
@behaviour Plug.Conn.Adapter
@moduledoc false
def conn(req) do
%{
path: path,
host: host,
port: port,
method: method,
headers: headers,
qs: qs,
peer: {remote_ip, _}
} = req
%Plug.Conn{
adapter: {__MODULE__, req},
host: host,
method: method,
owner: self(),
path_info: split_path(path),
port: port,
remote_ip: remote_ip,
query_string: qs,
req_headers: to_headers_list(headers),
request_path: path,
scheme: String.to_atom(:cowboy_req.scheme(req))
}
end
@impl true
def send_resp(req, status, headers, body) do
req = to_headers_map(req, headers)
status = Integer.to_string(status) <> " " <> Plug.Conn.Status.reason_phrase(status)
req = :cowboy_req.reply(status, %{}, body, req)
{:ok, nil, req}
end
@impl true
def send_file(req, status, headers, path, offset, length) do
%File.Stat{type: :regular, size: size} = File.stat!(path)
length =
cond do
length == :all -> size
is_integer(length) -> length
end
body = {:sendfile, offset, length, path}
req = to_headers_map(req, headers)
req = :cowboy_req.reply(status, %{}, body, req)
{:ok, nil, req}
end
@impl true
def send_chunked(req, status, headers) do
req = to_headers_map(req, headers)
req = :cowboy_req.stream_reply(status, %{}, req)
{:ok, nil, req}
end
@impl true
def chunk(req, body) do
:cowboy_req.stream_body(body, :nofin, req)
end
@impl true
def read_req_body(req, opts) do
length = Keyword.get(opts, :length, 8_000_000)
read_length = Keyword.get(opts, :read_length, 1_000_000)
read_timeout = Keyword.get(opts, :read_timeout, 15_000)
opts = %{length: read_length, period: read_timeout}
read_req_body(req, opts, length, [])
end
defp read_req_body(req, opts, length, acc) when length >= 0 do
case :cowboy_req.read_body(req, opts) do
{:ok, data, req} -> {:ok, IO.iodata_to_binary([acc | data]), req}
{:more, data, req} -> read_req_body(req, opts, length - byte_size(data), [acc | data])
end
end
defp read_req_body(req, _opts, _length, acc) do
{:more, IO.iodata_to_binary(acc), req}
end
@impl true
def inform(req, status, headers) do
:cowboy_req.inform(status, to_headers_map(headers), req)
end
@impl true
def upgrade(req, :websocket, args) do
case args do
{handler, _state, cowboy_opts} when is_atom(handler) and is_map(cowboy_opts) ->
:ok
_ ->
raise ArgumentError,
"expected websocket upgrade on Cowboy to be on the format {handler :: atom(), arg :: term(), opts :: map()}, got: " <>
inspect(args)
end
{:ok, Map.put(req, :upgrade, {:websocket, args})}
end
def upgrade(_req, _protocol, _args), do: {:error, :not_supported}
@impl true
def push(req, path, headers) do
opts =
case {req.port, req.sock} do
{:undefined, {_, port}} -> %{port: port}
{port, _} when port in [80, 443] -> %{}
{port, _} -> %{port: port}
end
req = to_headers_map(req, headers)
:cowboy_req.push(path, %{}, req, opts)
end
@impl true
def get_peer_data(%{peer: {ip, port}, cert: cert}) do
%{
address: ip,
port: port,
ssl_cert: if(cert == :undefined, do: nil, else: cert)
}
end
@impl true
def get_http_protocol(req) do
:cowboy_req.version(req)
end
## Helpers
defp to_headers_list(headers) when is_list(headers) do
headers
end
defp to_headers_list(headers) when is_map(headers) do
:maps.to_list(headers)
end
defp to_headers_map(req, headers) do
headers = to_headers_map(headers)
Map.update(req, :resp_headers, headers, &Map.merge(&1, headers))
end
defp to_headers_map(headers) when is_list(headers) do
# Group set-cookie headers into a list for a single `set-cookie`
# key since cowboy 2 requires headers as a map.
Enum.reduce(headers, %{}, fn
{key = "set-cookie", value}, acc ->
case acc do
%{^key => existing} -> %{acc | key => [value | existing]}
%{} -> Map.put(acc, key, [value])
end
{key, value}, acc ->
case acc do
%{^key => existing} -> %{acc | key => existing <> ", " <> value}
%{} -> Map.put(acc, key, value)
end
end)
end
defp split_path(path) do
segments = :binary.split(path, "/", [:global])
for segment <- segments, segment != "", do: segment
end
end

View File

@ -0,0 +1,113 @@
defmodule Plug.Cowboy.Drainer do
@moduledoc """
Process to drain cowboy connections at shutdown.
When starting `Plug.Cowboy` in a supervision tree, it will create a listener that receives
requests and creates a connection process to handle that request. During shutdown, a
`Plug.Cowboy` process will immediately exit, closing the listener and any open connections
that are still being served. However, in most cases, it is desirable to allow connections
to complete before shutting down.
This module provides a process that during shutdown will close listeners and wait
for connections to complete. It should be placed after other supervised processes that
handle cowboy connections.
## Options
The following options can be given to the child spec:
* `:refs` - A list of refs to drain. `:all` is also supported and will drain all cowboy
listeners, including those started by means other than `Plug.Cowboy`.
* `:id` - The ID for the process.
Defaults to `Plug.Cowboy.Drainer`.
* `:shutdown` - How long to wait for connections to drain.
Defaults to 5000ms.
* `:check_interval` - How frequently to check if a listener's
connections have been drained. Defaults to 1000ms.
## Examples
# In your application
def start(_type, _args) do
children = [
{Plug.Cowboy, scheme: :http, plug: MyApp, options: [port: 4040]},
{Plug.Cowboy, scheme: :https, plug: MyApp, options: [port: 4041]},
{Plug.Cowboy.Drainer, refs: [MyApp.HTTP, MyApp.HTTPS]}
]
opts = [strategy: :one_for_one, name: MyApp.Supervisor]
Supervisor.start_link(children, opts)
end
"""
use GenServer
@doc false
@spec child_spec(opts :: Keyword.t()) :: Supervisor.child_spec()
def child_spec(opts) when is_list(opts) do
{spec_opts, opts} = Keyword.split(opts, [:id, :shutdown])
Supervisor.child_spec(
%{
id: __MODULE__,
start: {__MODULE__, :start_link, [opts]},
type: :worker
},
spec_opts
)
end
@doc false
def start_link(opts) do
opts
|> Keyword.fetch!(:refs)
|> validate_refs!()
GenServer.start_link(__MODULE__, opts)
end
@doc false
@impl true
def init(opts) do
Process.flag(:trap_exit, true)
{:ok, opts}
end
@doc false
@impl true
def terminate(_reason, opts) do
opts
|> Keyword.fetch!(:refs)
|> drain(opts[:check_interval] || opts[:drain_check_interval] || 1_000)
end
defp drain(:all, check_interval) do
:ranch.info()
|> Enum.map(&elem(&1, 0))
|> drain(check_interval)
end
defp drain(refs, check_interval) do
refs
|> Enum.filter(&suspend_listener/1)
|> Enum.each(&wait_for_connections(&1, check_interval))
end
defp suspend_listener(ref) do
:ranch.suspend_listener(ref) == :ok
end
defp wait_for_connections(ref, check_interval) do
:ranch.wait_for_connections(ref, :==, 0, check_interval)
end
defp validate_refs!(:all), do: :ok
defp validate_refs!(refs) when is_list(refs), do: :ok
defp validate_refs!(refs) do
raise ArgumentError,
":refs should be :all or a list of references, got: #{inspect(refs)}"
end
end

View File

@ -0,0 +1,73 @@
defmodule Plug.Cowboy.Handler do
@moduledoc false
@connection Plug.Cowboy.Conn
@already_sent {:plug_conn, :sent}
def init(req, {plug, opts}) do
conn = @connection.conn(req)
try do
conn
|> plug.call(opts)
|> maybe_send(plug)
|> case do
%Plug.Conn{adapter: {@connection, %{upgrade: {:websocket, websocket_args}} = req}} = conn ->
{handler, state, cowboy_opts} = websocket_args
{__MODULE__, copy_resp_headers(conn, req), {handler, state}, cowboy_opts}
%Plug.Conn{adapter: {@connection, req}} ->
{:ok, req, {plug, opts}}
end
catch
kind, reason ->
exit_on_error(kind, reason, __STACKTRACE__, {plug, :call, [conn, opts]})
after
receive do
@already_sent -> :ok
after
0 -> :ok
end
end
end
def upgrade(req, env, __MODULE__, {handler, state}, opts) do
:cowboy_websocket.upgrade(req, env, handler.module_info(:module), state, opts)
end
defp copy_resp_headers(%Plug.Conn{} = conn, req) do
Enum.reduce(conn.resp_headers, req, fn {key, val}, acc ->
:cowboy_req.set_resp_header(key, val, acc)
end)
end
defp exit_on_error(
:error,
%Plug.Conn.WrapperError{kind: kind, reason: reason, stack: stack},
_stack,
call
) do
exit_on_error(kind, reason, stack, call)
end
defp exit_on_error(:error, value, stack, call) do
exception = Exception.normalize(:error, value, stack)
:erlang.raise(:exit, {{exception, stack}, call}, [])
end
defp exit_on_error(:throw, value, stack, call) do
:erlang.raise(:exit, {{{:nocatch, value}, stack}, call}, [])
end
defp exit_on_error(:exit, value, _stack, call) do
:erlang.raise(:exit, {value, call}, [])
end
defp maybe_send(%Plug.Conn{state: :unset}, _plug), do: raise(Plug.Conn.NotSentError)
defp maybe_send(%Plug.Conn{state: :set} = conn, _plug), do: Plug.Conn.send_resp(conn)
defp maybe_send(%Plug.Conn{} = conn, _plug), do: conn
defp maybe_send(other, plug) do
raise "Cowboy2 adapter expected #{inspect(plug)} to return Plug.Conn but got: " <>
inspect(other)
end
end

View File

@ -0,0 +1,116 @@
defmodule Plug.Cowboy.Translator do
@moduledoc false
# Cowboy 2.12.0 and below error format
@doc """
The `translate/4` function expected by custom Logger translators.
"""
def translate(
min_level,
:error,
:format,
{~c"Ranch listener" ++ _, [ref, conn_pid, stream_id, stream_pid, reason, stack]}
) do
extra = [" (connection ", inspect(conn_pid), ", stream id ", inspect(stream_id), ?)]
translate_ranch(min_level, ref, extra, stream_pid, reason, stack)
end
# Cowboy 2.13.0 error format
def translate(
min_level,
:error,
:format,
{~c"Ranch listener" ++ _, [ref, conn_pid, stream_id, stream_pid, {reason, stack}]}
) do
extra = [" (connection ", inspect(conn_pid), ", stream id ", inspect(stream_id), ?)]
translate_ranch(min_level, ref, extra, stream_pid, reason, stack)
end
def translate(_min_level, _level, _kind, _data) do
:none
end
## Ranch/Cowboy
defp translate_ranch(
min_level,
_ref,
extra,
pid,
{reason, {mod, :call, [%Plug.Conn{} = conn, _opts]}},
_stack
) do
if log_exception?(reason) do
message = [
inspect(pid),
" running ",
inspect(mod),
extra,
" terminated\n",
conn_info(min_level, conn)
| Exception.format(:exit, reason, [])
]
metadata =
[
crash_reason: reason,
domain: [:cowboy]
] ++ maybe_conn_metadata(conn)
{:ok, message, metadata}
else
:skip
end
end
defp translate_ranch(_min_level, ref, extra, pid, reason, stack) do
{:ok,
[
"Ranch protocol ",
inspect(pid),
" of listener ",
inspect(ref),
extra,
" terminated\n"
| Exception.format_exit({reason, stack})
], crash_reason: {reason, stack}, domain: [:cowboy]}
end
defp log_exception?({%{__exception__: true} = exception, _}) do
status_ranges =
Application.get_env(:plug_cowboy, :log_exceptions_with_status_code, [500..599])
status = Plug.Exception.status(exception)
Enum.any?(status_ranges, &(status in &1))
end
defp log_exception?(_), do: true
defp conn_info(_min_level, conn) do
[server_info(conn), request_info(conn)]
end
defp server_info(%Plug.Conn{host: host, port: :undefined, scheme: scheme}) do
["Server: ", host, ?\s, ?(, Atom.to_string(scheme), ?), ?\n]
end
defp server_info(%Plug.Conn{host: host, port: port, scheme: scheme}) do
["Server: ", host, ":", Integer.to_string(port), ?\s, ?(, Atom.to_string(scheme), ?), ?\n]
end
defp request_info(%Plug.Conn{method: method, query_string: query_string} = conn) do
["Request: ", method, ?\s, path_to_iodata(conn.request_path, query_string), ?\n]
end
defp maybe_conn_metadata(conn) do
if Application.get_env(:plug_cowboy, :conn_in_exception_metadata, true) do
[conn: conn]
else
[]
end
end
defp path_to_iodata(path, ""), do: path
defp path_to_iodata(path, qs), do: [path, ??, qs]
end