aboutsummaryrefslogtreecommitdiffstats
path: root/doc/src
diff options
context:
space:
mode:
Diffstat (limited to 'doc/src')
-rw-r--r--doc/src/guide/broken_clients.ezdoc3
-rw-r--r--doc/src/guide/constraints.ezdoc51
-rw-r--r--doc/src/guide/cookies.ezdoc41
-rw-r--r--doc/src/guide/getting_started.ezdoc2
-rw-r--r--doc/src/guide/hooks.ezdoc3
-rw-r--r--doc/src/guide/http_handlers.ezdoc4
-rw-r--r--doc/src/guide/http_req_life.ezdoc2
-rw-r--r--doc/src/guide/index.ezdoc1
-rw-r--r--doc/src/guide/loop_handlers.ezdoc6
-rw-r--r--doc/src/guide/multipart_req.ezdoc2
-rw-r--r--doc/src/guide/req.ezdoc156
-rw-r--r--doc/src/guide/req_body.ezdoc4
-rw-r--r--doc/src/guide/resp.ezdoc24
-rw-r--r--doc/src/guide/ws_handlers.ezdoc12
-rw-r--r--doc/src/manual/cowboy.ezdoc6
-rw-r--r--doc/src/manual/cowboy_req.ezdoc195
16 files changed, 299 insertions, 213 deletions
diff --git a/doc/src/guide/broken_clients.ezdoc b/doc/src/guide/broken_clients.ezdoc
index 26568a3..c508358 100644
--- a/doc/src/guide/broken_clients.ezdoc
+++ b/doc/src/guide/broken_clients.ezdoc
@@ -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
diff --git a/doc/src/guide/constraints.ezdoc b/doc/src/guide/constraints.ezdoc
new file mode 100644
index 0000000..a05f489
--- /dev/null
+++ b/doc/src/guide/constraints.ezdoc
@@ -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.
diff --git a/doc/src/guide/cookies.ezdoc b/doc/src/guide/cookies.ezdoc
index fe9246c..459d4d8 100644
--- a/doc/src/guide/cookies.ezdoc
+++ b/doc/src/guide/cookies.ezdoc
@@ -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
+Cookies = cowboy_req:parse_cookies(Req),
+{_, Lang} = lists:keyfind(<<"lang">>, 1, Cookies).
+```
+
+You can match the cookies into a map.
``` erlang
-{CookieVal, Req2} = cowboy_req:cookie(<<"lang">>, Req).
+#{id := ID, lang := Lang} = cowboy_req:match_cookies(Req, [id, lang]).
```
-You can also get a default value returned when the cookie
-isn't set.
+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
-{CookieVal, Req2} = cowboy_req:cookie(<<"lang">>, Req, <<"fr">>).
+CookiesMap = cowboy_req:match_cookies(Req, [{id, int}, {lang, nonempty}]).
```
-And you can obtain all cookies at once as a list of
-key/value tuples.
+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
-{AllCookies, Req2} = cowboy_req:cookies(Req).
+#{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.
diff --git a/doc/src/guide/getting_started.ezdoc b/doc/src/guide/getting_started.ezdoc
index f0a701d..34f02dc 100644
--- a/doc/src/guide/getting_started.ezdoc
+++ b/doc/src/guide/getting_started.ezdoc
@@ -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),
diff --git a/doc/src/guide/hooks.ezdoc b/doc/src/guide/hooks.ezdoc
index edef971..e835a6f 100644
--- a/doc/src/guide/hooks.ezdoc
+++ b/doc/src/guide/hooks.ezdoc
@@ -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.
```
diff --git a/doc/src/guide/http_handlers.ezdoc b/doc/src/guide/http_handlers.ezdoc
index 9a450a6..9c6e41d 100644
--- a/doc/src/guide/http_handlers.ezdoc
+++ b/doc/src/guide/http_handlers.ezdoc
@@ -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}.
diff --git a/doc/src/guide/http_req_life.ezdoc b/doc/src/guide/http_req_life.ezdoc
index 5fd8486..ffe5dfa 100644
--- a/doc/src/guide/http_req_life.ezdoc
+++ b/doc/src/guide/http_req_life.ezdoc
@@ -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).
```
diff --git a/doc/src/guide/index.ezdoc b/doc/src/guide/index.ezdoc
index 38b2ac0..300cea8 100644
--- a/doc/src/guide/index.ezdoc
+++ b/doc/src/guide/index.ezdoc
@@ -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
diff --git a/doc/src/guide/loop_handlers.ezdoc b/doc/src/guide/loop_handlers.ezdoc
index fba4feb..b1c033f 100644
--- a/doc/src/guide/loop_handlers.ezdoc
+++ b/doc/src/guide/loop_handlers.ezdoc
@@ -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}.
diff --git a/doc/src/guide/multipart_req.ezdoc b/doc/src/guide/multipart_req.ezdoc
index a807e48..21762f6 100644
--- a/doc/src/guide/multipart_req.ezdoc
+++ b/doc/src/guide/multipart_req.ezdoc
@@ -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).
```
diff --git a/doc/src/guide/req.ezdoc b/doc/src/guide/req.ezdoc
index 9501158..1349af3 100644
--- a/doc/src/guide/req.ezdoc
+++ b/doc/src/guide/req.ezdoc
@@ -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.
-
-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}`,
-`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.
+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 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}`.
+
+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 = cowboy_req:qs(Req).
+```
+
+You can parse the query string and then use standard library
+functions to access individual values.
``` erlang
-{Qs, Req2} = cowboy_req:qs(Req).
+QsVals = cowboy_req:parse_qs(Req),
+{_, Lang} = lists:keyfind(<<"lang">>, 1, QsVals).
```
-You can also requests only one value.
+You can match the query string into a map.
``` erlang
-{QsVal, Req2} = cowboy_req:qs_val(<<"lang">>, Req).
+#{id := ID, lang := Lang} = cowboy_req:match_qs(Req, [id, lang]).
```
-If that value is optional, you can define a default to simplify
-your task.
+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
-{QsVal, Req2} = cowboy_req:qs_val(<<"lang">>, Req, <<"en">>).
+QsMap = cowboy_req:match_qs(Req, [{id, int}, {lang, nonempty}]).
```
-Finally, you can obtain all query string values.
+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
-{AllValues, Req2} = cowboy_req:qs_vals(Req).
+#{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`.
-
-``` 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.
+where you would call `header/{2,3}` otherwise.
``` erlang
-{undefined, HeaderVal, Req2}
- = cowboy_req:parse_header(<<"unicorn-header">>, Req).
+ParsedVal = cowboy_req:parse_header(<<"content-type">>, 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
diff --git a/doc/src/guide/req_body.ezdoc b/doc/src/guide/req_body.ezdoc
index 44f32f8..8864035 100644
--- a/doc/src/guide/req_body.ezdoc
+++ b/doc/src/guide/req_body.ezdoc
@@ -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}
]).
diff --git a/doc/src/guide/resp.ezdoc b/doc/src/guide/resp.ezdoc
index 28f2544..009756a 100644
--- a/doc/src/guide/resp.ezdoc
+++ b/doc/src/guide/resp.ezdoc
@@ -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
diff --git a/doc/src/guide/ws_handlers.ezdoc b/doc/src/guide/ws_handlers.ezdoc
index 9c3e2b7..1c84b98 100644
--- a/doc/src/guide/ws_handlers.ezdoc
+++ b/doc/src/guide/ws_handlers.ezdoc
@@ -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.
```
diff --git a/doc/src/manual/cowboy.ezdoc b/doc/src/manual/cowboy.ezdoc
index a207afe..209d473 100644
--- a/doc/src/manual/cowboy.ezdoc
+++ b/doc/src/manual/cowboy.ezdoc
@@ -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.
diff --git a/doc/src/manual/cowboy_req.ezdoc b/doc/src/manual/cowboy_req.ezdoc
index beac1f4..94556b2 100644
--- a/doc/src/manual/cowboy_req.ezdoc
+++ b/doc/src/manual/cowboy_req.ezdoc
@@ -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.