diff options
Diffstat (limited to 'doc/src')
-rw-r--r-- | doc/src/guide/broken_clients.ezdoc | 3 | ||||
-rw-r--r-- | doc/src/guide/constraints.ezdoc | 51 | ||||
-rw-r--r-- | doc/src/guide/cookies.ezdoc | 41 | ||||
-rw-r--r-- | doc/src/guide/getting_started.ezdoc | 2 | ||||
-rw-r--r-- | doc/src/guide/hooks.ezdoc | 3 | ||||
-rw-r--r-- | doc/src/guide/http_handlers.ezdoc | 4 | ||||
-rw-r--r-- | doc/src/guide/http_req_life.ezdoc | 2 | ||||
-rw-r--r-- | doc/src/guide/index.ezdoc | 1 | ||||
-rw-r--r-- | doc/src/guide/loop_handlers.ezdoc | 6 | ||||
-rw-r--r-- | doc/src/guide/multipart_req.ezdoc | 2 | ||||
-rw-r--r-- | doc/src/guide/req.ezdoc | 156 | ||||
-rw-r--r-- | doc/src/guide/req_body.ezdoc | 4 | ||||
-rw-r--r-- | doc/src/guide/resp.ezdoc | 24 | ||||
-rw-r--r-- | doc/src/guide/ws_handlers.ezdoc | 12 | ||||
-rw-r--r-- | doc/src/manual/cowboy.ezdoc | 6 | ||||
-rw-r--r-- | doc/src/manual/cowboy_req.ezdoc | 195 |
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. |