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