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

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
===============
Setting up a working Erlang application is a little more complex than
for most other languages. The reason is that Erlang is designed to
build systems and not just simple applications.
Erlang is more than a language, it is also an operating system
for your applications. Erlang developers rarely write standalone
modules, they write libraries or applications, and then bundle
those into what is called a release. A release contains the
Erlang VM plus all applications required to run the node, so
it can be pushed to production directly.
An Erlang system is typically comprised of many different nodes,
each containing many different OTP applications, each containing
many different modules and running many different processes.
Nodes may or may not be identical depending on the nature of the
system.
This chapter walks you through all the steps of setting up
Cowboy, writing your first application and generating your first
release. At the end of this chapter you should know everything
you need to push your first Cowboy application to production.
To get started though, we only need one node that contains your own
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.
Application skeleton
--------------------
Let's start by creating this application. We will simply call it
`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.
Setting up Cowboy
-----------------
Cowboy does nothing by default.
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.
Handling HTTP requests
----------------------
Cowboy features many kinds of handlers. For this simple example,
we will just use the plain HTTP handler, which has three callback
functions: `init/3`, `handle/2` and `terminate/3`. You can find more
@ -235,6 +240,9 @@ Its usage is documented in the
The code for our application is ready, so let's build a release!
Compiling
---------
First we need to download `erlang.mk`.
``` bash
@ -271,6 +279,9 @@ haven't made any typo when creating the previous files.
$ make
```
Generating the release
----------------------
That's not all however, as we want to create a working release.
For that purpose, we need to create a `relx.config` file. When
this file exists, `erlang.mk` will automatically download `relx`
@ -299,6 +310,3 @@ $ ./_rel/bin/hello_erlang console
If you then access `http://localhost:8080` using your browser,
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
=====
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 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.
On response
-----------
Onresponse
----------
The `onresponse` hook is called right before sending the response
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
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
cowboy:start_http(my_http_listener, 100,

View file

@ -1,24 +1,11 @@
Loop handlers
=============
Purpose
-------
Loop handlers are a special kind of HTTP handlers used when the
response can not be sent right away. The handler enters instead
a receive loop waiting for the right message before it can send
a response.
They are most useful when performing long-polling operations or
when using server-sent events.
While the same can be accomplished using plain HTTP handlers,
it is recommended to use loop handlers because they are well-tested
and allow using built-in features like hibernation and timeouts.
Usage
-----
Loop handlers are used for requests where a response might not
be immediately available, but where you would like to keep the
connection open for a while in case the response arrives. The
@ -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
practice is known as server-sent events.
While the same can be accomplished using plain HTTP handlers,
it is recommended to use loop handlers because they are well-tested
and allow using built-in features like hibernation and timeouts.
Loop handlers essentially wait for one or more Erlang messages
and feed these messages to the `info/3` callback. It also features
the `init/3` and `terminate/3` callbacks which work the same as
for plain HTTP handlers.
The following handler waits for a message `{reply, Body}` before
sending a response. If this message doesn't arrive within 60
seconds, it gives up and a `204 No Content` will be replied.
It also hibernates the process to save memory while waiting for
this message.
Initialization
--------------
The `init/3` function must return a `loop` tuple to enable
loop handler behavior. This tuple may optionally contain
a timeout value and/or the atom `hibernate` to make the
process enter hibernation until a message is received.
This snippet enables the loop handler.
``` erlang
-module(my_loop_handler).
-behaviour(cowboy_loop_handler).
init(_Type, Req, _Opts) ->
{loop, Req, undefined_state}.
```
-export([init/3]).
-export([info/3]).
-export([terminate/3]).
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.
init({tcp, http}, Req, Opts) ->
{loop, Req, undefined_state, 60000, hibernate}.
``` erlang
init(_Type, Req, _Opts) ->
{loop, Req, undefined_state, 30000, hibernate}.
```
Receive loop
------------
Once initialized, Cowboy will wait for messages to arrive
in the process' mailbox. When a message arrives, Cowboy
calls the `info/3` function with the message, the Req object
and the handler's state.
The following snippet sends a reply when it receives a
`reply` message from another process, or waits for another
message otherwise.
``` erlang
info({reply, Body}, Req, State) ->
{ok, Req2} = cowboy_req:reply(200, [], Body, Req),
{ok, Req2, State};
info(Message, Req, State) ->
info(_Msg, Req, State) ->
{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
===========
Purpose
-------
Cowboy delegates the request processing to middleware components.
By default, two middlewares are defined, for the routing and handling
of the request, as is detailed in most of this guide.

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
--------------
Like Websocket, REST is a sub-protocol of HTTP. It therefore
requires a protocol upgrade.
First, the `init/3` callback is called. This callback is common
to all handlers. To use REST for the current request, this function
must return an `upgrade` tuple.
``` erlang
init({tcp, http}, Req, Opts) ->

View file

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