0
Fork 0
mirror of https://github.com/ninenines/cowboy.git synced 2025-07-14 12:20:24 +00:00

Greatly expand on the Req object

Cut in four different chapters: request, request body,
response and cookies.
This commit is contained in:
Loïc Hoguin 2013-09-23 15:44:10 +02:00
parent eb4843a46b
commit 2b2829f585
5 changed files with 778 additions and 169 deletions

140
guide/cookies.md Normal file
View file

@ -0,0 +1,140 @@
Using cookies
=============
Cookies are a mechanism allowing applications to maintain
state on top of the stateless HTTP protocol.
Cowboy provides facilities for handling cookies. It is highly
recommended to use them instead of writing your own, as the
implementation of cookies can vary greatly between clients.
Cookies are stored client-side and sent with every subsequent
request that matches the domain and path for which they were
stored, including requests for static files. For this reason
they can incur a cost which must be taken in consideration.
Also consider that, regardless of the options used, cookies
are not to be trusted. They may be read and modified by any
program on the user's computer, but also by proxies. You
should always validate cookie values before using them. Do
not store any sensitive information in cookies either.
When explicitly setting the domain, the cookie will be sent
for the domain and all subdomains from that domain. Otherwise
the current domain will be used. The same is true for the
path.
When the server sets cookies, they will only be available
for requests that are sent after the client receives the
response.
Cookies are sent in HTTP headers, therefore they must have
text values. It is your responsibility to encode any other
data type. Also note that cookie names are de facto case
sensitive.
Cookies can be set for the client session (which generally
means until the browser is closed), or it can be set for
a number of seconds. Once it expires, or when the server
says the cookie must exist for up to 0 seconds, the cookie
is deleted by the client. To avoid this while the user
is browsing your site, you should set the cookie for
every request, essentially resetting the expiration time.
Cookies can be restricted to secure channels. This typically
means that such a cookie will only be sent over HTTPS,
and that it will only be available by client-side scripts
that run from HTTPS webpages.
Finally, cookies can be restricted to HTTP and HTTPS requests,
essentially disabling their access from client-side scripts.
Setting cookies
---------------
By default, cookies you set are defined for the session.
``` erlang
SessionID = generate_session_id(),
Req2 = cowboy_req:set_resp_cookie(<<"sessionid">>, SessionID, [], Req).
```
You can also make them expire at a specific point in the
future.
``` erlang
SessionID = generate_session_id(),
Req2 = cowboy_req:set_resp_cookie(<<"sessionid">>, SessionID, [
{max_age, 3600}
], Req).
```
You can delete cookies that have already been set. The value
is ignored.
``` erlang
Req2 = cowboy_req:set_resp_cookie(<<"sessionid">>, <<>>, [
{max_age, 0}
], Req).
```
You can restrict them to a specific domain and path.
For example, the following cookie will be set for the domain
`my.example.org` and all its subdomains, but only on the path
`/account` and all its subdirectories.
``` erlang
Req2 = cowboy_req:set_resp_cookie(<<"inaccount">>, <<"1">>, [
{domain, "my.example.org"},
{path, "/account"}
], Req).
```
You can restrict the cookie to secure channels, typically HTTPS.
``` erlang
SessionID = generate_session_id(),
Req2 = cowboy_req:set_resp_cookie(<<"sessionid">>, SessionID, [
{secure, true}
], Req).
```
You can restrict the cookie to client-server communication
only. Such a cookie will not be available to client-side scripts.
``` erlang
SessionID = generate_session_id(),
Req2 = cowboy_req:set_resp_cookie(<<"sessionid">>, SessionID, [
{http_only, true}
], Req).
```
Cookies may also be set client-side, for example using
Javascript.
Reading cookies
---------------
As we said, the client sends cookies with every request.
But unlike the server, the client only sends the cookie
name and value.
You can read the value of a cookie.
``` erlang
{CookieVal, Req2} = cowboy_req:cookie(<<"lang">>, Req).
```
You can also get a default value returned when the cookie
isn't set.
``` erlang
{CookieVal, Req2} = cowboy_req:cookie(<<"lang">>, Req, <<"fr">>).
```
And you can obtain all cookies at once as a list of
key/value tuples.
``` erlang
{AllCookies, Req2} = cowboy_req:cookies(Req).
```

View file

@ -1,204 +1,298 @@
Request object The Req object
============== ==============
Purpose The Req object is this variable that you will use to obtain
------- information about a request, read the body of the request
and send a response.
The request object is a special variable that can be used A special variable
to interact with a request, extracting information from it ------------------
or modifying it, and sending a response.
It's a special variable because it contains both immutable While we call it an "object", it is not an object in the
and mutable state. This means that some operations performed OOP sense of the term. In fact it is completely opaque
on the request object will always return the same result, to you and the only way you can perform operations using
while others will not. For example, obtaining request headers it is by calling the functions from the `cowboy_req`
can be repeated safely. Obtaining the request body can only module.
be done once, as it is read directly from the socket.
With few exceptions, all calls to the `cowboy_req` module Almost all the calls to the `cowboy_req` module will
will return an updated request object. You MUST use the new return an updated request object. Just like you would
request object instead of the old one for all subsequent keep the updated `State` variable in a gen_server,
operations. you MUST keep the updated `Req` variable in a Cowboy
handler. Cowboy will use this object to know whether
a response has been sent when the handler has finished
executing.
The Req object allows accessing both immutable and
mutable state. This means that calling some of the
functions twice will not produce the same result.
For example, when streaming the request body, the
function will return the body by chunks, one at a
time, until there is none left.
It also caches the result of operations performed
on the immutable state. That means that some calls
will give a result much faster when called many times.
Overview of the cowboy_req interface
------------------------------------
The `cowboy_req` interface is divided in four groups
of functions, each having a well defined return type
signature common to the entire group.
The first group, access functions, will always return
`{Value, Req}`. The group includes all the following
functions: `binding/{2,3}`, `bindings/1`, `body_length/1`,
`cookie/{2,3}`, `cookies/1`, `header/{2,3}`, `headers/1`,
`host/1`, `host_info/1`, `host_url/1`, `meta/{2,3}`,
`method/1`, `path/1`, `path_info/1`, `peer/1`, `port/1`,
`qs/1`, `qs_val/{2,3}`, `qs_vals/1`, `url/1`, `version/1`.
The second group, question functions, will always return
a `boolean()`. The group includes the following three
functions: `has_body/1`, `has_resp_body/1`, `has_resp_header/2`.
The third group contains the functions that manipulate
the socket or perform operations that may legitimately fail.
They may return `{Result, Req}`, `{Result, Value, Req}`
or `{error, atom()}`. This includes the following functions:
`body/{1,2}`, `body_qs/{1,2}`, `chunked_reply/{2,3}`,
`init_stream/4`, `parse_header/{2,3}`, `reply/{2,3,4}`,
`skip_body/1`, `stream_body/{1,2}`. Finally, the group
also includes the `chunk/2` function which always returns
`ok`.
The final group modifies the Req object, so it always return
a new `Req`. It includes the following functions: `compact/1`,
`delete_resp_header/2`, `set_meta/3`, `set_resp_body/2`,
`set_resp_body_fun/{2,3}`, `set_resp_cookie/4`, `set_resp_header/3`.
This chapter covers most of the first group, plus a few other
functions. The next few chapters cover cookies handling, reading
the request body and sending a response.
Request Request
------- -------
Cowboy allows you to retrieve a lot of information about When a client performs a request, it first sends a few required
the request. All these calls return a `{Value, Req}` tuple, values. They are sent differently depending on the protocol
with `Value` the requested value and `Req` the updated being used, but the intent is the same. They indicate to the
request object. server the type of action it wants to do and how to locate
the resource to perform it on.
The following access functions are defined in `cowboy_req`: The method identifies the action. Standard methods include
GET, HEAD, OPTIONS, PATCH, POST, PUT, DELETE. Method names
are case sensitive.
* `method/1`: the request method (`<<"GET">>`, `<<"POST">>`...) ``` erlang
* `version/1`: the HTTP version (`'HTTP/1.0'` or `'HTTP/1.1'`) {Method, Req2} = cowboy_req:method(Req).
* `peer/1`: the peer address and port number ```
* `host/1`: the hostname requested
* `host_info/1`: the result of the `[...]` match on the host
* `port/1`: the port number used for the connection
* `path/1`: the path requested
* `path_info/1`: the result of the `[...]` match on the path
* `qs/1`: the entire query string unmodified
* `qs_val/{2,3}`: the value for the requested query string key
* `qs_vals/1`: all key/values found in the query string
* `host_url/1`: the requested URL without the path and query string
* `url/1`: the requested URL
* `binding/{2,3}`: the value for the requested binding found during routing
* `bindings/1`: all key/values found during routing
* `header/{2,3}`: the value for the requested header name
* `headers/1`: all headers name/value
* `cookie/{2,3}`: the value for the requested cookie name
* `cookies/1`: all cookies name/value
* `meta/{2,3}`: the meta information for the requested key
All the functions above that can take two or three arguments The host, port and path parts of the URL identify the resource
take an optional third argument for the default value if being accessed. The host and port information may not be
none is found. Otherwise it will return `undefined`. available if the client uses HTTP/1.0.
In addition, Cowboy allows you to parse headers using the ``` erlang
`parse_header/{2,3}` function, which takes a header name {Host, Req2} = cowboy_req:host(Req),
as lowercase binary, the request object, and an optional {Port, Req3} = cowboy_req:port(Req2),
default value. It returns `{ok, ParsedValue, Req}` if it {Path, Req4} = cowboy_req:path(Req3).
could be parsed, `{undefined, RawValue, Req}` if Cowboy ```
doesn't know this header, and `{error, badarg}` if Cowboy
encountered an error while trying to parse it.
Finally, Cowboy allows you to set request meta information The version used by the client can of course also be obtained.
using the `set_meta/3` function, which takes a name, a value
and the request object and returns the latter modified.
Request body ``` erlang
------------ {Version, Req2} = cowboy_req:version(Req).
```
Cowboy will not read the request body until you ask it to. Do note however that clients claiming to implement one version
If you don't, then Cowboy will simply discard it. It will of the protocol does not mean they implement it fully, or even
not take extra memory space until you start reading it. properly.
Cowboy has a few utility functions for dealing with the Bindings
request body.
The function `has_body/1` will return whether the request
contains a body. Note that some clients may not send the
right headers while still sending a body, but as Cowboy has
no way of detecting it this function will return `false`.
The function `body_length/1` retrieves the size of the
request body. If the body is compressed, the value returned
here is the compressed size. If a `Transfer-Encoding` header
was passed in the request, then Cowboy will return a size
of `undefined`, as it has no way of knowing it.
If you know the request contains a body, and that it is
within 8MB (for `body/1`) or 16KB (for `body_qs/1`) bytes,
then you can read it directly with either `body/1` or `body_qs/1`.
If you want to override the default size limits of `body/1`
or `body_qs/1`, you can pass the maximum body length byte
size as first parameter to `body/2` and `body_qs/2` or pass
atom `infinity` to ignore size limits.
If the request contains bigger body than allowed default sizes
or supplied maximum body length, `body/1`, `body/2`, `body_qs/1`
and `body_qs/2` will return `{error, badlength}`. If the request
contains chunked body, `body/1`, `body/2`, `body_qs/1`
and `body_qs/2` will return `{error, chunked}`.
If you get either of the above two errors, you will want to
handle the body of the request using `stream_body/1`,
`stream_body/2` and `skip_body/1`, with the streaming process
optionally initialized using `init_stream/4`.
Multipart request body
----------------------
Cowboy provides facilities for dealing with multipart bodies.
They are typically used for uploading files. You can use two
functions to process these bodies, `multipart_data/1` and
`multipart_skip/1`.
Response
-------- --------
You can send a response by calling the `reply/{2,3,4}` function. After routing the request, bindings are available. Bindings
It takes the status code for the response (usually `200`), are these parts of the host or path that you chose to extract
an optional list of headers, an optional body and the request when defining the routes of your application.
object.
The following snippet sends a simple response with no headers You can fetch a single binding. The value will be `undefined`
specified but with a body. if the binding doesn't exist.
``` erlang ``` erlang
{ok, Req2} = cowboy_req:reply(200, [], "Hello world!", Req). {Binding, Req2} = cowboy_req:binding(my_binding, Req).
``` ```
If this is the only line in your handler then make sure to return If you need a different value when the binding doesn't exist,
the `Req2` variable to Cowboy so it can know you replied. you can change the default.
If you want to send HTML you'll need to specify the `Content-Type`
header so the client can properly interpret it.
``` erlang ``` erlang
{ok, Req2} = cowboy_req:reply(200, {Binding, Req2} = cowboy_req:binding(my_binding, Req, 42).
[{<<"content-type">>, <<"text/html">>}],
"<html><head>Hello world!</head><body><p>Hats off!</p></body></html>",
Req).
``` ```
You only need to make sure to follow conventions and to use a You can also obtain all bindings in one call. They will be
lowercase header name. returned as a list of key/value tuples.
Chunked response
----------------
You can also send chunked responses using `chunked_reply/{2,3}`.
Chunked responses allow you to send the body in chunks of various
sizes. It is the recommended way of performing streaming if the
client supports it.
You must first initiate the response by calling the aforementioned
function, then you can call `chunk/2` as many times as needed.
The following snippet sends a body in three chunks.
``` erlang ``` erlang
{ok, Req2} = cowboy_req:chunked_reply(200, Req), {AllBindings, Req2} = cowboy_req:bindings(Req).
ok = cowboy_req:chunk("Hello...", Req2),
ok = cowboy_req:chunk("chunked...", Req2),
ok = cowboy_req:chunk("world!!", Req2).
``` ```
As you can see the call to `chunk/2` does not return a modified If you used `...` at the beginning of the route's pattern
request object. It may return an error, however, so you should for the host, you can retrieve the matched part of the host.
make sure that you match the return value on `ok`. The value will be `undefined` otherwise.
Response preconfiguration
-------------------------
Cowboy allows you to set response cookies, headers or body
in advance without having to send the response at the same time.
Then, when you decide to send it, all these informations will be
built into the resulting response.
Some of the functions available for this purpose also give you
additional functionality, like `set_resp_cookie/4` which will build
the appropriate `Set-Cookie` header, or `set_resp_body_fun/{2,3}`
which allows you to stream the response body.
Note that any value given directly to `reply/{2,3,4}` will
override all preset values. This means for example that you
can set a default body and then override it when you decide
to send a reply.
Closing the connection
----------------------
HTTP/1.1 keep-alive allows clients to send more than one request
on the same connection. This can be useful for speeding up the
loading of webpages, but is not required. You can tell Cowboy
explicitly that you want to close the connection by setting the
`Connection` header to `close`.
``` erlang ``` erlang
{ok, Req2} = cowboy_req:reply(200, {HostInfo, Req2} = cowboy_req:host_info(Req).
[{<<"connection">>, <<"close">>}], ```
Req).
Similarly, if you used `...` at the end of the route's
pattern for the path, you can retrieve the matched part,
or get `undefined` otherwise.
``` erlang
{PathInfo, Req2} = cowboy_req:path_info(Req).
```
Query string
------------
The query string can be obtained directly.
``` erlang
{Qs, Req2} = cowboy_req:qs(Req).
```
You can also requests only one value.
``` erlang
{QsVal, Req2} = cowboy_req:qs_val(<<"lang">>, Req).
```
If that value is optional, you can define a default to simplify
your task.
``` erlang
{QsVal, Req2} = cowboy_req:qs_val(<<"lang">>, Req, <<"en">>).
```
Finally, you can obtain all query string values.
``` erlang
{AllValues, Req2} = cowboy_req:qs_vals(Req).
```
Request URL
-----------
You can reconstruct the full URL of the resource.
``` erlang
{URL, Req2} = cowboy_req:url(Req).
```
You can also obtain only the base of the URL, excluding the
path and query string.
``` erlang
{BaseURL, Req2} = cowboy_req:host_url(Req).
```
Headers
-------
Cowboy allows you to obtain the header values as string,
or parsed into a more meaningful representation.
This will get the string value of a header.
``` erlang
{HeaderVal, Req2} = cowboy_req:header(<<"content-type">>, Req).
```
You can of course set a default in case the header is missing.
``` erlang
{HeaderVal, Req2}
= cowboy_req:header(<<"content-type">>, Req, <<"text/plain">>).
```
And also obtain all headers.
``` erlang
{AllHeaders, Req2} = cowboy_req:headers(Req).
```
To parse the previous header, simply call `parse_header/{2,3}`
where you would call `header/{2,3}` otherwise. Note that the
return value changes and includes the result of the operation
as the first element of the returned tuple. A successful parse
returns `ok`.
``` erlang
{ok, ParsedVal, Req2} = cowboy_req:parse_header(<<"content-type">>, Req).
```
When Cowboy doesn't know how to parse the given header, the
result of the operation will be `undefined` and the string value
will be returned instead.
``` erlang
{undefined, HeaderVal, Req2}
= cowboy_req:parse_header(<<"unicorn-header">>, Req).
```
When parsing fails, `{error, Reason}` is returned instead.
You can of course define a default value. Note that the default
value you specify here is the parsed value you'd like to get
by default.
``` erlang
{ok, ParsedVal, Req2}
= cowboy_req:parse_header(<<"content-type">>, Req,
{<<"text">>, <<"plain">>, []}).
```
The list of known headers and default values is defined in the
manual. Also note that the result of parsing is cached, so
calling this function multiple times for the same values will
not have a significant performance impact.
Meta
----
Cowboy will sometimes associate some meta information with
the request. Built-in meta values are listed in the manual
for their respective modules.
This will get a meta value. The returned value will be `undefined`
if it isn't defined.
``` erlang
{MetaVal, Req2} = cowboy_req:meta(websocket_version, Req).
```
You can change the default value if needed.
``` erlang
{MetaVal, Req2} = cowboy_req:meta(websocket_version, Req, 13).
```
You can also define your own meta values. The name must be
an `atom()`.
``` erlang
Req2 = cowboy_req:set_meta(the_answer, 42, Req).
```
Peer
----
You can obtain the peer address and port number. This is
not necessarily the actual IP and port of the client, but
rather the one of the machine that connected to the server.
``` erlang
{{IP, Port}, Req2} = cowboy_req:peer(Req).
``` ```
Reducing the memory footprint Reducing the memory footprint
@ -213,3 +307,5 @@ free memory.
``` erlang ``` erlang
Req2 = cowboy_req:compact(Req). Req2 = cowboy_req:compact(Req).
``` ```
You will still be able to send a reply if needed.

169
guide/req_body.md Normal file
View file

@ -0,0 +1,169 @@
Reading the request body
========================
The Req object also allows you to read the request body.
Because the request body can be of any size, all body
reading operations will only work once, as Cowboy will
not cache the result of these operations.
Cowboy will not attempt to read the body until you do.
If handler execution ends without reading it, Cowboy
will simply skip it.
Check for request body
----------------------
You can check whether a body was sent with the request.
``` erlang
cowboy_req:has_body(Req).
```
It will return `true` if there is a request body, and
`false` otherwise.
Note that it is generally safe to assume that a body is
sent for `POST`, `PUT` and `PATCH` requests, without
having to explicitly check for it.
Request body length
-------------------
You can obtain the body length if it was sent with the
request.
``` erlang
{Length, Req2} = cowboy_req:body_length(Req).
```
The value returned will be `undefined` if the length
couldn't be figured out from the request headers. If
there's a body but no length is given, this means that
the chunked transfer-encoding was used. You can read
chunked bodies by using the stream functions.
Reading the body
----------------
If a content-length header was sent with the request,
you can read the whole body directly.
``` erlang
{ok, Body, Req2} = cowboy_req:body(Req).
```
If no content-length header is available, Cowboy will
return the `{error, chunked}` tuple. You will need to
stream the request body instead.
By default, Cowboy will reject all body sizes above 8MB,
to prevent an attacker from needlessly filling up memory.
You can override this limit however.
``` erlang
{ok, Body, Req2} = cowboy_req:body(100000000, Req).
```
You can also disable it.
``` erlang
{ok, Body, Req2} = cowboy_req:body(infinity, Req).
```
It is recommended that you do not disable it for public
facing websites.
Reading a body sent from an HTML form
-------------------------------------
You can directly obtain a list of key/value pairs if the
body was sent using the application/x-www-form-urlencoded
content-type.
``` erlang
{ok, KeyValues, Req2} = cowboy_req:body_qs(Req).
```
You can then retrieve an individual value from that list.
``` erlang
{_, Lang} = lists:keyfind(lang, 1, KeyValues).
```
You should not attempt to match on the list as the order
of the values is undefined.
By default Cowboy will reject bodies with a size above
16KB when using this function. You can override this limit.
``` erlang
{ok, KeyValues, Req2} = cowboy_req:body_qs(500000, Req).
```
You can also disable it by passing the atom `infinity`,
although it is not recommended.
Streaming the body
------------------
You can stream the request body by chunks.
``` erlang
{ok, Chunk, Req2} = cowboy_req:stream_body(Req).
```
By default, Cowboy will attempt to read chunks of up to
1MB in size. The chunks returned by this function will
often be smaller, however. You can also change this limit.
``` erlang
{ok, Chunk, Req2} = cowboy_req:stream_body(500000, Req).
```
When Cowboy finishes reading the body, any subsequent call
will return `{done, Req2}`. You can thus write a recursive
function to read the whole body and perform an action on
all chunks, for example printing them to the console.
``` erlang
body_to_console(Req) ->
case cowboy_req:stream_body(Req) of
{ok, Chunk, Req2} ->
io:format("~s", [Chunk]),
body_to_console(Req2);
{done, Req2} ->
Req2
end.
```
Advanced streaming
------------------
Cowboy will by default decode the chunked transfer-encoding
if any. It will not decode any content-encoding by default.
Before starting to stream, you can configure the functions
that will be used for decoding both transfer-encoding and
content-encoding.
``` erlang
{ok, Req2} = cowboy_req:init_stream(fun transfer_decode/2,
TransferStartState, fun content_decode/1, Req).
```
Note that you do not need to call this function generally,
as Cowboy will happily initialize the stream on its own.
Skipping the body
-----------------
If you do not need the body, or if you started streaming
the body but do not need the rest of it, you can skip it.
``` erlang
{ok, Req2} = cowboy_req:skip_body(Req).
```
You do not have to call this function though, as Cowboy will
do it automatically when handler execution ends.

203
guide/resp.md Normal file
View file

@ -0,0 +1,203 @@
Sending a response
==================
The Req object also allows you to send a response.
You can only send one response. Any other attempt will
trigger a crash. The response may be sent in one go or
with its body streamed by chunks of arbitrary size.
You can also set headers or the response body in advance
and Cowboy will use them when you finally do reply.
Reply
-----
You can send a reply with no particular headers or body.
Cowboy will make sure to send the mandatory headers with
the response.
``` erlang
{ok, Req2} = cowboy_req:reply(200, Req).
```
You can define headers to be sent with the response. Note
that header names must be lowercase. Again, Cowboy will
make sure to send the mandatory headers with the response.
``` erlang
{ok, Req2} = cowboy_req:reply(303, [
{<<"location">>, <<"http://ninenines.eu">>}
], Req).
```
You can override headers that Cowboy would send otherwise.
Any header set by the user will be used over the ones set
by Cowboy. For example, you can advertise yourself as a
different server.
``` erlang
{ok, Req2} = cowboy_req:reply(200, [
{<<"server">>, <<"yaws">>}
], Req).
```
We also saw earlier how to force close the connection by
overriding the connection header.
Finally, you can also send a body with the response. Cowboy
will automatically set the content-length header if you do.
We recommend that you set the content-type header so the
client may know how to read the body.
``` erlang
{ok, Req2} = cowboy_req:reply(200, [
{<<"content-type">>, <<"text/plain">>
], "Hello world!", Req).
```
Here is the same example but sending HTML this time.
``` erlang
{ok, Req2} = cowboy_req:reply(200, [
{<<"content-type">>, <<"text/html">>}
], "<html><head>Hello world!</head><body><p>Hats off!</p></body></html>", Req).
```
Note that the reply is sent immediately.
Chunked reply
-------------
You can also stream the response body. First, you need to
initiate the reply by sending the response status code.
Then you can send the body in chunks of arbitrary size.
``` erlang
{ok, Req2} = cowboy_req:chunked_reply(200, Req),
ok = cowboy_req:chunk("Hello...", Req2),
ok = cowboy_req:chunk("chunked...", Req2),
ok = cowboy_req:chunk("world!!", Req2).
```
You should make sure to match on `ok` as an error may be
returned.
While it is possible to send a chunked response without
a content-type header, it is still recommended. You can
set this header or any other just like for normal replies.
``` erlang
{ok, Req2} = cowboy_req:chunked_reply(200, [
{<<"content-type">>, <<"text/html">>}
], Req),
ok = cowboy_req:chunk("<html><head>Hello world!</head>", Req2),
ok = cowboy_req:chunk("<body><p>Hats off!</p></body></html>", Req2).
```
Note that the reply and each chunk following it are sent
immediately.
Preset response headers
-----------------------
You can define response headers in advance. They will be
merged into the headers given in the reply call. Headers
in the reply call override preset response headers which
override the default Cowboy headers.
``` erlang
Req2 = cowboy_req:set_resp_header(<<"allow">>, "GET", Req).
```
You can check if a response header has already been set.
This will only check the response headers that you set,
and not the ones Cowboy will add when actually sending
the reply.
``` erlang
cowboy_req:has_resp_header(<<"allow">>, Req).
```
It will return `true` if the header is defined, and `false`
otherwise.
Finally, you can also delete a preset response header if
needed. If you do, it will not be sent.
``` erlang
Req2 = cowboy_req:delete_resp_header(<<"allow">>, Req).
```
Preset response body
--------------------
You can set the response body in advance. Note that this
body will be ignored if you then choose to send a chunked
reply, or if you send a reply with an explicit body.
``` erlang
Req2 = cowboy_req:set_resp_body("Hello world!", Req).
```
You can also set a fun that will be called when it is time
to send the body. There are three different ways of doing
that.
If you know the length of the body that needs to be sent,
you should specify it, as it will help clients determine
the remaining download time and allow them to inform the
user.
``` erlang
F = fun (Socket, Transport) ->
Transport:send(Socket, "Hello world!")
end,
Req2 = cowboy_req:set_resp_body_fun(12, F, Req).
```
If you do not know the length of the body, you should use
a chunked response body fun instead.
``` erlang
F = fun (SendChunk) ->
Body = lists:duplicate(random:uniform(1024, $a)),
SendChunk(Body)
end,
Req2 = cowboy_req:set_resp_body_fun(chunked, F, Req).
```
Finally, you can also send data on the socket directly,
without knowing the length in advance. Cowboy may be
forced to close the connection at the end of the response
though depending on the protocol capabilities.
``` erlang
F = fun (Socket, Transport) ->
Body = lists:duplicate(random:uniform(1024, $a)),
Transport:send(Socket, Body)
end,
Req2 = cowboy_req:set_resp_body_fun(F, Req).
```
Sending files
-------------
You can send files directly from disk without having to
read them. Cowboy will use the `sendfile` syscall when
possible, which means that the file is sent to the socket
directly from the kernel, which is a lot more performant
than doing it from userland.
Again, it is recommended to set the size of the file if it
can be known in advance.
``` erlang
F = fun (Socket, Transport) ->
Transport:sendfile(Socket, "priv/styles.css")
end,
Req2 = cowboy_req:set_resp_body_fun(FileSize, F, Req).
```
Please see the Ranch guide for more information about
sending files.

View file

@ -20,8 +20,9 @@ HTTP
* [Routing](routing.md) * [Routing](routing.md)
* [Handling plain HTTP requests](http_handlers.md) * [Handling plain HTTP requests](http_handlers.md)
* [The Req object](req.md) * [The Req object](req.md)
* Reading the request body * [Reading the request body](req_body.md)
* Sending a response * [Sending a response](resp.md)
* [Using cookies](cookies.md)
Static files Static files
------------ ------------