Browse Source

Greatly improve the README.

Forward me any more questions and I'll continue on improving it.
Loïc Hoguin 14 years ago
parent
commit
5a7b7429ff
1 changed files with 181 additions and 38 deletions
  1. 181 38
      README.md

+ 181 - 38
README.md

@@ -8,9 +8,10 @@ Goals
 
 
 Cowboy aims to provide the following advantages:
 Cowboy aims to provide the following advantages:
 
 
-* **Small** codebase.
+* **Small** code base.
 * Damn **fast**.
 * Damn **fast**.
-* **Modular**: transport, protocol and handlers are replaceable. (see below)
+* **Modular**: transport and protocol handlers are replaceable.
+* **Binary HTTP** for greater speed and lower memory usage.
 * Easy to **embed** inside another application.
 * Easy to **embed** inside another application.
 * Selectively **dispatch** requests to handlers, allowing you to send some
 * Selectively **dispatch** requests to handlers, allowing you to send some
   requests to your embedded code and others to a FastCGI application in
   requests to your embedded code and others to a FastCGI application in
@@ -19,69 +20,211 @@ Cowboy aims to provide the following advantages:
 
 
 The server is currently in early development stage. Comments, suggestions are
 The server is currently in early development stage. Comments, suggestions are
 more than welcome. To contribute, either open bug reports, or fork the project
 more than welcome. To contribute, either open bug reports, or fork the project
-and send us pull requests with new or improved functionality. Of course you
-might want to discuss your plans with us before you do any serious work so
-we can share ideas and save everyone time.
+and send us pull requests with new or improved functionality. You should
+discuss your plans with us before doing any serious work, though, to avoid
+duplicating efforts.
 
 
-Embedding Cowboy
-----------------
+Quick start
+-----------
 
 
 * Add Cowboy as a rebar or agner dependency to your application.
 * Add Cowboy as a rebar or agner dependency to your application.
 * Start Cowboy and add one or more listeners.
 * Start Cowboy and add one or more listeners.
-* Write handlers.
+* Write handlers for your application.
 
 
 Getting Started
 Getting Started
 ---------------
 ---------------
 
 
-Cowboy can be started and stopped like any other application. However, the
-Cowboy application does not start any listener, those must be started manually.
+At heart, Cowboy is nothing more than an TCP acceptor pool. All it does is
+accept connections received on a given port and using a given transport,
+like TCP or SSL, and forward them to a request handler for the given
+protocol. Acceptors and request handlers are of course supervised
+automatically.
 
 
-A listener is a special kind of supervisor that handles a pool of acceptor
-processes. It also manages all its associated request processes. This allows
-you to shutdown all processes related to a listener by stopping the supervisor.
+It just so happens that Cowboy also includes an HTTP protocol handler.
+But Cowboy does nothing by default. You need to explicitly ask Cowboy
+to listen on a port with your chosen transport and protocol handlers.
+To do so, you must start a listener.
 
 
-An acceptor simply accepts connections and forwards them to a protocol module,
-for example HTTP. You must thus define the transport and protocol module to
-use for the listener, their options and the number of acceptors in the pool
-before you can start a listener supervisor.
+A listener is a special kind of supervisor that manages both the
+acceptor pool and the request processes. It is named and can thus be
+started and stopped at will.
 
 
-For HTTP applications the transport can be either TCP or SSL for HTTP and
-HTTPS respectively. On the other hand, the protocol is of course HTTP.
+An acceptor pool is a pool of processes whose only role is to accept
+new connections. It's good practice to have many of these processes
+as they are very cheap and allow much quicker response when you get
+many connections. Of course, as with everything else, you should
+**benchmark** before you decide what's best for you.
 
 
-You can start and stop listeners by calling cowboy:start_listener and
-cowboy:stop_listener respectively. It is your responsability to give each
-listener a unique name.
+Cowboy includes a TCP transport handler for HTTP and an SSL transport
+handler for HTTPS. The transport handlers can of course be reused for
+other protocols like FTP or IRC.
+
+The HTTP protocol requires one last thing to continue: dispatching rules.
+Don't worry about it right now though and continue reading, it'll all
+be explained.
 
 
-Code speaks more than words:
+You can start and stop listeners by calling cowboy:start_listener and
+cowboy:stop_listener respectively, as demonstrated in the following
+example.
 
 
 ``` erlang
 ``` erlang
-application:start(cowboy),
-Dispatch = [
-    %% {Host, list({Path, Handler, Opts})}
-    {'_', [{'_', my_handler, []}]}
-],
-%% Name, NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts
-cowboy:start_listener(http, 100,
-    cowboy_tcp_transport, [{port, 8080}],
-    cowboy_http_protocol, [{dispatch, Dispatch}]
-).
+-module(my_app).
+-behaviour(application).
+-export([start/2, stop/1]).
+
+start(_Type, _Args) ->
+    application:start(cowboy),
+    Dispatch = [
+        %% {Host, list({Path, Handler, Opts})}
+        {'_', [{'_', my_handler, []}]}
+    ],
+    %% Name, NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts
+    cowboy:start_listener(http, 100,
+        cowboy_tcp_transport, [{port, 8080}],
+        cowboy_http_protocol, [{dispatch, Dispatch}]
+    ).
+
+stop(_State) ->
+	ok.
 ```
 ```
 
 
-You must also write the `my_handler` module to process requests. You can
-use one of the predefined handlers or write your own. An hello world HTTP
-handler could be written like this:
+This is not enough though, you must also write the my_handler module
+to process the incoming HTTP requests. Of course Cowboy comes with
+predefined handlers for specific tasks but most of the time you'll
+want to write your own handlers for your application.
+
+Following is an example of an "Hello World!" HTTP handler.
 
 
 ``` erlang
 ``` erlang
 -module(my_handler).
 -module(my_handler).
+-behaviour(cowboy_http_handler).
 -export([init/3, handle/2, terminate/2]).
 -export([init/3, handle/2, terminate/2]).
 
 
 init({tcp, http}, Req, Opts) ->
 init({tcp, http}, Req, Opts) ->
-    {ok, Req, undefined}.
+    {ok, Req, undefined_state}.
 
 
 handle(Req, State) ->
 handle(Req, State) ->
-    {ok, Req2} = cowboy_http_req:reply(200, [], "Hello World!", Req),
+    {ok, Req2} = cowboy_http_req:reply(200, [], <<"Hello World!">>, Req),
     {ok, Req2, State}.
     {ok, Req2, State}.
 
 
 terminate(Req, State) ->
 terminate(Req, State) ->
     ok.
     ok.
 ```
 ```
+
+Continue reading to learn how to dispatch rules and handle requests.
+
+Dispatch rules
+--------------
+
+Cowboy allows you to dispatch HTTP requests directly to a specific handler
+based on the hostname and path information from the request. It also lets
+you define static options for the handler directly in the rules.
+
+To match the hostname and path, Cowboy requires a list of tokens. For
+example, to match the "dev-extend.eu" domain name, you must specify
+[<<"dev-extend">>, <<"eu">>]. Or, to match the "/path/to/my/resource"
+you must use [<<"path">>, <<"to">>, <<"my">>, <<"resource">>]. All the
+tokens must be given as binary.
+
+You can use the special token '_' (the atom underscore) to indicate that
+you accept anything in that position. For example if you have both
+"dev-extend.eu" and "dev-extend.fr" domains, you can use the match spec
+[<<"dev-extend">>, '_'] to match any top level extension.
+
+Any other atom used as a token will bind the value to this atom when
+matching. To follow on our hostnames example, [<<"dev-extend">>, ext]
+would bind the values <<"eu">> and <<"fr">> to the ext atom, that you
+can later retrieve in your handler by calling `cowboy_http_req:binding/{2,3}`.
+
+You can also accept any match spec by using the atom '_' directly instead of
+a list of tokens. Our hello world example above uses this to forward all
+requests to a single handler.
+
+There is currently no way to match multiple tokens at once.
+
+Requests handling
+-----------------
+
+Requests are passed around in the Request variable. Although they are
+defined as a record, it is recommended to access them only through the
+cowboy_http_req module API.
+
+You can retrieve the HTTP method, HTTP version, peer address and port,
+host tokens, raw host, used port, path tokens, raw path, query string
+values, bound values from the dispatch step, header values from the
+request. You can also read the request body, if any, optionally parsing
+it as a query string. Finally, the request allows you to send a response
+to the client.
+
+See the cowboy_http_req module for more information.
+
+Websockets
+----------
+
+The Websocket protocol is built upon the HTTP protocol. It first sends
+an HTTP request for an handshake, performs it and then switches
+to Websocket. Therefore you need to write a standard HTTP handler to
+confirm the handshake should be completed and then the Websocket-specific
+callbacks.
+
+A simple handler doing nothing but sending a repetitive message using
+Websocket would look like this:
+
+``` erlang
+-module(my_ws_handler).
+-behaviour(cowboy_http_handler).
+-behaviour(cowboy_http_websocket_handler).
+-export([init/3, handle/2, terminate/2]).
+-export([websocket_init/3, websocket_handle/3, websocket_terminate/3]).
+
+init({tcp, http}, Req, Opts) ->
+    {upgrade, protocol, cowboy_http_websocket}.
+
+handle(Req, State) ->
+	error(foo). %% Will never be called.
+
+terminate(Req, State) ->
+    error(foo). %% Same for that one.
+
+websocket_init(TransportName, Req, _Opts) ->
+    erlang:start_timer(1000, self(), <<"Hello!">>),
+    {ok, Req, undefined_state}.
+
+websocket_handle({timeout, _Ref, Msg}, Req, State) ->
+    erlang:start_timer(1000, self(), <<"How' you doin'?">>),
+    {reply, Msg, Req, State};
+websocket_handle({websocket, Msg}, Req, State) ->
+    {reply, <<"That's what she said! ", Msg/binary >>, Req, State}.
+
+websocket_terminate(_Reason, _Req, _State) ->
+    ok.
+```
+
+Of course you can have an HTTP handler doing both HTTP and Websocket
+handling, but for the sake of this example we're ignoring the HTTP
+part entirely.
+
+Using Cowboy with other protocols
+---------------------------------
+
+One of the strength of Cowboy is of course that you can use it with any
+protocol you want. The only downside is that if it's not HTTP, you'll
+probably have to write the protocol handler yourself.
+
+The only exported function a protocol handler needs is the start_link/3
+function, with arguments Socket, Transport and Opts. Socket is of course
+the client socket; Transport is the module name of the chosen transport
+handler and Opts is protocol options defined when starting the listener.
+Anything you do past this point is up to you!
+
+You should definitely look at the cowboy_http_protocol module for a great
+example of fast requests handling if you need to. Otherwise it's probably
+safe to use {active, once} mode and handle everything as it comes.
+
+Note that while you technically can run a protocol handler directly as a
+gen_server or a gen_fsm, it's probably not a good idea, as the only call
+you'll ever receive from Cowboy is the start_link/3 call. On the other
+hand, feel free to write a very basic protocol handler which then forwards
+requests to a gen_server or gen_fsm. By doing so however you must take
+care to supervise their processes as Cowboy only know about the protocol
+handler itself.