diff options
Diffstat (limited to 'guide')
-rw-r--r-- | guide/handlers.md | 55 | ||||
-rw-r--r-- | guide/hooks.md | 77 | ||||
-rw-r--r-- | guide/http_handlers.md | 27 | ||||
-rw-r--r-- | guide/internals.md | 79 | ||||
-rw-r--r-- | guide/introduction.md | 12 | ||||
-rw-r--r-- | guide/loop_handlers.md | 62 | ||||
-rw-r--r-- | guide/middlewares.md | 72 | ||||
-rw-r--r-- | guide/req.md | 205 | ||||
-rw-r--r-- | guide/rest_handlers.md | 28 | ||||
-rw-r--r-- | guide/routing.md | 244 | ||||
-rw-r--r-- | guide/static_handlers.md | 30 | ||||
-rw-r--r-- | guide/toc.md | 41 | ||||
-rw-r--r-- | guide/ws_handlers.md | 75 |
13 files changed, 986 insertions, 21 deletions
diff --git a/guide/handlers.md b/guide/handlers.md new file mode 100644 index 0000000..e2c1264 --- /dev/null +++ b/guide/handlers.md @@ -0,0 +1,55 @@ +Handlers +======== + +Purpose +------- + +Handlers are Erlang modules that represent a resource. + +Handlers must process the request and send a reply. The nature of the +reply will vary between handlers. + +Different kinds of handlers can be combined in a single module. This +allows a module to handle both websocket and long-polling code in a +single place, for example. + +Protocol upgrades +----------------- + +Cowboy features many different handlers: HTTP handlers, loop handlers, +websocket handlers, REST handlers and static handlers. All of them +have a common entry point: the `init/3` function. + +By default, Cowboy considers your handler to be an HTTP handler. + +To switch to a different protocol, like, for example, Websocket, +you must perform a protocol upgrade. This is done by returning +a protocol upgrade tuple at the end of `init/3`. + +The following snippet upgrades the handler to `my_protocol`. + +``` erlang +init(_Any, _Req, _Opts) -> + {upgrade, protocol, my_protocol}. +``` + +Cowboy comes with two protocol upgrades: `cowboy_rest` and +`cowboy_websocket`. Use these values in place of `my_protocol` +to use them. + +Custom protocol upgrades +------------------------ + +The `my_protocol` module above will be used for further processing +of the request. It requires only one callback, `upgrade/4`. + +It receives the request object, the middleware environment, and +the handler this request has been routed to along with its options. + +``` erlang +upgrade(Req, Env, Handler, HandlerOpts) -> + %% ... +``` + +This callback is expected to behave like any middleware. Please +see the corresponding chapter for more information. diff --git a/guide/hooks.md b/guide/hooks.md new file mode 100644 index 0000000..d4b520a --- /dev/null +++ b/guide/hooks.md @@ -0,0 +1,77 @@ +Hooks +===== + +On request +---------- + +The `onrequest` hook is called as soon as Cowboy finishes fetching +the request headers. It occurs before any other processing, including +routing. It can be used to perform any modification needed on the +request object before continuing with the processing. If a reply is +sent inside this hook, then Cowboy will move on to the next request, +skipping any subsequent handling. + +This hook is a function that takes a request object as argument, +and returns a request object. This function MUST NOT crash. Cowboy +will not send any reply if a crash occurs in this function. + +You can specify the `onrequest` hook when creating the listener, +inside the request options. + +``` erlang +cowboy:start_http(my_http_listener, 100, + [{port, 8080}], + [ + {env, [{dispatch, Dispatch}]}, + {onrequest, fun ?MODULE:debug_hook/1} + ] +). +``` + +The following hook function prints the request object everytime a +request is received. This can be useful for debugging, for example. + +``` erlang +debug_hook(Req) -> + erlang:display(Req), + Req. +``` + +Make sure to always return the last request object obtained. + +On response +----------- + +The `onresponse` hook is called right before sending the response +to the socket. It can be used for the purposes of logging responses, +or for modifying the response headers or body. The best example is +providing custom error pages. + +Note that like the `onrequest` hook, this function MUST NOT crash. +Cowboy may or may not send a reply if this function crashes. + +You can specify the `onresponse` hook when creating the listener also. + +``` erlang +cowboy:start_http(my_http_listener, 100, + [{port, 8080}], + [ + {env, [{dispatch, Dispatch}]}, + {onresponse, fun ?MODULE:custom_404_hook/4} + ] +). +``` + +The following hook function will provide a custom body for 404 errors +when it has not been provided before, and will let Cowboy proceed with +the default response otherwise. + +``` erlang +custom_404_hook(404, Headers, <<>>, Req) -> + {ok, Req2} = cowboy_req:reply(404, Headers, <<"404 Not Found.">>, Req), + Req2; +custom_404_hook(_, _, _, Req) -> + Req. +``` + +Again, make sure to always return the last request object obtained. diff --git a/guide/http_handlers.md b/guide/http_handlers.md new file mode 100644 index 0000000..aba0e06 --- /dev/null +++ b/guide/http_handlers.md @@ -0,0 +1,27 @@ +HTTP handlers +============= + +Purpose +------- + +HTTP handlers are the simplest Cowboy module to handle a request. + +Usage +----- + +You need to implement three callbacks for HTTP handlers. The first, +`init/3`, is common to all handlers. In the context of HTTP handlers +this should be used for any initialization needs. + +The second callback, `handle/2`, is where most of your code should +be. As the name explains, this is where you handle the request. + +The last callback, `terminate/3`, will be empty most of the time. +It's used for any needed cleanup. If you used the process dictionary, +timers, monitors then you most likely want to stop them in this +callback, as Cowboy might end up reusing this process for subsequent +requests. Please see the Internals chapter for more information. + +Of course the general advice is to not use the process dictionary, +and that any operation requiring reception of messages should be +done in a loop handler, documented in its own chapter. diff --git a/guide/internals.md b/guide/internals.md new file mode 100644 index 0000000..0f8adc2 --- /dev/null +++ b/guide/internals.md @@ -0,0 +1,79 @@ +Internals +========= + +Architecture +------------ + +Cowboy is a lightweight HTTP server. + +It is built on top of Ranch. Please see the Ranch guide for more +informations. + +It uses only one process per connection. The process where your +code runs is the process controlling the socket. Using one process +instead of two allows for lower memory usage. + +It uses binaries. Binaries are more efficient than lists for +representing strings because they take less memory space. Processing +performance can vary depending on the operation. Binaries are known +for generally getting a great boost if the code is compiled natively. +Please see the HiPE documentation for more details. + +Because querying for the current date and time can be expensive, +Cowboy generates one `Date` header value every second, shares it +to all other processes, which then simply copy it in the response. +This allows compliance with HTTP/1.1 with no actual performance loss. + +One process for many requests +----------------------------- + +As previously mentioned, Cowboy only use one process per connection. +Because there can be more than one request per connection with the +keepalive feature of HTTP/1.1, that means the same process will be +used to handle many requests. + +Because of this, you are expected to make sure your process cleans +up before terminating the handling of the current request. This may +include cleaning up the process dictionary, timers, monitoring and +more. + +Lowercase header names +---------------------- + +For consistency reasons it has been chosen to convert all header names +to lowercase binary strings. This prevents the programmer from making +case mistakes, and is possible because header names are case insensitive. + +This works fine for the large majority of clients. However, some badly +implemented clients, especially ones found in corporate code or closed +source products, may not handle header names in a case insensitive manner. +This means that when Cowboy returns lowercase header names, these clients +will not find the headers they are looking for. + +A simple way to solve this is to create an `onresponse` hook that will +format the header names with the expected case. + +``` erlang +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. +``` + +Improving performance +--------------------- + +By default the maximum number of active connections is set to a +generally accepted big enough number. This is meant to prevent having +too many processes performing potentially heavy work and slowing +everything else down, or taking up all the memory. + +Disabling this feature, by setting the `{max_connections, infinity}` +protocol option, would give you greater performance when you are +only processing short-lived requests. + +Another option is to define platform-specific socket options that +are known to improve their efficiency. + +Please see the Ranch guide for more information. diff --git a/guide/introduction.md b/guide/introduction.md index 871e243..c7f48e2 100644 --- a/guide/introduction.md +++ b/guide/introduction.md @@ -77,8 +77,8 @@ Dispatch = [ ], %% Name, NbAcceptors, TransOpts, ProtoOpts cowboy:start_http(my_http_listener, 100, - [{port, 8080}], - [{dispatch, Dispatch}] + [{port, 8080}], + [{env, [{dispatch, Dispatch}]}] ). ``` @@ -87,7 +87,7 @@ handlers, Websocket handlers, REST handlers and static handlers. Their usage is documented in the respective sections of the guide. Most applications use the plain HTTP handler, which has three callback -functions: init/3, handle/2 and terminate/2. Following is an example of +functions: init/3, handle/2 and terminate/3. Following is an example of a simple handler module. ``` erlang @@ -96,7 +96,7 @@ a simple handler module. -export([init/3]). -export([handle/2]). --export([terminate/2]). +-export([terminate/3]). init({tcp, http}, Req, Opts) -> {ok, Req, undefined_state}. @@ -105,10 +105,10 @@ handle(Req, State) -> {ok, Req2} = cowboy_req:reply(200, [], <<"Hello World!">>, Req), {ok, Req2, State}. -terminate(Req, State) -> +terminate(Reason, Req, State) -> ok. ``` The `Req` variable above is the Req object, which allows the developer -to obtain informations about the request and to perform a reply. Its usage +to obtain information about the request and to perform a reply. Its usage is explained in its respective section of the guide. diff --git a/guide/loop_handlers.md b/guide/loop_handlers.md new file mode 100644 index 0000000..c3d1891 --- /dev/null +++ b/guide/loop_handlers.md @@ -0,0 +1,62 @@ +Loop handlers +============= + +Purpose +------- + +Loop handlers are a special kind of HTTP handlers used when the +response can not be sent right away. The handler enters instead +a receive loop waiting for the right message before it can send +a response. + +They are most useful when performing long-polling operations or +when using server-sent events. + +While the same can be accomplished using plain HTTP handlers, +it is recommended to use loop handlers because they are well-tested +and allow using built-in features like hibernation and timeouts. + +Usage +----- + +Loop handlers are used for requests where a response might not +be immediately available, but where you would like to keep the +connection open for a while in case the response arrives. The +most known example of such practice is known as long-polling. + +Loop handlers can also be used for requests where a response is +partially available and you need to stream the response body +while the connection is open. The most known example of such +practice is known as server-sent events. + +Loop handlers essentially wait for one or more Erlang messages +and feed these messages to the `info/3` callback. It also features +the `init/3` and `terminate/3` callbacks which work the same as +for plain HTTP handlers. + +The following handler waits for a message `{reply, Body}` before +sending a response. If this message doesn't arrive within 60 +seconds, it gives up and a `204 No Content` will be replied. +It also hibernates the process to save memory while waiting for +this message. + +``` erlang +-module(my_loop_handler). +-behaviour(cowboy_loop_handler). + +-export([init/3]). +-export([info/3]). +-export([terminate/3]). + +init({tcp, http}, Req, Opts) -> + {loop, Req, undefined_state, 60000, hibernate}. + +info({reply, Body}, Req, State) -> + {ok, Req2} = cowboy_req:reply(200, [], Body, Req), + {ok, Req2, State}; +info(Message, Req, State) -> + {loop, Req, State, hibernate}. + +terminate(Reason, Req, State) -> + ok. +``` diff --git a/guide/middlewares.md b/guide/middlewares.md new file mode 100644 index 0000000..0ab6dc2 --- /dev/null +++ b/guide/middlewares.md @@ -0,0 +1,72 @@ +Middlewares +=========== + +Purpose +------- + +Cowboy delegates the request processing to middleware components. +By default, two middlewares are defined, for the routing and handling +of the request, as is detailed in most of this guide. + +Middlewares give you complete control over how requests are to be +processed. You can add your own middlewares to the mix or completely +change the chain of middlewares as needed. + +Cowboy will execute all middlewares in the given order, unless one +of them decides to stop processing. + +Usage +----- + +Middlewares only need to implement a single callback: `execute/2`. +It is defined in the `cowboy_middleware` behavior. + +This callback has two arguments. The first is the `Req` object. +The second is the environment. + +Middlewares can return one of four different values: + * `{ok, Req, Env}` to continue the request processing + * `{suspend, Module, Function, Args}` to hibernate + * `{halt, Req}` to stop processing and move on to the next request + * `{error, StatusCode, Req}` to reply an error and close the socket + +Of note is that when hibernating, processing will resume on the given +MFA, discarding all previous stacktrace. Make sure you keep the `Req` +and `Env` in the arguments of this MFA for later use. + +If an error happens during middleware processing, Cowboy will not try +to send an error back to the socket, the process will just crash. It +is up to the middleware to make sure that a reply is sent if something +goes wrong. + +Configuration +------------- + +The middleware environment is defined as the `env` protocol option. +In the previous chapters we saw it briefly when we needed to pass +the routing information. It is a list of tuples with the first +element being an atom and the second any Erlang term. + +Two values in the environment are reserved: + * `listener` contains the pid of the listener process + * `result` contains the result of the processing + +The `listener` value is always defined. The `result` value can be +set by any middleware. If set to anything other than `ok`, Cowboy +will not process any subsequent requests on this connection. + +The middlewares that come with Cowboy may define or require other +environment values to perform. + +Routing middleware +------------------ + +The routing middleware requires the `dispatch` value. If routing +succeeds, it will put the handler name and options in the `handler` +and `handler_opts` values of the environment, respectively. + +Handler middleware +------------------ + +The handler middleware requires the `handler` and `handler_opts` +values. It puts the result of the request handling into `result`. diff --git a/guide/req.md b/guide/req.md new file mode 100644 index 0000000..e13d3a5 --- /dev/null +++ b/guide/req.md @@ -0,0 +1,205 @@ +Request object +============== + +Purpose +------- + +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. + +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. + +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. + +Request +------- + +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 (`{1,0}` or `{1,1}`) + * `peer/1`: the peer address and port number + * `peer_addr/1`: the peer address guessed using the request headers + * `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 + * `fragment/1`: the fragment part of the URL (e.g. `#nav-links`) + * `host_url/1`: the requested URL without the path, qs and fragment + * `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 +------------ + +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 +of appropriate size, then you can read it directly with +either `body/1` or `body_qs/1`. Otherwise, you will want +to stream it with `stream_body/1` and `skip_body/1`, with +the streaming process optionally initialized using `init_stream/4`. + +Multipart request body +---------------------- + +Cowboy provides facilities for dealing with multipart bodies. +They are typically used for uploading files. You can use two +functions to process these bodies, `multipart_data/1` and +`multipart_skip/1`. + +Response +-------- + +You can send a response by calling the `reply/{2,3,4}` function. +It takes the status code for the response (usually `200`), +an optional list of headers, an optional body and the request +object. + +The following snippet sends a simple response with no headers +specified but with a body. + +``` erlang +{ok, Req2} = cowboy_req:reply(200, [], "Hello world!", 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. + +If you want to send HTML you'll need to specify the `Content-Type` +header so the client can properly interpret it. + +``` erlang +{ok, Req2} = cowboy_req:reply(200, + [{<<"content-type">>, <<"text/html">>}], + "<html><head>Hello world!</head><body><p>Hats off!</p></body></html>", + Req). +``` + +You only need to make sure to follow conventions and to use a +lowercase header name. + +Chunked response +---------------- + +You can also send chunked responses using `chunked_reply/{2,3}`. +Chunked responses allow you to send the body in chunks of various +sizes. It is the recommended way of performing streaming if the +client supports it. + +You must first initiate the response by calling the aforementioned +function, then you can call `chunk/2` as many times as needed. +The following snippet sends a body in three chunks. + +``` erlang +{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). +``` + +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`. + +Response preconfiguration +------------------------- + +Cowboy allows you to set response cookies, headers or body +in advance without having to send the response at the same time. +Then, when you decide to send it, all these informations will be +built into the resulting response. + +Some of the functions available for this purpose also give you +additional functionality, like `set_resp_cookie/4` which will build +the appropriate `Set-Cookie` header, or `set_resp_body_fun/{2,3}` +which allows you to stream the response body. + +Note that any value given directly to `reply/{2,3,4}` will +override all preset values. This means for example that you +can set a default body and then override it when you decide +to send a reply. + +Closing the connection +---------------------- + +HTTP/1.1 keep-alive allows clients to send more than one request +on the same connection. This can be useful for speeding up the +loading of webpages, but is not required. You can tell Cowboy +explicitly that you want to close the connection by setting the +`Connection` header to `close`. + +``` erlang +{ok, Req2} = cowboy_req:reply(200, + [{<<"connection">>, <<"close">>}], + Req). +``` + +Reducing the memory footprint +----------------------------- + +When you are done reading information from the request object +and know you are not going to access it anymore, for example +when using long-polling or Websocket, you can use the `compact/1` +function to remove most of the data from the request object and +free memory. + +``` erlang +Req2 = cowboy_req:compact(Req). +``` diff --git a/guide/rest_handlers.md b/guide/rest_handlers.md new file mode 100644 index 0000000..df5f841 --- /dev/null +++ b/guide/rest_handlers.md @@ -0,0 +1,28 @@ +REST handlers +============= + +Purpose +------- + +REST is a set of constraints that, when applied to HTTP, dictates how +resources must behave. It is the recommended way to handle requests +with Cowboy. + +REST is implemented in Cowboy as a protocol upgrade. Once upgraded, +the request is handled as a state machine with many optional callbacks +describing the resource and modifying the machine's behavior. + +Flow diagram +------------ + +@todo Add the beautiful flow diagram here. + +Callbacks +--------- + +@todo Describe the callbacks. + +Usage +----- + +@todo Explain how to use them. diff --git a/guide/routing.md b/guide/routing.md new file mode 100644 index 0000000..9d5c5af --- /dev/null +++ b/guide/routing.md @@ -0,0 +1,244 @@ +Routing +======= + +Purpose +------- + +Cowboy does nothing by default. + +To make Cowboy useful, you need to map URLs to Erlang modules that will +handle the requests. This is called routing. + +When Cowboy receives a request, it tries to match the requested host and +path to the resources given in the dispatch rules. If it matches, then +the associated Erlang code will be executed. + +Routing rules are given per host. Cowboy will first match on the host, +and then try to find a matching path. + +Routes need to be compiled before they can be used by Cowboy. + +Structure +--------- + +The general structure for the routes is defined as follow. + +``` erlang +Routes = [Host1, Host2, ... HostN]. +``` + +Each host contains matching rules for the host along with optional +constraints, and a list of routes for the path component. + +``` erlang +Host1 = {HostMatch, PathsList}. +Host2 = {HostMatch, Constraints, PathsList}. +``` + +The list of routes for the path component is defined similar to the +list of hosts. + +``` erlang +PathsList = [Path1, Path2, ... PathN]. +``` + +Finally, each path contains matching rules for the path along with +optional constraints, and gives us the handler module to be used +along with options that will be given to it on initialization. + +``` erlang +Path1 = {PathMatch, Handler, Opts}. +Path2 = {PathMatch, Constraints, Handler, Opts}. +``` + +Continue reading to learn more about the match syntax and the optional +constraints. + +Match syntax +------------ + +The match syntax is used to associate host names and paths with their +respective handlers. + +The match syntax is the same for host and path with a few subtleties. +Indeed, the segments separator is different, and the host is matched +starting from the last segment going to the first. All examples will +feature both host and path match rules and explain the differences +when encountered. + +Excluding special values that we will explain at the end of this section, +the simplest match value is a host or a path. It can be given as either +a `string()` or a `binary()`. + +``` erlang +PathMatch1 = "/". +PathMatch2 = "/path/to/resource". + +HostMatch1 = "cowboy.example.org". +``` + +As you can see, all paths defined this way must start with a slash +character. Note that these two paths are identical as far as routing +is concerned. + +``` erlang +PathMatch2 = "/path/to/resource". +PathMatch3 = "/path/to/resource/". +``` + +Hosts with and without a trailing dot are equivalent for routing. +Similarly, hosts with and without a leading dot are also equivalent. + +``` erlang +HostMatch1 = "cowboy.example.org". +HostMatch2 = "cowboy.example.org.". +HostMatch3 = ".cowboy.example.org". +``` + +It is possible to extract segments of the host and path and to store +the values in the `Req` object for later use. We call these kind of +values bindings. + +The syntax for bindings is very simple. A segment that begins with +the `:` character means that what follows until the end of the segment +is the name of the binding in which the segment value will be stored. + +``` erlang +PathMatch = "/hats/:name/prices". +HostMatch = ":subdomain.example.org". +``` + +If these two end up matching when routing, you will end up with two +bindings defined, `subdomain` and `name`, each containing the +segment value where they were defined. For example, the URL +`http://test.example.org/hats/wild_cowboy_legendary/prices` will +result in having the value `test` bound to the name `subdomain` +and the value `wild_cowboy_legendary` bound to the name `name`. +They can later be retrieved using `cowboy_req:binding/{2,3}`. The +binding name must be given as an atom. + +There is a special binding name you can use to mimic the underscore +variable in Erlang. Any match against the `_` binding will succeed +but the data will be discarded. This is especially useful for +matching against many domain names in one go. + +``` erlang +HostMatch = "ninenines.:_". +``` + +Similarly, it is possible to have optional segments. Anything +between brackets is optional. + +``` erlang +PathMatch = "/hats/[page/:number]". +HostMatch = "[www.]ninenines.eu". +``` + +You can also have imbricated optional segments. + +``` erlang +PathMatch = "/hats/[page/[:number]]". +``` + +You can retrieve the rest of the host or path using `[...]`. +In the case of hosts it will match anything before, in the case +of paths anything after the previously matched segments. It is +a special case of optional segments, in that it can have +zero, one or many segments. You can then find the segments using +`cowboy_req:host_info/1` and `cowboy_req:path_info/1` respectively. +They will be represented as a list of segments. + +``` erlang +PathMatch = "/hats/[...]". +HostMatch = "[...]ninenines.eu". +``` + +If a binding appears twice in the routing rules, then the match +will succeed only if they share the same value. This copies the +Erlang pattern matching behavior. + +``` erlang +PathMatch = "/hats/:name/:name". +``` + +This is also true when an optional segment is present. In this +case the two values must be identical only if the segment is +available. + +``` erlang +PathMatch = "/hats/:name/[:name]". +``` + +If a binding is defined in both the host and path, then they must +also share the same value. + +``` erlang +PathMatch = "/:user/[...]". +HostMatch = ":user.github.com". +``` + +Finally, there are two special match values that can be used. The +first is the atom `'_'` which will match any host or path. + +``` erlang +PathMatch = '_'. +HostMatch = '_'. +``` + +The second is the special host match `"*"` which will match the +wildcard path, generally used alongside the `OPTIONS` method. + +``` erlang +HostMatch = "*". +``` + +Constraints +----------- + +After the matching has completed, the resulting bindings can be tested +against a set of constraints. Constraints are only tested when the +binding is defined. They run in the order you defined them. The match +will succeed only if they all succeed. + +They are always given as a two or three elements tuple, where the first +element is the name of the binding, the second element is the constraint's +name, and the optional third element is the constraint's arguments. + +The following constraints are currently defined: + + * {Name, int} + * {Name, function, fun ((Value) -> true | {true, NewValue} | false)} + +The `int` constraint will check if the binding is a binary string +representing an integer, and if it is, will convert the value to integer. + +The `function` constraint will pass the binding value to a user specified +function that receives the binary value as its only argument and must +return whether it fulfills the constraint, optionally modifying the value. +The value thus returned can be of any type. + +Note that constraint functions SHOULD be pure and MUST NOT crash. + +Compilation +----------- + +The structure defined in this chapter needs to be compiled before it is +passed to Cowboy. This allows Cowboy to efficiently lookup the correct +handler to run instead of having to parse the routes repeatedly. + +This can be done with a simple call to `cowboy_router:compile/1`. + +``` erlang +{ok, Routes} = cowboy_router:compile([ + %% {HostMatch, list({PathMatch, Handler, Opts})} + {'_', [{'_', my_handler, []}]} +]), +%% Name, NbAcceptors, TransOpts, ProtoOpts +cowboy:start_http(my_http_listener, 100, + [{port, 8080}], + [{env, [{routes, Routes}]}] +). +``` + +Note that this function will return `{error, badarg}` if the structure +given is incorrect. diff --git a/guide/static_handlers.md b/guide/static_handlers.md new file mode 100644 index 0000000..f87515a --- /dev/null +++ b/guide/static_handlers.md @@ -0,0 +1,30 @@ +Static handlers +=============== + +Purpose +------- + +Static handlers are a built-in REST handler for serving files. They +are available as a convenience and provide fast file serving with +proper cache handling. + +Usage +----- + +Static handlers are pre-written REST handlers. They only need +to be specified in the routing information with the proper options. + +The following example routing serves all files found in the +`priv_dir/static/` directory of the application. It uses a +mimetypes library to figure out the files' content types. + +``` erlang +Dispatch = [ + {'_', [ + {['...'], cowboy_static, [ + {directory, {priv_dir, static, []}}, + {mimetypes, {fun mimetypes:path_to_mimes/2, default}} + ]} + ]} +]. +``` diff --git a/guide/toc.md b/guide/toc.md index b57b92e..2f8fa36 100644 --- a/guide/toc.md +++ b/guide/toc.md @@ -6,43 +6,54 @@ Cowboy User Guide * Prerequisites * Conventions * Getting started - * Routing + * [Routing](routing.md) * Purpose * Dispatch list * Match rules * Bindings * Constraints - * Handlers + * [Handlers](handlers.md) * Purpose * Protocol upgrades - * HTTP handlers + * Custom protocol upgrades + * [HTTP handlers](http_handlers.md) * Purpose - * Callbacks * Usage - * Loop handlers + * [Loop handlers](loop_handlers.md) * Purpose - * Callbacks * Usage - * Websocket handlers + * [Websocket handlers](ws_handlers.md) * Purpose - * Callbacks * Usage - * REST handlers + * [REST handlers](rest_handlers.md) * Purpose * Flow diagram * Callbacks * Usage - * Static handlers + * [Static handlers](static_handlers.md) * Purpose * Usage - * Request object + * [Request object](req.md) * Purpose * Request * Request body - * Reply - * Hooks + * Multipart request body + * Response + * Chunked response + * Response preconfiguration + * Closing the connection + * Reducing the memory footprint + * [Hooks](hooks.md) * On request * On response - * Internals + * [Middlewares](middlewares.md) + * Purpose + * Usage + * Configuration + * Routing middleware + * Handler middleware + * [Internals](internals.md) * Architecture - * Efficiency considerations + * One process for many requests + * Lowercase header names + * Improving performance diff --git a/guide/ws_handlers.md b/guide/ws_handlers.md new file mode 100644 index 0000000..c1e551e --- /dev/null +++ b/guide/ws_handlers.md @@ -0,0 +1,75 @@ +Websocket handlers +================== + +Purpose +------- + +Websocket is an extension to HTTP to emulate plain TCP connections +between the user's browser and the server. Requests that are upgraded +are then handled by websocket handlers. + +Both sides of the socket can send data at any time asynchronously. + +Websocket is an IETF standard. Cowboy supports the standard and all +the drafts that were previously implemented by browsers. Websocket +is implemented by most browsers today, although for backward +compatibility reasons a solution like [Bullet](https://github.com/extend/bullet) +might be preferred. + +Usage +----- + +Websocket handlers are a bridge between the client and your system. +They can receive data from the client, through `websocket_handle/3`, +or from the system, through `websocket_info/3`. It is up to the +handler to decide to process this data, and optionally send a reply +to the client. + +The first thing to do to be able to handle websockets is to tell +Cowboy that it should upgrade the connection to use the Websocket +protocol, as follow. + +``` erlang +init({tcp, http}, Req, Opts) -> + {upgrade, protocol, cowboy_websocket}. +``` + +Cowboy will then switch the protocol and call `websocket_init`, +followed by zero or more calls to `websocket_data` and +`websocket_info`. Then, when the connection is shutting down, +`websocket_terminate` will be called. + +The following handler sends a message every second. It also echoes +back what it receives. + +``` erlang +-module(my_ws_handler). +-behaviour(cowboy_websocket_handler). + +-export([init/3]). +-export([websocket_init/3]). +-export([websocket_handle/3]). +-export([websocket_info/3]). +-export([websocket_terminate/3]). + +init({tcp, http}, Req, Opts) -> + {upgrade, protocol, cowboy_websocket}. + +websocket_init(TransportName, Req, _Opts) -> + erlang:start_timer(1000, self(), <<"Hello!">>), + {ok, Req, undefined_state}. + +websocket_handle({text, Msg}, Req, State) -> + {reply, {text, << "That's what she said! ", Msg/binary >>}, Req, State}; +websocket_handle(_Data, Req, State) -> + {ok, Req, State}. + +websocket_info({timeout, _Ref, Msg}, Req, State) -> + erlang:start_timer(1000, self(), <<"How' you doin'?">>), + {reply, {text, Msg}, Req, State}; +websocket_info(_Info, Req, State) -> + {ok, Req, State}. + +websocket_terminate(_Reason, _Req, _State) -> + ok. +``` |