Subiendo api v2
This commit is contained in:
		
							
								
								
									
										472
									
								
								deps/plug_cowboy/lib/plug/cowboy.ex
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										472
									
								
								deps/plug_cowboy/lib/plug/cowboy.ex
									
									
									
									
										vendored
									
									
										Normal 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
									
								
							
							
						
						
									
										174
									
								
								deps/plug_cowboy/lib/plug/cowboy/conn.ex
									
									
									
									
										vendored
									
									
										Normal 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
 | 
			
		||||
							
								
								
									
										113
									
								
								deps/plug_cowboy/lib/plug/cowboy/drainer.ex
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								deps/plug_cowboy/lib/plug/cowboy/drainer.ex
									
									
									
									
										vendored
									
									
										Normal 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
 | 
			
		||||
							
								
								
									
										73
									
								
								deps/plug_cowboy/lib/plug/cowboy/handler.ex
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								deps/plug_cowboy/lib/plug/cowboy/handler.ex
									
									
									
									
										vendored
									
									
										Normal 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
 | 
			
		||||
							
								
								
									
										116
									
								
								deps/plug_cowboy/lib/plug/cowboy/translator.ex
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								deps/plug_cowboy/lib/plug/cowboy/translator.ex
									
									
									
									
										vendored
									
									
										Normal 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
 | 
			
		||||
		Reference in New Issue
	
	Block a user