diff options
Diffstat (limited to 'guide')
-rw-r--r-- | guide/getting_started.md | 284 |
1 files changed, 254 insertions, 30 deletions
diff --git a/guide/getting_started.md b/guide/getting_started.md index 2ad67db..3143a7f 100644 --- a/guide/getting_started.md +++ b/guide/getting_started.md @@ -1,18 +1,136 @@ Getting started =============== -Cowboy does nothing by default. +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. + +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. + +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. + +Let's start by creating this application. We will simply call it +`hello_erlang`. This application will have the following directory +structure: + +``` +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: + +``` +hello_erlang/ + ebin/ + hello_erlang.app + hello_erlang_app.beam + hello_erlang_sup.beam + hello_handler.beam + _rel/ +``` + +As you can probably guess, the `.app.src` file end up becoming +the `.app` file, and the `.erl` files are compiled into `.beam`. +Then, the whole release will be copied into the `_rel/` directory. + +The `.app` file contains various informations about the application. +It contains its name, a description, a version, a list of modules, +default configuration and more. + +Using a build system like [erlang.mk](https://github.com/extend/erlang.mk), +the list of modules will be included automatically in the `.app` file, +so you don't need to manually put them in your `.app.src` file. + +For generating the release, we will use [relx](https://github.com/erlware/relx) +as it is a much simpler alternative to the tool coming with Erlang. + +First, create the `hello_erlang` directory. It should have the same name +as the application within it. Then we create the `src` directory inside +it, which will contain the source code for our application. + +``` bash +$ mkdir hello_erlang +$ cd hello_erlang +$ mkdir src +``` -Cowboy requires the `crypto`, `cowlib` and `ranch` applications to be -started. +Let's first create the `hello_erlang.app.src` file. It should be pretty +straightforward for the most part. You can use the following template +and change what you like in it. ``` erlang -ok = application:start(crypto). -ok = application:start(cowlib). -ok = application:start(ranch). -ok = application:start(cowboy). +{application, hello_erlang, [ + {description, "Hello world with Cowboy!"}, + {vsn, "0.1.0"}, + {modules, []}, + {registered, [hello_erlang_sup]}, + {applications, [ + kernel, + stdlib, + cowboy + ]}, + {mod, {hello_erlang_app, []}}, + {env, []} +]}. ``` +The `modules` line will be replaced with the list of modules during +compilation. Make sure to leave this line even if you do not use it +directly. + +The `registered` value indicates which processes are registered by this +application. You will often only register the top-level supervisor +of the application. + +The `applications` value lists the applications that must be started +for this application to work. The Erlang release will start all the +applications listed here automatically. + +The `mod` value defines how the application should be started. Erlang +will use the `hello_erlang_app` module for starting the application. + +The `hello_erlang_app` module is what we call an application behavior. +The application behavior must define two functions: `start/2` and +`stop/1`, for starting and stopping the application. A typical +application module would look like this: + +``` erlang +-module(hello_erlang_app). +-behavior(application). + +-export([start/2]). +-export([stop/1]). + +start(_Type, _Args) -> + hello_erlang_sup:start_link(). + +stop(_State) -> + ok. +``` + +That's not enough however. Since we are building a Cowboy based +application, we also need to initialize Cowboy when we start our +application. + +Cowboy does nothing by default. + Cowboy uses Ranch for handling the connections and provides convenience functions to start Ranch listeners. @@ -33,50 +151,156 @@ 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.md) chapter. +[Routing](routing.md) 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 how the `hello_erlang_app:start/2` function looks like +with Cowboy initialized. ``` erlang -Dispatch = cowboy_router:compile([ - %% {URIHost, list({URIPath, Handler, Opts})} - {'_', [{'_', my_handler, []}]} -]), -%% Name, NbAcceptors, TransOpts, ProtoOpts -cowboy:start_http(my_http_listener, 100, - [{port, 8080}], - [{env, [{dispatch, Dispatch}]}] -). +start(_Type, _Args) -> + Dispatch = cowboy_router:compile([ + %% {URIHost, list({URIPath, Handler, Opts})} + {'_', [{'_', hello_handler, []}]} + ]), + %% Name, NbAcceptors, TransOpts, ProtoOpts + cowboy:start_http(my_http_listener, 100, + [{port, 8080}], + [{env, [{dispatch, Dispatch}]}] + ), + hello_erlang_sup:start_link(). ``` +Do note that we told Cowboy to start listening on port 8080. +You can change this value if needed. + +Our application doesn't need to start any process, as Cowboy +will automatically start processes for every incoming +connections. We are still required to have a top-level supervisor +however, albeit a fairly small one. + +``` erlang +-module(hello_erlang_sup). +-behavior(supervisor). + +-export([start_link/0]). +-export([init/1]). + +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +init([]) -> + {ok, {{one_for_one, 10, 10}, []}}. +``` + +Finally, we need to write the code for handling incoming requests. + 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 +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). -Here is an example of a simple HTTP handler module. + +Our handler will only send a friendly hello back to the client. ``` erlang --module(my_handler). --behaviour(cowboy_http_handler). +-module(hello_handler). +-behavior(cowboy_http_handler). -export([init/3]). -export([handle/2]). -export([terminate/3]). -init({tcp, http}, Req, Opts) -> +init(_Type, Req, _Opts) -> {ok, Req, undefined_state}. handle(Req, State) -> - {ok, Req2} = cowboy_req:reply(200, [], <<"Hello World!">>, Req), + {ok, Req2} = cowboy_req:reply(200, [ + {<<"content-type">>, <<"text/plain">>} + ], <<"Hello World!">>, Req), {ok, Req2, State}. -terminate(Reason, Req, 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 explained in the [cowboy_req function reference](http://ninenines.eu/docs/en/cowboy/HEAD/manual/cowboy_req). +to obtain information about the request and to perform a reply. +Its usage is documented in the +[cowboy_req function reference](http://ninenines.eu/docs/en/cowboy/HEAD/manual/cowboy_req). + +The code for our application is ready, so let's build a release! + +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 +``` + +That's not all however, as we want to create a working release. +For that purpose we will need `relx`. You can download it directly +[from Github](https://github.com/erlware/relx). After downloading +it, you will need to build it using `make`, which should give +you a `relx` executable that you can then put in your `$PATH`. +You only need to do this once. + +We are almost ready to build the release. All that's left is +the `relx.config` file! In it, 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 +$ relx +$ ./_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 examples in the `examples/` directory of the -Cowboy repository. A more complete "Hello world" example can be -found in the `examples/hello_world/` directory. +You can find many more examples in the `examples/` directory +of the Cowboy repository. |