mirror of
https://github.com/ninenines/cowboy.git
synced 2025-07-14 20:30:23 +00:00
Convert the documentation to Asciidoc
A few small revisions were made, and Erlang.mk has been updated.
This commit is contained in:
parent
b7d666cfc7
commit
4023e7f4e4
55 changed files with 5701 additions and 1889 deletions
|
@ -1,11 +1,12 @@
|
|||
::: Architecture
|
||||
[[architecture]]
|
||||
== 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
|
||||
=== 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
|
||||
|
@ -20,7 +21,7 @@ up before terminating the handling of the current request. This may
|
|||
include cleaning up the process dictionary, timers, monitoring and
|
||||
more.
|
||||
|
||||
:: Binaries
|
||||
=== Binaries
|
||||
|
||||
It uses binaries. Binaries are more efficient than lists for
|
||||
representing strings because they take less memory space. Processing
|
||||
|
@ -28,14 +29,14 @@ 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
|
||||
=== 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
|
||||
=== 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
|
72
doc/src/guide/book.asciidoc
Normal file
72
doc/src/guide/book.asciidoc
Normal file
|
@ -0,0 +1,72 @@
|
|||
// a2x: --dblatex-opts "-P latex.output.revhistory=0 -P doc.publisher.show=0 -P index.numbered=0"
|
||||
// a2x: -d book --attribute tabsize=4
|
||||
|
||||
= Cowboy User Guide
|
||||
|
||||
= Rationale
|
||||
|
||||
include::modern_web.asciidoc[The modern Web]
|
||||
|
||||
include::erlang_web.asciidoc[Erlang and the Web]
|
||||
|
||||
= Introduction
|
||||
|
||||
include::introduction.asciidoc[Introduction]
|
||||
|
||||
include::getting_started.asciidoc[Getting started]
|
||||
|
||||
include::overview.asciidoc[Request overview]
|
||||
|
||||
include::erlang_beginners.asciidoc[Erlang for beginners]
|
||||
|
||||
= Configuration
|
||||
|
||||
include::routing.asciidoc[routing]
|
||||
|
||||
include::constraints.asciidoc[Constraints]
|
||||
|
||||
include::static_files.asciidoc[Static files]
|
||||
|
||||
= Request and response
|
||||
|
||||
include::handlers.asciidoc[Handlers]
|
||||
|
||||
include::loop_handlers.asciidoc[Loop handlers]
|
||||
|
||||
include::req.asciidoc[The Req object]
|
||||
|
||||
include::req_body.asciidoc[Reading the request body]
|
||||
|
||||
include::resp.asciidoc[Sending a response]
|
||||
|
||||
include::cookies.asciidoc[Using cookies]
|
||||
|
||||
include::multipart.asciidoc[Multipart]
|
||||
|
||||
= REST
|
||||
|
||||
include::rest_principles.asciidoc[REST principles]
|
||||
|
||||
include::rest_handlers.asciidoc[Handling REST requests]
|
||||
|
||||
include::rest_flowcharts.asciidoc[REST flowcharts]
|
||||
|
||||
include::resource_design.asciidoc[Designing a resource handler]
|
||||
|
||||
= Websocket
|
||||
|
||||
include::ws_protocol.asciidoc[The Websocket protocol]
|
||||
|
||||
include::ws_handlers.asciidoc[Handling Websocket connections]
|
||||
|
||||
= Internals
|
||||
|
||||
include::architecture.asciidoc[Architecture]
|
||||
|
||||
include::broken_clients.asciidoc[Dealing with broken clients]
|
||||
|
||||
include::middlewares.asciidoc[Middlewares]
|
||||
|
||||
include::sub_protocols.asciidoc[Sub protocols]
|
||||
|
||||
include::hooks.asciidoc[Hooks]
|
|
@ -1,4 +1,5 @@
|
|||
::: Dealing with broken clients
|
||||
[[broken_clients]]
|
||||
== Dealing with broken clients
|
||||
|
||||
There exists a very large number of implementations for the
|
||||
HTTP protocol. Most widely used clients, like browsers,
|
||||
|
@ -15,7 +16,7 @@ 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
|
||||
=== Lowercase headers
|
||||
|
||||
Cowboy converts all headers it receives to lowercase, and
|
||||
similarly sends back headers all in lowercase. Some broken
|
||||
|
@ -24,19 +25,20 @@ 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
|
||||
[source,erlang]
|
||||
----
|
||||
capitalize_hook(Status, Headers, Body, Req) ->
|
||||
Headers2 = [{cowboy_bstr:capitalize_token(N), V}
|
||||
|| {N, V} <- Headers],
|
||||
cowboy_req:reply(Status, Headers2, Body, Req).
|
||||
```
|
||||
----
|
||||
|
||||
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
|
||||
=== 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
|
||||
|
@ -44,7 +46,7 @@ implementations. There is no easy solution for this other than
|
|||
forking the project and editing the `cowboy_protocol` file
|
||||
directly.
|
||||
|
||||
:: Chunked transfer-encoding
|
||||
=== Chunked transfer-encoding
|
||||
|
||||
Sometimes an HTTP client advertises itself as HTTP/1.1 but
|
||||
does not support chunked transfer-encoding. This is invalid
|
||||
|
@ -55,6 +57,5 @@ 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
|
||||
[source,erlang]
|
||||
Req2 = cowboy_req:set(resp_state, waiting_stream).
|
||||
```
|
|
@ -1,4 +1,5 @@
|
|||
::: Constraints
|
||||
[[constraints]]
|
||||
== Constraints
|
||||
|
||||
Cowboy provides an optional constraints based validation feature
|
||||
when interacting with user input.
|
||||
|
@ -16,7 +17,7 @@ Finally, constraints can be used to not only validate input,
|
|||
but also convert said input into proper Erlang terms, all in
|
||||
one step.
|
||||
|
||||
:: Structure
|
||||
=== Structure
|
||||
|
||||
Constraints are provided as a list of fields and for each
|
||||
field a list of constraints for that field can be provided.
|
||||
|
@ -32,14 +33,16 @@ All constraints for a field will be used to match its value
|
|||
in the order they are given. If the value is modified by a
|
||||
constraint, the next constraint receives the updated value.
|
||||
|
||||
:: Built-in constraints
|
||||
=== Built-in constraints
|
||||
|
||||
|| Constraint Description
|
||||
|
|
||||
| int Convert binary value to integer
|
||||
| nonempty Ensures the binary value is non-empty
|
||||
[cols="<,<",options="header"]
|
||||
|===
|
||||
| Constraint | Description
|
||||
| int | Convert binary value to integer.
|
||||
| nonempty | Ensures the binary value is non-empty.
|
||||
|===
|
||||
|
||||
:: Custom constraint
|
||||
=== Custom constraint
|
||||
|
||||
In addition to the predefined constraints, Cowboy will accept
|
||||
a fun. This fun must accept one argument and return one of
|
|
@ -1,4 +1,5 @@
|
|||
::: Using cookies
|
||||
[[cookies]]
|
||||
== Using cookies
|
||||
|
||||
Cookies are a mechanism allowing applications to maintain
|
||||
state on top of the stateless HTTP protocol.
|
||||
|
@ -48,69 +49,73 @@ 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
|
||||
=== Setting cookies
|
||||
|
||||
By default, cookies you set are defined for the session.
|
||||
|
||||
``` erlang
|
||||
[source,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
|
||||
[source,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
|
||||
[source,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
|
||||
[source,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
|
||||
[source,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
|
||||
[source,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
|
||||
=== Reading cookies
|
||||
|
||||
As we said, the client sends cookies with every request.
|
||||
But unlike the server, the client only sends the cookie
|
||||
|
@ -124,16 +129,14 @@ to the values or providing a default if they are missing.
|
|||
You can parse the cookies and then use standard library
|
||||
functions to access individual values.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
Cookies = cowboy_req:parse_cookies(Req),
|
||||
{_, Lang} = lists:keyfind(<<"lang">>, 1, Cookies).
|
||||
```
|
||||
|
||||
You can match the cookies into a map.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
#{id := ID, lang := Lang} = cowboy_req:match_cookies([id, lang], Req).
|
||||
```
|
||||
|
||||
You can use constraints to validate the values while matching
|
||||
them. The following snippet will crash if the `id` cookie is
|
||||
|
@ -141,22 +144,20 @@ not an integer number or if the `lang` cookie is empty. Additionally
|
|||
the `id` cookie value will be converted to an integer term, saving
|
||||
you a conversion step.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
CookiesMap = cowboy_req:match_cookies([{id, int}, {lang, nonempty}], Req).
|
||||
```
|
||||
|
||||
Note that if two cookies share the same name, then the map value
|
||||
will be a list of the two cookie values.
|
||||
|
||||
Read more about ^constraints^.
|
||||
Read more about xref:constraints[constraints].
|
||||
|
||||
A default value can be provided. The default will be used
|
||||
if the `lang` cookie is not found. It will not be used if
|
||||
the cookie is found but has an empty value.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
#{lang := Lang} = cowboy_req:match_cookies([{lang, [], <<"en-US">>}], Req).
|
||||
```
|
||||
|
||||
If no default is provided and the value is missing, the
|
||||
query string is deemed invalid and the process will crash.
|
|
@ -1,4 +1,5 @@
|
|||
::: Erlang for beginners
|
||||
[[erlang_beginners]]
|
||||
== Erlang for beginners
|
||||
|
||||
Chances are you are interested in using Cowboy, but have
|
||||
no idea how to write an Erlang program. Fear not! This
|
||||
|
@ -8,10 +9,10 @@ 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!
|
||||
=== 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^,
|
||||
a book with the funny name of http://learnyousomeerlang.com[LYSE],
|
||||
as we affectionately call it.
|
||||
|
||||
It will get right into the syntax and quickly answer the questions
|
||||
|
@ -22,12 +23,12 @@ 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
|
||||
=== 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^.
|
||||
in his book http://pragprog.com/book/jaerlang2/programming-erlang[Programming Erlang].
|
||||
|
||||
Instead of going into every single details of the language,
|
||||
Joe focuses on the central concepts behind Erlang, and shows
|
|
@ -1,6 +1,7 @@
|
|||
::: Erlang and the Web
|
||||
[[erlang_web]]
|
||||
== Erlang and the Web
|
||||
|
||||
:: The Web is concurrent
|
||||
=== The Web is concurrent
|
||||
|
||||
When you access a website there is little concurrency
|
||||
involved. A few connections are opened and requests
|
||||
|
@ -53,7 +54,7 @@ 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
|
||||
=== 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
|
||||
|
@ -82,7 +83,7 @@ can guarantee stable low latency of operations.
|
|||
Erlang provides the guarantees that the soft real time Web
|
||||
requires.
|
||||
|
||||
:: The Web is asynchronous
|
||||
=== 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.
|
||||
|
@ -114,7 +115,7 @@ 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 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,
|
||||
|
@ -167,7 +168,7 @@ 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 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
|
|
@ -1,4 +1,5 @@
|
|||
::: Getting started
|
||||
[[getting_started]]
|
||||
== Getting started
|
||||
|
||||
Erlang is more than a language, it is also an operating system
|
||||
for your applications. Erlang developers rarely write standalone
|
||||
|
@ -12,44 +13,40 @@ 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.
|
||||
|
||||
:: Bootstrap
|
||||
=== Bootstrap
|
||||
|
||||
We are going to use the ^"erlang.mk^https://github.com/ninenines/erlang.mk
|
||||
We are going to use the https://github.com/ninenines/erlang.mk[Erlang.mk]
|
||||
build system. It also offers bootstrap features allowing us to
|
||||
quickly get started without having to deal with minute details.
|
||||
|
||||
First, let's create the directory for our application.
|
||||
|
||||
``` bash
|
||||
[source,bash]
|
||||
$ mkdir hello_erlang
|
||||
$ cd hello_erlang
|
||||
```
|
||||
|
||||
Then we need to download `erlang.mk`. Either use the following
|
||||
Then we need to download Erlang.mk. Either use the following
|
||||
command or download it manually.
|
||||
|
||||
``` bash
|
||||
[source,bash]
|
||||
$ wget https://raw.githubusercontent.com/ninenines/erlang.mk/master/erlang.mk
|
||||
```
|
||||
|
||||
We can now bootstrap our application. Since we are going to generate
|
||||
a release, we will also bootstrap it at the same time.
|
||||
|
||||
``` bash
|
||||
[source,bash]
|
||||
$ make -f erlang.mk bootstrap bootstrap-rel
|
||||
```
|
||||
|
||||
This creates a Makefile, a base application, and the release files
|
||||
necessary for creating the release. We can already build and start
|
||||
this release.
|
||||
|
||||
``` bash
|
||||
$ make
|
||||
...
|
||||
$ ./_rel/hello_erlang_release/bin/hello_erlang_release console
|
||||
[source,bash]
|
||||
----
|
||||
$ make run
|
||||
...
|
||||
(hello_erlang@127.0.0.1)1>
|
||||
```
|
||||
----
|
||||
|
||||
Entering the command `i().` will show the running processes, including
|
||||
one called `hello_erlang_sup`. This is the supervisor for our
|
||||
|
@ -59,64 +56,34 @@ The release currently does nothing. In the rest of this chapter we
|
|||
will add Cowboy as a dependency and write a simple "Hello world!"
|
||||
handler.
|
||||
|
||||
:: Cowboy setup
|
||||
=== Cowboy setup
|
||||
|
||||
To add Cowboy as a dependency to your application, you need to modify
|
||||
two files: the Makefile and the application resource file.
|
||||
|
||||
Modifying the Makefile allows the build system to know it needs to
|
||||
Modifying the 'Makefile' allows the build system to know it needs to
|
||||
fetch and compile Cowboy. To do that we simply need to add one line
|
||||
to our Makefile to make it look like this:
|
||||
|
||||
``` Makefile
|
||||
[source,make]
|
||||
PROJECT = hello_erlang
|
||||
DEPS = cowboy
|
||||
include erlang.mk
|
||||
```
|
||||
|
||||
Modifying the application resource file, `src/hello_erlang.app.src`,
|
||||
allows the build system to know it needs to include Cowboy in the
|
||||
release and start it automatically. This is a different step because
|
||||
some dependencies are only needed during development.
|
||||
|
||||
We are simply going to add `cowboy` to the list of `applications`,
|
||||
right after `stdlib`. Don't forget the comma separator.
|
||||
|
||||
``` erlang
|
||||
{application, hello_erlang, [
|
||||
{description, "Hello Erlang!"},
|
||||
{vsn, "0.1.0"},
|
||||
{modules, []},
|
||||
{registered, []},
|
||||
{applications, [
|
||||
kernel,
|
||||
stdlib,
|
||||
cowboy
|
||||
]},
|
||||
{mod, {hello_erlang_app, []}},
|
||||
{env, []}
|
||||
]}.
|
||||
```
|
||||
|
||||
You may want to set a description for the application while you
|
||||
are editing the file.
|
||||
|
||||
If you run `make` now and start the release, Cowboy will be included
|
||||
If you run `make run` now, Cowboy will be included in the release
|
||||
and started automatically. This is not enough however, as Cowboy
|
||||
doesn't do anything by default. We still need to tell Cowboy to
|
||||
listen for connections.
|
||||
|
||||
:: Listening for connections
|
||||
=== Listening for connections
|
||||
|
||||
We will do this when our application starts. It's a two step process.
|
||||
First we need to define and compile the dispatch list, a list of
|
||||
routes that Cowboy will use to map requests to handler modules.
|
||||
Then we tell Cowboy to listen for connections.
|
||||
|
||||
Open the `src/hello_erlang_app.erl` file and add the necessary
|
||||
Open the 'src/hello_erlang_app.erl' file and add the necessary
|
||||
code to the `start/2` function to make it look like this:
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
----
|
||||
start(_Type, _Args) ->
|
||||
Dispatch = cowboy_router:compile([
|
||||
{'_', [{"/", hello_handler, []}]}
|
||||
|
@ -125,19 +92,19 @@ start(_Type, _Args) ->
|
|||
[{env, [{dispatch, Dispatch}]}]
|
||||
),
|
||||
hello_erlang_sup:start_link().
|
||||
```
|
||||
----
|
||||
|
||||
The dispatch list is explained in great details in the
|
||||
^"Routing^routing^ chapter. For this tutorial we map the
|
||||
xref:routing[Routing] chapter. For this tutorial we map the
|
||||
path `/` to the handler module `hello_handler`. This module
|
||||
doesn't exist yet, we still have to write it.
|
||||
|
||||
If you build the release, start it and open ^http://localhost:8080
|
||||
now, you will get an error because the module is missing. Any
|
||||
other URL, like ^http://localhost:8080/test^, will result in a
|
||||
If you build and start the release, then open http://localhost:8080
|
||||
in your browser, you will get an error because the module is missing.
|
||||
Any other URL, like http://localhost:8080/test, will result in a
|
||||
404 error.
|
||||
|
||||
:: Handling requests
|
||||
=== Handling requests
|
||||
|
||||
Cowboy features different kinds of handlers, including REST
|
||||
and Websocket handlers. For this tutorial we will use a plain
|
||||
|
@ -145,25 +112,25 @@ HTTP handler.
|
|||
|
||||
First, let's generate a handler from a template.
|
||||
|
||||
``` bash
|
||||
[source,bash]
|
||||
$ make new t=cowboy_http n=hello_handler
|
||||
```
|
||||
|
||||
You can then open the `src/hello_handler.erl` file and modify
|
||||
You can then open the 'src/hello_handler.erl' file and modify
|
||||
the `init/2` function like this to send a reply.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
----
|
||||
init(Req, Opts) ->
|
||||
Req2 = cowboy_req:reply(200,
|
||||
[{<<"content-type">>, <<"text/plain">>}],
|
||||
<<"Hello Erlang!">>,
|
||||
Req),
|
||||
{ok, Req2, Opts}.
|
||||
```
|
||||
----
|
||||
|
||||
What the above code does is send a `200 OK` reply, with the
|
||||
`content-type` header set to `text/plain` and the response
|
||||
body set to `Hello Erlang!`.
|
||||
|
||||
If you build the release, start it and open ^http://localhost:8080
|
||||
If you run the release and open http://localhost:8080
|
||||
in your browser, you should get a nice `Hello Erlang!` displayed!
|
|
@ -1,22 +1,24 @@
|
|||
::: Handlers
|
||||
[[handlers]]
|
||||
== Handlers
|
||||
|
||||
Handlers are Erlang modules that handle HTTP requests.
|
||||
|
||||
:: Plain HTTP handlers
|
||||
=== Plain HTTP handlers
|
||||
|
||||
The most basic handler in Cowboy implements the mandatory
|
||||
`init/2` callback, manipulates the request, optionally
|
||||
sends a response and then returns.
|
||||
|
||||
This callback receives the ^"Req object^req and the options
|
||||
defined during the ^"router configuration^routing^.
|
||||
This callback receives the xref:req[Req object] and the options
|
||||
defined during the xref:routing[router configuration].
|
||||
|
||||
A handler that does nothing would look like this:
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
----
|
||||
init(Req, _Opts) ->
|
||||
{ok, Req, #state{}}.
|
||||
```
|
||||
----
|
||||
|
||||
Despite sending no reply, a `204 No Content` reply will be
|
||||
sent to the client, as Cowboy makes sure that a reply is
|
||||
|
@ -24,13 +26,14 @@ sent for every request.
|
|||
|
||||
We need to use the Req object for sending a reply.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
----
|
||||
init(Req, _Opts) ->
|
||||
Req2 = cowboy_req:reply(200, [
|
||||
{<<"content-type">>, <<"text/plain">>}
|
||||
], <<"Hello World!">>, Req),
|
||||
{ok, Req2, #state{}}.
|
||||
```
|
||||
----
|
||||
|
||||
As you can see we return a 3-tuple. `ok` means that the
|
||||
handler ran successfully. The Req object is returned as
|
||||
|
@ -43,7 +46,7 @@ in every subsequent callbacks to this handler. Plain HTTP
|
|||
handlers only have one additional callback, the optional
|
||||
`terminate/3`.
|
||||
|
||||
:: Other handlers
|
||||
=== Other handlers
|
||||
|
||||
The `init/2` callback can also be used to inform Cowboy
|
||||
that this is a different kind of handler and that Cowboy
|
||||
|
@ -51,38 +54,41 @@ should switch to it. To do this you simply need to return
|
|||
the module name of the handler type you want to switch to.
|
||||
|
||||
Cowboy comes with three handler types you can switch to:
|
||||
^"cowboy_rest^rest_handlers^, ^"cowboy_websocket^ws_handlers^
|
||||
and ^"cowboy_loop^loop_handlers^. In addition to those you
|
||||
xref:rest_handlers[cowboy_rest], xref:ws_handlers[cowboy_websocket]
|
||||
and xref:loop_handlers[cowboy_loop]. In addition to those you
|
||||
can define your own handler types.
|
||||
|
||||
Switching is simple. Instead of returning `ok`, you simply
|
||||
return the name of the handler type you want to use. The
|
||||
following snippet switches to a Websocket handler:
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
----
|
||||
init(Req, _Opts) ->
|
||||
{cowboy_websocket, Req, #state{}}.
|
||||
```
|
||||
----
|
||||
|
||||
You can also switch to your own custom handler type:
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
----
|
||||
init(Req, _Opts) ->
|
||||
{my_handler_type, Req, #state{}}.
|
||||
```
|
||||
----
|
||||
|
||||
How to implement a custom handler type is described in the
|
||||
^"Sub protocols^sub_protocols chapter.
|
||||
xref:sub_protocols[Sub protocols] chapter.
|
||||
|
||||
:: Cleaning up
|
||||
=== Cleaning up
|
||||
|
||||
All handlers coming with Cowboy allow the use of the optional
|
||||
`terminate/3` callback.
|
||||
|
||||
``` erlang
|
||||
[source,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
|
|
@ -1,9 +1,10 @@
|
|||
::: Hooks
|
||||
[[hooks]]
|
||||
== Hooks
|
||||
|
||||
Hooks allow the user to customize Cowboy's behavior during specific
|
||||
operations.
|
||||
|
||||
:: Onresponse
|
||||
=== Onresponse
|
||||
|
||||
The `onresponse` hook is called right before sending the response
|
||||
to the socket. It can be used for the purposes of logging responses,
|
||||
|
@ -16,7 +17,8 @@ explicitly provide all headers that are needed.
|
|||
|
||||
You can specify the `onresponse` hook when creating the listener.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
----
|
||||
cowboy:start_http(my_http_listener, 100,
|
||||
[{port, 8080}],
|
||||
[
|
||||
|
@ -24,13 +26,14 @@ cowboy:start_http(my_http_listener, 100,
|
|||
{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
|
||||
[source,erlang]
|
||||
----
|
||||
custom_404_hook(404, Headers, <<>>, Req) ->
|
||||
Body = <<"404 Not Found.">>,
|
||||
Headers2 = lists:keyreplace(<<"content-length">>, 1, Headers,
|
||||
|
@ -38,6 +41,6 @@ custom_404_hook(404, Headers, <<>>, Req) ->
|
|||
cowboy_req:reply(404, Headers2, Body, Req);
|
||||
custom_404_hook(_, _, _, Req) ->
|
||||
Req.
|
||||
```
|
||||
----
|
||||
|
||||
Again, make sure to always return the last request object obtained.
|
|
@ -1,58 +0,0 @@
|
|||
::: Cowboy User Guide
|
||||
|
||||
The Cowboy User Guide explores the modern Web and how to make
|
||||
best use of Cowboy for writing powerful Web applications.
|
||||
|
||||
:: Rationale
|
||||
|
||||
* ^"The modern Web^modern_web
|
||||
* ^"Erlang and the Web^erlang_web
|
||||
|
||||
:: Introduction
|
||||
|
||||
* ^"Introduction^introduction
|
||||
* ^"Getting started^getting_started
|
||||
* ^"Request overview^overview
|
||||
* ^"Erlang for beginners^erlang_beginners
|
||||
|
||||
:: Configuration
|
||||
|
||||
* ^"Routing^routing
|
||||
* ^"Constraints^constraints
|
||||
* ^"Static files^static_files
|
||||
|
||||
:: Request and response
|
||||
|
||||
* ^"Handlers^handlers
|
||||
* ^"The Req object^req
|
||||
* ^"Reading the request body^req_body
|
||||
* ^"Sending a response^resp
|
||||
* ^"Using cookies^cookies
|
||||
* ^"Multipart^multipart
|
||||
|
||||
:: 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
|
||||
|
||||
:: Push technology
|
||||
|
||||
* ^"Loop handlers^loop_handlers
|
||||
|
||||
:: Extensions
|
||||
|
||||
* ^"Middlewares^middlewares
|
||||
* ^"Sub protocols^sub_protocols
|
||||
* ^"Hooks^hooks
|
||||
|
||||
:: Internals
|
||||
|
||||
* ^"Architecture^architecture
|
||||
* ^"Dealing with broken clients^broken_clients
|
|
@ -1,4 +1,5 @@
|
|||
::: Introduction
|
||||
[[introduction]]
|
||||
== Introduction
|
||||
|
||||
Cowboy is a small, fast and modular HTTP server written in Erlang.
|
||||
|
||||
|
@ -14,14 +15,14 @@ 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
|
||||
=== Prerequisites
|
||||
|
||||
Beginner Erlang knowledge is recommended for reading this guide.
|
||||
|
||||
Knowledge of the HTTP protocol is recommended but not required, as it
|
||||
will be detailed throughout the guide.
|
||||
|
||||
:: Supported platforms
|
||||
=== Supported platforms
|
||||
|
||||
Cowboy is tested and supported on Linux.
|
||||
|
||||
|
@ -39,11 +40,11 @@ modifications but there is no guarantee that it will work as expected.
|
|||
|
||||
Cowboy uses the maps data type which was introduced in Erlang 17.0.
|
||||
|
||||
:: Versioning
|
||||
=== Versioning
|
||||
|
||||
Cowboy uses ^"Semantic Versioning 2.0.0^http://semver.org/^.
|
||||
Cowboy uses http://semver.org/[Semantic Versioning 2.0.0].
|
||||
|
||||
:: Conventions
|
||||
=== Conventions
|
||||
|
||||
In the HTTP protocol, the method name is case sensitive. All standard
|
||||
method names are uppercase.
|
|
@ -1,4 +1,5 @@
|
|||
::: Loop handlers
|
||||
[[loop_handlers]]
|
||||
== 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
|
||||
|
@ -24,7 +25,7 @@ and feed these messages to the `info/3` callback. It also features
|
|||
the `init/2` and `terminate/3` callbacks which work the same as
|
||||
for plain HTTP handlers.
|
||||
|
||||
:: Initialization
|
||||
=== Initialization
|
||||
|
||||
The `init/2` function must return a `cowboy_loop` tuple to enable
|
||||
loop handler behavior. This tuple may optionally contain
|
||||
|
@ -33,21 +34,23 @@ process enter hibernation until a message is received.
|
|||
|
||||
This snippet enables the loop handler.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
----
|
||||
init(Req, _Opts) ->
|
||||
{cowboy_loop, Req, #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
|
||||
[source,erlang]
|
||||
----
|
||||
init(Req, _Opts) ->
|
||||
{cowboy_loop, Req, #state{}, 30000, hibernate}.
|
||||
```
|
||||
----
|
||||
|
||||
:: Receive loop
|
||||
=== Receive loop
|
||||
|
||||
Once initialized, Cowboy will wait for messages to arrive
|
||||
in the process' mailbox. When a message arrives, Cowboy
|
||||
|
@ -58,13 +61,14 @@ The following snippet sends a reply when it receives a
|
|||
`reply` message from another process, or waits for another
|
||||
message otherwise.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
----
|
||||
info({reply, Body}, Req, State) ->
|
||||
Req2 = cowboy_req:reply(200, [], Body, Req),
|
||||
{stop, Req2, State};
|
||||
info(_Msg, Req, State) ->
|
||||
{ok, Req, State, hibernate}.
|
||||
```
|
||||
----
|
||||
|
||||
Do note that the `reply` tuple here may be any message
|
||||
and is simply an example.
|
||||
|
@ -81,7 +85,7 @@ This will instruct Cowboy to end the request.
|
|||
|
||||
Otherwise an `ok` tuple should be returned.
|
||||
|
||||
:: Streaming loop
|
||||
=== Streaming loop
|
||||
|
||||
Another common case well suited for loop handlers is
|
||||
streaming data received in the form of Erlang messages.
|
||||
|
@ -93,7 +97,8 @@ 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
|
||||
[source,erlang]
|
||||
----
|
||||
init(Req, _Opts) ->
|
||||
Req2 = cowboy_req:chunked_reply(200, [], Req),
|
||||
{cowboy_loop, Req2, #state{}}.
|
||||
|
@ -105,18 +110,18 @@ info({chunk, Chunk}, Req, State) ->
|
|||
{ok, Req, State};
|
||||
info(_Msg, Req, State) ->
|
||||
{ok, Req, State}.
|
||||
```
|
||||
----
|
||||
|
||||
:: Cleaning up
|
||||
==== 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 ^"Handlers chapter^handlers
|
||||
Please refer to the xref:handlers[Handlers chapter]
|
||||
for general instructions about cleaning up.
|
||||
|
||||
:: Timeout
|
||||
=== Timeout
|
||||
|
||||
By default Cowboy will not attempt to close the connection
|
||||
if there is no activity from the client. This is not always
|
||||
|
@ -132,7 +137,7 @@ 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
|
||||
=== Hibernate
|
||||
|
||||
To save memory, you may hibernate the process in between
|
||||
messages received. This is done by returning the atom
|
|
@ -1,4 +1,5 @@
|
|||
::: Middlewares
|
||||
[[middlewares]]
|
||||
== Middlewares
|
||||
|
||||
Cowboy delegates the request processing to middleware components.
|
||||
By default, two middlewares are defined, for the routing and handling
|
||||
|
@ -11,7 +12,7 @@ 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
|
||||
=== Usage
|
||||
|
||||
Middlewares only need to implement a single callback: `execute/2`.
|
||||
It is defined in the `cowboy_middleware` behavior.
|
||||
|
@ -34,7 +35,7 @@ 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
|
||||
=== Configuration
|
||||
|
||||
The middleware environment is defined as the `env` protocol option.
|
||||
In the previous chapters we saw it briefly when we needed to pass
|
||||
|
@ -56,13 +57,13 @@ 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
|
||||
=== 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
|
||||
=== Handler middleware
|
||||
|
||||
The handler middleware requires the `handler` and `handler_opts`
|
||||
values. It puts the result of the request handling into `result`.
|
|
@ -1,4 +1,5 @@
|
|||
::: The modern Web
|
||||
[[modern_web]]
|
||||
== 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
|
||||
|
@ -8,7 +9,7 @@ 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
|
||||
=== The prehistoric Web
|
||||
|
||||
HTTP was initially created to serve HTML pages and only
|
||||
had the GET method for retrieving them. This initial
|
||||
|
@ -29,7 +30,7 @@ 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
|
||||
|
||||
HTTP/1.1 quickly followed and added a keep-alive mechanism
|
||||
to allow using the same connection for many requests, as
|
||||
|
@ -47,7 +48,7 @@ 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
|
||||
=== REST
|
||||
|
||||
The design of HTTP/1.1 was influenced by the REST architectural
|
||||
style. REST, or REpresentational State Transfer, is a style of
|
||||
|
@ -72,7 +73,7 @@ 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
|
||||
=== XmlHttpRequest
|
||||
|
||||
Also know as AJAX, this technology allows Javascript code running
|
||||
on a web page to perform asynchronous requests to the server.
|
||||
|
@ -88,7 +89,7 @@ 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
|
||||
=== 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
|
||||
|
@ -116,7 +117,7 @@ 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
|
||||
|
||||
HTML5 is, of course, the HTML version after HTML4. But HTML5
|
||||
emerged to solve a specific problem: dynamic web applications.
|
||||
|
@ -140,7 +141,7 @@ events from the server.
|
|||
The solution went on to become HTML5. At the time of writing
|
||||
it is being standardized.
|
||||
|
||||
:: EventSource
|
||||
=== EventSource
|
||||
|
||||
EventSource, sometimes also called Server-Sent Events, is a
|
||||
technology allowing servers to push data to HTML5 applications.
|
||||
|
@ -159,7 +160,7 @@ 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
|
||||
|
||||
Websocket is a protocol built on top of HTTP/1.1 that provides
|
||||
a two-ways communication channel between the client and the
|
||||
|
@ -179,7 +180,7 @@ 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
|
||||
|
||||
SPDY is an attempt to reduce page loading time by opening a
|
||||
single connection per server, keeping it open for subsequent
|
||||
|
@ -203,7 +204,7 @@ 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
|
||||
|
||||
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
|
||||
|
@ -211,5 +212,3 @@ time of writing.
|
|||
|
||||
HTTP/2.0 is an asynchronous two-ways communication channel
|
||||
between two endpoints.
|
||||
|
||||
It is planned to be ready late 2014.
|
|
@ -1,4 +1,5 @@
|
|||
::: Multipart requests
|
||||
[[multipart]]
|
||||
== Multipart requests
|
||||
|
||||
Multipart originates from MIME, an Internet standard that
|
||||
extends the format of emails. Multipart messages are a
|
||||
|
@ -24,7 +25,7 @@ Req object directly.
|
|||
Cowboy defines two functions that allows you to get
|
||||
information about each part and read their contents.
|
||||
|
||||
:: Structure
|
||||
=== Structure
|
||||
|
||||
A multipart message is a list of parts. Parts may
|
||||
contain either a multipart message or a non-multipart
|
||||
|
@ -32,7 +33,7 @@ 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
|
||||
=== Form-data
|
||||
|
||||
In the normal case, when a form is submitted, the
|
||||
browser will use the `application/x-www-form-urlencoded`
|
||||
|
@ -55,7 +56,7 @@ 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.
|
||||
|
||||
:: Checking the content-type
|
||||
=== Checking the content-type
|
||||
|
||||
While there is a variety of multipart messages, the
|
||||
most common on the Web is `multipart/form-data`. It's
|
||||
|
@ -65,18 +66,20 @@ allows uploading files.
|
|||
You can quickly figure out if a multipart message
|
||||
has been sent by parsing the `content-type` header.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
----
|
||||
{<<"multipart">>, <<"form-data">>, _}
|
||||
= cowboy_req:parse_header(<<"content-type">>, Req).
|
||||
```
|
||||
----
|
||||
|
||||
:: Reading a multipart message
|
||||
=== 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
|
||||
[source,erlang]
|
||||
----
|
||||
multipart(Req) ->
|
||||
case cowboy_req:part(Req) of
|
||||
{ok, _Headers, Req2} ->
|
||||
|
@ -85,7 +88,7 @@ multipart(Req) ->
|
|||
{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
|
||||
|
@ -100,7 +103,8 @@ 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
|
||||
[source,erlang]
|
||||
----
|
||||
multipart(Req) ->
|
||||
case cowboy_req:part(Req) of
|
||||
{ok, Headers, Req2} ->
|
||||
|
@ -123,14 +127,14 @@ stream_file(Req) ->
|
|||
{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
|
||||
=== 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
|
||||
|
@ -140,7 +144,8 @@ you request the next part.
|
|||
The following snippet reads all part headers and skips
|
||||
all bodies:
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
----
|
||||
multipart(Req) ->
|
||||
case cowboy_req:part(Req) of
|
||||
{ok, _Headers, Req2} ->
|
||||
|
@ -148,7 +153,7 @@ multipart(Req) ->
|
|||
{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,
|
|
@ -1,10 +1,11 @@
|
|||
::: Request overview
|
||||
[[overview]]
|
||||
== Request overview
|
||||
|
||||
This chapter explains the different steps a request
|
||||
goes through until a response is sent, along with
|
||||
details of the Cowboy implementation.
|
||||
|
||||
:: Request/response
|
||||
=== Request/response
|
||||
|
||||
As you already know, HTTP clients connect to the server and
|
||||
send a request for a resource; the server then sends a
|
||||
|
@ -18,7 +19,7 @@ add like writing logs.
|
|||
|
||||
Requests take the following route in Cowboy:
|
||||
|
||||
^"HTTP request/response flowchart^!http_req_resp.png
|
||||
image::http_req_resp.png[HTTP request/response flowchart]
|
||||
|
||||
This shows the default middlewares, but they may be
|
||||
configured differently in your setup. The dark green
|
||||
|
@ -42,7 +43,7 @@ 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?
|
||||
=== And then?
|
||||
|
||||
Behavior depends on what protocol is in use.
|
||||
|
||||
|
@ -59,7 +60,7 @@ asynchronously on the same connection. Details on what
|
|||
this means for your application is described in this
|
||||
chapter.
|
||||
|
||||
:: Keep-alive (HTTP/1.1)
|
||||
=== Keep-alive (HTTP/1.1)
|
||||
|
||||
With HTTP/1.1, the connection may be left open for
|
||||
subsequent requests to come. This mechanism is called
|
||||
|
@ -79,11 +80,12 @@ as the reply is sent.
|
|||
|
||||
This snippet will force Cowboy to close the connection.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
----
|
||||
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
|
||||
|
@ -91,12 +93,13 @@ requests. This number can be changed by setting the
|
|||
`max_keepalive` configuration value when starting an
|
||||
HTTP listener.
|
||||
|
||||
``` erlang
|
||||
[source,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
|
||||
|
@ -106,7 +109,7 @@ 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)
|
||||
=== Pipelining (HTTP/1.1)
|
||||
|
||||
While HTTP is designed as a sequential protocol, with
|
||||
the client sending a request and then waiting for the
|
||||
|
@ -123,7 +126,7 @@ static files for example.
|
|||
|
||||
This is handled automatically by the server.
|
||||
|
||||
:: Asynchronous requests (SPDY)
|
||||
=== 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.
|
|
@ -1,10 +1,11 @@
|
|||
::: The Req object
|
||||
[[req]]
|
||||
== 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
|
||||
=== 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
|
||||
|
@ -27,7 +28,7 @@ For example, when streaming the request body, the
|
|||
function will return the body by chunks, one at a
|
||||
time, until there is none left.
|
||||
|
||||
:: Overview of the cowboy_req interface
|
||||
=== Overview of the cowboy_req interface
|
||||
|
||||
With the exception of functions manipulating the request
|
||||
body, all functions return a single value. Depending on
|
||||
|
@ -44,7 +45,7 @@ This chapter covers the access functions mainly. Cookies,
|
|||
request body and response functions are covered in their
|
||||
own chapters.
|
||||
|
||||
:: Request
|
||||
=== Request
|
||||
|
||||
When a client performs a request, it first sends a few required
|
||||
values. They are sent differently depending on the protocol
|
||||
|
@ -56,31 +57,28 @@ The method identifies the action. Standard methods include
|
|||
GET, HEAD, OPTIONS, PATCH, POST, PUT, DELETE. Method names
|
||||
are case sensitive.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
Method = 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
|
||||
[source,erlang]
|
||||
Host = cowboy_req:host(Req),
|
||||
Port = cowboy_req:port(Req),
|
||||
Path = cowboy_req:path(Req).
|
||||
```
|
||||
|
||||
The version used by the client can of course also be obtained.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
Version = 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
|
||||
=== Bindings
|
||||
|
||||
After routing the request, bindings are available. Bindings
|
||||
are these parts of the host or path that you chose to extract
|
||||
|
@ -89,61 +87,53 @@ 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
|
||||
[source,erlang]
|
||||
Binding = cowboy_req:binding(my_binding, Req).
|
||||
```
|
||||
|
||||
If you need a different value when the binding doesn't exist,
|
||||
you can change the default.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
Binding = cowboy_req:binding(my_binding, Req, 42).
|
||||
```
|
||||
|
||||
You can also obtain all bindings in one call. They will be
|
||||
returned as a list of key/value tuples.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
AllBindings = 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
|
||||
[source,erlang]
|
||||
HostInfo = 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
|
||||
[source,erlang]
|
||||
PathInfo = cowboy_req:path_info(Req).
|
||||
```
|
||||
|
||||
:: Query string
|
||||
=== Query string
|
||||
|
||||
The raw query string can be obtained directly.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
Qs = cowboy_req:qs(Req).
|
||||
```
|
||||
|
||||
You can parse the query string and then use standard library
|
||||
functions to access individual values.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
QsVals = cowboy_req:parse_qs(Req),
|
||||
{_, Lang} = lists:keyfind(<<"lang">>, 1, QsVals).
|
||||
```
|
||||
|
||||
You can match the query string into a map.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
#{id := ID, lang := Lang} = cowboy_req:match_qs([id, lang], Req).
|
||||
```
|
||||
|
||||
You can use constraints to validate the values while matching
|
||||
them. The following snippet will crash if the `id` value is
|
||||
|
@ -151,9 +141,8 @@ not an integer number or if the `lang` value is empty. Additionally
|
|||
the `id` value will be converted to an integer term, saving
|
||||
you a conversion step.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
QsMap = cowboy_req:match_qs([{id, int}, {lang, nonempty}], Req).
|
||||
```
|
||||
|
||||
Note that in the case of duplicate query string keys, the map
|
||||
value will become a list of the different values.
|
||||
|
@ -164,58 +153,50 @@ A default value can be provided. The default will be used
|
|||
if the `lang` key is not found. It will not be used if
|
||||
the key is found but has an empty value.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
#{lang := Lang} = cowboy_req:match_qs([{lang, [], <<"en-US">>}], Req).
|
||||
```
|
||||
|
||||
If no default is provided and the value is missing, the
|
||||
query string is deemed invalid and the process will crash.
|
||||
|
||||
:: Request URL
|
||||
=== Request URL
|
||||
|
||||
You can reconstruct the full URL of the resource.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
URL = cowboy_req:url(Req).
|
||||
```
|
||||
|
||||
You can also obtain only the base of the URL, excluding the
|
||||
path and query string.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
BaseURL = cowboy_req:host_url(Req).
|
||||
```
|
||||
|
||||
:: Headers
|
||||
=== 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
|
||||
[source,erlang]
|
||||
HeaderVal = cowboy_req:header(<<"content-type">>, Req).
|
||||
```
|
||||
|
||||
You can of course set a default in case the header is missing.
|
||||
|
||||
``` erlang
|
||||
HeaderVal
|
||||
= cowboy_req:header(<<"content-type">>, Req, <<"text/plain">>).
|
||||
```
|
||||
[source,erlang]
|
||||
HeaderVal = cowboy_req:header(<<"content-type">>, Req, <<"text/plain">>).
|
||||
|
||||
And also obtain all headers.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
AllHeaders = cowboy_req:headers(Req).
|
||||
```
|
||||
|
||||
To parse the previous header, simply call `parse_header/{2,3}`
|
||||
where you would call `header/{2,3}` otherwise.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
ParsedVal = cowboy_req:parse_header(<<"content-type">>, Req).
|
||||
```
|
||||
|
||||
Cowboy will crash if it doesn't know how to parse the given
|
||||
header, or if the value is invalid.
|
||||
|
@ -224,15 +205,16 @@ 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
|
||||
[source,erlang]
|
||||
----
|
||||
ParsedVal = cowboy_req:parse_header(<<"content-type">>, Req,
|
||||
{<<"text">>, <<"plain">>, []}).
|
||||
```
|
||||
----
|
||||
|
||||
The list of known headers and default values is defined in the
|
||||
manual.
|
||||
|
||||
:: Meta
|
||||
=== Meta
|
||||
|
||||
Cowboy will sometimes associate some meta information with
|
||||
the request. Built-in meta values are listed in the manual
|
||||
|
@ -241,29 +223,25 @@ for their respective modules.
|
|||
This will get a meta value. The returned value will be `undefined`
|
||||
if it isn't defined.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
MetaVal = cowboy_req:meta(websocket_version, Req).
|
||||
```
|
||||
|
||||
You can change the default value if needed.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
MetaVal = cowboy_req:meta(websocket_version, Req, 13).
|
||||
```
|
||||
|
||||
You can also define your own meta values. The name must be
|
||||
an `atom()`.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
Req2 = cowboy_req:set_meta(the_answer, 42, Req).
|
||||
```
|
||||
|
||||
:: Peer
|
||||
=== 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
|
||||
[source,erlang]
|
||||
{IP, Port} = cowboy_req:peer(Req).
|
||||
```
|
|
@ -1,4 +1,5 @@
|
|||
::: Reading the request body
|
||||
[[req_body]]
|
||||
== Reading the request body
|
||||
|
||||
The Req object also allows you to read the request body.
|
||||
|
||||
|
@ -16,13 +17,12 @@ 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
|
||||
=== Check for request body
|
||||
|
||||
You can check whether a body was sent with the request.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
cowboy_req:has_body(Req).
|
||||
```
|
||||
|
||||
It will return `true` if there is a request body, and
|
||||
`false` otherwise.
|
||||
|
@ -31,14 +31,13 @@ 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
|
||||
=== Request body length
|
||||
|
||||
You can obtain the body length if it was sent with the
|
||||
request.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
Length = cowboy_req:body_length(Req).
|
||||
```
|
||||
|
||||
The value returned will be `undefined` if the length
|
||||
couldn't be figured out from the request headers. If
|
||||
|
@ -46,26 +45,23 @@ 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
|
||||
=== Reading the body
|
||||
|
||||
You can read the whole body directly in one call.
|
||||
|
||||
``` erlang
|
||||
[source,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
|
||||
[source,erlang]
|
||||
{ok, Body, Req2} = cowboy_req:body(Req, [{length, 100000000}]).
|
||||
```
|
||||
|
||||
You can also disable it.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
{ok, Body, Req2} = cowboy_req:body(Req, [{length, infinity}]).
|
||||
```
|
||||
|
||||
It is recommended that you do not disable it for public
|
||||
facing websites.
|
||||
|
@ -74,7 +70,7 @@ 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
|
||||
=== Streaming the body
|
||||
|
||||
You can stream the request body by chunks.
|
||||
|
||||
|
@ -82,7 +78,8 @@ 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
|
||||
[source,erlang]
|
||||
----
|
||||
body_to_console(Req) ->
|
||||
case cowboy_req:body(Req) of
|
||||
{ok, Data, Req2} ->
|
||||
|
@ -92,12 +89,12 @@ body_to_console(Req) ->
|
|||
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
|
||||
=== Rate of data transmission
|
||||
|
||||
You can control the rate of data transmission by setting
|
||||
options when calling body functions. This applies not only
|
||||
|
@ -110,7 +107,7 @@ 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
|
||||
=== Transfer and content decoding
|
||||
|
||||
Cowboy will by default decode the chunked transfer-encoding
|
||||
if any. It will not decode any content-encoding by default.
|
||||
|
@ -122,28 +119,27 @@ ignored.
|
|||
|
||||
The following example shows how to set both options.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
----
|
||||
{ok, Data, Req2} = cowboy_req:body(Req, [
|
||||
{transfer_decode, fun transfer_decode/2, TransferState},
|
||||
{content_decode, fun content_decode/1}
|
||||
]).
|
||||
```
|
||||
----
|
||||
|
||||
:: Reading a form urlencoded body
|
||||
=== 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
|
||||
[source,erlang]
|
||||
{ok, KeyValues, Req2} = cowboy_req:body_qs(Req).
|
||||
```
|
||||
|
||||
You can then retrieve an individual value from that list.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
{_, Lang} = lists:keyfind(lang, 1, KeyValues).
|
||||
```
|
||||
|
||||
You should not attempt to match on the list as the order
|
||||
of the values is undefined.
|
||||
|
@ -152,7 +148,5 @@ 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}]).
|
||||
```
|
||||
[source,erlang]
|
||||
{ok, KeyValues, Req2} = cowboy_req:body_qs(Req, [{length, 2000000}]).
|
|
@ -1,10 +1,11 @@
|
|||
::: Designing a resource handler
|
||||
[[resource_design]]
|
||||
== 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
|
||||
=== The service
|
||||
|
||||
Can the service become unavailable, and when it does, can
|
||||
we detect it? For example database connectivity problems
|
||||
|
@ -17,7 +18,7 @@ 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
|
||||
=== Type of resource handler
|
||||
|
||||
Am I writing a handler for a collection of resources,
|
||||
or for a single resource?
|
||||
|
@ -26,7 +27,7 @@ The semantics for each of these are quite different.
|
|||
You should not mix collection and single resource in
|
||||
the same handler.
|
||||
|
||||
:: Collection handler
|
||||
=== Collection handler
|
||||
|
||||
Skip this section if you are not doing a collection.
|
||||
|
||||
|
@ -70,7 +71,7 @@ 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
|
||||
=== Single resource handler
|
||||
|
||||
Skip this section if you are doing a collection.
|
||||
|
||||
|
@ -98,7 +99,7 @@ operation is atomic. The PATCH operation may
|
|||
be used for adding, removing or modifying specific
|
||||
values in the resource.
|
||||
|
||||
:: The resource
|
||||
=== The resource
|
||||
|
||||
Following the above discussion, implement the
|
||||
`allowed_methods` callback.
|
||||
|
@ -125,7 +126,7 @@ 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
|
||||
=== Representations
|
||||
|
||||
What media types do I provide? If text based, what charsets
|
||||
are provided? What languages do I provide?
|
||||
|
@ -149,7 +150,7 @@ representation available? Send a list of available
|
|||
representations in the response body and implement
|
||||
the `multiple_choices` callback.
|
||||
|
||||
:: Redirections
|
||||
=== Redirections
|
||||
|
||||
Do I need to keep track of what resources were deleted?
|
||||
For example you may have a mechanism where moving a
|
||||
|
@ -161,7 +162,7 @@ it is explicitly temporary, for example due to maintenance,
|
|||
implement the `moved_temporarily` callback. Otherwise,
|
||||
implement the `moved_permanently` callback.
|
||||
|
||||
:: The request
|
||||
=== The request
|
||||
|
||||
Do we need to perform extra checks to make sure the request
|
||||
is valid? Cowboy will do many checks when receiving the
|
||||
|
@ -176,20 +177,20 @@ to accept? Implement `valid_entity_length`.
|
|||
Finally, take a look at the sections corresponding to the
|
||||
methods you are implementing.
|
||||
|
||||
:: OPTIONS method
|
||||
=== 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
|
||||
=== 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
|
||||
=== PUT, POST and PATCH methods
|
||||
|
||||
If you implement the methods PUT, POST and/or PATCH,
|
||||
you must implement the `content_types_accepted` callback,
|
||||
|
@ -208,7 +209,7 @@ 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
|
||||
=== DELETE methods
|
||||
|
||||
If you implement the method DELETE, you must implement
|
||||
the `delete_resource` callback.
|
|
@ -1,4 +1,5 @@
|
|||
::: Sending a response
|
||||
[[resp]]
|
||||
== Sending a response
|
||||
|
||||
The Req object also allows you to send a response.
|
||||
|
||||
|
@ -9,36 +10,37 @@ 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
|
||||
=== 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
|
||||
[source,erlang]
|
||||
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
|
||||
[source,erlang]
|
||||
----
|
||||
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
|
||||
[source,erlang]
|
||||
----
|
||||
Req2 = cowboy_req:reply(200, [
|
||||
{<<"server">>, <<"yaws">>}
|
||||
], Req).
|
||||
```
|
||||
----
|
||||
|
||||
We also saw earlier how to force close the connection by
|
||||
overriding the connection header.
|
||||
|
@ -48,34 +50,35 @@ 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
|
||||
[source,erlang]
|
||||
----
|
||||
Req2 = cowboy_req:reply(200, [
|
||||
{<<"content-type">>, <<"text/plain">>}
|
||||
], "Hello world!", Req).
|
||||
```
|
||||
----
|
||||
|
||||
Here is the same example but sending HTML this time.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
----
|
||||
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
|
||||
=== 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
|
||||
[source,erlang]
|
||||
Req2 = cowboy_req:chunked_reply(200, Req),
|
||||
cowboy_req:chunk("Hello...", Req2),
|
||||
cowboy_req:chunk("chunked...", Req2),
|
||||
cowboy_req:chunk("world!!", Req2).
|
||||
```
|
||||
|
||||
You should make sure to match on `ok` as an error may be
|
||||
returned.
|
||||
|
@ -84,36 +87,35 @@ 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
|
||||
[source,erlang]
|
||||
----
|
||||
Req2 = cowboy_req:chunked_reply(200, [
|
||||
{<<"content-type">>, <<"text/html">>}
|
||||
], Req),
|
||||
cowboy_req:chunk("<html><head>Hello world!</head>", Req2),
|
||||
cowboy_req:chunk("<body><p>Hats off!</p></body></html>", Req2).
|
||||
```
|
||||
----
|
||||
|
||||
Note that the reply and each chunk following it are sent
|
||||
immediately.
|
||||
|
||||
:: Preset response headers
|
||||
=== 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
|
||||
[source,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
|
||||
[source,erlang]
|
||||
cowboy_req:has_resp_header(<<"allow">>, Req).
|
||||
```
|
||||
|
||||
It will return `true` if the header is defined, and `false`
|
||||
otherwise.
|
||||
|
@ -121,19 +123,17 @@ otherwise.
|
|||
Finally, you can also delete a preset response header if
|
||||
needed. If you do, it will not be sent.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
Req2 = cowboy_req:delete_resp_header(<<"allow">>, Req).
|
||||
```
|
||||
|
||||
:: Preset response body
|
||||
=== 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
|
||||
[source,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
|
||||
|
@ -144,38 +144,41 @@ you should specify it, as it will help clients determine
|
|||
the remaining download time and allow them to inform the
|
||||
user.
|
||||
|
||||
``` erlang
|
||||
[source,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
|
||||
[source,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
|
||||
[source,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
|
||||
=== Sending files
|
||||
|
||||
You can send files directly from disk without having to
|
||||
read them. Cowboy will use the `sendfile` syscall when
|
||||
|
@ -186,12 +189,13 @@ than doing it from userland.
|
|||
Again, it is recommended to set the size of the file if it
|
||||
can be known in advance.
|
||||
|
||||
``` erlang
|
||||
[source,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.
|
|
@ -1,4 +1,5 @@
|
|||
::: REST flowcharts
|
||||
[[rest_flowcharts]]
|
||||
== REST flowcharts
|
||||
|
||||
This chapter will explain the REST handler state machine through
|
||||
a number of different diagrams.
|
||||
|
@ -17,11 +18,11 @@ 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
|
||||
=== Start
|
||||
|
||||
All requests start from here.
|
||||
|
||||
^"REST starting flowchart^!rest_start.png
|
||||
image::rest_start.png[REST starting flowchart]
|
||||
|
||||
A series of callbacks are called in succession to perform
|
||||
a general checkup of the service, the request line and
|
||||
|
@ -48,11 +49,11 @@ 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
|
||||
=== OPTIONS method
|
||||
|
||||
This diagram only applies to OPTIONS requests.
|
||||
|
||||
^"REST OPTIONS method flowchart^!rest_options.png
|
||||
image::rest_options.png[REST OPTIONS method flowchart]
|
||||
|
||||
The `options` callback may be used to add information
|
||||
about the resource, such as media types or languages
|
||||
|
@ -64,13 +65,13 @@ If the `options` callback is not defined, Cowboy will
|
|||
send a response containing the list of allowed methods
|
||||
by default.
|
||||
|
||||
:: Content negotiation
|
||||
=== 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
|
||||
image::rest_conneg.png[REST content negotiation flowchart]
|
||||
|
||||
The purpose of these steps is to determine an appropriate
|
||||
representation to be sent back to the client.
|
||||
|
@ -105,14 +106,14 @@ the "PUT, POST and PATCH methods" diagram,
|
|||
or the "DELETE method" diagram, depending on the
|
||||
method.
|
||||
|
||||
:: GET and HEAD methods
|
||||
=== 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
|
||||
image::rest_get_head.png[REST GET/HEAD methods flowchart]
|
||||
|
||||
When the resource exists, and the conditional steps
|
||||
succeed, the resource can be retrieved.
|
||||
|
@ -134,14 +135,14 @@ 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
|
||||
=== 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
|
||||
image::rest_put_post_patch.png[REST PUT/POST/PATCH methods flowchart]
|
||||
|
||||
When the resource exists, first the conditional steps
|
||||
are executed. When that succeeds, and the method is PUT,
|
||||
|
@ -188,14 +189,14 @@ 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
|
||||
=== 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
|
||||
image::rest_delete.png[REST DELETE method flowchart]
|
||||
|
||||
When the resource exists, and the conditional steps
|
||||
succeed, the resource can be deleted.
|
||||
|
@ -227,13 +228,13 @@ The `moved_permanently` and `moved_temporarily` callbacks
|
|||
must return the new location of the resource if it was in
|
||||
fact moved.
|
||||
|
||||
:: Conditional requests
|
||||
=== 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
|
||||
image::rest_cond.png[REST conditional requests flowchart]
|
||||
|
||||
A request becomes conditional when it includes either of
|
||||
the if-match header; the if-unmodified-since header; the
|
|
@ -1,4 +1,5 @@
|
|||
::: REST handlers
|
||||
[[rest_handlers]]
|
||||
== REST handlers
|
||||
|
||||
REST is implemented in Cowboy as a sub protocol. The request
|
||||
is handled as a state machine with many optional callbacks
|
||||
|
@ -6,16 +7,17 @@ describing the resource and modifying the machine's behavior.
|
|||
|
||||
The REST handler is the recommended way to handle HTTP requests.
|
||||
|
||||
:: Initialization
|
||||
=== Initialization
|
||||
|
||||
First, the `init/2` callback is called. This callback is common
|
||||
to all handlers. To use REST for the current request, this function
|
||||
must return a `cowboy_rest` tuple.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
----
|
||||
init(Req, _Opts) ->
|
||||
{cowboy_rest, Req, #state{}}.
|
||||
```
|
||||
----
|
||||
|
||||
Cowboy will then switch to the REST protocol and start executing
|
||||
the state machine.
|
||||
|
@ -23,7 +25,7 @@ the state machine.
|
|||
After reaching the end of the flowchart, the `terminate/3` callback
|
||||
will be called if it is defined.
|
||||
|
||||
:: Methods
|
||||
=== Methods
|
||||
|
||||
The REST component has code for handling the following HTTP methods:
|
||||
HEAD, GET, POST, PATCH, PUT, DELETE and OPTIONS.
|
||||
|
@ -31,7 +33,7 @@ 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
|
||||
=== Callbacks
|
||||
|
||||
All callbacks are optional. Some may become mandatory depending
|
||||
on what other defined callbacks return. The various flowcharts
|
||||
|
@ -53,35 +55,37 @@ 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_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 `[]`
|
||||
[cols="<,^",options="header"]
|
||||
|===
|
||||
| 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_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.
|
||||
|
@ -94,32 +98,36 @@ 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
|
||||
=== 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.
|
||||
[cols="<,<",options="header"]
|
||||
|===
|
||||
| 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
|
||||
=== 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
|
||||
[cols="<,<",options="header"]
|
||||
|===
|
||||
| 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
|
||||
|===
|
|
@ -1,4 +1,5 @@
|
|||
::: REST principles
|
||||
[[rest_principles]]
|
||||
== REST principles
|
||||
|
||||
This chapter will attempt to define the concepts behind REST
|
||||
and explain what makes a service RESTful.
|
||||
|
@ -10,11 +11,11 @@ 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
|
||||
http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm[Roy T. Fielding's dissertation]
|
||||
as it does a great job explaining where it comes from and
|
||||
what it achieves.
|
||||
|
||||
:: REST architecture
|
||||
=== 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
|
||||
|
@ -54,7 +55,7 @@ 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
|
||||
=== 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,
|
||||
|
@ -79,7 +80,7 @@ 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
|
||||
=== Resource representations
|
||||
|
||||
The representation of a resource is a sequence of bytes associated
|
||||
with metadata.
|
||||
|
@ -111,7 +112,7 @@ 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
|
||||
=== Self-descriptive messages
|
||||
|
||||
Messages must be self-descriptive. That means that the data
|
||||
format of a representation must always come with its media
|
||||
|
@ -132,7 +133,7 @@ This means that you can create your own media types, like
|
|||
specifications for it and that both endpoints agree about
|
||||
it then the constraint is respected.
|
||||
|
||||
:: Hypermedia as the engine of application state
|
||||
=== 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
|
|
@ -1,4 +1,5 @@
|
|||
::: Routing
|
||||
[[routing]]
|
||||
== Routing
|
||||
|
||||
Cowboy does nothing by default.
|
||||
|
||||
|
@ -14,42 +15,38 @@ and then try to find a matching path.
|
|||
|
||||
Routes need to be compiled before they can be used by Cowboy.
|
||||
|
||||
:: Structure
|
||||
=== Structure
|
||||
|
||||
The general structure for the routes is defined as follow.
|
||||
|
||||
``` erlang
|
||||
[source,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
|
||||
[source,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
|
||||
[source,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
|
||||
[source,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
|
||||
=== Match syntax
|
||||
|
||||
The match syntax is used to associate host names and paths with their
|
||||
respective handlers.
|
||||
|
@ -64,30 +61,29 @@ 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
|
||||
[source,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
|
||||
[source,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
|
||||
[source,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
|
||||
|
@ -97,10 +93,9 @@ 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
|
||||
[source,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
|
||||
|
@ -116,23 +111,20 @@ 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
|
||||
[source,erlang]
|
||||
HostMatch = "ninenines.:_".
|
||||
```
|
||||
|
||||
Similarly, it is possible to have optional segments. Anything
|
||||
between brackets is optional.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
PathMatch = "/hats/[page/:number]".
|
||||
HostMatch = "[www.]ninenines.eu".
|
||||
```
|
||||
|
||||
You can also have imbricated optional segments.
|
||||
|
||||
``` erlang
|
||||
[source,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
|
||||
|
@ -142,51 +134,45 @@ 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
|
||||
[source,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
|
||||
[source,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
|
||||
[source,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
|
||||
[source,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
|
||||
[source,erlang]
|
||||
PathMatch = '_'.
|
||||
HostMatch = '_'.
|
||||
```
|
||||
|
||||
The second is the special host match `"*"` which will match the
|
||||
wildcard path, generally used alongside the `OPTIONS` method.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
HostMatch = "*".
|
||||
```
|
||||
|
||||
:: Constraints
|
||||
=== Constraints
|
||||
|
||||
After the matching has completed, the resulting bindings can be tested
|
||||
against a set of constraints. Constraints are only tested when the
|
||||
|
@ -200,9 +186,9 @@ one or more constraints. While the router accepts the same format,
|
|||
it will skip fields with no constraints and will also ignore default
|
||||
values, if any.
|
||||
|
||||
Read more about ^constraints^.
|
||||
Read more about xref:constraints[constraints].
|
||||
|
||||
:: Compilation
|
||||
=== 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
|
||||
|
@ -210,7 +196,8 @@ 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
|
||||
[source,erlang]
|
||||
----
|
||||
Dispatch = cowboy_router:compile([
|
||||
%% {HostMatch, list({PathMatch, Handler, Opts})}
|
||||
{'_', [{'_', my_handler, []}]}
|
||||
|
@ -220,20 +207,18 @@ 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
|
||||
=== 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)).
|
||||
```
|
||||
[source,erlang]
|
||||
cowboy:set_env(my_http_listener, dispatch, cowboy_router:compile(Dispatch)).
|
||||
|
||||
Note that you need to compile the routes before updating.
|
|
@ -1,4 +1,5 @@
|
|||
::: Static files
|
||||
[[static_files]]
|
||||
== Static files
|
||||
|
||||
Cowboy comes with a special handler built as a REST handler
|
||||
and designed specifically for serving static files. It is
|
||||
|
@ -20,30 +21,28 @@ client-side caching.
|
|||
To use the static file handler, simply add routes for it
|
||||
with the appropriate options.
|
||||
|
||||
:: Serve one file
|
||||
=== 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
|
||||
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`
|
||||
The following rule will serve the file 'static/index.html'
|
||||
from the application `my_app`'s priv directory whenever the
|
||||
path `/` is accessed.
|
||||
|
||||
``` erlang
|
||||
[source,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
|
||||
[source,erlang]
|
||||
{"/", cowboy_static, {file, "/var/www/index.html"}}
|
||||
```
|
||||
|
||||
:: Serve all files from a directory
|
||||
=== 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
|
||||
|
@ -59,18 +58,16 @@ 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
|
||||
[source,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
|
||||
[source,erlang]
|
||||
{"/assets/[...]", cowboy_static, {dir, "/var/www/assets"}}
|
||||
```
|
||||
|
||||
:: Customize the mimetype detection
|
||||
=== Customize the mimetype detection
|
||||
|
||||
By default, Cowboy will attempt to recognize the mimetype
|
||||
of your static files by looking at the extension.
|
||||
|
@ -91,10 +88,11 @@ 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
|
||||
[source,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
|
||||
|
@ -103,19 +101,21 @@ types have this optional field.
|
|||
To use the function that will detect almost any mimetype,
|
||||
the following configuration will do.
|
||||
|
||||
``` erlang
|
||||
[source,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
|
||||
[source,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
|
||||
|
@ -133,12 +133,13 @@ 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
|
||||
[source,erlang]
|
||||
----
|
||||
{"/", cowboy_static, {priv_file, my_app, "static/index.html",
|
||||
[{mimetypes, {<<"text">>, <<"html">>, []}}]}}
|
||||
```
|
||||
----
|
||||
|
||||
:: Generate an etag
|
||||
=== Generate an etag
|
||||
|
||||
By default, the static handler will generate an etag header
|
||||
value based on the size and modified time. This solution
|
||||
|
@ -149,10 +150,11 @@ different etag on each server.
|
|||
|
||||
You can however change the way the etag is calculated.
|
||||
|
||||
``` erlang
|
||||
[source,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
|
||||
|
@ -162,7 +164,8 @@ all your servers.
|
|||
|
||||
You can also completely disable etag handling.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
----
|
||||
{"/assets/[...]", cowboy_static, {priv_dir, my_app, "static/assets",
|
||||
[{etag, false}]}}
|
||||
```
|
||||
----
|
|
@ -1,4 +1,5 @@
|
|||
::: Sub protocols
|
||||
[[sub_protocols]]
|
||||
== Sub protocols
|
||||
|
||||
Sub protocols are used for creating new types of handlers that
|
||||
provide extra functionality in a reusable way. Cowboy uses this
|
||||
|
@ -7,16 +8,17 @@ mechanism to provide its loop, REST and Websocket handlers.
|
|||
This chapter will explain how to create your own sub protocols
|
||||
and handler types.
|
||||
|
||||
:: Usage
|
||||
=== Usage
|
||||
|
||||
To switch to a sub protocol, the `init/2` callback must return
|
||||
the name of the sub protocol module. Everything past this point
|
||||
is handled by the sub protocol.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
----
|
||||
init(Req, _Opts) ->
|
||||
{cowboy_websocket, Req, #state{}}.
|
||||
```
|
||||
----
|
||||
|
||||
The return value may also have a `Timeout` value and/or the
|
||||
atom `hibernate`. These options are useful for long living
|
||||
|
@ -27,15 +29,16 @@ The following snippet switches to the `my_protocol` sub
|
|||
protocol, sets the timeout value to 5 seconds and enables
|
||||
hibernation:
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
----
|
||||
init(Req, _Opts) ->
|
||||
{my_protocol, Req, #state{}, 5000, hibernate}.
|
||||
```
|
||||
----
|
||||
|
||||
If a sub protocol does not make use of these options, it should
|
||||
crash if it receives anything other than the default values.
|
||||
|
||||
:: Upgrade
|
||||
=== Upgrade
|
||||
|
||||
After the `init/2` function returns, Cowboy will then call the
|
||||
`upgrade/6` function. This is the only callback defined by the
|
||||
|
@ -51,10 +54,11 @@ The upgrade callback receives the Req object, the middleware
|
|||
environment, the handler and its options, and the aforementioned
|
||||
timeout and hibernate values.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
----
|
||||
upgrade(Req, Env, Handler, HandlerOpts, Timeout, Hibernate) ->
|
||||
%% Sub protocol code here.
|
||||
```
|
||||
----
|
||||
|
||||
This callback is expected to behave like a middleware and to
|
||||
return an updated environment and Req object.
|
|
@ -1,4 +1,5 @@
|
|||
::: Handling Websocket connections
|
||||
[[ws_handlers]]
|
||||
== Handling Websocket connections
|
||||
|
||||
A special handler is required for handling Websocket connections.
|
||||
Websocket handlers allow you to initialize the connection,
|
||||
|
@ -9,16 +10,17 @@ 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
|
||||
=== Initialization
|
||||
|
||||
First, the `init/2` callback is called. This callback is common
|
||||
to all handlers. To establish a Websocket connection, this function
|
||||
must return a `ws` tuple.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
----
|
||||
init(Req, _Opts) ->
|
||||
{cowboy_websocket, Req, #state{}}.
|
||||
```
|
||||
----
|
||||
|
||||
Upon receiving this tuple, Cowboy will switch to the code
|
||||
that handles Websocket connections and perform the handshake
|
||||
|
@ -30,7 +32,8 @@ 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
|
||||
[source,erlang]
|
||||
----
|
||||
init(Req, _Opts) ->
|
||||
case cowboy_req:parse_header(<<"sec-websocket-protocol">>, Req) of
|
||||
undefined ->
|
||||
|
@ -45,7 +48,7 @@ init(Req, _Opts) ->
|
|||
{stop, Req, undefined}
|
||||
end
|
||||
end.
|
||||
```
|
||||
----
|
||||
|
||||
It is not recommended to wait too long inside the `init/2`
|
||||
function. Any extra initialization may be done after returning by
|
||||
|
@ -57,7 +60,8 @@ 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
|
||||
[source,erlang]
|
||||
----
|
||||
init(Req, _Opts) ->
|
||||
self() ! post_init,
|
||||
%% Register process here...
|
||||
|
@ -66,9 +70,9 @@ init(Req, _Opts) ->
|
|||
websocket_info(post_init, Req, State) ->
|
||||
%% Perform post_init initialization here...
|
||||
{ok, Req, State}.
|
||||
```
|
||||
----
|
||||
|
||||
:: Handling frames from the client
|
||||
=== 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
|
||||
|
@ -81,14 +85,15 @@ or just continue without sending anything.
|
|||
The following snippet echoes back any text frame received and
|
||||
ignores all others.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
----
|
||||
websocket_handle(Frame = {text, _}, Req, State) ->
|
||||
{reply, Frame, Req, State};
|
||||
websocket_handle(_Frame, Req, State) ->
|
||||
{ok, Req, State}.
|
||||
```
|
||||
----
|
||||
|
||||
:: Handling Erlang messages
|
||||
=== Handling Erlang messages
|
||||
|
||||
Cowboy will call `websocket_info/3` whenever an Erlang message
|
||||
arrives.
|
||||
|
@ -99,14 +104,15 @@ or just continue without sending anything.
|
|||
The following snippet forwards any `log` message to the socket
|
||||
and ignores all others.
|
||||
|
||||
``` erlang
|
||||
[source,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
|
||||
=== Sending frames to the socket
|
||||
|
||||
Cowboy allows sending either a single frame or a list of
|
||||
frames to the socket, in which case the frames are sent
|
||||
|
@ -116,7 +122,8 @@ pong or close frames.
|
|||
The following example sends three frames using a single `reply`
|
||||
tuple.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
----
|
||||
websocket_info(hello_world, Req, State) ->
|
||||
{reply, [
|
||||
{text, "Hello"},
|
||||
|
@ -124,7 +131,7 @@ websocket_info(hello_world, Req, State) ->
|
|||
{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
|
||||
|
@ -137,7 +144,7 @@ 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
|
||||
=== Ping and timeout
|
||||
|
||||
The biggest performance improvement you can do when dealing
|
||||
with a huge number of Websocket connections is to reduce the
|
||||
|
@ -160,15 +167,16 @@ leave the process alive forever.
|
|||
|
||||
A good timeout value is 60 seconds.
|
||||
|
||||
``` erlang
|
||||
[source,erlang]
|
||||
----
|
||||
init(Req, _Opts) ->
|
||||
{cowboy_websocket, Req, #state{}, 60000}.
|
||||
```
|
||||
----
|
||||
|
||||
This value cannot be changed once it is set. It defaults to
|
||||
`infinity`.
|
||||
|
||||
:: Hibernate
|
||||
=== Hibernate
|
||||
|
||||
Most tuples returned from handler callbacks can include an
|
||||
extra value `hibernate`. After doing any necessary operations
|
||||
|
@ -180,9 +188,9 @@ 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
|
||||
=== 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
|
||||
https://github.com/ninenines/bullet[Bullet] can be used to
|
||||
emulate Websocket connections on older browsers.
|
|
@ -1,9 +1,10 @@
|
|||
::: The Websocket protocol
|
||||
[[ws_protocol]]
|
||||
== The Websocket protocol
|
||||
|
||||
This chapter explains what Websocket is and why it is
|
||||
a vital component of soft realtime Web applications.
|
||||
|
||||
:: Description
|
||||
=== Description
|
||||
|
||||
Websocket is an extension to HTTP that emulates plain TCP
|
||||
connections between the client, typically a Web browser,
|
||||
|
@ -22,7 +23,7 @@ and all drafts that were previously implemented by browsers,
|
|||
excluding the initial flawed draft sometimes known as
|
||||
"version 0".
|
||||
|
||||
:: Implementation
|
||||
=== Implementation
|
||||
|
||||
Cowboy implements Websocket as a protocol upgrade. Once the
|
||||
upgrade is performed from the `init/2` callback, Cowboy
|
Loading…
Add table
Add a link
Reference in a new issue