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

Update the Getting started chapter to new erlang.mk

Length of the chapter divided by 2! \o/
This commit is contained in:
Loïc Hoguin 2014-08-01 14:27:04 +02:00
parent 62de3a62f9
commit 8d436c4744

View file

@ -12,73 +12,82 @@ Cowboy, writing your first application and generating your first
release. At the end of this chapter you should know everything
you need to push your first Cowboy application to production.
:: Application skeleton
:: Bootstrap
Let's start by creating this application. We will simply call it
`hello_erlang`. This application will have the following directory
structure:
We are going to use the ^"erlang.mk^https://github.com/extend/erlang.mk
build system. It also offers bootstrap features allowing us to
quickly get started without having to deal with minute details.
``` bash
hello_erlang/
src/
hello_erlang.app.src
hello_erlang_app.erl
hello_erlang_sup.erl
hello_handler.erl
erlang.mk
Makefile
relx.config
```
Once the release is generated, we will also have the following
files added:
``` bash
hello_erlang/
ebin/
hello_erlang.app
hello_erlang_app.beam
hello_erlang_sup.beam
hello_handler.beam
_rel/
relx
```
As you can probably guess, the `.app.src` file end up becoming
the `.app` file, and the `.erl` files are compiled into `.beam`.
Then, the whole release will be copied into the `_rel/` directory.
The `.app` file contains various informations about the application.
It contains its name, a description, a version, a list of modules,
default configuration and more.
Using a build system like ^"erlang.mk^https://github.com/extend/erlang.mk^,
the list of modules will be included automatically in the `.app` file,
so you don't need to manually put them in your `.app.src` file.
For generating the release, we will use ^"relx^https://github.com/erlware/relx
as it is a much simpler alternative to the tool coming with Erlang.
First, create the `hello_erlang` directory. It should have the same name
as the application within it. Then we create the `src` directory inside
it, which will contain the source code for our application.
First, let's create the directory for our application.
``` bash
$ mkdir hello_erlang
$ cd hello_erlang
$ mkdir src
```
Let's first create the `hello_erlang.app.src` file. It should be pretty
straightforward for the most part. You can use the following template
and change what you like in it.
Then we need to download `erlang.mk`. Either use the following
command or download it manually.
``` bash
$ wget https://raw.githubusercontent.com/extend/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
$ 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
...
(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
application.
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
To add Cowboy as a dependency of your application, we need to modify
two files: the Makefile and the application resource file.
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
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 world with Cowboy!"},
{description, "Hello Erlang!"},
{vsn, "0.1.0"},
{modules, []},
{registered, [hello_erlang_sup]},
{registered, []},
{applications, [
kernel,
stdlib,
@ -89,218 +98,72 @@ and change what you like in it.
]}.
```
The `modules` line will be replaced with the list of modules during
compilation. Make sure to leave this line even if you do not use it
directly.
You may want to set a description for the application while you
are editing the file.
The `registered` value indicates which processes are registered by this
application. You will often only register the top-level supervisor
of the application.
If you run `make` now and start the release, Cowboy will be included
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.
The `applications` value lists the applications that must be started
for this application to work. The Erlang release will start all the
applications listed here automatically.
:: Listening for connections
The `mod` value defines how the application should be started. Erlang
will use the `hello_erlang_app` module for starting the application.
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.
The `hello_erlang_app` module is what we call an application behavior.
The application behavior must define two functions: `start/2` and
`stop/1`, for starting and stopping the application. A typical
application module would look like this:
``` erlang
-module(hello_erlang_app).
-behavior(application).
-export([start/2]).
-export([stop/1]).
start(_Type, _Args) ->
hello_erlang_sup:start_link().
stop(_State) ->
ok.
```
That's not enough however. Since we are building a Cowboy based
application, we also need to initialize Cowboy when we start our
application.
:: Setting up Cowboy
Cowboy does nothing by default.
Cowboy uses Ranch for handling the connections and provides convenience
functions to start Ranch listeners.
The `cowboy:start_http/4` function starts a listener for HTTP connections
using the TCP transport. The `cowboy:start_https/4` function starts a
listener for HTTPS connections using the SSL transport.
Listeners are a group of processes that are used to accept and manage
connections. The processes used specifically for accepting connections
are called acceptors. The number of acceptor processes is unrelated to
the maximum number of connections Cowboy can handle. Please refer to
the ^"Ranch guide^http://ninenines.eu/docs/en/ranch/HEAD/guide/
for in-depth information.
Listeners are named. They spawn a given number of acceptors, listen for
connections using the given transport options and pass along the protocol
options to the connection processes. The protocol options must include
the dispatch list for routing requests to handlers.
The dispatch list is explained in greater details in the
^"Routing^routing^ chapter. For the purpose of this example
we will simply map all URLs to our handler `hello_handler`,
using the wildcard `_` for both the hostname and path parts
of the URL.
This is what the `hello_erlang_app:start/2` function looks like
with Cowboy initialized.
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
start(_Type, _Args) ->
Dispatch = cowboy_router:compile([
%% {URIHost, list({URIPath, Handler, Opts})}
{'_', [{'_', hello_handler, []}]}
{'_', [{"/", hello_handler, []}]}
]),
%% Name, NbAcceptors, TransOpts, ProtoOpts
cowboy:start_http(my_http_listener, 100,
[{port, 8080}],
cowboy:start_http(my_http_listener, 100, [{port, 8080}],
[{env, [{dispatch, Dispatch}]}]
),
hello_erlang_sup:start_link().
```
Do note that we told Cowboy to start listening on port 8080.
You can change this value if needed.
The dispatch list is explained in great details in the
^"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.
Our application doesn't need to start any process, as Cowboy
will automatically start processes for every incoming
connections. We are still required to have a top-level supervisor
however, albeit a fairly small one.
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
404 error.
``` erlang
-module(hello_erlang_sup).
-behavior(supervisor).
:: Handling requests
-export([start_link/0]).
-export([init/1]).
Cowboy features different kinds of handlers, including REST
and Websocket handlers. For this tutorial we will use a plain
HTTP handler.
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
First, let's generate a handler from a template.
init([]) ->
{ok, {{one_for_one, 10, 10}, []}}.
``` bash
$ make new t=cowboy_http n=hello_handler
```
Finally, we need to write the code for handling incoming requests.
:: Handling HTTP requests
Cowboy features many kinds of handlers. For this simple example,
we will just use the plain HTTP handler, which has three callback
functions: `init/3`, `handle/2` and `terminate/3`. You can find more
information about the arguments and possible return values of these
callbacks in the
^"cowboy_http_handler function reference^http://ninenines.eu/docs/en/cowboy/HEAD/manual/cowboy_http_handler^.
Our handler will only send a friendly hello back to the client.
You can then open the `src/hello_handler.erl` file and modify
the `handle/2` function like this to send a reply.
``` erlang
-module(hello_handler).
-behavior(cowboy_http_handler).
-export([init/3]).
-export([handle/2]).
-export([terminate/3]).
init(_Type, Req, _Opts) ->
{ok, Req, undefined_state}.
handle(Req, State) ->
{ok, Req2} = cowboy_req:reply(200, [
{<<"content-type">>, <<"text/plain">>}
], <<"Hello World!">>, Req),
handle(Req, State=#state{}) ->
{ok, Req2} = cowboy_req:reply(200,
[{<<"content-type">>, <<"text/plain">>}],
<<"Hello Erlang!">>,
Req),
{ok, Req2, State}.
terminate(_Reason, _Req, _State) ->
ok.
```
The `Req` variable above is the Req object, which allows the developer
to obtain information about the request and to perform a reply.
Its usage is documented in the
^"cowboy_req function reference^http://ninenines.eu/docs/en/cowboy/HEAD/manual/cowboy_req^.
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!`.
The code for our application is ready, so let's build a release!
:: Compiling
First we need to download `erlang.mk`.
``` bash
$ wget https://raw.github.com/extend/erlang.mk/master/erlang.mk
$ ls
src/
erlang.mk
```
Then we need to create a Makefile that will include `erlang.mk`
for building our application. We need to define the Cowboy
dependency in the Makefile. Thankfully `erlang.mk` already
knows where to find Cowboy as it features a package index,
so we can just tell it to look there.
``` Makefile
PROJECT = hello_erlang
DEPS = cowboy
dep_cowboy = pkg://cowboy master
include erlang.mk
```
Note that when creating production nodes you will most likely
want to use a specific version of Cowboy instead of `master`,
and properly test your release every time you update Cowboy.
If you type `make` in a shell now, your application should build
as expected. If you get compilation errors, double check that you
haven't made any typo when creating the previous files.
``` bash
$ make
```
:: Generating the release
That's not all however, as we want to create a working release.
For that purpose, we need to create a `relx.config` file. When
this file exists, `erlang.mk` will automatically download `relx`
and build the release when you type `make`.
In the `relx.config` file, we only need to tell `relx` that
we want the release to include the `hello_erlang` application,
and that we want an extended start script for convenience.
`relx` will figure out which other applications are required
by looking into the `.app` files for dependencies.
``` erlang
{release, {hello_erlang, "1"}, [hello_erlang]}.
{extended_start_script, true}.
```
The `release` value is used to specify the release name, its
version, and the applications to be included.
We can now build and start the release.
``` bash
$ make
$ ./_rel/hello_erlang/bin/hello_erlang console
```
If you then access `http://localhost:8080` using your browser,
you should receive a nice greet!
If you build the release, start it and open ^http://localhost:8080
in your browser, you should get a nice `Hello Erlang!` displayed!