0
Fork 0
mirror of https://github.com/ninenines/cowboy.git synced 2025-07-14 20:30:23 +00:00

Wrap-up the user guide

This commit is contained in:
Loïc Hoguin 2014-06-25 11:23:58 +02:00
parent 642630fea1
commit fd3c40c7ee
11 changed files with 291 additions and 209 deletions

View file

@ -1,33 +1,18 @@
Internals
=========
Architecture Architecture
------------ ============
Cowboy is a lightweight HTTP server. Cowboy is a lightweight HTTP server.
It is built on top of Ranch. Please see the Ranch guide for more It is built on top of Ranch. Please see the Ranch guide for more
informations. information.
One process per connection
--------------------------
It uses only one process per connection. The process where your It uses only one process per connection. The process where your
code runs is the process controlling the socket. Using one process code runs is the process controlling the socket. Using one process
instead of two allows for lower memory usage. instead of two allows for lower memory usage.
It uses binaries. Binaries are more efficient than lists for
representing strings because they take less memory space. Processing
performance can vary depending on the operation. Binaries are known
for generally getting a great boost if the code is compiled natively.
Please see the HiPE documentation for more details.
Because querying for the current date and time can be expensive,
Cowboy generates one `Date` header value every second, shares it
to all other processes, which then simply copy it in the response.
This allows compliance with HTTP/1.1 with no actual performance loss.
One process for many requests
-----------------------------
As previously mentioned, Cowboy only use one process per connection.
Because there can be more than one request per connection with the Because there can be more than one request per connection with the
keepalive feature of HTTP/1.1, that means the same process will be keepalive feature of HTTP/1.1, that means the same process will be
used to handle many requests. used to handle many requests.
@ -37,32 +22,25 @@ up before terminating the handling of the current request. This may
include cleaning up the process dictionary, timers, monitoring and include cleaning up the process dictionary, timers, monitoring and
more. more.
Lowercase header names Binaries
---------------------- --------
For consistency reasons it has been chosen to convert all header names It uses binaries. Binaries are more efficient than lists for
to lowercase binary strings. This prevents the programmer from making representing strings because they take less memory space. Processing
case mistakes, and is possible because header names are case insensitive. 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.
This works fine for the large majority of clients. However, some badly Date header
implemented clients, especially ones found in corporate code or closed -----------
source products, may not handle header names in a case insensitive manner.
This means that when Cowboy returns lowercase header names, these clients
will not find the headers they are looking for.
A simple way to solve this is to create an `onresponse` hook that will Because querying for the current date and time can be expensive,
format the header names with the expected case. 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.
``` erlang Max connections
capitalize_hook(Status, Headers, Body, Req) -> ---------------
Headers2 = [{cowboy_bstr:capitalize_token(N), V}
|| {N, V} <- Headers],
{ok, Req2} = cowboy_req:reply(Status, Headers2, Body, Req),
Req2.
```
Improving performance
---------------------
By default the maximum number of active connections is set to a By default the maximum number of active connections is set to a
generally accepted big enough number. This is meant to prevent having generally accepted big enough number. This is meant to prevent having
@ -72,8 +50,3 @@ everything else down, or taking up all the memory.
Disabling this feature, by setting the `{max_connections, infinity}` Disabling this feature, by setting the `{max_connections, infinity}`
protocol option, would give you greater performance when you are protocol option, would give you greater performance when you are
only processing short-lived requests. only processing short-lived requests.
Another option is to define platform-specific socket options that
are known to improve their efficiency.
Please see the Ranch guide for more information.

65
guide/broken_clients.md Normal file
View file

@ -0,0 +1,65 @@
Dealing with broken clients
===========================
There exists a very large number of implementations for the
HTTP protocol. Most widely used clients, like browsers,
follow the standard quite well, but others may not. In
particular custom enterprise clients tend to be very badly
written.
Cowboy tries to follow the standard as much as possible,
but is not trying to handle very possible special cases.
Instead Cowboy focuses on the cases reported in the wild,
on the public Web.
That means clients that ignore the HTTP standard completely
may fail to understand Cowboy's responses. There are of
course workarounds. This chapter aims to cover them.
Lowercase headers
-----------------
Cowboy converts all headers it receives to lowercase, and
similarly sends back headers all in lowercase. Some broken
HTTP clients have issues with that.
A simple way to solve this is to create an `onresponse` hook
that will format the header names with the expected case.
``` erlang
capitalize_hook(Status, Headers, Body, Req) ->
Headers2 = [{cowboy_bstr:capitalize_token(N), V}
|| {N, V} <- Headers],
{ok, Req2} = cowboy_req:reply(Status, Headers2, Body, Req),
Req2.
```
Note that SPDY clients do not have that particular issue
because the specification explicitly says all headers are
lowercase, unlike HTTP which allows any case but treats
them as case insensitive.
Camel-case headers
------------------
Sometimes it is desirable to keep the actual case used by
clients, for example when acting as a proxy between two broken
implementations. There is no easy solution for this other than
forking the project and editing the `cowboy_protocol` file
directly.
Chunked transfer-encoding
-------------------------
Sometimes an HTTP client advertises itself as HTTP/1.1 but
does not support chunked transfer-encoding. This is invalid
behavior, as HTTP/1.1 clients are required to support it.
A simple workaround exists in these cases. By changing the
Req object response state to `waiting_stream`, Cowboy will
understand that it must use the identity transfer-encoding
when replying, just like if it was an HTTP/1.0 client.
``` erlang
Req2 = cowboy_req:set(resp_state, waiting_stream).
```

View file

@ -1,21 +1,20 @@
Getting started Getting started
=============== ===============
Setting up a working Erlang application is a little more complex than Erlang is more than a language, it is also an operating system
for most other languages. The reason is that Erlang is designed to for your applications. Erlang developers rarely write standalone
build systems and not just simple applications. modules, they write libraries or applications, and then bundle
those into what is called a release. A release contains the
Erlang VM plus all applications required to run the node, so
it can be pushed to production directly.
An Erlang system is typically comprised of many different nodes, This chapter walks you through all the steps of setting up
each containing many different OTP applications, each containing Cowboy, writing your first application and generating your first
many different modules and running many different processes. release. At the end of this chapter you should know everything
Nodes may or may not be identical depending on the nature of the you need to push your first Cowboy application to production.
system.
To get started though, we only need one node that contains your own Application skeleton
HTTP application, plus the dependencies that it needs, like Cowboy. --------------------
To create our node, we need to build what is called a release. A
release is a set of files that contain the Erlang VM plus all the
applications required to run our node.
Let's start by creating this application. We will simply call it Let's start by creating this application. We will simply call it
`hello_erlang`. This application will have the following directory `hello_erlang`. This application will have the following directory
@ -130,6 +129,9 @@ That's not enough however. Since we are building a Cowboy based
application, we also need to initialize Cowboy when we start our application, we also need to initialize Cowboy when we start our
application. application.
Setting up Cowboy
-----------------
Cowboy does nothing by default. Cowboy does nothing by default.
Cowboy uses Ranch for handling the connections and provides convenience Cowboy uses Ranch for handling the connections and provides convenience
@ -198,6 +200,9 @@ init([]) ->
Finally, we need to write the code for handling incoming requests. Finally, we need to write the code for handling incoming requests.
Handling HTTP requests
----------------------
Cowboy features many kinds of handlers. For this simple example, Cowboy features many kinds of handlers. For this simple example,
we will just use the plain HTTP handler, which has three callback we will just use the plain HTTP handler, which has three callback
functions: `init/3`, `handle/2` and `terminate/3`. You can find more functions: `init/3`, `handle/2` and `terminate/3`. You can find more
@ -235,6 +240,9 @@ Its usage is documented in the
The code for our application is ready, so let's build a release! The code for our application is ready, so let's build a release!
Compiling
---------
First we need to download `erlang.mk`. First we need to download `erlang.mk`.
``` bash ``` bash
@ -271,6 +279,9 @@ haven't made any typo when creating the previous files.
$ make $ make
``` ```
Generating the release
----------------------
That's not all however, as we want to create a working release. That's not all however, as we want to create a working release.
For that purpose, we need to create a `relx.config` file. When For that purpose, we need to create a `relx.config` file. When
this file exists, `erlang.mk` will automatically download `relx` this file exists, `erlang.mk` will automatically download `relx`
@ -299,6 +310,3 @@ $ ./_rel/bin/hello_erlang console
If you then access `http://localhost:8080` using your browser, If you then access `http://localhost:8080` using your browser,
you should receive a nice greet! you should receive a nice greet!
You can find many more examples in the `examples/` directory
of the Cowboy repository.

View file

@ -1,56 +0,0 @@
Handlers
========
Purpose
-------
Handlers are Erlang modules that represent a resource.
Handlers must process the request and send a reply. The nature of the
reply will vary between handlers.
Different kinds of handlers can be combined in a single module. This
allows a module to handle both websocket and long-polling code in a
single place, for example.
Protocol upgrades
-----------------
Cowboy features many different handlers: HTTP handlers, loop handlers,
websocket handlers, REST handlers and static handlers. All of them
have a common entry point: the `init/3` function.
By default, Cowboy considers your handler to be an HTTP handler.
To switch to a different protocol, like, for example, Websocket,
you must perform a protocol upgrade. This is done by returning
a protocol upgrade tuple at the end of `init/3`.
The following snippet upgrades the handler to `my_protocol`.
``` erlang
init(_Any, _Req, _Opts) ->
{upgrade, protocol, my_protocol}.
```
Cowboy comes with two protocol upgrades: `cowboy_rest` and
`cowboy_websocket`. Use these values in place of `my_protocol`
to use them.
Custom protocol upgrades
------------------------
The `my_protocol` module above will be used for further processing
of the request. It should use the `cowboy_sub_protocol` behaviour,
which requires only one callback, `upgrade/4`.
It receives the request object, the middleware environment, and
the handler this request has been routed to along with its options.
``` erlang
upgrade(Req, Env, Handler, HandlerOpts) ->
%% ...
```
This callback is expected to behave like any middleware. Please
see the corresponding chapter for more information.

View file

@ -1,8 +1,12 @@
Hooks Hooks
===== =====
On request Cowboy provides two hooks. `onrequest` is called once the request
---------- line and headers have been received. `onresponse` is called just
before sending the response.
Onrequest
---------
The `onrequest` hook is called as soon as Cowboy finishes fetching The `onrequest` hook is called as soon as Cowboy finishes fetching
the request headers. It occurs before any other processing, including the request headers. It occurs before any other processing, including
@ -39,8 +43,8 @@ debug_hook(Req) ->
Make sure to always return the last request object obtained. Make sure to always return the last request object obtained.
On response Onresponse
----------- ----------
The `onresponse` hook is called right before sending the response The `onresponse` hook is called right before sending the response
to the socket. It can be used for the purposes of logging responses, to the socket. It can be used for the purposes of logging responses,
@ -51,7 +55,7 @@ Note that like the `onrequest` hook, this function MUST NOT crash.
Cowboy may or may not send a reply if this function crashes. If a reply Cowboy may or may not send a reply if this function crashes. If a reply
is sent, the hook MUST explicitly provide all headers that are needed. is sent, the hook MUST explicitly provide all headers that are needed.
You can specify the `onresponse` hook when creating the listener also. You can specify the `onresponse` hook when creating the listener.
``` erlang ``` erlang
cowboy:start_http(my_http_listener, 100, cowboy:start_http(my_http_listener, 100,

View file

@ -1,24 +1,11 @@
Loop handlers Loop handlers
============= =============
Purpose
-------
Loop handlers are a special kind of HTTP handlers used when the Loop handlers are a special kind of HTTP handlers used when the
response can not be sent right away. The handler enters instead response can not be sent right away. The handler enters instead
a receive loop waiting for the right message before it can send a receive loop waiting for the right message before it can send
a response. a response.
They are most useful when performing long-polling operations or
when using server-sent events.
While the same can be accomplished using plain HTTP handlers,
it is recommended to use loop handlers because they are well-tested
and allow using built-in features like hibernation and timeouts.
Usage
-----
Loop handlers are used for requests where a response might not Loop handlers are used for requests where a response might not
be immediately available, but where you would like to keep the be immediately available, but where you would like to keep the
connection open for a while in case the response arrives. The connection open for a while in case the response arrives. The
@ -29,34 +16,133 @@ partially available and you need to stream the response body
while the connection is open. The most known example of such while the connection is open. The most known example of such
practice is known as server-sent events. practice is known as server-sent events.
While the same can be accomplished using plain HTTP handlers,
it is recommended to use loop handlers because they are well-tested
and allow using built-in features like hibernation and timeouts.
Loop handlers essentially wait for one or more Erlang messages Loop handlers essentially wait for one or more Erlang messages
and feed these messages to the `info/3` callback. It also features and feed these messages to the `info/3` callback. It also features
the `init/3` and `terminate/3` callbacks which work the same as the `init/3` and `terminate/3` callbacks which work the same as
for plain HTTP handlers. for plain HTTP handlers.
The following handler waits for a message `{reply, Body}` before Initialization
sending a response. If this message doesn't arrive within 60 --------------
seconds, it gives up and a `204 No Content` will be replied.
It also hibernates the process to save memory while waiting for The `init/3` function must return a `loop` tuple to enable
this message. loop handler behavior. This tuple may optionally contain
a timeout value and/or the atom `hibernate` to make the
process enter hibernation until a message is received.
This snippet enables the loop handler.
``` erlang ``` erlang
-module(my_loop_handler). init(_Type, Req, _Opts) ->
-behaviour(cowboy_loop_handler). {loop, Req, undefined_state}.
```
-export([init/3]). However it is largely recommended that you set a timeout
-export([info/3]). value. The next example sets a timeout value of 30s and
-export([terminate/3]). also makes the process hibernate.
init({tcp, http}, Req, Opts) -> ``` erlang
{loop, Req, undefined_state, 60000, hibernate}. init(_Type, Req, _Opts) ->
{loop, Req, undefined_state, 30000, hibernate}.
```
Receive loop
------------
Once initialized, Cowboy will wait for messages to arrive
in the process' mailbox. When a message arrives, Cowboy
calls the `info/3` function with the message, the Req object
and the handler's state.
The following snippet sends a reply when it receives a
`reply` message from another process, or waits for another
message otherwise.
``` erlang
info({reply, Body}, Req, State) -> info({reply, Body}, Req, State) ->
{ok, Req2} = cowboy_req:reply(200, [], Body, Req), {ok, Req2} = cowboy_req:reply(200, [], Body, Req),
{ok, Req2, State}; {ok, Req2, State};
info(Message, Req, State) -> info(_Msg, Req, State) ->
{loop, Req, State, hibernate}. {loop, Req, State, hibernate}.
terminate(Reason, Req, State) ->
ok.
``` ```
Do note that the `reply` tuple here may be any message
and is simply an example.
This callback may perform any necessary operation including
sending all or parts of a reply, and will subsequently
return a tuple indicating if more messages are to be expected.
The callback may also choose to do nothing at all and just
skip the message received.
If a reply is sent, then the `ok` tuple should be returned.
This will instruct Cowboy to end the request.
Otherwise a `loop` tuple should be returned.
Streaming loop
--------------
Another common case well suited for loop handlers is
streaming data received in the form of Erlang messages.
This can be done by initiating a chunked reply in the
`init/3` callback and then using `cowboy_req:chunk/2`
every time a message is received.
The following snippet does exactly that. As you can see
a chunk is sent every time a `chunk` message is received,
and the loop is stopped by sending an `eof` message.
``` erlang
init(_Type, Req, _Opts) ->
{ok, Req2} = cowboy_req:chunked_reply(200, [], Req),
{loop, Req2, undefined_state}.
info(eof, Req, State) ->
{ok, Req, State};
info({chunk, Chunk}, Req, State) ->
ok = cowboy_req:chunk(Chunk, Req),
{loop, Req, State};
info(_Msg, Req, State) ->
{loop, Req, State}.
```
Cleaning up
-----------
It is recommended that you set the connection header to
`close` when replying, as this process may be reused for
a subsequent request.
Please refer to the [HTTP handlers chapter](http_handlers.md)
for general instructions about cleaning up.
Timeout
-------
By default Cowboy will not attempt to close the connection
if there is no activity from the client. This is not always
desirable, which is why you can set a timeout. Cowboy will
close the connection if no data was received from the client
after the configured time. The timeout only needs to be set
once and can't be modified afterwards.
Because the request may have had a body, or may be followed
by another request, Cowboy is forced to buffer all data it
receives. This data may grow to become too large though,
so there is a configurable limit for it. The default buffer
size is of 5000 bytes, but it may be changed by setting the
`loop_max_buffer` middleware environment value.
Hibernate
---------
To save memory, you may hibernate the process in between
messages received. This is done by returning the atom
`hibernate` as part of the `loop` tuple callbacks normally
return. Just add the atom at the end and Cowboy will hibernate
accordingly.

View file

@ -1,9 +1,6 @@
Middlewares Middlewares
=========== ===========
Purpose
-------
Cowboy delegates the request processing to middleware components. Cowboy delegates the request processing to middleware components.
By default, two middlewares are defined, for the routing and handling By default, two middlewares are defined, for the routing and handling
of the request, as is detailed in most of this guide. of the request, as is detailed in most of this guide.

View file

@ -1,17 +0,0 @@
Resources
=========
Frameworks
----------
* Please send a pull request!
Helper libraries
----------------
* [eventsource](https://github.com/jdavisp3/eventsource)
Articles
--------
* Please send a pull request!

View file

@ -10,8 +10,9 @@ The REST handler is the recommended way to handle requests.
Initialization Initialization
-------------- --------------
Like Websocket, REST is a sub-protocol of HTTP. It therefore First, the `init/3` callback is called. This callback is common
requires a protocol upgrade. to all handlers. To use REST for the current request, this function
must return an `upgrade` tuple.
``` erlang ``` erlang
init({tcp, http}, Req, Opts) -> init({tcp, http}, Req, Opts) ->

View file

@ -24,6 +24,12 @@ HTTP
* [Sending a response](resp.md) * [Sending a response](resp.md)
* [Using cookies](cookies.md) * [Using cookies](cookies.md)
Multipart
---------
* [Introduction to multipart](multipart_intro.md)
* [Multipart requests](multipart_req.md)
Static files Static files
------------ ------------
@ -36,48 +42,26 @@ REST
* [Handling REST requests](rest_handlers.md) * [Handling REST requests](rest_handlers.md)
* [REST flowcharts](rest_flowcharts.md) * [REST flowcharts](rest_flowcharts.md)
Multipart Websocket
--------- ---------
* [Introduction to multipart](multipart_intro.md)
* [Multipart requests](multipart_req.md)
* Multipart responses
Server push technologies
------------------------
* Push technologies
* [Using loop handlers for server push](loop_handlers.md)
* CORS
Using Websocket
---------------
* [The Websocket protocol](ws_protocol.md) * [The Websocket protocol](ws_protocol.md)
* [Handling Websocket connections](ws_handlers.md) * [Handling Websocket connections](ws_handlers.md)
Advanced HTTP Server push
------------- -----------
* Authentication * [Loop handlers](loop_handlers.md)
* Sessions
Advanced Cowboy usage Pluggable interface
--------------------- -------------------
* Optimization guide
* [Hooks](hooks.md)
* [Middlewares](middlewares.md) * [Middlewares](middlewares.md)
* Access and error logs * [Protocol upgrades](upgrade_protocol.md)
* Handling broken clients * [Hooks](hooks.md)
* HTTP header names
* HTTP/1.1 streaming not chunked
Old guide misc Internals
-------------- ---------
This section will be removed as content is moved into other chapters. * [Architecture](architecture.md)
* [Dealing with broken clients](broken_clients.md)
* [Handlers](handlers.md)
* [Internals](internals.md)
* [Resources](resources.md)

37
guide/upgrade_protocol.md Normal file
View file

@ -0,0 +1,37 @@
Protocol upgrades
=================
Cowboy features many different handlers, each for different purposes.
All handlers have a common entry point: the `init/3` function.
The default handler type is the simple HTTP handler.
To switch to a different protocol, you must perform a protocol
upgrade. This is what is done for Websocket and REST and is
explained in details in the respective chapters.
You can also create your own protocol on top of Cowboy and use
the protocol upgrade mechanism to switch to it.
For example, if you create the `my_protocol` module implementing
the `cowboy_sub_protocol` behavior, then you can upgrade to it
by simply returning the module name from `init/3`.
``` erlang
init(_, _, _Opts) ->
{upgrade, protocol, my_protocol}.
```
The `cowboy_sub_protocol` behavior only requires one callback,
`upgrade/4`. It receives the Req object, the middleware environment,
and the handler and options for this request. This is the same
module as the `init/3` function and the same options that were
passed to it.
``` erlang
upgrade(Req, Env, Handler, HandlerOpts) ->
%% ...
```
This callback is expected to behave like a middleware. Please
see the corresponding chapter for more information.