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/.
47
doc/src/guide/architecture.ezdoc
Normal file
|
@ -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.
|
61
doc/src/guide/broken_clients.ezdoc
Normal file
|
@ -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).
|
||||
```
|
137
doc/src/guide/cookies.ezdoc
Normal file
|
@ -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).
|
||||
```
|
40
doc/src/guide/erlang_beginners.ezdoc
Normal file
|
@ -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!
|
175
doc/src/guide/erlang_web.ezdoc
Normal file
|
@ -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.
|
306
doc/src/guide/getting_started.ezdoc
Normal file
|
@ -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!
|
82
doc/src/guide/hooks.ezdoc
Normal file
|
@ -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.
|
145
doc/src/guide/http_handlers.ezdoc
Normal file
|
@ -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.
|
147
doc/src/guide/http_req_life.ezdoc
Normal file
|
@ -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.
|
BIN
doc/src/guide/http_req_resp.png
Normal file
After Width: | Height: | Size: 32 KiB |
558
doc/src/guide/http_req_resp.svg
Normal file
|
@ -0,0 +1,558 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="744.09448819"
|
||||
height="1052.3622047"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.4 r9939"
|
||||
sodipodi:docname="http_req_resp.svg"
|
||||
inkscape:export-filename="/home/essen/Dropbox/Public/drawing.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90">
|
||||
<defs
|
||||
id="defs4">
|
||||
<linearGradient
|
||||
id="linearGradient5265">
|
||||
<stop
|
||||
style="stop-color:#69d2e7;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop5267" />
|
||||
<stop
|
||||
style="stop-color:#69d2e7;stop-opacity:0.58823532;"
|
||||
offset="1"
|
||||
id="stop5269" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient5251">
|
||||
<stop
|
||||
style="stop-color:#69d2e7;stop-opacity:0.78431374;"
|
||||
offset="0"
|
||||
id="stop5253" />
|
||||
<stop
|
||||
id="stop5263"
|
||||
offset="0.5"
|
||||
style="stop-color:#69d2e7;stop-opacity:1;" />
|
||||
<stop
|
||||
style="stop-color:#69d2e7;stop-opacity:0.39215687;"
|
||||
offset="1"
|
||||
id="stop5255" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient5233"
|
||||
osb:paint="solid">
|
||||
<stop
|
||||
style="stop-color:#69d2e7;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop5235" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="1"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1.4142136"
|
||||
inkscape:cx="229.71447"
|
||||
inkscape:cy="764.83183"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1014"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="33"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:snap-global="true"
|
||||
showguides="true">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid5357"
|
||||
empspacing="5"
|
||||
visible="true"
|
||||
enabled="true"
|
||||
snapvisiblegridlinesonly="true" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#path5757"
|
||||
id="use5779"
|
||||
transform="matrix(0.59961275,-0.80029029,0.80029029,0.59961275,-103.8895,437.48518)"
|
||||
width="744.09448"
|
||||
height="1052.3622"
|
||||
inkscape:export-filename="/home/essen/extend/cowboy/guide/http_req_resp.png"
|
||||
inkscape:export-xdpi="89.926643"
|
||||
inkscape:export-ydpi="89.926643" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#path5757"
|
||||
id="use5777"
|
||||
transform="matrix(0.92125726,-0.38895379,0.38895379,0.92125726,-85.14742,176.0134)"
|
||||
width="744.09448"
|
||||
height="1052.3622"
|
||||
inkscape:export-filename="/home/essen/extend/cowboy/guide/http_req_resp.png"
|
||||
inkscape:export-xdpi="89.926643"
|
||||
inkscape:export-ydpi="89.926643" />
|
||||
<path
|
||||
style="fill:none;stroke:#6d8e41;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:2, 4;stroke-dashoffset:0"
|
||||
d="m 188.5,231.36218 187,79"
|
||||
id="path5757"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:export-filename="/home/essen/extend/cowboy/guide/http_req_resp.png"
|
||||
inkscape:export-xdpi="89.926643"
|
||||
inkscape:export-ydpi="89.926643" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#g5650"
|
||||
id="use5753"
|
||||
transform="translate(475.11201,-33.017248)"
|
||||
width="744.09448"
|
||||
height="1052.3622"
|
||||
inkscape:export-filename="/home/essen/extend/cowboy/guide/http_req_resp.png"
|
||||
inkscape:export-xdpi="89.926643"
|
||||
inkscape:export-ydpi="89.926643" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#use5753"
|
||||
id="use5755"
|
||||
transform="translate(3.984568e-6,86.977569)"
|
||||
width="744.09448"
|
||||
height="1052.3622"
|
||||
inkscape:export-filename="/home/essen/extend/cowboy/guide/http_req_resp.png"
|
||||
inkscape:export-xdpi="89.926643"
|
||||
inkscape:export-ydpi="89.926643" />
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#a9ca7d;stroke-width:2.44279908999999984;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
id="rect5367"
|
||||
width="207.05719"
|
||||
height="171.55719"
|
||||
x="43.721401"
|
||||
y="360.88528"
|
||||
rx="11.072577"
|
||||
inkscape:export-filename="/home/essen/extend/cowboy/guide/http_req_resp.png"
|
||||
inkscape:export-xdpi="89.926643"
|
||||
inkscape:export-ydpi="89.926643" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#g5650"
|
||||
id="use5654"
|
||||
transform="translate(205.03261,53.351708)"
|
||||
width="744.09448"
|
||||
height="1052.3622"
|
||||
inkscape:export-filename="/home/essen/extend/cowboy/guide/http_req_resp.png"
|
||||
inkscape:export-xdpi="89.926643"
|
||||
inkscape:export-ydpi="89.926643" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#use5656"
|
||||
id="use5658"
|
||||
transform="translate(0,-86.13396)"
|
||||
width="744.09448"
|
||||
height="1052.3622"
|
||||
inkscape:export-filename="/home/essen/extend/cowboy/guide/http_req_resp.png"
|
||||
inkscape:export-xdpi="89.926643"
|
||||
inkscape:export-ydpi="89.926643" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#use5658"
|
||||
id="use5660"
|
||||
transform="translate(0,-87.519558)"
|
||||
width="744.09448"
|
||||
height="1052.3622"
|
||||
inkscape:export-filename="/home/essen/extend/cowboy/guide/http_req_resp.png"
|
||||
inkscape:export-xdpi="89.926643"
|
||||
inkscape:export-ydpi="89.926643" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#use5660"
|
||||
id="use5662"
|
||||
transform="translate(0,-86.562562)"
|
||||
width="744.09448"
|
||||
height="1052.3622"
|
||||
inkscape:export-filename="/home/essen/extend/cowboy/guide/http_req_resp.png"
|
||||
inkscape:export-xdpi="89.926643"
|
||||
inkscape:export-ydpi="89.926643" />
|
||||
<g
|
||||
id="g5650">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path5570"
|
||||
d="m -57.78256,351.41962 0,52.3259"
|
||||
style="fill:none;stroke:#6d8e41;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;opacity:0.8" />
|
||||
<path
|
||||
transform="matrix(0.58787746,0,0,0.58787746,73.160466,163.35774)"
|
||||
inkscape:transform-center-y="2.1823437"
|
||||
d="m -222.73865,430.10821 -12.85982,-22.27386 25.71964,0 z"
|
||||
inkscape:randomized="0"
|
||||
inkscape:rounded="0"
|
||||
inkscape:flatsided="true"
|
||||
sodipodi:arg2="2.6179939"
|
||||
sodipodi:arg1="1.5707963"
|
||||
sodipodi:r2="7.4246211"
|
||||
sodipodi:r1="14.849242"
|
||||
sodipodi:cy="415.25897"
|
||||
sodipodi:cx="-222.73865"
|
||||
sodipodi:sides="3"
|
||||
id="path5576"
|
||||
style="fill:#6d8e41;fill-opacity:1;fill-rule:nonzero;stroke:#6d8e41;stroke-width:0;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;opacity:0.8"
|
||||
sodipodi:type="star" />
|
||||
</g>
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#use5654"
|
||||
id="use5656"
|
||||
transform="translate(6.1542801e-7,-87.19819)"
|
||||
width="744.09448"
|
||||
height="1052.3622"
|
||||
inkscape:export-filename="/home/essen/extend/cowboy/guide/http_req_resp.png"
|
||||
inkscape:export-xdpi="89.926643"
|
||||
inkscape:export-ydpi="89.926643" />
|
||||
<rect
|
||||
style="fill:#d1f2a5;fill-opacity:1;fill-rule:nonzero;stroke:#a9ca7d;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
id="rect5273"
|
||||
width="104.5895"
|
||||
height="36.392323"
|
||||
x="-224.02068"
|
||||
y="29.41218"
|
||||
rx="15" />
|
||||
<rect
|
||||
style="fill:#effab4;fill-opacity:1;fill-rule:nonzero;stroke:#c7d28c;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
id="rect5273-7"
|
||||
width="104.5895"
|
||||
height="36.392323"
|
||||
x="-224.02068"
|
||||
y="90.691978"
|
||||
rx="15" />
|
||||
<rect
|
||||
style="fill:#ffc48c;fill-opacity:1;fill-rule:nonzero;stroke:#d79c64;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
id="rect5273-2"
|
||||
width="104.5895"
|
||||
height="36.392323"
|
||||
x="-224.02068"
|
||||
y="151.97169"
|
||||
rx="15" />
|
||||
<rect
|
||||
style="fill:#ff9f80;fill-opacity:1;fill-rule:nonzero;stroke:#d77758;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
id="rect5273-22"
|
||||
width="104.5895"
|
||||
height="36.392323"
|
||||
x="-224.02068"
|
||||
y="213.25146"
|
||||
rx="15" />
|
||||
<rect
|
||||
style="fill:#f56991;fill-opacity:1;fill-rule:nonzero;stroke:#cd4169;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
id="rect5273-8"
|
||||
width="104.5895"
|
||||
height="36.392323"
|
||||
x="-224.02068"
|
||||
y="274.53128"
|
||||
rx="15" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#rect5273"
|
||||
id="use5335"
|
||||
transform="translate(318.97597,268.31614)"
|
||||
width="744.09448"
|
||||
height="1052.3622"
|
||||
inkscape:export-filename="/home/essen/extend/cowboy/guide/http_req_resp.png"
|
||||
inkscape:export-xdpi="89.926643"
|
||||
inkscape:export-ydpi="89.926643" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#rect5273-22"
|
||||
id="use5355"
|
||||
transform="translate(318.97592,-176.5)"
|
||||
width="744.09448"
|
||||
height="1052.3622"
|
||||
inkscape:export-filename="/home/essen/extend/cowboy/guide/http_req_resp.png"
|
||||
inkscape:export-xdpi="89.926643"
|
||||
inkscape:export-ydpi="89.926643" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#rect5273-7"
|
||||
id="use5359"
|
||||
transform="translate(318.97597,32.954225)"
|
||||
width="744.09448"
|
||||
height="1052.3622"
|
||||
inkscape:export-filename="/home/essen/extend/cowboy/guide/http_req_resp.png"
|
||||
inkscape:export-xdpi="89.926643"
|
||||
inkscape:export-ydpi="89.926643" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#use5359"
|
||||
id="use5361"
|
||||
transform="translate(1.630859e-6,86.769591)"
|
||||
width="744.09448"
|
||||
height="1052.3622"
|
||||
inkscape:export-filename="/home/essen/extend/cowboy/guide/http_req_resp.png"
|
||||
inkscape:export-xdpi="89.926643"
|
||||
inkscape:export-ydpi="89.926643" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#use5335"
|
||||
id="use5363"
|
||||
transform="translate(0,173.33215)"
|
||||
width="744.09448"
|
||||
height="1052.3622"
|
||||
inkscape:export-filename="/home/essen/extend/cowboy/guide/http_req_resp.png"
|
||||
inkscape:export-xdpi="89.926643"
|
||||
inkscape:export-ydpi="89.926643" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#use5361"
|
||||
id="use5365"
|
||||
transform="translate(0,173.66424)"
|
||||
width="744.09448"
|
||||
height="1052.3622"
|
||||
inkscape:export-filename="/home/essen/extend/cowboy/guide/http_req_resp.png"
|
||||
inkscape:export-xdpi="89.926643"
|
||||
inkscape:export-ydpi="89.926643" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#77823c;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
|
||||
x="-58.692513"
|
||||
y="114.39204"
|
||||
id="text5371"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan5373"
|
||||
x="-58.692513"
|
||||
y="114.39204">some text</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#77823c;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
|
||||
x="146.77734"
|
||||
y="147.73293"
|
||||
id="text5371-7"
|
||||
sodipodi:linespacing="125%"
|
||||
inkscape:export-filename="/home/essen/extend/cowboy/guide/http_req_resp.png"
|
||||
inkscape:export-xdpi="89.926643"
|
||||
inkscape:export-ydpi="89.926643"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan5373-3"
|
||||
x="146.77734"
|
||||
y="147.73293">acceptor</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#77823c;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
|
||||
x="146.53125"
|
||||
y="233.42836"
|
||||
id="text5371-74"
|
||||
sodipodi:linespacing="125%"
|
||||
inkscape:export-filename="/home/essen/extend/cowboy/guide/http_req_resp.png"
|
||||
inkscape:export-xdpi="89.926643"
|
||||
inkscape:export-ydpi="89.926643"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan5373-5"
|
||||
x="146.53125"
|
||||
y="233.42836">parser</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#77823c;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
|
||||
x="146.53125"
|
||||
y="407.78009"
|
||||
id="text5371-5"
|
||||
sodipodi:linespacing="125%"
|
||||
inkscape:export-filename="/home/essen/extend/cowboy/guide/http_req_resp.png"
|
||||
inkscape:export-xdpi="89.926643"
|
||||
inkscape:export-ydpi="89.926643"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan5373-0"
|
||||
x="146.53125"
|
||||
y="407.78009">router</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#6d8e41;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
|
||||
x="-58.692513"
|
||||
y="53.112247"
|
||||
id="text5371-2"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan5373-6"
|
||||
x="-58.692513"
|
||||
y="53.112247">some text</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#6d8e41;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
|
||||
x="147.00391"
|
||||
y="321.39722"
|
||||
id="text5371-2-3"
|
||||
sodipodi:linespacing="125%"
|
||||
inkscape:export-filename="/home/essen/extend/cowboy/guide/http_req_resp.png"
|
||||
inkscape:export-xdpi="89.926643"
|
||||
inkscape:export-ydpi="89.926643"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan5373-6-7"
|
||||
x="147.00391"
|
||||
y="321.39722">onrequest</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#6d8e41;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
|
||||
x="146.53125"
|
||||
y="495.07318"
|
||||
id="text5371-2-3-0"
|
||||
sodipodi:linespacing="125%"
|
||||
inkscape:export-filename="/home/essen/extend/cowboy/guide/http_req_resp.png"
|
||||
inkscape:export-xdpi="89.926643"
|
||||
inkscape:export-ydpi="89.926643"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan5373-6-7-3"
|
||||
x="146.53125"
|
||||
y="495.07318">handler</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#6d8e41;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
|
||||
x="-446.99591"
|
||||
y="63.078125"
|
||||
id="text5371-2-3-0-7"
|
||||
sodipodi:linespacing="125%"
|
||||
transform="matrix(0,-1,1,0,0,0)"
|
||||
inkscape:export-filename="/home/essen/extend/cowboy/guide/http_req_resp.png"
|
||||
inkscape:export-xdpi="89.926643"
|
||||
inkscape:export-ydpi="89.926643"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan5373-6-7-3-9"
|
||||
x="-446.99591"
|
||||
y="63.078125">middlewares</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#9b3b1c;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
|
||||
x="-58.692513"
|
||||
y="236.95154"
|
||||
id="text5371-4"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan5373-9"
|
||||
x="-58.692513"
|
||||
y="236.95154">some text</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#9b3b1c;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
|
||||
x="147.00391"
|
||||
y="60.912468"
|
||||
id="text5371-4-0"
|
||||
sodipodi:linespacing="125%"
|
||||
inkscape:export-filename="/home/essen/extend/cowboy/guide/http_req_resp.png"
|
||||
inkscape:export-xdpi="89.926643"
|
||||
inkscape:export-ydpi="89.926643"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan5373-9-2"
|
||||
x="147.00391"
|
||||
y="60.912468">client</tspan></text>
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#rect5273-7"
|
||||
id="use5668"
|
||||
transform="translate(589.05532,207.03588)"
|
||||
width="744.09448"
|
||||
height="1052.3622"
|
||||
inkscape:export-filename="/home/essen/extend/cowboy/guide/http_req_resp.png"
|
||||
inkscape:export-xdpi="89.926643"
|
||||
inkscape:export-ydpi="89.926643" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#rect5273"
|
||||
id="use5670"
|
||||
transform="translate(589.05538,355.27934)"
|
||||
width="744.09448"
|
||||
height="1052.3622"
|
||||
inkscape:export-filename="/home/essen/extend/cowboy/guide/http_req_resp.png"
|
||||
inkscape:export-xdpi="89.926643"
|
||||
inkscape:export-ydpi="89.926643" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#use5355"
|
||||
id="use5672"
|
||||
transform="translate(270.07946,434.91762)"
|
||||
width="744.09448"
|
||||
height="1052.3622"
|
||||
inkscape:export-filename="/home/essen/extend/cowboy/guide/http_req_resp.png"
|
||||
inkscape:export-xdpi="89.926643"
|
||||
inkscape:export-ydpi="89.926643" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#text5371-4-0"
|
||||
id="use5674"
|
||||
transform="translate(270.29655,434.16115)"
|
||||
width="744.09448"
|
||||
height="1052.3622"
|
||||
inkscape:export-filename="/home/essen/extend/cowboy/guide/http_req_resp.png"
|
||||
inkscape:export-xdpi="89.926643"
|
||||
inkscape:export-ydpi="89.926643" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#77823c;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
|
||||
x="417.30829"
|
||||
y="321.42792"
|
||||
id="text5371-9"
|
||||
sodipodi:linespacing="125%"
|
||||
inkscape:export-filename="/home/essen/extend/cowboy/guide/http_req_resp.png"
|
||||
inkscape:export-xdpi="89.926643"
|
||||
inkscape:export-ydpi="89.926643"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan5373-57"
|
||||
x="417.30829"
|
||||
y="321.42792">reply</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#6d8e41;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
|
||||
x="417.30829"
|
||||
y="407.77994"
|
||||
id="text5371-2-0"
|
||||
sodipodi:linespacing="125%"
|
||||
inkscape:export-filename="/home/essen/extend/cowboy/guide/http_req_resp.png"
|
||||
inkscape:export-xdpi="89.926643"
|
||||
inkscape:export-ydpi="89.926643"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan5373-6-8"
|
||||
x="417.30829"
|
||||
y="407.77994">onresponse</tspan></text>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 21 KiB |
58
doc/src/guide/index.ezdoc
Normal file
|
@ -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
|
49
doc/src/guide/introduction.ezdoc
Normal file
|
@ -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.
|
141
doc/src/guide/loop_handlers.ezdoc
Normal file
|
@ -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.
|
69
doc/src/guide/middlewares.ezdoc
Normal file
|
@ -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`.
|
215
doc/src/guide/modern_web.ezdoc
Normal file
|
@ -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.
|
50
doc/src/guide/multipart_intro.ezdoc
Normal file
|
@ -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.
|
115
doc/src/guide/multipart_req.ezdoc
Normal file
|
@ -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.
|
301
doc/src/guide/req.ezdoc
Normal file
|
@ -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.
|
158
doc/src/guide/req_body.ezdoc
Normal file
|
@ -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}]).
|
||||
```
|
220
doc/src/guide/resource_design.ezdoc
Normal file
|
@ -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.
|
197
doc/src/guide/resp.ezdoc
Normal file
|
@ -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">>}
|
||||
], "<html><head>Hello world!</head><body><p>Hats off!</p></body></html>", Req).
|
||||
```
|
||||
|
||||
Note that the reply is sent immediately.
|
||||
|
||||
:: Chunked reply
|
||||
|
||||
You can also stream the response body. First, you need to
|
||||
initiate the reply by sending the response status code.
|
||||
Then you can send the body in chunks of arbitrary size.
|
||||
|
||||
``` erlang
|
||||
{ok, Req2} = cowboy_req:chunked_reply(200, Req),
|
||||
ok = cowboy_req:chunk("Hello...", Req2),
|
||||
ok = cowboy_req:chunk("chunked...", Req2),
|
||||
ok = cowboy_req:chunk("world!!", Req2).
|
||||
```
|
||||
|
||||
You should make sure to match on `ok` as an error may be
|
||||
returned.
|
||||
|
||||
While it is possible to send a chunked response without
|
||||
a content-type header, it is still recommended. You can
|
||||
set this header or any other just like for normal replies.
|
||||
|
||||
``` erlang
|
||||
{ok, Req2} = cowboy_req:chunked_reply(200, [
|
||||
{<<"content-type">>, <<"text/html">>}
|
||||
], Req),
|
||||
ok = cowboy_req:chunk("<html><head>Hello world!</head>", Req2),
|
||||
ok = cowboy_req:chunk("<body><p>Hats off!</p></body></html>", Req2).
|
||||
```
|
||||
|
||||
Note that the reply and each chunk following it are sent
|
||||
immediately.
|
||||
|
||||
:: Preset response headers
|
||||
|
||||
You can define response headers in advance. They will be
|
||||
merged into the headers given in the reply call. Headers
|
||||
in the reply call override preset response headers which
|
||||
override the default Cowboy headers.
|
||||
|
||||
``` erlang
|
||||
Req2 = cowboy_req:set_resp_header(<<"allow">>, "GET", Req).
|
||||
```
|
||||
|
||||
You can check if a response header has already been set.
|
||||
This will only check the response headers that you set,
|
||||
and not the ones Cowboy will add when actually sending
|
||||
the reply.
|
||||
|
||||
``` erlang
|
||||
cowboy_req:has_resp_header(<<"allow">>, Req).
|
||||
```
|
||||
|
||||
It will return `true` if the header is defined, and `false`
|
||||
otherwise.
|
||||
|
||||
Finally, you can also delete a preset response header if
|
||||
needed. If you do, it will not be sent.
|
||||
|
||||
``` erlang
|
||||
Req2 = cowboy_req:delete_resp_header(<<"allow">>, Req).
|
||||
```
|
||||
|
||||
:: Preset response body
|
||||
|
||||
You can set the response body in advance. Note that this
|
||||
body will be ignored if you then choose to send a chunked
|
||||
reply, or if you send a reply with an explicit body.
|
||||
|
||||
``` erlang
|
||||
Req2 = cowboy_req:set_resp_body("Hello world!", Req).
|
||||
```
|
||||
|
||||
You can also set a fun that will be called when it is time
|
||||
to send the body. There are three different ways of doing
|
||||
that.
|
||||
|
||||
If you know the length of the body that needs to be sent,
|
||||
you should specify it, as it will help clients determine
|
||||
the remaining download time and allow them to inform the
|
||||
user.
|
||||
|
||||
``` erlang
|
||||
F = fun (Socket, Transport) ->
|
||||
Transport:send(Socket, "Hello world!")
|
||||
end,
|
||||
Req2 = cowboy_req:set_resp_body_fun(12, F, Req).
|
||||
```
|
||||
|
||||
If you do not know the length of the body, you should use
|
||||
a chunked response body fun instead.
|
||||
|
||||
``` erlang
|
||||
F = fun (SendChunk) ->
|
||||
Body = lists:duplicate(random:uniform(1024, $a)),
|
||||
SendChunk(Body)
|
||||
end,
|
||||
Req2 = cowboy_req:set_resp_body_fun(chunked, F, Req).
|
||||
```
|
||||
|
||||
Finally, you can also send data on the socket directly,
|
||||
without knowing the length in advance. Cowboy may be
|
||||
forced to close the connection at the end of the response
|
||||
though depending on the protocol capabilities.
|
||||
|
||||
``` erlang
|
||||
F = fun (Socket, Transport) ->
|
||||
Body = lists:duplicate(random:uniform(1024, $a)),
|
||||
Transport:send(Socket, Body)
|
||||
end,
|
||||
Req2 = cowboy_req:set_resp_body_fun(F, Req).
|
||||
```
|
||||
|
||||
:: Sending files
|
||||
|
||||
You can send files directly from disk without having to
|
||||
read them. Cowboy will use the `sendfile` syscall when
|
||||
possible, which means that the file is sent to the socket
|
||||
directly from the kernel, which is a lot more performant
|
||||
than doing it from userland.
|
||||
|
||||
Again, it is recommended to set the size of the file if it
|
||||
can be known in advance.
|
||||
|
||||
``` erlang
|
||||
F = fun (Socket, Transport) ->
|
||||
Transport:sendfile(Socket, "priv/styles.css")
|
||||
end,
|
||||
Req2 = cowboy_req:set_resp_body_fun(FileSize, F, Req).
|
||||
```
|
||||
|
||||
Please see the Ranch guide for more information about
|
||||
sending files.
|
BIN
doc/src/guide/rest_cond.png
Normal file
After Width: | Height: | Size: 109 KiB |
1656
doc/src/guide/rest_cond.svg
Normal file
After Width: | Height: | Size: 76 KiB |
BIN
doc/src/guide/rest_conneg.png
Normal file
After Width: | Height: | Size: 76 KiB |
1135
doc/src/guide/rest_conneg.svg
Normal file
After Width: | Height: | Size: 52 KiB |
BIN
doc/src/guide/rest_delete.png
Normal file
After Width: | Height: | Size: 119 KiB |
1718
doc/src/guide/rest_delete.svg
Normal file
After Width: | Height: | Size: 80 KiB |
247
doc/src/guide/rest_flowcharts.ezdoc
Normal file
|
@ -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.
|
BIN
doc/src/guide/rest_get_head.png
Normal file
After Width: | Height: | Size: 98 KiB |
1523
doc/src/guide/rest_get_head.svg
Normal file
After Width: | Height: | Size: 70 KiB |
135
doc/src/guide/rest_handlers.ezdoc
Normal file
|
@ -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
|
BIN
doc/src/guide/rest_options.png
Normal file
After Width: | Height: | Size: 8.3 KiB |
387
doc/src/guide/rest_options.svg
Normal file
|
@ -0,0 +1,387 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="744.09448819"
|
||||
height="1052.3622047"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.4 r9939"
|
||||
sodipodi:docname="rest_options.svg"
|
||||
inkscape:export-filename="/home/essen/Dropbox/Public/drawing.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90">
|
||||
<defs
|
||||
id="defs4">
|
||||
<linearGradient
|
||||
id="linearGradient5265">
|
||||
<stop
|
||||
style="stop-color:#69d2e7;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop5267" />
|
||||
<stop
|
||||
style="stop-color:#69d2e7;stop-opacity:0.58823532;"
|
||||
offset="1"
|
||||
id="stop5269" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient5251">
|
||||
<stop
|
||||
style="stop-color:#69d2e7;stop-opacity:0.78431374;"
|
||||
offset="0"
|
||||
id="stop5253" />
|
||||
<stop
|
||||
id="stop5263"
|
||||
offset="0.5"
|
||||
style="stop-color:#69d2e7;stop-opacity:1;" />
|
||||
<stop
|
||||
style="stop-color:#69d2e7;stop-opacity:0.39215687;"
|
||||
offset="1"
|
||||
id="stop5255" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient5233"
|
||||
osb:paint="solid">
|
||||
<stop
|
||||
style="stop-color:#69d2e7;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop5235" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="1"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1.0000001"
|
||||
inkscape:cx="166.77748"
|
||||
inkscape:cy="548.36436"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1402"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="38"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:snap-global="true"
|
||||
showguides="true">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid5357"
|
||||
empspacing="5"
|
||||
visible="true"
|
||||
enabled="true"
|
||||
snapvisiblegridlinesonly="true" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<path
|
||||
inkscape:export-ydpi="89.926643"
|
||||
inkscape:export-xdpi="89.926643"
|
||||
inkscape:export-filename="/home/essen/extend/cowboy/guide/http_req_resp.png"
|
||||
inkscape:connector-curvature="0"
|
||||
id="use5777"
|
||||
d="m -360.31658,371.70113 203.00246,0.045"
|
||||
style="fill:none;stroke:#6d8e41;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:1.99999999, 3.99999998;stroke-dashoffset:0" />
|
||||
<g
|
||||
transform="translate(205.92143,-296.03137)"
|
||||
id="g5650-7">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path5570-9"
|
||||
d="m -57.78256,351.41962 0,52.3259"
|
||||
style="opacity:0.8;fill:none;stroke:#6d8e41;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||
<path
|
||||
transform="matrix(0.58787746,0,0,0.58787746,73.160466,163.35774)"
|
||||
inkscape:transform-center-y="2.1823437"
|
||||
d="m -222.73865,430.10821 -12.85982,-22.27386 25.71964,0 z"
|
||||
inkscape:randomized="0"
|
||||
inkscape:rounded="0"
|
||||
inkscape:flatsided="true"
|
||||
sodipodi:arg2="2.6179939"
|
||||
sodipodi:arg1="1.5707963"
|
||||
sodipodi:r2="7.4246211"
|
||||
sodipodi:r1="14.849242"
|
||||
sodipodi:cy="415.25897"
|
||||
sodipodi:cx="-222.73865"
|
||||
sodipodi:sides="3"
|
||||
id="path5576-0"
|
||||
style="opacity:0.8;fill:#6d8e41;fill-opacity:1;fill-rule:nonzero;stroke:#6d8e41;stroke-width:0;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
sodipodi:type="star" />
|
||||
</g>
|
||||
<g
|
||||
transform="translate(205.92143,-212.00698)"
|
||||
id="g5650-9">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path5570-7"
|
||||
d="m -57.78256,351.41962 0,52.3259"
|
||||
style="opacity:0.8;fill:none;stroke:#6d8e41;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||
<path
|
||||
transform="matrix(0.58787746,0,0,0.58787746,73.160466,163.35774)"
|
||||
inkscape:transform-center-y="2.1823437"
|
||||
d="m -222.73865,430.10821 -12.85982,-22.27386 25.71964,0 z"
|
||||
inkscape:randomized="0"
|
||||
inkscape:rounded="0"
|
||||
inkscape:flatsided="true"
|
||||
sodipodi:arg2="2.6179939"
|
||||
sodipodi:arg1="1.5707963"
|
||||
sodipodi:r2="7.4246211"
|
||||
sodipodi:r1="14.849242"
|
||||
sodipodi:cy="415.25897"
|
||||
sodipodi:cx="-222.73865"
|
||||
sodipodi:sides="3"
|
||||
id="path5576-09"
|
||||
style="opacity:0.8;fill:#6d8e41;fill-opacity:1;fill-rule:nonzero;stroke:#6d8e41;stroke-width:0;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
sodipodi:type="star" />
|
||||
</g>
|
||||
<rect
|
||||
style="fill:#effab4;fill-opacity:1;fill-rule:nonzero;stroke:#c7d28c;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
id="rect5273-1-28"
|
||||
width="210.17955"
|
||||
height="35.209244"
|
||||
x="43.049091"
|
||||
y="204.67757"
|
||||
rx="15" />
|
||||
<rect
|
||||
style="fill:#d1f2a5;fill-opacity:1;fill-rule:nonzero;stroke:#a9ca7d;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
id="rect5273-1"
|
||||
width="210.17955"
|
||||
height="35.209244"
|
||||
x="43.049091"
|
||||
y="121.0042"
|
||||
rx="15" />
|
||||
<g
|
||||
id="g5650"
|
||||
transform="translate(0,-0.47597102)">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path5570"
|
||||
d="m -57.78256,351.41962 0,52.3259"
|
||||
style="opacity:0.8;fill:none;stroke:#6d8e41;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||
<path
|
||||
transform="matrix(0.58787746,0,0,0.58787746,73.160466,163.35774)"
|
||||
inkscape:transform-center-y="2.1823437"
|
||||
d="m -222.73865,430.10821 -12.85982,-22.27386 25.71964,0 z"
|
||||
inkscape:randomized="0"
|
||||
inkscape:rounded="0"
|
||||
inkscape:flatsided="true"
|
||||
sodipodi:arg2="2.6179939"
|
||||
sodipodi:arg1="1.5707963"
|
||||
sodipodi:r2="7.4246211"
|
||||
sodipodi:r1="14.849242"
|
||||
sodipodi:cy="415.25897"
|
||||
sodipodi:cx="-222.73865"
|
||||
sodipodi:sides="3"
|
||||
id="path5576"
|
||||
style="opacity:0.8;fill:#6d8e41;fill-opacity:1;fill-rule:nonzero;stroke:#6d8e41;stroke-width:0;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
sodipodi:type="star" />
|
||||
</g>
|
||||
<rect
|
||||
style="fill:#d1f2a5;fill-opacity:1;fill-rule:nonzero;stroke:#a9ca7d;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
id="rect5273"
|
||||
width="104.5895"
|
||||
height="36.392323"
|
||||
x="-224.02068"
|
||||
y="29.41218"
|
||||
rx="15" />
|
||||
<rect
|
||||
style="fill:#effab4;fill-opacity:1;fill-rule:nonzero;stroke:#c7d28c;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
id="rect5273-7"
|
||||
width="104.5895"
|
||||
height="36.392323"
|
||||
x="-224.02068"
|
||||
y="90.691978"
|
||||
rx="15" />
|
||||
<rect
|
||||
style="fill:#ffc48c;fill-opacity:1;fill-rule:nonzero;stroke:#d79c64;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
id="rect5273-2"
|
||||
width="104.5895"
|
||||
height="36.392323"
|
||||
x="-224.02068"
|
||||
y="151.97169"
|
||||
rx="15" />
|
||||
<rect
|
||||
style="fill:#ff9f80;fill-opacity:1;fill-rule:nonzero;stroke:#d77758;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
id="rect5273-22"
|
||||
width="104.5895"
|
||||
height="36.392323"
|
||||
x="-224.02068"
|
||||
y="213.25146"
|
||||
rx="15" />
|
||||
<rect
|
||||
style="fill:#f56991;fill-opacity:1;fill-rule:nonzero;stroke:#cd4169;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
id="rect5273-8"
|
||||
width="104.5895"
|
||||
height="36.392323"
|
||||
x="-224.02068"
|
||||
y="274.53128"
|
||||
rx="15" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#rect5273-22"
|
||||
id="use5355"
|
||||
transform="translate(319.86479,-176.50006)"
|
||||
width="744.09448"
|
||||
height="1052.3622"
|
||||
inkscape:export-filename="/home/essen/extend/cowboy/guide/http_req_resp.png"
|
||||
inkscape:export-xdpi="89.926643"
|
||||
inkscape:export-ydpi="89.926643" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#77823c;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
|
||||
x="-58.692513"
|
||||
y="114.39204"
|
||||
id="text5371"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan5373"
|
||||
x="-58.692513"
|
||||
y="114.39204">some text</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#6d8e41;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
|
||||
x="-58.692513"
|
||||
y="53.112247"
|
||||
id="text5371-2"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan5373-6"
|
||||
x="-58.692513"
|
||||
y="53.112247">some text</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#9b3b1c;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
|
||||
x="-58.692513"
|
||||
y="236.95154"
|
||||
id="text5371-4"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan5373-9"
|
||||
x="-58.692513"
|
||||
y="236.95154">some text</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#9b3b1c;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
|
||||
x="147.00391"
|
||||
y="60.912468"
|
||||
id="text5371-4-0"
|
||||
sodipodi:linespacing="125%"
|
||||
inkscape:export-filename="/home/essen/extend/cowboy/guide/http_req_resp.png"
|
||||
inkscape:export-xdpi="89.926643"
|
||||
inkscape:export-ydpi="89.926643"><tspan
|
||||
sodipodi:role="line"
|
||||
x="147.00391"
|
||||
y="60.912468"
|
||||
id="tspan17171">start</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#6d8e41;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
|
||||
x="148.13106"
|
||||
y="142.80627"
|
||||
id="text5371-2-95"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan5373-6-80"
|
||||
x="148.13106"
|
||||
y="142.80627">options</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#6d8e41;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
|
||||
x="147.80684"
|
||||
y="226.4736"
|
||||
id="text5371-2-32"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan5373-6-27"
|
||||
x="147.80684"
|
||||
y="226.4736">200 OK</tspan></text>
|
||||
<rect
|
||||
style="fill:#d1f2a5;fill-opacity:1;fill-rule:nonzero;stroke:#a9ca7d;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
id="rect5273-1-2"
|
||||
width="210.17955"
|
||||
height="35.209244"
|
||||
x="-489.75586"
|
||||
y="33.4944"
|
||||
rx="15" />
|
||||
<g
|
||||
transform="matrix(0,-1,1,0,-513.31414,353.05561)"
|
||||
id="g5650-2">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path5570-2"
|
||||
d="m -57.78256,275.13761 0,129.13992"
|
||||
style="opacity:0.8;fill:none;stroke:#9b3b1c;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||
<path
|
||||
transform="matrix(0.58787746,0,0,0.58787746,73.160466,163.35774)"
|
||||
inkscape:transform-center-y="2.1823437"
|
||||
d="m -222.73865,430.10821 -12.85982,-22.27386 25.71964,0 z"
|
||||
inkscape:randomized="0"
|
||||
inkscape:rounded="0"
|
||||
inkscape:flatsided="true"
|
||||
sodipodi:arg2="2.6179939"
|
||||
sodipodi:arg1="1.5707963"
|
||||
sodipodi:r2="7.4246211"
|
||||
sodipodi:r1="14.849242"
|
||||
sodipodi:cy="415.25897"
|
||||
sodipodi:cx="-222.73865"
|
||||
sodipodi:sides="3"
|
||||
id="path5576-12"
|
||||
style="opacity:0.8;fill:#9b3b1c;fill-opacity:1;fill-rule:nonzero;stroke:#9b3b1c;stroke-width:0;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
sodipodi:type="star" />
|
||||
</g>
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#a9ca7d;stroke-width:2.44279909;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
id="rect5367"
|
||||
width="207.05719"
|
||||
height="171.55719"
|
||||
x="-373.52859"
|
||||
y="458.58362"
|
||||
rx="11.072577"
|
||||
inkscape:export-filename="/home/essen/extend/cowboy/guide/http_req_resp.png"
|
||||
inkscape:export-xdpi="89.926643"
|
||||
inkscape:export-ydpi="89.926643" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#6d8e41;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
|
||||
x="-544.69421"
|
||||
y="-354.17184"
|
||||
id="text5371-2-3-0-7"
|
||||
sodipodi:linespacing="125%"
|
||||
transform="matrix(0,-1,1,0,0,0)"
|
||||
inkscape:export-filename="/home/essen/extend/cowboy/guide/http_req_resp.png"
|
||||
inkscape:export-xdpi="89.926643"
|
||||
inkscape:export-ydpi="89.926643"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan5373-6-7-3-9"
|
||||
x="-544.69421"
|
||||
y="-354.17184">middlewares</tspan></text>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 16 KiB |
159
doc/src/guide/rest_principles.ezdoc
Normal file
|
@ -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.
|
BIN
doc/src/guide/rest_put_post_patch.png
Normal file
After Width: | Height: | Size: 214 KiB |
2856
doc/src/guide/rest_put_post_patch.svg
Normal file
After Width: | Height: | Size: 134 KiB |
BIN
doc/src/guide/rest_start.png
Normal file
After Width: | Height: | Size: 115 KiB |
1468
doc/src/guide/rest_start.svg
Normal file
After Width: | Height: | Size: 65 KiB |
249
doc/src/guide/routing.ezdoc
Normal file
|
@ -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.
|
167
doc/src/guide/static_handlers.ezdoc
Normal file
|
@ -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}]}}
|
||||
```
|
36
doc/src/guide/upgrade_protocol.ezdoc
Normal file
|
@ -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.
|
222
doc/src/guide/ws_handlers.ezdoc
Normal file
|
@ -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.
|
42
doc/src/guide/ws_protocol.ezdoc
Normal file
|
@ -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.
|
101
doc/src/manual/cowboy.ezdoc
Normal file
|
@ -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.
|
23
doc/src/manual/cowboy_app.ezdoc
Normal file
|
@ -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.
|
24
doc/src/manual/cowboy_handler.ezdoc
Normal file
|
@ -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.
|
57
doc/src/manual/cowboy_http_handler.ezdoc
Normal file
|
@ -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.
|
91
doc/src/manual/cowboy_loop_handler.ezdoc
Normal file
|
@ -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.
|
54
doc/src/manual/cowboy_middleware.ezdoc
Normal file
|
@ -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.
|
84
doc/src/manual/cowboy_protocol.ezdoc
Normal file
|
@ -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.
|
704
doc/src/manual/cowboy_req.ezdoc
Normal file
|
@ -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.
|
561
doc/src/manual/cowboy_rest.ezdoc
Normal file
|
@ -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.
|
70
doc/src/manual/cowboy_router.ezdoc
Normal file
|
@ -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.
|
43
doc/src/manual/cowboy_spdy.ezdoc
Normal file
|
@ -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.
|
32
doc/src/manual/cowboy_static.ezdoc
Normal file
|
@ -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()}]}}
|
32
doc/src/manual/cowboy_sub_protocol.ezdoc
Normal file
|
@ -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.
|
36
doc/src/manual/cowboy_websocket.ezdoc
Normal file
|
@ -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.
|
133
doc/src/manual/cowboy_websocket_handler.ezdoc
Normal file
|
@ -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.
|
151
doc/src/manual/http_status_codes.ezdoc
Normal file
|
@ -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.
|
20
doc/src/manual/index.ezdoc
Normal file
|
@ -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
|