74 lines
		
	
	
		
			2.1 KiB
		
	
	
	
		
			Elixir
		
	
	
	
	
	
			
		
		
	
	
			74 lines
		
	
	
		
			2.1 KiB
		
	
	
	
		
			Elixir
		
	
	
	
	
	
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
 |