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

Breaking update of the cowboy_req interface

Simplify the interface for most cowboy_req functions. They all return
a single value except the four body reading functions. The reply functions
now only return a Req value.

Access functions do not return a Req anymore.

Functions that used to cache results do not have a cache anymore.

The interface for accessing query string and cookies has therefore
been changed.

There are now three query string functions: qs/1 provides access
to the raw query string value; parse_qs/1 returns the query string
as a list of key/values; match_qs/2 returns a map containing the
values requested in the second argument, after applying constraints
and default value.

Similarly, there are two cookie functions: parse_cookies/1 and
match_cookies/2. More match functions will be added in future commits.

None of the functions return an error tuple anymore. It either works
or crashes. Cowboy will attempt to provide an appropriate status code
in the response of crashed handlers.

As a result, the content decode function has its return value changed
to a simple binary, and the body reading functions only return on success.
This commit is contained in:
Loïc Hoguin 2014-09-23 16:43:29 +03:00
parent b57f94661f
commit f1c3b6d76f
61 changed files with 814 additions and 767 deletions

View file

@ -28,8 +28,7 @@ that will format the header names with the expected case.
capitalize_hook(Status, Headers, Body, Req) ->
Headers2 = [{cowboy_bstr:capitalize_token(N), V}
|| {N, V} <- Headers],
{ok, Req2} = cowboy_req:reply(Status, Headers2, Body, Req),
Req2.
cowboy_req:reply(Status, Headers2, Body, Req).
```
Note that SPDY clients do not have that particular issue

View file

@ -0,0 +1,51 @@
::: Constraints
Cowboy provides an optional constraints based validation feature
when interacting with user input.
Constraints are first used during routing. The router uses
constraints to more accurately match bound values, allowing
to create routes where a segment is an integer for example,
and rejecting the others.
Constraints are also used when performing a match operation
on input data, like the query string or cookies. There, a
default value can also be provided for optional values.
Finally, constraints can be used to not only validate input,
but also convert said input into proper Erlang terms, all in
one step.
:: Structure
Constraints are provided as a list of fields and for each
field a list of constraints for that field can be provided.
Fields are either the name of the field; the name and
one or more constraints; or the name, one or more constraints
and a default value.
When no default value is provided then the field is required.
Otherwise the default value is used.
All constraints for a field will be used to match its value
in the order they are given. If the value is modified by a
constraint, the next constraint receives the updated value.
:: Built-in constraints
|| Constraint Description
|
| int Convert binary value to integer
| nonempty Ensures the binary value is non-empty
:: Custom constraint
In addition to the predefined constraints, Cowboy will accept
a fun. This fun must accept one argument and return one of
`true`, `{true, NewValue}` or `false`. The result indicates
whether the value matches the constraint, and if it does it
can optionally be modified. This allows converting the value
to a more appropriate Erlang term.
Note that constraint functions SHOULD be pure and MUST NOT crash.

View file

@ -116,22 +116,47 @@ 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.
Cowboy provides two different ways to read cookies. You
can either parse them as a list of key/value pairs, or
match them into a map, optionally applying constraints
to the values or providing a default if they are missing.
You can parse the cookies and then use standard library
functions to access individual values.
``` erlang
{CookieVal, Req2} = cowboy_req:cookie(<<"lang">>, Req).
Cookies = cowboy_req:parse_cookies(Req),
{_, Lang} = lists:keyfind(<<"lang">>, 1, Cookies).
```
You can also get a default value returned when the cookie
isn't set.
You can match the cookies into a map.
``` erlang
{CookieVal, Req2} = cowboy_req:cookie(<<"lang">>, Req, <<"fr">>).
#{id := ID, lang := Lang} = cowboy_req:match_cookies(Req, [id, lang]).
```
And you can obtain all cookies at once as a list of
key/value tuples.
You can use constraints to validate the values while matching
them. The following snippet will crash if the `id` cookie is
not an integer number or if the `lang` cookie is empty. Additionally
the `id` cookie value will be converted to an integer term, saving
you a conversion step.
``` erlang
{AllCookies, Req2} = cowboy_req:cookies(Req).
CookiesMap = cowboy_req:match_cookies(Req, [{id, int}, {lang, nonempty}]).
```
Note that if two cookies share the same name, then the map value
will be a list of the two cookie values.
Read more about ^constraints^.
A default value can be provided. The default will be used
if the `lang` cookie is not found. It will not be used if
the cookie is found but has an empty value.
``` erlang
#{lang := Lang} = cowboy_req:match_cookies(Req, [{lang, [], <<"en-US">>}]).
```
If no default is provided and the value is missing, the
query string is deemed invalid and the process will crash.

View file

@ -154,7 +154,7 @@ the `handle/2` function like this to send a reply.
``` erlang
handle(Req, State=#state{}) ->
{ok, Req2} = cowboy_req:reply(200,
Req2 = cowboy_req:reply(200,
[{<<"content-type">>, <<"text/plain">>}],
<<"Hello Erlang!">>,
Req),

View file

@ -73,8 +73,7 @@ custom_404_hook(404, Headers, <<>>, Req) ->
Body = <<"404 Not Found.">>,
Headers2 = lists:keyreplace(<<"content-length">>, 1, Headers,
{<<"content-length">>, integer_to_list(byte_size(Body))}),
{ok, Req2} = cowboy_req:reply(404, Headers2, Body, Req),
Req2;
cowboy_req:reply(404, Headers2, Body, Req);
custom_404_hook(_, _, _, Req) ->
Req.
```

View file

@ -61,7 +61,7 @@ continue with the handler code, so we use the
init(_Type, Req, Opts) ->
case lists:keyfind(lang, 1, Opts) of
false ->
{ok, Req2} = cowboy_req:reply(500, [
Req2 = cowboy_req:reply(500, [
{<<"content-type">>, <<"text/plain">>}
], "Missing option 'lang'.", Req),
{shutdown, Req2, no_state};
@ -110,7 +110,7 @@ The following handle function will send a fairly original response.
``` erlang
handle(Req, State) ->
{ok, Req2} = cowboy_req:reply(200, [
Req2 = cowboy_req:reply(200, [
{<<"content-type">>, <<"text/plain">>}
], <<"Hello World!">>, Req),
{ok, Req2, State}.

View file

@ -80,7 +80,7 @@ as the reply is sent.
This snippet will force Cowboy to close the connection.
``` erlang
{ok, Req2} = cowboy_req:reply(200, [
Req2 = cowboy_req:reply(200, [
{<<"connection">>, <<"close">>},
], <<"Closing the socket in 3.. 2.. 1..">>, Req).
```

View file

@ -15,6 +15,7 @@ best use of Cowboy for writing powerful web applications.
* ^"The life of a request^http_req_life
* ^"Routing^routing
* ^"Constraints^constraints
* ^"Handling plain HTTP requests^http_handlers
* ^"The Req object^req
* ^"Reading the request body^req_body

View file

@ -60,7 +60,7 @@ message otherwise.
``` erlang
info({reply, Body}, Req, State) ->
{ok, Req2} = cowboy_req:reply(200, [], Body, Req),
Req2 = cowboy_req:reply(200, [], Body, Req),
{ok, Req2, State};
info(_Msg, Req, State) ->
{loop, Req, State, hibernate}.
@ -95,13 +95,13 @@ and the loop is stopped by sending an `eof` message.
``` erlang
init(_Type, Req, _Opts) ->
{ok, Req2} = cowboy_req:chunked_reply(200, [], Req),
Req2 = cowboy_req:chunked_reply(200, [], Req),
{loop, Req2, undefined_state}.
info(eof, Req, State) ->
{ok, Req, State};
info({chunk, Chunk}, Req, State) ->
ok = cowboy_req:chunk(Chunk, Req),
cowboy_req:chunk(Chunk, Req),
{loop, Req, State};
info(_Msg, Req, State) ->
{loop, Req, State}.

View file

@ -17,7 +17,7 @@ You can quickly figure out if a multipart message
has been sent by parsing the `content-type` header.
``` erlang
{ok, {<<"multipart">>, <<"form-data">>, _}, Req2}
{<<"multipart">>, <<"form-data">>, _}
= cowboy_req:parse_header(<<"content-type">>, Req).
```

View file

@ -27,47 +27,22 @@ 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.
With the exception of functions manipulating the request
body, all functions return a single value. Depending on
the function this can be the requested value (method,
host, path, ...), a boolean (has_body, has_resp_header...)
a new Req object (set_resp_body, set_resp_header...), or
simply the atom `ok` (chunk, continue, ...).
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 request body reading functions may return `{Result, Req}`
or `{Result, Value, Req}`. The functions in this category
are `body/{1,2}`, `body_qs/{1,2}`, `part/{1,2}`, `part_body/{1,2}`.
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}`,
`parse_header/{2,3}`, `part/{1,2}`, `part_body/{1,2}`
and `reply/{2,3,4}`. Finally, the group also includes the
`chunk/2` and `continue/1` functions which always return `ok`.
The final group modifies the Req object state without
performing any immediate operations. As these functions
can't fail, they always return a new `Req` directly.
This 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.
This chapter covers the access functions mainly. Cookies,
request body and response functions are covered in their
own chapters.
:: Request
@ -82,7 +57,7 @@ GET, HEAD, OPTIONS, PATCH, POST, PUT, DELETE. Method names
are case sensitive.
``` erlang
{Method, Req2} = cowboy_req:method(Req).
Method = cowboy_req:method(Req).
```
The host, port and path parts of the URL identify the resource
@ -90,15 +65,15 @@ being accessed. The host and port information may not be
available if the client uses HTTP/1.0.
``` erlang
{Host, Req2} = cowboy_req:host(Req),
{Port, Req3} = cowboy_req:port(Req2),
{Path, Req4} = cowboy_req:path(Req3).
Host = cowboy_req:host(Req),
Port = cowboy_req:port(Req),
Path = cowboy_req:path(Req).
```
The version used by the client can of course also be obtained.
``` erlang
{Version, Req2} = cowboy_req:version(Req).
Version = cowboy_req:version(Req).
```
Do note however that clients claiming to implement one version
@ -115,21 +90,21 @@ You can fetch a single binding. The value will be `undefined`
if the binding doesn't exist.
``` erlang
{Binding, Req2} = cowboy_req:binding(my_binding, Req).
Binding = cowboy_req:binding(my_binding, Req).
```
If you need a different value when the binding doesn't exist,
you can change the default.
``` erlang
{Binding, Req2} = cowboy_req:binding(my_binding, Req, 42).
Binding = cowboy_req:binding(my_binding, Req, 42).
```
You can also obtain all bindings in one call. They will be
returned as a list of key/value tuples.
``` erlang
{AllBindings, Req2} = cowboy_req:bindings(Req).
AllBindings = cowboy_req:bindings(Req).
```
If you used `...` at the beginning of the route's pattern
@ -137,7 +112,7 @@ for the host, you can retrieve the matched part of the host.
The value will be `undefined` otherwise.
``` erlang
{HostInfo, Req2} = cowboy_req:host_info(Req).
HostInfo = cowboy_req:host_info(Req).
```
Similarly, if you used `...` at the end of the route's
@ -145,49 +120,70 @@ pattern for the path, you can retrieve the matched part,
or get `undefined` otherwise.
``` erlang
{PathInfo, Req2} = cowboy_req:path_info(Req).
PathInfo = cowboy_req:path_info(Req).
```
:: Query string
The query string can be obtained directly.
The raw query string can be obtained directly.
``` erlang
{Qs, Req2} = cowboy_req:qs(Req).
Qs = cowboy_req:qs(Req).
```
You can also requests only one value.
You can parse the query string and then use standard library
functions to access individual values.
``` erlang
{QsVal, Req2} = cowboy_req:qs_val(<<"lang">>, Req).
QsVals = cowboy_req:parse_qs(Req),
{_, Lang} = lists:keyfind(<<"lang">>, 1, QsVals).
```
If that value is optional, you can define a default to simplify
your task.
You can match the query string into a map.
``` erlang
{QsVal, Req2} = cowboy_req:qs_val(<<"lang">>, Req, <<"en">>).
#{id := ID, lang := Lang} = cowboy_req:match_qs(Req, [id, lang]).
```
Finally, you can obtain all query string values.
You can use constraints to validate the values while matching
them. The following snippet will crash if the `id` value is
not an integer number or if the `lang` value is empty. Additionally
the `id` value will be converted to an integer term, saving
you a conversion step.
``` erlang
{AllValues, Req2} = cowboy_req:qs_vals(Req).
QsMap = cowboy_req:match_qs(Req, [{id, int}, {lang, nonempty}]).
```
Note that in the case of duplicate query string keys, the map
value will become a list of the different values.
Read more about ^constraints^.
A default value can be provided. The default will be used
if the `lang` key is not found. It will not be used if
the key is found but has an empty value.
``` erlang
#{lang := Lang} = cowboy_req:match_qs(Req, [{lang, [], <<"en-US">>}]).
```
If no default is provided and the value is missing, the
query string is deemed invalid and the process will crash.
:: Request URL
You can reconstruct the full URL of the resource.
``` erlang
{URL, Req2} = cowboy_req:url(Req).
URL = 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).
BaseURL = cowboy_req:host_url(Req).
```
:: Headers
@ -198,57 +194,43 @@ 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).
HeaderVal = cowboy_req:header(<<"content-type">>, Req).
```
You can of course set a default in case the header is missing.
``` erlang
{HeaderVal, Req2}
HeaderVal
= cowboy_req:header(<<"content-type">>, Req, <<"text/plain">>).
```
And also obtain all headers.
``` erlang
{AllHeaders, Req2} = cowboy_req:headers(Req).
AllHeaders = 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`.
where you would call `header/{2,3}` otherwise.
``` erlang
{ok, ParsedVal, Req2} = cowboy_req:parse_header(<<"content-type">>, Req).
ParsedVal = 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.
Cowboy will crash if it doesn't know how to parse the given
header, or if the value is invalid.
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">>, []}).
ParsedVal = 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.
manual.
:: Meta
@ -260,13 +242,13 @@ 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).
MetaVal = cowboy_req:meta(websocket_version, Req).
```
You can change the default value if needed.
``` erlang
{MetaVal, Req2} = cowboy_req:meta(websocket_version, Req, 13).
MetaVal = cowboy_req:meta(websocket_version, Req, 13).
```
You can also define your own meta values. The name must be
@ -283,7 +265,7 @@ 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).
{IP, Port} = cowboy_req:peer(Req).
```
:: Reducing the memory footprint

View file

@ -37,7 +37,7 @@ You can obtain the body length if it was sent with the
request.
``` erlang
{Length, Req2} = cowboy_req:body_length(Req).
Length = cowboy_req:body_length(Req).
```
The value returned will be `undefined` if the length
@ -123,7 +123,7 @@ ignored.
The following example shows how to set both options.
``` erlang
{ok, Req2} = cowboy_req:body(Req, [
{ok, Data, Req2} = cowboy_req:body(Req, [
{transfer_decode, fun transfer_decode/2, TransferState},
{content_decode, fun content_decode/1}
]).

View file

@ -16,7 +16,7 @@ Cowboy will make sure to send the mandatory headers with
the response.
``` erlang
{ok, Req2} = cowboy_req:reply(200, Req).
Req2 = cowboy_req:reply(200, Req).
```
You can define headers to be sent with the response. Note
@ -24,7 +24,7 @@ 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, [
Req2 = cowboy_req:reply(303, [
{<<"location">>, <<"http://ninenines.eu">>}
], Req).
```
@ -35,7 +35,7 @@ by Cowboy. For example, you can advertise yourself as a
different server.
``` erlang
{ok, Req2} = cowboy_req:reply(200, [
Req2 = cowboy_req:reply(200, [
{<<"server">>, <<"yaws">>}
], Req).
```
@ -49,7 +49,7 @@ 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, [
Req2 = cowboy_req:reply(200, [
{<<"content-type">>, <<"text/plain">>}
], "Hello world!", Req).
```
@ -57,7 +57,7 @@ client may know how to read the body.
Here is the same example but sending HTML this time.
``` erlang
{ok, Req2} = cowboy_req:reply(200, [
Req2 = cowboy_req:reply(200, [
{<<"content-type">>, <<"text/html">>}
], "<html><head>Hello world!</head><body><p>Hats off!</p></body></html>", Req).
```
@ -71,10 +71,10 @@ 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).
Req2 = cowboy_req:chunked_reply(200, Req),
cowboy_req:chunk("Hello...", Req2),
cowboy_req:chunk("chunked...", Req2),
cowboy_req:chunk("world!!", Req2).
```
You should make sure to match on `ok` as an error may be
@ -85,11 +85,11 @@ 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, [
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).
cowboy_req:chunk("<html><head>Hello world!</head>", Req2),
cowboy_req:chunk("<body><p>Hats off!</p></body></html>", Req2).
```
Note that the reply and each chunk following it are sent

View file

@ -68,16 +68,16 @@ the connection, assuming no correct subprotocol was found.
``` erlang
websocket_init(_Type, Req, _Opts) ->
case cowboy_req:parse_header(<<"sec-websocket-protocol">>, Req) of
{ok, undefined, Req2} ->
undefined ->
{ok, Req, #state{}};
{ok, Subprotocols, Req2} ->
Subprotocols ->
case lists:keymember(<<"mychat2">>, 1, Subprotocols) of
true ->
Req3 = cowboy_req:set_resp_header(<<"sec-websocket-protocol">>,
<<"mychat2">>, Req2),
{ok, Req3, #state{}};
Req2 = cowboy_req:set_resp_header(<<"sec-websocket-protocol">>,
<<"mychat2">>, Req),
{ok, Req2, #state{}};
false ->
{shutdown, Req2}
{shutdown, Req}
end
end.
```

View file

@ -5,6 +5,12 @@ manipulating Ranch listeners.
:: Types
: fields() = [atom()
| {atom(), cowboy_constraints:constraint() | [cowboy_constraints:constraint()]}
| {atom(), cowboy_constraints:constraint() | [cowboy_constraints:constraint()], any()}]
Fields for match operations. Constraint(s) and default value are optional.
: http_headers() = [{binary(), iodata()}]
HTTP headers as a list of key/values.

View file

@ -6,21 +6,19 @@ and respond to requests.
The functions in this module follow patterns for their return types,
based on the kind of function.
* access: `{Value, Req}`
* action: `{Result, Req} | {Result, Value, Req} | {error, atom()}`
* access: `Value`
* action: `ok | {Result, Req} | {Result, Value, Req}`
* modification: `Req`
* question: `boolean()`
The only exception is the `chunk/2` function which may return `ok`.
Whenever `Req` is returned, you must use this returned value and
ignore any previous you may have had. This value contains various
state informations which are necessary for Cowboy to do some lazy
evaluation or cache results where appropriate.
values which are necessary for Cowboy to keep track of the request
and response states.
All functions which perform an action should only be called once.
This includes reading the request body or replying. Cowboy will
generally throw an error on the second call.
throw an error on the second call when it detects suspicious behavior.
It is highly discouraged to pass the Req object to another process.
Doing so and calling `cowboy_req` functions from it leads to
@ -54,7 +52,7 @@ the function descriptions below.
:: Request related exports
: binding(Name, Req) -> binding(Name, Req, undefined)
: binding(Name, Req, Default) -> {Value, Req2}
: binding(Name, Req, Default) -> Value
Types:
@ -68,7 +66,7 @@ By default the value is a binary, however constraints may change
the type of this value (for example automatically converting
numbers to integer).
: bindings(Req) -> {[{Name, Value}], Req2}
: bindings(Req) -> [{Name, Value}]
Types:
@ -81,30 +79,8 @@ By default the value is a binary, however constraints may change
the type of this value (for example automatically converting
numbers to integer).
: cookie(Name, Req) -> cookie(Name, Req, undefined)
: cookie(Name, Req, Default) -> {Value, Req2}
Types:
* Name = binary()
* Default = any()
* Value = binary() | Default
Return the value for the given cookie.
Cookie names are case sensitive.
: cookies(Req) -> {[{Name, Value}], Req2}
Types:
* Name = binary()
* Value = binary()
Return all cookies.
: header(Name, Req) -> header(Name, Req, undefined)
: header(Name, Req, Default) -> {Value, Req2}
: header(Name, Req, Default) -> Value
Types:
@ -117,7 +93,7 @@ Return the value for the given header.
While header names are case insensitive, this function expects
the name to be a lowercase binary.
: headers(Req) -> {Headers, Req2}
: headers(Req) -> Headers
Types:
@ -125,7 +101,7 @@ Types:
Return all headers.
: host(Req) -> {Host, Req2}
: host(Req) -> Host
Types:
@ -133,7 +109,7 @@ Types:
Return the requested host.
: host_info(Req) -> {HostInfo, Req2}
: host_info(Req) -> HostInfo
Types:
@ -141,7 +117,7 @@ Types:
Return the extra tokens from matching against `...` during routing.
: host_url(Req) -> {HostURL, Req2}
: host_url(Req) -> HostURL
Types:
@ -153,8 +129,57 @@ This function will always return `undefined` until the
`cowboy_router` middleware has been executed. This includes
the `onrequest` hook.
: match_cookies(Req, Fields) -> Map
Types:
* Fields = cowboy:fields()
* Map = map()
Match cookies against the given fields.
Cowboy will only return the cookie values specified in the
fields list, and ignore all others. Fields can be either
the name of the cookie requested; the name along with a
list of constraints; or the name, a list of constraints
and a default value in case the cookie is missing.
This function will crash if the cookie is missing and no
default value is provided. This function will also crash
if a constraint fails.
The name of the cookie must be provided as an atom. The
key of the returned map will be that atom. The value may
be converted through the use of constraints, making this
function able to extract, validate and convert values all
in one step.
: match_qs(Req, Fields) -> Map
Types:
* Fields = cowboy:fields()
* Map = map()
Match the query string against the given fields.
Cowboy will only return the query string values specified
in the fields list, and ignore all others. Fields can be
either the key requested; the key along with a list of
constraints; or the key, a list of constraints and a
default value in case the key is missing.
This function will crash if the key is missing and no
default value is provided. This function will also crash
if a constraint fails.
The key must be provided as an atom. The key of the
returned map will be that atom. The value may be converted
through the use of constraints, making this function able
to extract, validate and convert values all in one step.
: meta(Name, Req) -> meta(Name, Req, undefined)
: meta(Name, Req, Default) -> {Value, Req2}
: meta(Name, Req, Default) -> Value
Types:
@ -164,7 +189,7 @@ Types:
Return metadata about the request.
: method(Req) -> {Method, Req2}
: method(Req) -> Method
Types:
@ -174,16 +199,25 @@ Return the method.
Methods are case sensitive. Standard methods are always uppercase.
: parse_header(Name, Req) ->
: parse_header(Name, Req, Default) -> {ok, ParsedValue, Req2}
| {undefined, Value, Req2} | {error, badarg}
: parse_cookies(Req) -> [{Name, Value}]
Types:
* Name = binary()
* Value = binary()
Parse and return all cookies.
Cookie names are case sensitive.
: parse_header(Name, Req) -> see below
: parse_header(Name, Req, Default) -> ParsedValue | Default
Types:
* Name = binary()
* Default = any()
* ParsedValue - see below
* Value = any()
Parse the given header.
@ -196,6 +230,8 @@ following table summarizes the default values used.
|| Header name Default value
|
| content-length `0`
| cookie `[]`
| transfer-encoding `[<<"identity">>]`
| Any other header `undefined`
@ -241,8 +277,8 @@ except the value of the charset parameter, which is case insensitive.
All other values are case insensitive and will be returned as lowercase.
The headers accept, accept-encoding and cookie headers can return
an empty list. Others will return `{error, badarg}` if the header
value is empty.
an empty list. Some other headers are expected to have a value if provided
and may crash if the value is missing.
The authorization header parsing code currently only supports basic
HTTP authentication. The `Credentials` type is thus `{Username, Password}`
@ -257,7 +293,21 @@ The range header value `Range` can take three forms:
An `undefined` tuple will be returned if Cowboy doesn't know how
to parse the requested header.
: path(Req) -> {Path, Req2}
: parse_qs(Req) -> [{Name, Value}]
Types:
* Name = binary()
* Value = binary() | true
Return the request's query string as a list of tuples.
The atom `true` is returned for keys which have no value.
Keys with no value are different from keys with an empty
value in that they do not have a `=` indicating the presence
of a value.
: path(Req) -> Path
Types:
@ -265,7 +315,7 @@ Types:
Return the requested path.
: path_info(Req) -> {PathInfo, Req2}
: path_info(Req) -> PathInfo
Types:
@ -273,7 +323,7 @@ Types:
Return the extra tokens from matching against `...` during routing.
: peer(Req) -> {Peer, Req2}
: peer(Req) -> Peer
Types:
@ -281,7 +331,7 @@ Types:
Return the client's IP address and port number.
: port(Req) -> {Port, Req2}
: port(Req) -> Port
Types:
@ -293,7 +343,7 @@ The port returned by this function is obtained by parsing
the host header. It may be different than the actual port
the client used to connect to the Cowboy server.
: qs(Req) -> {QueryString, Req2}
: qs(Req) -> QueryString
Types:
@ -301,32 +351,6 @@ Types:
Return the request's query string.
: qs_val(Name, Req) -> qs_val(Name, Req, undefined)
: qs_val(Name, Req, Default) -> {Value, Req2}
Types:
* Name = binary()
* Default = any()
* Value = binary() | true
Return a value from the request's query string.
The value `true` will be returned when the name was found
in the query string without an associated value.
: qs_vals(Req) -> {[{Name, Value}], Req2}
Types:
* Name = binary()
* Value = binary() | true
Return the request's query string as a list of tuples.
The value `true` will be returned when a name was found
in the query string without an associated value.
: set_meta(Name, Value, Req) -> Req2
Types:
@ -338,7 +362,7 @@ Set metadata about the request.
An existing value will be overwritten.
: url(Req) -> {URL, Req2}
: url(Req) -> URL
Types:
@ -350,7 +374,7 @@ This function will always return `undefined` until the
`cowboy_router` middleware has been executed. This includes
the `onrequest` hook.
: version(Req) -> {Version, Req2}
: version(Req) -> Version
Types:
@ -361,7 +385,7 @@ Return the HTTP version used for this request.
:: Request body related exports
: body(Req) -> body(Req, [])
: body(Req, Opts) -> {ok, Data, Req2} | {more, Data, Req2} | {error, Reason}
: body(Req, Opts) -> {ok, Data, Req2} | {more, Data, Req2}
Types:
@ -400,7 +424,7 @@ the content-length header if it wasn't already there.
This function can only be called once. Cowboy will not cache
the result of this call.
: body_length(Req) -> {Length, Req2}
: body_length(Req) -> Length
Types:
@ -414,8 +438,7 @@ is present.
: body_qs(Req) -> body_qs(Req,
[{length, 64000}, {read_length, 64000}, {read_timeout, 5000}])
: body_qs(Req, Opts) -> {ok, [{Name, Value}], Req2}
| {badlength, Req2} | {error, Reason}
: body_qs(Req, Opts) -> {ok, [{Name, Value}], Req2} | {badlength, Req2}
Types:
@ -501,7 +524,7 @@ it cannot be read again.
:: Response related exports
: chunk(Data, Req) -> ok | {error, Reason}
: chunk(Data, Req) -> ok
Types:
@ -520,7 +543,7 @@ without wrapping it in an HTTP/1.1 chunk, providing
compatibility with older clients.
: chunked_reply(StatusCode, Req) -> chunked_reply(StatusCode, [], Req)
: chunked_reply(StatusCode, Headers, Req) -> {ok, Req2}
: chunked_reply(StatusCode, Headers, Req) -> Req2
Types:
@ -543,7 +566,7 @@ compatibility with older clients.
This function can only be called once, with the exception
of overriding the response in the `onresponse` hook.
: continue(Req) -> ok | {error, Reason}
: continue(Req) -> ok
Types:
@ -591,7 +614,7 @@ the name to be a lowercase binary.
: reply(StatusCode, Req) -> reply(StatusCode, [], Req)
: reply(StatusCode, Headers, Req) - see below
: reply(StatusCode, Headers, Body, Req) -> {ok, Req2}
: reply(StatusCode, Headers, Body, Req) -> Req2
Types:
@ -657,7 +680,7 @@ arguments. Only send and sendfile operations are supported.
Types:
* Fun = fun((ChunkFun) -> ok)
* ChunkFun = fun((iodata()) -> ok | {error, atom()})
* ChunkFun = fun((iodata()) -> ok)
Set a fun for sending the response body using chunked transfer-encoding.