From 078d686a0ac0aed212db97d73bd1e4a9387a4956 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= Date: Sun, 6 Jul 2014 13:10:35 +0200 Subject: Provide installable man pages make docs: generate Markdown and man pages in doc/ make install-docs: install man pages to be usable directly Docs are generated from the ezdoc files in doc/src/. --- doc/src/guide/architecture.ezdoc | 47 + doc/src/guide/broken_clients.ezdoc | 61 + doc/src/guide/cookies.ezdoc | 137 ++ doc/src/guide/erlang_beginners.ezdoc | 40 + doc/src/guide/erlang_web.ezdoc | 175 ++ doc/src/guide/getting_started.ezdoc | 306 +++ doc/src/guide/hooks.ezdoc | 82 + doc/src/guide/http_handlers.ezdoc | 145 ++ doc/src/guide/http_req_life.ezdoc | 147 ++ doc/src/guide/http_req_resp.png | Bin 0 -> 33228 bytes doc/src/guide/http_req_resp.svg | 558 +++++ doc/src/guide/index.ezdoc | 58 + doc/src/guide/introduction.ezdoc | 49 + doc/src/guide/loop_handlers.ezdoc | 141 ++ doc/src/guide/middlewares.ezdoc | 69 + doc/src/guide/modern_web.ezdoc | 215 ++ doc/src/guide/multipart_intro.ezdoc | 50 + doc/src/guide/multipart_req.ezdoc | 115 + doc/src/guide/req.ezdoc | 301 +++ doc/src/guide/req_body.ezdoc | 158 ++ doc/src/guide/resource_design.ezdoc | 220 ++ doc/src/guide/resp.ezdoc | 197 ++ doc/src/guide/rest_cond.png | Bin 0 -> 111628 bytes doc/src/guide/rest_cond.svg | 1656 ++++++++++++++ doc/src/guide/rest_conneg.png | Bin 0 -> 78133 bytes doc/src/guide/rest_conneg.svg | 1135 ++++++++++ doc/src/guide/rest_delete.png | Bin 0 -> 122185 bytes doc/src/guide/rest_delete.svg | 1718 +++++++++++++++ doc/src/guide/rest_flowcharts.ezdoc | 247 +++ doc/src/guide/rest_get_head.png | Bin 0 -> 99942 bytes doc/src/guide/rest_get_head.svg | 1523 +++++++++++++ doc/src/guide/rest_handlers.ezdoc | 135 ++ doc/src/guide/rest_options.png | Bin 0 -> 8539 bytes doc/src/guide/rest_options.svg | 387 ++++ doc/src/guide/rest_principles.ezdoc | 159 ++ doc/src/guide/rest_put_post_patch.png | Bin 0 -> 218656 bytes doc/src/guide/rest_put_post_patch.svg | 2856 +++++++++++++++++++++++++ doc/src/guide/rest_start.png | Bin 0 -> 118210 bytes doc/src/guide/rest_start.svg | 1468 +++++++++++++ doc/src/guide/routing.ezdoc | 249 +++ doc/src/guide/static_handlers.ezdoc | 167 ++ doc/src/guide/upgrade_protocol.ezdoc | 36 + doc/src/guide/ws_handlers.ezdoc | 222 ++ doc/src/guide/ws_protocol.ezdoc | 42 + doc/src/manual/cowboy.ezdoc | 101 + doc/src/manual/cowboy_app.ezdoc | 23 + doc/src/manual/cowboy_handler.ezdoc | 24 + doc/src/manual/cowboy_http_handler.ezdoc | 57 + doc/src/manual/cowboy_loop_handler.ezdoc | 91 + doc/src/manual/cowboy_middleware.ezdoc | 54 + doc/src/manual/cowboy_protocol.ezdoc | 84 + doc/src/manual/cowboy_req.ezdoc | 704 ++++++ doc/src/manual/cowboy_rest.ezdoc | 561 +++++ doc/src/manual/cowboy_router.ezdoc | 70 + doc/src/manual/cowboy_spdy.ezdoc | 43 + doc/src/manual/cowboy_static.ezdoc | 32 + doc/src/manual/cowboy_sub_protocol.ezdoc | 32 + doc/src/manual/cowboy_websocket.ezdoc | 36 + doc/src/manual/cowboy_websocket_handler.ezdoc | 133 ++ doc/src/manual/http_status_codes.ezdoc | 151 ++ doc/src/manual/index.ezdoc | 20 + 61 files changed, 17487 insertions(+) create mode 100644 doc/src/guide/architecture.ezdoc create mode 100644 doc/src/guide/broken_clients.ezdoc create mode 100644 doc/src/guide/cookies.ezdoc create mode 100644 doc/src/guide/erlang_beginners.ezdoc create mode 100644 doc/src/guide/erlang_web.ezdoc create mode 100644 doc/src/guide/getting_started.ezdoc create mode 100644 doc/src/guide/hooks.ezdoc create mode 100644 doc/src/guide/http_handlers.ezdoc create mode 100644 doc/src/guide/http_req_life.ezdoc create mode 100644 doc/src/guide/http_req_resp.png create mode 100644 doc/src/guide/http_req_resp.svg create mode 100644 doc/src/guide/index.ezdoc create mode 100644 doc/src/guide/introduction.ezdoc create mode 100644 doc/src/guide/loop_handlers.ezdoc create mode 100644 doc/src/guide/middlewares.ezdoc create mode 100644 doc/src/guide/modern_web.ezdoc create mode 100644 doc/src/guide/multipart_intro.ezdoc create mode 100644 doc/src/guide/multipart_req.ezdoc create mode 100644 doc/src/guide/req.ezdoc create mode 100644 doc/src/guide/req_body.ezdoc create mode 100644 doc/src/guide/resource_design.ezdoc create mode 100644 doc/src/guide/resp.ezdoc create mode 100644 doc/src/guide/rest_cond.png create mode 100644 doc/src/guide/rest_cond.svg create mode 100644 doc/src/guide/rest_conneg.png create mode 100644 doc/src/guide/rest_conneg.svg create mode 100644 doc/src/guide/rest_delete.png create mode 100644 doc/src/guide/rest_delete.svg create mode 100644 doc/src/guide/rest_flowcharts.ezdoc create mode 100644 doc/src/guide/rest_get_head.png create mode 100644 doc/src/guide/rest_get_head.svg create mode 100644 doc/src/guide/rest_handlers.ezdoc create mode 100644 doc/src/guide/rest_options.png create mode 100644 doc/src/guide/rest_options.svg create mode 100644 doc/src/guide/rest_principles.ezdoc create mode 100644 doc/src/guide/rest_put_post_patch.png create mode 100644 doc/src/guide/rest_put_post_patch.svg create mode 100644 doc/src/guide/rest_start.png create mode 100644 doc/src/guide/rest_start.svg create mode 100644 doc/src/guide/routing.ezdoc create mode 100644 doc/src/guide/static_handlers.ezdoc create mode 100644 doc/src/guide/upgrade_protocol.ezdoc create mode 100644 doc/src/guide/ws_handlers.ezdoc create mode 100644 doc/src/guide/ws_protocol.ezdoc create mode 100644 doc/src/manual/cowboy.ezdoc create mode 100644 doc/src/manual/cowboy_app.ezdoc create mode 100644 doc/src/manual/cowboy_handler.ezdoc create mode 100644 doc/src/manual/cowboy_http_handler.ezdoc create mode 100644 doc/src/manual/cowboy_loop_handler.ezdoc create mode 100644 doc/src/manual/cowboy_middleware.ezdoc create mode 100644 doc/src/manual/cowboy_protocol.ezdoc create mode 100644 doc/src/manual/cowboy_req.ezdoc create mode 100644 doc/src/manual/cowboy_rest.ezdoc create mode 100644 doc/src/manual/cowboy_router.ezdoc create mode 100644 doc/src/manual/cowboy_spdy.ezdoc create mode 100644 doc/src/manual/cowboy_static.ezdoc create mode 100644 doc/src/manual/cowboy_sub_protocol.ezdoc create mode 100644 doc/src/manual/cowboy_websocket.ezdoc create mode 100644 doc/src/manual/cowboy_websocket_handler.ezdoc create mode 100644 doc/src/manual/http_status_codes.ezdoc create mode 100644 doc/src/manual/index.ezdoc (limited to 'doc/src') diff --git a/doc/src/guide/architecture.ezdoc b/doc/src/guide/architecture.ezdoc new file mode 100644 index 0000000..745505a --- /dev/null +++ b/doc/src/guide/architecture.ezdoc @@ -0,0 +1,47 @@ +::: Architecture + +Cowboy is a lightweight HTTP server. + +It is built on top of Ranch. Please see the Ranch guide for more +information. + +:: One process per connection + +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. + +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. + +:: Binaries + +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. + +:: Date header + +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. + +:: Max connections + +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. diff --git a/doc/src/guide/broken_clients.ezdoc b/doc/src/guide/broken_clients.ezdoc new file mode 100644 index 0000000..26568a3 --- /dev/null +++ b/doc/src/guide/broken_clients.ezdoc @@ -0,0 +1,61 @@ +::: Dealing with broken clients + +There exists a very large number of implementations for the +HTTP protocol. Most widely used clients, like browsers, +follow the standard quite well, but others may not. In +particular custom enterprise clients tend to be very badly +written. + +Cowboy tries to follow the standard as much as possible, +but is not trying to handle every possible special cases. +Instead Cowboy focuses on the cases reported in the wild, +on the public Web. + +That means clients that ignore the HTTP standard completely +may fail to understand Cowboy's responses. There are of +course workarounds. This chapter aims to cover them. + +:: Lowercase headers + +Cowboy converts all headers it receives to lowercase, and +similarly sends back headers all in lowercase. Some broken +HTTP clients have issues with that. + +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. +``` + +Note that SPDY clients do not have that particular issue +because the specification explicitly says all headers are +lowercase, unlike HTTP which allows any case but treats +them as case insensitive. + +:: Camel-case headers + +Sometimes it is desirable to keep the actual case used by +clients, for example when acting as a proxy between two broken +implementations. There is no easy solution for this other than +forking the project and editing the `cowboy_protocol` file +directly. + +:: Chunked transfer-encoding + +Sometimes an HTTP client advertises itself as HTTP/1.1 but +does not support chunked transfer-encoding. This is invalid +behavior, as HTTP/1.1 clients are required to support it. + +A simple workaround exists in these cases. By changing the +Req object response state to `waiting_stream`, Cowboy will +understand that it must use the identity transfer-encoding +when replying, just like if it was an HTTP/1.0 client. + +``` erlang +Req2 = cowboy_req:set(resp_state, waiting_stream). +``` diff --git a/doc/src/guide/cookies.ezdoc b/doc/src/guide/cookies.ezdoc new file mode 100644 index 0000000..fe9246c --- /dev/null +++ b/doc/src/guide/cookies.ezdoc @@ -0,0 +1,137 @@ +::: 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/doc/src/guide/erlang_beginners.ezdoc b/doc/src/guide/erlang_beginners.ezdoc new file mode 100644 index 0000000..f62543f --- /dev/null +++ b/doc/src/guide/erlang_beginners.ezdoc @@ -0,0 +1,40 @@ +::: Erlang for beginners + +Chances are you are interested in using Cowboy, but have +no idea how to write an Erlang program. Fear not! This +chapter will help you get started. + +We recommend two books for beginners. You should read them +both at some point, as they cover Erlang from two entirely +different perspectives. + +:: Learn You Some Erlang for Great Good! + +The quickest way to get started with Erlang is by reading +a book with the funny name of ^"LYSE^http://learnyousomeerlang.com^, +as we affectionately call it. + +It will get right into the syntax and quickly answer the questions +a beginner would ask themselves, all the while showing funny +pictures and making insightful jokes. + +You can read an early version of the book online for free, +but you really should buy the much more refined paper and +ebook versions. + +:: Programming Erlang + +After writing some code, you will probably want to understand +the very concepts that make Erlang what it is today. These +are best explained by Joe Armstrong, the godfather of Erlang, +in his book ^"Programming Erlang^http://pragprog.com/book/jaerlang2/programming-erlang^. + +Instead of going into every single details of the language, +Joe focuses on the central concepts behind Erlang, and shows +you how they can be used to write a variety of different +applications. + +At the time of writing, the 2nd edition of the book is in beta, +and includes a few details about upcoming Erlang features that +cannot be used today. Choose the edition you want, then get +reading! diff --git a/doc/src/guide/erlang_web.ezdoc b/doc/src/guide/erlang_web.ezdoc new file mode 100644 index 0000000..42fcd34 --- /dev/null +++ b/doc/src/guide/erlang_web.ezdoc @@ -0,0 +1,175 @@ +::: Erlang and the Web + +:: The Web is concurrent + +When you access a website there is little concurrency +involved. A few connections are opened and requests +are sent through these connections. Then the web page +is displayed on your screen. Your browser will only +open up to 4 or 8 connections to the server, depending +on your settings. This isn't much. + +But think about it. You are not the only one accessing +the server at the same time. There can be hundreds, if +not thousands, if not millions of connections to the +same server at the same time. + +Even today a lot of systems used in production haven't +solved the C10K problem (ten thousand concurrent connections). +And the ones who did are trying hard to get to the next +step, C100K, and are pretty far from it. + +Erlang meanwhile has no problem handling millions of +connections. At the time of writing there are application +servers written in Erlang that can handle more than two +million connections on a single server in a real production +application, with spare memory and CPU! + +The Web is concurrent, and Erlang is a language designed +for concurrency, so it is a perfect match. + +Of course, various platforms need to scale beyond a few +million connections. This is where Erlang's built-in +distribution mechanisms come in. If one server isn't +enough, add more! Erlang allows you to use the same code +for talking to local processes or to processes in other +parts of your cluster, which means you can scale very +quickly if the need arises. + +The Web has large userbases, and the Erlang platform was +designed to work in a distributed setting, so it is a +perfect match. + +Or is it? Surely you can find solutions to handle that many +concurrent connections with your favorite language... But all +these solutions will break down in the next few years. Why? +Firstly because servers don't get any more powerful, they +instead get a lot more cores and memory. This is only useful +if your application can use them properly, and Erlang is +light-years away from anything else in that area. Secondly, +today your computer and your phone are online, tomorrow your +watch, goggles, bike, car, fridge and tons of other devices +will also connect to various applications on the Internet. + +Only Erlang is prepared to deal with what's coming. + +:: The Web is soft real time + +What does soft real time mean, you ask? It means we want the +operations done as quickly as possible, and in the case of +web applications, it means we want the data propagated fast. + +In comparison, hard real time has a similar meaning, but also +has a hard time constraint, for example an operation needs to +be done in under N milliseconds otherwise the system fails +entirely. + +Users aren't that needy yet, they just want to get access +to their content in a reasonable delay, and they want the +actions they make to register at most a few seconds after +they submitted them, otherwise they'll start worrying about +whether it successfully went through. + +The Web is soft real time because taking longer to perform an +operation would be seen as bad quality of service. + +Erlang is a soft real time system. It will always run +processes fairly, a little at a time, switching to another +process after a while and preventing a single process to +steal resources from all others. This means that Erlang +can guarantee stable low latency of operations. + +Erlang provides the guarantees that the soft real time Web +requires. + +:: The Web is asynchronous + +Long ago, the Web was synchronous because HTTP was synchronous. +You fired a request, and then waited for a response. Not anymore. +It all began when XmlHttpRequest started being used. It allowed +the client to perform asynchronous calls to the server. + +Then Websocket appeared and allowed both the server and the client +to send data to the other endpoint completely asynchronously. The +data is contained within frames and no response is necessary. + +Erlang processes work the same. They send each other data contained +within messages and then continue running without needing a response. +They tend to spend most of their time inactive, waiting for a new +message, and the Erlang VM happily activate them when one is received. + +It is therefore quite easy to imagine Erlang being good at receiving +Websocket frames, which may come in at unpredictable times, pass the +data to the responsible processes which are always ready waiting for +new messages, and perform the operations required by only activating +the required parts of the system. + +The more recent Web technologies, like Websocket of course, but also +SPDY and HTTP/2.0, are all fully asynchronous protocols. The concept +of requests and responses is retained of course, but anything could +be sent in between, by both the client or the browser, and the +responses could also be received in a completely different order. + +Erlang is by nature asynchronous and really good at it thanks to the +great engineering that has been done in the VM over the years. It's +only natural that it's so good at dealing with the asynchronous Web. + +:: The Web is omnipresent + +The Web has taken a very important part of our lives. We're +connected at all times, when we're on our phone, using our computer, +passing time using a tablet while in the bathroom... And this +isn't going to slow down, every single device at home or on us +will be connected. + +All these devices are always connected. And with the number of +alternatives to give you access to the content you seek, users +tend to not stick around when problems arise. Users today want +their applications to be always available and if it's having +too many issues they just move on. + +Despite this, when developers choose a product to use for building +web applications, their only concern seem to be "Is it fast?", +and they look around for synthetic benchmarks showing which one +is the fastest at sending "Hello world" with only a handful +concurrent connections. Web benchmarks haven't been representative +of reality in a long time, and are drifting further away as +time goes on. + +What developers should really ask themselves is "Can I service +all my users with no interruption?" and they'd find that they have +two choices. They can either hope for the best, or they can use +Erlang. + +Erlang is built for fault tolerance. When writing code in any other +language, you have to check all the return values and act accordingly +to avoid any unforeseen issues. If you're lucky, you won't miss +anything important. When writing Erlang code, you can just check +the success condition and ignore all errors. If an error happen, +the Erlang process crashes and is then restarted by a special +process called a supervisor. + +The Erlang developer thus has no need to fear about unhandled +errors, and can focus on handling only the errors that should +give some feedback to the user and let the system take care of +the rest. This also has the advantage of allowing him to write +a lot less code, and letting him sleep at night. + +Erlang's fault tolerance oriented design is the first piece of +what makes it the best choice for the omnipresent, always available +Web. + +The second piece is Erlang's built-in distribution. Distribution +is a key part of building a fault tolerant system, because it +allows you to handle bigger failures, like a whole server going +down, or even a data center entirely. + +Fault tolerance and distribution are important today, and will be +vital in the future of the Web. Erlang is ready. + +:: Erlang is the ideal platform for the Web + +Erlang provides all the important features that the Web requires +or will require in the near future. Erlang is a perfect match +for the Web, and it only makes sense to use it to build web +applications. diff --git a/doc/src/guide/getting_started.ezdoc b/doc/src/guide/getting_started.ezdoc new file mode 100644 index 0000000..ff34699 --- /dev/null +++ b/doc/src/guide/getting_started.ezdoc @@ -0,0 +1,306 @@ +::: Getting started + +Erlang is more than a language, it is also an operating system +for your applications. Erlang developers rarely write standalone +modules, they write libraries or applications, and then bundle +those into what is called a release. A release contains the +Erlang VM plus all applications required to run the node, so +it can be pushed to production directly. + +This chapter walks you through all the steps of setting up +Cowboy, writing your first application and generating your first +release. At the end of this chapter you should know everything +you need to push your first Cowboy application to production. + +:: Application skeleton + +Let's start by creating this application. We will simply call it +`hello_erlang`. This application will have the following directory +structure: + +``` bash +hello_erlang/ + src/ + hello_erlang.app.src + hello_erlang_app.erl + hello_erlang_sup.erl + hello_handler.erl + erlang.mk + Makefile + relx.config +``` + +Once the release is generated, we will also have the following +files added: + +``` bash +hello_erlang/ + ebin/ + hello_erlang.app + hello_erlang_app.beam + hello_erlang_sup.beam + hello_handler.beam + _rel/ + relx +``` + +As you can probably guess, the `.app.src` file end up becoming +the `.app` file, and the `.erl` files are compiled into `.beam`. +Then, the whole release will be copied into the `_rel/` directory. + +The `.app` file contains various informations about the application. +It contains its name, a description, a version, a list of modules, +default configuration and more. + +Using a build system like ^"erlang.mk^https://github.com/extend/erlang.mk^, +the list of modules will be included automatically in the `.app` file, +so you don't need to manually put them in your `.app.src` file. + +For generating the release, we will use ^"relx^https://github.com/erlware/relx +as it is a much simpler alternative to the tool coming with Erlang. + +First, create the `hello_erlang` directory. It should have the same name +as the application within it. Then we create the `src` directory inside +it, which will contain the source code for our application. + +``` bash +$ mkdir hello_erlang +$ cd hello_erlang +$ mkdir src +``` + +Let's first create the `hello_erlang.app.src` file. It should be pretty +straightforward for the most part. You can use the following template +and change what you like in it. + +``` erlang +{application, hello_erlang, [ + {description, "Hello world with Cowboy!"}, + {vsn, "0.1.0"}, + {modules, []}, + {registered, [hello_erlang_sup]}, + {applications, [ + kernel, + stdlib, + cowboy + ]}, + {mod, {hello_erlang_app, []}}, + {env, []} +]}. +``` + +The `modules` line will be replaced with the list of modules during +compilation. Make sure to leave this line even if you do not use it +directly. + +The `registered` value indicates which processes are registered by this +application. You will often only register the top-level supervisor +of the application. + +The `applications` value lists the applications that must be started +for this application to work. The Erlang release will start all the +applications listed here automatically. + +The `mod` value defines how the application should be started. Erlang +will use the `hello_erlang_app` module for starting the application. + +The `hello_erlang_app` module is what we call an application behavior. +The application behavior must define two functions: `start/2` and +`stop/1`, for starting and stopping the application. A typical +application module would look like this: + +``` erlang +-module(hello_erlang_app). +-behavior(application). + +-export([start/2]). +-export([stop/1]). + +start(_Type, _Args) -> + hello_erlang_sup:start_link(). + +stop(_State) -> + ok. +``` + +That's not enough however. Since we are building a Cowboy based +application, we also need to initialize Cowboy when we start our +application. + +:: Setting up Cowboy + +Cowboy does nothing by default. + +Cowboy uses Ranch for handling the connections and provides convenience +functions to start Ranch listeners. + +The `cowboy:start_http/4` function starts a listener for HTTP connections +using the TCP transport. The `cowboy:start_https/4` function starts a +listener for HTTPS connections using the SSL transport. + +Listeners are a group of processes that are used to accept and manage +connections. The processes used specifically for accepting connections +are called acceptors. The number of acceptor processes is unrelated to +the maximum number of connections Cowboy can handle. Please refer to +the ^"Ranch guide^http://ninenines.eu/docs/en/ranch/HEAD/guide/ +for in-depth information. + +Listeners are named. They spawn a given number of acceptors, listen for +connections using the given transport options and pass along the protocol +options to the connection processes. The protocol options must include +the dispatch list for routing requests to handlers. + +The dispatch list is explained in greater details in the +^"Routing^routing^ chapter. For the purpose of this example +we will simply map all URLs to our handler `hello_handler`, +using the wildcard `_` for both the hostname and path parts +of the URL. + +This is what the `hello_erlang_app:start/2` function looks like +with Cowboy initialized. + +``` erlang +start(_Type, _Args) -> + Dispatch = cowboy_router:compile([ + %% {URIHost, list({URIPath, Handler, Opts})} + {'_', [{'_', hello_handler, []}]} + ]), + %% Name, NbAcceptors, TransOpts, ProtoOpts + cowboy:start_http(my_http_listener, 100, + [{port, 8080}], + [{env, [{dispatch, Dispatch}]}] + ), + hello_erlang_sup:start_link(). +``` + +Do note that we told Cowboy to start listening on port 8080. +You can change this value if needed. + +Our application doesn't need to start any process, as Cowboy +will automatically start processes for every incoming +connections. We are still required to have a top-level supervisor +however, albeit a fairly small one. + +``` erlang +-module(hello_erlang_sup). +-behavior(supervisor). + +-export([start_link/0]). +-export([init/1]). + +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +init([]) -> + {ok, {{one_for_one, 10, 10}, []}}. +``` + +Finally, we need to write the code for handling incoming requests. + +:: Handling HTTP requests + +Cowboy features many kinds of handlers. For this simple example, +we will just use the plain HTTP handler, which has three callback +functions: `init/3`, `handle/2` and `terminate/3`. You can find more +information about the arguments and possible return values of these +callbacks in the +^"cowboy_http_handler function reference^http://ninenines.eu/docs/en/cowboy/HEAD/manual/cowboy_http_handler^. + +Our handler will only send a friendly hello back to the client. + +``` erlang +-module(hello_handler). +-behavior(cowboy_http_handler). + +-export([init/3]). +-export([handle/2]). +-export([terminate/3]). + +init(_Type, Req, _Opts) -> + {ok, Req, undefined_state}. + +handle(Req, State) -> + {ok, Req2} = cowboy_req:reply(200, [ + {<<"content-type">>, <<"text/plain">>} + ], <<"Hello World!">>, Req), + {ok, Req2, State}. + +terminate(_Reason, _Req, _State) -> + ok. +``` + +The `Req` variable above is the Req object, which allows the developer +to obtain information about the request and to perform a reply. +Its usage is documented in the +^"cowboy_req function reference^http://ninenines.eu/docs/en/cowboy/HEAD/manual/cowboy_req^. + +The code for our application is ready, so let's build a release! + +:: Compiling + +First we need to download `erlang.mk`. + +``` bash +$ wget https://raw.github.com/extend/erlang.mk/master/erlang.mk +$ ls +src/ +erlang.mk +``` + +Then we need to create a Makefile that will include `erlang.mk` +for building our application. We need to define the Cowboy +dependency in the Makefile. Thankfully `erlang.mk` already +knows where to find Cowboy as it features a package index, +so we can just tell it to look there. + +``` Makefile +PROJECT = hello_erlang + +DEPS = cowboy +dep_cowboy = pkg://cowboy master + +include erlang.mk +``` + +Note that when creating production nodes you will most likely +want to use a specific version of Cowboy instead of `master`, +and properly test your release every time you update Cowboy. + +If you type `make` in a shell now, your application should build +as expected. If you get compilation errors, double check that you +haven't made any typo when creating the previous files. + +``` bash +$ make +``` + +:: Generating the release + +That's not all however, as we want to create a working release. +For that purpose, we need to create a `relx.config` file. When +this file exists, `erlang.mk` will automatically download `relx` +and build the release when you type `make`. + +In the `relx.config` file, we only need to tell `relx` that +we want the release to include the `hello_erlang` application, +and that we want an extended start script for convenience. +`relx` will figure out which other applications are required +by looking into the `.app` files for dependencies. + +``` erlang +{release, {hello_erlang, "1"}, [hello_erlang]}. +{extended_start_script, true}. +``` + +The `release` value is used to specify the release name, its +version, and the applications to be included. + +We can now build and start the release. + +``` bash +$ make +$ ./_rel/hello_erlang/bin/hello_erlang console +``` + +If you then access `http://localhost:8080` using your browser, +you should receive a nice greet! diff --git a/doc/src/guide/hooks.ezdoc b/doc/src/guide/hooks.ezdoc new file mode 100644 index 0000000..edef971 --- /dev/null +++ b/doc/src/guide/hooks.ezdoc @@ -0,0 +1,82 @@ +::: Hooks + +Cowboy provides two hooks. `onrequest` is called once the request +line and headers have been received. `onresponse` is called just +before sending the response. + +:: Onrequest + +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. + +:: Onresponse + +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. If a reply +is sent, the hook MUST explicitly provide all headers that are needed. + +You can specify the `onresponse` hook when creating the listener. + +``` 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) -> + 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; +custom_404_hook(_, _, _, Req) -> + Req. +``` + +Again, make sure to always return the last request object obtained. diff --git a/doc/src/guide/http_handlers.ezdoc b/doc/src/guide/http_handlers.ezdoc new file mode 100644 index 0000000..9a450a6 --- /dev/null +++ b/doc/src/guide/http_handlers.ezdoc @@ -0,0 +1,145 @@ +::: Handling plain HTTP requests + +The simplest way to handle a request is by writing a +plain HTTP handler. It is modeled after Erlang/OTP's +gen_server behaviour, although simplified, as Cowboy +will simply call the three callbacks sequentially. + +:: Initialization + +The first callback, `init/3`, is common to all handlers, +as it is used to identify the type of handler. Plain +HTTP handlers just return `ok`. + +``` erlang +init(_Type, Req, _Opts) -> + {ok, Req, no_state}. +``` + +This function receives the name of the transport and +protocol modules used for processing the request. +They can be used to quickly dismiss requests. For +example the following handler will crash when accessed +using TCP instead of SSL. + +``` erlang +init({ssl, _}, Req, _Opts) -> + {ok, Req, no_state}. +``` + +This function also receives the options associated with +this route that you configured previously. If your +handler does not use options, then it is recommended +you match the value `[]` directly to quickly detect +configuration errors. + +``` erlang +init(_Type, Req, []) -> + {ok, Req, no_state}. +``` + +You do not need to validate the options unless they +are user configured. If they are, and there's a +configuration error, you may choose to crash. For +example, this will crash if the required `lang` +option is not found. + +``` erlang +init(_Type, Req, Opts) -> + {_, _Lang} = lists:keyfind(lang, 1, Opts), + {ok, Req, no_state}. +``` + +If your users are unlikely to figure out the issue +without explanations, then you should send a more +meaningful error back to the user. Since we already +replied to the user, there's no need for us to +continue with the handler code, so we use the +`shutdown` return value to stop early. + +``` erlang +init(_Type, Req, Opts) -> + case lists:keyfind(lang, 1, Opts) of + false -> + {ok, Req2} = cowboy_req:reply(500, [ + {<<"content-type">>, <<"text/plain">>} + ], "Missing option 'lang'.", Req), + {shutdown, Req2, no_state}; + _ -> + {ok, Req, no_state} + end. +``` + +Once the options have been validated, we can use them +safely. So we need to pass them onward to the rest of +the handler. That's what the third element of the return +tuple, the state, is for. + +We recommend that you create a state record for this. +The record will make your handler code clearer and +will allow you to better use Dialyzer for type checking. + +``` erlang +-record(state, { + lang :: en | fr + %% More fields here. +}). + +init(_Type, Req, Opts) -> + {_, Lang} = lists:keyfind(lang, 1, Opts), + {ok, Req, #state{lang=Lang}}. +``` + +:: Handling the request + +The second callback, `handle/2`, is specific to plain HTTP +handlers. It's where you, wait for it, handle the request. + +A handle function that does nothing would look like this: + +``` erlang +handle(Req, State) -> + {ok, Req, State}. +``` + +There's no other return value. To obtain information about +the request, or send a response, you would use the Req object +here. The Req object is documented in its own chapter. + +The following handle function will send a fairly original response. + +``` erlang +handle(Req, State) -> + {ok, Req2} = cowboy_req:reply(200, [ + {<<"content-type">>, <<"text/plain">>} + ], <<"Hello World!">>, Req), + {ok, Req2, State}. +``` + +:: Cleaning up + +The third and last callback, `terminate/3`, will most likely +be empty in your handler. + +``` erlang +terminate(_Reason, Req, State) -> + ok. +``` + +This callback is strictly reserved for any required cleanup. +You cannot send a response from this function. There is no +other return value. + +If you used the process dictionary, timers, monitors or may +be receiving messages, then you can use this function to clean +them up, as Cowboy might reuse the process for the next +keep-alive request. + +The chances of any of this happening in your handler are pretty +thin however. The use of the process dictionary is discouraged +in Erlang code in general. And if you need to use timers, monitors +or to receive messages, you are better off with a loop handler, +a different kind of handler meant specifically for this use. + +This function is still available should you need it. It will +always be called. diff --git a/doc/src/guide/http_req_life.ezdoc b/doc/src/guide/http_req_life.ezdoc new file mode 100644 index 0000000..5fd8486 --- /dev/null +++ b/doc/src/guide/http_req_life.ezdoc @@ -0,0 +1,147 @@ +::: The life of a request + +This chapter explains the different steps a request +goes through until a response is sent, along with +details of the Cowboy implementation. + +:: Request/response + +As you already know, HTTP clients connect to the server and +send a request for a resource; the server then sends a +response containing the resource if it could obtain it. + +Before the server can send the resource, however, it +needs to perform many different operations to read the +request, find the resource, prepare the response being +sent and often other related operations the user can +add like writing logs. + +Requests take the following route in Cowboy: + +^"HTTP request/response flowchart^!http_req_resp.png + +This shows the default middlewares, but they may be +configured differently in your setup. The dark green +indicates the points where you can hook your own code, +the light green is the Cowboy code that you can of +course configure as needed. + +The `acceptor` is the part of the server that accepts +the connection and create an Erlang process to handle +it. The `parser` then starts reading from the socket +and handling requests as they come until the socket +is closed. + +A response may be sent at many different points in the +life of the request. If Cowboy can't parse the request, +it gives up with an error response. If the router can't +find the resource, it sends a not found error. Your +own code can of course send a response at any time. + +When a response is sent, you can optionally modify it +or act upon it by enabling the `onresponse` hook. By +default the response is sent directly to the client. + +:: And then? + +Behavior depends on what protocol is in use. + +HTTP/1.0 can only process one request per connection, +so Cowboy will close the connection immediately after +it sends the response. + +HTTP/1.1 allows the client to request that the server +keeps the connection alive. This mechanism is described +in the next section. + +SPDY is designed to allow sending multiple requests +asynchronously on the same connection. Details on what +this means for your application is described in this +chapter. + +:: Keep-alive (HTTP/1.1) + +With HTTP/1.1, the connection may be left open for +subsequent requests to come. This mechanism is called +`keep-alive`. + +When the client sends a request to the server, it includes +a header indicating whether it would like to leave the +socket open. The server may or may not accept, indicating +its choice by sending the same header in the response. + +Cowboy will include this header automatically in all +responses to HTTP/1.1 requests. You can however force +the closing of the socket if you want. When Cowboy sees +you want to send a `connection: close` header, it will +not override it and will close the connection as soon +as the reply is sent. + +This snippet will force Cowboy to close the connection. + +``` erlang +{ok, Req2} = cowboy_req:reply(200, [ + {<<"connection">>, <<"close">>}, +], <<"Closing the socket in 3.. 2.. 1..">>, Req). +``` + +Cowboy will only accept a certain number of new requests +on the same connection. By default it will run up to 100 +requests. This number can be changed by setting the +`max_keepalive` configuration value when starting an +HTTP listener. + +``` erlang +cowboy:start_http(my_http_listener, 100, [{port, 8080}], [ + {env, [{dispatch, Dispatch}]}, + {max_keepalive, 5} +]). +``` + +Cowboy implements the keep-alive mechanism by reusing +the same process for all requests. This allows Cowboy +to save memory. This works well because most code will +not have any side effect impacting subsequent requests. +But it also means you need to clean up if you do have +code with side effects. The `terminate/3` function can +be used for this purpose. + +:: Pipelining (HTTP/1.1) + +While HTTP is designed as a sequential protocol, with +the client sending a request and then waiting for the +response from the server, nothing prevents the client +from sending more requests to the server without waiting +for the response, due to how sockets work. The server +still handles the requests sequentially and sends the +responses in the same order. + +This mechanism is called pipelining. It allows reducing +latency when a client needs to request many resources +at the same time. This is used by browsers when requesting +static files for example. + +This is handled automatically by the server. + +:: Asynchronous requests (SPDY) + +In SPDY, the client can send a request at any time. +And the server can send a response at any time too. + +This means for example that the client does not need +to wait for a request to be fully sent to send another, +it is possible to interleave a request with the request +body of another request. The same is true with responses. +Responses may also be sent in a different order. + +Because requests and responses are fully asynchronous, +Cowboy creates a new process for each request, and these +processes are managed by another process that handles the +connection itself. + +SPDY servers may also decide to send resources to the +client before the client requests them. This is especially +useful for sending static files associated with the HTML +page requested, as this reduces the latency of the overall +response. Cowboy does not support this particular mechanism +at this point, however. diff --git a/doc/src/guide/http_req_resp.png b/doc/src/guide/http_req_resp.png new file mode 100644 index 0000000..e38935f Binary files /dev/null and b/doc/src/guide/http_req_resp.png differ diff --git a/doc/src/guide/http_req_resp.svg b/doc/src/guide/http_req_resp.svg new file mode 100644 index 0000000..0cfa0ae --- /dev/null +++ b/doc/src/guide/http_req_resp.svg @@ -0,0 +1,558 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + some text + acceptor + parser + router + some text + onrequest + handler + middlewares + some text + client + + + + + reply + onresponse + + diff --git a/doc/src/guide/index.ezdoc b/doc/src/guide/index.ezdoc new file mode 100644 index 0000000..38b2ac0 --- /dev/null +++ b/doc/src/guide/index.ezdoc @@ -0,0 +1,58 @@ +::: Cowboy User Guide + +The Cowboy User Guide explores the modern Web and how to make +best use of Cowboy for writing powerful web applications. + +:: Introducing Cowboy + +* ^"Introduction^introduction +* ^"The modern Web^modern_web +* ^"Erlang and the Web^erlang_web +* ^"Erlang for beginners^erlang_beginners +* ^"Getting started^getting_started + +:: HTTP + +* ^"The life of a request^http_req_life +* ^"Routing^routing +* ^"Handling plain HTTP requests^http_handlers +* ^"The Req object^req +* ^"Reading the request body^req_body +* ^"Sending a response^resp +* ^"Using cookies^cookies + +:: Multipart + +* ^"Introduction to multipart^multipart_intro +* ^"Multipart requests^multipart_req + +:: Static files + +* ^"Static handler^static_handlers + +:: REST + +* ^"REST principles^rest_principles +* ^"Handling REST requests^rest_handlers +* ^"REST flowcharts^rest_flowcharts +* ^"Designing a resource handler^resource_design + +:: Websocket + +* ^"The Websocket protocol^ws_protocol +* ^"Handling Websocket connections^ws_handlers + +:: Server push + +* ^"Loop handlers^loop_handlers + +:: Pluggable interface + +* ^"Middlewares^middlewares +* ^"Protocol upgrades^upgrade_protocol +* ^"Hooks^hooks + +:: Internals + +* ^"Architecture^architecture +* ^"Dealing with broken clients^broken_clients diff --git a/doc/src/guide/introduction.ezdoc b/doc/src/guide/introduction.ezdoc new file mode 100644 index 0000000..7f77fcc --- /dev/null +++ b/doc/src/guide/introduction.ezdoc @@ -0,0 +1,49 @@ +::: Introduction + +Cowboy is a small, fast and modular HTTP server written in Erlang. + +Cowboy aims to provide a complete HTTP stack, including its derivatives +SPDY, Websocket and REST. Cowboy currently supports HTTP/1.0, HTTP/1.1, +Websocket (all implemented drafts + standard) and Webmachine-based REST. + +Cowboy is a high quality project. It has a small code base, is very +efficient (both in latency and memory use) and can easily be embedded +in another application. + +Cowboy is clean Erlang code. It includes hundreds of tests and its code +is fully compliant with the Dialyzer. It is also well documented and +features both a Function Reference and a User Guide. + +:: Prerequisites + +No Erlang knowledge is required for reading this guide. The reader will +be introduced to Erlang concepts and redirected to reference material +whenever necessary. + +Knowledge of the HTTP protocol is recommended but not required, as it +will be detailed throughout the guide. + +:: Supported platforms + +Cowboy is tested and supported on Linux. + +Cowboy has been reported to work on other platforms, but we make no +guarantee that the experience will be safe and smooth. You are advised +to perform the necessary testing and security audits prior to deploying +on other platforms. + +Cowboy is developed for Erlang R15B+. + +Cowboy may be compiled on earlier Erlang versions with small source code +modifications but there is no guarantee that it will work as expected. + +:: Conventions + +In the HTTP protocol, the method name is case sensitive. All standard +method names are uppercase. + +Header names are case insensitive. Cowboy converts all the request +header names to lowercase, and expects your application to provide +lowercase header names in the response. + +The same applies to any other case insensitive value. diff --git a/doc/src/guide/loop_handlers.ezdoc b/doc/src/guide/loop_handlers.ezdoc new file mode 100644 index 0000000..fba4feb --- /dev/null +++ b/doc/src/guide/loop_handlers.ezdoc @@ -0,0 +1,141 @@ +::: Loop handlers + +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. + +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. + +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. + +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. + +:: Initialization + +The `init/3` function must return a `loop` tuple to enable +loop handler behavior. This tuple may optionally contain +a timeout value and/or the atom `hibernate` to make the +process enter hibernation until a message is received. + +This snippet enables the loop handler. + +``` erlang +init(_Type, Req, _Opts) -> + {loop, Req, undefined_state}. +``` + +However it is largely recommended that you set a timeout +value. The next example sets a timeout value of 30s and +also makes the process hibernate. + +``` erlang +init(_Type, Req, _Opts) -> + {loop, Req, undefined_state, 30000, hibernate}. +``` + +:: Receive loop + +Once initialized, Cowboy will wait for messages to arrive +in the process' mailbox. When a message arrives, Cowboy +calls the `info/3` function with the message, the Req object +and the handler's state. + +The following snippet sends a reply when it receives a +`reply` message from another process, or waits for another +message otherwise. + +``` erlang +info({reply, Body}, Req, State) -> + {ok, Req2} = cowboy_req:reply(200, [], Body, Req), + {ok, Req2, State}; +info(_Msg, Req, State) -> + {loop, Req, State, hibernate}. +``` + +Do note that the `reply` tuple here may be any message +and is simply an example. + +This callback may perform any necessary operation including +sending all or parts of a reply, and will subsequently +return a tuple indicating if more messages are to be expected. + +The callback may also choose to do nothing at all and just +skip the message received. + +If a reply is sent, then the `ok` tuple should be returned. +This will instruct Cowboy to end the request. + +Otherwise a `loop` tuple should be returned. + +:: Streaming loop + +Another common case well suited for loop handlers is +streaming data received in the form of Erlang messages. +This can be done by initiating a chunked reply in the +`init/3` callback and then using `cowboy_req:chunk/2` +every time a message is received. + +The following snippet does exactly that. As you can see +a chunk is sent every time a `chunk` message is received, +and the loop is stopped by sending an `eof` message. + +``` erlang +init(_Type, Req, _Opts) -> + {ok, 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), + {loop, Req, State}; +info(_Msg, Req, State) -> + {loop, Req, State}. +``` + +:: Cleaning up + +It is recommended that you set the connection header to +`close` when replying, as this process may be reused for +a subsequent request. + +Please refer to the ^"HTTP handlers chapter^http_handlers +for general instructions about cleaning up. + +:: Timeout + +By default Cowboy will not attempt to close the connection +if there is no activity from the client. This is not always +desirable, which is why you can set a timeout. Cowboy will +close the connection if no data was received from the client +after the configured time. The timeout only needs to be set +once and can't be modified afterwards. + +Because the request may have had a body, or may be followed +by another request, Cowboy is forced to buffer all data it +receives. This data may grow to become too large though, +so there is a configurable limit for it. The default buffer +size is of 5000 bytes, but it may be changed by setting the +`loop_max_buffer` middleware environment value. + +:: Hibernate + +To save memory, you may hibernate the process in between +messages received. This is done by returning the atom +`hibernate` as part of the `loop` tuple callbacks normally +return. Just add the atom at the end and Cowboy will hibernate +accordingly. diff --git a/doc/src/guide/middlewares.ezdoc b/doc/src/guide/middlewares.ezdoc new file mode 100644 index 0000000..e33abfb --- /dev/null +++ b/doc/src/guide/middlewares.ezdoc @@ -0,0 +1,69 @@ +::: Middlewares + +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 name of the listener +* `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. + +You can update the environment by calling the `cowboy:set_env/3` +convenience function, adding or replacing a value in the environment. + +:: 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/doc/src/guide/modern_web.ezdoc b/doc/src/guide/modern_web.ezdoc new file mode 100644 index 0000000..1c2c342 --- /dev/null +++ b/doc/src/guide/modern_web.ezdoc @@ -0,0 +1,215 @@ +::: The modern Web + +Let's take a look at various technologies from the beginnings +of the Web up to this day, and get a preview of what's +coming next. + +Cowboy is compatible with all the technology cited in this +chapter except of course HTTP/2.0 which has no implementation +in the wild at the time of writing. + +:: The prehistoric Web + +HTTP was initially created to serve HTML pages and only +had the GET method for retrieving them. This initial +version is documented and is sometimes called HTTP/0.9. +HTTP/1.0 defined the GET, HEAD and POST methods, and +was able to send data with POST requests. + +HTTP/1.0 works in a very simple way. A TCP connection +is first established to the server. Then a request is +sent. Then the server sends a response back and closes +the connection. + +Suffice to say, HTTP/1.0 is not very efficient. Opening +a TCP connection takes some time, and pages containing +many assets load much slower than they could because of +this. + +Most improvements done in recent years focused on reducing +this load time and reducing the latency of the requests. + +:: HTTP/1.1 + +HTTP/1.1 quickly followed and added a keep-alive mechanism +to allow using the same connection for many requests, as +well as streaming capabilities, allowing an endpoint to send +a body in well defined chunks. + +HTTP/1.1 defines the OPTIONS, GET, HEAD, POST, PUT, DELETE, +TRACE and CONNECT methods. The PATCH method was added in more +recent years. It also improves the caching capabilities with +the introduction of many headers. + +HTTP/1.1 still works like HTTP/1.0 does, except the connection +can be kept alive for subsequent requests. This however allows +clients to perform what is called as pipelining: sending many +requests in a row, and then processing the responses which will +be received in the same order as the requests. + +:: REST + +The design of HTTP/1.1 was influenced by the REST architectural +style. REST, or REpresentational State Transfer, is a style of +architecture for loosely connected distributed systems. + +REST defines constraints that systems must obey to in order to +be RESTful. A system which doesn't follow all the constraints +cannot be considered RESTful. + +REST is a client-server architecture with a clean separation +of concerns between the client and the server. They communicate +by referencing resources. Resources can be identified, but +also manipulated. A resource representation has a media type +and information about whether it can be cached and how. Hypermedia +determines how resources are related and how they can be used. +REST is also stateless. All requests contain the complete +information necessary to perform the action. + +HTTP/1.1 defines all the methods, headers and semantics required +to implement RESTful systems. + +REST is most often used when designing web application APIs +which are generally meant to be used by executable code directly. + +:: XmlHttpRequest + +Also know as AJAX, this technology allows Javascript code running +on a web page to perform asynchronous requests to the server. +This is what started the move from static websites to dynamic +web applications. + +XmlHttpRequest still performs HTTP requests under the hood, +and then waits for a response, but the Javascript code can +continue to run until the response arrives. It will then receive +the response through a callback previously defined. + +This is of course still requests initiated by the client, +the server still had no way of pushing data to the client +on its own, so new technology appeared to allow that. + +:: Long-polling + +Polling was a technique used to overcome the fact that the server +cannot push data directly to the client. Therefore the client had +to repeatedly create a connection, make a request, get a response, +then try again a few seconds later. This is overly expensive and +adds an additional delay before the client receives the data. + +Polling was necessary to implement message queues and other +similar mechanisms, where a user must be informed of something +when it happens, rather than when he refreshes the page next. +A typical example would be a chat application. + +Long-polling was created to reduce the server load by creating +less connections, but also to improve latency by getting the +response back to the client as soon as it becomes available +on the server. + +Long-polling works in a similar manner to polling, except the +request will not get a response immediately. Instead the server +leaves it open until it has a response to send. After getting +the response, the client creates a new request and gets back +to waiting. + +You probably guessed by now that long-polling is a hack, and +like most hacks it can suffer from unforeseen issues, in this +case it doesn't always play well with proxies. + +:: HTML5 + +HTML5 is, of course, the HTML version after HTML4. But HTML5 +emerged to solve a specific problem: dynamic web applications. + +HTML was initially created to write web pages which compose +a website. But soon people and companies wanted to use HTML +to write more and more complex websites, eventually known as +web applications. They are for example your news reader, your +email client in the browser, or your video streaming website. + +Because HTML wasn't enough, they started using proprietary +solutions, often implemented using plug-ins. This wasn't +perfect of course, but worked well enough for most people. + +However, the needs for a standard solution eventually became +apparent. The browser needed to be able to play media natively. +It needed to be able to draw anything. It needed an efficient +way of streaming events to the server, but also receiving +events from the server. + +The solution went on to become HTML5. At the time of writing +it is being standardized. + +:: EventSource + +EventSource, sometimes also called Server-Sent Events, is a +technology allowing servers to push data to HTML5 applications. + +EventSource is one-way communication channel from the server +to the client. The client has no means to talk to the server +other than by using HTTP requests. + +It consists of a Javascript object allowing setting up an +EventSource connection to the server, and a very small protocol +for sending events to the client on top of the HTTP/1.1 +connection. + +EventSource is a lightweight solution that only works for +UTF-8 encoded text data. Binary data and text data encoded +differently are not allowed by the protocol. A heavier but +more generic approach can be found in Websocket. + +:: Websocket + +Websocket is a protocol built on top of HTTP/1.1 that provides +a two-ways communication channel between the client and the +server. Communication is asynchronous and can occur concurrently. + +It consists of a Javascript object allowing setting up a +Websocket connection to the server, and a binary based +protocol for sending data to the server or the client. + +Websocket connections can transfer either UTF-8 encoded text +data or binary data. The protocol also includes support for +implementing a ping/pong mechanism, allowing the server and +the client to have more confidence that the connection is still +alive. + +A Websocket connection can be used to transfer any kind of data, +small or big, text or binary. Because of this Websocket is +sometimes used for communication between systems. + +:: SPDY + +SPDY is an attempt to reduce page loading time by opening a +single connection per server, keeping it open for subsequent +requests, and also by compressing the HTTP headers to reduce +the size of requests. + +SPDY is compatible with HTTP/1.1 semantics, and is actually +just a different way of performing HTTP requests and responses, +by using binary frames instead of a text-based protocol. +SPDY also allows the server to send extra responses following +a request. This is meant to allow sending the resources +associated with the request before the client requests them, +saving latency when loading websites. + +SPDY is an experiment that has proven successful and is used +as the basis for the HTTP/2.0 standard. + +Browsers make use of TLS Next Protocol Negotiation to upgrade +to a SPDY connection seamlessly if the protocol supports it. + +The protocol itself has a few shortcomings which are being +fixed in HTTP/2.0. + +:: HTTP/2.0 + +HTTP/2.0 is the long-awaited update to the HTTP/1.1 protocol. +It is based on SPDY although a lot has been improved at the +time of writing. + +HTTP/2.0 is an asynchronous two-ways communication channel +between two endpoints. + +It is planned to be ready late 2014. diff --git a/doc/src/guide/multipart_intro.ezdoc b/doc/src/guide/multipart_intro.ezdoc new file mode 100644 index 0000000..b9a7fa9 --- /dev/null +++ b/doc/src/guide/multipart_intro.ezdoc @@ -0,0 +1,50 @@ +::: Introduction to multipart + +Multipart originates from MIME, an Internet standard that +extends the format of emails. Multipart messages are a +container for parts of any content-type. + +For example, a multipart message may have a part +containing text and a second part containing an +image. This is what allows you to attach files +to emails. + +In the context of HTTP, multipart is most often used +with the `multipart/form-data` content-type. This is +the content-type you have to use when you want browsers +to be allowed to upload files through HTML forms. + +Multipart is of course not required for uploading +files, it is only required when you want to do so +through HTML forms. + +:: Structure + +A multipart message is a list of parts. Parts may +contain either a multipart message or a non-multipart +content-type. This allows parts to be arranged in a +tree structure, although this is a rare case as far +as the Web is concerned. + +:: Form-data + +In the normal case, when a form is submitted, the +browser will use the `application/x-www-form-urlencoded` +content-type. This type is just a list of keys and +values and is therefore not fit for uploading files. + +That's where the `multipart/form-data` content-type +comes in. When the form is configured to use this +content-type, the browser will use one part of the +message for each form field. This means that a file +input field will be sent in its own part, but the +same applies to all other kinds of fields. + +A form with a text input, a file input and a select +choice box will result in a multipart message with +three parts, one for each field. + +The browser does its best to determine the content-type +of the files it sends this way, but you should not +rely on it for determining the contents of the file. +Proper investigation of the contents is recommended. diff --git a/doc/src/guide/multipart_req.ezdoc b/doc/src/guide/multipart_req.ezdoc new file mode 100644 index 0000000..a807e48 --- /dev/null +++ b/doc/src/guide/multipart_req.ezdoc @@ -0,0 +1,115 @@ +::: Multipart requests + +You can read and parse multipart messages using the +Req object directly. + +Cowboy defines two functions that allows you to get +information about each part and read their contents. + +:: Checking the content-type + +While there is a variety of multipart messages, the +most common on the Web is `multipart/form-data`. It's +the type of message being sent when an HTML form +allows uploading files. + +You can quickly figure out if a multipart message +has been sent by parsing the `content-type` header. + +``` erlang +{ok, {<<"multipart">>, <<"form-data">>, _}, Req2} + = cowboy_req:parse_header(<<"content-type">>, Req). +``` + +:: Reading a multipart message + +To read a message you have to iterate over all its +parts. Then, for each part, you can inspect its headers +and read its body. + +``` erlang +multipart(Req) -> + case cowboy_req:part(Req) of + {ok, _Headers, Req2} -> + {ok, _Body, Req3} = cowboy_req:part_body(Req2), + multipart(Req3); + {done, Req2} -> + Req2 + end. +``` + +Parts do not have a size limit. When a part body is +too big, Cowboy will return what it read so far and +allow you to continue if you wish to do so. + +The function `cow_multipart:form_data/1` can be used +to quickly obtain information about a part from a +`multipart/form-data` message. This function will +tell you if the part is for a normal field or if it +is a file being uploaded. + +This can be used for example to allow large part bodies +for files but crash when a normal field is too large. + +``` erlang +multipart(Req) -> + case cowboy_req:part(Req) of + {ok, Headers, Req2} -> + Req4 = case cow_multipart:form_data(Headers) of + {data, _FieldName} -> + {ok, _Body, Req3} = cowboy_req:part_body(Req2), + Req3; + {file, _FieldName, _Filename, _CType, _CTransferEncoding} -> + stream_file(Req2) + end, + multipart(Req4); + {done, Req2} -> + Req2 + end. + +stream_file(Req) -> + case cowboy_req:part_body(Req) of + {ok, _Body, Req2} -> + Req2; + {more, _Body, Req2} -> + stream_file(Req2) + end. +``` + +By default the body chunk Cowboy will return is limited +to 8MB. This can of course be overriden. Both functions +can take a second argument, the same list of options that +will be passed to `cowboy_req:body/2` function. + +:: Skipping unwanted parts + +If you do not want to read a part's body, you can skip it. +Skipping is easy. If you do not call the function to read +the part's body, Cowboy will automatically skip it when +you request the next part. + +The following snippet reads all part headers and skips +all bodies: + +``` erlang +multipart(Req) -> + case cowboy_req:part(Req) of + {ok, _Headers, Req2} -> + multipart(Req2); + {done, Req2} -> + Req2 + end. +``` + +Similarly, if you start reading the body and it ends up +being too big, you can simply continue with the next part, +Cowboy will automatically skip what remains. + +Note that the skipping rate may not be adequate for your +application. If you observe poor performance when skipping, +you might want to consider manually skipping by calling +the `cowboy_req:part_body/1` function directly. + +And if you started reading the message but decide that you +do not need the remaining parts, you can simply stop reading +entirely and Cowboy will automatically figure out what to do. diff --git a/doc/src/guide/req.ezdoc b/doc/src/guide/req.ezdoc new file mode 100644 index 0000000..9501158 --- /dev/null +++ b/doc/src/guide/req.ezdoc @@ -0,0 +1,301 @@ +::: The Req object + +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}`, +`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. + +:: Request + +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. + +The method identifies the action. Standard methods include +GET, HEAD, OPTIONS, PATCH, POST, PUT, DELETE. Method names +are case sensitive. + +``` erlang +{Method, Req2} = cowboy_req:method(Req). +``` + +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. + +``` erlang +{Host, Req2} = cowboy_req:host(Req), +{Port, Req3} = cowboy_req:port(Req2), +{Path, Req4} = cowboy_req:path(Req3). +``` + +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 + +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 + +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 +{HeaderVal, Req2} = cowboy_req:header(<<"content-type">>, Req). +``` + +You can of course set a default in case the header is missing. + +``` erlang +{HeaderVal, Req2} + = cowboy_req:header(<<"content-type">>, Req, <<"text/plain">>). +``` + +And also obtain all headers. + +``` erlang +{AllHeaders, Req2} = 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. + +``` 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, ParsedVal, Req2} + = 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. + +:: Meta + +Cowboy will sometimes associate some meta information with +the request. Built-in meta values are listed in the manual +for their respective modules. + +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). +``` + +You can change the default value if needed. + +``` erlang +{MetaVal, Req2} = cowboy_req:meta(websocket_version, Req, 13). +``` + +You can also define your own meta values. The name must be +an `atom()`. + +``` 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 +{{IP, Port}, Req2} = cowboy_req:peer(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). +``` + +You will still be able to send a reply if needed. diff --git a/doc/src/guide/req_body.ezdoc b/doc/src/guide/req_body.ezdoc new file mode 100644 index 0000000..44f32f8 --- /dev/null +++ b/doc/src/guide/req_body.ezdoc @@ -0,0 +1,158 @@ +::: 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. + +Cowboy provides different ways to read the request body. +You can read it directly, stream it, but also read and +parse in a single call for form urlencoded formats or +multipart. All of these except multipart are covered in +this chapter. Multipart is covered later on in the guide. + +:: 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 + +You can read the whole body directly in one call. + +``` erlang +{ok, Body, Req2} = cowboy_req:body(Req). +``` + +By default, Cowboy will attempt to read up to a +size of 8MB. You can override this limit as needed. + +``` erlang +{ok, Body, Req2} = cowboy_req:body(Req, [{length, 100000000}]). +``` + +You can also disable it. + +``` erlang +{ok, Body, Req2} = cowboy_req:body(Req, [{length, infinity}]). +``` + +It is recommended that you do not disable it for public +facing websites. + +If the body is larger than the limit, then Cowboy will return +a `more` tuple instead, allowing you to stream it if you +would like to. + +:: Streaming the body + +You can stream the request body by chunks. + +Cowboy returns a `more` tuple when there is more body to +be read, and an `ok` tuple for the last chunk. This allows +you to loop over all chunks. + +``` erlang +body_to_console(Req) -> + case cowboy_req:body(Req) of + {ok, Data, Req2} -> + io:format("~s", [Data]), + Req2; + {more, Data, Req2} -> + io:format("~s", [Data]), + body_to_console(Req2) + end. +``` + +You can of course set the `length` option to configure the +size of chunks. + +:: Rate of data transmission + +You can control the rate of data transmission by setting +options when calling body functions. This applies not only +to the functions described in this chapter, but also to +the multipart functions. + +The `read_length` option defines the maximum amount of data +to be received from the socket at once, in bytes. + +The `read_timeout` option defines the time Cowboy waits +before that amount is received, in milliseconds. + +:: Transfer and content decoding + +Cowboy will by default decode the chunked transfer-encoding +if any. It will not decode any content-encoding by default. + +The first time you call a body function you can set the +`transfer_decode` and `content_decode` options. If the body +was already started being read these options are simply +ignored. + +The following example shows how to set both options. + +``` erlang +{ok, Req2} = cowboy_req:body(Req, [ + {transfer_decode, fun transfer_decode/2, TransferState}, + {content_decode, fun content_decode/1} +]). +``` + +:: Reading a form urlencoded body + +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 +64KB when using this function. You can override this limit +by setting the `length` option. + +``` erlang +{ok, KeyValues, Req2} = cowboy_req:body_qs(Req, + [{length, 2000000}]). +``` diff --git a/doc/src/guide/resource_design.ezdoc b/doc/src/guide/resource_design.ezdoc new file mode 100644 index 0000000..67cb09b --- /dev/null +++ b/doc/src/guide/resource_design.ezdoc @@ -0,0 +1,220 @@ +::: Designing a resource handler + +This chapter aims to provide you with a list of questions +you must answer in order to write a good resource handler. +It is meant to be usable as a step by step guide. + +:: The service + +Can the service become unavailable, and when it does, can +we detect it? For example database connectivity problems +may be detected early. We may also have planned outages +of all or parts of the system. Implement the +`service_available` callback. + +What HTTP methods does the service implement? Do we need +more than the standard OPTIONS, HEAD, GET, PUT, POST, +PATCH and DELETE? Are we not using one of those at all? +Implement the `known_methods` callback. + +:: Type of resource handler + +Am I writing a handler for a collection of resources, +or for a single resource? + +The semantics for each of these are quite different. +You should not mix collection and single resource in +the same handler. + +:: Collection handler + +Skip this section if you are not doing a collection. + +Is the collection hardcoded or dynamic? For example +if you use the route `/users` for the collection of +users then the collection is hardcoded; if you use +`/forums/:category` for the collection of threads +then it isn't. When the collection is hardcoded you +can safely assume the resource always exists. + +What methods should I implement? + +OPTIONS is used to get some information about the +collection. It is recommended to allow it even if you +do not implement it, as Cowboy has a default +implementation built-in. + +HEAD and GET are used to retrieve the collection. +If you allow GET, also allow HEAD as there's no extra +work required to make it work. + +POST is used to create a new resource inside the +collection. Creating a resource by using POST on +the collection is useful when resources may be +created before knowing their URI, usually because +parts of it are generated dynamically. A common +case is some kind of auto incremented integer +identifier. + +The next methods are more rarely allowed. + +PUT is used to create a new collection (when +the collection isn't hardcoded), or replace +the entire collection. + +DELETE is used to delete the entire collection. + +PATCH is used to modify the collection using +instructions given in the request body. A PATCH +operation is atomic. The PATCH operation may +be used for such things as reordering; adding, +modifying or deleting parts of the collection. + +:: Single resource handler + +Skip this section if you are doing a collection. + +What methods should I implement? + +OPTIONS is used to get some information about the +resource. It is recommended to allow it even if you +do not implement it, as Cowboy has a default +implementation built-in. + +HEAD and GET are used to retrieve the resource. +If you allow GET, also allow HEAD as there's no extra +work required to make it work. + +POST is used to update the resource. + +PUT is used to create a new resource (when it doesn't +already exist) or replace the resource. + +DELETE is used to delete the resource. + +PATCH is used to modify the resource using +instructions given in the request body. A PATCH +operation is atomic. The PATCH operation may +be used for adding, removing or modifying specific +values in the resource. + +:: The resource + +Following the above discussion, implement the +`allowed_methods` callback. + +Does the resource always exist? If it may not, implement +the `resource_exists` callback. + +Do I need to authenticate the client before they can +access the resource? What authentication mechanisms +should I provide? This may include form-based, token-based +(in the URL or a cookie), HTTP basic, HTTP digest, +SSL certificate or any other form of authentication. +Implement the `is_authorized` callback. + +Do I need fine-grained access control? How do I determine +that they are authorized access? Handle that in your +`is_authorized` callback. + +Can access to a resource be forbidden regardless of access +being authorized? A simple example of that is censorship +of a resource. Implement the `forbidden` callback. + +Is there any constraints on the length of the resource URI? +For example the URI may be used as a key in storage and may +have a limit in length. Implement `uri_too_long`. + +:: Representations + +What media types do I provide? If text based, what charsets +are provided? What languages do I provide? + +Implement the mandatory `content_types_provided`. Prefix +the callbacks with `to_` for clarity. For example `to_html` +or `to_text`. + +Implement the `languages_provided` or `charsets_provided` +callbacks if applicable. + +Is there any other header that may make the representation +of the resource vary? Implement the `variances` callback. + +Depending on your choices for caching content, you may +want to implement one or more of the `generate_etag`, +`last_modified` and `expires` callbacks. + +Do I want the user or user agent to actively choose a +representation available? Send a list of available +representations in the response body and implement +the `multiple_choices` callback. + +:: Redirections + +Do I need to keep track of what resources were deleted? +For example you may have a mechanism where moving a +resource leaves a redirect link to its new location. +Implement the `previously_existed` callback. + +Was the resource moved, and is the move temporary? If +it is explicitly temporary, for example due to maintenance, +implement the `moved_temporarily` callback. Otherwise, +implement the `moved_permanently` callback. + +:: The request + +Do we need to perform extra checks to make sure the request +is valid? Cowboy will do many checks when receiving the +request already, do we need more? Note that this only +applies to the request-line and headers of the request, +and not the body. Implement `malformed_request`. + +May there be a request body? Will I know its size? +What's the maximum size of the request body I'm willing +to accept? Implement `valid_entity_length`. + +Finally, take a look at the sections corresponding to the +methods you are implementing. + +:: OPTIONS method + +Cowboy by default will send back a list of allowed methods. +Do I need to add more information to the response? Implement +the `options` method. + +:: GET and HEAD methods + +If you implement the methods GET and/or HEAD, you must +implement one `ProvideResource` callback for each +content-type returned by the `content_types_provided` +callback. + +:: PUT, POST and PATCH methods + +If you implement the methods PUT, POST and/or PATCH, +you must implement the `content_types_accepted` callback, +and one `AcceptResource` callback for each content-type +it returns. Prefix the `AcceptResource` callback names +with `from_` for clarity. For example `from_html` or +`from_json`. + +Do we want to allow the POST method to create individual +resources directly through their URI (like PUT)? Implement +the `allow_missing_post` callback. It is recommended to +explicitly use PUT in these cases instead. + +May there be conflicts when using PUT to create or replace +a resource? Do we want to make sure that two updates around +the same time are not cancelling one another? Implement the +`is_conflict` callback. + +:: DELETE methods + +If you implement the method DELETE, you must implement +the `delete_resource` callback. + +When `delete_resource` returns, is the resource completely +removed from the server, including from any caching service? +If not, and/or if the deletion is asynchronous and we have +no way of knowing it has been completed yet, implement the +`delete_completed` callback. diff --git a/doc/src/guide/resp.ezdoc b/doc/src/guide/resp.ezdoc new file mode 100644 index 0000000..28f2544 --- /dev/null +++ b/doc/src/guide/resp.ezdoc @@ -0,0 +1,197 @@ +::: 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">>} +], "Hello world!

Hats off!

", 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("Hello world!", Req2), +ok = cowboy_req:chunk("

Hats off!

", 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/doc/src/guide/rest_cond.png b/doc/src/guide/rest_cond.png new file mode 100644 index 0000000..64cda34 Binary files /dev/null and b/doc/src/guide/rest_cond.png differ diff --git a/doc/src/guide/rest_cond.svg b/doc/src/guide/rest_cond.svg new file mode 100644 index 0000000..542ae17 --- /dev/null +++ b/doc/src/guide/rest_cond.svg @@ -0,0 +1,1656 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + some text + some text + has if-unmodified-since? + has if-none-match? + some text + ... + generate_etag + has if-modified-since? + has if-match? + generate_etag + last_modified + + true + match* + true + not modified* + true + no match* + + + + + false + false, orinvalid + modified* + false + + + + + + 412 precondition failed + + middlewares + + + + + + + + + + + + + + + + + no match* + + + + + + date is in the future? + + + + + + + + + + last_modified + + + + + + 304 not modified + + ... + false, orinvalid + match* + + method is GET/HEAD? + true + false + true + false + true + modified* + not modified* + + + + + + generate_etag + + + + + + expires + + diff --git a/doc/src/guide/rest_conneg.png b/doc/src/guide/rest_conneg.png new file mode 100644 index 0000000..65ecdcf Binary files /dev/null and b/doc/src/guide/rest_conneg.png differ diff --git a/doc/src/guide/rest_conneg.svg b/doc/src/guide/rest_conneg.svg new file mode 100644 index 0000000..247567a --- /dev/null +++ b/doc/src/guide/rest_conneg.svg @@ -0,0 +1,1135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + some text + some text + has accept-language? + has accept-charset? + some text + start + charsets_provided + variances + has accept? + content_types_provided + languages_provided + + true + provided* + true + provided* + true + provided* + + + + + false + false + not provided* + false + not provided* + + + + + + 406 not acceptable + + middlewares + + + + + + + + + + + + + + + + + not provided* + + ... + + diff --git a/doc/src/guide/rest_delete.png b/doc/src/guide/rest_delete.png new file mode 100644 index 0000000..56a861c Binary files /dev/null and b/doc/src/guide/rest_delete.png differ diff --git a/doc/src/guide/rest_delete.svg b/doc/src/guide/rest_delete.svg new file mode 100644 index 0000000..2f5513c --- /dev/null +++ b/doc/src/guide/rest_delete.svg @@ -0,0 +1,1718 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + some text + some text + delete_completed + has response body? + some text + conneg + multiple_choices + resource_exists + delete_resource + + true + false + + + + + false + + + + + + middlewares + + + + + true + true + + + + + + cond + + 300 multiple choices + + 200 OK + + + + + + has if-match? + false + + + + + + + + + + previously_existed + + 404 not found + false + + + + + + + + + + moved_permanently + + + + + + 412 precondition failed + true + true* + false + + 301 moved permanently + + + + + + + + + + moved_temporarily + true* + false + + 307 moved temporarily + + 410 gone + + + + + false + + 202 accepted + + 204 no content + true + true + + 500 internal server error + false + true + false + + diff --git a/doc/src/guide/rest_flowcharts.ezdoc b/doc/src/guide/rest_flowcharts.ezdoc new file mode 100644 index 0000000..7da3721 --- /dev/null +++ b/doc/src/guide/rest_flowcharts.ezdoc @@ -0,0 +1,247 @@ +::: REST flowcharts + +This chapter will explain the REST handler state machine through +a number of different diagrams. + +There are four main paths that requests may follow. One for the +method OPTIONS; one for the methods GET and HEAD; one for the +methods PUT, POST and PATCH; and one for the method DELETE. + +All paths start with the "Start" diagram, and all paths excluding +the OPTIONS path go through the "Content negotiation" diagram +and optionally the "Conditional requests" diagram if the resource +exists. + +The red squares refer to another diagram. The light green squares +indicate a response. Other squares may be either a callback or a +question answered by Cowboy itself. Green arrows tend to indicate +the default behavior if the callback is undefined. + +:: Start + +All requests start from here. + +^"REST starting flowchart^!rest_start.png + +A series of callbacks are called in succession to perform +a general checkup of the service, the request line and +request headers. + +The request body, if any, is not expected to have been +received for any of these steps. It is only processed +at the end of the "PUT, POST and PATCH methods" diagram, +when all conditions have been met. + +The `known_methods` and `allowed_methods` callbacks +return a list of methods. Cowboy then checks if the request +method is in the list, and stops otherwise. + +The `is_authorized` callback may be used to check that +access to the resource is authorized. Authentication +may also be performed as needed. When authorization is +denied, the return value from the callback must include +a challenge applicable to the requested resource, which +will be sent back to the client in the www-authenticate +header. + +This diagram is immediately followed by either the +"OPTIONS method" diagram when the request method is +OPTIONS, or the "Content negotiation" diagram otherwise. + +:: OPTIONS method + +This diagram only applies to OPTIONS requests. + +^"REST OPTIONS method flowchart^!rest_options.png + +The `options` callback may be used to add information +about the resource, such as media types or languages +provided; allowed methods; any extra information. A +response body may also be set, although clients should +not be expected to read it. + +If the `options` callback is not defined, Cowboy will +send a response containing the list of allowed methods +by default. + +:: Content negotiation + +This diagram applies to all request methods other than +OPTIONS. It is executed right after the "Start" diagram +is completed. + +^"REST content negotiation flowchart^!rest_conneg.png + +The purpose of these steps is to determine an appropriate +representation to be sent back to the client. + +The request may contain any of the accept header; the +accept-language header; or the accept-charset header. +When present, Cowboy will parse the headers and then +call the corresponding callback to obtain the list +of provided content-type, language or charset for this +resource. It then automatically select the best match +based on the request. + +If a callback is not defined, Cowboy will select the +content-type, language or charset that the client +prefers. + +The `content_types_provided` also returns the name of +a callback for every content-type it accepts. This +callback will only be called at the end of the +"GET and HEAD methods" diagram, when all conditions +have been met. + +The selected content-type, language and charset are +saved as meta values in the Req object. You *should* +use the appropriate representation if you set a +response body manually (alongside an error code, +for example). + +This diagram is immediately followed by +the "GET and HEAD methods" diagram, +the "PUT, POST and PATCH methods" diagram, +or the "DELETE method" diagram, depending on the +method. + +:: GET and HEAD methods + +This diagram only applies to GET and HEAD requests. + +For a description of the `cond` step, please see +the "Conditional requests" diagram. + +^"REST GET/HEAD methods flowchart^!rest_get_head.png + +When the resource exists, and the conditional steps +succeed, the resource can be retrieved. + +Cowboy prepares the response by first retrieving +metadata about the representation, then by calling +the `ProvideResource` callback. This is the callback +you defined for each content-types you returned from +`content_types_provided`. This callback returns the body +that will be sent back to the client, or a fun if the +body must be streamed. + +When the resource does not exist, Cowboy will figure out +whether the resource existed previously, and if so whether +it was moved elsewhere in order to redirect the client to +the new URI. + +The `moved_permanently` and `moved_temporarily` callbacks +must return the new location of the resource if it was in +fact moved. + +:: PUT, POST and PATCH methods + +This diagram only applies to PUT, POST and PATCH requests. + +For a description of the `cond` step, please see +the "Conditional requests" diagram. + +^"REST PUT/POST/PATCH methods flowchart^!rest_put_post_patch.png + +When the resource exists, first the conditional steps +are executed. When that succeeds, and the method is PUT, +Cowboy will call the `is_conflict` callback. This function +can be used to prevent potential race conditions, by locking +the resource for example. + +Then all three methods reach the `content_types_accepted` +step that we will describe in a few paragraphs. + +When the resource does not exist, and the method is PUT, +Cowboy will check for conflicts and then move on to the +`content_types_accepted` step. For other methods, Cowboy +will figure out whether the resource existed previously, +and if so whether it was moved elsewhere. If the resource +is truly non-existent, the method is POST and the call +for `accept_missing_post` returns `true`, then Cowboy will +move on to the `content_types_accepted` step. Otherwise +the request processing ends there. + +The `moved_permanently` and `moved_temporarily` callbacks +must return the new location of the resource if it was in +fact moved. + +The `content_types_accepted` returns a list of +content-types it accepts, but also the name of a callback +for each of them. Cowboy will select the appropriate +callback for processing the request body and call it. + +This callback may return one of three different return +values. + +If an error occurred while processing the request body, +it must return `false` and Cowboy will send an +appropriate error response. + +If the method is POST, then you may return `true` with +an URI of where the resource has been created. This is +especially useful for writing handlers for collections. + +Otherwise, return `true` to indicate success. Cowboy +will select the appropriate response to be sent depending +on whether a resource has been created, rather than +modified, and on the availability of a location header +or a body in the response. + +:: DELETE method + +This diagram only applies to DELETE requests. + +For a description of the `cond` step, please see +the "Conditional requests" diagram. + +^"REST DELETE method flowchart^!rest_delete.png + +When the resource exists, and the conditional steps +succeed, the resource can be deleted. + +Deleting the resource is a two steps process. First +the callback `delete_resource` is executed. Use this +callback to delete the resource. + +Because the resource may be cached, you must also +delete all cached representations of this resource +in the system. This operation may take a while though, +so you may return before it finished. + +Cowboy will then call the `delete_completed` callback. +If you know that the resource has been completely +deleted from your system, including from caches, then +you can return `true`. If any doubts persist, return +`false`. Cowboy will assume `true` by default. + +To finish, Cowboy checks if you set a response body, +and depending on that, sends the appropriate response. + +When the resource does not exist, Cowboy will figure out +whether the resource existed previously, and if so whether +it was moved elsewhere in order to redirect the client to +the new URI. + +The `moved_permanently` and `moved_temporarily` callbacks +must return the new location of the resource if it was in +fact moved. + +:: Conditional requests + +This diagram applies to all request methods other than +OPTIONS. It is executed right after the `resource_exists` +callback, when the resource exists. + +^"REST conditional requests flowchart^!rest_cond.png + +A request becomes conditional when it includes either of +the if-match header; the if-unmodified-since header; the +if-none-match header; or the if-modified-since header. + +If the condition fails, the request ends immediately +without any retrieval or modification of the resource. + +The `generate_etag` and `last_modified` are called as +needed. Cowboy will only call them once and then cache +the results for subsequent use. diff --git a/doc/src/guide/rest_get_head.png b/doc/src/guide/rest_get_head.png new file mode 100644 index 0000000..efee892 Binary files /dev/null and b/doc/src/guide/rest_get_head.png differ diff --git a/doc/src/guide/rest_get_head.svg b/doc/src/guide/rest_get_head.svg new file mode 100644 index 0000000..c78e939 --- /dev/null +++ b/doc/src/guide/rest_get_head.svg @@ -0,0 +1,1523 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + some text + some text + last_modified + ProvideResource + some text + conneg + multiple_choices + resource_exists + generate_etag + expires + + true + false + + + + + false + + + + + + middlewares + + + + + true + true + + + + + + cond + + 300 multiple choices + + 200 OK + + + + + + has if-match? + false + + + + + + + + + + previously_existed + + 404 not found + false + + + + + + + + + + moved_permanently + + + + + + 412 precondition failed + true + true* + false + + 301 moved permanently + + + + + + + + + + moved_temporarily + true* + false + + 307 moved temporarily + + 410 gone + + + + + + diff --git a/doc/src/guide/rest_handlers.ezdoc b/doc/src/guide/rest_handlers.ezdoc new file mode 100644 index 0000000..ee3e5aa --- /dev/null +++ b/doc/src/guide/rest_handlers.ezdoc @@ -0,0 +1,135 @@ +::: REST handlers + +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. + +The REST handler is the recommended way to handle requests. + +:: Initialization + +First, the `init/3` callback is called. This callback is common +to all handlers. To use REST for the current request, this function +must return an `upgrade` tuple. + +``` erlang +init({tcp, http}, Req, Opts) -> + {upgrade, protocol, cowboy_rest}. +``` + +Cowboy will then switch to the REST protocol and start executing +the state machine, starting from `rest_init/2` if it's defined, +and ending with `rest_terminate/2` also if defined. + +:: Methods + +The REST component has code for handling the following HTTP methods: +HEAD, GET, POST, PATCH, PUT, DELETE and OPTIONS. + +Other methods can be accepted, however they have no specific callback +defined for them at this time. + +:: Callbacks + +All callbacks are optional. Some may become mandatory depending +on what other defined callbacks return. The various flowcharts +in the next chapter should be a useful to determine which callbacks +you need. + +When the request starts being processed, Cowboy will call the +`rest_init/2` function if it is defined, with the Req object +and the handler options as arguments. This function must return +`{ok, Req, State}` where `State` is the handler's state that all +subsequent callbacks will receive. + +At the end of every request, the special callback `rest_terminate/2` +will be called if it is defined. It cannot be used to send a reply, +and must always return `ok`. + +All other callbacks are resource callbacks. They all take two +arguments, the Req object and the State, and return a three-element +tuple of the form `{Value, Req, State}`. + +The following table summarizes the callbacks and their default values. +If the callback isn't defined, then the default value will be used. +Please look at the flowcharts to find out the result of each return +value. + +All callbacks can also return `{halt, Req, State}` to stop execution +of the request, at which point `rest_terminate/2` will be called. + +In the following table, "skip" means the callback is entirely skipped +if it is undefined, moving directly to the next step. Similarly, +"none" means there is no default value for this callback. + +|| Callback name Default value +| +| allowed_methods `[<<"GET">>, <<"HEAD">>, <<"OPTIONS">>]` +| allow_missing_post `true` +| charsets_provided skip +| content_types_accepted none +| content_types_provided `[{{<<"text">>, <<"html">>, '*'}, to_html}] ` +| delete_completed `true` +| delete_resource `false` +| expires `undefined` +| forbidden `false` +| generate_etag `undefined` +| is_authorized `true` +| is_conflict `false` +| known_content_type `true` +| known_methods `[<<"GET">>, <<"HEAD">>, <<"POST">>, <<"PUT">>, <<"PATCH">>, <<"DELETE">>, <<"OPTIONS">>]` +| languages_provided skip +| last_modified `undefined` +| malformed_request `false` +| moved_permanently `false` +| moved_temporarily `false` +| multiple_choices `false` +| options `ok` +| previously_existed `false` +| resource_exists `true` +| service_available `true` +| uri_too_long `false` +| valid_content_headers `true` +| valid_entity_length `true` +| variances `[]` + +As you can see, Cowboy tries to move on with the request whenever +possible by using well thought out default values. + +In addition to these, there can be any number of user-defined +callbacks that are specified through `content_types_accepted/2` +and `content_types_provided/2`. They can take any name, however +it is recommended to use a separate prefix for the callbacks of +each function. For example, `from_html` and `to_html` indicate +in the first case that we're accepting a resource given as HTML, +and in the second case that we send one as HTML. + +:: Meta data + +Cowboy will set informative meta values at various points of the +execution. You can retrieve them using `cowboy_req:meta/{2,3}`. +The values are defined in the following table. + +|| Meta key Details +| +| media_type The content-type negotiated for the response entity. +| language The language negotiated for the response entity. +| charset The charset negotiated for the response entity. + +They can be used to send a proper body with the response to a +request that used a method other than HEAD or GET. + +:: Response headers + +Cowboy will set response headers automatically over the execution +of the REST code. They are listed in the following table. + +|| Header name Details +| +| content-language Language used in the response body +| content-type Media type and charset of the response body +| etag Etag of the resource +| expires Expiration date of the resource +| last-modified Last modification date for the resource +| location Relative or absolute URI to the requested resource +| vary List of headers that may change the representation of the resource diff --git a/doc/src/guide/rest_options.png b/doc/src/guide/rest_options.png new file mode 100644 index 0000000..90fd6f0 Binary files /dev/null and b/doc/src/guide/rest_options.png differ diff --git a/doc/src/guide/rest_options.svg b/doc/src/guide/rest_options.svg new file mode 100644 index 0000000..496c050 --- /dev/null +++ b/doc/src/guide/rest_options.svg @@ -0,0 +1,387 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + some text + some text + some text + start + options + 200 OK + + + + + + + middlewares + + diff --git a/doc/src/guide/rest_principles.ezdoc b/doc/src/guide/rest_principles.ezdoc new file mode 100644 index 0000000..1d54594 --- /dev/null +++ b/doc/src/guide/rest_principles.ezdoc @@ -0,0 +1,159 @@ +::: REST principles + +This chapter will attempt to define the concepts behind REST +and explain what makes a service RESTful. + +REST is often confused with performing a distinct operation +depending on the HTTP method, while using more than the GET +and POST methods. That's highly misguided at best. + +We will first attempt to define REST and will look at what +it means in the context of HTTP and the Web. +For a more in-depth explanation of REST, you can read +^"Roy T. Fielding's dissertation^http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm +as it does a great job explaining where it comes from and +what it achieves. + +:: REST architecture + +REST is a *client-server* architecture. The client and the server +both have a different set of concerns. The server stores and/or +manipulates information and makes it available to the user in +an efficient manner. The client takes that information and +displays it to the user and/or uses it to perform subsequent +requests for information. This separation of concerns allows both +the client and the server to evolve independently as it only +requires that the interface stays the same. + +REST is *stateless*. That means the communication between the +client and the server always contains all the information needed +to perform the request. There is no session state in the server, +it is kept entirely on the client's side. If access to a resource +requires authentication, then the client needs to authenticate +itself with every request. + +REST is *cacheable*. The client, the server and any intermediary +components can all cache resources in order to improve performance. + +REST provides a *uniform interface* between components. This +simplifies the architecture, as all components follow the same +rules to speak to one another. It also makes it easier to understand +the interactions between the different components of the system. +A number of constraints are required to achieve this. They are +covered in the rest of the chapter. + +REST is a *layered system*. Individual components cannot see +beyond the immediate layer with which they are interacting. This +means that a client connecting to an intermediate component, like +a proxy, has no knowledge of what lies beyond. This allows +components to be independent and thus easily replaceable or +extendable. + +REST optionally provides *code on demand*. Code may be downloaded +to extend client functionality. This is optional however because +the client may not be able to download or run this code, and so +a REST component cannot rely on it being executed. + +:: Resources and resource identifiers + +A resource is an abstract concept. In a REST system, any information +that can be named may be a resource. This includes documents, images, +a collection of resources and any other information. Any information +that can be the target of an hypertext link can be a resource. + +A resource is a conceptual mapping to a set of entities. The set of +entities evolves over time; a resource doesn't. For example a resource +can map to "users who have logged in this past month" and another +to "all users". At some point in time they may map to the same set of +entities, because all users logged in this past month. But they are +still different resources. Similarly, if nobody logged in recently, +then the first resource may map to the empty set. This resource exists +regardless of the information it maps to. + +Resources are identified by uniform resource identifiers, also known +as URIs. Sometimes internationalized resource identifiers, or IRIs, +may also be used, but these can be directly translated into a URI. + +In practice we will identify two kinds of resources. Individual +resources map to a set of one element, for example "user Joe". +Collection of resources map to a set of 0 to N elements, +for example "all users". + +:: Resource representations + +The representation of a resource is a sequence of bytes associated +with metadata. + +The metadata comes as a list of key-value pairs, where the name +corresponds to a standard that defines the value's structure and +semantics. With HTTP, the metadata comes in the form of request +or response headers. The headers' structure and semantics are well +defined in the HTTP standard. Metadata includes representation +metadata, resource metadata and control data. + +The representation metadata gives information about the +representation, such as its media type, the date of last +modification, or even a checksum. + +Resource metadata could be link to related resources or +information about additional representations of the resource. + +Control data allows parameterizing the request or response. +For example, we may only want the representation returned if +it is more recent than the one we have in cache. Similarly, +we may want to instruct the client about how it should cache +the representation. This isn't restricted to caching. We may +for example want to store a new representation of a resource +only if it wasn't modified since we first retrieved it. + +The data format of a representation is also known as the media +type. Some media types are intended for direct rendering to the +user, while others are intended for automated processing. The +media type is a key component of the REST architecture. + +:: Self-descriptive messages + +Messages must be self-descriptive. That means that the data +format of a representation must always come with its media +type (and similarly requesting a resource involves choosing +the media type of the representation returned). If you are +sending HTML, then you must say it is HTML by sending the +media type with the representation. In HTTP this is done +using the content-type header. + +The media type is often an IANA registered media type, like +`text/html` or `image/png`, but does not need to be. Exactly +two things are important for respecting this constraint: that +the media type is well specified, and that the sender and +recipient agree about what the media type refers to. + +This means that you can create your own media types, like +`application/x-mine`, and that as long as you write the +specifications for it and that both endpoints agree about +it then the constraint is respected. + +:: Hypermedia as the engine of application state + +The last constraint is generally where services that claim +to be RESTful fail. Interactions with a server must be +entirely driven by hypermedia. The client does not need +any prior knowledge of the service in order to use it, +other than an entry point and of course basic understanding +of the media type of the representations, at the very least +enough to find and identify hyperlinks and link relations. + +To give a simple example, if your service only works with +the `application/json` media type then this constraint +cannot be respected (as there are no concept of links in +JSON) and thus your service isn't RESTful. This is the case +for the majority of self-proclaimed REST services. + +On the other hand if you create a JSON based media type +that has a concept of links and link relations, then +your service might be RESTful. + +Respecting this constraint means that the entirety of the +service becomes self-discoverable, not only the resources +in it, but also the operations you can perform on it. This +makes clients very thin as there is no need to implement +anything specific to the service to operate on it. diff --git a/doc/src/guide/rest_put_post_patch.png b/doc/src/guide/rest_put_post_patch.png new file mode 100644 index 0000000..4afca9e Binary files /dev/null and b/doc/src/guide/rest_put_post_patch.png differ diff --git a/doc/src/guide/rest_put_post_patch.svg b/doc/src/guide/rest_put_post_patch.svg new file mode 100644 index 0000000..263cc94 --- /dev/null +++ b/doc/src/guide/rest_put_post_patch.svg @@ -0,0 +1,2856 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + some text + some text + some text + conneg + resource_exists + + true + + + + + false + + + + + + middlewares + + + + + true + + + + + + + + + + + + + + + cond + + + + + + has if-match? + false + + + + + + method is POST/PATCH? + true + + + + + + + + + + + + + + + + + + + method is POST? + + 412 precondition failed + + + + + + + + + + + + + + previously_existed + + + + + + 404 not found + false + + + + + + + + + true* + false + + 301 moved permanently + + + + + + + + + + moved_temporarily + true* + false + + 307 moved temporarily + + 400 bad request + + + + + true + + + + + + allow_missing_post + + method is POST? + allow_missing_post + + + + + + method is PUT? + + + + + + + + + + is_conflict + true + + 409 conflict + + + + + + content_types_accepted + + AcceptResource + + + + + + + + + + new resource? + + + + + + + + + + new resource? + + 201 created + + 303 see other + + + + + + + + + + has resp location? + + + + + + + + + + + has resp body? + + + + + + + + + + multiple_choices + false + + 300 multiple choices + + 200 OK + 204 no content + true + + + + + true + + moved_permanently + + 410 gone + false + true + false + false + false + false + + + + + true + + + + + true, URI* + + + + + true + false + true + true + false + true + false + true + false + false + false + + + + + + + + true + + + + + false + true + + diff --git a/doc/src/guide/rest_start.png b/doc/src/guide/rest_start.png new file mode 100644 index 0000000..7f26464 Binary files /dev/null and b/doc/src/guide/rest_start.png differ diff --git a/doc/src/guide/rest_start.svg b/doc/src/guide/rest_start.svg new file mode 100644 index 0000000..d75e1cc --- /dev/null +++ b/doc/src/guide/rest_start.svg @@ -0,0 +1,1468 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + some text + some text + uri_too_long + malformed_request + some text + init + is_authorized + forbidden + valid_content_headers + known_content_type + valid_entity_length + ... + service_available + known_methods + allowed_methods + + true + known* + false + allowed* + false + true + false + true + true + true + + + + + false + unknown* + true + unallowed* + true + false* + true + false + false + false + + 503 service unavailable + + + + + + + + + + 501 not implemented + 414 request URI too long + 405 method not allowed + 400 bad request + 401 unauthorized + 403 forbidden + 501 not implemented + 415 unsupported media type + 413 request entity too large + + middlewares + + diff --git a/doc/src/guide/routing.ezdoc b/doc/src/guide/routing.ezdoc new file mode 100644 index 0000000..e7b43f2 --- /dev/null +++ b/doc/src/guide/routing.ezdoc @@ -0,0 +1,249 @@ +::: Routing + +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 +Dispatch = cowboy_router:compile([ + %% {HostMatch, list({PathMatch, Handler, Opts})} + {'_', [{'_', my_handler, []}]} +]), +%% Name, NbAcceptors, TransOpts, ProtoOpts +cowboy:start_http(my_http_listener, 100, + [{port, 8080}], + [{env, [{dispatch, Dispatch}]}] +). +``` + +Note that this function will return `{error, badarg}` if the structure +given is incorrect. + +:: Live update + +You can use the `cowboy:set_env/3` function for updating the dispatch +list used by routing. This will apply to all new connections accepted +by the listener. + +``` erlang +cowboy:set_env(my_http_listener, dispatch, + cowboy_router:compile(Dispatch)). +``` + +Note that you need to compile the routes before updating. diff --git a/doc/src/guide/static_handlers.ezdoc b/doc/src/guide/static_handlers.ezdoc new file mode 100644 index 0000000..f5eaac3 --- /dev/null +++ b/doc/src/guide/static_handlers.ezdoc @@ -0,0 +1,167 @@ +::: Static handler + +The static handler is a built-in REST handler for serving files. +It is available as a convenience and provides a quick solution +for serving files during development. + +For systems in production, consider using one of the many +Content Distribution Network (CDN) available on the market, +as they are the best solution for serving files. They are +covered in the next chapter. If you decide against using a +CDN solution, then please look at the chapter after that, +as it explains how to efficiently serve static files on +your own. + +The static handler can serve either one file or all files +from a given directory. It can also send etag headers for +client-side caching. + +To use the static file handler, simply add routes for it +with the appropriate options. + +:: Serve one file + +You can use the static handler to serve one specific file +from an application's private directory. This is particularly +useful to serve an `index.html` file when the client requests +the `/` path, for example. The path configured is relative +to the given application's private directory. + +The following rule will serve the file `static/index.html` +from the application `my_app`'s priv directory whenever the +path `/` is accessed. + +``` erlang +{"/", cowboy_static, {priv_file, my_app, "static/index.html"}} +``` + +You can also specify the absolute path to a file, or the +path to the file relative to the current directory. + +``` erlang +{"/", cowboy_static, {file, "/var/www/index.html"}} +``` + +:: Serve all files from a directory + +You can also use the static handler to serve all files that +can be found in the configured directory. The handler will +use the `path_info` information to resolve the file location, +which means that your route must end with a `[...]` pattern +for it to work. All files are served, including the ones that +may be found in subfolders. + +You can specify the directory relative to an application's +private directory. + +The following rule will serve any file found in the application +`my_app`'s priv directory inside the `static/assets` folder +whenever the requested path begins with `/assets/`. + +``` erlang +{"/assets/[...]", cowboy_static, {priv_dir, my_app, "static/assets"}} +``` + +You can also specify the absolute path to the directory or +set it relative to the current directory. + +``` erlang +{"/assets/[...]", cowboy_static, {dir, "/var/www/assets"}} +``` + +:: Customize the mimetype detection + +By default, Cowboy will attempt to recognize the mimetype +of your static files by looking at the extension. + +You can override the function that figures out the mimetype +of the static files. It can be useful when Cowboy is missing +a mimetype you need to handle, or when you want to reduce +the list to make lookups faster. You can also give a +hard-coded mimetype that will be used unconditionally. + +Cowboy comes with two functions built-in. The default +function only handles common file types used when building +Web applications. The other function is an extensive list +of hundreds of mimetypes that should cover almost any need +you may have. You can of course create your own function. + +To use the default function, you should not have to configure +anything, as it is the default. If you insist, though, the +following will do the job. + +``` erlang +{"/assets/[...]", cowboy_static, {priv_dir, my_app, "static/assets", + [{mimetypes, cow_mimetypes, web}]}} +``` + +As you can see, there is an optional field that may contain +a list of less used options, like mimetypes or etag. All option +types have this optional field. + +To use the function that will detect almost any mimetype, +the following configuration will do. + +``` erlang +{"/assets/[...]", cowboy_static, {priv_dir, my_app, "static/assets", + [{mimetypes, cow_mimetypes, all}]}} +``` + +You probably noticed the pattern by now. The configuration +expects a module and a function name, so you can use any +of your own functions instead. + +``` erlang +{"/assets/[...]", cowboy_static, {priv_dir, my_app, "static/assets", + [{mimetypes, Module, Function}]}} +``` + +The function that performs the mimetype detection receives +a single argument that is the path to the file on disk. It +is recommended to return the mimetype in tuple form, although +a binary string is also allowed (but will require extra +processing). If the function can't figure out the mimetype, +then it should return `{<<"application">>, <<"octet-stream">>, []}`. + +When the static handler fails to find the extension in the +list, it will send the file as `application/octet-stream`. +A browser receiving such file will attempt to download it +directly to disk. + +Finally, the mimetype can be hard-coded for all files. +This is especially useful in combination with the `file` +and `priv_file` options as it avoids needless computation. + +``` erlang +{"/", cowboy_static, {priv_file, my_app, "static/index.html", + [{mimetypes, {<<"text">>, <<"html">>, []}}]}} +``` + +:: Generate an etag + +By default, the static handler will generate an etag header +value based on the size and modified time. This solution +can not be applied to all systems though. It would perform +rather poorly over a cluster of nodes, for example, as the +file metadata will vary from server to server, giving a +different etag on each server. + +You can however change the way the etag is calculated. + +``` erlang +{"/assets/[...]", cowboy_static, {priv_dir, my_app, "static/assets", + [{etag, Module, Function}]}} +``` + +This function will receive three arguments: the path to the +file on disk, the size of the file and the last modification +time. In a distributed setup, you would typically use the +file path to retrieve an etag value that is identical across +all your servers. + +You can also completely disable etag handling. + +``` erlang +{"/assets/[...]", cowboy_static, {priv_dir, my_app, "static/assets", + [{etag, false}]}} +``` diff --git a/doc/src/guide/upgrade_protocol.ezdoc b/doc/src/guide/upgrade_protocol.ezdoc new file mode 100644 index 0000000..eebce74 --- /dev/null +++ b/doc/src/guide/upgrade_protocol.ezdoc @@ -0,0 +1,36 @@ +::: Protocol upgrades + +Cowboy features many different handlers, each for different purposes. +All handlers have a common entry point: the `init/3` function. + +The default handler type is the simple HTTP handler. + +To switch to a different protocol, you must perform a protocol +upgrade. This is what is done for Websocket and REST and is +explained in details in the respective chapters. + +You can also create your own protocol on top of Cowboy and use +the protocol upgrade mechanism to switch to it. + +For example, if you create the `my_protocol` module implementing +the `cowboy_sub_protocol` behavior, then you can upgrade to it +by simply returning the module name from `init/3`. + +``` erlang +init(_, _, _Opts) -> + {upgrade, protocol, my_protocol}. +``` + +The `cowboy_sub_protocol` behavior only requires one callback, +`upgrade/4`. It receives the Req object, the middleware environment, +and the handler and options for this request. This is the same +module as the `init/3` function and the same options that were +passed to it. + +``` erlang +upgrade(Req, Env, Handler, HandlerOpts) -> + %% ... +``` + +This callback is expected to behave like a middleware. Please +see the corresponding chapter for more information. diff --git a/doc/src/guide/ws_handlers.ezdoc b/doc/src/guide/ws_handlers.ezdoc new file mode 100644 index 0000000..0de7910 --- /dev/null +++ b/doc/src/guide/ws_handlers.ezdoc @@ -0,0 +1,222 @@ +::: Handling Websocket connections + +A special handler is required for handling Websocket connections. +Websocket handlers allow you to initialize the connection, +handle incoming frames from the socket, handle incoming Erlang +messages and then clean up on termination. + +Websocket handlers essentially act as a bridge between the client +and the Erlang system. They will typically do little more than +socket communication and decoding/encoding of frames. + +:: Initialization + +First, the `init/3` callback is called. This callback is common +to all handlers. To establish a Websocket connection, this function +must return an `upgrade` tuple. + +``` erlang +init(_, Req, Opts) -> + {upgrade, protocol, cowboy_websocket}. +``` + +It is also possible to return an update Req object and options +using the longer form of this tuple. + +``` erlang +init(_Type, Req, Opts) -> + {upgrade, protocol, cowboy_websocket, Req, Opts}. +``` + +Upon receiving this tuple, Cowboy will switch to the code +that handles Websocket connections. It does not immediately +perform the handshake however. First, it calls the `websocket_init/3` +callback. + +This function must be used to initialize the state, and can +also be used to register the process, start a timer, etc. +As long as the function returns an `ok` tuple, then Cowboy +performs the Websocket handshake. + +``` erlang +websocket_init(_Type, Req, _Opts) -> + {ok, Req, #state{}}. +``` + +A `shutdown` tuple can be returned to refuse to perform the +handshake. When doing so, Cowboy will send a `400 Bad Request` +response to the client and close the connection. + +``` erlang +websocket_init(_Type, Req, _Opts) -> + {shutdown, Req}. +``` + +It is also possible to perform a `cowboy_req:reply/{2,3,4}` +before returning a `shutdown` tuple, allowing you to override +the response sent back to the client. + +Note that browser support for handling Websocket connection +failures may vary. + +If the sec-websocket-protocol header was sent with the request +for establishing a Websocket connection, then the Websocket +handler *must* select one of these subprotocol and send it +back to the client, otherwise the client might decide to close +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} -> + {ok, Req, #state{}}; + {ok, Subprotocols, Req2} -> + case lists:keymember(<<"mychat2">>, 1, Subprotocols) of + true -> + Req3 = cowboy:set_resp_header(<<"sec-websocket-protocol">>, + <<"mychat2">>, Req2), + {ok, Req3, #state{}}; + false -> + {shutdown, Req2} + end + end. +``` + +It is not recommended to wait too long inside the `websocket_init/3` +function. Any extra initialization may be done after returning by +sending yourself a message before doing anything. Any message sent +to `self()` from `websocket_init/3` is guaranteed to arrive before +any frames from the client. + +It is also very easy to ensure that this message arrives before +any message from other processes by sending it before registering +or enabling timers. + +``` erlang +websocket_init(_Type, Req, _Opts) -> + self() ! post_init, + %% Register process here... + {ok, Req, #state{}}. + +websocket_info(post_init, Req, State) -> + %% Perform post_init initialization here... + {ok, Req, State}. +``` + +:: Handling frames from the client + +Cowboy will call `websocket_handle/3` whenever a text, binary, +ping or pong frame arrives from the client. Note that in the +case of ping and pong frames, no action is expected as Cowboy +automatically replies to ping frames. + +The handler can decide to send frames to the socket, shutdown +or just continue without sending anything. + +The following snippet echoes back any text frame received and +ignores all others. + +``` erlang +websocket_handle(Frame = {text, _}, Req, State) -> + {reply, Frame, Req, State}; +websocket_handle(_Frame, Req, State) -> + {ok, Req, State}. +``` + +:: Handling Erlang messages + +Cowboy will call `websocket_info/3` whenever an Erlang message +arrives. + +The handler can decide to send frames to the socket, shutdown +or just continue without sending anything. + +The following snippet forwards any `log` message to the socket +and ignores all others. + +``` erlang +websocket_info({log, Text}, Req, State) -> + {reply, {text, Text}, Req, State}; +websocket_info(_Info, Req, State) -> + {ok, Req, State}. +``` + +:: Sending frames to the socket + +Cowboy allows sending either a single frame or a list of +frames to the socket. Any frame can be sent: text, binary, ping, +pong or close frames. + +The following example sends three frames using a single `reply` +tuple. + +``` erlang +websocket_info(hello_world, Req, State) -> + {reply, [ + {text, "Hello"}, + {text, <<"world!">>}, + {binary, <<0:8000>>} + ], Req, State}; +%% More websocket_info/3 clauses here... +``` + +Note that the payload for text and binary frames is of type +`iodata()`, meaning it can be either a `binary()` or an +`iolist()`. + +Sending a `close` frame will immediately initiate the closing +of the Websocket connection. Be aware that any additional +frames sent by the client or any Erlang messages waiting to +be received will not be processed. Also note that when replying +a list of frames that includes close, any frame found after the +close frame will not be sent. + +:: Ping and timeout + +The biggest performance improvement you can do when dealing +with a huge number of Websocket connections is to reduce the +number of timers that are started on the server. A common use +of timers when dealing with connections is for sending a ping +every once in a while. This should be done exclusively on the +client side. Indeed, a server handling one million Websocket +connections will perform a lot better when it doesn't have to +handle one million extra timers too! + +Cowboy will automatically respond to ping frames sent by the +client. It will still forward the frame to the handler for +informative purpose, but no further action is required. + +Cowboy can be configured to automatically close the Websocket +connection when no data arrives on the socket. It is highly +recommended to configure a timeout for it, as otherwise you +may end up with zombie "half-connected" sockets that may +leave the process alive forever. + +A good timeout value is 60 seconds. + +``` erlang +websocket_init(_Type, Req, _Opts) -> + {ok, Req, #state{}, 60000}. +``` + +This value cannot be changed once it is set. It defaults to +`infinity`. + +:: Hibernate + +Most tuples returned from handler callbacks can include an +extra value `hibernate`. After doing any necessary operations +following the return of the callback, Cowboy will hibernate +the process. + +It is highly recommended to hibernate processes that do not +handle much traffic. It is a good idea to hibernate all +connections by default and investigate only when you start +noticing increased CPU usage. + +:: Supporting older browsers + +Unfortunately Websocket is a relatively recent technology, +which means that not all browsers support it. A library like +^"Bullet^https://github.com/extend/bullet^ can be used to +emulate Websocket connections on older browsers. diff --git a/doc/src/guide/ws_protocol.ezdoc b/doc/src/guide/ws_protocol.ezdoc new file mode 100644 index 0000000..15aea2c --- /dev/null +++ b/doc/src/guide/ws_protocol.ezdoc @@ -0,0 +1,42 @@ +::: The Websocket protocol + +This chapter explains what Websocket is and why it is +a vital component of soft realtime Web applications. + +:: Description + +Websocket is an extension to HTTP that emulates plain TCP +connections between the client, typically a Web browser, +and the server. It uses the HTTP Upgrade mechanism to +establish the connection. + +Websocket connections are asynchronous, unlike HTTP. This +means that not only can the client send frames to the server +at any time, but the server can also send frames to the client +without the client initiating anything other than the +Websocket connection itself. This allows the server to push +data to the client directly. + +Websocket is an IETF standard. Cowboy supports the standard +and all drafts that were previously implemented by browsers, +excluding the initial flawed draft sometimes known as +"version 0". + +:: Implementation + +Cowboy implements Websocket as a protocol upgrade. Once the +upgrade is performed from the `init/3` callback, Cowboy +switches to Websocket. Please consult the next chapter for +more information on initiating and handling Websocket +connections. + +The implementation of Websocket in Cowboy is validated using +the Autobahn test suite, which is an extensive suite of tests +covering all aspects of the protocol. Cowboy passes the +suite with 100% success, including all optional tests. + +Cowboy's Websocket implementation also includes the +x-webkit-deflate-frame compression draft which is being used +by some browsers to reduce the size of data being transmitted. +Cowboy will automatically use compression as long as the +`compress` protocol option is set when starting the listener. diff --git a/doc/src/manual/cowboy.ezdoc b/doc/src/manual/cowboy.ezdoc new file mode 100644 index 0000000..a207afe --- /dev/null +++ b/doc/src/manual/cowboy.ezdoc @@ -0,0 +1,101 @@ +::: cowboy + +The `cowboy` module provides convenience functions for +manipulating Ranch listeners. + +:: Types + +: http_headers() = [{binary(), iodata()}] + +HTTP headers as a list of key/values. + +: http_status() = non_neg_integer() | binary() + +HTTP status. + +A binary status can be used to set a custom message. + +: http_version() = 'HTTP/1.1' | 'HTTP/1.0' + +HTTP version. + +: onrequest_fun() = fun((cowboy_req:req()) -> cowboy_req:req()) + +Fun called immediately after receiving a request. + +It can perform any operation on the Req object, including +reading the request body or replying. If a reply is sent, +the processing of the request ends here, before any middleware +is executed. + +: onresponse_fun() = fun((http_status(), http_headers(), + iodata(), cowboy_req:req()) -> cowboy_req:req()) + +Fun called immediately before sending the response. + +It can perform any operation on the Req object, including +reading the request body or replying. If a reply is sent, it +overrides the reply initially sent. The callback will not be +called again for the new reply. + +:: Exports + +: start_http(Ref, NbAcceptors, TransOpts, ProtoOpts) -> {ok, pid()} + +Types: + +* Ref = ranch:ref() +* NbAcceptors = non_neg_integer() +* TransOpts = ranch_tcp:opts() +* ProtoOpts = cowboy_protocol:opts() + +Start listening for HTTP connections. Returns the pid for this +listener's supervisor. + +: start_https(Ref, NbAcceptors, TransOpts, ProtoOpts) -> {ok, pid()} + +Types: + +* Ref = ranch:ref() +* NbAcceptors = non_neg_integer() +* TransOpts = ranch_ssl:opts() +* ProtoOpts = cowboy_protocol:opts() + +Start listening for HTTPS connections. Returns the pid for this +listener's supervisor. + +: start_spdy(Ref, NbAcceptors, TransOpts, ProtoOpts) -> {ok, pid()} + +Types: + +* Ref = ranch:ref() +* NbAcceptors = non_neg_integer() +* TransOpts = ranch_ssl:opts() +* ProtoOpts = cowboy_spdy:opts() + +Start listening for SPDY connections. Returns the pid for this +listener's supervisor. + +: stop_listener(Ref) -> ok | {error, not_found} + +Types: + +* Ref = ranch:ref() + +Stop a previously started listener. + +: set_env(Ref, Name, Value) -> ok + +Types: + +* Ref = ranch:ref() +* Name = atom() +* Value = any() + +Set or update an environment value for an already running listener. +This will take effect on all subsequent connections. + +:: See also + +The ^"Ranch guide^http://ninenines.eu/docs/en/ranch/HEAD/guide +provides detailed information about how listeners work. diff --git a/doc/src/manual/cowboy_app.ezdoc b/doc/src/manual/cowboy_app.ezdoc new file mode 100644 index 0000000..2e2b877 --- /dev/null +++ b/doc/src/manual/cowboy_app.ezdoc @@ -0,0 +1,23 @@ +::: The Cowboy Application + +Small, fast, modular HTTP server. + +:: Dependencies + +The `cowboy` application uses the Erlang applications `ranch` +for listening and accepting TCP connections, `crypto` for +establishing Websocket connections, and `cowlib` for parsing and +building messages for Web protocols. These dependencies must +be loaded for the `cowboy` application to work. In an embedded +environment this means that they need to be started with the +`application:start/{1,2}` function before the `cowboy` +application is started. + +The `cowboy` application also uses the Erlang applications +`asn1`, `public_key` and `ssl` when listening for HTTPS connections. +These are started automatically if they weren't before. + +:: Environment + +The `cowboy` application does not define any application +environment configuration parameters. diff --git a/doc/src/manual/cowboy_handler.ezdoc b/doc/src/manual/cowboy_handler.ezdoc new file mode 100644 index 0000000..0495f28 --- /dev/null +++ b/doc/src/manual/cowboy_handler.ezdoc @@ -0,0 +1,24 @@ +::: cowboy_handler + +The `cowboy_handler` middleware executes the handler passed +through the environment values `handler` and `handler_opts`, +and adds the result of this execution to the environment as +the value `result`, indicating that the request has been +handled and received a response. + +Environment input: + +* handler = module() +* handler_opts = any() + +Environment output: + +* result = ok + +:: Types + +None. + +:: Exports + +None. diff --git a/doc/src/manual/cowboy_http_handler.ezdoc b/doc/src/manual/cowboy_http_handler.ezdoc new file mode 100644 index 0000000..6776598 --- /dev/null +++ b/doc/src/manual/cowboy_http_handler.ezdoc @@ -0,0 +1,57 @@ +::: cowboy_http_handler + +The `cowboy_http_handler` behaviour defines the interface used +by plain HTTP handlers. + +Unless noted otherwise, the callbacks will be executed sequentially. + +:: Types + +None. + +:: Callbacks + +: init({TransportName, ProtocolName}, Req, Opts) + -> {ok, Req, State} | {shutdown, Req, State} + +Types: + +* TransportName = tcp | ssl | atom() +* ProtocolName = http | atom() +* Req = cowboy_req:req() +* Opts = any() +* State = any() + +Initialize the state for this request. + +The `shutdown` return value can be used to skip the `handle/2` +call entirely. + +: handle(Req, State) -> {ok, Req, State} + +Types: + +* Req = cowboy_req:req() +* State = any() + +Handle the request. + +This callback is where the request is handled and a response +should be sent. If a response is not sent, Cowboy will send +a `204 No Content` response automatically. + +: terminate(Reason, Req, State) -> ok + +Types: + +* Reason = {normal, shutdown} | {error, atom()} +* Req = cowboy_req:req() +* State = any() + +Perform any necessary cleanup of the state. + +This callback should release any resource currently in use, +clear any active timer and reset the process to its original +state, as it might be reused for future requests sent on the +same connection. Typical plain HTTP handlers rarely need to +use it. diff --git a/doc/src/manual/cowboy_loop_handler.ezdoc b/doc/src/manual/cowboy_loop_handler.ezdoc new file mode 100644 index 0000000..0811a9a --- /dev/null +++ b/doc/src/manual/cowboy_loop_handler.ezdoc @@ -0,0 +1,91 @@ +::: cowboy_loop_handler + +The `cowboy_loop_handler` behaviour defines the interface used +by HTTP handlers that do not send a response directly, instead +requiring a receive loop to process Erlang messages. + +This interface is best fit for long-polling types of requests. + +The `init/3` callback will always be called, followed by zero +or more calls to `info/3`. The `terminate/3` callback will +always be called last. + +:: Types + +None. + +:: Callbacks + +: init({TransportName, ProtocolName}, Req, Opts) + -> {loop, Req, State} + | {loop, Req, State, hibernate} + | {loop, Req, State, Timeout} + | {loop, Req, State, Timeout, hibernate} + | {shutdown, Req, State} + +Types: + +* TransportName = tcp | ssl | atom() +* ProtocolName = http | atom() +* Req = cowboy_req:req() +* Opts = any() +* State = any() +* Timeout = timeout() + +Initialize the state for this request. + +This callback will typically be used to register this process +to an event manager or a message queue in order to receive +the messages the handler wants to process. + +The receive loop will run for a duration of up to `Timeout` +milliseconds after it last received data from the socket, +at which point it will stop and send a `204 No Content` reply. +By default this value is set to `infinity`. It is recommended +to either set this value or ensure by any other mechanism +that the handler will be closed after a certain period of +inactivity. + +The `hibernate` option will hibernate the process until it +starts receiving messages. + +The `shutdown` return value can be used to skip the receive +loop entirely. + +: info(Info, Req, State) -> {ok, Req, State} | {loop, Req, State} + | {loop, Req, State, hibernate} + +Types: + +* Info = any() +* Req = cowboy_req:req() +* State = any() + +Handle the Erlang message received. + +This function will be called every time an Erlang message +has been received. The message can be any Erlang term. + +The `ok` return value can be used to stop the receive loop, +typically because a response has been sent. + +The `hibernate` option will hibernate the process until +it receives another message. + +: terminate(Reason, Req, State) -> ok + +Types: + +* Reason = {normal, shutdown} | {normal, timeout} | {error, closed} | {error, overflow} | {error, atom()} +* Req = cowboy_req:req() +* State = any() + +Perform any necessary cleanup of the state. + +This callback will typically unregister from any event manager +or message queue it registered to in `init/3`. + +This callback should release any resource currently in use, +clear any active timer and reset the process to its original +state, as it might be reused for future requests sent on the +same connection. diff --git a/doc/src/manual/cowboy_middleware.ezdoc b/doc/src/manual/cowboy_middleware.ezdoc new file mode 100644 index 0000000..065139c --- /dev/null +++ b/doc/src/manual/cowboy_middleware.ezdoc @@ -0,0 +1,54 @@ +::: cowboy_middleware + +The `cowboy_middleware` behaviour defines the interface used +by Cowboy middleware modules. + +Middlewares process the request sequentially in the order they +are configured. + +:: Types + +: env() = [{atom(), any()}] + +The environment variable. + +One is created for every request. It is passed to each +middleware module executed and subsequently returned, +optionally with its contents modified. + +:: Callbacks + +: execute(Req, Env) + -> {ok, Req, Env} + | {suspend, Module, Function, Args} + | {halt, Req} + | {error, StatusCode, Req} + +Types: + +* Req = cowboy_req:req() +* Env = env() +* Module = module() +* Function = atom() +* Args = [any()] +* StatusCode = cowboy:http_status() + +Execute the middleware. + +The `ok` return value indicates that everything went well +and that Cowboy should continue processing the request. A +response may or may not have been sent. + +The `suspend` return value will hibernate the process until +an Erlang message is received. Note that when resuming, any +previous stacktrace information will be gone. + +The `halt` return value stops Cowboy from doing any further +processing of the request, even if there are middlewares +that haven't been executed yet. The connection may be left +open to receive more requests from the client. + +The `error` return value sends an error response identified +by the `StatusCode` and then proceeds to terminate the +connection. Middlewares that haven't been executed yet +will not be called. diff --git a/doc/src/manual/cowboy_protocol.ezdoc b/doc/src/manual/cowboy_protocol.ezdoc new file mode 100644 index 0000000..6813295 --- /dev/null +++ b/doc/src/manual/cowboy_protocol.ezdoc @@ -0,0 +1,84 @@ +::: cowboy_protocol + +The `cowboy_protocol` module implements HTTP/1.1 and HTTP/1.0 +as a Ranch protocol. + +:: Types + +: opts() = [{compress, boolean()} + | {env, cowboy_middleware:env()} + | {max_empty_lines, non_neg_integer()} + | {max_header_name_length, non_neg_integer()} + | {max_header_value_length, non_neg_integer()} + | {max_headers, non_neg_integer()} + | {max_keepalive, non_neg_integer()} + | {max_request_line_length, non_neg_integer()} + | {middlewares, [module()]} + | {onrequest, cowboy:onrequest_fun()} + | {onresponse, cowboy:onresponse_fun()} + | {timeout, timeout()}] + +Configuration for the HTTP protocol handler. + +This configuration is passed to Cowboy when starting listeners +using `cowboy:start_http/4` or `cowboy:start_https/4` functions. + +It can be updated without restarting listeners using the +Ranch functions `ranch:get_protocol_options/1` and +`ranch:set_protocol_options/2`. + +:: Option descriptions + +The default value is given next to the option name. + +: compress (false) + +When enabled, Cowboy will attempt to compress the response body. + +: env ([{listener, Ref}]) + +Initial middleware environment. + +: max_empty_lines (5) + +Maximum number of empty lines before a request. + +: max_header_name_length (64) + +Maximum length of header names. + +: max_header_value_length (4096) + +Maximum length of header values. + +: max_headers (100) + +Maximum number of headers allowed per request. + +: max_keepalive (100) + +Maximum number of requests allowed per connection. + +: max_request_line_length (4096) + +Maximum length of the request line. + +: middlewares ([cowboy_router, cowboy_handler]) + +List of middlewares to execute for every requests. + +: onrequest (undefined) + +Fun called every time a request is received. + +: onresponse (undefined) + +Fun called every time a response is sent. + +: timeout (5000) + +Time in ms with no requests before Cowboy closes the connection. + +:: Exports + +None. diff --git a/doc/src/manual/cowboy_req.ezdoc b/doc/src/manual/cowboy_req.ezdoc new file mode 100644 index 0000000..beac1f4 --- /dev/null +++ b/doc/src/manual/cowboy_req.ezdoc @@ -0,0 +1,704 @@ +::: cowboy_req + +The `cowboy_req` module provides functions to access, manipulate +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()}` +* 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. + +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. + +It is highly discouraged to pass the Req object to another process. +Doing so and calling `cowboy_req` functions from it leads to +undefined behavior. + +:: Types + +: body_opts() = [{continue, boolean()} + | {length, non_neg_integer()} + | {read_length, non_neg_integer()} + | {read_timeout, timeout()} + | {transfer_decode, transfer_decode_fun(), any()} + | {content_decode, content_decode_fun()}] + +Request body reading options. + +: cookie_opts() = [{max_age, non_neg_integer()} + | {domain, binary()} | {path, binary()} + | {secure, boolean()} | {http_only, boolean()}] + +Cookie options. + +: req() - opaque to the user + +The Req object. + +All functions in this module receive a `Req` as argument, +and most of them return a new object labelled `Req2` in +the function descriptions below. + +:: Request related exports + +: binding(Name, Req) -> binding(Name, Req, undefined) +: binding(Name, Req, Default) -> {Value, Req2} + +Types: + +* Name = atom() +* Default = any() +* Value = any() | Default + +Return the value for the given binding. + +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} + +Types: + +* Name = atom() +* Value = any() + +Return all bindings. + +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} + +Types: + +* Name = binary() +* Default = any() +* Value = binary() | Default + +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} + +Types: + +* Headers = cowboy:http_headers() + +Return all headers. + +: host(Req) -> {Host, Req2} + +Types: + +* Host = binary() + +Return the requested host. + +: host_info(Req) -> {HostInfo, Req2} + +Types: + +* HostInfo = cowboy_router:tokens() | undefined + +Return the extra tokens from matching against `...` during routing. + +: host_url(Req) -> {HostURL, Req2} + +Types: + +* HostURL = binary() | undefined + +Return the requested URL excluding the path component. + +This function will always return `undefined` until the +`cowboy_router` middleware has been executed. This includes +the `onrequest` hook. + +: meta(Name, Req) -> meta(Name, Req, undefined) +: meta(Name, Req, Default) -> {Value, Req2} + +Types: + +* Name = atom() +* Default = any() +* Value = any() + +Return metadata about the request. + +: method(Req) -> {Method, Req2} + +Types: + +* Method = binary() + +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} + +Types: + +* Name = binary() +* Default = any() +* ParsedValue - see below +* Value = any() + +Parse the given header. + +While header names are case insensitive, this function expects +the name to be a lowercase binary. + +The `parse_header/2` function will call `parser_header/3` with a +different default value depending on the header being parsed. The +following table summarizes the default values used. + +|| Header name Default value +| +| transfer-encoding `[<<"identity">>]` +| Any other header `undefined` + +The parsed value differs depending on the header being parsed. The +following table summarizes the different types returned. + +|| Header name Type +| +| accept `[{{Type, SubType, Params}, Quality, AcceptExt}]` +| accept-charset `[{Charset, Quality}]` +| accept-encoding `[{Encoding, Quality}]` +| accept-language `[{LanguageTag, Quality}]` +| authorization `{AuthType, Credentials}` +| content-length `non_neg_integer()` +| content-type `{Type, SubType, ContentTypeParams}` +| cookie `[{binary(), binary()}]` +| expect `[Expect | {Expect, ExpectValue, Params}]` +| if-match `'*' | [{weak | strong, OpaqueTag}]` +| if-modified-since `calendar:datetime()` +| if-none-match `'*' | [{weak | strong, OpaqueTag}]` +| if-unmodified-since `calendar:datetime()` +| range `{Unit, [Range]}` +| sec-websocket-protocol `[binary()]` +| transfer-encoding `[binary()]` +| upgrade `[binary()]` +| x-forwarded-for `[binary()]` + +Types for the above table: + +* Type = SubType = Charset = Encoding = LanguageTag = binary() +* AuthType = Expect = OpaqueTag = Unit = binary() +* Params = ContentTypeParams = [{binary(), binary()}] +* Quality = 0..1000 +* AcceptExt = [{binary(), binary()} | binary()] +* Credentials - see below +* Range = {non_neg_integer(), non_neg_integer() | infinity} | neg_integer() + +The cookie names and values, the values of the sec-websocket-protocol +and x-forwarded-for headers, the values in `AcceptExt` and `Params`, +the authorization `Credentials`, the `ExpectValue` and `OpaqueTag` +are case sensitive. All values in `ContentTypeParams` are case sensitive +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. + +The authorization header parsing code currently only supports basic +HTTP authentication. The `Credentials` type is thus `{Username, Password}` +with `Username` and `Password` being `binary()`. + +The range header value `Range` can take three forms: + +* `{From, To}`: from `From` to `To` units +* `{From, infinity}`: everything after `From` units +* `-Final`: the final `Final` units + +An `undefined` tuple will be returned if Cowboy doesn't know how +to parse the requested header. + +: path(Req) -> {Path, Req2} + +Types: + +* Path = binary() + +Return the requested path. + +: path_info(Req) -> {PathInfo, Req2} + +Types: + +* PathInfo = cowboy_router:tokens() | undefined + +Return the extra tokens from matching against `...` during routing. + +: peer(Req) -> {Peer, Req2} + +Types: + +* Peer = {inet:ip_address(), inet:port_number()} + +Return the client's IP address and port number. + +: port(Req) -> {Port, Req2} + +Types: + +* Port = inet:port_number() + +Return the request's port. + +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} + +Types: + +* QueryString = binary() + +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: + +* Name = atom() +* Value = any() + +Set metadata about the request. + +An existing value will be overwritten. + +: url(Req) -> {URL, Req2} + +Types: + +* URL = binary() | undefined + +Return the requested URL. + +This function will always return `undefined` until the +`cowboy_router` middleware has been executed. This includes +the `onrequest` hook. + +: version(Req) -> {Version, Req2} + +Types: + +* Version = cowboy:http_version() + +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} + +Types: + +* Opts = [body_opt()] +* Data = binary() +* Reason = atom() + +Read the request body. + +This function will read a chunk of the request body. If there is +more data to be read after this function call, then a `more` tuple +is returned. Otherwise an `ok` tuple is returned. + +Cowboy will automatically send a `100 Continue` reply if +required. If this behavior is not desirable, it can be disabled +by setting the `continue` option to `false`. + +Cowboy will by default attempt to read up to 8MB of the body, +but in chunks of 1MB. It will use a timeout of 15s per chunk. +All these values can be changed using the `length`, `read_length` +and `read_timeout` options respectively. Note that the size +of the data may not be the same as requested as the decoding +functions may grow or shrink it, and Cowboy makes not attempt +at returning an exact amount. + +Cowboy will properly handle chunked transfer-encoding by +default. If any other transfer-encoding or content-encoding +has been used for the request, custom decoding functions +can be used. The `content_decode` and `transfer_decode` +options allow setting the decode functions manually. + +After the body has been streamed fully, Cowboy will remove +the transfer-encoding header from the Req object, and add +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} + +Types: + +* Length = non_neg_integer() | undefined + +Return the length of the request body. + +The length will only be returned if the request does not +use any transfer-encoding and if the content-length header +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} + +Types: + +* Opts = [body_opt()] +* Name = binary() +* Value = binary() | true +* Reason = chunked | badlength | atom() + +Return the request body as a list of tuples. + +This function will parse the body assuming the content-type +application/x-www-form-urlencoded, commonly used for the +query string. + +This function calls `body/2` for reading the body, with the +same options it received. By default it will attempt to read +a body of 64KB in one chunk, with a timeout of 5s. If the +body is larger then a `badlength` tuple is returned. + +This function can only be called once. Cowboy will not cache +the result of this call. + +: has_body(Req) -> boolean() + +Return whether the request has a body. + +: part(Req) -> part(Req, + [{length, 64000}, {read_length, 64000}, {read_timeout, 5000}]) +: part(Req, Opts) -> {ok, Headers, Req2} | {done, Req2} + +Types: + +* Opts = [body_opt()] +* Headers = cow_multipart:headers() + +Read the headers for the next part of the multipart message. + +Cowboy will skip any data remaining until the beginning of +the next part. This includes the preamble to the multipart +message but also the body of a previous part if it hasn't +been read. Both are skipped automatically when calling this +function. + +The headers returned are MIME headers, NOT HTTP headers. +They can be parsed using the functions from the `cow_multipart` +module. In addition, the `cow_multipart:form_data/1` function +can be used to quickly figure out `multipart/form-data` messages. +It takes the list of headers and returns whether this part is +a simple form field or a file being uploaded. + +Note that once a part has been read, or skipped, it cannot +be read again. + +This function calls `body/2` for reading the body, with the +same options it received. By default it will only read chunks +of 64KB with a timeout of 5s. This is tailored for reading +part headers, not for skipping the previous part's body. +You might want to consider skipping large parts manually. + +: part_body(Req) -> part_body(Req, []) +: part_body(Req, Opts) -> {ok, Data, Req2} | {more, Data, Req2} + +Types: + +* Opts = [body_opt()] +* Data = binary() + +Read the body of the current part of the multipart message. + +This function calls `body/2` for reading the body, with the +same options it received. It uses the same defaults. + +If there are more data to be read from the socket for this +part, the function will return what it could read inside a +`more` tuple. Otherwise, it will return an `ok` tuple. + +Calling this function again after receiving a `more` tuple +will return another chunk of body. The last chunk will be +returned inside an `ok` tuple. + +Note that once the body has been read, fully or partially, +it cannot be read again. + +:: Response related exports + +: chunk(Data, Req) -> ok | {error, Reason} + +Types: + +* Data = iodata() +* Reason = atom() + +Send a chunk of data. + +This function should be called as many times as needed +to send data chunks after calling `chunked_reply/{2,3}`. + +When the method is HEAD, no data will actually be sent. + +If the request uses HTTP/1.0, the data is sent directly +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} + +Types: + +* StatusCode = cowboy:http_status() +* Headers = cowboy:http_headers() + +Send a response using chunked transfer-encoding. + +This function effectively sends the response status line +and headers to the client. + +This function will not send any body set previously. After +this call the handler must use the `chunk/2` function +repeatedly to send the body in as many chunks as needed. + +If the request uses HTTP/1.0, the data is sent directly +without wrapping it in an HTTP/1.1 chunk, providing +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} + +Types: + +* Reason = atom() + +Send a 100 Continue intermediate reply. + +This reply is required before the client starts sending the +body when the request contains the `expect` header with the +`100-continue` value. + +Cowboy will send this automatically when required. However +you may want to do it manually by disabling this behavior +with the `continue` body option and then calling this +function. + +: delete_resp_header(Name, Req) -> Req2 + +Types: + +* Name = binary() + +Delete the given response header. + +While header names are case insensitive, this function expects +the name to be a lowercase binary. + +: has_resp_body(Req) -> boolean() + +Return whether a response body has been set. + +This function will return false if a response body has +been set with a length of 0. + +: has_resp_header(Name, Req) -> boolean() + +Types: + +* Name = binary() + +Return whether the given response header has been set. + +While header names are case insensitive, this function expects +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} + +Types: + +* StatusCode = cowboy:http_status() +* Headers = cowboy:http_headers() +* Body = iodata() + +Send a response. + +This function effectively sends the response status line, +headers and body to the client, in a single send function +call. + +The `reply/2` and `reply/3` functions will send the body +set previously, if any. The `reply/4` function overrides +any body set previously and sends `Body` instead. + +If a body function was set, and `reply/2` or `reply/3` was +used, it will be called before returning. + +No more data can be sent to the client after this function +returns. + +This function can only be called once, with the exception +of overriding the response in the `onresponse` hook. + +: set_resp_body(Body, Req) -> Req2 + +Types: + +* Body = iodata() + +Set a response body. + +This body will not be sent if `chunked_reply/{2,3}` or +`reply/4` is used, as they override it. + +: set_resp_body_fun(Fun, Req) -> Req2 +: set_resp_body_fun(Length, Fun, Req) -> Req2 + +Types: + +* Fun = fun((Socket, Transport) -> ok) +* Socket = inet:socket() +* Transport = module() +* Length = non_neg_integer() + +Set a fun for sending the response body. + +If a `Length` is provided, it will be sent in the +content-length header in the response. It is recommended +to set the length if it can be known in advance. Otherwise, +the transfer-encoding header will be set to identity. + +This function will only be called if the response is sent +using the `reply/2` or `reply/3` function. + +The fun will receive the Ranch `Socket` and `Transport` as +arguments. Only send and sendfile operations are supported. + +: set_resp_body_fun(chunked, Fun, Req) -> Req2 + +Types: + +* Fun = fun((ChunkFun) -> ok) +* ChunkFun = fun((iodata()) -> ok | {error, atom()}) + +Set a fun for sending the response body using chunked transfer-encoding. + +This function will only be called if the response is sent +using the `reply/2` or `reply/3` function. + +The fun will receive another fun as argument. This fun is to +be used to send chunks in a similar way to the `chunk/2` function, +except the fun only takes one argument, the data to be sent in +the chunk. + +: set_resp_cookie(Name, Value, Opts, Req) -> Req2 + +Types: + +* Name = iodata() +* Value = iodata() +* Opts = cookie_opts() + +Set a cookie in the response. + +Cookie names are case sensitive. + +: set_resp_header(Name, Value, Req) -> Req2 + +Types: + +* Name = binary() +* Value = iodata() + +Set a response header. + +You should use `set_resp_cookie/4` instead of this function +to set cookies. + +:: Misc. exports + +: compact(Req) -> Req2 + +Remove any non-essential data from the Req object. + +Long-lived connections usually only need to manipulate the +Req object at initialization. Compacting allows saving up +memory by discarding extraneous information. diff --git a/doc/src/manual/cowboy_rest.ezdoc b/doc/src/manual/cowboy_rest.ezdoc new file mode 100644 index 0000000..4d13530 --- /dev/null +++ b/doc/src/manual/cowboy_rest.ezdoc @@ -0,0 +1,561 @@ +::: cowboy_rest + +The `cowboy_rest` module implements REST semantics on top of +the HTTP protocol. + +This module cannot be described as a behaviour due to most of +the callbacks it defines being optional. It has the same +semantics as a behaviour otherwise. + +The only mandatory callback is `init/3`, needed to perform +the protocol upgrade. + +:: Types + +None. + +:: Meta values + +: charset + +Type: binary() + +Negotiated charset. + +This value may not be defined if no charset was negotiated. + +: language + +Type: binary() + +Negotiated language. + +This value may not be defined if no language was negotiated. + +: media_type + +Type: {binary(), binary(), '*' | [{binary(), binary()}]} + +Negotiated media-type. + +The media-type is the content-type, excluding the charset. + +This value is always defined after the call to +`content_types_provided/2`. + +:: Callbacks + +: init({TransportName, ProtocolName}, Req, Opts) + -> {upgrade, protocol, cowboy_rest} + | {upgrade, protocol, cowboy_rest, Req, Opts} + +Types: + +* TransportName = tcp | ssl | atom() +* ProtocolName = http | atom() +* Req = cowboy_req:req() +* Opts = any() + +Upgrade the protocol to `cowboy_rest`. + +This is the only mandatory callback. + +: rest_init(Req, Opts) -> {ok, Req, State} + +Types: + +* Req = cowboy_req:req() +* Opts = any() +* State = any() + +Initialize the state for this request. + +: rest_terminate(Req, State) -> ok + +Types: + +* Req = cowboy_req:req() +* State = any() + +Perform any necessary cleanup of the state. + +This callback should release any resource currently in use, +clear any active timer and reset the process to its original +state, as it might be reused for future requests sent on the +same connection. + +: Callback(Req, State) -> {Value, Req, State} | {halt, Req, State} + +Types: + +* Callback - one of the REST callbacks described below +* Req = cowboy_req:req() +* State = any() +* Value - see the REST callbacks description below + +Please see the REST callbacks description below for details +on the `Value` type, the default value if the callback is +not defined, and more general information on when the +callback is called and what its intended use is. + +The `halt` tuple can be returned to stop REST processing. +It is up to the resource code to send a reply before that, +otherwise a `204 No Content` will be sent. + +:: REST callbacks description + +: allowed_methods + +* Methods: all +* Value type: [binary()] +* Default value: [<<"GET">>, <<"HEAD">>, <<"OPTIONS">>] + +Return the list of allowed methods. + +Methods are case sensitive. Standard methods are always uppercase. + +: allow_missing_post + +* Methods: POST +* Value type: boolean() +* Default value: true + +Return whether POST is allowed when the resource doesn't exist. + +Returning `true` here means that a new resource will be +created. The URL to the created resource should also be +returned from the `AcceptResource` callback. + +: charsets_provided + +* Methods: GET, HEAD, POST, PUT, PATCH, DELETE +* Value type: [binary()] +* Skip to the next step if undefined + +Return the list of charsets the resource provides. + +The list must be ordered in order of preference. + +If the accept-charset header was not sent, the first charset +in the list will be selected. Otherwise Cowboy will select +the most appropriate charset from the list. + +The chosen charset will be set in the `Req` object as the meta +value `charset`. + +While charsets are case insensitive, this callback is expected +to return them as lowercase binary. + +: content_types_accepted + +* Methods: POST, PUT, PATCH +* No default + +Types: + +* Value = [{binary() | {Type, SubType, Params}, AcceptResource}] +* Type = SubType = binary() +* Params = '*' | [{binary(), binary()}] +* AcceptResource = atom() + +Return the list of content-types the resource accepts. + +The list must be ordered in order of preference. + +Each content-type can be given either as a binary string or as +a tuple containing the type, subtype and parameters. + +Cowboy will select the most appropriate content-type from the list. +If any parameter is acceptable, then the tuple form should be used +with parameters set to `'*'`. If the parameters value is set to `[]` +only content-type values with no parameters will be accepted. All +parameter values are treated in a case sensitive manner except the +`charset` parameter, if present, which is case insensitive. + +This function will be called for POST, PUT and PATCH requests. +It is entirely possible to define different callbacks for different +methods if the handling of the request differs. Simply verify +what the method is with `cowboy_req:method/1` and return a +different list for each methods. + +The `AcceptResource` value is the name of the callback that will +be called if the content-type matches. It is defined as follow. + +* Value type: true | {true, URL} | false +* No default + +Process the request body. + +This function should create or update the resource with the +information contained in the request body. This information +may be full or partial depending on the request method. + +If the request body was processed successfully, `true` or +`{true, URL}` may be returned. If an URL is provided, the +response will redirect the client to the location of the +resource. + +If a response body must be sent, the appropriate media-type, charset +and language can be retrieved using the `cowboy_req:meta/{2,3}` +functions. The respective keys are `media_type`, `charset` +and `language`. The body can be set using `cowboy_req:set_resp_body/2`. + +: content_types_provided + +* Methods: GET, HEAD, POST, PUT, PATCH, DELETE +* Default value: [{{<<"text">>, <<"html">>, '*'}, to_html}] + +Types: + +* Value = [{binary() | {Type, SubType, Params}, ProvideResource}] +* Type = SubType = binary() +* Params = '*' | [{binary(), binary()}] +* ProvideResource = atom() + +Return the list of content-types the resource provides. + +The list must be ordered in order of preference. + +Each content-type can be given either as a binary string or as +a tuple containing the type, subtype and parameters. + +Cowboy will select the most appropriate content-type from the list. +If any parameter is acceptable, then the tuple form should be used +with parameters set to `'*'`. If the parameters value is set to `[]` +only content-type values with no parameters will be accepted. All +parameter values are treated in a case sensitive manner except the +`charset` parameter, if present, which is case insensitive. + +The `ProvideResource` value is the name of the callback that will +be called if the content-type matches. It will only be called when +a representation of the resource needs to be returned. It is defined +as follow. + +* Methods: GET, HEAD +* Value type: iodata() | {stream, Fun} | {stream, Len, Fun} | {chunked, ChunkedFun} +* No default + +Return the response body. + +The response body may be provided directly or through a fun. +If a fun tuple is returned, the appropriate `set_resp_body_fun` +function will be called. Please refer to the documentation for +these functions for more information about the types. + +The call to this callback happens a good time after the call to +`content_types_provided/2`, when it is time to start rendering +the response body. + +: delete_completed + +* Methods: DELETE +* Value type: boolean() +* Default value: true + +Return whether the delete action has been completed. + +This function should return `false` if there is no guarantee +that the resource gets deleted immediately from the system, +including from any internal cache. + +When this function returns `false`, a `202 Accepted` +response will be sent instead of a `200 OK` or `204 No Content`. + +: delete_resource + +* Methods: DELETE +* Value type: boolean() +* Default value: false + +Delete the resource. + +The value returned indicates if the action was successful, +regardless of whether the resource is immediately deleted +from the system. + +: expires + +* Methods: GET, HEAD +* Value type: calendar:datetime() | binary() | undefined +* Default value: undefined + +Return the date of expiration of the resource. + +This date will be sent as the value of the expires header. + +: forbidden + +* Methods: all +* Value type: boolean() +* Default value: false + +Return whether access to the resource is forbidden. + +A `403 Forbidden` response will be sent if this +function returns `true`. This status code means that +access is forbidden regardless of authentication, +and that the request shouldn't be repeated. + +: generate_etag + +* Methods: GET, HEAD, POST, PUT, PATCH, DELETE +* Value type: binary() | {weak | strong, binary()} +* Default value: undefined + +Return the entity tag of the resource. + +This value will be sent as the value of the etag header. + +If a binary is returned, then the value will be parsed +to the tuple form automatically. The value must be in +the same format as the etag header, including quotes. + +: is_authorized + +* Methods: all +* Value type: true | {false, AuthHeader} +* Default value: true + +Return whether the user is authorized to perform the action. + +This function should be used to perform any necessary +authentication of the user before attempting to perform +any action on the resource. + +If the authentication fails, the value returned will be sent +as the value for the www-authenticate header in the +`401 Unauthorized` response. + +: is_conflict + +* Methods: PUT +* Value type: boolean() +* Default value: false + +Return whether the put action results in a conflict. + +A `409 Conflict` response will be sent if this function +returns `true`. + +: known_content_type + +* Methods: all +* Value type: boolean() +* Default value: true + +Return whether the content-type is known. + +This function determines if the server understands the +content-type, regardless of its use by the resource. + +: known_methods + +* Methods: all +* Value type: [binary()] +* Default value: [<<"GET">>, <<"HEAD">>, <<"POST">>, <<"PUT">>, <<"PATCH">>, <<"DELETE">>, <<"OPTIONS">>] + +Return the list of known methods. + +The full list of methods known by the server should be +returned, regardless of their use in the resource. + +The default value lists the methods Cowboy knows and +implement in `cowboy_rest`. + +Methods are case sensitive. Standard methods are always uppercase. + +: languages_provided + +* Methods: GET, HEAD, POST, PUT, PATCH, DELETE +* Value type: [binary()] +* Skip to the next step if undefined + +Return the list of languages the resource provides. + +The list must be ordered in order of preference. + +If the accept-language header was not sent, the first language +in the list will be selected. Otherwise Cowboy will select +the most appropriate language from the list. + +The chosen language will be set in the `Req` object as the meta +value `language`. + +While languages are case insensitive, this callback is expected +to return them as lowercase binary. + +: last_modified + +* Methods: GET, HEAD, POST, PUT, PATCH, DELETE +* Value type: calendar:datetime() +* Default value: undefined + +Return the date of last modification of the resource. + +This date will be used to test against the if-modified-since +and if-unmodified-since headers, and sent as the last-modified +header in the response of GET and HEAD requests. + +: malformed_request + +* Methods: all +* Value type: boolean() +* Default value: false + +Return whether the request is malformed. + +Cowboy has already performed all the necessary checks +by the time this function is called, so few resources +are expected to implement it. + +The check is to be done on the request itself, not on +the request body, which is processed later. + +: moved_permanently + +* Methods: GET, HEAD, POST, PUT, PATCH, DELETE +* Value type: {true, URL} | false +* Default value: false + +Return whether the resource was permanently moved. + +If it was, its new URL is also returned and sent in the +location header in the response. + +: moved_temporarily + +* Methods: GET, HEAD, POST, PATCH, DELETE +* Value type: {true, URL} | false +* Default value: false + +Return whether the resource was temporarily moved. + +If it was, its new URL is also returned and sent in the +location header in the response. + +: multiple_choices + +* Methods: GET, HEAD, POST, PUT, PATCH, DELETE +* Value type: boolean() +* Default value: false + +Return whether there are multiple representations of the resource. + +This function should be used to inform the client if there +are different representations of the resource, for example +different content-type. If this function returns `true`, +the response body should include information about these +different representations using `cowboy_req:set_resp_body/2`. +The content-type of the response should be the one previously +negociated and that can be obtained by calling +`cowboy_req:meta(media_type, Req)`. + +: options + +* Methods: OPTIONS +* Value type: ok +* Default value: ok + +Handle a request for information. + +The response should inform the client the communication +options available for this resource. + +By default, Cowboy will send a `200 OK` response with the +allow header set. + +: previously_existed + +* Methods: GET, HEAD, POST, PATCH, DELETE +* Value type: boolean() +* Default value: false + +Return whether the resource existed previously. + +: resource_exists + +* Methods: GET, HEAD, POST, PUT, PATCH, DELETE +* Value type: boolean() +* Default value: true + +Return whether the resource exists. + +If it exists, conditional headers will be tested before +attempting to perform the action. Otherwise, Cowboy will +check if the resource previously existed first. + +: service_available + +* Methods: all +* Value type: boolean() +* Default value: true + +Return whether the service is available. + +This function can be used to test that all relevant backend +systems are up and able to handle requests. + +A `503 Service Unavailable` response will be sent if this +function returns `false`. + +: uri_too_long + +* Methods: all +* Value type: boolean() +* Default value: false + +Return whether the requested URI is too long. + +Cowboy has already performed all the necessary checks +by the time this function is called, so few resources +are expected to implement it. + +A `414 Request-URI Too Long` response will be sent if this +function returns `true`. + +: valid_content_headers + +* Methods: all +* Value type: boolean() +* Default value: true + +Return whether the content-* headers are valid. + +This also applies to the transfer-encoding header. This +function must return `false` for any unknown content-* +headers, or if the headers can't be understood. The +function `cowboy_req:parse_header/2` can be used to +quickly check the headers can be parsed. + +A `501 Not Implemented` response will be sent if this +function returns `false`. + +: valid_entity_length + +* Methods: all +* Value type: boolean() +* Default value: true + +Return whether the request body length is within acceptable boundaries. + +A `413 Request Entity Too Large` response will be sent if this +function returns `false`. + +: variances + +* Methods: GET, HEAD, POST, PUT, PATCH, DELETE +* Value type: [binary()] +* Default value: [] + +Return the list of headers that affect the representation of the resource. + +These request headers return the same resource but with different +parameters, like another language or a different content-type. + +Cowboy will automatically add the accept, accept-language and +accept-charset headers to the list if the respective functions +were defined in the resource. + +This operation is performed right before the `resource_exists/2` +callback. All responses past that point will contain the vary +header which holds this list. diff --git a/doc/src/manual/cowboy_router.ezdoc b/doc/src/manual/cowboy_router.ezdoc new file mode 100644 index 0000000..f76acf6 --- /dev/null +++ b/doc/src/manual/cowboy_router.ezdoc @@ -0,0 +1,70 @@ +::: cowboy_router + +The `cowboy_router` middleware maps the requested host and +path to the handler to be used for processing the request. +It uses the dispatch rules compiled from the routes given +to the `compile/1` function for this purpose. It adds the +handler name and options to the environment as the values +`handler` and `handler_opts` respectively. + +Environment input: + +* dispatch = dispatch_rules() + +Environment output: + +* handler = module() +* handler_opts = any() + +:: Types + +: bindings() = [{atom(), binary()}] + +List of bindings found during routing. + +: constraints() = [IntConstraint | FunConstraint] + +Types: + +* IntConstraint = {atom(), int} +* FunConstraint = {atom(), function, Fun} +* Fun = fun((binary()) -> true | {true, any()} | false) + +List of constraints to apply to the bindings. + +The int constraint will convert the binding to an integer. +The fun constraint allows writing custom code for checking +the bindings. Returning a new value from that fun allows +replacing the current binding with a new value. + +: dispatch_rules() - opaque to the user + +Rules for dispatching request used by Cowboy. + +: routes() = [{Host, Paths} | {Host, constraints(), Paths}] + +Types: + +* Host = Path = '_' | iodata() +* Paths = [{Path, Handler, Opts} | {Path, constraints(), Handler, Opts}] +* Handler = module() +* Opts = any() + +Human readable list of routes mapping hosts and paths to handlers. + +The syntax for routes is defined in the user guide. + +: tokens() = [binary()] + +List of host_info and path_info tokens found during routing. + +:: Exports + +: compile(Routes) -> Dispatch + +Types: + +* Routes = routes() +* Dispatch = dispatch_rules() + +Compile the routes for use by Cowboy. diff --git a/doc/src/manual/cowboy_spdy.ezdoc b/doc/src/manual/cowboy_spdy.ezdoc new file mode 100644 index 0000000..51a2110 --- /dev/null +++ b/doc/src/manual/cowboy_spdy.ezdoc @@ -0,0 +1,43 @@ +::: cowboy_spdy + +The `cowboy_spdy` module implements SPDY/3 as a Ranch protocol. + +:: Types + +: opts() = [{env, cowboy_middleware:env()} + | {middlewares, [module()]} + | {onrequest, cowboy:onrequest_fun()} + | {onresponse, cowboy:onresponse_fun()}] + +Configuration for the SPDY protocol handler. + +This configuration is passed to Cowboy when starting listeners +using the `cowboy:start_spdy/4` function. + +It can be updated without restarting listeners using the +Ranch functions `ranch:get_protocol_options/1` and +`ranch:set_protocol_options/2`. + +:: Option descriptions + +The default value is given next to the option name. + +: env ([{listener, Ref}]) + +Initial middleware environment. + +: middlewares ([cowboy_router, cowboy_handler]) + +List of middlewares to execute for every requests. + +: onrequest (undefined) + +Fun called every time a request is received. + +: onresponse (undefined) + +Fun called every time a response is sent. + +:: Exports + +None. diff --git a/doc/src/manual/cowboy_static.ezdoc b/doc/src/manual/cowboy_static.ezdoc new file mode 100644 index 0000000..ee122c1 --- /dev/null +++ b/doc/src/manual/cowboy_static.ezdoc @@ -0,0 +1,32 @@ +::: cowboy_static + +The `cowboy_static` module implements file serving capabilities +by using the REST semantics provided by `cowboy_rest`. + +:: Types + +: opts() = {priv_file, atom(), string() | binary()} + | {priv_file, atom(), string() | binary(), extra()} + | {file, string() | binary()} + | {file, string() | binary(), extra()} + | {priv_dir, atom(), string() | binary()} + | {priv_dir, atom(), string() | binary(), extra()} + | {dir, atom(), string() | binary()} + | {dir, atom(), string() | binary(), extra()} + +Configuration for the static handler. + +The handler can be configured for sending either one file or +a directory (including its subdirectories). + +Extra options allow you to define how the etag should be calculated +and how the mimetype of files should be detected. They are defined +as follow, but do note that these types are not exported, only the +`opts/0` type is public. + +: extra() = [extra_etag() | extra_mimetypes()] + +: extra_etag() = {etag, module(), function()} | {etag, false} + +: extra_mimetypes() = {mimetypes, module(), function()} + | {mimetypes, binary() | {binary(), binary(), [{binary(), binary()}]}} diff --git a/doc/src/manual/cowboy_sub_protocol.ezdoc b/doc/src/manual/cowboy_sub_protocol.ezdoc new file mode 100644 index 0000000..2ad0cf7 --- /dev/null +++ b/doc/src/manual/cowboy_sub_protocol.ezdoc @@ -0,0 +1,32 @@ +::: cowboy_sub_protocol + +The `cowboy_sub_protocol` behaviour defines the interface used +by modules that implement a protocol on top of HTTP. + +:: Types + +None. + +:: Callbacks + +: upgrade(Req, Env, Handler, Opts) + -> {ok, Req, Env} + | {suspend, Module, Function, Args} + | {halt, Req} + | {error, StatusCode, Req} + +Types: + +* Req = cowboy_req:req() +* Env = env() +* Handler = module() +* Opts = any() +* Module = module() +* Function = atom() +* Args = [any()] +* StatusCode = cowboy:http_status() + +Upgrade the protocol. + +Please refer to the `cowboy_middleware` manual for a +description of the return values. diff --git a/doc/src/manual/cowboy_websocket.ezdoc b/doc/src/manual/cowboy_websocket.ezdoc new file mode 100644 index 0000000..59a6248 --- /dev/null +++ b/doc/src/manual/cowboy_websocket.ezdoc @@ -0,0 +1,36 @@ +::: cowboy_websocket + +The `cowboy_websocket` module implements the Websocket protocol. + +The callbacks for websocket handlers are defined in the manual +for the `cowboy_websocket_handler` behaviour. + +:: Types + +: close_code() = 1000..4999 + +Reason for closing the connection. + +: frame() = close | ping | pong + | {text | binary | close | ping | pong, iodata()} + | {close, close_code(), iodata()} + +Frames that can be sent to the client. + +:: Meta values + +: websocket_compress + +Type: true | false + +Whether a websocket compression extension in in use. + +: websocket_version + +Type: 7 | 8 | 13 + +The version of the Websocket protocol being used. + +:: Exports + +None. diff --git a/doc/src/manual/cowboy_websocket_handler.ezdoc b/doc/src/manual/cowboy_websocket_handler.ezdoc new file mode 100644 index 0000000..0d31a54 --- /dev/null +++ b/doc/src/manual/cowboy_websocket_handler.ezdoc @@ -0,0 +1,133 @@ +::: cowboy_websocket_handler + +The `cowboy_websocket_handler` behaviour defines the interface used +by Websocket handlers. + +The `init/3` and `websocket_init/3` callbacks will always be called, +followed by zero or more calls to `websocket_handle/3` and +`websocket_info/3`. The `websocket_terminate/3` will always +be called last. + +:: Types + +None. + +:: Callbacks + +: init({TransportName, ProtocolName}, Req, Opts) + -> {upgrade, protocol, cowboy_websocket} + | {upgrade, protocol, cowboy_websocket, Req, Opts} + +Types: + +* TransportName = tcp | ssl | atom() +* ProtocolName = http | atom() +* Req = cowboy_req:req() +* Opts = any() + +Upgrade the protocol to `cowboy_websocket`. + +: websocket_init(TransportName, Req, Opts) + -> {ok, Req, State} + | {ok, Req, State, hibernate} + | {ok, Req, State, Timeout} + | {ok, Req, State, Timeout, hibernate} + | {shutdown, Req} + +Types: + +* TransportName = tcp | ssl | atom() +* Req = cowboy_req:req() +* Opts = any() +* State = any() +* Timeout = timeout() + +Initialize the state for this session. + +This function is called before the upgrade to Websocket occurs. +It can be used to negotiate Websocket protocol extensions +with the client. It will typically be used to register this process +to an event manager or a message queue in order to receive +the messages the handler wants to process. + +The connection will stay up for a duration of up to `Timeout` +milliseconds after it last received data from the socket, +at which point it will stop and close the connection. +By default this value is set to `infinity`. It is recommended +to either set this value or ensure by any other mechanism +that the handler will be closed after a certain period of +inactivity. + +The `hibernate` option will hibernate the process until it +starts receiving either data from the Websocket connection +or Erlang messages. + +The `shutdown` return value can be used to close the connection +before upgrading to Websocket. + +: websocket_handle(InFrame, Req, State) + -> {ok, Req, State} + | {ok, Req, State, hibernate} + | {reply, OutFrame | [OutFrame], Req, State} + | {reply, OutFrame | [OutFrame], Req, State, hibernate} + | {shutdown, Req, State} + +Types: + +* InFrame = {text | binary | ping | pong, binary()} +* Req = cowboy_req:req() +* State = any() +* OutFrame = cowboy_websocket:frame() + +Handle the data received from the Websocket connection. + +This function will be called every time data is received +from the Websocket connection. + +The `shutdown` return value can be used to close the +connection. A close reply will also result in the connection +being closed. + +The `hibernate` option will hibernate the process until +it receives new data from the Websocket connection or an +Erlang message. + +: websocket_info(Info, Req, State) + -> {ok, Req, State} + | {ok, Req, State, hibernate} + | {reply, OutFrame | [OutFrame], Req, State} + | {reply, OutFrame | [OutFrame], Req, State, hibernate} + | {shutdown, Req, State} + +Types: + +* Info = any() +* Req = cowboy_req:req() +* State = any() +* OutFrame = cowboy_websocket:frame() + +Handle the Erlang message received. + +This function will be called every time an Erlang message +has been received. The message can be any Erlang term. + +The `shutdown` return value can be used to close the +connection. A close reply will also result in the connection +being closed. + +The `hibernate` option will hibernate the process until +it receives another message or new data from the Websocket +connection. + +: websocket_terminate(Reason, Req, State) -> ok + +Types: + +* Reason = {normal, shutdown | timeout} | {remote, closed} | {remote, cowboy_websocket:close_code(), binary()} | {error, badencoding | badframe | closed | atom()} +* Req = cowboy_req:req() +* State = any() + +Perform any necessary cleanup of the state. + +The connection will be closed and the process stopped right +after this call. diff --git a/doc/src/manual/http_status_codes.ezdoc b/doc/src/manual/http_status_codes.ezdoc new file mode 100644 index 0000000..4d24b20 --- /dev/null +++ b/doc/src/manual/http_status_codes.ezdoc @@ -0,0 +1,151 @@ +::: HTTP status codes + +This chapter aims to list all HTTP status codes that Cowboy +may return, with details on the reasons why. The list given +here only includes the replies that Cowboy sends, not user +replies. + +: 100 Continue + +When the client sends an `expect: 100-continue` header, +Cowboy automatically sends a this status code before +trying to read the request body. This behavior can be +disabled using the appropriate body option. + +: 101 Switching Protocols + +This is the status code sent when switching to the +Websocket protocol. + +: 200 OK + +This status code is sent by `cowboy_rest`. + +: 201 Created + +This status code is sent by `cowboy_rest`. + +: 202 Accepted + +This status code is sent by `cowboy_rest`. + +: 204 No Content + +This status code is sent when the processing of a request +ends without any reply having been sent. It may also be +sent by `cowboy_rest` under normal conditions. + +: 300 Multiple Choices + +This status code is sent by `cowboy_rest`. + +: 301 Moved Permanently + +This status code is sent by `cowboy_rest`. + +: 303 See Other + +This status code is sent by `cowboy_rest`. + +: 304 Not Modified + +This status code is sent by `cowboy_rest`. + +: 307 Temporary Redirect + +This status code is sent by `cowboy_rest`. + +: 400 Bad Request + +Cowboy will send this status code for any of the +following reasons: + +* Too many empty lines were sent before the request. +* The request-line could not be parsed. +* Too many headers were sent. +* A header name was too long. +* A header value was too long. +* The host header was missing from an HTTP/1.1 request. +* The host header could not be parsed. +* The requested host was not found. +* The requested path could not be parsed. +* The accept header could not be parsed when using REST. +* REST under normal conditions. +* A Websocket upgrade failed. + +: 401 Unauthorized + +This status code is sent by `cowboy_rest`. + +: 403 Forbidden + +This status code is sent by `cowboy_rest`. + +: 404 Not Found + +This status code is sent when the router successfully +resolved the host but didn't find a matching path for +the request. It may also be sent by `cowboy_rest` under +normal conditions. + +: 405 Method Not Allowed + +This status code is sent by `cowboy_rest`. + +: 406 Not Acceptable + +This status code is sent by `cowboy_rest`. + +: 408 Request Timeout + +Cowboy will send this status code to the client if the +client started to send a request, indicated by the +request-line being received fully, but failed to send +all headers in a reasonable time. + +: 409 Conflict + +This status code is sent by `cowboy_rest`. + +: 410 Gone + +This status code is sent by `cowboy_rest`. + +: 412 Precondition Failed + +This status code is sent by `cowboy_rest`. + +: 413 Request Entity Too Large + +This status code is sent by `cowboy_rest`. + +: 414 Request-URI Too Long + +Cowboy will send this status code to the client if the +request-line is too long. It may also be sent by +`cowboy_rest` under normal conditions. + +: 415 Unsupported Media Type + +This status code is sent by `cowboy_rest`. + +: 500 Internal Server Error + +This status code is sent when a crash occurs in HTTP, loop +or REST handlers, or when an invalid return value is +returned. It may also be sent by `cowboy_rest` under +normal conditions. + +: 501 Not Implemented + +This status code is sent by `cowboy_rest`. + +: 503 Service Unavailable + +This status code is sent by `cowboy_rest`. + +: 505 HTTP Version Not Supported + +Cowboy only supports the versions 1.0 and 1.1 of HTTP. +In all other cases this status code is sent back to the +client and the connection is closed. diff --git a/doc/src/manual/index.ezdoc b/doc/src/manual/index.ezdoc new file mode 100644 index 0000000..e364e90 --- /dev/null +++ b/doc/src/manual/index.ezdoc @@ -0,0 +1,20 @@ +::: Cowboy Function Reference + +The function reference documents the public interface of Cowboy. + +* ^"The Cowboy Application^cowboy_app +* ^cowboy +* ^cowboy_handler +* ^cowboy_http_handler +* ^cowboy_loop_handler +* ^cowboy_middleware +* ^cowboy_protocol +* ^cowboy_req +* ^cowboy_rest +* ^cowboy_router +* ^cowboy_spdy +* ^cowboy_static +* ^cowboy_sub_protocol +* ^cowboy_websocket +* ^cowboy_websocket_handler +* ^"HTTP status codes^http_status_codes -- cgit v1.2.3