zf

zenflows testing
git clone https://s.sonu.ch/~srfsh/zf.git
Log | Files | Refs | Submodules | README | LICENSE

conn.ex (67803B)


      1 alias Plug.Conn.Unfetched
      2 
      3 defmodule Plug.Conn do
      4   @moduledoc """
      5   The Plug connection.
      6 
      7   This module defines a struct and the main functions for working with
      8   requests and responses in an HTTP connection.
      9 
     10   Note request headers are normalized to lowercase and response
     11   headers are expected to have lowercase keys.
     12 
     13   ## Request fields
     14 
     15   These fields contain request information:
     16 
     17     * `host` - the requested host as a binary, example: `"www.example.com"`
     18     * `method` - the request method as a binary, example: `"GET"`
     19     * `path_info` - the path split into segments, example: `["hello", "world"]`
     20     * `script_name` - the initial portion of the URL's path that corresponds to
     21       the application routing, as segments, example: `["sub","app"]`
     22     * `request_path` - the requested path, example: `/trailing/and//double//slashes/`
     23     * `port` - the requested port as an integer, example: `80`
     24     * `remote_ip` - the IP of the client, example: `{151, 236, 219, 228}`. This field
     25       is meant to be overwritten by plugs that understand e.g. the `X-Forwarded-For`
     26       header or HAProxy's PROXY protocol. It defaults to peer's IP
     27     * `req_headers` - the request headers as a list, example: `[{"content-type", "text/plain"}]`.
     28       Note all headers will be downcased
     29     * `scheme` - the request scheme as an atom, example: `:http`
     30     * `query_string` - the request query string as a binary, example: `"foo=bar"`
     31 
     32   ## Fetchable fields
     33 
     34   The request information in these fields is not populated until it is fetched
     35   using the associated `fetch_` function. For example, the `cookies` field uses
     36   `fetch_cookies/2`.
     37 
     38   If you access these fields before fetching them, they will be returned as
     39   `Plug.Conn.Unfetched` structs.
     40 
     41     * `cookies`- the request cookies with the response cookies
     42     * `body_params` - the request body params, populated through a `Plug.Parsers` parser.
     43     * `query_params` - the request query params, populated through `fetch_query_params/2`
     44     * `path_params` - the request path params, populated by routers such as `Plug.Router`
     45     * `params` - the request params, the result of merging the `:path_params` on top of
     46        `:body_params` on top of `:query_params`
     47     * `req_cookies` - the request cookies (without the response ones)
     48 
     49   ## Session vs Assigns
     50 
     51   HTTP is stateless.
     52   This means that a server begins each request cycle with no knowledge about
     53   the client except the request itself.
     54   Its response may include one or more `"Set-Cookie"` headers, asking the client
     55   to send that value back in a `"Cookie"` header on subsequent requests.
     56   This is the basis for stateful interactions with a client, so that the server
     57   can remember the client's name, the contents of their shopping cart, and so on.
     58 
     59   In Plug, a "session" is a place to store data that persists from one request
     60   to the next.
     61   Typically, this data is stored in a cookie using `Plug.Session.COOKIE`.
     62   A minimal approach would be to store only a user's id in the session, then
     63   use that during the request cycle to look up other information (in a database
     64   or elsewhere).
     65   More can be stored in a session cookie, but be careful: this makes requests
     66   and responses heavier, and clients may reject cookies beyond a certain size.
     67   Also, whatever is stored in a session cookie is not shared between a user's
     68   different browsers or devices.
     69 
     70   If the session is stored elsewhere, such as with `Plug.Session.ETS`,
     71   something like a user id would still be needed to look it up on each request.
     72 
     73   Unlike data in a session, data in the `assigns` field lasts only for a single
     74   request.
     75   A typical use case would be for an authentication plug to look up a
     76   user by id and store the user's details in the assigns for later plugs to
     77   access during the same request.
     78   When the next request happens, this data will be gone.
     79 
     80   To summarize: `assigns` is for storing data to be accessed during the current
     81   request, and the session is for storing data to be accessed in subsequent
     82   requests.
     83 
     84   ## Response fields
     85 
     86   These fields contain response information:
     87 
     88     * `resp_body` - the response body, by default is an empty string. It is set
     89       to nil after the response is sent, except for test connections. The response
     90       charset used defaults to "utf-8".
     91     * `resp_cookies` - the response cookies with their name and options
     92     * `resp_headers` - the response headers as a list of tuples, by default `cache-control`
     93       is set to `"max-age=0, private, must-revalidate"`. Note, response headers
     94       are expected to have lowercase keys.
     95     * `status` - the response status
     96 
     97   ## Connection fields
     98 
     99     * `assigns` - shared user data as a map
    100     * `owner` - the Elixir process that owns the connection
    101     * `halted` - the boolean status on whether the pipeline was halted
    102     * `secret_key_base` - a secret key used to verify and encrypt cookies.
    103       the field must be set manually whenever one of those features are used.
    104       This data must be kept in the connection and never used directly, always
    105       use `Plug.Crypto.KeyGenerator.generate/3` to derive keys from it
    106     * `state` - the connection state
    107 
    108   The connection state is used to track the connection lifecycle. It starts as
    109   `:unset` but is changed to `:set` (via `resp/3`) or `:set_chunked`
    110   (used only for `before_send` callbacks by `send_chunked/2`) or `:file`
    111   (when invoked via `send_file/3`). Its final result is `:sent`, `:file`, `:chunked` 
    112   or `:upgraded` depending on the response model.
    113 
    114   ## Private fields
    115 
    116   These fields are reserved for libraries/framework usage.
    117 
    118     * `adapter` - holds the adapter information in a tuple
    119     * `private` - shared library data as a map
    120 
    121   ## Custom status codes
    122 
    123   Plug allows status codes to be overridden or added in order to allow new codes
    124   not directly specified by Plug or its adapters. Adding or overriding a status
    125   code is done through the Mix configuration of the `:plug` application. For
    126   example, to override the existing 404 reason phrase for the 404 status code
    127   ("Not Found" by default) and add a new 998 status code, the following config
    128   can be specified:
    129 
    130       config :plug, :statuses, %{
    131         404 => "Actually This Was Found",
    132         998 => "Not An RFC Status Code"
    133       }
    134 
    135   As this configuration is Plug specific, Plug will need to be recompiled for
    136   the changes to take place: this will not happen automatically as dependencies
    137   are not automatically recompiled when their configuration changes. To recompile
    138   Plug:
    139 
    140       mix deps.clean --build plug
    141 
    142   The atoms that can be used in place of the status code in many functions are
    143   inflected from the reason phrase of the status code. With the above
    144   configuration, the following will all work:
    145 
    146       put_status(conn, :not_found)                     # 404
    147       put_status(conn, :actually_this_was_found)       # 404
    148       put_status(conn, :not_an_rfc_status_code)        # 998
    149 
    150   Even though 404 has been overridden, the `:not_found` atom can still be used
    151   to set the status to 404 as well as the new atom `:actually_this_was_found`
    152   inflected from the reason phrase "Actually This Was Found".
    153 
    154   ## Protocol Upgrades
    155 
    156   Plug provides basic support for protocol upgrades via the `upgrade_adapter/3`
    157   function to facilitate connection upgrades to protocols such as WebSockets.
    158   As the name suggests, this functionality is adapter-dependent and the 
    159   functionality & requirements of a given upgrade require explicit coordination
    160   between a Plug application & the underlying adapter. Plug provides upgrade 
    161   related functionality only to the extent necessary to allow a Plug application 
    162   to request protocol upgrades from the underlying adapter. See the documentation
    163   for `upgrade_adapter/3` for details.
    164   """
    165 
    166   @type adapter :: {module, term}
    167   @type assigns :: %{optional(atom) => any}
    168   @type body :: iodata
    169   @type req_cookies :: %{optional(binary) => binary}
    170   @type cookies :: %{optional(binary) => term}
    171   @type halted :: boolean
    172   @type headers :: [{binary, binary}]
    173   @type host :: binary
    174   @type int_status :: non_neg_integer | nil
    175   @type owner :: pid
    176   @type method :: binary
    177   @type query_param :: binary | %{optional(binary) => query_param} | [query_param]
    178   @type query_params :: %{optional(binary) => query_param}
    179   @type params :: %{optional(binary) => term}
    180   @type port_number :: :inet.port_number()
    181   @type query_string :: String.t()
    182   @type resp_cookies :: %{optional(binary) => map()}
    183   @type scheme :: :http | :https
    184   @type secret_key_base :: binary | nil
    185   @type segments :: [binary]
    186   @type state :: :unset | :set | :set_chunked | :set_file | :file | :chunked | :sent | :upgraded
    187   @type status :: atom | int_status
    188 
    189   @type t :: %__MODULE__{
    190           adapter: adapter,
    191           assigns: assigns,
    192           body_params: params | Unfetched.t(),
    193           cookies: cookies | Unfetched.t(),
    194           halted: halted,
    195           host: host,
    196           method: method,
    197           owner: owner,
    198           params: params | Unfetched.t(),
    199           path_info: segments,
    200           path_params: query_params,
    201           port: :inet.port_number(),
    202           private: assigns,
    203           query_params: query_params | Unfetched.t(),
    204           query_string: query_string,
    205           remote_ip: :inet.ip_address(),
    206           req_cookies: req_cookies | Unfetched.t(),
    207           req_headers: headers,
    208           request_path: binary,
    209           resp_body: body | nil,
    210           resp_cookies: resp_cookies,
    211           resp_headers: headers,
    212           scheme: scheme,
    213           script_name: segments,
    214           secret_key_base: secret_key_base,
    215           state: state,
    216           status: int_status
    217         }
    218 
    219   defstruct adapter: {Plug.MissingAdapter, nil},
    220             assigns: %{},
    221             body_params: %Unfetched{aspect: :body_params},
    222             cookies: %Unfetched{aspect: :cookies},
    223             halted: false,
    224             host: "www.example.com",
    225             method: "GET",
    226             owner: nil,
    227             params: %Unfetched{aspect: :params},
    228             path_info: [],
    229             path_params: %{},
    230             port: 0,
    231             private: %{},
    232             query_params: %Unfetched{aspect: :query_params},
    233             query_string: "",
    234             remote_ip: nil,
    235             req_cookies: %Unfetched{aspect: :cookies},
    236             req_headers: [],
    237             request_path: "",
    238             resp_body: nil,
    239             resp_cookies: %{},
    240             resp_headers: [{"cache-control", "max-age=0, private, must-revalidate"}],
    241             scheme: :http,
    242             script_name: [],
    243             secret_key_base: nil,
    244             state: :unset,
    245             status: nil
    246 
    247   defmodule NotSentError do
    248     defexception message: "a response was neither set nor sent from the connection"
    249 
    250     @moduledoc """
    251     Error raised when no response is sent in a request
    252     """
    253   end
    254 
    255   defmodule AlreadySentError do
    256     defexception message: "the response was already sent"
    257 
    258     @moduledoc """
    259     Error raised when trying to modify or send an already sent response
    260     """
    261   end
    262 
    263   defmodule CookieOverflowError do
    264     defexception message: "cookie exceeds maximum size of 4096 bytes"
    265 
    266     @moduledoc """
    267     Error raised when the cookie exceeds the maximum size of 4096 bytes.
    268     """
    269   end
    270 
    271   defmodule InvalidHeaderError do
    272     defexception message: "header is invalid"
    273 
    274     @moduledoc ~S"""
    275     Error raised when trying to send a header that has errors, for example:
    276 
    277       * the header key contains uppercase chars
    278       * the header value contains newlines \n
    279     """
    280   end
    281 
    282   defmodule InvalidQueryError do
    283     @moduledoc """
    284     Raised when the request string is malformed, for example:
    285 
    286       * the query has bad utf-8 encoding
    287       * the query fails to www-form decode
    288     """
    289 
    290     defexception message: "query string is invalid", plug_status: 400
    291   end
    292 
    293   alias Plug.Conn
    294   @epoch {{1970, 1, 1}, {0, 0, 0}}
    295   @already_sent {:plug_conn, :sent}
    296   @unsent [:unset, :set, :set_chunked, :set_file]
    297 
    298   @doc """
    299   Assigns a value to a key in the connection.
    300 
    301   The "assigns" storage is meant to be used to store values in the connection
    302   so that other plugs in your plug pipeline can access them. The assigns storage
    303   is a map.
    304 
    305   ## Examples
    306 
    307       iex> conn.assigns[:hello]
    308       nil
    309       iex> conn = assign(conn, :hello, :world)
    310       iex> conn.assigns[:hello]
    311       :world
    312 
    313   """
    314   @spec assign(t, atom, term) :: t
    315   def assign(%Conn{assigns: assigns} = conn, key, value) when is_atom(key) do
    316     %{conn | assigns: Map.put(assigns, key, value)}
    317   end
    318 
    319   @doc """
    320   Assigns multiple values to keys in the connection.
    321 
    322   Equivalent to multiple calls to `assign/3`.
    323 
    324   ## Examples
    325 
    326       iex> conn.assigns[:hello]
    327       nil
    328       iex> conn = merge_assigns(conn, hello: :world)
    329       iex> conn.assigns[:hello]
    330       :world
    331 
    332   """
    333   @spec merge_assigns(t, Keyword.t()) :: t
    334   def merge_assigns(%Conn{assigns: assigns} = conn, keyword) when is_list(keyword) do
    335     %{conn | assigns: Enum.into(keyword, assigns)}
    336   end
    337 
    338   @doc false
    339   @spec async_assign(t, atom, (() -> term)) :: t
    340   def async_assign(%Conn{} = conn, key, fun) when is_atom(key) and is_function(fun, 0) do
    341     IO.warn("Plug.Conn.async_assign/3 is deprecated, please call assign + Task.async instead")
    342     assign(conn, key, Task.async(fun))
    343   end
    344 
    345   @doc false
    346   @spec await_assign(t, atom, timeout) :: t
    347   def await_assign(%Conn{} = conn, key, timeout \\ 5000) when is_atom(key) do
    348     IO.warn(
    349       "Plug.Conn.await_assign/3 is deprecated, please fetch the assign and call Task.await instead"
    350     )
    351 
    352     task = Map.fetch!(conn.assigns, key)
    353     assign(conn, key, Task.await(task, timeout))
    354   end
    355 
    356   @doc """
    357   Assigns a new **private** key and value in the connection.
    358 
    359   This storage is meant to be used by libraries and frameworks to avoid writing
    360   to the user storage (the `:assigns` field). It is recommended for
    361   libraries/frameworks to prefix the keys with the library name.
    362 
    363   For example, if a plug called `my_plug` needs to store a `:hello`
    364   key, it would store it as `:my_plug_hello`:
    365 
    366       iex> conn.private[:my_plug_hello]
    367       nil
    368       iex> conn = put_private(conn, :my_plug_hello, :world)
    369       iex> conn.private[:my_plug_hello]
    370       :world
    371 
    372   """
    373   @spec put_private(t, atom, term) :: t
    374   def put_private(%Conn{private: private} = conn, key, value) when is_atom(key) do
    375     %{conn | private: Map.put(private, key, value)}
    376   end
    377 
    378   @doc """
    379   Assigns multiple **private** keys and values in the connection.
    380 
    381   Equivalent to multiple `put_private/3` calls.
    382 
    383   ## Examples
    384 
    385       iex> conn.private[:my_plug_hello]
    386       nil
    387       iex> conn = merge_private(conn, my_plug_hello: :world)
    388       iex> conn.private[:my_plug_hello]
    389       :world
    390 
    391   """
    392   @spec merge_private(t, Keyword.t()) :: t
    393   def merge_private(%Conn{private: private} = conn, keyword) when is_list(keyword) do
    394     %{conn | private: Enum.into(keyword, private)}
    395   end
    396 
    397   @doc """
    398   Stores the given status code in the connection.
    399 
    400   The status code can be `nil`, an integer, or an atom. The list of allowed
    401   atoms is available in `Plug.Conn.Status`.
    402 
    403   Raises a `Plug.Conn.AlreadySentError` if the connection has already been
    404   `:sent`, `:chunked` or `:upgraded`.
    405 
    406   ## Examples
    407 
    408       Plug.Conn.put_status(conn, :not_found)
    409       Plug.Conn.put_status(conn, 200)
    410 
    411   """
    412   @spec put_status(t, status) :: t
    413   def put_status(%Conn{state: state}, _status) when state not in @unsent do
    414     raise AlreadySentError
    415   end
    416 
    417   def put_status(%Conn{} = conn, nil), do: %{conn | status: nil}
    418   def put_status(%Conn{} = conn, status), do: %{conn | status: Plug.Conn.Status.code(status)}
    419 
    420   @doc """
    421   Sends a response to the client.
    422 
    423   It expects the connection state to be `:set`, otherwise raises an
    424   `ArgumentError` for `:unset` connections or a `Plug.Conn.AlreadySentError` for
    425   already `:sent`, `:chunked` or `:upgraded` connections.
    426 
    427   At the end sets the connection state to `:sent`.
    428 
    429   Note that this function does not halt the connection, so if
    430   subsequent plugs try to send another response, it will error out.
    431   Use `halt/1` after this function if you want to halt the plug pipeline.
    432 
    433   ## Examples
    434 
    435       conn
    436       |> Plug.Conn.resp(404, "Not found")
    437       |> Plug.Conn.send_resp()
    438 
    439   """
    440   @spec send_resp(t) :: t | no_return
    441   def send_resp(conn)
    442 
    443   def send_resp(%Conn{state: :unset}) do
    444     raise ArgumentError, "cannot send a response that was not set"
    445   end
    446 
    447   def send_resp(%Conn{adapter: {adapter, payload}, state: :set, owner: owner} = conn) do
    448     conn = run_before_send(conn, :set)
    449 
    450     {:ok, body, payload} =
    451       adapter.send_resp(payload, conn.status, conn.resp_headers, conn.resp_body)
    452 
    453     send(owner, @already_sent)
    454     %{conn | adapter: {adapter, payload}, resp_body: body, state: :sent}
    455   end
    456 
    457   def send_resp(%Conn{}) do
    458     raise AlreadySentError
    459   end
    460 
    461   @doc """
    462   Sends a file as the response body with the given `status`
    463   and optionally starting at the given offset until the given length.
    464 
    465   If available, the file is sent directly over the socket using
    466   the operating system `sendfile` operation.
    467 
    468   It expects a connection that has not been `:sent`, `:chunked` or `:upgraded` yet and sets its
    469   state to `:file` afterwards. Otherwise raises `Plug.Conn.AlreadySentError`.
    470 
    471   ## Examples
    472 
    473       Plug.Conn.send_file(conn, 200, "README.md")
    474 
    475   """
    476   @spec send_file(t, status, filename :: binary, offset :: integer, length :: integer | :all) ::
    477           t | no_return
    478   def send_file(conn, status, file, offset \\ 0, length \\ :all)
    479 
    480   def send_file(%Conn{state: state}, status, _file, _offset, _length)
    481       when state not in @unsent do
    482     _ = Plug.Conn.Status.code(status)
    483     raise AlreadySentError
    484   end
    485 
    486   def send_file(
    487         %Conn{adapter: {adapter, payload}, owner: owner} = conn,
    488         status,
    489         file,
    490         offset,
    491         length
    492       )
    493       when is_binary(file) do
    494     if file =~ "\0" do
    495       raise ArgumentError, "cannot send_file/5 with null byte"
    496     end
    497 
    498     conn =
    499       run_before_send(%{conn | status: Plug.Conn.Status.code(status), resp_body: nil}, :set_file)
    500 
    501     {:ok, body, payload} =
    502       adapter.send_file(payload, conn.status, conn.resp_headers, file, offset, length)
    503 
    504     send(owner, @already_sent)
    505     %{conn | adapter: {adapter, payload}, state: :file, resp_body: body}
    506   end
    507 
    508   @doc """
    509   Sends the response headers as a chunked response.
    510 
    511   It expects a connection that has not been `:sent` or `:upgraded` yet and sets its
    512   state to `:chunked` afterwards. Otherwise, raises `Plug.Conn.AlreadySentError`.
    513   After `send_chunked/2` is called, chunks can be sent to the client via
    514   the `chunk/2` function.
    515 
    516   HTTP/2 does not support chunking and will instead stream the response without a
    517   transfer encoding. When using HTTP/1.1, the Cowboy adapter will stream the response
    518   instead of emitting chunks if the `content-length` header has been set before calling
    519   `send_chunked/2`.
    520   """
    521   @spec send_chunked(t, status) :: t | no_return
    522   def send_chunked(%Conn{state: state}, status)
    523       when state not in @unsent do
    524     _ = Plug.Conn.Status.code(status)
    525     raise AlreadySentError
    526   end
    527 
    528   def send_chunked(%Conn{adapter: {adapter, payload}, owner: owner} = conn, status) do
    529     conn = %{conn | status: Plug.Conn.Status.code(status), resp_body: nil}
    530     conn = run_before_send(conn, :set_chunked)
    531     {:ok, body, payload} = adapter.send_chunked(payload, conn.status, conn.resp_headers)
    532     send(owner, @already_sent)
    533     %{conn | adapter: {adapter, payload}, state: :chunked, resp_body: body}
    534   end
    535 
    536   @doc """
    537   Sends a chunk as part of a chunked response.
    538 
    539   It expects a connection with state `:chunked` as set by
    540   `send_chunked/2`. It returns `{:ok, conn}` in case of success,
    541   otherwise `{:error, reason}`.
    542 
    543   To stream data use `Enum.reduce_while/3` instead of `Enum.into/2`.
    544   `Enum.reduce_while/3` allows aborting the execution if `chunk/2` fails to
    545   deliver the chunk of data.
    546 
    547   ## Example
    548 
    549       Enum.reduce_while(~w(each chunk as a word), conn, fn (chunk, conn) ->
    550         case Plug.Conn.chunk(conn, chunk) do
    551           {:ok, conn} ->
    552             {:cont, conn}
    553           {:error, :closed} ->
    554             {:halt, conn}
    555         end
    556       end)
    557 
    558   """
    559   @spec chunk(t, body) :: {:ok, t} | {:error, term} | no_return
    560   def chunk(%Conn{adapter: {adapter, payload}, state: :chunked} = conn, chunk) do
    561     if iodata_empty?(chunk) do
    562       {:ok, conn}
    563     else
    564       case adapter.chunk(payload, chunk) do
    565         :ok -> {:ok, conn}
    566         {:ok, body, payload} -> {:ok, %{conn | resp_body: body, adapter: {adapter, payload}}}
    567         {:error, _} = error -> error
    568       end
    569     end
    570   end
    571 
    572   def chunk(%Conn{}, chunk) when is_binary(chunk) or is_list(chunk) do
    573     raise ArgumentError,
    574           "chunk/2 expects a chunked response. Please ensure " <>
    575             "you have called send_chunked/2 before you send a chunk"
    576   end
    577 
    578   defp iodata_empty?(""), do: true
    579   defp iodata_empty?([]), do: true
    580   defp iodata_empty?([head | tail]), do: iodata_empty?(head) and iodata_empty?(tail)
    581   defp iodata_empty?(_), do: false
    582 
    583   @doc """
    584   Sends a response with the given status and body.
    585 
    586   This is equivalent to setting the status and the body and then
    587   calling `send_resp/1`.
    588 
    589   Note that this function does not halt the connection, so if
    590   subsequent plugs try to send another response, it will error out.
    591   Use `halt/1` after this function if you want to halt the plug pipeline.
    592 
    593   ## Examples
    594 
    595       Plug.Conn.send_resp(conn, 404, "Not found")
    596 
    597   """
    598   @spec send_resp(t, status, body) :: t | no_return
    599   def send_resp(%Conn{} = conn, status, body) do
    600     conn |> resp(status, body) |> send_resp()
    601   end
    602 
    603   @doc """
    604   Sets the response to the given `status` and `body`.
    605 
    606   It sets the connection state to `:set` (if not already `:set`)
    607   and raises `Plug.Conn.AlreadySentError` if it was already `:sent`, `:chunked` or `:upgraded`.
    608 
    609   If you also want to send the response, use `send_resp/1` after this
    610   or use `send_resp/3`.
    611 
    612   The status can be an integer, an atom, or `nil`. See `Plug.Conn.Status`
    613   for more information.
    614 
    615   ## Examples
    616 
    617       Plug.Conn.resp(conn, 404, "Not found")
    618 
    619   """
    620   @spec resp(t, status, body) :: t
    621   def resp(%Conn{state: state}, status, _body)
    622       when state not in @unsent do
    623     _ = Plug.Conn.Status.code(status)
    624     raise AlreadySentError
    625   end
    626 
    627   def resp(%Conn{}, _status, nil) do
    628     raise ArgumentError, "response body cannot be set to nil"
    629   end
    630 
    631   def resp(%Conn{} = conn, status, body)
    632       when is_binary(body) or is_list(body) do
    633     %{conn | status: Plug.Conn.Status.code(status), resp_body: body, state: :set}
    634   end
    635 
    636   @doc """
    637   Returns the request peer data if one is present.
    638   """
    639   @spec get_peer_data(t) :: Plug.Conn.Adapter.peer_data()
    640   def get_peer_data(%Conn{adapter: {adapter, payload}}) do
    641     adapter.get_peer_data(payload)
    642   end
    643 
    644   @doc """
    645   Returns the HTTP protocol and version.
    646 
    647   ## Examples
    648 
    649       iex> get_http_protocol(conn)
    650       :"HTTP/1.1"
    651 
    652   """
    653   @spec get_http_protocol(t) :: Plug.Conn.Adapter.http_protocol()
    654   def get_http_protocol(%Conn{adapter: {adapter, payload}}) do
    655     adapter.get_http_protocol(payload)
    656   end
    657 
    658   @doc """
    659   Returns the values of the request header specified by `key`.
    660 
    661   ## Examples
    662 
    663       iex> get_req_header(conn, "accept")
    664       ["application/json"]
    665 
    666   """
    667   @spec get_req_header(t, binary) :: [binary]
    668   def get_req_header(%Conn{req_headers: headers}, key) when is_binary(key) do
    669     for {^key, value} <- headers, do: value
    670   end
    671 
    672   @doc ~S"""
    673   Prepends the list of headers to the connection request headers.
    674 
    675   Similar to `put_req_header` this functions adds a new request header
    676   (`key`) but rather than replacing the existing one it prepends another
    677   header with the same `key`.
    678 
    679   The "host" header will be overridden by `conn.host` and should not be set
    680   with this method. Instead, do `%Plug.Conn{conn | host: value}`.
    681 
    682   Because header keys are case-insensitive in both HTTP/1.1 and HTTP/2,
    683   it is recommended for header keys to be in lowercase, to avoid sending
    684   duplicate keys in a request.
    685   Additionally, requests with mixed-case headers served over HTTP/2 are not
    686   considered valid by common clients, resulting in dropped requests.
    687   As a convenience, when using the `Plug.Adapters.Conn.Test` adapter, any
    688   headers that aren't lowercase will raise a `Plug.Conn.InvalidHeaderError`.
    689 
    690   Raises a `Plug.Conn.AlreadySentError` if the connection has already been
    691   `:sent`, `:chunked` or `:upgraded`.
    692 
    693   ## Examples
    694 
    695       Plug.Conn.prepend_req_headers(conn, [{"accept", "application/json"}])
    696 
    697   """
    698   @spec prepend_req_headers(t, headers) :: t
    699   def prepend_req_headers(conn, headers)
    700 
    701   def prepend_req_headers(%Conn{state: state}, _headers) when state not in @unsent do
    702     raise AlreadySentError
    703   end
    704 
    705   def prepend_req_headers(%Conn{adapter: adapter, req_headers: req_headers} = conn, headers)
    706       when is_list(headers) do
    707     for {key, _value} <- headers do
    708       validate_req_header!(adapter, key)
    709     end
    710 
    711     %{conn | req_headers: headers ++ req_headers}
    712   end
    713 
    714   @doc """
    715   Merges a series of request headers into the connection.
    716 
    717   The "host" header will be overridden by `conn.host` and should not be set
    718   with this method. Instead, do `%Plug.Conn{conn | host: value}`.
    719 
    720   Because header keys are case-insensitive in both HTTP/1.1 and HTTP/2,
    721   it is recommended for header keys to be in lowercase, to avoid sending
    722   duplicate keys in a request.
    723   Additionally, requests with mixed-case headers served over HTTP/2 are not
    724   considered valid by common clients, resulting in dropped requests.
    725   As a convenience, when using the `Plug.Adapters.Conn.Test` adapter, any
    726   headers that aren't lowercase will raise a `Plug.Conn.InvalidHeaderError`.
    727 
    728   ## Example
    729 
    730       Plug.Conn.merge_req_headers(conn, [{"accept", "text/plain"}, {"X-1337", "5P34K"}])
    731 
    732   """
    733   @spec merge_req_headers(t, Enum.t()) :: t
    734   def merge_req_headers(conn, headers)
    735 
    736   def merge_req_headers(%Conn{state: state}, _headers) when state not in @unsent do
    737     raise AlreadySentError
    738   end
    739 
    740   def merge_req_headers(conn, headers) when headers == %{} do
    741     conn
    742   end
    743 
    744   def merge_req_headers(%Conn{req_headers: current, adapter: adapter} = conn, headers) do
    745     headers =
    746       Enum.reduce(headers, current, fn {key, value}, acc
    747                                        when is_binary(key) and is_binary(value) ->
    748         validate_req_header!(adapter, key)
    749         List.keystore(acc, key, 0, {key, value})
    750       end)
    751 
    752     %{conn | req_headers: headers}
    753   end
    754 
    755   @doc """
    756   Adds a new request header (`key`) if not present, otherwise replaces the
    757   previous value of that header with `value`.
    758 
    759   The "host" header will be overridden by `conn.host` and should not be set
    760   with this method. Instead, do `%Plug.Conn{conn | host: value}`.
    761 
    762   Because header keys are case-insensitive in both HTTP/1.1 and HTTP/2,
    763   it is recommended for header keys to be in lowercase, to avoid sending
    764   duplicate keys in a request.
    765   Additionally, requests with mixed-case headers served over HTTP/2 are not
    766   considered valid by common clients, resulting in dropped requests.
    767   As a convenience, when using the `Plug.Adapters.Conn.Test` adapter, any
    768   headers that aren't lowercase will raise a `Plug.Conn.InvalidHeaderError`.
    769 
    770   Raises a `Plug.Conn.AlreadySentError` if the connection has already been
    771   `:sent`, `:chunked` or `:upgraded`.
    772 
    773   ## Examples
    774 
    775       Plug.Conn.put_req_header(conn, "accept", "application/json")
    776 
    777   """
    778   @spec put_req_header(t, binary, binary) :: t
    779   def put_req_header(conn, key, value)
    780 
    781   def put_req_header(%Conn{state: state}, _key, _value) when state not in @unsent do
    782     raise AlreadySentError
    783   end
    784 
    785   def put_req_header(%Conn{adapter: adapter, req_headers: headers} = conn, key, value)
    786       when is_binary(key) and is_binary(value) do
    787     validate_req_header!(adapter, key)
    788     %{conn | req_headers: List.keystore(headers, key, 0, {key, value})}
    789   end
    790 
    791   @doc """
    792   Deletes a request header if present.
    793 
    794   Raises a `Plug.Conn.AlreadySentError` if the connection has already been
    795   `:sent`, `:chunked` or `:upgraded`.
    796 
    797   ## Examples
    798 
    799       Plug.Conn.delete_req_header(conn, "content-type")
    800 
    801   """
    802   @spec delete_req_header(t, binary) :: t
    803   def delete_req_header(conn, key)
    804 
    805   def delete_req_header(%Conn{state: state}, _key) when state not in @unsent do
    806     raise AlreadySentError
    807   end
    808 
    809   def delete_req_header(%Conn{req_headers: headers} = conn, key)
    810       when is_binary(key) do
    811     %{conn | req_headers: List.keydelete(headers, key, 0)}
    812   end
    813 
    814   @doc """
    815   Updates a request header if present, otherwise it sets it to an initial
    816   value.
    817 
    818   Raises a `Plug.Conn.AlreadySentError` if the connection has already been
    819   `:sent`, `:chunked` or `:upgraded`.
    820 
    821   Only the first value of the header `key` is updated if present.
    822 
    823   ## Examples
    824 
    825       Plug.Conn.update_req_header(
    826         conn,
    827         "accept",
    828         "application/json; charset=utf-8",
    829         &(&1 <> "; charset=utf-8")
    830       )
    831 
    832   """
    833   @spec update_req_header(t, binary, binary, (binary -> binary)) :: t
    834   def update_req_header(conn, key, initial, fun)
    835 
    836   def update_req_header(%Conn{state: state}, _key, _initial, _fun) when state not in @unsent do
    837     raise AlreadySentError
    838   end
    839 
    840   def update_req_header(%Conn{} = conn, key, initial, fun)
    841       when is_binary(key) and is_binary(initial) and is_function(fun, 1) do
    842     case get_req_header(conn, key) do
    843       [] -> put_req_header(conn, key, initial)
    844       [current | _] -> put_req_header(conn, key, fun.(current))
    845     end
    846   end
    847 
    848   @doc """
    849   Returns the values of the response header specified by `key`.
    850 
    851   ## Examples
    852 
    853       iex> conn = %{conn | resp_headers: [{"content-type", "text/plain"}]}
    854       iex> get_resp_header(conn, "content-type")
    855       ["text/plain"]
    856 
    857   """
    858   @spec get_resp_header(t, binary) :: [binary]
    859   def get_resp_header(%Conn{resp_headers: headers}, key) when is_binary(key) do
    860     for {^key, value} <- headers, do: value
    861   end
    862 
    863   @doc ~S"""
    864   Adds a new response header (`key`) if not present, otherwise replaces the
    865   previous value of that header with `value`.
    866 
    867   Because header keys are case-insensitive in both HTTP/1.1 and HTTP/2,
    868   it is recommended for header keys to be in lowercase, to avoid sending
    869   duplicate keys in a request.
    870   Additionally, responses with mixed-case headers served over HTTP/2 are not
    871   considered valid by common clients, resulting in dropped responses.
    872   As a convenience, when using the `Plug.Adapters.Conn.Test` adapter, any
    873   headers that aren't lowercase will raise a `Plug.Conn.InvalidHeaderError`.
    874 
    875   Raises a `Plug.Conn.AlreadySentError` if the connection has already been
    876   `:sent`, `:chunked` or `:upgraded`.
    877 
    878   Raises a `Plug.Conn.InvalidHeaderError` if the header value contains control
    879   feed (`\r`) or newline (`\n`) characters.
    880 
    881   ## Examples
    882 
    883       Plug.Conn.put_resp_header(conn, "content-type", "application/json")
    884 
    885   """
    886   @spec put_resp_header(t, binary, binary) :: t
    887   def put_resp_header(%Conn{state: state}, _key, _value) when state not in @unsent do
    888     raise AlreadySentError
    889   end
    890 
    891   def put_resp_header(%Conn{adapter: adapter, resp_headers: headers} = conn, key, value)
    892       when is_binary(key) and is_binary(value) do
    893     validate_header_key_if_test!(adapter, key)
    894     validate_header_value!(key, value)
    895     %{conn | resp_headers: List.keystore(headers, key, 0, {key, value})}
    896   end
    897 
    898   @doc ~S"""
    899   Prepends the list of headers to the connection response headers.
    900 
    901   Similar to `put_resp_header` this functions adds a new response header
    902   (`key`) but rather than replacing the existing one it prepends another header
    903   with the same `key`.
    904 
    905   It is recommended for header keys to be in lowercase, to avoid sending
    906   duplicate keys in a request.
    907   Additionally, responses with mixed-case headers served over HTTP/2 are not
    908   considered valid by common clients, resulting in dropped responses.
    909   As a convenience, when using the `Plug.Adapters.Conn.Test` adapter, any
    910   headers that aren't lowercase will raise a `Plug.Conn.InvalidHeaderError`.
    911 
    912   Raises a `Plug.Conn.AlreadySentError` if the connection has already been
    913   `:sent`, `:chunked` or `:upgraded`.
    914 
    915   Raises a `Plug.Conn.InvalidHeaderError` if the header value contains control
    916   feed (`\r`) or newline (`\n`) characters.
    917 
    918   ## Examples
    919 
    920       Plug.Conn.prepend_resp_headers(conn, [{"content-type", "application/json"}])
    921 
    922   """
    923   @spec prepend_resp_headers(t, headers) :: t
    924   def prepend_resp_headers(conn, headers)
    925 
    926   def prepend_resp_headers(%Conn{state: state}, _headers) when state not in @unsent do
    927     raise AlreadySentError
    928   end
    929 
    930   def prepend_resp_headers(%Conn{adapter: adapter, resp_headers: resp_headers} = conn, headers)
    931       when is_list(headers) do
    932     for {key, value} <- headers do
    933       validate_header_key_if_test!(adapter, key)
    934       validate_header_value!(key, value)
    935     end
    936 
    937     %{conn | resp_headers: headers ++ resp_headers}
    938   end
    939 
    940   @doc """
    941   Merges a series of response headers into the connection.
    942 
    943   It is recommended for header keys to be in lowercase, to avoid sending
    944   duplicate keys in a request.
    945   Additionally, responses with mixed-case headers served over HTTP/2 are not
    946   considered valid by common clients, resulting in dropped responses.
    947   As a convenience, when using the `Plug.Adapters.Conn.Test` adapter, any
    948   headers that aren't lowercase will raise a `Plug.Conn.InvalidHeaderError`.
    949 
    950   ## Example
    951 
    952       Plug.Conn.merge_resp_headers(conn, [{"content-type", "text/plain"}, {"X-1337", "5P34K"}])
    953 
    954   """
    955   @spec merge_resp_headers(t, Enum.t()) :: t
    956   def merge_resp_headers(conn, headers)
    957 
    958   def merge_resp_headers(%Conn{state: state}, _headers) when state not in @unsent do
    959     raise AlreadySentError
    960   end
    961 
    962   def merge_resp_headers(conn, headers) when headers == %{} do
    963     conn
    964   end
    965 
    966   def merge_resp_headers(%Conn{resp_headers: current, adapter: adapter} = conn, headers) do
    967     headers =
    968       Enum.reduce(headers, current, fn {key, value}, acc
    969                                        when is_binary(key) and is_binary(value) ->
    970         validate_header_key_if_test!(adapter, key)
    971         validate_header_value!(key, value)
    972         List.keystore(acc, key, 0, {key, value})
    973       end)
    974 
    975     %{conn | resp_headers: headers}
    976   end
    977 
    978   @doc """
    979   Deletes a response header if present.
    980 
    981   Raises a `Plug.Conn.AlreadySentError` if the connection has already been
    982   `:sent`, `:chunked` or `:upgraded`.
    983 
    984   ## Examples
    985 
    986       Plug.Conn.delete_resp_header(conn, "content-type")
    987 
    988   """
    989   @spec delete_resp_header(t, binary) :: t
    990   def delete_resp_header(%Conn{state: state}, _key) when state not in @unsent do
    991     raise AlreadySentError
    992   end
    993 
    994   def delete_resp_header(%Conn{resp_headers: headers} = conn, key)
    995       when is_binary(key) do
    996     %{conn | resp_headers: List.keydelete(headers, key, 0)}
    997   end
    998 
    999   @doc """
   1000   Updates a response header if present, otherwise it sets it to an initial
   1001   value.
   1002 
   1003   Raises a `Plug.Conn.AlreadySentError` if the connection has already been
   1004   `:sent`, `:chunked` or `:upgraded`.
   1005 
   1006   Only the first value of the header `key` is updated if present.
   1007 
   1008   ## Examples
   1009 
   1010       Plug.Conn.update_resp_header(
   1011         conn,
   1012         "content-type",
   1013         "application/json; charset=utf-8",
   1014         &(&1 <> "; charset=utf-8")
   1015       )
   1016 
   1017   """
   1018   @spec update_resp_header(t, binary, binary, (binary -> binary)) :: t
   1019   def update_resp_header(conn, key, initial, fun)
   1020 
   1021   def update_resp_header(%Conn{state: state}, _key, _initial, _fun) when state not in @unsent do
   1022     raise AlreadySentError
   1023   end
   1024 
   1025   def update_resp_header(%Conn{} = conn, key, initial, fun)
   1026       when is_binary(key) and is_binary(initial) and is_function(fun, 1) do
   1027     case get_resp_header(conn, key) do
   1028       [] -> put_resp_header(conn, key, initial)
   1029       [current | _] -> put_resp_header(conn, key, fun.(current))
   1030     end
   1031   end
   1032 
   1033   @doc """
   1034   Sets the value of the `"content-type"` response header taking into account the
   1035   `charset`.
   1036 
   1037   If `charset` is `nil`, the value of the `"content-type"` response header won't
   1038   specify a charset.
   1039 
   1040   ## Examples
   1041 
   1042       iex> conn = put_resp_content_type(conn, "application/json")
   1043       iex> get_resp_header(conn, "content-type")
   1044       ["application/json; charset=utf-8"]
   1045 
   1046   """
   1047   @spec put_resp_content_type(t, binary, binary | nil) :: t
   1048   def put_resp_content_type(conn, content_type, charset \\ "utf-8")
   1049 
   1050   def put_resp_content_type(conn, content_type, nil) when is_binary(content_type) do
   1051     put_resp_header(conn, "content-type", content_type)
   1052   end
   1053 
   1054   def put_resp_content_type(conn, content_type, charset)
   1055       when is_binary(content_type) and is_binary(charset) do
   1056     put_resp_header(conn, "content-type", "#{content_type}; charset=#{charset}")
   1057   end
   1058 
   1059   @doc """
   1060   Fetches query parameters from the query string.
   1061 
   1062   Params are decoded as `"x-www-form-urlencoded"` in which key/value pairs
   1063   are separated by `&` and keys are separated from values by `=`.
   1064 
   1065   This function does not fetch parameters from the body. To fetch
   1066   parameters from the body, use the `Plug.Parsers` plug.
   1067 
   1068   ## Options
   1069 
   1070     * `:length` - the maximum query string length. Defaults to `1_000_000` bytes.
   1071       Keep in mind the webserver you are using may have a more strict limit. For
   1072       example, for the Cowboy webserver, [please read](https://hexdocs.pm/plug_cowboy/Plug.Cowboy.html#module-safety-limits).
   1073 
   1074     * `:validate_utf8` - boolean that tells whether or not to validate the keys and
   1075       values of the decoded query string are UTF-8 encoded. Defaults to `true`.
   1076 
   1077   """
   1078   @spec fetch_query_params(t, Keyword.t()) :: t
   1079   def fetch_query_params(conn, opts \\ [])
   1080 
   1081   def fetch_query_params(%Conn{query_params: %Unfetched{}} = conn, opts) do
   1082     %{params: params, query_string: query_string} = conn
   1083     length = Keyword.get(opts, :length, 1_000_000)
   1084 
   1085     if byte_size(query_string) > length do
   1086       raise InvalidQueryError,
   1087         message:
   1088           "maximum query string length is #{length}, got a query with #{byte_size(query_string)} bytes",
   1089         plug_status: 414
   1090     end
   1091 
   1092     query_params =
   1093       Plug.Conn.Query.decode(
   1094         query_string,
   1095         %{},
   1096         Plug.Conn.InvalidQueryError,
   1097         Keyword.get(opts, :validate_utf8, true)
   1098       )
   1099 
   1100     case params do
   1101       %Unfetched{} -> %{conn | query_params: query_params, params: query_params}
   1102       %{} -> %{conn | query_params: query_params, params: Map.merge(query_params, params)}
   1103     end
   1104   end
   1105 
   1106   def fetch_query_params(%Conn{} = conn, _opts) do
   1107     conn
   1108   end
   1109 
   1110   @doc """
   1111   Reads the request body.
   1112 
   1113   This function reads a chunk of the request body up to a given length (specified
   1114   by the `:length` option). If there is more data to be read, then
   1115   `{:more, partial_body, conn}` is returned. Otherwise `{:ok, body, conn}` is
   1116   returned. In case of an error reading the socket, `{:error, reason}` is
   1117   returned as per `:gen_tcp.recv/2`.
   1118 
   1119   Like all functions in this module, the `conn` returned by `read_body` must
   1120   be passed to the next stage of your pipeline and should not be ignored.
   1121 
   1122   In order to, for instance, support slower clients you can tune the
   1123   `:read_length` and `:read_timeout` options. These specify how much time should
   1124   be allowed to pass for each read from the underlying socket.
   1125 
   1126   Because the request body can be of any size, reading the body will only
   1127   work once, as Plug will not cache the result of these operations. If you
   1128   need to access the body multiple times, it is your responsibility to store
   1129   it. Finally keep in mind some plugs like `Plug.Parsers` may read the body,
   1130   so the body may be unavailable after being accessed by such plugs.
   1131 
   1132   This function is able to handle both chunked and identity transfer-encoding
   1133   by default.
   1134 
   1135   ## Options
   1136 
   1137     * `:length` - sets the maximum number of bytes to read from the body on
   1138       every call, defaults to `8_000_000` bytes
   1139     * `:read_length` - sets the amount of bytes to read at one time from the
   1140       underlying socket to fill the chunk, defaults to `1_000_000` bytes
   1141     * `:read_timeout` - sets the timeout for each socket read, defaults to
   1142       `15_000` milliseconds
   1143 
   1144   The values above are not meant to be exact. For example, setting the
   1145   length to `8_000_000` may end up reading some hundred bytes more from
   1146   the socket until we halt.
   1147 
   1148   ## Examples
   1149 
   1150       {:ok, body, conn} = Plug.Conn.read_body(conn, length: 1_000_000)
   1151 
   1152   """
   1153   @spec read_body(t, Keyword.t()) ::
   1154           {:ok, binary, t}
   1155           | {:more, binary, t}
   1156           | {:error, term}
   1157   def read_body(%Conn{adapter: {adapter, state}} = conn, opts \\ []) do
   1158     case adapter.read_req_body(state, opts) do
   1159       {:ok, data, state} ->
   1160         {:ok, data, %{conn | adapter: {adapter, state}}}
   1161 
   1162       {:more, data, state} ->
   1163         {:more, data, %{conn | adapter: {adapter, state}}}
   1164 
   1165       {:error, reason} ->
   1166         {:error, reason}
   1167     end
   1168   end
   1169 
   1170   @doc """
   1171   Reads the headers of a multipart request.
   1172 
   1173   It returns `{:ok, headers, conn}` with the headers or
   1174   `{:done, conn}` if there are no more parts.
   1175 
   1176   Once `read_part_headers/2` is invoked, you may call
   1177   `read_part_body/2` to read the body associated to the headers.
   1178   If `read_part_headers/2` is called instead, the body is automatically
   1179   skipped until the next part headers.
   1180 
   1181   ## Options
   1182 
   1183     * `:length` - sets the maximum number of bytes to read from the body for
   1184       each chunk, defaults to `64_000` bytes
   1185     * `:read_length` - sets the amount of bytes to read at one time from the
   1186       underlying socket to fill the chunk, defaults to `64_000` bytes
   1187     * `:read_timeout` - sets the timeout for each socket read, defaults to
   1188       `5_000` milliseconds
   1189 
   1190   """
   1191   @spec read_part_headers(t, Keyword.t()) :: {:ok, headers, t} | {:done, t}
   1192   def read_part_headers(%Conn{adapter: {adapter, state}} = conn, opts \\ []) do
   1193     opts = opts ++ [length: 64_000, read_length: 64_000, read_timeout: 5000]
   1194 
   1195     case init_multipart(conn) do
   1196       {boundary, buffer} ->
   1197         {data, state} = read_multipart_from_buffer_or_adapter(buffer, adapter, state, opts)
   1198         read_part_headers(conn, data, boundary, adapter, state, opts)
   1199 
   1200       :done ->
   1201         {:done, conn}
   1202     end
   1203   end
   1204 
   1205   defp read_part_headers(conn, data, boundary, adapter, state, opts) do
   1206     case :plug_multipart.parse_headers(data, boundary) do
   1207       {:ok, headers, rest} ->
   1208         {:ok, headers, store_multipart(conn, {boundary, rest}, adapter, state)}
   1209 
   1210       :more ->
   1211         {_, next, state} = next_multipart(adapter, state, opts)
   1212         read_part_headers(conn, data <> next, boundary, adapter, state, opts)
   1213 
   1214       {:more, rest} ->
   1215         {_, next, state} = next_multipart(adapter, state, opts)
   1216         read_part_headers(conn, rest <> next, boundary, adapter, state, opts)
   1217 
   1218       {:done, _} ->
   1219         {:done, store_multipart(conn, :done, adapter, state)}
   1220     end
   1221   end
   1222 
   1223   @doc """
   1224   Reads the body of a multipart request.
   1225 
   1226   Returns `{:ok, body, conn}` if all body has been read,
   1227   `{:more, binary, conn}` otherwise, and `{:done, conn}`
   1228   if there is no more body.
   1229 
   1230   It accepts the same options as `read_body/2`.
   1231   """
   1232   @spec read_part_body(t, Keyword.t()) :: {:ok, binary, t} | {:more, binary, t} | {:done, t}
   1233   def read_part_body(%Conn{adapter: {adapter, state}} = conn, opts) do
   1234     case init_multipart(conn) do
   1235       {boundary, buffer} ->
   1236         # Note we will read the whole length from the socket
   1237         # and then break it apart as necessary.
   1238         length = Keyword.get(opts, :length, 8_000_000)
   1239         {data, state} = read_multipart_from_buffer_or_adapter(buffer, adapter, state, opts)
   1240         read_part_body(conn, data, "", length, boundary, adapter, state, opts)
   1241 
   1242       :done ->
   1243         {:done, conn}
   1244     end
   1245   end
   1246 
   1247   defp read_part_body(%Conn{} = conn, data, acc, length, boundary, adapter, state, _opts)
   1248        when byte_size(acc) > length do
   1249     {:more, acc, store_multipart(conn, {boundary, data}, adapter, state)}
   1250   end
   1251 
   1252   defp read_part_body(%Conn{} = conn, data, acc, length, boundary, adapter, state, opts) do
   1253     case :plug_multipart.parse_body(data, boundary) do
   1254       {:ok, body} ->
   1255         {_, next, state} = next_multipart(adapter, state, opts)
   1256         acc = prepend_unless_empty(acc, body)
   1257         read_part_body(conn, next, acc, length, boundary, adapter, state, opts)
   1258 
   1259       {:ok, body, rest} ->
   1260         {_, next, state} = next_multipart(adapter, state, opts)
   1261         next = prepend_unless_empty(rest, next)
   1262         acc = prepend_unless_empty(acc, body)
   1263         read_part_body(conn, next, acc, length, boundary, adapter, state, opts)
   1264 
   1265       :done ->
   1266         {:ok, acc, store_multipart(conn, {boundary, ""}, adapter, state)}
   1267 
   1268       {:done, body} ->
   1269         {:ok, acc <> body, store_multipart(conn, {boundary, ""}, adapter, state)}
   1270 
   1271       {:done, body, rest} ->
   1272         {:ok, acc <> body, store_multipart(conn, {boundary, rest}, adapter, state)}
   1273     end
   1274   end
   1275 
   1276   @compile {:inline, prepend_unless_empty: 2}
   1277   defp prepend_unless_empty("", body), do: body
   1278   defp prepend_unless_empty(acc, body), do: acc <> body
   1279 
   1280   defp init_multipart(%{private: %{plug_multipart: plug_multipart}}) do
   1281     plug_multipart
   1282   end
   1283 
   1284   defp init_multipart(%{req_headers: req_headers}) do
   1285     {_, content_type} = List.keyfind(req_headers, "content-type", 0)
   1286     {:ok, "multipart", _, keys} = Plug.Conn.Utils.content_type(content_type)
   1287 
   1288     case keys do
   1289       %{"boundary" => boundary} -> {boundary, ""}
   1290       %{} -> :done
   1291     end
   1292   end
   1293 
   1294   defp next_multipart(adapter, state, opts) do
   1295     case adapter.read_req_body(state, opts) do
   1296       {:ok, "", _} -> raise "invalid multipart, body terminated too soon"
   1297       valid -> valid
   1298     end
   1299   end
   1300 
   1301   defp store_multipart(conn, multipart, adapter, state) do
   1302     %{put_in(conn.private[:plug_multipart], multipart) | adapter: {adapter, state}}
   1303   end
   1304 
   1305   defp read_multipart_from_buffer_or_adapter("", adapter, state, opts) do
   1306     {_, data, state} = adapter.read_req_body(state, opts)
   1307     {data, state}
   1308   end
   1309 
   1310   defp read_multipart_from_buffer_or_adapter(buffer, _adapter, state, _opts) do
   1311     {buffer, state}
   1312   end
   1313 
   1314   @doc """
   1315   Sends an informational response to the client.
   1316 
   1317   An informational response, such as an early hint, must happen prior to a response
   1318   being sent. If an informational request is attempted after a response is sent then
   1319   a `Plug.Conn.AlreadySentError` will be raised. Only status codes from 100-199 are valid.
   1320 
   1321   To use inform for early hints send one or more informs with a status of 103.
   1322 
   1323   If the adapter does not support informational responses then this is a noop.
   1324 
   1325   Most HTTP/1.1 clients do not properly support informational responses but some
   1326   proxies require it to support server push for HTTP/2. You can call
   1327   `get_http_protocol/1` to retrieve the protocol and version.
   1328   """
   1329   @spec inform(t, status, Keyword.t()) :: t
   1330   def inform(%Conn{} = conn, status, headers \\ []) do
   1331     status_code = Plug.Conn.Status.code(status)
   1332     adapter_inform(conn, status_code, headers)
   1333     conn
   1334   end
   1335 
   1336   @doc """
   1337   Sends an information response to a client but raises if the adapter does not support inform.
   1338 
   1339   See `inform/3` for more information.
   1340   """
   1341   @spec inform!(t, status, Keyword.t()) :: t
   1342   def inform!(%Conn{adapter: {adapter, _}} = conn, status, headers \\ []) do
   1343     status_code = Plug.Conn.Status.code(status)
   1344 
   1345     case adapter_inform(conn, status_code, headers) do
   1346       :ok ->
   1347         conn
   1348 
   1349       _ ->
   1350         raise "inform is not supported by #{inspect(adapter)}." <>
   1351                 "You should either delete the call to `inform!/3` or switch to an " <>
   1352                 "adapter that does support informational such as Plug.Cowboy"
   1353     end
   1354   end
   1355 
   1356   defp adapter_inform(_conn, status, _headers)
   1357        when not (status >= 100 and status <= 199 and is_integer(status)) do
   1358     raise ArgumentError, "inform expects a status code between 100 and 199, got: #{status}"
   1359   end
   1360 
   1361   defp adapter_inform(%Conn{state: state}, _status, _headers)
   1362        when state not in @unsent do
   1363     raise AlreadySentError
   1364   end
   1365 
   1366   defp adapter_inform(%Conn{adapter: {adapter, payload}}, status, headers),
   1367     do: adapter.inform(payload, status, headers)
   1368 
   1369   @doc """
   1370   Request a protocol upgrade from the underlying adapter.
   1371 
   1372   The precise semantics of an upgrade are deliberately left unspecified here in order to
   1373   support arbitrary upgrades, even to protocols which may not exist today. The primary intent of
   1374   this function is solely to allow an application to issue an upgrade request, not to manage how
   1375   a given protocol upgrade takes place or what APIs the application must support in order to serve
   1376   this updated protocol. For details in this regard, consult the documentation of the underlying
   1377   adapter (such a Plug.Cowboy or Bandit).
   1378 
   1379   Takes an argument describing the requested upgrade (for example, `:websocket`), and an argument
   1380   which contains arbitrary data which the underlying adapter is expected to interpret in the
   1381   context of the requested upgrade.
   1382 
   1383   If the upgrade is accepted by the adapter, the returned `Plug.Conn` will have a `state` of
   1384   `:upgraded`. This state is considered equivalently to a 'sent' state, and is subject to the same
   1385   limitation on subsequent mutating operations. Note that there is no guarantee or expectation
   1386   that the actual upgrade process is undertaken within this function; it is entirely possible that
   1387   the server will only do the actual upgrade later in the connection lifecycle.
   1388 
   1389   If the adapter does not support the requested upgrade then this is a noop and the returned 
   1390   `Plug.Conn` will be unchanged. The application can detect this and operate on the conn as it
   1391   normally would in order to indicate an upgrade failure to the client.
   1392   """
   1393   @spec upgrade_adapter(t, atom, term) :: t
   1394   def upgrade_adapter(%Conn{adapter: {adapter, payload}, state: state} = conn, protocol, args)
   1395       when state in @unsent do
   1396     case adapter.upgrade(payload, protocol, args) do
   1397       {:ok, payload} ->
   1398         %{conn | adapter: {adapter, payload}, state: :upgraded}
   1399 
   1400       {:error, :not_supported} ->
   1401         raise ArgumentError, "upgrade to #{protocol} not supported by #{inspect(adapter)}"
   1402     end
   1403   end
   1404 
   1405   def upgrade_adapter(_conn, _protocol, _args) do
   1406     raise AlreadySentError
   1407   end
   1408 
   1409   @doc """
   1410   Pushes a resource to the client.
   1411 
   1412   Server pushes must happen prior to a response being sent. If a server
   1413   push is attempted after a response is sent then a `Plug.Conn.AlreadySentError`
   1414   will be raised.
   1415 
   1416   If the adapter does not support server push then this is a noop.
   1417 
   1418   Note that certain browsers (such as Google Chrome) will not accept a pushed
   1419   resource if your certificate is not trusted. In the case of Chrome this means
   1420   a valid cert with a SAN. See https://www.chromestatus.com/feature/4981025180483584
   1421   """
   1422   @deprecated "Most browsers and clients have removed push support"
   1423   @spec push(t, String.t(), Keyword.t()) :: t
   1424   def push(%Conn{} = conn, path, headers \\ []) do
   1425     adapter_push(conn, path, headers)
   1426     conn
   1427   end
   1428 
   1429   @doc """
   1430   Pushes a resource to the client but raises if the adapter
   1431   does not support server push.
   1432   """
   1433   @deprecated "Most browsers and clients have removed push support"
   1434   @spec push!(t, String.t(), Keyword.t()) :: t
   1435   def push!(%Conn{adapter: {adapter, _}} = conn, path, headers \\ []) do
   1436     case adapter_push(conn, path, headers) do
   1437       :ok ->
   1438         conn
   1439 
   1440       _ ->
   1441         raise "server push not supported by #{inspect(adapter)}." <>
   1442                 "You should either delete the call to `push!/3` or switch to an " <>
   1443                 "adapter that does support server push such as Plug.Cowboy."
   1444     end
   1445   end
   1446 
   1447   defp adapter_push(%Conn{state: state}, _path, _headers)
   1448        when state not in @unsent do
   1449     raise AlreadySentError
   1450   end
   1451 
   1452   defp adapter_push(%Conn{adapter: {adapter, payload}}, path, headers) do
   1453     headers =
   1454       case List.keyfind(headers, "accept", 0) do
   1455         nil -> [{"accept", MIME.from_path(path)} | headers]
   1456         _ -> headers
   1457       end
   1458 
   1459     adapter.push(payload, path, headers)
   1460   end
   1461 
   1462   @doc """
   1463   Fetches cookies from the request headers.
   1464 
   1465   ## Options
   1466 
   1467     * `:signed` - a list of one or more cookies that are signed and must
   1468       be verified accordingly
   1469 
   1470     * `:encrypted` - a list of one or more cookies that are encrypted and
   1471       must be decrypted accordingly
   1472 
   1473   See `put_resp_cookie/4` for more information.
   1474   """
   1475   @spec fetch_cookies(t, Keyword.t()) :: t
   1476   def fetch_cookies(conn, opts \\ [])
   1477 
   1478   def fetch_cookies(%Conn{req_cookies: %Unfetched{}} = conn, opts) do
   1479     %{resp_cookies: resp_cookies, req_headers: req_headers} = conn
   1480 
   1481     req_cookies =
   1482       for {"cookie", cookie} <- req_headers,
   1483           kv <- Plug.Conn.Cookies.decode(cookie),
   1484           into: %{},
   1485           do: kv
   1486 
   1487     cookies =
   1488       Enum.reduce(resp_cookies, req_cookies, fn {key, opts}, acc ->
   1489         if value = Map.get(opts, :value) do
   1490           Map.put(acc, key, value)
   1491         else
   1492           Map.delete(acc, key)
   1493         end
   1494       end)
   1495 
   1496     fetch_cookies(%{conn | req_cookies: req_cookies, cookies: cookies}, opts)
   1497   end
   1498 
   1499   def fetch_cookies(%Conn{} = conn, []) do
   1500     conn
   1501   end
   1502 
   1503   def fetch_cookies(%Conn{} = conn, opts) do
   1504     %{req_cookies: req_cookies, cookies: cookies, secret_key_base: secret_key_base} = conn
   1505 
   1506     cookies =
   1507       verify_or_decrypt(
   1508         opts[:signed],
   1509         req_cookies,
   1510         cookies,
   1511         &Plug.Crypto.verify(secret_key_base, &1 <> "_cookie", &2, keys: Plug.Keys)
   1512       )
   1513 
   1514     cookies =
   1515       verify_or_decrypt(
   1516         opts[:encrypted],
   1517         req_cookies,
   1518         cookies,
   1519         &Plug.Crypto.decrypt(secret_key_base, &1 <> "_cookie", &2, keys: Plug.Keys)
   1520       )
   1521 
   1522     %{conn | cookies: cookies}
   1523   end
   1524 
   1525   defp verify_or_decrypt(names, req_cookies, cookies, fun) do
   1526     names
   1527     |> List.wrap()
   1528     |> Enum.reduce(cookies, fn name, acc ->
   1529       if value = req_cookies[name] do
   1530         case fun.(name, value) do
   1531           {:ok, verified_value} -> Map.put(acc, name, verified_value)
   1532           {_, _} -> Map.delete(acc, name)
   1533         end
   1534       else
   1535         acc
   1536       end
   1537     end)
   1538   end
   1539 
   1540   @doc """
   1541   Puts a response cookie in the connection.
   1542 
   1543   If the `:sign` or `:encrypt` flag are given, then the cookie
   1544   value can be any term.
   1545 
   1546   If the cookie is not signed nor encrypted, then the value must be a binary.
   1547   Note the value is not automatically escaped. Therefore if you want to store
   1548   values with non-alphanumeric characters, you must either sign or encrypt
   1549   the cookie or consider explicitly escaping the cookie value by using a
   1550   function such as `Base.encode64(value, padding: false)` when writing and
   1551   `Base.decode64(encoded, padding: false)` when reading the cookie.
   1552   It is important for padding to be disabled since `=` is not a valid
   1553   character in cookie values.
   1554 
   1555   ## Signing and encrypting cookies
   1556 
   1557   This function allows you to automatically sign and encrypt cookies.
   1558   When signing or encryption is enabled, then any Elixir value can be
   1559   stored in the cookie (except anonymous functions for security reasons).
   1560   Once a value is signed or encrypted, you must also call `fetch_cookies/2`
   1561   with the name of the cookies that are either signed or encrypted.
   1562 
   1563   To sign, you would do:
   1564 
   1565       put_resp_cookie(conn, "my-cookie", %{user_id: user.id}, sign: true)
   1566 
   1567   and then:
   1568 
   1569       fetch_cookies(conn, signed: ~w(my-cookie))
   1570 
   1571   To encrypt, you would do:
   1572 
   1573       put_resp_cookie(conn, "my-cookie", %{user_id: user.id}, encrypt: true)
   1574 
   1575   and then:
   1576 
   1577       fetch_cookies(conn, encrypted: ~w(my-cookie))
   1578 
   1579   By default a signed or encrypted cookie is only valid for a day, unless
   1580   a `:max_age` is specified.
   1581 
   1582   The signing and encryption keys are derived from the connection's
   1583   `secret_key_base` using a salt that is built by appending "_cookie" to
   1584   the cookie name. Care should be taken not to derive other keys using
   1585   this value as the salt. Similarly do not use the same cookie name to
   1586   store different values with distinct purposes.
   1587 
   1588   ## Options
   1589 
   1590     * `:domain` - the domain the cookie applies to
   1591     * `:max_age` - the cookie max-age, in seconds. Providing a value for this
   1592       option will set both the _max-age_ and _expires_ cookie attributes.
   1593     * `:path` - the path the cookie applies to
   1594     * `:http_only` - when `false`, the cookie is accessible beyond HTTP
   1595     * `:secure` - if the cookie must be sent only over https. Defaults
   1596       to true when the connection is HTTPS
   1597     * `:extra` - string to append to cookie. Use this to take advantage of
   1598       non-standard cookie attributes.
   1599     * `:sign` - when true, signs the cookie
   1600     * `:encrypt` - when true, encrypts the cookie
   1601     * `:same_site` - set the cookie SameSite attribute to a string value.
   1602       If no string value is set, the attribute is omitted.
   1603 
   1604   """
   1605   @spec put_resp_cookie(t, binary, any(), Keyword.t()) :: t
   1606   def put_resp_cookie(%Conn{} = conn, key, value, opts \\ [])
   1607       when is_binary(key) and is_list(opts) do
   1608     %{resp_cookies: resp_cookies, scheme: scheme} = conn
   1609     {to_send_value, opts} = maybe_sign_or_encrypt_cookie(conn, key, value, opts)
   1610     cookie = [{:value, to_send_value} | opts] |> Map.new() |> maybe_secure_cookie(scheme)
   1611     resp_cookies = Map.put(resp_cookies, key, cookie)
   1612     update_cookies(%{conn | resp_cookies: resp_cookies}, &Map.put(&1, key, value))
   1613   end
   1614 
   1615   defp maybe_sign_or_encrypt_cookie(conn, key, value, opts) do
   1616     {sign?, opts} = Keyword.pop(opts, :sign, false)
   1617     {encrypt?, opts} = Keyword.pop(opts, :encrypt, false)
   1618 
   1619     case {sign?, encrypt?} do
   1620       {true, true} ->
   1621         raise ArgumentError,
   1622               ":encrypt automatically implies :sign. Please pass only one or the other"
   1623 
   1624       {true, false} ->
   1625         {Plug.Crypto.sign(conn.secret_key_base, key <> "_cookie", value, max_age(opts)), opts}
   1626 
   1627       {false, true} ->
   1628         {Plug.Crypto.encrypt(conn.secret_key_base, key <> "_cookie", value, max_age(opts)), opts}
   1629 
   1630       {false, false} when is_binary(value) ->
   1631         {value, opts}
   1632 
   1633       {false, false} ->
   1634         raise ArgumentError, "cookie value must be a binary unless the cookie is signed/encrypted"
   1635     end
   1636   end
   1637 
   1638   defp max_age(opts) do
   1639     [keys: Plug.Keys, max_age: Keyword.get(opts, :max_age, 86400)]
   1640   end
   1641 
   1642   defp maybe_secure_cookie(cookie, :https), do: Map.put_new(cookie, :secure, true)
   1643   defp maybe_secure_cookie(cookie, _), do: cookie
   1644 
   1645   @doc """
   1646   Deletes a response cookie.
   1647 
   1648   Deleting a cookie requires the same options as to when the cookie was put.
   1649   Check `put_resp_cookie/4` for more information.
   1650   """
   1651   @spec delete_resp_cookie(t, binary, Keyword.t()) :: t
   1652   def delete_resp_cookie(%Conn{} = conn, key, opts \\ [])
   1653       when is_binary(key) and is_list(opts) do
   1654     %{resp_cookies: resp_cookies, scheme: scheme} = conn
   1655     opts = opts ++ [universal_time: @epoch, max_age: 0]
   1656     cookie = opts |> Map.new() |> maybe_secure_cookie(scheme)
   1657     resp_cookies = Map.put(resp_cookies, key, cookie)
   1658     update_cookies(%{conn | resp_cookies: resp_cookies}, &Map.delete(&1, key))
   1659   end
   1660 
   1661   @doc """
   1662   Fetches the session from the session store. Will also fetch cookies.
   1663   """
   1664   @spec fetch_session(t, Keyword.t()) :: t
   1665   def fetch_session(conn, opts \\ [])
   1666 
   1667   def fetch_session(%Conn{private: private} = conn, _opts) do
   1668     case Map.fetch(private, :plug_session_fetch) do
   1669       {:ok, :done} -> conn
   1670       {:ok, fun} -> conn |> fetch_cookies |> fun.()
   1671       :error -> raise ArgumentError, "cannot fetch session without a configured session plug"
   1672     end
   1673   end
   1674 
   1675   @doc """
   1676   Puts the specified `value` in the session for the given `key`.
   1677 
   1678   The key can be a string or an atom, where atoms are
   1679   automatically converted to strings. Can only be invoked
   1680   on unsent `conn`s. Will raise otherwise.
   1681   """
   1682   @spec put_session(t, String.t() | atom, any) :: t
   1683   def put_session(%Conn{state: state}, _key, _value) when state not in @unsent,
   1684     do: raise(AlreadySentError)
   1685 
   1686   def put_session(conn, key, value) when is_atom(key) or is_binary(key) do
   1687     put_session(conn, &Map.put(&1, session_key(key), value))
   1688   end
   1689 
   1690   @doc """
   1691   Returns session value for the given `key`. If `key`
   1692   is not set, `nil` is returned.
   1693 
   1694   The key can be a string or an atom, where atoms are
   1695   automatically converted to strings.
   1696   """
   1697   @spec get_session(t, String.t() | atom) :: any
   1698   def get_session(conn, key) when is_atom(key) or is_binary(key) do
   1699     conn |> get_session |> Map.get(session_key(key))
   1700   end
   1701 
   1702   @doc """
   1703   Returns the whole session.
   1704 
   1705   Although `get_session/2` and `put_session/3` allow atom keys,
   1706   they are always normalized to strings. So this function always
   1707   returns a map with string keys.
   1708 
   1709   Raises if the session was not yet fetched.
   1710   """
   1711   @spec get_session(t) :: %{optional(String.t()) => any}
   1712   def get_session(%Conn{private: private}) do
   1713     if session = Map.get(private, :plug_session) do
   1714       session
   1715     else
   1716       raise ArgumentError, "session not fetched, call fetch_session/2"
   1717     end
   1718   end
   1719 
   1720   @doc """
   1721   Deletes `key` from session.
   1722 
   1723   The key can be a string or an atom, where atoms are
   1724   automatically converted to strings.
   1725   """
   1726   @spec delete_session(t, String.t() | atom) :: t
   1727   def delete_session(%Conn{state: state}, _key) when state not in @unsent,
   1728     do: raise(AlreadySentError)
   1729 
   1730   def delete_session(conn, key) when is_atom(key) or is_binary(key) do
   1731     put_session(conn, &Map.delete(&1, session_key(key)))
   1732   end
   1733 
   1734   @doc """
   1735   Clears the entire session.
   1736 
   1737   This function removes every key from the session, clearing the session.
   1738 
   1739   Note that, even if `clear_session/1` is used, the session is still sent to the
   1740   client. If the session should be effectively *dropped*, `configure_session/2`
   1741   should be used with the `:drop` option set to `true`.
   1742   """
   1743   @spec clear_session(t) :: t
   1744   def clear_session(conn) do
   1745     put_session(conn, fn _existing -> Map.new() end)
   1746   end
   1747 
   1748   @doc """
   1749   Configures the session.
   1750 
   1751   ## Options
   1752 
   1753     * `:renew` - When `true`, generates a new session id for the cookie
   1754     * `:drop` - When `true`, drops the session, a session cookie will not be included in the
   1755       response
   1756     * `:ignore` - When `true`, ignores all changes made to the session in this request cycle
   1757 
   1758   ## Examples
   1759 
   1760       configure_session(conn, renew: true)
   1761 
   1762   """
   1763   @spec configure_session(t, Keyword.t()) :: t
   1764   def configure_session(conn, opts)
   1765 
   1766   def configure_session(%Conn{state: state}, _opts) when state not in @unsent,
   1767     do: raise(AlreadySentError)
   1768 
   1769   def configure_session(conn, opts) do
   1770     # Ensure the session is available.
   1771     _ = get_session(conn)
   1772 
   1773     cond do
   1774       opts[:renew] -> put_private(conn, :plug_session_info, :renew)
   1775       opts[:drop] -> put_private(conn, :plug_session_info, :drop)
   1776       opts[:ignore] -> put_private(conn, :plug_session_info, :ignore)
   1777       true -> conn
   1778     end
   1779   end
   1780 
   1781   @doc ~S"""
   1782   Registers a callback to be invoked before the response is sent.
   1783 
   1784   Callbacks are invoked in the reverse order they are defined (callbacks
   1785   defined first are invoked last).
   1786 
   1787   ## Examples
   1788 
   1789   To log the status of response being sent:
   1790 
   1791       require Logger
   1792 
   1793       Plug.Conn.register_before_send(conn, fn conn ->
   1794         Logger.info("Sent a #{conn.status} response")
   1795         conn
   1796       end)
   1797 
   1798   """
   1799   @spec register_before_send(t, (t -> t)) :: t
   1800   def register_before_send(conn, callback)
   1801 
   1802   def register_before_send(%Conn{state: state}, _callback)
   1803       when state not in @unsent do
   1804     raise AlreadySentError
   1805   end
   1806 
   1807   def register_before_send(%Conn{} = conn, callback)
   1808       when is_function(callback, 1) do
   1809     update_in(conn.private[:before_send], &[callback | &1 || []])
   1810   end
   1811 
   1812   @doc """
   1813   Halts the Plug pipeline by preventing further plugs downstream from being
   1814   invoked. See the docs for `Plug.Builder` for more information on halting a
   1815   Plug pipeline.
   1816   """
   1817   @spec halt(t) :: t
   1818   def halt(%Conn{} = conn) do
   1819     %{conn | halted: true}
   1820   end
   1821 
   1822   @doc """
   1823   Returns the full request URL.
   1824   """
   1825   def request_url(%Conn{} = conn) do
   1826     IO.iodata_to_binary([
   1827       to_string(conn.scheme),
   1828       "://",
   1829       conn.host,
   1830       request_url_port(conn.scheme, conn.port),
   1831       conn.request_path,
   1832       request_url_qs(conn.query_string)
   1833     ])
   1834   end
   1835 
   1836   ## Helpers
   1837 
   1838   defp run_before_send(%Conn{private: private} = conn, new) do
   1839     conn = Enum.reduce(private[:before_send] || [], %{conn | state: new}, & &1.(&2))
   1840 
   1841     if conn.state != new do
   1842       raise ArgumentError, "cannot send/change response from run_before_send callback"
   1843     end
   1844 
   1845     %{conn | resp_headers: merge_headers(conn.resp_headers, conn.resp_cookies)}
   1846   end
   1847 
   1848   defp merge_headers(headers, cookies) do
   1849     Enum.reduce(cookies, headers, fn {key, opts}, acc ->
   1850       value =
   1851         key
   1852         |> Plug.Conn.Cookies.encode(opts)
   1853         |> verify_cookie!(key)
   1854 
   1855       [{"set-cookie", value} | acc]
   1856     end)
   1857   end
   1858 
   1859   defp verify_cookie!(cookie, key) when byte_size(cookie) > 4096 do
   1860     raise Plug.Conn.CookieOverflowError,
   1861           "cookie named #{inspect(key)} exceeds maximum size of 4096 bytes"
   1862   end
   1863 
   1864   defp verify_cookie!(cookie, _key) do
   1865     validate_header_value!("set-cookie", cookie)
   1866   end
   1867 
   1868   defp update_cookies(%Conn{state: state}, _fun) when state not in @unsent do
   1869     raise AlreadySentError
   1870   end
   1871 
   1872   defp update_cookies(%Conn{cookies: %Unfetched{}} = conn, _fun), do: conn
   1873   defp update_cookies(%Conn{cookies: cookies} = conn, fun), do: %{conn | cookies: fun.(cookies)}
   1874 
   1875   defp session_key(binary) when is_binary(binary), do: binary
   1876   defp session_key(atom) when is_atom(atom), do: Atom.to_string(atom)
   1877 
   1878   defp put_session(conn, fun) do
   1879     private =
   1880       conn.private
   1881       |> Map.put(:plug_session, fun.(get_session(conn)))
   1882       |> Map.put_new(:plug_session_info, :write)
   1883 
   1884     %{conn | private: private}
   1885   end
   1886 
   1887   # host is an HTTP header, but if you store it in the main list it will be
   1888   # overridden by conn.host.
   1889   defp validate_req_header!(_adapter, "host") do
   1890     raise InvalidHeaderError,
   1891           "set the host header with %Plug.Conn{conn | host: \"example.com\"}"
   1892   end
   1893 
   1894   defp validate_req_header!(adapter, key), do: validate_header_key_if_test!(adapter, key)
   1895 
   1896   defp validate_header_key_if_test!({Plug.Adapters.Test.Conn, _}, key) do
   1897     if Application.fetch_env!(:plug, :validate_header_keys_during_test) and
   1898          not valid_header_key?(key) do
   1899       raise InvalidHeaderError, "header key is not lowercase: " <> inspect(key)
   1900     end
   1901   end
   1902 
   1903   defp validate_header_key_if_test!(_adapter, _key) do
   1904     :ok
   1905   end
   1906 
   1907   # Any string containing an UPPERCASE char is not valid.
   1908   defp valid_header_key?(<<h, _::binary>>) when h in ?A..?Z, do: false
   1909   defp valid_header_key?(<<_, t::binary>>), do: valid_header_key?(t)
   1910   defp valid_header_key?(<<>>), do: true
   1911   defp valid_header_key?(_), do: false
   1912 
   1913   defp validate_header_value!(key, value) do
   1914     case :binary.match(value, ["\n", "\r"]) do
   1915       {_, _} ->
   1916         raise InvalidHeaderError,
   1917               "value for header #{inspect(key)} contains control feed (\\r) or newline " <>
   1918                 "(\\n): #{inspect(value)}"
   1919 
   1920       :nomatch ->
   1921         value
   1922     end
   1923   end
   1924 
   1925   defp request_url_port(:http, 80), do: ""
   1926   defp request_url_port(:https, 443), do: ""
   1927   defp request_url_port(_, port), do: [?:, Integer.to_string(port)]
   1928 
   1929   defp request_url_qs(""), do: ""
   1930   defp request_url_qs(qs), do: [??, qs]
   1931 end
   1932 
   1933 defimpl Inspect, for: Plug.Conn do
   1934   def inspect(conn, opts) do
   1935     conn
   1936     |> no_secret_key_base()
   1937     |> no_adapter_data(opts)
   1938     |> Inspect.Any.inspect(opts)
   1939   end
   1940 
   1941   defp no_secret_key_base(%{secret_key_base: nil} = conn), do: conn
   1942   defp no_secret_key_base(conn), do: %{conn | secret_key_base: :...}
   1943 
   1944   defp no_adapter_data(conn, %{limit: :infinity}), do: conn
   1945   defp no_adapter_data(%{adapter: {adapter, _}} = conn, _), do: %{conn | adapter: {adapter, :...}}
   1946 end
   1947 
   1948 defimpl Collectable, for: Plug.Conn do
   1949   def into(conn) do
   1950     IO.warn(
   1951       "using Enum.into/2 for conn is deprecated, use Plug.Conn.chunk/2 " <>
   1952         "and Enum.reduce_while/3 instead (see the Plug.Conn.chunk/2 docs for an example)"
   1953     )
   1954 
   1955     fun = fn
   1956       conn, {:cont, x} ->
   1957         {:ok, conn} = Plug.Conn.chunk(conn, x)
   1958         conn
   1959 
   1960       conn, _ ->
   1961         conn
   1962     end
   1963 
   1964     {conn, fun}
   1965   end
   1966 end