diff options
author | Loïc Hoguin <[email protected]> | 2013-09-23 15:44:10 +0200 |
---|---|---|
committer | Loïc Hoguin <[email protected]> | 2013-09-23 15:44:10 +0200 |
commit | 2b2829f5855e4b2a6ba5e0ca5ccefd42fa2e9524 (patch) | |
tree | 70507b858d1004fb9e3c6c5a9c964ebdbce34546 | |
parent | eb4843a46b781f93030b623e2b1feb1898ba3da0 (diff) | |
download | cowboy-2b2829f5855e4b2a6ba5e0ca5ccefd42fa2e9524.tar.gz cowboy-2b2829f5855e4b2a6ba5e0ca5ccefd42fa2e9524.tar.bz2 cowboy-2b2829f5855e4b2a6ba5e0ca5ccefd42fa2e9524.zip |
Greatly expand on the Req object
Cut in four different chapters: request, request body,
response and cookies.
-rw-r--r-- | guide/cookies.md | 140 | ||||
-rw-r--r-- | guide/req.md | 426 | ||||
-rw-r--r-- | guide/req_body.md | 169 | ||||
-rw-r--r-- | guide/resp.md | 203 | ||||
-rw-r--r-- | guide/toc.md | 5 |
5 files changed, 776 insertions, 167 deletions
diff --git a/guide/cookies.md b/guide/cookies.md new file mode 100644 index 0000000..bfc8651 --- /dev/null +++ b/guide/cookies.md @@ -0,0 +1,140 @@ +Using cookies +============= + +Cookies are a mechanism allowing applications to maintain +state on top of the stateless HTTP protocol. + +Cowboy provides facilities for handling cookies. It is highly +recommended to use them instead of writing your own, as the +implementation of cookies can vary greatly between clients. + +Cookies are stored client-side and sent with every subsequent +request that matches the domain and path for which they were +stored, including requests for static files. For this reason +they can incur a cost which must be taken in consideration. + +Also consider that, regardless of the options used, cookies +are not to be trusted. They may be read and modified by any +program on the user's computer, but also by proxies. You +should always validate cookie values before using them. Do +not store any sensitive information in cookies either. + +When explicitly setting the domain, the cookie will be sent +for the domain and all subdomains from that domain. Otherwise +the current domain will be used. The same is true for the +path. + +When the server sets cookies, they will only be available +for requests that are sent after the client receives the +response. + +Cookies are sent in HTTP headers, therefore they must have +text values. It is your responsibility to encode any other +data type. Also note that cookie names are de facto case +sensitive. + +Cookies can be set for the client session (which generally +means until the browser is closed), or it can be set for +a number of seconds. Once it expires, or when the server +says the cookie must exist for up to 0 seconds, the cookie +is deleted by the client. To avoid this while the user +is browsing your site, you should set the cookie for +every request, essentially resetting the expiration time. + +Cookies can be restricted to secure channels. This typically +means that such a cookie will only be sent over HTTPS, +and that it will only be available by client-side scripts +that run from HTTPS webpages. + +Finally, cookies can be restricted to HTTP and HTTPS requests, +essentially disabling their access from client-side scripts. + +Setting cookies +--------------- + +By default, cookies you set are defined for the session. + +``` erlang +SessionID = generate_session_id(), +Req2 = cowboy_req:set_resp_cookie(<<"sessionid">>, SessionID, [], Req). +``` + +You can also make them expire at a specific point in the +future. + +``` erlang +SessionID = generate_session_id(), +Req2 = cowboy_req:set_resp_cookie(<<"sessionid">>, SessionID, [ + {max_age, 3600} +], Req). +``` + +You can delete cookies that have already been set. The value +is ignored. + +``` erlang +Req2 = cowboy_req:set_resp_cookie(<<"sessionid">>, <<>>, [ + {max_age, 0} +], Req). +``` + +You can restrict them to a specific domain and path. +For example, the following cookie will be set for the domain +`my.example.org` and all its subdomains, but only on the path +`/account` and all its subdirectories. + +``` erlang +Req2 = cowboy_req:set_resp_cookie(<<"inaccount">>, <<"1">>, [ + {domain, "my.example.org"}, + {path, "/account"} +], Req). +``` + +You can restrict the cookie to secure channels, typically HTTPS. + +``` erlang +SessionID = generate_session_id(), +Req2 = cowboy_req:set_resp_cookie(<<"sessionid">>, SessionID, [ + {secure, true} +], Req). +``` + +You can restrict the cookie to client-server communication +only. Such a cookie will not be available to client-side scripts. + +``` erlang +SessionID = generate_session_id(), +Req2 = cowboy_req:set_resp_cookie(<<"sessionid">>, SessionID, [ + {http_only, true} +], Req). +``` + +Cookies may also be set client-side, for example using +Javascript. + +Reading cookies +--------------- + +As we said, the client sends cookies with every request. +But unlike the server, the client only sends the cookie +name and value. + +You can read the value of a cookie. + +``` erlang +{CookieVal, Req2} = cowboy_req:cookie(<<"lang">>, Req). +``` + +You can also get a default value returned when the cookie +isn't set. + +``` erlang +{CookieVal, Req2} = cowboy_req:cookie(<<"lang">>, Req, <<"fr">>). +``` + +And you can obtain all cookies at once as a list of +key/value tuples. + +``` erlang +{AllCookies, Req2} = cowboy_req:cookies(Req). +``` diff --git a/guide/req.md b/guide/req.md index 96f72b9..2b59152 100644 --- a/guide/req.md +++ b/guide/req.md @@ -1,204 +1,298 @@ -Request object +The Req object ============== -Purpose +The Req object is this variable that you will use to obtain +information about a request, read the body of the request +and send a response. + +A special variable +------------------ + +While we call it an "object", it is not an object in the +OOP sense of the term. In fact it is completely opaque +to you and the only way you can perform operations using +it is by calling the functions from the `cowboy_req` +module. + +Almost all the calls to the `cowboy_req` module will +return an updated request object. Just like you would +keep the updated `State` variable in a gen_server, +you MUST keep the updated `Req` variable in a Cowboy +handler. Cowboy will use this object to know whether +a response has been sent when the handler has finished +executing. + +The Req object allows accessing both immutable and +mutable state. This means that calling some of the +functions twice will not produce the same result. +For example, when streaming the request body, the +function will return the body by chunks, one at a +time, until there is none left. + +It also caches the result of operations performed +on the immutable state. That means that some calls +will give a result much faster when called many times. + +Overview of the cowboy_req interface +------------------------------------ + +The `cowboy_req` interface is divided in four groups +of functions, each having a well defined return type +signature common to the entire group. + +The first group, access functions, will always return +`{Value, Req}`. The group includes all the following +functions: `binding/{2,3}`, `bindings/1`, `body_length/1`, +`cookie/{2,3}`, `cookies/1`, `header/{2,3}`, `headers/1`, +`host/1`, `host_info/1`, `host_url/1`, `meta/{2,3}`, +`method/1`, `path/1`, `path_info/1`, `peer/1`, `port/1`, +`qs/1`, `qs_val/{2,3}`, `qs_vals/1`, `url/1`, `version/1`. + +The second group, question functions, will always return +a `boolean()`. The group includes the following three +functions: `has_body/1`, `has_resp_body/1`, `has_resp_header/2`. + +The third group contains the functions that manipulate +the socket or perform operations that may legitimately fail. +They may return `{Result, Req}`, `{Result, Value, Req}` +or `{error, atom()}`. This includes the following functions: +`body/{1,2}`, `body_qs/{1,2}`, `chunked_reply/{2,3}`, +`init_stream/4`, `parse_header/{2,3}`, `reply/{2,3,4}`, +`skip_body/1`, `stream_body/{1,2}`. Finally, the group +also includes the `chunk/2` function which always returns +`ok`. + +The final group modifies the Req object, so it always return +a new `Req`. It includes the following functions: `compact/1`, +`delete_resp_header/2`, `set_meta/3`, `set_resp_body/2`, +`set_resp_body_fun/{2,3}`, `set_resp_cookie/4`, `set_resp_header/3`. + +This chapter covers most of the first group, plus a few other +functions. The next few chapters cover cookies handling, reading +the request body and sending a response. + +Request ------- -The request object is a special variable that can be used -to interact with a request, extracting information from it -or modifying it, and sending a response. +When a client performs a request, it first sends a few required +values. They are sent differently depending on the protocol +being used, but the intent is the same. They indicate to the +server the type of action it wants to do and how to locate +the resource to perform it on. -It's a special variable because it contains both immutable -and mutable state. This means that some operations performed -on the request object will always return the same result, -while others will not. For example, obtaining request headers -can be repeated safely. Obtaining the request body can only -be done once, as it is read directly from the socket. +The method identifies the action. Standard methods include +GET, HEAD, OPTIONS, PATCH, POST, PUT, DELETE. Method names +are case sensitive. -With few exceptions, all calls to the `cowboy_req` module -will return an updated request object. You MUST use the new -request object instead of the old one for all subsequent -operations. +``` erlang +{Method, Req2} = cowboy_req:method(Req). +``` -Request -------- +The host, port and path parts of the URL identify the resource +being accessed. The host and port information may not be +available if the client uses HTTP/1.0. -Cowboy allows you to retrieve a lot of information about -the request. All these calls return a `{Value, Req}` tuple, -with `Value` the requested value and `Req` the updated -request object. - -The following access functions are defined in `cowboy_req`: - - * `method/1`: the request method (`<<"GET">>`, `<<"POST">>`...) - * `version/1`: the HTTP version (`'HTTP/1.0'` or `'HTTP/1.1'`) - * `peer/1`: the peer address and port number - * `host/1`: the hostname requested - * `host_info/1`: the result of the `[...]` match on the host - * `port/1`: the port number used for the connection - * `path/1`: the path requested - * `path_info/1`: the result of the `[...]` match on the path - * `qs/1`: the entire query string unmodified - * `qs_val/{2,3}`: the value for the requested query string key - * `qs_vals/1`: all key/values found in the query string - * `host_url/1`: the requested URL without the path and query string - * `url/1`: the requested URL - * `binding/{2,3}`: the value for the requested binding found during routing - * `bindings/1`: all key/values found during routing - * `header/{2,3}`: the value for the requested header name - * `headers/1`: all headers name/value - * `cookie/{2,3}`: the value for the requested cookie name - * `cookies/1`: all cookies name/value - * `meta/{2,3}`: the meta information for the requested key - -All the functions above that can take two or three arguments -take an optional third argument for the default value if -none is found. Otherwise it will return `undefined`. - -In addition, Cowboy allows you to parse headers using the -`parse_header/{2,3}` function, which takes a header name -as lowercase binary, the request object, and an optional -default value. It returns `{ok, ParsedValue, Req}` if it -could be parsed, `{undefined, RawValue, Req}` if Cowboy -doesn't know this header, and `{error, badarg}` if Cowboy -encountered an error while trying to parse it. - -Finally, Cowboy allows you to set request meta information -using the `set_meta/3` function, which takes a name, a value -and the request object and returns the latter modified. - -Request body ------------- +``` erlang +{Host, Req2} = cowboy_req:host(Req), +{Port, Req3} = cowboy_req:port(Req2), +{Path, Req4} = cowboy_req:path(Req3). +``` -Cowboy will not read the request body until you ask it to. -If you don't, then Cowboy will simply discard it. It will -not take extra memory space until you start reading it. - -Cowboy has a few utility functions for dealing with the -request body. - -The function `has_body/1` will return whether the request -contains a body. Note that some clients may not send the -right headers while still sending a body, but as Cowboy has -no way of detecting it this function will return `false`. - -The function `body_length/1` retrieves the size of the -request body. If the body is compressed, the value returned -here is the compressed size. If a `Transfer-Encoding` header -was passed in the request, then Cowboy will return a size -of `undefined`, as it has no way of knowing it. - -If you know the request contains a body, and that it is -within 8MB (for `body/1`) or 16KB (for `body_qs/1`) bytes, -then you can read it directly with either `body/1` or `body_qs/1`. -If you want to override the default size limits of `body/1` -or `body_qs/1`, you can pass the maximum body length byte -size as first parameter to `body/2` and `body_qs/2` or pass -atom `infinity` to ignore size limits. - -If the request contains bigger body than allowed default sizes -or supplied maximum body length, `body/1`, `body/2`, `body_qs/1` -and `body_qs/2` will return `{error, badlength}`. If the request -contains chunked body, `body/1`, `body/2`, `body_qs/1` -and `body_qs/2` will return `{error, chunked}`. -If you get either of the above two errors, you will want to -handle the body of the request using `stream_body/1`, -`stream_body/2` and `skip_body/1`, with the streaming process -optionally initialized using `init_stream/4`. - -Multipart request body ----------------------- - -Cowboy provides facilities for dealing with multipart bodies. -They are typically used for uploading files. You can use two -functions to process these bodies, `multipart_data/1` and -`multipart_skip/1`. - -Response +The version used by the client can of course also be obtained. + +``` erlang +{Version, Req2} = cowboy_req:version(Req). +``` + +Do note however that clients claiming to implement one version +of the protocol does not mean they implement it fully, or even +properly. + +Bindings -------- -You can send a response by calling the `reply/{2,3,4}` function. -It takes the status code for the response (usually `200`), -an optional list of headers, an optional body and the request -object. +After routing the request, bindings are available. Bindings +are these parts of the host or path that you chose to extract +when defining the routes of your application. + +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). +``` + +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). +``` + +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). +``` + +If you used `...` at the beginning of the route's pattern +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). +``` + +Similarly, if you used `...` at the end of the route's +pattern for the path, you can retrieve the matched part, +or get `undefined` otherwise. + +``` erlang +{PathInfo, Req2} = cowboy_req:path_info(Req). +``` + +Query string +------------ + +The query string can be obtained directly. + +``` erlang +{Qs, Req2} = cowboy_req:qs(Req). +``` + +You can also requests only one value. + +``` erlang +{QsVal, Req2} = cowboy_req:qs_val(<<"lang">>, Req). +``` + +If that value is optional, you can define a default to simplify +your task. + +``` erlang +{QsVal, Req2} = cowboy_req:qs_val(<<"lang">>, Req, <<"en">>). +``` + +Finally, you can obtain all query string values. + +``` erlang +{AllValues, Req2} = cowboy_req:qs_vals(Req). +``` + +Request URL +----------- + +You can reconstruct the full URL of the resource. + +``` erlang +{URL, Req2} = cowboy_req:url(Req). +``` + +You can also obtain only the base of the URL, excluding the +path and query string. + +``` erlang +{BaseURL, Req2} = cowboy_req:host_url(Req). +``` + +Headers +------- -The following snippet sends a simple response with no headers -specified but with a body. +Cowboy allows you to obtain the header values as string, +or parsed into a more meaningful representation. + +This will get the string value of a header. ``` erlang -{ok, Req2} = cowboy_req:reply(200, [], "Hello world!", Req). +{HeaderVal, Req2} = cowboy_req:header(<<"content-type">>, Req). ``` -If this is the only line in your handler then make sure to return -the `Req2` variable to Cowboy so it can know you replied. +You can of course set a default in case the header is missing. + +``` erlang +{HeaderVal, Req2} + = cowboy_req:header(<<"content-type">>, Req, <<"text/plain">>). +``` -If you want to send HTML you'll need to specify the `Content-Type` -header so the client can properly interpret it. +And also obtain all headers. ``` erlang -{ok, Req2} = cowboy_req:reply(200, - [{<<"content-type">>, <<"text/html">>}], - "<html><head>Hello world!</head><body><p>Hats off!</p></body></html>", - Req). +{AllHeaders, Req2} = cowboy_req:headers(Req). ``` -You only need to make sure to follow conventions and to use a -lowercase header name. +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`. -Chunked response ----------------- +``` erlang +{ok, ParsedVal, Req2} = cowboy_req:parse_header(<<"content-type">>, Req). +``` -You can also send chunked responses using `chunked_reply/{2,3}`. -Chunked responses allow you to send the body in chunks of various -sizes. It is the recommended way of performing streaming if the -client supports it. +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. -You must first initiate the response by calling the aforementioned -function, then you can call `chunk/2` as many times as needed. -The following snippet sends a body in three chunks. +``` erlang +{undefined, HeaderVal, Req2} + = cowboy_req:parse_header(<<"unicorn-header">>, Req). +``` + +When parsing fails, `{error, Reason}` is returned instead. + +You can of course define a default value. Note that the default +value you specify here is the parsed value you'd like to get +by default. ``` erlang -{ok, 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). +{ok, ParsedVal, Req2} + = cowboy_req:parse_header(<<"content-type">>, Req, + {<<"text">>, <<"plain">>, []}). ``` -As you can see the call to `chunk/2` does not return a modified -request object. It may return an error, however, so you should -make sure that you match the return value on `ok`. +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. -Response preconfiguration -------------------------- +Meta +---- -Cowboy allows you to set response cookies, headers or body -in advance without having to send the response at the same time. -Then, when you decide to send it, all these informations will be -built into the resulting response. +Cowboy will sometimes associate some meta information with +the request. Built-in meta values are listed in the manual +for their respective modules. -Some of the functions available for this purpose also give you -additional functionality, like `set_resp_cookie/4` which will build -the appropriate `Set-Cookie` header, or `set_resp_body_fun/{2,3}` -which allows you to stream the response body. +This will get a meta value. The returned value will be `undefined` +if it isn't defined. -Note that any value given directly to `reply/{2,3,4}` will -override all preset values. This means for example that you -can set a default body and then override it when you decide -to send a reply. +``` erlang +{MetaVal, Req2} = cowboy_req:meta(websocket_version, Req). +``` + +You can change the default value if needed. + +``` erlang +{MetaVal, Req2} = cowboy_req:meta(websocket_version, Req, 13). +``` -Closing the connection ----------------------- +You can also define your own meta values. The name must be +an `atom()`. -HTTP/1.1 keep-alive allows clients to send more than one request -on the same connection. This can be useful for speeding up the -loading of webpages, but is not required. You can tell Cowboy -explicitly that you want to close the connection by setting the -`Connection` header to `close`. +``` erlang +Req2 = cowboy_req:set_meta(the_answer, 42, Req). +``` + +Peer +---- + +You can obtain the peer address and port number. This is +not necessarily the actual IP and port of the client, but +rather the one of the machine that connected to the server. ``` erlang -{ok, Req2} = cowboy_req:reply(200, - [{<<"connection">>, <<"close">>}], - Req). +{{IP, Port}, Req2} = cowboy_req:peer(Req). ``` Reducing the memory footprint @@ -213,3 +307,5 @@ free memory. ``` erlang Req2 = cowboy_req:compact(Req). ``` + +You will still be able to send a reply if needed. diff --git a/guide/req_body.md b/guide/req_body.md new file mode 100644 index 0000000..d573505 --- /dev/null +++ b/guide/req_body.md @@ -0,0 +1,169 @@ +Reading the request body +======================== + +The Req object also allows you to read the request body. + +Because the request body can be of any size, all body +reading operations will only work once, as Cowboy will +not cache the result of these operations. + +Cowboy will not attempt to read the body until you do. +If handler execution ends without reading it, Cowboy +will simply skip it. + +Check for request body +---------------------- + +You can check whether a body was sent with the request. + +``` erlang +cowboy_req:has_body(Req). +``` + +It will return `true` if there is a request body, and +`false` otherwise. + +Note that it is generally safe to assume that a body is +sent for `POST`, `PUT` and `PATCH` requests, without +having to explicitly check for it. + +Request body length +------------------- + +You can obtain the body length if it was sent with the +request. + +``` erlang +{Length, Req2} = cowboy_req:body_length(Req). +``` + +The value returned will be `undefined` if the length +couldn't be figured out from the request headers. If +there's a body but no length is given, this means that +the chunked transfer-encoding was used. You can read +chunked bodies by using the stream functions. + +Reading the body +---------------- + +If a content-length header was sent with the request, +you can read the whole body directly. + +``` erlang +{ok, Body, Req2} = cowboy_req:body(Req). +``` + +If no content-length header is available, Cowboy will +return the `{error, chunked}` tuple. You will need to +stream the request body instead. + +By default, Cowboy will reject all body sizes above 8MB, +to prevent an attacker from needlessly filling up memory. +You can override this limit however. + +``` erlang +{ok, Body, Req2} = cowboy_req:body(100000000, Req). +``` + +You can also disable it. + +``` erlang +{ok, Body, Req2} = cowboy_req:body(infinity, Req). +``` + +It is recommended that you do not disable it for public +facing websites. + +Reading a body sent from an HTML form +------------------------------------- + +You can directly obtain a list of key/value pairs if the +body was sent using the application/x-www-form-urlencoded +content-type. + +``` erlang +{ok, KeyValues, Req2} = cowboy_req:body_qs(Req). +``` + +You can then retrieve an individual value from that list. + +``` erlang +{_, Lang} = lists:keyfind(lang, 1, KeyValues). +``` + +You should not attempt to match on the list as the order +of the values is undefined. + +By default Cowboy will reject bodies with a size above +16KB when using this function. You can override this limit. + +``` erlang +{ok, KeyValues, Req2} = cowboy_req:body_qs(500000, Req). +``` + +You can also disable it by passing the atom `infinity`, +although it is not recommended. + +Streaming the body +------------------ + +You can stream the request body by chunks. + +``` erlang +{ok, Chunk, Req2} = cowboy_req:stream_body(Req). +``` + +By default, Cowboy will attempt to read chunks of up to +1MB in size. The chunks returned by this function will +often be smaller, however. You can also change this limit. + +``` erlang +{ok, Chunk, Req2} = cowboy_req:stream_body(500000, Req). +``` + +When Cowboy finishes reading the body, any subsequent call +will return `{done, Req2}`. You can thus write a recursive +function to read the whole body and perform an action on +all chunks, for example printing them to the console. + +``` erlang +body_to_console(Req) -> + case cowboy_req:stream_body(Req) of + {ok, Chunk, Req2} -> + io:format("~s", [Chunk]), + body_to_console(Req2); + {done, Req2} -> + Req2 + end. +``` + +Advanced streaming +------------------ + +Cowboy will by default decode the chunked transfer-encoding +if any. It will not decode any content-encoding by default. + +Before starting to stream, you can configure the functions +that will be used for decoding both transfer-encoding and +content-encoding. + +``` erlang +{ok, Req2} = cowboy_req:init_stream(fun transfer_decode/2, + TransferStartState, fun content_decode/1, Req). +``` + +Note that you do not need to call this function generally, +as Cowboy will happily initialize the stream on its own. + +Skipping the body +----------------- + +If you do not need the body, or if you started streaming +the body but do not need the rest of it, you can skip it. + +``` erlang +{ok, Req2} = cowboy_req:skip_body(Req). +``` + +You do not have to call this function though, as Cowboy will +do it automatically when handler execution ends. diff --git a/guide/resp.md b/guide/resp.md new file mode 100644 index 0000000..088b10d --- /dev/null +++ b/guide/resp.md @@ -0,0 +1,203 @@ +Sending a response +================== + +The Req object also allows you to send a response. + +You can only send one response. Any other attempt will +trigger a crash. The response may be sent in one go or +with its body streamed by chunks of arbitrary size. + +You can also set headers or the response body in advance +and Cowboy will use them when you finally do reply. + +Reply +----- + +You can send a reply with no particular headers or body. +Cowboy will make sure to send the mandatory headers with +the response. + +``` erlang +{ok, Req2} = cowboy_req:reply(200, Req). +``` + +You can define headers to be sent with the response. Note +that header names must be lowercase. Again, Cowboy will +make sure to send the mandatory headers with the response. + +``` erlang +{ok, Req2} = cowboy_req:reply(303, [ + {<<"location">>, <<"http://ninenines.eu">>} +], Req). +``` + +You can override headers that Cowboy would send otherwise. +Any header set by the user will be used over the ones set +by Cowboy. For example, you can advertise yourself as a +different server. + +``` erlang +{ok, Req2} = cowboy_req:reply(200, [ + {<<"server">>, <<"yaws">>} +], Req). +``` + +We also saw earlier how to force close the connection by +overriding the connection header. + +Finally, you can also send a body with the response. Cowboy +will automatically set the content-length header if you do. +We recommend that you set the content-type header so the +client may know how to read the body. + +``` erlang +{ok, Req2} = cowboy_req:reply(200, [ + {<<"content-type">>, <<"text/plain">> +], "Hello world!", Req). +``` + +Here is the same example but sending HTML this time. + +``` erlang +{ok, Req2} = cowboy_req:reply(200, [ + {<<"content-type">>, <<"text/html">>} +], "<html><head>Hello world!</head><body><p>Hats off!</p></body></html>", Req). +``` + +Note that the reply is sent immediately. + +Chunked reply +------------- + +You can also stream the response body. First, you need to +initiate the reply by sending the response status code. +Then you can send the body in chunks of arbitrary size. + +``` erlang +{ok, Req2} = cowboy_req:chunked_reply(200, Req), +ok = cowboy_req:chunk("Hello...", Req2), +ok = cowboy_req:chunk("chunked...", Req2), +ok = cowboy_req:chunk("world!!", Req2). +``` + +You should make sure to match on `ok` as an error may be +returned. + +While it is possible to send a chunked response without +a content-type header, it is still recommended. You can +set this header or any other just like for normal replies. + +``` erlang +{ok, Req2} = cowboy_req:chunked_reply(200, [ + {<<"content-type">>, <<"text/html">>} +], Req), +ok = cowboy_req:chunk("<html><head>Hello world!</head>", Req2), +ok = cowboy_req:chunk("<body><p>Hats off!</p></body></html>", Req2). +``` + +Note that the reply and each chunk following it are sent +immediately. + +Preset response headers +----------------------- + +You can define response headers in advance. They will be +merged into the headers given in the reply call. Headers +in the reply call override preset response headers which +override the default Cowboy headers. + +``` erlang +Req2 = cowboy_req:set_resp_header(<<"allow">>, "GET", Req). +``` + +You can check if a response header has already been set. +This will only check the response headers that you set, +and not the ones Cowboy will add when actually sending +the reply. + +``` erlang +cowboy_req:has_resp_header(<<"allow">>, Req). +``` + +It will return `true` if the header is defined, and `false` +otherwise. + +Finally, you can also delete a preset response header if +needed. If you do, it will not be sent. + +``` erlang +Req2 = cowboy_req:delete_resp_header(<<"allow">>, Req). +``` + +Preset response body +-------------------- + +You can set the response body in advance. Note that this +body will be ignored if you then choose to send a chunked +reply, or if you send a reply with an explicit body. + +``` erlang +Req2 = cowboy_req:set_resp_body("Hello world!", Req). +``` + +You can also set a fun that will be called when it is time +to send the body. There are three different ways of doing +that. + +If you know the length of the body that needs to be sent, +you should specify it, as it will help clients determine +the remaining download time and allow them to inform the +user. + +``` erlang +F = fun (Socket, Transport) -> + Transport:send(Socket, "Hello world!") +end, +Req2 = cowboy_req:set_resp_body_fun(12, F, Req). +``` + +If you do not know the length of the body, you should use +a chunked response body fun instead. + +``` erlang +F = fun (SendChunk) -> + Body = lists:duplicate(random:uniform(1024, $a)), + SendChunk(Body) +end, +Req2 = cowboy_req:set_resp_body_fun(chunked, F, Req). +``` + +Finally, you can also send data on the socket directly, +without knowing the length in advance. Cowboy may be +forced to close the connection at the end of the response +though depending on the protocol capabilities. + +``` erlang +F = fun (Socket, Transport) -> + Body = lists:duplicate(random:uniform(1024, $a)), + Transport:send(Socket, Body) +end, +Req2 = cowboy_req:set_resp_body_fun(F, Req). +``` + +Sending files +------------- + +You can send files directly from disk without having to +read them. Cowboy will use the `sendfile` syscall when +possible, which means that the file is sent to the socket +directly from the kernel, which is a lot more performant +than doing it from userland. + +Again, it is recommended to set the size of the file if it +can be known in advance. + +``` erlang +F = fun (Socket, Transport) -> + Transport:sendfile(Socket, "priv/styles.css") +end, +Req2 = cowboy_req:set_resp_body_fun(FileSize, F, Req). +``` + +Please see the Ranch guide for more information about +sending files. diff --git a/guide/toc.md b/guide/toc.md index 3397f46..315dfac 100644 --- a/guide/toc.md +++ b/guide/toc.md @@ -20,8 +20,9 @@ HTTP * [Routing](routing.md) * [Handling plain HTTP requests](http_handlers.md) * [The Req object](req.md) - * Reading the request body - * Sending a response + * [Reading the request body](req_body.md) + * [Sending a response](resp.md) + * [Using cookies](cookies.md) Static files ------------ |