From 4023e7f4e429179fd9c2cce4487c33646c6bd327 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= Date: Thu, 14 Jan 2016 13:35:25 +0100 Subject: Convert the documentation to Asciidoc A few small revisions were made, and Erlang.mk has been updated. --- .gitattributes | 1 + .gitignore | 3 +- Makefile | 52 - build.config | 36 - doc/src/guide/architecture.asciidoc | 48 + doc/src/guide/architecture.ezdoc | 47 - doc/src/guide/book.asciidoc | 72 + doc/src/guide/broken_clients.asciidoc | 61 + doc/src/guide/broken_clients.ezdoc | 60 - doc/src/guide/constraints.asciidoc | 54 + doc/src/guide/constraints.ezdoc | 51 - doc/src/guide/cookies.asciidoc | 163 + doc/src/guide/cookies.ezdoc | 162 - doc/src/guide/erlang_beginners.asciidoc | 36 + doc/src/guide/erlang_beginners.ezdoc | 35 - doc/src/guide/erlang_web.asciidoc | 176 ++ doc/src/guide/erlang_web.ezdoc | 175 -- doc/src/guide/getting_started.asciidoc | 136 + doc/src/guide/getting_started.ezdoc | 169 - doc/src/guide/handlers.asciidoc | 105 + doc/src/guide/handlers.ezdoc | 99 - doc/src/guide/hooks.asciidoc | 46 + doc/src/guide/hooks.ezdoc | 43 - doc/src/guide/index.ezdoc | 58 - doc/src/guide/introduction.asciidoc | 56 + doc/src/guide/introduction.ezdoc | 55 - doc/src/guide/loop_handlers.asciidoc | 146 + doc/src/guide/loop_handlers.ezdoc | 141 - doc/src/guide/middlewares.asciidoc | 69 + doc/src/guide/middlewares.ezdoc | 68 - doc/src/guide/modern_web.asciidoc | 214 ++ doc/src/guide/modern_web.ezdoc | 215 -- doc/src/guide/multipart.asciidoc | 169 + doc/src/guide/multipart.ezdoc | 164 - doc/src/guide/overview.asciidoc | 150 + doc/src/guide/overview.ezdoc | 147 - doc/src/guide/req.asciidoc | 247 ++ doc/src/guide/req.ezdoc | 269 -- doc/src/guide/req_body.asciidoc | 152 + doc/src/guide/req_body.ezdoc | 158 - doc/src/guide/resource_design.asciidoc | 221 ++ doc/src/guide/resource_design.ezdoc | 220 -- doc/src/guide/resp.asciidoc | 201 ++ doc/src/guide/resp.ezdoc | 197 -- doc/src/guide/rest_flowcharts.asciidoc | 248 ++ doc/src/guide/rest_flowcharts.ezdoc | 247 -- doc/src/guide/rest_handlers.asciidoc | 133 + doc/src/guide/rest_handlers.ezdoc | 125 - doc/src/guide/rest_principles.asciidoc | 160 + doc/src/guide/rest_principles.ezdoc | 159 - doc/src/guide/routing.asciidoc | 224 ++ doc/src/guide/routing.ezdoc | 239 -- doc/src/guide/static_files.asciidoc | 171 ++ doc/src/guide/static_files.ezdoc | 168 - doc/src/guide/sub_protocols.asciidoc | 68 + doc/src/guide/sub_protocols.ezdoc | 64 - doc/src/guide/ws_handlers.asciidoc | 196 ++ doc/src/guide/ws_handlers.ezdoc | 188 -- doc/src/guide/ws_protocol.asciidoc | 43 + doc/src/guide/ws_protocol.ezdoc | 42 - doc/src/manual/cowboy.asciidoc | 98 + doc/src/manual/cowboy.ezdoc | 98 - doc/src/manual/cowboy_app.asciidoc | 25 + doc/src/manual/cowboy_app.ezdoc | 23 - doc/src/manual/cowboy_handler.asciidoc | 99 + doc/src/manual/cowboy_handler.ezdoc | 106 - doc/src/manual/cowboy_loop.asciidoc | 86 + doc/src/manual/cowboy_loop.ezdoc | 92 - doc/src/manual/cowboy_middleware.asciidoc | 48 + doc/src/manual/cowboy_middleware.ezdoc | 47 - doc/src/manual/cowboy_protocol.asciidoc | 75 + doc/src/manual/cowboy_protocol.ezdoc | 75 - doc/src/manual/cowboy_req.asciidoc | 690 +++++ doc/src/manual/cowboy_req.ezdoc | 715 ----- doc/src/manual/cowboy_rest.asciidoc | 529 ++++ doc/src/manual/cowboy_rest.ezdoc | 536 ---- doc/src/manual/cowboy_router.asciidoc | 56 + doc/src/manual/cowboy_router.ezdoc | 55 - doc/src/manual/cowboy_spdy.asciidoc | 42 + doc/src/manual/cowboy_spdy.ezdoc | 34 - doc/src/manual/cowboy_static.asciidoc | 41 + doc/src/manual/cowboy_static.ezdoc | 32 - doc/src/manual/cowboy_sub_protocol.asciidoc | 27 + doc/src/manual/cowboy_sub_protocol.ezdoc | 27 - doc/src/manual/cowboy_websocket.asciidoc | 143 + doc/src/manual/cowboy_websocket.ezdoc | 149 - doc/src/manual/http_status_codes.asciidoc | 157 + doc/src/manual/http_status_codes.ezdoc | 151 - doc/src/manual/index.ezdoc | 18 - erlang.mk | 4446 +++++++++++++++++++++++++-- 90 files changed, 10077 insertions(+), 6265 deletions(-) create mode 100644 .gitattributes delete mode 100644 build.config create mode 100644 doc/src/guide/architecture.asciidoc delete mode 100644 doc/src/guide/architecture.ezdoc create mode 100644 doc/src/guide/book.asciidoc create mode 100644 doc/src/guide/broken_clients.asciidoc delete mode 100644 doc/src/guide/broken_clients.ezdoc create mode 100644 doc/src/guide/constraints.asciidoc delete mode 100644 doc/src/guide/constraints.ezdoc create mode 100644 doc/src/guide/cookies.asciidoc delete mode 100644 doc/src/guide/cookies.ezdoc create mode 100644 doc/src/guide/erlang_beginners.asciidoc delete mode 100644 doc/src/guide/erlang_beginners.ezdoc create mode 100644 doc/src/guide/erlang_web.asciidoc delete mode 100644 doc/src/guide/erlang_web.ezdoc create mode 100644 doc/src/guide/getting_started.asciidoc delete mode 100644 doc/src/guide/getting_started.ezdoc create mode 100644 doc/src/guide/handlers.asciidoc delete mode 100644 doc/src/guide/handlers.ezdoc create mode 100644 doc/src/guide/hooks.asciidoc delete mode 100644 doc/src/guide/hooks.ezdoc delete mode 100644 doc/src/guide/index.ezdoc create mode 100644 doc/src/guide/introduction.asciidoc delete mode 100644 doc/src/guide/introduction.ezdoc create mode 100644 doc/src/guide/loop_handlers.asciidoc delete mode 100644 doc/src/guide/loop_handlers.ezdoc create mode 100644 doc/src/guide/middlewares.asciidoc delete mode 100644 doc/src/guide/middlewares.ezdoc create mode 100644 doc/src/guide/modern_web.asciidoc delete mode 100644 doc/src/guide/modern_web.ezdoc create mode 100644 doc/src/guide/multipart.asciidoc delete mode 100644 doc/src/guide/multipart.ezdoc create mode 100644 doc/src/guide/overview.asciidoc delete mode 100644 doc/src/guide/overview.ezdoc create mode 100644 doc/src/guide/req.asciidoc delete mode 100644 doc/src/guide/req.ezdoc create mode 100644 doc/src/guide/req_body.asciidoc delete mode 100644 doc/src/guide/req_body.ezdoc create mode 100644 doc/src/guide/resource_design.asciidoc delete mode 100644 doc/src/guide/resource_design.ezdoc create mode 100644 doc/src/guide/resp.asciidoc delete mode 100644 doc/src/guide/resp.ezdoc create mode 100644 doc/src/guide/rest_flowcharts.asciidoc delete mode 100644 doc/src/guide/rest_flowcharts.ezdoc create mode 100644 doc/src/guide/rest_handlers.asciidoc delete mode 100644 doc/src/guide/rest_handlers.ezdoc create mode 100644 doc/src/guide/rest_principles.asciidoc delete mode 100644 doc/src/guide/rest_principles.ezdoc create mode 100644 doc/src/guide/routing.asciidoc delete mode 100644 doc/src/guide/routing.ezdoc create mode 100644 doc/src/guide/static_files.asciidoc delete mode 100644 doc/src/guide/static_files.ezdoc create mode 100644 doc/src/guide/sub_protocols.asciidoc delete mode 100644 doc/src/guide/sub_protocols.ezdoc create mode 100644 doc/src/guide/ws_handlers.asciidoc delete mode 100644 doc/src/guide/ws_handlers.ezdoc create mode 100644 doc/src/guide/ws_protocol.asciidoc delete mode 100644 doc/src/guide/ws_protocol.ezdoc create mode 100644 doc/src/manual/cowboy.asciidoc delete mode 100644 doc/src/manual/cowboy.ezdoc create mode 100644 doc/src/manual/cowboy_app.asciidoc delete mode 100644 doc/src/manual/cowboy_app.ezdoc create mode 100644 doc/src/manual/cowboy_handler.asciidoc delete mode 100644 doc/src/manual/cowboy_handler.ezdoc create mode 100644 doc/src/manual/cowboy_loop.asciidoc delete mode 100644 doc/src/manual/cowboy_loop.ezdoc create mode 100644 doc/src/manual/cowboy_middleware.asciidoc delete mode 100644 doc/src/manual/cowboy_middleware.ezdoc create mode 100644 doc/src/manual/cowboy_protocol.asciidoc delete mode 100644 doc/src/manual/cowboy_protocol.ezdoc create mode 100644 doc/src/manual/cowboy_req.asciidoc delete mode 100644 doc/src/manual/cowboy_req.ezdoc create mode 100644 doc/src/manual/cowboy_rest.asciidoc delete mode 100644 doc/src/manual/cowboy_rest.ezdoc create mode 100644 doc/src/manual/cowboy_router.asciidoc delete mode 100644 doc/src/manual/cowboy_router.ezdoc create mode 100644 doc/src/manual/cowboy_spdy.asciidoc delete mode 100644 doc/src/manual/cowboy_spdy.ezdoc create mode 100644 doc/src/manual/cowboy_static.asciidoc delete mode 100644 doc/src/manual/cowboy_static.ezdoc create mode 100644 doc/src/manual/cowboy_sub_protocol.asciidoc delete mode 100644 doc/src/manual/cowboy_sub_protocol.ezdoc create mode 100644 doc/src/manual/cowboy_websocket.asciidoc delete mode 100644 doc/src/manual/cowboy_websocket.ezdoc create mode 100644 doc/src/manual/http_status_codes.asciidoc delete mode 100644 doc/src/manual/http_status_codes.ezdoc delete mode 100644 doc/src/manual/index.ezdoc diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..9ad9fa0 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +erlang.mk -diff diff --git a/.gitignore b/.gitignore index c5924cf..337239e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,9 +3,10 @@ _rel cowboy.d deps +doc/guide.pdf +doc/html doc/man3 doc/man7 -doc/markdown ebin/*.beam ebin/test examples/*/ebin diff --git a/Makefile b/Makefile index 7552a03..19eccce 100644 --- a/Makefile +++ b/Makefile @@ -45,55 +45,3 @@ app:: rebar.config ci-setup:: clean deps test-deps $(gen_verbose) cp ~/.kerl/builds/$(CI_OTP_RELEASE)/otp_src_git/lib/ssl/test/erl_make_certs.erl deps/ct_helper/src/ - -# Documentation. - -dep_ezdoc = git https://github.com/ninenines/ezdoc master -$(eval $(call dep_target,ezdoc)) - -build-doc-deps: $(DEPS_DIR)/ezdoc - $(MAKE) -C $(DEPS_DIR)/ezdoc - -define ezdoc_script -io:format("Building manual~n"), -[begin - AST = ezdoc:parse_file(F), - BF = filename:rootname(filename:basename(F)), - io:format(" ~s~n", [BF]), - file:write_file("doc/markdown/manual/" ++ BF ++ ".md", ezdoc_markdown:export(AST)), - case BF of - "cowboy" ++ _ when BF =/= "cowboy_app" -> - file:write_file("doc/man3/" ++ BF ++ ".3", ezdoc_man:export(3, AST)); - _ when BF =/= "index" -> - file:write_file("doc/man7/" ++ BF ++ ".7", ezdoc_man:export(7, AST)); - _ -> - ok - end -end || F <- filelib:wildcard("doc/src/manual/*.ezdoc")], -io:format("Building guide~n"), -[begin - AST = ezdoc:parse_file(F), - BF = filename:rootname(filename:basename(F)), - io:format(" ~s~n", [BF]), - file:write_file("doc/markdown/guide/" ++ BF ++ ".md", ezdoc_markdown:export(AST)) -end || F <- filelib:wildcard("doc/src/guide/*.ezdoc")], -io:format("Done.~n"), -init:stop(). -endef -export ezdoc_script - -docs:: clean-docs build-doc-deps - @mkdir -p doc/man3 doc/man7 doc/markdown/guide doc/markdown/manual - $(gen_verbose) erl -noinput -pa ebin deps/ezdoc/ebin -eval "$$ezdoc_script" - @gzip doc/man3/*.3 doc/man7/*.7 - @cp doc/src/guide/*.png doc/markdown/guide - -clean-docs: - $(gen_verbose) rm -rf doc/man3 doc/man7 doc/markdown - -MAN_INSTALL_PATH ?= /usr/local/share/man - -install-docs: - mkdir -p $(MAN_INSTALL_PATH)/man3/ $(MAN_INSTALL_PATH)/man7/ - install -g 0 -o 0 -m 0644 doc/man3/*.gz $(MAN_INSTALL_PATH)/man3/ - install -g 0 -o 0 -m 0644 doc/man7/*.gz $(MAN_INSTALL_PATH)/man7/ diff --git a/build.config b/build.config deleted file mode 100644 index 6cdfbb4..0000000 --- a/build.config +++ /dev/null @@ -1,36 +0,0 @@ -# Do *not* comment or remove core modules -# unless you know what you are doing. -# -# Feel free to comment plugins out however. - -# Core modules. -core/core -core/deps - -# Plugins that must run before Erlang code gets compiled. -plugins/protobuffs - -# Core modules, continued. -core/erlc -core/docs -core/test - -# Plugins. -plugins/asciidoc -plugins/bootstrap -plugins/c_src -plugins/ci -plugins/ct -plugins/dialyzer -#plugins/edoc -plugins/elvis -plugins/erlydtl -plugins/escript -plugins/eunit -plugins/relx -plugins/shell -plugins/triq -plugins/xref - -# Plugins enhancing the functionality of other plugins. -plugins/cover diff --git a/doc/src/guide/architecture.asciidoc b/doc/src/guide/architecture.asciidoc new file mode 100644 index 0000000..416ef36 --- /dev/null +++ b/doc/src/guide/architecture.asciidoc @@ -0,0 +1,48 @@ +[[architecture]] +== Architecture + +Cowboy is a lightweight HTTP server. + +It is built on top of Ranch. Please see the Ranch guide for more +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. + +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. + +Because of this, you are expected to make sure your process cleans +up before terminating the handling of the current request. This may +include cleaning up the process dictionary, timers, monitoring and +more. + +=== Binaries + +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. + +=== Date header + +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. + +=== 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 +too many processes performing potentially heavy work and slowing +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. diff --git a/doc/src/guide/architecture.ezdoc b/doc/src/guide/architecture.ezdoc deleted file mode 100644 index 745505a..0000000 --- a/doc/src/guide/architecture.ezdoc +++ /dev/null @@ -1,47 +0,0 @@ -::: Architecture - -Cowboy is a lightweight HTTP server. - -It is built on top of Ranch. Please see the Ranch guide for more -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. - -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. - -Because of this, you are expected to make sure your process cleans -up before terminating the handling of the current request. This may -include cleaning up the process dictionary, timers, monitoring and -more. - -:: Binaries - -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. - -:: Date header - -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. - -:: 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 -too many processes performing potentially heavy work and slowing -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. diff --git a/doc/src/guide/book.asciidoc b/doc/src/guide/book.asciidoc new file mode 100644 index 0000000..15aa42b --- /dev/null +++ b/doc/src/guide/book.asciidoc @@ -0,0 +1,72 @@ +// a2x: --dblatex-opts "-P latex.output.revhistory=0 -P doc.publisher.show=0 -P index.numbered=0" +// a2x: -d book --attribute tabsize=4 + += Cowboy User Guide + += Rationale + +include::modern_web.asciidoc[The modern Web] + +include::erlang_web.asciidoc[Erlang and the Web] + += Introduction + +include::introduction.asciidoc[Introduction] + +include::getting_started.asciidoc[Getting started] + +include::overview.asciidoc[Request overview] + +include::erlang_beginners.asciidoc[Erlang for beginners] + += Configuration + +include::routing.asciidoc[routing] + +include::constraints.asciidoc[Constraints] + +include::static_files.asciidoc[Static files] + += Request and response + +include::handlers.asciidoc[Handlers] + +include::loop_handlers.asciidoc[Loop handlers] + +include::req.asciidoc[The Req object] + +include::req_body.asciidoc[Reading the request body] + +include::resp.asciidoc[Sending a response] + +include::cookies.asciidoc[Using cookies] + +include::multipart.asciidoc[Multipart] + += REST + +include::rest_principles.asciidoc[REST principles] + +include::rest_handlers.asciidoc[Handling REST requests] + +include::rest_flowcharts.asciidoc[REST flowcharts] + +include::resource_design.asciidoc[Designing a resource handler] + += Websocket + +include::ws_protocol.asciidoc[The Websocket protocol] + +include::ws_handlers.asciidoc[Handling Websocket connections] + += Internals + +include::architecture.asciidoc[Architecture] + +include::broken_clients.asciidoc[Dealing with broken clients] + +include::middlewares.asciidoc[Middlewares] + +include::sub_protocols.asciidoc[Sub protocols] + +include::hooks.asciidoc[Hooks] diff --git a/doc/src/guide/broken_clients.asciidoc b/doc/src/guide/broken_clients.asciidoc new file mode 100644 index 0000000..e91e9a2 --- /dev/null +++ b/doc/src/guide/broken_clients.asciidoc @@ -0,0 +1,61 @@ +[[broken_clients]] +== 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 every 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. + +[source,erlang] +---- +capitalize_hook(Status, Headers, Body, Req) -> + Headers2 = [{cowboy_bstr:capitalize_token(N), V} + || {N, V} <- Headers], + cowboy_req:reply(Status, Headers2, Body, Req). +---- + +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. + +[source,erlang] +Req2 = cowboy_req:set(resp_state, waiting_stream). diff --git a/doc/src/guide/broken_clients.ezdoc b/doc/src/guide/broken_clients.ezdoc deleted file mode 100644 index c508358..0000000 --- a/doc/src/guide/broken_clients.ezdoc +++ /dev/null @@ -1,60 +0,0 @@ -::: 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 every 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], - cowboy_req:reply(Status, Headers2, Body, Req). -``` - -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). -``` diff --git a/doc/src/guide/constraints.asciidoc b/doc/src/guide/constraints.asciidoc new file mode 100644 index 0000000..0ae01d5 --- /dev/null +++ b/doc/src/guide/constraints.asciidoc @@ -0,0 +1,54 @@ +[[constraints]] +== Constraints + +Cowboy provides an optional constraints based validation feature +when interacting with user input. + +Constraints are first used during routing. The router uses +constraints to more accurately match bound values, allowing +to create routes where a segment is an integer for example, +and rejecting the others. + +Constraints are also used when performing a match operation +on input data, like the query string or cookies. There, a +default value can also be provided for optional values. + +Finally, constraints can be used to not only validate input, +but also convert said input into proper Erlang terms, all in +one step. + +=== Structure + +Constraints are provided as a list of fields and for each +field a list of constraints for that field can be provided. + +Fields are either the name of the field; the name and +one or more constraints; or the name, one or more constraints +and a default value. + +When no default value is provided then the field is required. +Otherwise the default value is used. + +All constraints for a field will be used to match its value +in the order they are given. If the value is modified by a +constraint, the next constraint receives the updated value. + +=== Built-in constraints + +[cols="<,<",options="header"] +|=== +| Constraint | Description +| int | Convert binary value to integer. +| nonempty | Ensures the binary value is non-empty. +|=== + +=== Custom constraint + +In addition to the predefined constraints, Cowboy will accept +a fun. This fun must accept one argument and return one of +`true`, `{true, NewValue}` or `false`. The result indicates +whether the value matches the constraint, and if it does it +can optionally be modified. This allows converting the value +to a more appropriate Erlang term. + +Note that constraint functions SHOULD be pure and MUST NOT crash. diff --git a/doc/src/guide/constraints.ezdoc b/doc/src/guide/constraints.ezdoc deleted file mode 100644 index a05f489..0000000 --- a/doc/src/guide/constraints.ezdoc +++ /dev/null @@ -1,51 +0,0 @@ -::: Constraints - -Cowboy provides an optional constraints based validation feature -when interacting with user input. - -Constraints are first used during routing. The router uses -constraints to more accurately match bound values, allowing -to create routes where a segment is an integer for example, -and rejecting the others. - -Constraints are also used when performing a match operation -on input data, like the query string or cookies. There, a -default value can also be provided for optional values. - -Finally, constraints can be used to not only validate input, -but also convert said input into proper Erlang terms, all in -one step. - -:: Structure - -Constraints are provided as a list of fields and for each -field a list of constraints for that field can be provided. - -Fields are either the name of the field; the name and -one or more constraints; or the name, one or more constraints -and a default value. - -When no default value is provided then the field is required. -Otherwise the default value is used. - -All constraints for a field will be used to match its value -in the order they are given. If the value is modified by a -constraint, the next constraint receives the updated value. - -:: Built-in constraints - -|| Constraint Description -| -| int Convert binary value to integer -| nonempty Ensures the binary value is non-empty - -:: Custom constraint - -In addition to the predefined constraints, Cowboy will accept -a fun. This fun must accept one argument and return one of -`true`, `{true, NewValue}` or `false`. The result indicates -whether the value matches the constraint, and if it does it -can optionally be modified. This allows converting the value -to a more appropriate Erlang term. - -Note that constraint functions SHOULD be pure and MUST NOT crash. diff --git a/doc/src/guide/cookies.asciidoc b/doc/src/guide/cookies.asciidoc new file mode 100644 index 0000000..6068db3 --- /dev/null +++ b/doc/src/guide/cookies.asciidoc @@ -0,0 +1,163 @@ +[[cookies]] +== Using cookies + +Cookies are a mechanism allowing applications to maintain +state on top of the stateless HTTP protocol. + +Cowboy provides facilities for handling cookies. It is highly +recommended to use them instead of writing your own, as the +implementation of cookies can vary greatly between clients. + +Cookies are stored client-side and sent with every subsequent +request that matches the domain and path for which they were +stored, including requests for static files. For this reason +they can incur a cost which must be taken in consideration. + +Also consider that, regardless of the options used, cookies +are not to be trusted. They may be read and modified by any +program on the user's computer, but also by proxies. You +should always validate cookie values before using them. Do +not store any sensitive information in cookies either. + +When explicitly setting the domain, the cookie will be sent +for the domain and all subdomains from that domain. Otherwise +the current domain will be used. The same is true for the +path. + +When the server sets cookies, they will only be available +for requests that are sent after the client receives the +response. + +Cookies are sent in HTTP headers, therefore they must have +text values. It is your responsibility to encode any other +data type. Also note that cookie names are de facto case +sensitive. + +Cookies can be set for the client session (which generally +means until the browser is closed), or it can be set for +a number of seconds. Once it expires, or when the server +says the cookie must exist for up to 0 seconds, the cookie +is deleted by the client. To avoid this while the user +is browsing your site, you should set the cookie for +every request, essentially resetting the expiration time. + +Cookies can be restricted to secure channels. This typically +means that such a cookie will only be sent over HTTPS, +and that it will only be available by client-side scripts +that run from HTTPS webpages. + +Finally, cookies can be restricted to HTTP and HTTPS requests, +essentially disabling their access from client-side scripts. + +=== Setting cookies + +By default, cookies you set are defined for the session. + +[source,erlang] +SessionID = generate_session_id(), +Req2 = cowboy_req:set_resp_cookie(<<"sessionid">>, SessionID, [], Req). + +You can also make them expire at a specific point in the +future. + +[source,erlang] +---- +SessionID = generate_session_id(), +Req2 = cowboy_req:set_resp_cookie(<<"sessionid">>, SessionID, [ + {max_age, 3600} +], Req). +---- + +You can delete cookies that have already been set. The value +is ignored. + +[source,erlang] +---- +Req2 = cowboy_req:set_resp_cookie(<<"sessionid">>, <<>>, [ + {max_age, 0} +], Req). +---- + +You can restrict them to a specific domain and path. +For example, the following cookie will be set for the domain +`my.example.org` and all its subdomains, but only on the path +`/account` and all its subdirectories. + +[source,erlang] +---- +Req2 = cowboy_req:set_resp_cookie(<<"inaccount">>, <<"1">>, [ + {domain, "my.example.org"}, + {path, "/account"} +], Req). +---- + +You can restrict the cookie to secure channels, typically HTTPS. + +[source,erlang] +---- +SessionID = generate_session_id(), +Req2 = cowboy_req:set_resp_cookie(<<"sessionid">>, SessionID, [ + {secure, true} +], Req). +---- + +You can restrict the cookie to client-server communication +only. Such a cookie will not be available to client-side scripts. + +[source,erlang] +---- +SessionID = generate_session_id(), +Req2 = cowboy_req:set_resp_cookie(<<"sessionid">>, SessionID, [ + {http_only, true} +], Req). +---- + +Cookies may also be set client-side, for example using +Javascript. + +=== Reading cookies + +As we said, the client sends cookies with every request. +But unlike the server, the client only sends the cookie +name and value. + +Cowboy provides two different ways to read cookies. You +can either parse them as a list of key/value pairs, or +match them into a map, optionally applying constraints +to the values or providing a default if they are missing. + +You can parse the cookies and then use standard library +functions to access individual values. + +[source,erlang] +Cookies = cowboy_req:parse_cookies(Req), +{_, Lang} = lists:keyfind(<<"lang">>, 1, Cookies). + +You can match the cookies into a map. + +[source,erlang] +#{id := ID, lang := Lang} = cowboy_req:match_cookies([id, lang], Req). + +You can use constraints to validate the values while matching +them. The following snippet will crash if the `id` cookie is +not an integer number or if the `lang` cookie is empty. Additionally +the `id` cookie value will be converted to an integer term, saving +you a conversion step. + +[source,erlang] +CookiesMap = cowboy_req:match_cookies([{id, int}, {lang, nonempty}], Req). + +Note that if two cookies share the same name, then the map value +will be a list of the two cookie values. + +Read more about xref:constraints[constraints]. + +A default value can be provided. The default will be used +if the `lang` cookie is not found. It will not be used if +the cookie is found but has an empty value. + +[source,erlang] +#{lang := Lang} = cowboy_req:match_cookies([{lang, [], <<"en-US">>}], Req). + +If no default is provided and the value is missing, the +query string is deemed invalid and the process will crash. diff --git a/doc/src/guide/cookies.ezdoc b/doc/src/guide/cookies.ezdoc deleted file mode 100644 index e6d1aeb..0000000 --- a/doc/src/guide/cookies.ezdoc +++ /dev/null @@ -1,162 +0,0 @@ -::: Using cookies - -Cookies are a mechanism allowing applications to maintain -state on top of the stateless HTTP protocol. - -Cowboy provides facilities for handling cookies. It is highly -recommended to use them instead of writing your own, as the -implementation of cookies can vary greatly between clients. - -Cookies are stored client-side and sent with every subsequent -request that matches the domain and path for which they were -stored, including requests for static files. For this reason -they can incur a cost which must be taken in consideration. - -Also consider that, regardless of the options used, cookies -are not to be trusted. They may be read and modified by any -program on the user's computer, but also by proxies. You -should always validate cookie values before using them. Do -not store any sensitive information in cookies either. - -When explicitly setting the domain, the cookie will be sent -for the domain and all subdomains from that domain. Otherwise -the current domain will be used. The same is true for the -path. - -When the server sets cookies, they will only be available -for requests that are sent after the client receives the -response. - -Cookies are sent in HTTP headers, therefore they must have -text values. It is your responsibility to encode any other -data type. Also note that cookie names are de facto case -sensitive. - -Cookies can be set for the client session (which generally -means until the browser is closed), or it can be set for -a number of seconds. Once it expires, or when the server -says the cookie must exist for up to 0 seconds, the cookie -is deleted by the client. To avoid this while the user -is browsing your site, you should set the cookie for -every request, essentially resetting the expiration time. - -Cookies can be restricted to secure channels. This typically -means that such a cookie will only be sent over HTTPS, -and that it will only be available by client-side scripts -that run from HTTPS webpages. - -Finally, cookies can be restricted to HTTP and HTTPS requests, -essentially disabling their access from client-side scripts. - -:: Setting cookies - -By default, cookies you set are defined for the session. - -``` erlang -SessionID = generate_session_id(), -Req2 = cowboy_req:set_resp_cookie(<<"sessionid">>, SessionID, [], Req). -``` - -You can also make them expire at a specific point in the -future. - -``` erlang -SessionID = generate_session_id(), -Req2 = cowboy_req:set_resp_cookie(<<"sessionid">>, SessionID, [ - {max_age, 3600} -], Req). -``` - -You can delete cookies that have already been set. The value -is ignored. - -``` erlang -Req2 = cowboy_req:set_resp_cookie(<<"sessionid">>, <<>>, [ - {max_age, 0} -], Req). -``` - -You can restrict them to a specific domain and path. -For example, the following cookie will be set for the domain -`my.example.org` and all its subdomains, but only on the path -`/account` and all its subdirectories. - -``` erlang -Req2 = cowboy_req:set_resp_cookie(<<"inaccount">>, <<"1">>, [ - {domain, "my.example.org"}, - {path, "/account"} -], Req). -``` - -You can restrict the cookie to secure channels, typically HTTPS. - -``` erlang -SessionID = generate_session_id(), -Req2 = cowboy_req:set_resp_cookie(<<"sessionid">>, SessionID, [ - {secure, true} -], Req). -``` - -You can restrict the cookie to client-server communication -only. Such a cookie will not be available to client-side scripts. - -``` erlang -SessionID = generate_session_id(), -Req2 = cowboy_req:set_resp_cookie(<<"sessionid">>, SessionID, [ - {http_only, true} -], Req). -``` - -Cookies may also be set client-side, for example using -Javascript. - -:: Reading cookies - -As we said, the client sends cookies with every request. -But unlike the server, the client only sends the cookie -name and value. - -Cowboy provides two different ways to read cookies. You -can either parse them as a list of key/value pairs, or -match them into a map, optionally applying constraints -to the values or providing a default if they are missing. - -You can parse the cookies and then use standard library -functions to access individual values. - -``` erlang -Cookies = cowboy_req:parse_cookies(Req), -{_, Lang} = lists:keyfind(<<"lang">>, 1, Cookies). -``` - -You can match the cookies into a map. - -``` erlang -#{id := ID, lang := Lang} = cowboy_req:match_cookies([id, lang], Req). -``` - -You can use constraints to validate the values while matching -them. The following snippet will crash if the `id` cookie is -not an integer number or if the `lang` cookie is empty. Additionally -the `id` cookie value will be converted to an integer term, saving -you a conversion step. - -``` erlang -CookiesMap = cowboy_req:match_cookies([{id, int}, {lang, nonempty}], Req). -``` - -Note that if two cookies share the same name, then the map value -will be a list of the two cookie values. - -Read more about ^constraints^. - -A default value can be provided. The default will be used -if the `lang` cookie is not found. It will not be used if -the cookie is found but has an empty value. - -``` erlang -#{lang := Lang} = cowboy_req:match_cookies([{lang, [], <<"en-US">>}], Req). -``` - -If no default is provided and the value is missing, the -query string is deemed invalid and the process will crash. diff --git a/doc/src/guide/erlang_beginners.asciidoc b/doc/src/guide/erlang_beginners.asciidoc new file mode 100644 index 0000000..b9a6c65 --- /dev/null +++ b/doc/src/guide/erlang_beginners.asciidoc @@ -0,0 +1,36 @@ +[[erlang_beginners]] +== Erlang for beginners + +Chances are you are interested in using Cowboy, but have +no idea how to write an Erlang program. Fear not! This +chapter will help you get started. + +We recommend two books for beginners. You should read them +both at some point, as they cover Erlang from two entirely +different perspectives. + +=== Learn You Some Erlang for Great Good! + +The quickest way to get started with Erlang is by reading +a book with the funny name of http://learnyousomeerlang.com[LYSE], +as we affectionately call it. + +It will get right into the syntax and quickly answer the questions +a beginner would ask themselves, all the while showing funny +pictures and making insightful jokes. + +You can read an early version of the book online for free, +but you really should buy the much more refined paper and +ebook versions. + +=== Programming Erlang + +After writing some code, you will probably want to understand +the very concepts that make Erlang what it is today. These +are best explained by Joe Armstrong, the godfather of Erlang, +in his book http://pragprog.com/book/jaerlang2/programming-erlang[Programming Erlang]. + +Instead of going into every single details of the language, +Joe focuses on the central concepts behind Erlang, and shows +you how they can be used to write a variety of different +applications. diff --git a/doc/src/guide/erlang_beginners.ezdoc b/doc/src/guide/erlang_beginners.ezdoc deleted file mode 100644 index 74d3470..0000000 --- a/doc/src/guide/erlang_beginners.ezdoc +++ /dev/null @@ -1,35 +0,0 @@ -::: Erlang for beginners - -Chances are you are interested in using Cowboy, but have -no idea how to write an Erlang program. Fear not! This -chapter will help you get started. - -We recommend two books for beginners. You should read them -both at some point, as they cover Erlang from two entirely -different perspectives. - -:: Learn You Some Erlang for Great Good! - -The quickest way to get started with Erlang is by reading -a book with the funny name of ^"LYSE^http://learnyousomeerlang.com^, -as we affectionately call it. - -It will get right into the syntax and quickly answer the questions -a beginner would ask themselves, all the while showing funny -pictures and making insightful jokes. - -You can read an early version of the book online for free, -but you really should buy the much more refined paper and -ebook versions. - -:: Programming Erlang - -After writing some code, you will probably want to understand -the very concepts that make Erlang what it is today. These -are best explained by Joe Armstrong, the godfather of Erlang, -in his book ^"Programming Erlang^http://pragprog.com/book/jaerlang2/programming-erlang^. - -Instead of going into every single details of the language, -Joe focuses on the central concepts behind Erlang, and shows -you how they can be used to write a variety of different -applications. diff --git a/doc/src/guide/erlang_web.asciidoc b/doc/src/guide/erlang_web.asciidoc new file mode 100644 index 0000000..91a9eca --- /dev/null +++ b/doc/src/guide/erlang_web.asciidoc @@ -0,0 +1,176 @@ +[[erlang_web]] +== Erlang and the Web + +=== The Web is concurrent + +When you access a website there is little concurrency +involved. A few connections are opened and requests +are sent through these connections. Then the web page +is displayed on your screen. Your browser will only +open up to 4 or 8 connections to the server, depending +on your settings. This isn't much. + +But think about it. You are not the only one accessing +the server at the same time. There can be hundreds, if +not thousands, if not millions of connections to the +same server at the same time. + +Even today a lot of systems used in production haven't +solved the C10K problem (ten thousand concurrent connections). +And the ones who did are trying hard to get to the next +step, C100K, and are pretty far from it. + +Erlang meanwhile has no problem handling millions of +connections. At the time of writing there are application +servers written in Erlang that can handle more than two +million connections on a single server in a real production +application, with spare memory and CPU! + +The Web is concurrent, and Erlang is a language designed +for concurrency, so it is a perfect match. + +Of course, various platforms need to scale beyond a few +million connections. This is where Erlang's built-in +distribution mechanisms come in. If one server isn't +enough, add more! Erlang allows you to use the same code +for talking to local processes or to processes in other +parts of your cluster, which means you can scale very +quickly if the need arises. + +The Web has large userbases, and the Erlang platform was +designed to work in a distributed setting, so it is a +perfect match. + +Or is it? Surely you can find solutions to handle that many +concurrent connections with your favorite language... But all +these solutions will break down in the next few years. Why? +Firstly because servers don't get any more powerful, they +instead get a lot more cores and memory. This is only useful +if your application can use them properly, and Erlang is +light-years away from anything else in that area. Secondly, +today your computer and your phone are online, tomorrow your +watch, goggles, bike, car, fridge and tons of other devices +will also connect to various applications on the Internet. + +Only Erlang is prepared to deal with what's coming. + +=== The Web is soft real time + +What does soft real time mean, you ask? It means we want the +operations done as quickly as possible, and in the case of +web applications, it means we want the data propagated fast. + +In comparison, hard real time has a similar meaning, but also +has a hard time constraint, for example an operation needs to +be done in under N milliseconds otherwise the system fails +entirely. + +Users aren't that needy yet, they just want to get access +to their content in a reasonable delay, and they want the +actions they make to register at most a few seconds after +they submitted them, otherwise they'll start worrying about +whether it successfully went through. + +The Web is soft real time because taking longer to perform an +operation would be seen as bad quality of service. + +Erlang is a soft real time system. It will always run +processes fairly, a little at a time, switching to another +process after a while and preventing a single process to +steal resources from all others. This means that Erlang +can guarantee stable low latency of operations. + +Erlang provides the guarantees that the soft real time Web +requires. + +=== The Web is asynchronous + +Long ago, the Web was synchronous because HTTP was synchronous. +You fired a request, and then waited for a response. Not anymore. +It all began when XmlHttpRequest started being used. It allowed +the client to perform asynchronous calls to the server. + +Then Websocket appeared and allowed both the server and the client +to send data to the other endpoint completely asynchronously. The +data is contained within frames and no response is necessary. + +Erlang processes work the same. They send each other data contained +within messages and then continue running without needing a response. +They tend to spend most of their time inactive, waiting for a new +message, and the Erlang VM happily activate them when one is received. + +It is therefore quite easy to imagine Erlang being good at receiving +Websocket frames, which may come in at unpredictable times, pass the +data to the responsible processes which are always ready waiting for +new messages, and perform the operations required by only activating +the required parts of the system. + +The more recent Web technologies, like Websocket of course, but also +SPDY and HTTP/2.0, are all fully asynchronous protocols. The concept +of requests and responses is retained of course, but anything could +be sent in between, by both the client or the browser, and the +responses could also be received in a completely different order. + +Erlang is by nature asynchronous and really good at it thanks to the +great engineering that has been done in the VM over the years. It's +only natural that it's so good at dealing with the asynchronous Web. + +=== The Web is omnipresent + +The Web has taken a very important part of our lives. We're +connected at all times, when we're on our phone, using our computer, +passing time using a tablet while in the bathroom... And this +isn't going to slow down, every single device at home or on us +will be connected. + +All these devices are always connected. And with the number of +alternatives to give you access to the content you seek, users +tend to not stick around when problems arise. Users today want +their applications to be always available and if it's having +too many issues they just move on. + +Despite this, when developers choose a product to use for building +web applications, their only concern seem to be "Is it fast?", +and they look around for synthetic benchmarks showing which one +is the fastest at sending "Hello world" with only a handful +concurrent connections. Web benchmarks haven't been representative +of reality in a long time, and are drifting further away as +time goes on. + +What developers should really ask themselves is "Can I service +all my users with no interruption?" and they'd find that they have +two choices. They can either hope for the best, or they can use +Erlang. + +Erlang is built for fault tolerance. When writing code in any other +language, you have to check all the return values and act accordingly +to avoid any unforeseen issues. If you're lucky, you won't miss +anything important. When writing Erlang code, you can just check +the success condition and ignore all errors. If an error happen, +the Erlang process crashes and is then restarted by a special +process called a supervisor. + +The Erlang developer thus has no need to fear about unhandled +errors, and can focus on handling only the errors that should +give some feedback to the user and let the system take care of +the rest. This also has the advantage of allowing him to write +a lot less code, and letting him sleep at night. + +Erlang's fault tolerance oriented design is the first piece of +what makes it the best choice for the omnipresent, always available +Web. + +The second piece is Erlang's built-in distribution. Distribution +is a key part of building a fault tolerant system, because it +allows you to handle bigger failures, like a whole server going +down, or even a data center entirely. + +Fault tolerance and distribution are important today, and will be +vital in the future of the Web. Erlang is ready. + +=== Erlang is the ideal platform for the Web + +Erlang provides all the important features that the Web requires +or will require in the near future. Erlang is a perfect match +for the Web, and it only makes sense to use it to build web +applications. diff --git a/doc/src/guide/erlang_web.ezdoc b/doc/src/guide/erlang_web.ezdoc deleted file mode 100644 index 42fcd34..0000000 --- a/doc/src/guide/erlang_web.ezdoc +++ /dev/null @@ -1,175 +0,0 @@ -::: Erlang and the Web - -:: The Web is concurrent - -When you access a website there is little concurrency -involved. A few connections are opened and requests -are sent through these connections. Then the web page -is displayed on your screen. Your browser will only -open up to 4 or 8 connections to the server, depending -on your settings. This isn't much. - -But think about it. You are not the only one accessing -the server at the same time. There can be hundreds, if -not thousands, if not millions of connections to the -same server at the same time. - -Even today a lot of systems used in production haven't -solved the C10K problem (ten thousand concurrent connections). -And the ones who did are trying hard to get to the next -step, C100K, and are pretty far from it. - -Erlang meanwhile has no problem handling millions of -connections. At the time of writing there are application -servers written in Erlang that can handle more than two -million connections on a single server in a real production -application, with spare memory and CPU! - -The Web is concurrent, and Erlang is a language designed -for concurrency, so it is a perfect match. - -Of course, various platforms need to scale beyond a few -million connections. This is where Erlang's built-in -distribution mechanisms come in. If one server isn't -enough, add more! Erlang allows you to use the same code -for talking to local processes or to processes in other -parts of your cluster, which means you can scale very -quickly if the need arises. - -The Web has large userbases, and the Erlang platform was -designed to work in a distributed setting, so it is a -perfect match. - -Or is it? Surely you can find solutions to handle that many -concurrent connections with your favorite language... But all -these solutions will break down in the next few years. Why? -Firstly because servers don't get any more powerful, they -instead get a lot more cores and memory. This is only useful -if your application can use them properly, and Erlang is -light-years away from anything else in that area. Secondly, -today your computer and your phone are online, tomorrow your -watch, goggles, bike, car, fridge and tons of other devices -will also connect to various applications on the Internet. - -Only Erlang is prepared to deal with what's coming. - -:: The Web is soft real time - -What does soft real time mean, you ask? It means we want the -operations done as quickly as possible, and in the case of -web applications, it means we want the data propagated fast. - -In comparison, hard real time has a similar meaning, but also -has a hard time constraint, for example an operation needs to -be done in under N milliseconds otherwise the system fails -entirely. - -Users aren't that needy yet, they just want to get access -to their content in a reasonable delay, and they want the -actions they make to register at most a few seconds after -they submitted them, otherwise they'll start worrying about -whether it successfully went through. - -The Web is soft real time because taking longer to perform an -operation would be seen as bad quality of service. - -Erlang is a soft real time system. It will always run -processes fairly, a little at a time, switching to another -process after a while and preventing a single process to -steal resources from all others. This means that Erlang -can guarantee stable low latency of operations. - -Erlang provides the guarantees that the soft real time Web -requires. - -:: The Web is asynchronous - -Long ago, the Web was synchronous because HTTP was synchronous. -You fired a request, and then waited for a response. Not anymore. -It all began when XmlHttpRequest started being used. It allowed -the client to perform asynchronous calls to the server. - -Then Websocket appeared and allowed both the server and the client -to send data to the other endpoint completely asynchronously. The -data is contained within frames and no response is necessary. - -Erlang processes work the same. They send each other data contained -within messages and then continue running without needing a response. -They tend to spend most of their time inactive, waiting for a new -message, and the Erlang VM happily activate them when one is received. - -It is therefore quite easy to imagine Erlang being good at receiving -Websocket frames, which may come in at unpredictable times, pass the -data to the responsible processes which are always ready waiting for -new messages, and perform the operations required by only activating -the required parts of the system. - -The more recent Web technologies, like Websocket of course, but also -SPDY and HTTP/2.0, are all fully asynchronous protocols. The concept -of requests and responses is retained of course, but anything could -be sent in between, by both the client or the browser, and the -responses could also be received in a completely different order. - -Erlang is by nature asynchronous and really good at it thanks to the -great engineering that has been done in the VM over the years. It's -only natural that it's so good at dealing with the asynchronous Web. - -:: The Web is omnipresent - -The Web has taken a very important part of our lives. We're -connected at all times, when we're on our phone, using our computer, -passing time using a tablet while in the bathroom... And this -isn't going to slow down, every single device at home or on us -will be connected. - -All these devices are always connected. And with the number of -alternatives to give you access to the content you seek, users -tend to not stick around when problems arise. Users today want -their applications to be always available and if it's having -too many issues they just move on. - -Despite this, when developers choose a product to use for building -web applications, their only concern seem to be "Is it fast?", -and they look around for synthetic benchmarks showing which one -is the fastest at sending "Hello world" with only a handful -concurrent connections. Web benchmarks haven't been representative -of reality in a long time, and are drifting further away as -time goes on. - -What developers should really ask themselves is "Can I service -all my users with no interruption?" and they'd find that they have -two choices. They can either hope for the best, or they can use -Erlang. - -Erlang is built for fault tolerance. When writing code in any other -language, you have to check all the return values and act accordingly -to avoid any unforeseen issues. If you're lucky, you won't miss -anything important. When writing Erlang code, you can just check -the success condition and ignore all errors. If an error happen, -the Erlang process crashes and is then restarted by a special -process called a supervisor. - -The Erlang developer thus has no need to fear about unhandled -errors, and can focus on handling only the errors that should -give some feedback to the user and let the system take care of -the rest. This also has the advantage of allowing him to write -a lot less code, and letting him sleep at night. - -Erlang's fault tolerance oriented design is the first piece of -what makes it the best choice for the omnipresent, always available -Web. - -The second piece is Erlang's built-in distribution. Distribution -is a key part of building a fault tolerant system, because it -allows you to handle bigger failures, like a whole server going -down, or even a data center entirely. - -Fault tolerance and distribution are important today, and will be -vital in the future of the Web. Erlang is ready. - -:: Erlang is the ideal platform for the Web - -Erlang provides all the important features that the Web requires -or will require in the near future. Erlang is a perfect match -for the Web, and it only makes sense to use it to build web -applications. diff --git a/doc/src/guide/getting_started.asciidoc b/doc/src/guide/getting_started.asciidoc new file mode 100644 index 0000000..5d72f0a --- /dev/null +++ b/doc/src/guide/getting_started.asciidoc @@ -0,0 +1,136 @@ +[[getting_started]] +== Getting started + +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. + +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. + +=== Bootstrap + +We are going to use the https://github.com/ninenines/erlang.mk[Erlang.mk] +build system. It also offers bootstrap features allowing us to +quickly get started without having to deal with minute details. + +First, let's create the directory for our application. + +[source,bash] +$ mkdir hello_erlang +$ cd hello_erlang + +Then we need to download Erlang.mk. Either use the following +command or download it manually. + +[source,bash] +$ wget https://raw.githubusercontent.com/ninenines/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. + +[source,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. + +[source,bash] +---- +$ make run +... +(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 + +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: + +[source,make] +PROJECT = hello_erlang +DEPS = cowboy +include erlang.mk + +If you run `make run` now, Cowboy will be included in the release +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. + +=== Listening for connections + +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. + +Open the 'src/hello_erlang_app.erl' file and add the necessary +code to the `start/2` function to make it look like this: + +[source,erlang] +---- +start(_Type, _Args) -> + Dispatch = cowboy_router:compile([ + {'_', [{"/", hello_handler, []}]} + ]), + {ok, _} = cowboy:start_http(my_http_listener, 100, [{port, 8080}], + [{env, [{dispatch, Dispatch}]}] + ), + hello_erlang_sup:start_link(). +---- + +The dispatch list is explained in great details in the +xref: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. + +If you build and start the release, then open http://localhost:8080 +in your browser, you will get an error because the module is missing. +Any other URL, like http://localhost:8080/test, will result in a +404 error. + +=== Handling requests + +Cowboy features different kinds of handlers, including REST +and Websocket handlers. For this tutorial we will use a plain +HTTP handler. + +First, let's generate a handler from a template. + +[source,bash] +$ make new t=cowboy_http n=hello_handler + +You can then open the 'src/hello_handler.erl' file and modify +the `init/2` function like this to send a reply. + +[source,erlang] +---- +init(Req, Opts) -> + Req2 = cowboy_req:reply(200, + [{<<"content-type">>, <<"text/plain">>}], + <<"Hello Erlang!">>, + Req), + {ok, Req2, Opts}. +---- + +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!`. + +If you run the release and open http://localhost:8080 +in your browser, you should get a nice `Hello Erlang!` displayed! diff --git a/doc/src/guide/getting_started.ezdoc b/doc/src/guide/getting_started.ezdoc deleted file mode 100644 index deb7bf2..0000000 --- a/doc/src/guide/getting_started.ezdoc +++ /dev/null @@ -1,169 +0,0 @@ -::: Getting started - -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. - -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. - -:: Bootstrap - -We are going to use the ^"erlang.mk^https://github.com/ninenines/erlang.mk -build system. It also offers bootstrap features allowing us to -quickly get started without having to deal with minute details. - -First, let's create the directory for our application. - -``` bash -$ mkdir hello_erlang -$ cd hello_erlang -``` - -Then we need to download `erlang.mk`. Either use the following -command or download it manually. - -``` bash -$ wget https://raw.githubusercontent.com/ninenines/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 to your application, you 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 Erlang!"}, - {vsn, "0.1.0"}, - {modules, []}, - {registered, []}, - {applications, [ - kernel, - stdlib, - cowboy - ]}, - {mod, {hello_erlang_app, []}}, - {env, []} -]}. -``` - -You may want to set a description for the application while you -are editing the file. - -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. - -:: Listening for connections - -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. - -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([ - {'_', [{"/", hello_handler, []}]} - ]), - {ok, _} = cowboy:start_http(my_http_listener, 100, [{port, 8080}], - [{env, [{dispatch, Dispatch}]}] - ), - hello_erlang_sup:start_link(). -``` - -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. - -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. - -:: Handling requests - -Cowboy features different kinds of handlers, including REST -and Websocket handlers. For this tutorial we will use a plain -HTTP handler. - -First, let's generate a handler from a template. - -``` bash -$ make new t=cowboy_http n=hello_handler -``` - -You can then open the `src/hello_handler.erl` file and modify -the `init/2` function like this to send a reply. - -``` erlang -init(Req, Opts) -> - Req2 = cowboy_req:reply(200, - [{<<"content-type">>, <<"text/plain">>}], - <<"Hello Erlang!">>, - Req), - {ok, Req2, Opts}. -``` - -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!`. - -If you build the release, start it and open ^http://localhost:8080 -in your browser, you should get a nice `Hello Erlang!` displayed! diff --git a/doc/src/guide/handlers.asciidoc b/doc/src/guide/handlers.asciidoc new file mode 100644 index 0000000..b6cefdd --- /dev/null +++ b/doc/src/guide/handlers.asciidoc @@ -0,0 +1,105 @@ +[[handlers]] +== Handlers + +Handlers are Erlang modules that handle HTTP requests. + +=== Plain HTTP handlers + +The most basic handler in Cowboy implements the mandatory +`init/2` callback, manipulates the request, optionally +sends a response and then returns. + +This callback receives the xref:req[Req object] and the options +defined during the xref:routing[router configuration]. + +A handler that does nothing would look like this: + +[source,erlang] +---- +init(Req, _Opts) -> + {ok, Req, #state{}}. +---- + +Despite sending no reply, a `204 No Content` reply will be +sent to the client, as Cowboy makes sure that a reply is +sent for every request. + +We need to use the Req object for sending a reply. + +[source,erlang] +---- +init(Req, _Opts) -> + Req2 = cowboy_req:reply(200, [ + {<<"content-type">>, <<"text/plain">>} + ], <<"Hello World!">>, Req), + {ok, Req2, #state{}}. +---- + +As you can see we return a 3-tuple. `ok` means that the +handler ran successfully. The Req object is returned as +it may have been modified as is the case here: replying +returns a modified Req object that you need to return +back to Cowboy for proper operations. + +The last value of the tuple is a state that will be used +in every subsequent callbacks to this handler. Plain HTTP +handlers only have one additional callback, the optional +`terminate/3`. + +=== Other handlers + +The `init/2` callback can also be used to inform Cowboy +that this is a different kind of handler and that Cowboy +should switch to it. To do this you simply need to return +the module name of the handler type you want to switch to. + +Cowboy comes with three handler types you can switch to: +xref:rest_handlers[cowboy_rest], xref:ws_handlers[cowboy_websocket] +and xref:loop_handlers[cowboy_loop]. In addition to those you +can define your own handler types. + +Switching is simple. Instead of returning `ok`, you simply +return the name of the handler type you want to use. The +following snippet switches to a Websocket handler: + +[source,erlang] +---- +init(Req, _Opts) -> + {cowboy_websocket, Req, #state{}}. +---- + +You can also switch to your own custom handler type: + +[source,erlang] +---- +init(Req, _Opts) -> + {my_handler_type, Req, #state{}}. +---- + +How to implement a custom handler type is described in the +xref:sub_protocols[Sub protocols] chapter. + +=== Cleaning up + +All handlers coming with Cowboy allow the use of the optional +`terminate/3` callback. + +[source,erlang] +---- +terminate(_Reason, Req, State) -> + ok. +---- + +This callback is strictly reserved for any required cleanup. +You cannot send a response from this function. There is no +other return value. + +If you used the process dictionary, timers, monitors or may +be receiving messages, then you can use this function to clean +them up, as Cowboy might reuse the process for the next +keep-alive request. + +Note that while this function may be called in a Websocket +handler, it is generally not useful to do any clean up as +the process terminates immediately after calling this callback +when using Websocket. diff --git a/doc/src/guide/handlers.ezdoc b/doc/src/guide/handlers.ezdoc deleted file mode 100644 index 9336488..0000000 --- a/doc/src/guide/handlers.ezdoc +++ /dev/null @@ -1,99 +0,0 @@ -::: Handlers - -Handlers are Erlang modules that handle HTTP requests. - -:: Plain HTTP handlers - -The most basic handler in Cowboy implements the mandatory -`init/2` callback, manipulates the request, optionally -sends a response and then returns. - -This callback receives the ^"Req object^req and the options -defined during the ^"router configuration^routing^. - -A handler that does nothing would look like this: - -``` erlang -init(Req, _Opts) -> - {ok, Req, #state{}}. -``` - -Despite sending no reply, a `204 No Content` reply will be -sent to the client, as Cowboy makes sure that a reply is -sent for every request. - -We need to use the Req object for sending a reply. - -``` erlang -init(Req, _Opts) -> - Req2 = cowboy_req:reply(200, [ - {<<"content-type">>, <<"text/plain">>} - ], <<"Hello World!">>, Req), - {ok, Req2, #state{}}. -``` - -As you can see we return a 3-tuple. `ok` means that the -handler ran successfully. The Req object is returned as -it may have been modified as is the case here: replying -returns a modified Req object that you need to return -back to Cowboy for proper operations. - -The last value of the tuple is a state that will be used -in every subsequent callbacks to this handler. Plain HTTP -handlers only have one additional callback, the optional -`terminate/3`. - -:: Other handlers - -The `init/2` callback can also be used to inform Cowboy -that this is a different kind of handler and that Cowboy -should switch to it. To do this you simply need to return -the module name of the handler type you want to switch to. - -Cowboy comes with three handler types you can switch to: -^"cowboy_rest^rest_handlers^, ^"cowboy_websocket^ws_handlers^ -and ^"cowboy_loop^loop_handlers^. In addition to those you -can define your own handler types. - -Switching is simple. Instead of returning `ok`, you simply -return the name of the handler type you want to use. The -following snippet switches to a Websocket handler: - -``` erlang -init(Req, _Opts) -> - {cowboy_websocket, Req, #state{}}. -``` - -You can also switch to your own custom handler type: - -``` erlang -init(Req, _Opts) -> - {my_handler_type, Req, #state{}}. -``` - -How to implement a custom handler type is described in the -^"Sub protocols^sub_protocols chapter. - -:: Cleaning up - -All handlers coming with Cowboy allow the use of the optional -`terminate/3` callback. - -``` erlang -terminate(_Reason, Req, State) -> - ok. -``` - -This callback is strictly reserved for any required cleanup. -You cannot send a response from this function. There is no -other return value. - -If you used the process dictionary, timers, monitors or may -be receiving messages, then you can use this function to clean -them up, as Cowboy might reuse the process for the next -keep-alive request. - -Note that while this function may be called in a Websocket -handler, it is generally not useful to do any clean up as -the process terminates immediately after calling this callback -when using Websocket. diff --git a/doc/src/guide/hooks.asciidoc b/doc/src/guide/hooks.asciidoc new file mode 100644 index 0000000..fc79f8a --- /dev/null +++ b/doc/src/guide/hooks.asciidoc @@ -0,0 +1,46 @@ +[[hooks]] +== Hooks + +Hooks allow the user to customize Cowboy's behavior during specific +operations. + +=== Onresponse + +The `onresponse` hook is called right before sending the response +to the socket. It can be used for the purposes of logging responses, +or for modifying the response headers or body. The best example is +providing custom error pages. + +Note that 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. + +[source,erlang] +---- +cowboy:start_http(my_http_listener, 100, + [{port, 8080}], + [ + {env, [{dispatch, Dispatch}]}, + {onresponse, fun ?MODULE:custom_404_hook/4} + ] +). +---- + +The following hook function will provide a custom body for 404 errors +when it has not been provided before, and will let Cowboy proceed with +the default response otherwise. + +[source,erlang] +---- +custom_404_hook(404, Headers, <<>>, Req) -> + Body = <<"404 Not Found.">>, + Headers2 = lists:keyreplace(<<"content-length">>, 1, Headers, + {<<"content-length">>, integer_to_list(byte_size(Body))}), + cowboy_req:reply(404, Headers2, Body, Req); +custom_404_hook(_, _, _, Req) -> + Req. +---- + +Again, make sure to always return the last request object obtained. diff --git a/doc/src/guide/hooks.ezdoc b/doc/src/guide/hooks.ezdoc deleted file mode 100644 index 1c19648..0000000 --- a/doc/src/guide/hooks.ezdoc +++ /dev/null @@ -1,43 +0,0 @@ -::: Hooks - -Hooks allow the user to customize Cowboy's behavior during specific -operations. - -:: Onresponse - -The `onresponse` hook is called right before sending the response -to the socket. It can be used for the purposes of logging responses, -or for modifying the response headers or body. The best example is -providing custom error pages. - -Note that 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. - -``` erlang -cowboy:start_http(my_http_listener, 100, - [{port, 8080}], - [ - {env, [{dispatch, Dispatch}]}, - {onresponse, fun ?MODULE:custom_404_hook/4} - ] -). -``` - -The following hook function will provide a custom body for 404 errors -when it has not been provided before, and will let Cowboy proceed with -the default response otherwise. - -``` erlang -custom_404_hook(404, Headers, <<>>, Req) -> - Body = <<"404 Not Found.">>, - Headers2 = lists:keyreplace(<<"content-length">>, 1, Headers, - {<<"content-length">>, integer_to_list(byte_size(Body))}), - cowboy_req:reply(404, Headers2, Body, Req); -custom_404_hook(_, _, _, Req) -> - Req. -``` - -Again, make sure to always return the last request object obtained. diff --git a/doc/src/guide/index.ezdoc b/doc/src/guide/index.ezdoc deleted file mode 100644 index dbe75ff..0000000 --- a/doc/src/guide/index.ezdoc +++ /dev/null @@ -1,58 +0,0 @@ -::: Cowboy User Guide - -The Cowboy User Guide explores the modern Web and how to make -best use of Cowboy for writing powerful Web applications. - -:: Rationale - -* ^"The modern Web^modern_web -* ^"Erlang and the Web^erlang_web - -:: Introduction - -* ^"Introduction^introduction -* ^"Getting started^getting_started -* ^"Request overview^overview -* ^"Erlang for beginners^erlang_beginners - -:: Configuration - -* ^"Routing^routing -* ^"Constraints^constraints -* ^"Static files^static_files - -:: Request and response - -* ^"Handlers^handlers -* ^"The Req object^req -* ^"Reading the request body^req_body -* ^"Sending a response^resp -* ^"Using cookies^cookies -* ^"Multipart^multipart - -:: REST - -* ^"REST principles^rest_principles -* ^"Handling REST requests^rest_handlers -* ^"REST flowcharts^rest_flowcharts -* ^"Designing a resource handler^resource_design - -:: Websocket - -* ^"The Websocket protocol^ws_protocol -* ^"Handling Websocket connections^ws_handlers - -:: Push technology - -* ^"Loop handlers^loop_handlers - -:: Extensions - -* ^"Middlewares^middlewares -* ^"Sub protocols^sub_protocols -* ^"Hooks^hooks - -:: Internals - -* ^"Architecture^architecture -* ^"Dealing with broken clients^broken_clients diff --git a/doc/src/guide/introduction.asciidoc b/doc/src/guide/introduction.asciidoc new file mode 100644 index 0000000..0ffeb91 --- /dev/null +++ b/doc/src/guide/introduction.asciidoc @@ -0,0 +1,56 @@ +[[introduction]] +== Introduction + +Cowboy is a small, fast and modular HTTP server written in Erlang. + +Cowboy aims to provide a complete HTTP stack, including its derivatives +SPDY, Websocket and REST. Cowboy currently supports HTTP/1.0, HTTP/1.1, +Websocket (all implemented drafts + standard) and Webmachine-based REST. + +Cowboy is a high quality project. It has a small code base, is very +efficient (both in latency and memory use) and can easily be embedded +in another application. + +Cowboy is clean Erlang code. It includes hundreds of tests and its code +is fully compliant with the Dialyzer. It is also well documented and +features both a Function Reference and a User Guide. + +=== Prerequisites + +Beginner Erlang knowledge is recommended for reading this guide. + +Knowledge of the HTTP protocol is recommended but not required, as it +will be detailed throughout the guide. + +=== Supported platforms + +Cowboy is tested and supported on Linux. + +Cowboy has been reported to work on other platforms, but we make no +guarantee that the experience will be safe and smooth. You are advised +to perform the necessary testing and security audits prior to deploying +on other platforms. + +Cowboy is developed for Erlang/OTP 17.0, 17.1.2 and 17.3. By the time +this branch gets released the target version will probably be 18.0 and +above. + +Cowboy may be compiled on other Erlang versions with small source code +modifications but there is no guarantee that it will work as expected. + +Cowboy uses the maps data type which was introduced in Erlang 17.0. + +=== Versioning + +Cowboy uses http://semver.org/[Semantic Versioning 2.0.0]. + +=== Conventions + +In the HTTP protocol, the method name is case sensitive. All standard +method names are uppercase. + +Header names are case insensitive. Cowboy converts all the request +header names to lowercase, and expects your application to provide +lowercase header names in the response. + +The same applies to any other case insensitive value. diff --git a/doc/src/guide/introduction.ezdoc b/doc/src/guide/introduction.ezdoc deleted file mode 100644 index e1d2e60..0000000 --- a/doc/src/guide/introduction.ezdoc +++ /dev/null @@ -1,55 +0,0 @@ -::: Introduction - -Cowboy is a small, fast and modular HTTP server written in Erlang. - -Cowboy aims to provide a complete HTTP stack, including its derivatives -SPDY, Websocket and REST. Cowboy currently supports HTTP/1.0, HTTP/1.1, -Websocket (all implemented drafts + standard) and Webmachine-based REST. - -Cowboy is a high quality project. It has a small code base, is very -efficient (both in latency and memory use) and can easily be embedded -in another application. - -Cowboy is clean Erlang code. It includes hundreds of tests and its code -is fully compliant with the Dialyzer. It is also well documented and -features both a Function Reference and a User Guide. - -:: Prerequisites - -Beginner Erlang knowledge is recommended for reading this guide. - -Knowledge of the HTTP protocol is recommended but not required, as it -will be detailed throughout the guide. - -:: Supported platforms - -Cowboy is tested and supported on Linux. - -Cowboy has been reported to work on other platforms, but we make no -guarantee that the experience will be safe and smooth. You are advised -to perform the necessary testing and security audits prior to deploying -on other platforms. - -Cowboy is developed for Erlang/OTP 17.0, 17.1.2 and 17.3. By the time -this branch gets released the target version will probably be 18.0 and -above. - -Cowboy may be compiled on other Erlang versions with small source code -modifications but there is no guarantee that it will work as expected. - -Cowboy uses the maps data type which was introduced in Erlang 17.0. - -:: Versioning - -Cowboy uses ^"Semantic Versioning 2.0.0^http://semver.org/^. - -:: Conventions - -In the HTTP protocol, the method name is case sensitive. All standard -method names are uppercase. - -Header names are case insensitive. Cowboy converts all the request -header names to lowercase, and expects your application to provide -lowercase header names in the response. - -The same applies to any other case insensitive value. diff --git a/doc/src/guide/loop_handlers.asciidoc b/doc/src/guide/loop_handlers.asciidoc new file mode 100644 index 0000000..58c4223 --- /dev/null +++ b/doc/src/guide/loop_handlers.asciidoc @@ -0,0 +1,146 @@ +[[loop_handlers]] +== Loop handlers + +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. + +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 +most known example of such practice is known as long polling. + +Loop handlers can also be used for requests where a response is +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/2` and `terminate/3` callbacks which work the same as +for plain HTTP handlers. + +=== Initialization + +The `init/2` function must return a `cowboy_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. + +[source,erlang] +---- +init(Req, _Opts) -> + {cowboy_loop, Req, #state{}}. +---- + +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. + +[source,erlang] +---- +init(Req, _Opts) -> + {cowboy_loop, Req, #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. + +[source,erlang] +---- +info({reply, Body}, Req, State) -> + Req2 = cowboy_req:reply(200, [], Body, Req), + {stop, Req2, State}; +info(_Msg, Req, State) -> + {ok, Req, State, hibernate}. +---- + +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 `stop` tuple should be returned. +This will instruct Cowboy to end the request. + +Otherwise an `ok` 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/2` 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. + +[source,erlang] +---- +init(Req, _Opts) -> + Req2 = cowboy_req:chunked_reply(200, [], Req), + {cowboy_loop, Req2, #state{}}. + +info(eof, Req, State) -> + {stop, Req, State}; +info({chunk, Chunk}, Req, State) -> + cowboy_req:chunk(Chunk, Req), + {ok, Req, State}; +info(_Msg, Req, State) -> + {ok, 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 xref:handlers[Handlers chapter] +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. diff --git a/doc/src/guide/loop_handlers.ezdoc b/doc/src/guide/loop_handlers.ezdoc deleted file mode 100644 index 47893a9..0000000 --- a/doc/src/guide/loop_handlers.ezdoc +++ /dev/null @@ -1,141 +0,0 @@ -::: Loop handlers - -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. - -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 -most known example of such practice is known as long polling. - -Loop handlers can also be used for requests where a response is -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/2` and `terminate/3` callbacks which work the same as -for plain HTTP handlers. - -:: Initialization - -The `init/2` function must return a `cowboy_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 -init(Req, _Opts) -> - {cowboy_loop, Req, #state{}}. -``` - -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. - -``` erlang -init(Req, _Opts) -> - {cowboy_loop, Req, #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) -> - Req2 = cowboy_req:reply(200, [], Body, Req), - {stop, Req2, State}; -info(_Msg, Req, State) -> - {ok, Req, State, hibernate}. -``` - -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 `stop` tuple should be returned. -This will instruct Cowboy to end the request. - -Otherwise an `ok` 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/2` 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(Req, _Opts) -> - Req2 = cowboy_req:chunked_reply(200, [], Req), - {cowboy_loop, Req2, #state{}}. - -info(eof, Req, State) -> - {stop, Req, State}; -info({chunk, Chunk}, Req, State) -> - cowboy_req:chunk(Chunk, Req), - {ok, Req, State}; -info(_Msg, Req, State) -> - {ok, 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 ^"Handlers chapter^handlers -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. diff --git a/doc/src/guide/middlewares.asciidoc b/doc/src/guide/middlewares.asciidoc new file mode 100644 index 0000000..e6be30d --- /dev/null +++ b/doc/src/guide/middlewares.asciidoc @@ -0,0 +1,69 @@ +[[middlewares]] +== Middlewares + +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. + +Middlewares give you complete control over how requests are to be +processed. You can add your own middlewares to the mix or completely +change the chain of middlewares as needed. + +Cowboy will execute all middlewares in the given order, unless one +of them decides to stop processing. + +=== Usage + +Middlewares only need to implement a single callback: `execute/2`. +It is defined in the `cowboy_middleware` behavior. + +This callback has two arguments. The first is the `Req` object. +The second is the environment. + +Middlewares can return one of three different values: + +* `{ok, Req, Env}` to continue the request processing +* `{suspend, Module, Function, Args}` to hibernate +* `{stop, Req}` to stop processing and move on to the next request + +Of note is that when hibernating, processing will resume on the given +MFA, discarding all previous stacktrace. Make sure you keep the `Req` +and `Env` in the arguments of this MFA for later use. + +If an error happens during middleware processing, Cowboy will not try +to send an error back to the socket, the process will just crash. It +is up to the middleware to make sure that a reply is sent if something +goes wrong. + +=== Configuration + +The middleware environment is defined as the `env` protocol option. +In the previous chapters we saw it briefly when we needed to pass +the routing information. It is a list of tuples with the first +element being an atom and the second any Erlang term. + +Two values in the environment are reserved: + +* `listener` contains the name of the listener +* `result` contains the result of the processing + +The `listener` value is always defined. The `result` value can be +set by any middleware. If set to anything other than `ok`, Cowboy +will not process any subsequent requests on this connection. + +The middlewares that come with Cowboy may define or require other +environment values to perform. + +You can update the environment by calling the `cowboy:set_env/3` +convenience function, adding or replacing a value in the environment. + +=== Routing middleware + +The routing middleware requires the `dispatch` value. If routing +succeeds, it will put the handler name and options in the `handler` +and `handler_opts` values of the environment, respectively. + +=== Handler middleware + +The handler middleware requires the `handler` and `handler_opts` +values. It puts the result of the request handling into `result`. diff --git a/doc/src/guide/middlewares.ezdoc b/doc/src/guide/middlewares.ezdoc deleted file mode 100644 index 8b047d7..0000000 --- a/doc/src/guide/middlewares.ezdoc +++ /dev/null @@ -1,68 +0,0 @@ -::: Middlewares - -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. - -Middlewares give you complete control over how requests are to be -processed. You can add your own middlewares to the mix or completely -change the chain of middlewares as needed. - -Cowboy will execute all middlewares in the given order, unless one -of them decides to stop processing. - -:: Usage - -Middlewares only need to implement a single callback: `execute/2`. -It is defined in the `cowboy_middleware` behavior. - -This callback has two arguments. The first is the `Req` object. -The second is the environment. - -Middlewares can return one of three different values: - -* `{ok, Req, Env}` to continue the request processing -* `{suspend, Module, Function, Args}` to hibernate -* `{stop, Req}` to stop processing and move on to the next request - -Of note is that when hibernating, processing will resume on the given -MFA, discarding all previous stacktrace. Make sure you keep the `Req` -and `Env` in the arguments of this MFA for later use. - -If an error happens during middleware processing, Cowboy will not try -to send an error back to the socket, the process will just crash. It -is up to the middleware to make sure that a reply is sent if something -goes wrong. - -:: Configuration - -The middleware environment is defined as the `env` protocol option. -In the previous chapters we saw it briefly when we needed to pass -the routing information. It is a list of tuples with the first -element being an atom and the second any Erlang term. - -Two values in the environment are reserved: - -* `listener` contains the name of the listener -* `result` contains the result of the processing - -The `listener` value is always defined. The `result` value can be -set by any middleware. If set to anything other than `ok`, Cowboy -will not process any subsequent requests on this connection. - -The middlewares that come with Cowboy may define or require other -environment values to perform. - -You can update the environment by calling the `cowboy:set_env/3` -convenience function, adding or replacing a value in the environment. - -:: Routing middleware - -The routing middleware requires the `dispatch` value. If routing -succeeds, it will put the handler name and options in the `handler` -and `handler_opts` values of the environment, respectively. - -:: Handler middleware - -The handler middleware requires the `handler` and `handler_opts` -values. It puts the result of the request handling into `result`. diff --git a/doc/src/guide/modern_web.asciidoc b/doc/src/guide/modern_web.asciidoc new file mode 100644 index 0000000..7dc812b --- /dev/null +++ b/doc/src/guide/modern_web.asciidoc @@ -0,0 +1,214 @@ +[[modern_web]] +== The modern Web + +Let's take a look at various technologies from the beginnings +of the Web up to this day, and get a preview of what's +coming next. + +Cowboy is compatible with all the technology cited in this +chapter except of course HTTP/2.0 which has no implementation +in the wild at the time of writing. + +=== The prehistoric Web + +HTTP was initially created to serve HTML pages and only +had the GET method for retrieving them. This initial +version is documented and is sometimes called HTTP/0.9. +HTTP/1.0 defined the GET, HEAD and POST methods, and +was able to send data with POST requests. + +HTTP/1.0 works in a very simple way. A TCP connection +is first established to the server. Then a request is +sent. Then the server sends a response back and closes +the connection. + +Suffice to say, HTTP/1.0 is not very efficient. Opening +a TCP connection takes some time, and pages containing +many assets load much slower than they could because of +this. + +Most improvements done in recent years focused on reducing +this load time and reducing the latency of the requests. + +=== HTTP/1.1 + +HTTP/1.1 quickly followed and added a keep-alive mechanism +to allow using the same connection for many requests, as +well as streaming capabilities, allowing an endpoint to send +a body in well defined chunks. + +HTTP/1.1 defines the OPTIONS, GET, HEAD, POST, PUT, DELETE, +TRACE and CONNECT methods. The PATCH method was added in more +recent years. It also improves the caching capabilities with +the introduction of many headers. + +HTTP/1.1 still works like HTTP/1.0 does, except the connection +can be kept alive for subsequent requests. This however allows +clients to perform what is called as pipelining: sending many +requests in a row, and then processing the responses which will +be received in the same order as the requests. + +=== REST + +The design of HTTP/1.1 was influenced by the REST architectural +style. REST, or REpresentational State Transfer, is a style of +architecture for loosely connected distributed systems. + +REST defines constraints that systems must obey to in order to +be RESTful. A system which doesn't follow all the constraints +cannot be considered RESTful. + +REST is a client-server architecture with a clean separation +of concerns between the client and the server. They communicate +by referencing resources. Resources can be identified, but +also manipulated. A resource representation has a media type +and information about whether it can be cached and how. Hypermedia +determines how resources are related and how they can be used. +REST is also stateless. All requests contain the complete +information necessary to perform the action. + +HTTP/1.1 defines all the methods, headers and semantics required +to implement RESTful systems. + +REST is most often used when designing web application APIs +which are generally meant to be used by executable code directly. + +=== XmlHttpRequest + +Also know as AJAX, this technology allows Javascript code running +on a web page to perform asynchronous requests to the server. +This is what started the move from static websites to dynamic +web applications. + +XmlHttpRequest still performs HTTP requests under the hood, +and then waits for a response, but the Javascript code can +continue to run until the response arrives. It will then receive +the response through a callback previously defined. + +This is of course still requests initiated by the client, +the server still had no way of pushing data to the client +on its own, so new technology appeared to allow that. + +=== Long-polling + +Polling was a technique used to overcome the fact that the server +cannot push data directly to the client. Therefore the client had +to repeatedly create a connection, make a request, get a response, +then try again a few seconds later. This is overly expensive and +adds an additional delay before the client receives the data. + +Polling was necessary to implement message queues and other +similar mechanisms, where a user must be informed of something +when it happens, rather than when he refreshes the page next. +A typical example would be a chat application. + +Long-polling was created to reduce the server load by creating +less connections, but also to improve latency by getting the +response back to the client as soon as it becomes available +on the server. + +Long-polling works in a similar manner to polling, except the +request will not get a response immediately. Instead the server +leaves it open until it has a response to send. After getting +the response, the client creates a new request and gets back +to waiting. + +You probably guessed by now that long-polling is a hack, and +like most hacks it can suffer from unforeseen issues, in this +case it doesn't always play well with proxies. + +=== HTML5 + +HTML5 is, of course, the HTML version after HTML4. But HTML5 +emerged to solve a specific problem: dynamic web applications. + +HTML was initially created to write web pages which compose +a website. But soon people and companies wanted to use HTML +to write more and more complex websites, eventually known as +web applications. They are for example your news reader, your +email client in the browser, or your video streaming website. + +Because HTML wasn't enough, they started using proprietary +solutions, often implemented using plug-ins. This wasn't +perfect of course, but worked well enough for most people. + +However, the needs for a standard solution eventually became +apparent. The browser needed to be able to play media natively. +It needed to be able to draw anything. It needed an efficient +way of streaming events to the server, but also receiving +events from the server. + +The solution went on to become HTML5. At the time of writing +it is being standardized. + +=== EventSource + +EventSource, sometimes also called Server-Sent Events, is a +technology allowing servers to push data to HTML5 applications. + +EventSource is one-way communication channel from the server +to the client. The client has no means to talk to the server +other than by using HTTP requests. + +It consists of a Javascript object allowing setting up an +EventSource connection to the server, and a very small protocol +for sending events to the client on top of the HTTP/1.1 +connection. + +EventSource is a lightweight solution that only works for +UTF-8 encoded text data. Binary data and text data encoded +differently are not allowed by the protocol. A heavier but +more generic approach can be found in Websocket. + +=== Websocket + +Websocket is a protocol built on top of HTTP/1.1 that provides +a two-ways communication channel between the client and the +server. Communication is asynchronous and can occur concurrently. + +It consists of a Javascript object allowing setting up a +Websocket connection to the server, and a binary based +protocol for sending data to the server or the client. + +Websocket connections can transfer either UTF-8 encoded text +data or binary data. The protocol also includes support for +implementing a ping/pong mechanism, allowing the server and +the client to have more confidence that the connection is still +alive. + +A Websocket connection can be used to transfer any kind of data, +small or big, text or binary. Because of this Websocket is +sometimes used for communication between systems. + +=== SPDY + +SPDY is an attempt to reduce page loading time by opening a +single connection per server, keeping it open for subsequent +requests, and also by compressing the HTTP headers to reduce +the size of requests. + +SPDY is compatible with HTTP/1.1 semantics, and is actually +just a different way of performing HTTP requests and responses, +by using binary frames instead of a text-based protocol. +SPDY also allows the server to send extra responses following +a request. This is meant to allow sending the resources +associated with the request before the client requests them, +saving latency when loading websites. + +SPDY is an experiment that has proven successful and is used +as the basis for the HTTP/2.0 standard. + +Browsers make use of TLS Next Protocol Negotiation to upgrade +to a SPDY connection seamlessly if the protocol supports it. + +The protocol itself has a few shortcomings which are being +fixed in HTTP/2.0. + +=== HTTP/2.0 + +HTTP/2.0 is the long-awaited update to the HTTP/1.1 protocol. +It is based on SPDY although a lot has been improved at the +time of writing. + +HTTP/2.0 is an asynchronous two-ways communication channel +between two endpoints. diff --git a/doc/src/guide/modern_web.ezdoc b/doc/src/guide/modern_web.ezdoc deleted file mode 100644 index 1c2c342..0000000 --- a/doc/src/guide/modern_web.ezdoc +++ /dev/null @@ -1,215 +0,0 @@ -::: The modern Web - -Let's take a look at various technologies from the beginnings -of the Web up to this day, and get a preview of what's -coming next. - -Cowboy is compatible with all the technology cited in this -chapter except of course HTTP/2.0 which has no implementation -in the wild at the time of writing. - -:: The prehistoric Web - -HTTP was initially created to serve HTML pages and only -had the GET method for retrieving them. This initial -version is documented and is sometimes called HTTP/0.9. -HTTP/1.0 defined the GET, HEAD and POST methods, and -was able to send data with POST requests. - -HTTP/1.0 works in a very simple way. A TCP connection -is first established to the server. Then a request is -sent. Then the server sends a response back and closes -the connection. - -Suffice to say, HTTP/1.0 is not very efficient. Opening -a TCP connection takes some time, and pages containing -many assets load much slower than they could because of -this. - -Most improvements done in recent years focused on reducing -this load time and reducing the latency of the requests. - -:: HTTP/1.1 - -HTTP/1.1 quickly followed and added a keep-alive mechanism -to allow using the same connection for many requests, as -well as streaming capabilities, allowing an endpoint to send -a body in well defined chunks. - -HTTP/1.1 defines the OPTIONS, GET, HEAD, POST, PUT, DELETE, -TRACE and CONNECT methods. The PATCH method was added in more -recent years. It also improves the caching capabilities with -the introduction of many headers. - -HTTP/1.1 still works like HTTP/1.0 does, except the connection -can be kept alive for subsequent requests. This however allows -clients to perform what is called as pipelining: sending many -requests in a row, and then processing the responses which will -be received in the same order as the requests. - -:: REST - -The design of HTTP/1.1 was influenced by the REST architectural -style. REST, or REpresentational State Transfer, is a style of -architecture for loosely connected distributed systems. - -REST defines constraints that systems must obey to in order to -be RESTful. A system which doesn't follow all the constraints -cannot be considered RESTful. - -REST is a client-server architecture with a clean separation -of concerns between the client and the server. They communicate -by referencing resources. Resources can be identified, but -also manipulated. A resource representation has a media type -and information about whether it can be cached and how. Hypermedia -determines how resources are related and how they can be used. -REST is also stateless. All requests contain the complete -information necessary to perform the action. - -HTTP/1.1 defines all the methods, headers and semantics required -to implement RESTful systems. - -REST is most often used when designing web application APIs -which are generally meant to be used by executable code directly. - -:: XmlHttpRequest - -Also know as AJAX, this technology allows Javascript code running -on a web page to perform asynchronous requests to the server. -This is what started the move from static websites to dynamic -web applications. - -XmlHttpRequest still performs HTTP requests under the hood, -and then waits for a response, but the Javascript code can -continue to run until the response arrives. It will then receive -the response through a callback previously defined. - -This is of course still requests initiated by the client, -the server still had no way of pushing data to the client -on its own, so new technology appeared to allow that. - -:: Long-polling - -Polling was a technique used to overcome the fact that the server -cannot push data directly to the client. Therefore the client had -to repeatedly create a connection, make a request, get a response, -then try again a few seconds later. This is overly expensive and -adds an additional delay before the client receives the data. - -Polling was necessary to implement message queues and other -similar mechanisms, where a user must be informed of something -when it happens, rather than when he refreshes the page next. -A typical example would be a chat application. - -Long-polling was created to reduce the server load by creating -less connections, but also to improve latency by getting the -response back to the client as soon as it becomes available -on the server. - -Long-polling works in a similar manner to polling, except the -request will not get a response immediately. Instead the server -leaves it open until it has a response to send. After getting -the response, the client creates a new request and gets back -to waiting. - -You probably guessed by now that long-polling is a hack, and -like most hacks it can suffer from unforeseen issues, in this -case it doesn't always play well with proxies. - -:: HTML5 - -HTML5 is, of course, the HTML version after HTML4. But HTML5 -emerged to solve a specific problem: dynamic web applications. - -HTML was initially created to write web pages which compose -a website. But soon people and companies wanted to use HTML -to write more and more complex websites, eventually known as -web applications. They are for example your news reader, your -email client in the browser, or your video streaming website. - -Because HTML wasn't enough, they started using proprietary -solutions, often implemented using plug-ins. This wasn't -perfect of course, but worked well enough for most people. - -However, the needs for a standard solution eventually became -apparent. The browser needed to be able to play media natively. -It needed to be able to draw anything. It needed an efficient -way of streaming events to the server, but also receiving -events from the server. - -The solution went on to become HTML5. At the time of writing -it is being standardized. - -:: EventSource - -EventSource, sometimes also called Server-Sent Events, is a -technology allowing servers to push data to HTML5 applications. - -EventSource is one-way communication channel from the server -to the client. The client has no means to talk to the server -other than by using HTTP requests. - -It consists of a Javascript object allowing setting up an -EventSource connection to the server, and a very small protocol -for sending events to the client on top of the HTTP/1.1 -connection. - -EventSource is a lightweight solution that only works for -UTF-8 encoded text data. Binary data and text data encoded -differently are not allowed by the protocol. A heavier but -more generic approach can be found in Websocket. - -:: Websocket - -Websocket is a protocol built on top of HTTP/1.1 that provides -a two-ways communication channel between the client and the -server. Communication is asynchronous and can occur concurrently. - -It consists of a Javascript object allowing setting up a -Websocket connection to the server, and a binary based -protocol for sending data to the server or the client. - -Websocket connections can transfer either UTF-8 encoded text -data or binary data. The protocol also includes support for -implementing a ping/pong mechanism, allowing the server and -the client to have more confidence that the connection is still -alive. - -A Websocket connection can be used to transfer any kind of data, -small or big, text or binary. Because of this Websocket is -sometimes used for communication between systems. - -:: SPDY - -SPDY is an attempt to reduce page loading time by opening a -single connection per server, keeping it open for subsequent -requests, and also by compressing the HTTP headers to reduce -the size of requests. - -SPDY is compatible with HTTP/1.1 semantics, and is actually -just a different way of performing HTTP requests and responses, -by using binary frames instead of a text-based protocol. -SPDY also allows the server to send extra responses following -a request. This is meant to allow sending the resources -associated with the request before the client requests them, -saving latency when loading websites. - -SPDY is an experiment that has proven successful and is used -as the basis for the HTTP/2.0 standard. - -Browsers make use of TLS Next Protocol Negotiation to upgrade -to a SPDY connection seamlessly if the protocol supports it. - -The protocol itself has a few shortcomings which are being -fixed in HTTP/2.0. - -:: HTTP/2.0 - -HTTP/2.0 is the long-awaited update to the HTTP/1.1 protocol. -It is based on SPDY although a lot has been improved at the -time of writing. - -HTTP/2.0 is an asynchronous two-ways communication channel -between two endpoints. - -It is planned to be ready late 2014. diff --git a/doc/src/guide/multipart.asciidoc b/doc/src/guide/multipart.asciidoc new file mode 100644 index 0000000..20d53d5 --- /dev/null +++ b/doc/src/guide/multipart.asciidoc @@ -0,0 +1,169 @@ +[[multipart]] +== Multipart requests + +Multipart originates from MIME, an Internet standard that +extends the format of emails. Multipart messages are a +container for parts of any content-type. + +For example, a multipart message may have a part +containing text and a second part containing an +image. This is what allows you to attach files +to emails. + +In the context of HTTP, multipart is most often used +with the `multipart/form-data` content-type. This is +the content-type you have to use when you want browsers +to be allowed to upload files through HTML forms. + +Multipart is of course not required for uploading +files, it is only required when you want to do so +through HTML forms. + +You can read and parse multipart messages using the +Req object directly. + +Cowboy defines two functions that allows you to get +information about each part and read their contents. + +=== Structure + +A multipart message is a list of parts. Parts may +contain either a multipart message or a non-multipart +content-type. This allows parts to be arranged in a +tree structure, although this is a rare case as far +as the Web is concerned. + +=== Form-data + +In the normal case, when a form is submitted, the +browser will use the `application/x-www-form-urlencoded` +content-type. This type is just a list of keys and +values and is therefore not fit for uploading files. + +That's where the `multipart/form-data` content-type +comes in. When the form is configured to use this +content-type, the browser will use one part of the +message for each form field. This means that a file +input field will be sent in its own part, but the +same applies to all other kinds of fields. + +A form with a text input, a file input and a select +choice box will result in a multipart message with +three parts, one for each field. + +The browser does its best to determine the content-type +of the files it sends this way, but you should not +rely on it for determining the contents of the file. +Proper investigation of the contents is recommended. + +=== Checking the content-type + +While there is a variety of multipart messages, the +most common on the Web is `multipart/form-data`. It's +the type of message being sent when an HTML form +allows uploading files. + +You can quickly figure out if a multipart message +has been sent by parsing the `content-type` header. + +[source,erlang] +---- +{<<"multipart">>, <<"form-data">>, _} + = cowboy_req:parse_header(<<"content-type">>, Req). +---- + +=== Reading a multipart message + +To read a message you have to iterate over all its +parts. Then, for each part, you can inspect its headers +and read its body. + +[source,erlang] +---- +multipart(Req) -> + case cowboy_req:part(Req) of + {ok, _Headers, Req2} -> + {ok, _Body, Req3} = cowboy_req:part_body(Req2), + multipart(Req3); + {done, Req2} -> + Req2 + end. +---- + +Parts do not have a size limit. When a part body is +too big, Cowboy will return what it read so far and +allow you to continue if you wish to do so. + +The function `cow_multipart:form_data/1` can be used +to quickly obtain information about a part from a +`multipart/form-data` message. This function will +tell you if the part is for a normal field or if it +is a file being uploaded. + +This can be used for example to allow large part bodies +for files but crash when a normal field is too large. + +[source,erlang] +---- +multipart(Req) -> + case cowboy_req:part(Req) of + {ok, Headers, Req2} -> + Req4 = case cow_multipart:form_data(Headers) of + {data, _FieldName} -> + {ok, _Body, Req3} = cowboy_req:part_body(Req2), + Req3; + {file, _FieldName, _Filename, _CType, _CTransferEncoding} -> + stream_file(Req2) + end, + multipart(Req4); + {done, Req2} -> + Req2 + end. + +stream_file(Req) -> + case cowboy_req:part_body(Req) of + {ok, _Body, Req2} -> + Req2; + {more, _Body, Req2} -> + stream_file(Req2) + end. +---- + +By default the body chunk Cowboy will return is limited +to 8MB. This can of course be overriden. Both functions +can take a second argument, the same list of options that +will be passed to `cowboy_req:body/2` function. + +=== Skipping unwanted parts + +If you do not want to read a part's body, you can skip it. +Skipping is easy. If you do not call the function to read +the part's body, Cowboy will automatically skip it when +you request the next part. + +The following snippet reads all part headers and skips +all bodies: + +[source,erlang] +---- +multipart(Req) -> + case cowboy_req:part(Req) of + {ok, _Headers, Req2} -> + multipart(Req2); + {done, Req2} -> + Req2 + end. +---- + +Similarly, if you start reading the body and it ends up +being too big, you can simply continue with the next part, +Cowboy will automatically skip what remains. + +Note that the skipping rate may not be adequate for your +application. If you observe poor performance when skipping, +you might want to consider manually skipping by calling +the `cowboy_req:part_body/1` function directly. + +And if you started reading the message but decide that you +do not need the remaining parts, you can simply stop reading +entirely and Cowboy will automatically figure out what to do. diff --git a/doc/src/guide/multipart.ezdoc b/doc/src/guide/multipart.ezdoc deleted file mode 100644 index d0b2e40..0000000 --- a/doc/src/guide/multipart.ezdoc +++ /dev/null @@ -1,164 +0,0 @@ -::: Multipart requests - -Multipart originates from MIME, an Internet standard that -extends the format of emails. Multipart messages are a -container for parts of any content-type. - -For example, a multipart message may have a part -containing text and a second part containing an -image. This is what allows you to attach files -to emails. - -In the context of HTTP, multipart is most often used -with the `multipart/form-data` content-type. This is -the content-type you have to use when you want browsers -to be allowed to upload files through HTML forms. - -Multipart is of course not required for uploading -files, it is only required when you want to do so -through HTML forms. - -You can read and parse multipart messages using the -Req object directly. - -Cowboy defines two functions that allows you to get -information about each part and read their contents. - -:: Structure - -A multipart message is a list of parts. Parts may -contain either a multipart message or a non-multipart -content-type. This allows parts to be arranged in a -tree structure, although this is a rare case as far -as the Web is concerned. - -:: Form-data - -In the normal case, when a form is submitted, the -browser will use the `application/x-www-form-urlencoded` -content-type. This type is just a list of keys and -values and is therefore not fit for uploading files. - -That's where the `multipart/form-data` content-type -comes in. When the form is configured to use this -content-type, the browser will use one part of the -message for each form field. This means that a file -input field will be sent in its own part, but the -same applies to all other kinds of fields. - -A form with a text input, a file input and a select -choice box will result in a multipart message with -three parts, one for each field. - -The browser does its best to determine the content-type -of the files it sends this way, but you should not -rely on it for determining the contents of the file. -Proper investigation of the contents is recommended. - -:: Checking the content-type - -While there is a variety of multipart messages, the -most common on the Web is `multipart/form-data`. It's -the type of message being sent when an HTML form -allows uploading files. - -You can quickly figure out if a multipart message -has been sent by parsing the `content-type` header. - -``` erlang -{<<"multipart">>, <<"form-data">>, _} - = cowboy_req:parse_header(<<"content-type">>, Req). -``` - -:: Reading a multipart message - -To read a message you have to iterate over all its -parts. Then, for each part, you can inspect its headers -and read its body. - -``` erlang -multipart(Req) -> - case cowboy_req:part(Req) of - {ok, _Headers, Req2} -> - {ok, _Body, Req3} = cowboy_req:part_body(Req2), - multipart(Req3); - {done, Req2} -> - Req2 - end. -``` - -Parts do not have a size limit. When a part body is -too big, Cowboy will return what it read so far and -allow you to continue if you wish to do so. - -The function `cow_multipart:form_data/1` can be used -to quickly obtain information about a part from a -`multipart/form-data` message. This function will -tell you if the part is for a normal field or if it -is a file being uploaded. - -This can be used for example to allow large part bodies -for files but crash when a normal field is too large. - -``` erlang -multipart(Req) -> - case cowboy_req:part(Req) of - {ok, Headers, Req2} -> - Req4 = case cow_multipart:form_data(Headers) of - {data, _FieldName} -> - {ok, _Body, Req3} = cowboy_req:part_body(Req2), - Req3; - {file, _FieldName, _Filename, _CType, _CTransferEncoding} -> - stream_file(Req2) - end, - multipart(Req4); - {done, Req2} -> - Req2 - end. - -stream_file(Req) -> - case cowboy_req:part_body(Req) of - {ok, _Body, Req2} -> - Req2; - {more, _Body, Req2} -> - stream_file(Req2) - end. -``` - -By default the body chunk Cowboy will return is limited -to 8MB. This can of course be overriden. Both functions -can take a second argument, the same list of options that -will be passed to `cowboy_req:body/2` function. - -:: Skipping unwanted parts - -If you do not want to read a part's body, you can skip it. -Skipping is easy. If you do not call the function to read -the part's body, Cowboy will automatically skip it when -you request the next part. - -The following snippet reads all part headers and skips -all bodies: - -``` erlang -multipart(Req) -> - case cowboy_req:part(Req) of - {ok, _Headers, Req2} -> - multipart(Req2); - {done, Req2} -> - Req2 - end. -``` - -Similarly, if you start reading the body and it ends up -being too big, you can simply continue with the next part, -Cowboy will automatically skip what remains. - -Note that the skipping rate may not be adequate for your -application. If you observe poor performance when skipping, -you might want to consider manually skipping by calling -the `cowboy_req:part_body/1` function directly. - -And if you started reading the message but decide that you -do not need the remaining parts, you can simply stop reading -entirely and Cowboy will automatically figure out what to do. diff --git a/doc/src/guide/overview.asciidoc b/doc/src/guide/overview.asciidoc new file mode 100644 index 0000000..b337e3d --- /dev/null +++ b/doc/src/guide/overview.asciidoc @@ -0,0 +1,150 @@ +[[overview]] +== Request overview + +This chapter explains the different steps a request +goes through until a response is sent, along with +details of the Cowboy implementation. + +=== Request/response + +As you already know, HTTP clients connect to the server and +send a request for a resource; the server then sends a +response containing the resource if it could obtain it. + +Before the server can send the resource, however, it +needs to perform many different operations to read the +request, find the resource, prepare the response being +sent and often other related operations the user can +add like writing logs. + +Requests take the following route in Cowboy: + +image::http_req_resp.png[HTTP request/response flowchart] + +This shows the default middlewares, but they may be +configured differently in your setup. The dark green +indicates the points where you can hook your own code, +the light green is the Cowboy code that you can of +course configure as needed. + +The `acceptor` is the part of the server that accepts +the connection and create an Erlang process to handle +it. The `parser` then starts reading from the socket +and handling requests as they come until the socket +is closed. + +A response may be sent at many different points in the +life of the request. If Cowboy can't parse the request, +it gives up with an error response. If the router can't +find the resource, it sends a not found error. Your +own code can of course send a response at any time. + +When a response is sent, you can optionally modify it +or act upon it by enabling the `onresponse` hook. By +default the response is sent directly to the client. + +=== And then? + +Behavior depends on what protocol is in use. + +HTTP/1.0 can only process one request per connection, +so Cowboy will close the connection immediately after +it sends the response. + +HTTP/1.1 allows the client to request that the server +keeps the connection alive. This mechanism is described +in the next section. + +SPDY is designed to allow sending multiple requests +asynchronously on the same connection. Details on what +this means for your application is described in this +chapter. + +=== Keep-alive (HTTP/1.1) + +With HTTP/1.1, the connection may be left open for +subsequent requests to come. This mechanism is called +`keep-alive`. + +When the client sends a request to the server, it includes +a header indicating whether it would like to leave the +socket open. The server may or may not accept, indicating +its choice by sending the same header in the response. + +Cowboy will include this header automatically in all +responses to HTTP/1.1 requests. You can however force +the closing of the socket if you want. When Cowboy sees +you want to send a `connection: close` header, it will +not override it and will close the connection as soon +as the reply is sent. + +This snippet will force Cowboy to close the connection. + +[source,erlang] +---- +Req2 = cowboy_req:reply(200, [ + {<<"connection">>, <<"close">>}, +], <<"Closing the socket in 3.. 2.. 1..">>, Req). +---- + +Cowboy will only accept a certain number of new requests +on the same connection. By default it will run up to 100 +requests. This number can be changed by setting the +`max_keepalive` configuration value when starting an +HTTP listener. + +[source,erlang] +---- +cowboy:start_http(my_http_listener, 100, [{port, 8080}], [ + {env, [{dispatch, Dispatch}]}, + {max_keepalive, 5} +]). +---- + +Cowboy implements the keep-alive mechanism by reusing +the same process for all requests. This allows Cowboy +to save memory. This works well because most code will +not have any side effect impacting subsequent requests. +But it also means you need to clean up if you do have +code with side effects. The `terminate/3` function can +be used for this purpose. + +=== Pipelining (HTTP/1.1) + +While HTTP is designed as a sequential protocol, with +the client sending a request and then waiting for the +response from the server, nothing prevents the client +from sending more requests to the server without waiting +for the response, due to how sockets work. The server +still handles the requests sequentially and sends the +responses in the same order. + +This mechanism is called pipelining. It allows reducing +latency when a client needs to request many resources +at the same time. This is used by browsers when requesting +static files for example. + +This is handled automatically by the server. + +=== Asynchronous requests (SPDY) + +In SPDY, the client can send a request at any time. +And the server can send a response at any time too. + +This means for example that the client does not need +to wait for a request to be fully sent to send another, +it is possible to interleave a request with the request +body of another request. The same is true with responses. +Responses may also be sent in a different order. + +Because requests and responses are fully asynchronous, +Cowboy creates a new process for each request, and these +processes are managed by another process that handles the +connection itself. + +SPDY servers may also decide to send resources to the +client before the client requests them. This is especially +useful for sending static files associated with the HTML +page requested, as this reduces the latency of the overall +response. Cowboy does not support this particular mechanism +at this point, however. diff --git a/doc/src/guide/overview.ezdoc b/doc/src/guide/overview.ezdoc deleted file mode 100644 index 725ae4e..0000000 --- a/doc/src/guide/overview.ezdoc +++ /dev/null @@ -1,147 +0,0 @@ -::: Request overview - -This chapter explains the different steps a request -goes through until a response is sent, along with -details of the Cowboy implementation. - -:: Request/response - -As you already know, HTTP clients connect to the server and -send a request for a resource; the server then sends a -response containing the resource if it could obtain it. - -Before the server can send the resource, however, it -needs to perform many different operations to read the -request, find the resource, prepare the response being -sent and often other related operations the user can -add like writing logs. - -Requests take the following route in Cowboy: - -^"HTTP request/response flowchart^!http_req_resp.png - -This shows the default middlewares, but they may be -configured differently in your setup. The dark green -indicates the points where you can hook your own code, -the light green is the Cowboy code that you can of -course configure as needed. - -The `acceptor` is the part of the server that accepts -the connection and create an Erlang process to handle -it. The `parser` then starts reading from the socket -and handling requests as they come until the socket -is closed. - -A response may be sent at many different points in the -life of the request. If Cowboy can't parse the request, -it gives up with an error response. If the router can't -find the resource, it sends a not found error. Your -own code can of course send a response at any time. - -When a response is sent, you can optionally modify it -or act upon it by enabling the `onresponse` hook. By -default the response is sent directly to the client. - -:: And then? - -Behavior depends on what protocol is in use. - -HTTP/1.0 can only process one request per connection, -so Cowboy will close the connection immediately after -it sends the response. - -HTTP/1.1 allows the client to request that the server -keeps the connection alive. This mechanism is described -in the next section. - -SPDY is designed to allow sending multiple requests -asynchronously on the same connection. Details on what -this means for your application is described in this -chapter. - -:: Keep-alive (HTTP/1.1) - -With HTTP/1.1, the connection may be left open for -subsequent requests to come. This mechanism is called -`keep-alive`. - -When the client sends a request to the server, it includes -a header indicating whether it would like to leave the -socket open. The server may or may not accept, indicating -its choice by sending the same header in the response. - -Cowboy will include this header automatically in all -responses to HTTP/1.1 requests. You can however force -the closing of the socket if you want. When Cowboy sees -you want to send a `connection: close` header, it will -not override it and will close the connection as soon -as the reply is sent. - -This snippet will force Cowboy to close the connection. - -``` erlang -Req2 = cowboy_req:reply(200, [ - {<<"connection">>, <<"close">>}, -], <<"Closing the socket in 3.. 2.. 1..">>, Req). -``` - -Cowboy will only accept a certain number of new requests -on the same connection. By default it will run up to 100 -requests. This number can be changed by setting the -`max_keepalive` configuration value when starting an -HTTP listener. - -``` erlang -cowboy:start_http(my_http_listener, 100, [{port, 8080}], [ - {env, [{dispatch, Dispatch}]}, - {max_keepalive, 5} -]). -``` - -Cowboy implements the keep-alive mechanism by reusing -the same process for all requests. This allows Cowboy -to save memory. This works well because most code will -not have any side effect impacting subsequent requests. -But it also means you need to clean up if you do have -code with side effects. The `terminate/3` function can -be used for this purpose. - -:: Pipelining (HTTP/1.1) - -While HTTP is designed as a sequential protocol, with -the client sending a request and then waiting for the -response from the server, nothing prevents the client -from sending more requests to the server without waiting -for the response, due to how sockets work. The server -still handles the requests sequentially and sends the -responses in the same order. - -This mechanism is called pipelining. It allows reducing -latency when a client needs to request many resources -at the same time. This is used by browsers when requesting -static files for example. - -This is handled automatically by the server. - -:: Asynchronous requests (SPDY) - -In SPDY, the client can send a request at any time. -And the server can send a response at any time too. - -This means for example that the client does not need -to wait for a request to be fully sent to send another, -it is possible to interleave a request with the request -body of another request. The same is true with responses. -Responses may also be sent in a different order. - -Because requests and responses are fully asynchronous, -Cowboy creates a new process for each request, and these -processes are managed by another process that handles the -connection itself. - -SPDY servers may also decide to send resources to the -client before the client requests them. This is especially -useful for sending static files associated with the HTML -page requested, as this reduces the latency of the overall -response. Cowboy does not support this particular mechanism -at this point, however. diff --git a/doc/src/guide/req.asciidoc b/doc/src/guide/req.asciidoc new file mode 100644 index 0000000..09d442a --- /dev/null +++ b/doc/src/guide/req.asciidoc @@ -0,0 +1,247 @@ +[[req]] +== The Req object + +The Req object is this variable that you will use to obtain +information about a request, read the body of the request +and send a response. + +=== A special variable + +While we call it an "object", it is not an object in the +OOP sense of the term. In fact it is completely opaque +to you and the only way you can perform operations using +it is by calling the functions from the `cowboy_req` +module. + +Almost all the calls to the `cowboy_req` module will +return an updated request object. Just like you would +keep the updated `State` variable in a gen_server, +you MUST keep the updated `Req` variable in a Cowboy +handler. Cowboy will use this object to know whether +a response has been sent when the handler has finished +executing. + +The Req object allows accessing both immutable and +mutable state. This means that calling some of the +functions twice will not produce the same result. +For example, when streaming the request body, the +function will return the body by chunks, one at a +time, until there is none left. + +=== Overview of the cowboy_req interface + +With the exception of functions manipulating the request +body, all functions return a single value. Depending on +the function this can be the requested value (method, +host, path, ...), a boolean (has_body, has_resp_header...) +a new Req object (set_resp_body, set_resp_header...), or +simply the atom `ok` (chunk, continue, ...). + +The request body reading functions may return `{Result, Req}` +or `{Result, Value, Req}`. The functions in this category +are `body/{1,2}`, `body_qs/{1,2}`, `part/{1,2}`, `part_body/{1,2}`. + +This chapter covers the access functions mainly. Cookies, +request body and response functions are covered in their +own chapters. + +=== Request + +When a client performs a request, it first sends a few required +values. They are sent differently depending on the protocol +being used, but the intent is the same. They indicate to the +server the type of action it wants to do and how to locate +the resource to perform it on. + +The method identifies the action. Standard methods include +GET, HEAD, OPTIONS, PATCH, POST, PUT, DELETE. Method names +are case sensitive. + +[source,erlang] +Method = cowboy_req:method(Req). + +The host, port and path parts of the URL identify the resource +being accessed. The host and port information may not be +available if the client uses HTTP/1.0. + +[source,erlang] +Host = cowboy_req:host(Req), +Port = cowboy_req:port(Req), +Path = cowboy_req:path(Req). + +The version used by the client can of course also be obtained. + +[source,erlang] +Version = cowboy_req:version(Req). + +Do note however that clients claiming to implement one version +of the protocol does not mean they implement it fully, or even +properly. + +=== Bindings + +After routing the request, bindings are available. Bindings +are these parts of the host or path that you chose to extract +when defining the routes of your application. + +You can fetch a single binding. The value will be `undefined` +if the binding doesn't exist. + +[source,erlang] +Binding = cowboy_req:binding(my_binding, Req). + +If you need a different value when the binding doesn't exist, +you can change the default. + +[source,erlang] +Binding = cowboy_req:binding(my_binding, Req, 42). + +You can also obtain all bindings in one call. They will be +returned as a list of key/value tuples. + +[source,erlang] +AllBindings = cowboy_req:bindings(Req). + +If you used `...` at the beginning of the route's pattern +for the host, you can retrieve the matched part of the host. +The value will be `undefined` otherwise. + +[source,erlang] +HostInfo = cowboy_req:host_info(Req). + +Similarly, if you used `...` at the end of the route's +pattern for the path, you can retrieve the matched part, +or get `undefined` otherwise. + +[source,erlang] +PathInfo = cowboy_req:path_info(Req). + +=== Query string + +The raw query string can be obtained directly. + +[source,erlang] +Qs = cowboy_req:qs(Req). + +You can parse the query string and then use standard library +functions to access individual values. + +[source,erlang] +QsVals = cowboy_req:parse_qs(Req), +{_, Lang} = lists:keyfind(<<"lang">>, 1, QsVals). + +You can match the query string into a map. + +[source,erlang] +#{id := ID, lang := Lang} = cowboy_req:match_qs([id, lang], Req). + +You can use constraints to validate the values while matching +them. The following snippet will crash if the `id` value is +not an integer number or if the `lang` value is empty. Additionally +the `id` value will be converted to an integer term, saving +you a conversion step. + +[source,erlang] +QsMap = cowboy_req:match_qs([{id, int}, {lang, nonempty}], Req). + +Note that in the case of duplicate query string keys, the map +value will become a list of the different values. + +Read more about ^constraints^. + +A default value can be provided. The default will be used +if the `lang` key is not found. It will not be used if +the key is found but has an empty value. + +[source,erlang] +#{lang := Lang} = cowboy_req:match_qs([{lang, [], <<"en-US">>}], Req). + +If no default is provided and the value is missing, the +query string is deemed invalid and the process will crash. + +=== Request URL + +You can reconstruct the full URL of the resource. + +[source,erlang] +URL = cowboy_req:url(Req). + +You can also obtain only the base of the URL, excluding the +path and query string. + +[source,erlang] +BaseURL = cowboy_req:host_url(Req). + +=== Headers + +Cowboy allows you to obtain the header values as string, +or parsed into a more meaningful representation. + +This will get the string value of a header. + +[source,erlang] +HeaderVal = cowboy_req:header(<<"content-type">>, Req). + +You can of course set a default in case the header is missing. + +[source,erlang] +HeaderVal = cowboy_req:header(<<"content-type">>, Req, <<"text/plain">>). + +And also obtain all headers. + +[source,erlang] +AllHeaders = cowboy_req:headers(Req). + +To parse the previous header, simply call `parse_header/{2,3}` +where you would call `header/{2,3}` otherwise. + +[source,erlang] +ParsedVal = cowboy_req:parse_header(<<"content-type">>, Req). + +Cowboy will crash if it doesn't know how to parse the given +header, or if the value is invalid. + +You can of course define a default value. Note that the default +value you specify here is the parsed value you'd like to get +by default. + +[source,erlang] +---- +ParsedVal = cowboy_req:parse_header(<<"content-type">>, Req, + {<<"text">>, <<"plain">>, []}). +---- + +The list of known headers and default values is defined in the +manual. + +=== Meta + +Cowboy will sometimes associate some meta information with +the request. Built-in meta values are listed in the manual +for their respective modules. + +This will get a meta value. The returned value will be `undefined` +if it isn't defined. + +[source,erlang] +MetaVal = cowboy_req:meta(websocket_version, Req). + +You can change the default value if needed. + +[source,erlang] +MetaVal = cowboy_req:meta(websocket_version, Req, 13). + +You can also define your own meta values. The name must be +an `atom()`. + +[source,erlang] +Req2 = cowboy_req:set_meta(the_answer, 42, Req). + +=== Peer + +You can obtain the peer address and port number. This is +not necessarily the actual IP and port of the client, but +rather the one of the machine that connected to the server. + +[source,erlang] +{IP, Port} = cowboy_req:peer(Req). diff --git a/doc/src/guide/req.ezdoc b/doc/src/guide/req.ezdoc deleted file mode 100644 index add6166..0000000 --- a/doc/src/guide/req.ezdoc +++ /dev/null @@ -1,269 +0,0 @@ -::: The Req object - -The Req object is this variable that you will use to obtain -information about a request, read the body of the request -and send a response. - -:: A special variable - -While we call it an "object", it is not an object in the -OOP sense of the term. In fact it is completely opaque -to you and the only way you can perform operations using -it is by calling the functions from the `cowboy_req` -module. - -Almost all the calls to the `cowboy_req` module will -return an updated request object. Just like you would -keep the updated `State` variable in a gen_server, -you MUST keep the updated `Req` variable in a Cowboy -handler. Cowboy will use this object to know whether -a response has been sent when the handler has finished -executing. - -The Req object allows accessing both immutable and -mutable state. This means that calling some of the -functions twice will not produce the same result. -For example, when streaming the request body, the -function will return the body by chunks, one at a -time, until there is none left. - -:: Overview of the cowboy_req interface - -With the exception of functions manipulating the request -body, all functions return a single value. Depending on -the function this can be the requested value (method, -host, path, ...), a boolean (has_body, has_resp_header...) -a new Req object (set_resp_body, set_resp_header...), or -simply the atom `ok` (chunk, continue, ...). - -The request body reading functions may return `{Result, Req}` -or `{Result, Value, Req}`. The functions in this category -are `body/{1,2}`, `body_qs/{1,2}`, `part/{1,2}`, `part_body/{1,2}`. - -This chapter covers the access functions mainly. Cookies, -request body and response functions are covered in their -own chapters. - -:: Request - -When a client performs a request, it first sends a few required -values. They are sent differently depending on the protocol -being used, but the intent is the same. They indicate to the -server the type of action it wants to do and how to locate -the resource to perform it on. - -The method identifies the action. Standard methods include -GET, HEAD, OPTIONS, PATCH, POST, PUT, DELETE. Method names -are case sensitive. - -``` erlang -Method = cowboy_req:method(Req). -``` - -The host, port and path parts of the URL identify the resource -being accessed. The host and port information may not be -available if the client uses HTTP/1.0. - -``` erlang -Host = cowboy_req:host(Req), -Port = cowboy_req:port(Req), -Path = cowboy_req:path(Req). -``` - -The version used by the client can of course also be obtained. - -``` erlang -Version = cowboy_req:version(Req). -``` - -Do note however that clients claiming to implement one version -of the protocol does not mean they implement it fully, or even -properly. - -:: Bindings - -After routing the request, bindings are available. Bindings -are these parts of the host or path that you chose to extract -when defining the routes of your application. - -You can fetch a single binding. The value will be `undefined` -if the binding doesn't exist. - -``` erlang -Binding = cowboy_req:binding(my_binding, Req). -``` - -If you need a different value when the binding doesn't exist, -you can change the default. - -``` erlang -Binding = cowboy_req:binding(my_binding, Req, 42). -``` - -You can also obtain all bindings in one call. They will be -returned as a list of key/value tuples. - -``` erlang -AllBindings = cowboy_req:bindings(Req). -``` - -If you used `...` at the beginning of the route's pattern -for the host, you can retrieve the matched part of the host. -The value will be `undefined` otherwise. - -``` erlang -HostInfo = cowboy_req:host_info(Req). -``` - -Similarly, if you used `...` at the end of the route's -pattern for the path, you can retrieve the matched part, -or get `undefined` otherwise. - -``` erlang -PathInfo = cowboy_req:path_info(Req). -``` - -:: Query string - -The raw query string can be obtained directly. - -``` erlang -Qs = cowboy_req:qs(Req). -``` - -You can parse the query string and then use standard library -functions to access individual values. - -``` erlang -QsVals = cowboy_req:parse_qs(Req), -{_, Lang} = lists:keyfind(<<"lang">>, 1, QsVals). -``` - -You can match the query string into a map. - -``` erlang -#{id := ID, lang := Lang} = cowboy_req:match_qs([id, lang], Req). -``` - -You can use constraints to validate the values while matching -them. The following snippet will crash if the `id` value is -not an integer number or if the `lang` value is empty. Additionally -the `id` value will be converted to an integer term, saving -you a conversion step. - -``` erlang -QsMap = cowboy_req:match_qs([{id, int}, {lang, nonempty}], Req). -``` - -Note that in the case of duplicate query string keys, the map -value will become a list of the different values. - -Read more about ^constraints^. - -A default value can be provided. The default will be used -if the `lang` key is not found. It will not be used if -the key is found but has an empty value. - -``` erlang -#{lang := Lang} = cowboy_req:match_qs([{lang, [], <<"en-US">>}], Req). -``` - -If no default is provided and the value is missing, the -query string is deemed invalid and the process will crash. - -:: Request URL - -You can reconstruct the full URL of the resource. - -``` erlang -URL = cowboy_req:url(Req). -``` - -You can also obtain only the base of the URL, excluding the -path and query string. - -``` erlang -BaseURL = cowboy_req:host_url(Req). -``` - -:: Headers - -Cowboy allows you to obtain the header values as string, -or parsed into a more meaningful representation. - -This will get the string value of a header. - -``` erlang -HeaderVal = cowboy_req:header(<<"content-type">>, Req). -``` - -You can of course set a default in case the header is missing. - -``` erlang -HeaderVal - = cowboy_req:header(<<"content-type">>, Req, <<"text/plain">>). -``` - -And also obtain all headers. - -``` erlang -AllHeaders = cowboy_req:headers(Req). -``` - -To parse the previous header, simply call `parse_header/{2,3}` -where you would call `header/{2,3}` otherwise. - -``` erlang -ParsedVal = cowboy_req:parse_header(<<"content-type">>, Req). -``` - -Cowboy will crash if it doesn't know how to parse the given -header, or if the value is invalid. - -You can of course define a default value. Note that the default -value you specify here is the parsed value you'd like to get -by default. - -``` erlang -ParsedVal = cowboy_req:parse_header(<<"content-type">>, Req, - {<<"text">>, <<"plain">>, []}). -``` - -The list of known headers and default values is defined in the -manual. - -:: Meta - -Cowboy will sometimes associate some meta information with -the request. Built-in meta values are listed in the manual -for their respective modules. - -This will get a meta value. The returned value will be `undefined` -if it isn't defined. - -``` erlang -MetaVal = cowboy_req:meta(websocket_version, Req). -``` - -You can change the default value if needed. - -``` erlang -MetaVal = cowboy_req:meta(websocket_version, Req, 13). -``` - -You can also define your own meta values. The name must be -an `atom()`. - -``` erlang -Req2 = cowboy_req:set_meta(the_answer, 42, Req). -``` - -:: Peer - -You can obtain the peer address and port number. This is -not necessarily the actual IP and port of the client, but -rather the one of the machine that connected to the server. - -``` erlang -{IP, Port} = cowboy_req:peer(Req). -``` diff --git a/doc/src/guide/req_body.asciidoc b/doc/src/guide/req_body.asciidoc new file mode 100644 index 0000000..d2a43d2 --- /dev/null +++ b/doc/src/guide/req_body.asciidoc @@ -0,0 +1,152 @@ +[[req_body]] +== Reading the request body + +The Req object also allows you to read the request body. + +Because the request body can be of any size, all body +reading operations will only work once, as Cowboy will +not cache the result of these operations. + +Cowboy will not attempt to read the body until you do. +If handler execution ends without reading it, Cowboy +will simply skip it. + +Cowboy provides different ways to read the request body. +You can read it directly, stream it, but also read and +parse in a single call for form urlencoded formats or +multipart. All of these except multipart are covered in +this chapter. Multipart is covered later on in the guide. + +=== Check for request body + +You can check whether a body was sent with the request. + +[source,erlang] +cowboy_req:has_body(Req). + +It will return `true` if there is a request body, and +`false` otherwise. + +Note that it is generally safe to assume that a body is +sent for `POST`, `PUT` and `PATCH` requests, without +having to explicitly check for it. + +=== Request body length + +You can obtain the body length if it was sent with the +request. + +[source,erlang] +Length = cowboy_req:body_length(Req). + +The value returned will be `undefined` if the length +couldn't be figured out from the request headers. If +there's a body but no length is given, this means that +the chunked transfer-encoding was used. You can read +chunked bodies by using the stream functions. + +=== Reading the body + +You can read the whole body directly in one call. + +[source,erlang] +{ok, Body, Req2} = cowboy_req:body(Req). + +By default, Cowboy will attempt to read up to a +size of 8MB. You can override this limit as needed. + +[source,erlang] +{ok, Body, Req2} = cowboy_req:body(Req, [{length, 100000000}]). + +You can also disable it. + +[source,erlang] +{ok, Body, Req2} = cowboy_req:body(Req, [{length, infinity}]). + +It is recommended that you do not disable it for public +facing websites. + +If the body is larger than the limit, then Cowboy will return +a `more` tuple instead, allowing you to stream it if you +would like to. + +=== Streaming the body + +You can stream the request body by chunks. + +Cowboy returns a `more` tuple when there is more body to +be read, and an `ok` tuple for the last chunk. This allows +you to loop over all chunks. + +[source,erlang] +---- +body_to_console(Req) -> + case cowboy_req:body(Req) of + {ok, Data, Req2} -> + io:format("~s", [Data]), + Req2; + {more, Data, Req2} -> + io:format("~s", [Data]), + body_to_console(Req2) + end. +---- + +You can of course set the `length` option to configure the +size of chunks. + +=== Rate of data transmission + +You can control the rate of data transmission by setting +options when calling body functions. This applies not only +to the functions described in this chapter, but also to +the multipart functions. + +The `read_length` option defines the maximum amount of data +to be received from the socket at once, in bytes. + +The `read_timeout` option defines the time Cowboy waits +before that amount is received, in milliseconds. + +=== Transfer and content decoding + +Cowboy will by default decode the chunked transfer-encoding +if any. It will not decode any content-encoding by default. + +The first time you call a body function you can set the +`transfer_decode` and `content_decode` options. If the body +was already started being read these options are simply +ignored. + +The following example shows how to set both options. + +[source,erlang] +---- +{ok, Data, Req2} = cowboy_req:body(Req, [ + {transfer_decode, fun transfer_decode/2, TransferState}, + {content_decode, fun content_decode/1} +]). +---- + +=== Reading a form urlencoded body + +You can directly obtain a list of key/value pairs if the +body was sent using the application/x-www-form-urlencoded +content-type. + +[source,erlang] +{ok, KeyValues, Req2} = cowboy_req:body_qs(Req). + +You can then retrieve an individual value from that list. + +[source,erlang] +{_, Lang} = lists:keyfind(lang, 1, KeyValues). + +You should not attempt to match on the list as the order +of the values is undefined. + +By default Cowboy will reject bodies with a size above +64KB when using this function. You can override this limit +by setting the `length` option. + +[source,erlang] +{ok, KeyValues, Req2} = cowboy_req:body_qs(Req, [{length, 2000000}]). diff --git a/doc/src/guide/req_body.ezdoc b/doc/src/guide/req_body.ezdoc deleted file mode 100644 index 8864035..0000000 --- a/doc/src/guide/req_body.ezdoc +++ /dev/null @@ -1,158 +0,0 @@ -::: Reading the request body - -The Req object also allows you to read the request body. - -Because the request body can be of any size, all body -reading operations will only work once, as Cowboy will -not cache the result of these operations. - -Cowboy will not attempt to read the body until you do. -If handler execution ends without reading it, Cowboy -will simply skip it. - -Cowboy provides different ways to read the request body. -You can read it directly, stream it, but also read and -parse in a single call for form urlencoded formats or -multipart. All of these except multipart are covered in -this chapter. Multipart is covered later on in the guide. - -:: Check for request body - -You can check whether a body was sent with the request. - -``` erlang -cowboy_req:has_body(Req). -``` - -It will return `true` if there is a request body, and -`false` otherwise. - -Note that it is generally safe to assume that a body is -sent for `POST`, `PUT` and `PATCH` requests, without -having to explicitly check for it. - -:: Request body length - -You can obtain the body length if it was sent with the -request. - -``` erlang -Length = cowboy_req:body_length(Req). -``` - -The value returned will be `undefined` if the length -couldn't be figured out from the request headers. If -there's a body but no length is given, this means that -the chunked transfer-encoding was used. You can read -chunked bodies by using the stream functions. - -:: Reading the body - -You can read the whole body directly in one call. - -``` erlang -{ok, Body, Req2} = cowboy_req:body(Req). -``` - -By default, Cowboy will attempt to read up to a -size of 8MB. You can override this limit as needed. - -``` erlang -{ok, Body, Req2} = cowboy_req:body(Req, [{length, 100000000}]). -``` - -You can also disable it. - -``` erlang -{ok, Body, Req2} = cowboy_req:body(Req, [{length, infinity}]). -``` - -It is recommended that you do not disable it for public -facing websites. - -If the body is larger than the limit, then Cowboy will return -a `more` tuple instead, allowing you to stream it if you -would like to. - -:: Streaming the body - -You can stream the request body by chunks. - -Cowboy returns a `more` tuple when there is more body to -be read, and an `ok` tuple for the last chunk. This allows -you to loop over all chunks. - -``` erlang -body_to_console(Req) -> - case cowboy_req:body(Req) of - {ok, Data, Req2} -> - io:format("~s", [Data]), - Req2; - {more, Data, Req2} -> - io:format("~s", [Data]), - body_to_console(Req2) - end. -``` - -You can of course set the `length` option to configure the -size of chunks. - -:: Rate of data transmission - -You can control the rate of data transmission by setting -options when calling body functions. This applies not only -to the functions described in this chapter, but also to -the multipart functions. - -The `read_length` option defines the maximum amount of data -to be received from the socket at once, in bytes. - -The `read_timeout` option defines the time Cowboy waits -before that amount is received, in milliseconds. - -:: Transfer and content decoding - -Cowboy will by default decode the chunked transfer-encoding -if any. It will not decode any content-encoding by default. - -The first time you call a body function you can set the -`transfer_decode` and `content_decode` options. If the body -was already started being read these options are simply -ignored. - -The following example shows how to set both options. - -``` erlang -{ok, Data, Req2} = cowboy_req:body(Req, [ - {transfer_decode, fun transfer_decode/2, TransferState}, - {content_decode, fun content_decode/1} -]). -``` - -:: Reading a form urlencoded body - -You can directly obtain a list of key/value pairs if the -body was sent using the application/x-www-form-urlencoded -content-type. - -``` erlang -{ok, KeyValues, Req2} = cowboy_req:body_qs(Req). -``` - -You can then retrieve an individual value from that list. - -``` erlang -{_, Lang} = lists:keyfind(lang, 1, KeyValues). -``` - -You should not attempt to match on the list as the order -of the values is undefined. - -By default Cowboy will reject bodies with a size above -64KB when using this function. You can override this limit -by setting the `length` option. - -``` erlang -{ok, KeyValues, Req2} = cowboy_req:body_qs(Req, - [{length, 2000000}]). -``` diff --git a/doc/src/guide/resource_design.asciidoc b/doc/src/guide/resource_design.asciidoc new file mode 100644 index 0000000..a8a6648 --- /dev/null +++ b/doc/src/guide/resource_design.asciidoc @@ -0,0 +1,221 @@ +[[resource_design]] +== Designing a resource handler + +This chapter aims to provide you with a list of questions +you must answer in order to write a good resource handler. +It is meant to be usable as a step by step guide. + +=== The service + +Can the service become unavailable, and when it does, can +we detect it? For example database connectivity problems +may be detected early. We may also have planned outages +of all or parts of the system. Implement the +`service_available` callback. + +What HTTP methods does the service implement? Do we need +more than the standard OPTIONS, HEAD, GET, PUT, POST, +PATCH and DELETE? Are we not using one of those at all? +Implement the `known_methods` callback. + +=== Type of resource handler + +Am I writing a handler for a collection of resources, +or for a single resource? + +The semantics for each of these are quite different. +You should not mix collection and single resource in +the same handler. + +=== Collection handler + +Skip this section if you are not doing a collection. + +Is the collection hardcoded or dynamic? For example +if you use the route `/users` for the collection of +users then the collection is hardcoded; if you use +`/forums/:category` for the collection of threads +then it isn't. When the collection is hardcoded you +can safely assume the resource always exists. + +What methods should I implement? + +OPTIONS is used to get some information about the +collection. It is recommended to allow it even if you +do not implement it, as Cowboy has a default +implementation built-in. + +HEAD and GET are used to retrieve the collection. +If you allow GET, also allow HEAD as there's no extra +work required to make it work. + +POST is used to create a new resource inside the +collection. Creating a resource by using POST on +the collection is useful when resources may be +created before knowing their URI, usually because +parts of it are generated dynamically. A common +case is some kind of auto incremented integer +identifier. + +The next methods are more rarely allowed. + +PUT is used to create a new collection (when +the collection isn't hardcoded), or replace +the entire collection. + +DELETE is used to delete the entire collection. + +PATCH is used to modify the collection using +instructions given in the request body. A PATCH +operation is atomic. The PATCH operation may +be used for such things as reordering; adding, +modifying or deleting parts of the collection. + +=== Single resource handler + +Skip this section if you are doing a collection. + +What methods should I implement? + +OPTIONS is used to get some information about the +resource. It is recommended to allow it even if you +do not implement it, as Cowboy has a default +implementation built-in. + +HEAD and GET are used to retrieve the resource. +If you allow GET, also allow HEAD as there's no extra +work required to make it work. + +POST is used to update the resource. + +PUT is used to create a new resource (when it doesn't +already exist) or replace the resource. + +DELETE is used to delete the resource. + +PATCH is used to modify the resource using +instructions given in the request body. A PATCH +operation is atomic. The PATCH operation may +be used for adding, removing or modifying specific +values in the resource. + +=== The resource + +Following the above discussion, implement the +`allowed_methods` callback. + +Does the resource always exist? If it may not, implement +the `resource_exists` callback. + +Do I need to authenticate the client before they can +access the resource? What authentication mechanisms +should I provide? This may include form-based, token-based +(in the URL or a cookie), HTTP basic, HTTP digest, +SSL certificate or any other form of authentication. +Implement the `is_authorized` callback. + +Do I need fine-grained access control? How do I determine +that they are authorized access? Handle that in your +`is_authorized` callback. + +Can access to a resource be forbidden regardless of access +being authorized? A simple example of that is censorship +of a resource. Implement the `forbidden` callback. + +Is there any constraints on the length of the resource URI? +For example the URI may be used as a key in storage and may +have a limit in length. Implement `uri_too_long`. + +=== Representations + +What media types do I provide? If text based, what charsets +are provided? What languages do I provide? + +Implement the mandatory `content_types_provided`. Prefix +the callbacks with `to_` for clarity. For example `to_html` +or `to_text`. + +Implement the `languages_provided` or `charsets_provided` +callbacks if applicable. + +Is there any other header that may make the representation +of the resource vary? Implement the `variances` callback. + +Depending on your choices for caching content, you may +want to implement one or more of the `generate_etag`, +`last_modified` and `expires` callbacks. + +Do I want the user or user agent to actively choose a +representation available? Send a list of available +representations in the response body and implement +the `multiple_choices` callback. + +=== Redirections + +Do I need to keep track of what resources were deleted? +For example you may have a mechanism where moving a +resource leaves a redirect link to its new location. +Implement the `previously_existed` callback. + +Was the resource moved, and is the move temporary? If +it is explicitly temporary, for example due to maintenance, +implement the `moved_temporarily` callback. Otherwise, +implement the `moved_permanently` callback. + +=== The request + +Do we need to perform extra checks to make sure the request +is valid? Cowboy will do many checks when receiving the +request already, do we need more? Note that this only +applies to the request-line and headers of the request, +and not the body. Implement `malformed_request`. + +May there be a request body? Will I know its size? +What's the maximum size of the request body I'm willing +to accept? Implement `valid_entity_length`. + +Finally, take a look at the sections corresponding to the +methods you are implementing. + +=== OPTIONS method + +Cowboy by default will send back a list of allowed methods. +Do I need to add more information to the response? Implement +the `options` method. + +=== GET and HEAD methods + +If you implement the methods GET and/or HEAD, you must +implement one `ProvideResource` callback for each +content-type returned by the `content_types_provided` +callback. + +=== PUT, POST and PATCH methods + +If you implement the methods PUT, POST and/or PATCH, +you must implement the `content_types_accepted` callback, +and one `AcceptResource` callback for each content-type +it returns. Prefix the `AcceptResource` callback names +with `from_` for clarity. For example `from_html` or +`from_json`. + +Do we want to allow the POST method to create individual +resources directly through their URI (like PUT)? Implement +the `allow_missing_post` callback. It is recommended to +explicitly use PUT in these cases instead. + +May there be conflicts when using PUT to create or replace +a resource? Do we want to make sure that two updates around +the same time are not cancelling one another? Implement the +`is_conflict` callback. + +=== DELETE methods + +If you implement the method DELETE, you must implement +the `delete_resource` callback. + +When `delete_resource` returns, is the resource completely +removed from the server, including from any caching service? +If not, and/or if the deletion is asynchronous and we have +no way of knowing it has been completed yet, implement the +`delete_completed` callback. diff --git a/doc/src/guide/resource_design.ezdoc b/doc/src/guide/resource_design.ezdoc deleted file mode 100644 index 67cb09b..0000000 --- a/doc/src/guide/resource_design.ezdoc +++ /dev/null @@ -1,220 +0,0 @@ -::: Designing a resource handler - -This chapter aims to provide you with a list of questions -you must answer in order to write a good resource handler. -It is meant to be usable as a step by step guide. - -:: The service - -Can the service become unavailable, and when it does, can -we detect it? For example database connectivity problems -may be detected early. We may also have planned outages -of all or parts of the system. Implement the -`service_available` callback. - -What HTTP methods does the service implement? Do we need -more than the standard OPTIONS, HEAD, GET, PUT, POST, -PATCH and DELETE? Are we not using one of those at all? -Implement the `known_methods` callback. - -:: Type of resource handler - -Am I writing a handler for a collection of resources, -or for a single resource? - -The semantics for each of these are quite different. -You should not mix collection and single resource in -the same handler. - -:: Collection handler - -Skip this section if you are not doing a collection. - -Is the collection hardcoded or dynamic? For example -if you use the route `/users` for the collection of -users then the collection is hardcoded; if you use -`/forums/:category` for the collection of threads -then it isn't. When the collection is hardcoded you -can safely assume the resource always exists. - -What methods should I implement? - -OPTIONS is used to get some information about the -collection. It is recommended to allow it even if you -do not implement it, as Cowboy has a default -implementation built-in. - -HEAD and GET are used to retrieve the collection. -If you allow GET, also allow HEAD as there's no extra -work required to make it work. - -POST is used to create a new resource inside the -collection. Creating a resource by using POST on -the collection is useful when resources may be -created before knowing their URI, usually because -parts of it are generated dynamically. A common -case is some kind of auto incremented integer -identifier. - -The next methods are more rarely allowed. - -PUT is used to create a new collection (when -the collection isn't hardcoded), or replace -the entire collection. - -DELETE is used to delete the entire collection. - -PATCH is used to modify the collection using -instructions given in the request body. A PATCH -operation is atomic. The PATCH operation may -be used for such things as reordering; adding, -modifying or deleting parts of the collection. - -:: Single resource handler - -Skip this section if you are doing a collection. - -What methods should I implement? - -OPTIONS is used to get some information about the -resource. It is recommended to allow it even if you -do not implement it, as Cowboy has a default -implementation built-in. - -HEAD and GET are used to retrieve the resource. -If you allow GET, also allow HEAD as there's no extra -work required to make it work. - -POST is used to update the resource. - -PUT is used to create a new resource (when it doesn't -already exist) or replace the resource. - -DELETE is used to delete the resource. - -PATCH is used to modify the resource using -instructions given in the request body. A PATCH -operation is atomic. The PATCH operation may -be used for adding, removing or modifying specific -values in the resource. - -:: The resource - -Following the above discussion, implement the -`allowed_methods` callback. - -Does the resource always exist? If it may not, implement -the `resource_exists` callback. - -Do I need to authenticate the client before they can -access the resource? What authentication mechanisms -should I provide? This may include form-based, token-based -(in the URL or a cookie), HTTP basic, HTTP digest, -SSL certificate or any other form of authentication. -Implement the `is_authorized` callback. - -Do I need fine-grained access control? How do I determine -that they are authorized access? Handle that in your -`is_authorized` callback. - -Can access to a resource be forbidden regardless of access -being authorized? A simple example of that is censorship -of a resource. Implement the `forbidden` callback. - -Is there any constraints on the length of the resource URI? -For example the URI may be used as a key in storage and may -have a limit in length. Implement `uri_too_long`. - -:: Representations - -What media types do I provide? If text based, what charsets -are provided? What languages do I provide? - -Implement the mandatory `content_types_provided`. Prefix -the callbacks with `to_` for clarity. For example `to_html` -or `to_text`. - -Implement the `languages_provided` or `charsets_provided` -callbacks if applicable. - -Is there any other header that may make the representation -of the resource vary? Implement the `variances` callback. - -Depending on your choices for caching content, you may -want to implement one or more of the `generate_etag`, -`last_modified` and `expires` callbacks. - -Do I want the user or user agent to actively choose a -representation available? Send a list of available -representations in the response body and implement -the `multiple_choices` callback. - -:: Redirections - -Do I need to keep track of what resources were deleted? -For example you may have a mechanism where moving a -resource leaves a redirect link to its new location. -Implement the `previously_existed` callback. - -Was the resource moved, and is the move temporary? If -it is explicitly temporary, for example due to maintenance, -implement the `moved_temporarily` callback. Otherwise, -implement the `moved_permanently` callback. - -:: The request - -Do we need to perform extra checks to make sure the request -is valid? Cowboy will do many checks when receiving the -request already, do we need more? Note that this only -applies to the request-line and headers of the request, -and not the body. Implement `malformed_request`. - -May there be a request body? Will I know its size? -What's the maximum size of the request body I'm willing -to accept? Implement `valid_entity_length`. - -Finally, take a look at the sections corresponding to the -methods you are implementing. - -:: OPTIONS method - -Cowboy by default will send back a list of allowed methods. -Do I need to add more information to the response? Implement -the `options` method. - -:: GET and HEAD methods - -If you implement the methods GET and/or HEAD, you must -implement one `ProvideResource` callback for each -content-type returned by the `content_types_provided` -callback. - -:: PUT, POST and PATCH methods - -If you implement the methods PUT, POST and/or PATCH, -you must implement the `content_types_accepted` callback, -and one `AcceptResource` callback for each content-type -it returns. Prefix the `AcceptResource` callback names -with `from_` for clarity. For example `from_html` or -`from_json`. - -Do we want to allow the POST method to create individual -resources directly through their URI (like PUT)? Implement -the `allow_missing_post` callback. It is recommended to -explicitly use PUT in these cases instead. - -May there be conflicts when using PUT to create or replace -a resource? Do we want to make sure that two updates around -the same time are not cancelling one another? Implement the -`is_conflict` callback. - -:: DELETE methods - -If you implement the method DELETE, you must implement -the `delete_resource` callback. - -When `delete_resource` returns, is the resource completely -removed from the server, including from any caching service? -If not, and/or if the deletion is asynchronous and we have -no way of knowing it has been completed yet, implement the -`delete_completed` callback. diff --git a/doc/src/guide/resp.asciidoc b/doc/src/guide/resp.asciidoc new file mode 100644 index 0000000..1ffdfbd --- /dev/null +++ b/doc/src/guide/resp.asciidoc @@ -0,0 +1,201 @@ +[[resp]] +== Sending a response + +The Req object also allows you to send a response. + +You can only send one response. Any other attempt will +trigger a crash. The response may be sent in one go or +with its body streamed by chunks of arbitrary size. + +You can also set headers or the response body in advance +and Cowboy will use them when you finally do reply. + +=== Reply + +You can send a reply with no particular headers or body. +Cowboy will make sure to send the mandatory headers with +the response. + +[source,erlang] +Req2 = cowboy_req:reply(200, Req). + +You can define headers to be sent with the response. Note +that header names must be lowercase. Again, Cowboy will +make sure to send the mandatory headers with the response. + +[source,erlang] +---- +Req2 = cowboy_req:reply(303, [ + {<<"location">>, <<"http://ninenines.eu">>} +], Req). +---- + +You can override headers that Cowboy would send otherwise. +Any header set by the user will be used over the ones set +by Cowboy. For example, you can advertise yourself as a +different server. + +[source,erlang] +---- +Req2 = cowboy_req:reply(200, [ + {<<"server">>, <<"yaws">>} +], Req). +---- + +We also saw earlier how to force close the connection by +overriding the connection header. + +Finally, you can also send a body with the response. Cowboy +will automatically set the content-length header if you do. +We recommend that you set the content-type header so the +client may know how to read the body. + +[source,erlang] +---- +Req2 = cowboy_req:reply(200, [ + {<<"content-type">>, <<"text/plain">>} +], "Hello world!", Req). +---- + +Here is the same example but sending HTML this time. + +[source,erlang] +---- +Req2 = cowboy_req:reply(200, [ + {<<"content-type">>, <<"text/html">>} +], "Hello world!

Hats off!

", Req). +---- + +Note that the reply is sent immediately. + +=== Chunked reply + +You can also stream the response body. First, you need to +initiate the reply by sending the response status code. +Then you can send the body in chunks of arbitrary size. + +[source,erlang] +Req2 = cowboy_req:chunked_reply(200, Req), +cowboy_req:chunk("Hello...", Req2), +cowboy_req:chunk("chunked...", Req2), +cowboy_req:chunk("world!!", Req2). + +You should make sure to match on `ok` as an error may be +returned. + +While it is possible to send a chunked response without +a content-type header, it is still recommended. You can +set this header or any other just like for normal replies. + +[source,erlang] +---- +Req2 = cowboy_req:chunked_reply(200, [ + {<<"content-type">>, <<"text/html">>} +], Req), +cowboy_req:chunk("Hello world!", Req2), +cowboy_req:chunk("

Hats off!

", Req2). +---- + +Note that the reply and each chunk following it are sent +immediately. + +=== Preset response headers + +You can define response headers in advance. They will be +merged into the headers given in the reply call. Headers +in the reply call override preset response headers which +override the default Cowboy headers. + +[source,erlang] +Req2 = cowboy_req:set_resp_header(<<"allow">>, "GET", Req). + +You can check if a response header has already been set. +This will only check the response headers that you set, +and not the ones Cowboy will add when actually sending +the reply. + +[source,erlang] +cowboy_req:has_resp_header(<<"allow">>, Req). + +It will return `true` if the header is defined, and `false` +otherwise. + +Finally, you can also delete a preset response header if +needed. If you do, it will not be sent. + +[source,erlang] +Req2 = cowboy_req:delete_resp_header(<<"allow">>, Req). + +=== Preset response body + +You can set the response body in advance. Note that this +body will be ignored if you then choose to send a chunked +reply, or if you send a reply with an explicit body. + +[source,erlang] +Req2 = cowboy_req:set_resp_body("Hello world!", Req). + +You can also set a fun that will be called when it is time +to send the body. There are three different ways of doing +that. + +If you know the length of the body that needs to be sent, +you should specify it, as it will help clients determine +the remaining download time and allow them to inform the +user. + +[source,erlang] +---- +F = fun (Socket, Transport) -> + Transport:send(Socket, "Hello world!") +end, +Req2 = cowboy_req:set_resp_body_fun(12, F, Req). +---- + +If you do not know the length of the body, you should use +a chunked response body fun instead. + +[source,erlang] +---- +F = fun (SendChunk) -> + Body = lists:duplicate(random:uniform(1024, $a)), + SendChunk(Body) +end, +Req2 = cowboy_req:set_resp_body_fun(chunked, F, Req). +---- + +Finally, you can also send data on the socket directly, +without knowing the length in advance. Cowboy may be +forced to close the connection at the end of the response +though depending on the protocol capabilities. + +[source,erlang] +---- +F = fun (Socket, Transport) -> + Body = lists:duplicate(random:uniform(1024, $a)), + Transport:send(Socket, Body) +end, +Req2 = cowboy_req:set_resp_body_fun(F, Req). +---- + +=== Sending files + +You can send files directly from disk without having to +read them. Cowboy will use the `sendfile` syscall when +possible, which means that the file is sent to the socket +directly from the kernel, which is a lot more performant +than doing it from userland. + +Again, it is recommended to set the size of the file if it +can be known in advance. + +[source,erlang] +---- +F = fun (Socket, Transport) -> + Transport:sendfile(Socket, "priv/styles.css") +end, +Req2 = cowboy_req:set_resp_body_fun(FileSize, F, Req). +---- + +Please see the Ranch guide for more information about +sending files. diff --git a/doc/src/guide/resp.ezdoc b/doc/src/guide/resp.ezdoc deleted file mode 100644 index 009756a..0000000 --- a/doc/src/guide/resp.ezdoc +++ /dev/null @@ -1,197 +0,0 @@ -::: Sending a response - -The Req object also allows you to send a response. - -You can only send one response. Any other attempt will -trigger a crash. The response may be sent in one go or -with its body streamed by chunks of arbitrary size. - -You can also set headers or the response body in advance -and Cowboy will use them when you finally do reply. - -:: Reply - -You can send a reply with no particular headers or body. -Cowboy will make sure to send the mandatory headers with -the response. - -``` erlang -Req2 = cowboy_req:reply(200, Req). -``` - -You can define headers to be sent with the response. Note -that header names must be lowercase. Again, Cowboy will -make sure to send the mandatory headers with the response. - -``` erlang -Req2 = cowboy_req:reply(303, [ - {<<"location">>, <<"http://ninenines.eu">>} -], Req). -``` - -You can override headers that Cowboy would send otherwise. -Any header set by the user will be used over the ones set -by Cowboy. For example, you can advertise yourself as a -different server. - -``` erlang -Req2 = cowboy_req:reply(200, [ - {<<"server">>, <<"yaws">>} -], Req). -``` - -We also saw earlier how to force close the connection by -overriding the connection header. - -Finally, you can also send a body with the response. Cowboy -will automatically set the content-length header if you do. -We recommend that you set the content-type header so the -client may know how to read the body. - -``` erlang -Req2 = cowboy_req:reply(200, [ - {<<"content-type">>, <<"text/plain">>} -], "Hello world!", Req). -``` - -Here is the same example but sending HTML this time. - -``` erlang -Req2 = cowboy_req:reply(200, [ - {<<"content-type">>, <<"text/html">>} -], "Hello world!

Hats off!

", Req). -``` - -Note that the reply is sent immediately. - -:: Chunked reply - -You can also stream the response body. First, you need to -initiate the reply by sending the response status code. -Then you can send the body in chunks of arbitrary size. - -``` erlang -Req2 = cowboy_req:chunked_reply(200, Req), -cowboy_req:chunk("Hello...", Req2), -cowboy_req:chunk("chunked...", Req2), -cowboy_req:chunk("world!!", Req2). -``` - -You should make sure to match on `ok` as an error may be -returned. - -While it is possible to send a chunked response without -a content-type header, it is still recommended. You can -set this header or any other just like for normal replies. - -``` erlang -Req2 = cowboy_req:chunked_reply(200, [ - {<<"content-type">>, <<"text/html">>} -], Req), -cowboy_req:chunk("Hello world!", Req2), -cowboy_req:chunk("

Hats off!

", Req2). -``` - -Note that the reply and each chunk following it are sent -immediately. - -:: Preset response headers - -You can define response headers in advance. They will be -merged into the headers given in the reply call. Headers -in the reply call override preset response headers which -override the default Cowboy headers. - -``` erlang -Req2 = cowboy_req:set_resp_header(<<"allow">>, "GET", Req). -``` - -You can check if a response header has already been set. -This will only check the response headers that you set, -and not the ones Cowboy will add when actually sending -the reply. - -``` erlang -cowboy_req:has_resp_header(<<"allow">>, Req). -``` - -It will return `true` if the header is defined, and `false` -otherwise. - -Finally, you can also delete a preset response header if -needed. If you do, it will not be sent. - -``` erlang -Req2 = cowboy_req:delete_resp_header(<<"allow">>, Req). -``` - -:: Preset response body - -You can set the response body in advance. Note that this -body will be ignored if you then choose to send a chunked -reply, or if you send a reply with an explicit body. - -``` erlang -Req2 = cowboy_req:set_resp_body("Hello world!", Req). -``` - -You can also set a fun that will be called when it is time -to send the body. There are three different ways of doing -that. - -If you know the length of the body that needs to be sent, -you should specify it, as it will help clients determine -the remaining download time and allow them to inform the -user. - -``` erlang -F = fun (Socket, Transport) -> - Transport:send(Socket, "Hello world!") -end, -Req2 = cowboy_req:set_resp_body_fun(12, F, Req). -``` - -If you do not know the length of the body, you should use -a chunked response body fun instead. - -``` erlang -F = fun (SendChunk) -> - Body = lists:duplicate(random:uniform(1024, $a)), - SendChunk(Body) -end, -Req2 = cowboy_req:set_resp_body_fun(chunked, F, Req). -``` - -Finally, you can also send data on the socket directly, -without knowing the length in advance. Cowboy may be -forced to close the connection at the end of the response -though depending on the protocol capabilities. - -``` erlang -F = fun (Socket, Transport) -> - Body = lists:duplicate(random:uniform(1024, $a)), - Transport:send(Socket, Body) -end, -Req2 = cowboy_req:set_resp_body_fun(F, Req). -``` - -:: Sending files - -You can send files directly from disk without having to -read them. Cowboy will use the `sendfile` syscall when -possible, which means that the file is sent to the socket -directly from the kernel, which is a lot more performant -than doing it from userland. - -Again, it is recommended to set the size of the file if it -can be known in advance. - -``` erlang -F = fun (Socket, Transport) -> - Transport:sendfile(Socket, "priv/styles.css") -end, -Req2 = cowboy_req:set_resp_body_fun(FileSize, F, Req). -``` - -Please see the Ranch guide for more information about -sending files. diff --git a/doc/src/guide/rest_flowcharts.asciidoc b/doc/src/guide/rest_flowcharts.asciidoc new file mode 100644 index 0000000..b569782 --- /dev/null +++ b/doc/src/guide/rest_flowcharts.asciidoc @@ -0,0 +1,248 @@ +[[rest_flowcharts]] +== REST flowcharts + +This chapter will explain the REST handler state machine through +a number of different diagrams. + +There are four main paths that requests may follow. One for the +method OPTIONS; one for the methods GET and HEAD; one for the +methods PUT, POST and PATCH; and one for the method DELETE. + +All paths start with the "Start" diagram, and all paths excluding +the OPTIONS path go through the "Content negotiation" diagram +and optionally the "Conditional requests" diagram if the resource +exists. + +The red squares refer to another diagram. The light green squares +indicate a response. Other squares may be either a callback or a +question answered by Cowboy itself. Green arrows tend to indicate +the default behavior if the callback is undefined. + +=== Start + +All requests start from here. + +image::rest_start.png[REST starting flowchart] + +A series of callbacks are called in succession to perform +a general checkup of the service, the request line and +request headers. + +The request body, if any, is not expected to have been +received for any of these steps. It is only processed +at the end of the "PUT, POST and PATCH methods" diagram, +when all conditions have been met. + +The `known_methods` and `allowed_methods` callbacks +return a list of methods. Cowboy then checks if the request +method is in the list, and stops otherwise. + +The `is_authorized` callback may be used to check that +access to the resource is authorized. Authentication +may also be performed as needed. When authorization is +denied, the return value from the callback must include +a challenge applicable to the requested resource, which +will be sent back to the client in the www-authenticate +header. + +This diagram is immediately followed by either the +"OPTIONS method" diagram when the request method is +OPTIONS, or the "Content negotiation" diagram otherwise. + +=== OPTIONS method + +This diagram only applies to OPTIONS requests. + +image::rest_options.png[REST OPTIONS method flowchart] + +The `options` callback may be used to add information +about the resource, such as media types or languages +provided; allowed methods; any extra information. A +response body may also be set, although clients should +not be expected to read it. + +If the `options` callback is not defined, Cowboy will +send a response containing the list of allowed methods +by default. + +=== Content negotiation + +This diagram applies to all request methods other than +OPTIONS. It is executed right after the "Start" diagram +is completed. + +image::rest_conneg.png[REST content negotiation flowchart] + +The purpose of these steps is to determine an appropriate +representation to be sent back to the client. + +The request may contain any of the accept header; the +accept-language header; or the accept-charset header. +When present, Cowboy will parse the headers and then +call the corresponding callback to obtain the list +of provided content-type, language or charset for this +resource. It then automatically select the best match +based on the request. + +If a callback is not defined, Cowboy will select the +content-type, language or charset that the client +prefers. + +The `content_types_provided` also returns the name of +a callback for every content-type it accepts. This +callback will only be called at the end of the +"GET and HEAD methods" diagram, when all conditions +have been met. + +The selected content-type, language and charset are +saved as meta values in the Req object. You *should* +use the appropriate representation if you set a +response body manually (alongside an error code, +for example). + +This diagram is immediately followed by +the "GET and HEAD methods" diagram, +the "PUT, POST and PATCH methods" diagram, +or the "DELETE method" diagram, depending on the +method. + +=== GET and HEAD methods + +This diagram only applies to GET and HEAD requests. + +For a description of the `cond` step, please see +the "Conditional requests" diagram. + +image::rest_get_head.png[REST GET/HEAD methods flowchart] + +When the resource exists, and the conditional steps +succeed, the resource can be retrieved. + +Cowboy prepares the response by first retrieving +metadata about the representation, then by calling +the `ProvideResource` callback. This is the callback +you defined for each content-types you returned from +`content_types_provided`. This callback returns the body +that will be sent back to the client, or a fun if the +body must be streamed. + +When the resource does not exist, Cowboy will figure out +whether the resource existed previously, and if so whether +it was moved elsewhere in order to redirect the client to +the new URI. + +The `moved_permanently` and `moved_temporarily` callbacks +must return the new location of the resource if it was in +fact moved. + +=== PUT, POST and PATCH methods + +This diagram only applies to PUT, POST and PATCH requests. + +For a description of the `cond` step, please see +the "Conditional requests" diagram. + +image::rest_put_post_patch.png[REST PUT/POST/PATCH methods flowchart] + +When the resource exists, first the conditional steps +are executed. When that succeeds, and the method is PUT, +Cowboy will call the `is_conflict` callback. This function +can be used to prevent potential race conditions, by locking +the resource for example. + +Then all three methods reach the `content_types_accepted` +step that we will describe in a few paragraphs. + +When the resource does not exist, and the method is PUT, +Cowboy will check for conflicts and then move on to the +`content_types_accepted` step. For other methods, Cowboy +will figure out whether the resource existed previously, +and if so whether it was moved elsewhere. If the resource +is truly non-existent, the method is POST and the call +for `allow_missing_post` returns `true`, then Cowboy will +move on to the `content_types_accepted` step. Otherwise +the request processing ends there. + +The `moved_permanently` and `moved_temporarily` callbacks +must return the new location of the resource if it was in +fact moved. + +The `content_types_accepted` returns a list of +content-types it accepts, but also the name of a callback +for each of them. Cowboy will select the appropriate +callback for processing the request body and call it. + +This callback may return one of three different return +values. + +If an error occurred while processing the request body, +it must return `false` and Cowboy will send an +appropriate error response. + +If the method is POST, then you may return `true` with +an URI of where the resource has been created. This is +especially useful for writing handlers for collections. + +Otherwise, return `true` to indicate success. Cowboy +will select the appropriate response to be sent depending +on whether a resource has been created, rather than +modified, and on the availability of a location header +or a body in the response. + +=== DELETE method + +This diagram only applies to DELETE requests. + +For a description of the `cond` step, please see +the "Conditional requests" diagram. + +image::rest_delete.png[REST DELETE method flowchart] + +When the resource exists, and the conditional steps +succeed, the resource can be deleted. + +Deleting the resource is a two steps process. First +the callback `delete_resource` is executed. Use this +callback to delete the resource. + +Because the resource may be cached, you must also +delete all cached representations of this resource +in the system. This operation may take a while though, +so you may return before it finished. + +Cowboy will then call the `delete_completed` callback. +If you know that the resource has been completely +deleted from your system, including from caches, then +you can return `true`. If any doubts persist, return +`false`. Cowboy will assume `true` by default. + +To finish, Cowboy checks if you set a response body, +and depending on that, sends the appropriate response. + +When the resource does not exist, Cowboy will figure out +whether the resource existed previously, and if so whether +it was moved elsewhere in order to redirect the client to +the new URI. + +The `moved_permanently` and `moved_temporarily` callbacks +must return the new location of the resource if it was in +fact moved. + +=== Conditional requests + +This diagram applies to all request methods other than +OPTIONS. It is executed right after the `resource_exists` +callback, when the resource exists. + +image::rest_cond.png[REST conditional requests flowchart] + +A request becomes conditional when it includes either of +the if-match header; the if-unmodified-since header; the +if-none-match header; or the if-modified-since header. + +If the condition fails, the request ends immediately +without any retrieval or modification of the resource. + +The `generate_etag` and `last_modified` are called as +needed. Cowboy will only call them once and then cache +the results for subsequent use. diff --git a/doc/src/guide/rest_flowcharts.ezdoc b/doc/src/guide/rest_flowcharts.ezdoc deleted file mode 100644 index cee9cf0..0000000 --- a/doc/src/guide/rest_flowcharts.ezdoc +++ /dev/null @@ -1,247 +0,0 @@ -::: REST flowcharts - -This chapter will explain the REST handler state machine through -a number of different diagrams. - -There are four main paths that requests may follow. One for the -method OPTIONS; one for the methods GET and HEAD; one for the -methods PUT, POST and PATCH; and one for the method DELETE. - -All paths start with the "Start" diagram, and all paths excluding -the OPTIONS path go through the "Content negotiation" diagram -and optionally the "Conditional requests" diagram if the resource -exists. - -The red squares refer to another diagram. The light green squares -indicate a response. Other squares may be either a callback or a -question answered by Cowboy itself. Green arrows tend to indicate -the default behavior if the callback is undefined. - -:: Start - -All requests start from here. - -^"REST starting flowchart^!rest_start.png - -A series of callbacks are called in succession to perform -a general checkup of the service, the request line and -request headers. - -The request body, if any, is not expected to have been -received for any of these steps. It is only processed -at the end of the "PUT, POST and PATCH methods" diagram, -when all conditions have been met. - -The `known_methods` and `allowed_methods` callbacks -return a list of methods. Cowboy then checks if the request -method is in the list, and stops otherwise. - -The `is_authorized` callback may be used to check that -access to the resource is authorized. Authentication -may also be performed as needed. When authorization is -denied, the return value from the callback must include -a challenge applicable to the requested resource, which -will be sent back to the client in the www-authenticate -header. - -This diagram is immediately followed by either the -"OPTIONS method" diagram when the request method is -OPTIONS, or the "Content negotiation" diagram otherwise. - -:: OPTIONS method - -This diagram only applies to OPTIONS requests. - -^"REST OPTIONS method flowchart^!rest_options.png - -The `options` callback may be used to add information -about the resource, such as media types or languages -provided; allowed methods; any extra information. A -response body may also be set, although clients should -not be expected to read it. - -If the `options` callback is not defined, Cowboy will -send a response containing the list of allowed methods -by default. - -:: Content negotiation - -This diagram applies to all request methods other than -OPTIONS. It is executed right after the "Start" diagram -is completed. - -^"REST content negotiation flowchart^!rest_conneg.png - -The purpose of these steps is to determine an appropriate -representation to be sent back to the client. - -The request may contain any of the accept header; the -accept-language header; or the accept-charset header. -When present, Cowboy will parse the headers and then -call the corresponding callback to obtain the list -of provided content-type, language or charset for this -resource. It then automatically select the best match -based on the request. - -If a callback is not defined, Cowboy will select the -content-type, language or charset that the client -prefers. - -The `content_types_provided` also returns the name of -a callback for every content-type it accepts. This -callback will only be called at the end of the -"GET and HEAD methods" diagram, when all conditions -have been met. - -The selected content-type, language and charset are -saved as meta values in the Req object. You *should* -use the appropriate representation if you set a -response body manually (alongside an error code, -for example). - -This diagram is immediately followed by -the "GET and HEAD methods" diagram, -the "PUT, POST and PATCH methods" diagram, -or the "DELETE method" diagram, depending on the -method. - -:: GET and HEAD methods - -This diagram only applies to GET and HEAD requests. - -For a description of the `cond` step, please see -the "Conditional requests" diagram. - -^"REST GET/HEAD methods flowchart^!rest_get_head.png - -When the resource exists, and the conditional steps -succeed, the resource can be retrieved. - -Cowboy prepares the response by first retrieving -metadata about the representation, then by calling -the `ProvideResource` callback. This is the callback -you defined for each content-types you returned from -`content_types_provided`. This callback returns the body -that will be sent back to the client, or a fun if the -body must be streamed. - -When the resource does not exist, Cowboy will figure out -whether the resource existed previously, and if so whether -it was moved elsewhere in order to redirect the client to -the new URI. - -The `moved_permanently` and `moved_temporarily` callbacks -must return the new location of the resource if it was in -fact moved. - -:: PUT, POST and PATCH methods - -This diagram only applies to PUT, POST and PATCH requests. - -For a description of the `cond` step, please see -the "Conditional requests" diagram. - -^"REST PUT/POST/PATCH methods flowchart^!rest_put_post_patch.png - -When the resource exists, first the conditional steps -are executed. When that succeeds, and the method is PUT, -Cowboy will call the `is_conflict` callback. This function -can be used to prevent potential race conditions, by locking -the resource for example. - -Then all three methods reach the `content_types_accepted` -step that we will describe in a few paragraphs. - -When the resource does not exist, and the method is PUT, -Cowboy will check for conflicts and then move on to the -`content_types_accepted` step. For other methods, Cowboy -will figure out whether the resource existed previously, -and if so whether it was moved elsewhere. If the resource -is truly non-existent, the method is POST and the call -for `allow_missing_post` returns `true`, then Cowboy will -move on to the `content_types_accepted` step. Otherwise -the request processing ends there. - -The `moved_permanently` and `moved_temporarily` callbacks -must return the new location of the resource if it was in -fact moved. - -The `content_types_accepted` returns a list of -content-types it accepts, but also the name of a callback -for each of them. Cowboy will select the appropriate -callback for processing the request body and call it. - -This callback may return one of three different return -values. - -If an error occurred while processing the request body, -it must return `false` and Cowboy will send an -appropriate error response. - -If the method is POST, then you may return `true` with -an URI of where the resource has been created. This is -especially useful for writing handlers for collections. - -Otherwise, return `true` to indicate success. Cowboy -will select the appropriate response to be sent depending -on whether a resource has been created, rather than -modified, and on the availability of a location header -or a body in the response. - -:: DELETE method - -This diagram only applies to DELETE requests. - -For a description of the `cond` step, please see -the "Conditional requests" diagram. - -^"REST DELETE method flowchart^!rest_delete.png - -When the resource exists, and the conditional steps -succeed, the resource can be deleted. - -Deleting the resource is a two steps process. First -the callback `delete_resource` is executed. Use this -callback to delete the resource. - -Because the resource may be cached, you must also -delete all cached representations of this resource -in the system. This operation may take a while though, -so you may return before it finished. - -Cowboy will then call the `delete_completed` callback. -If you know that the resource has been completely -deleted from your system, including from caches, then -you can return `true`. If any doubts persist, return -`false`. Cowboy will assume `true` by default. - -To finish, Cowboy checks if you set a response body, -and depending on that, sends the appropriate response. - -When the resource does not exist, Cowboy will figure out -whether the resource existed previously, and if so whether -it was moved elsewhere in order to redirect the client to -the new URI. - -The `moved_permanently` and `moved_temporarily` callbacks -must return the new location of the resource if it was in -fact moved. - -:: Conditional requests - -This diagram applies to all request methods other than -OPTIONS. It is executed right after the `resource_exists` -callback, when the resource exists. - -^"REST conditional requests flowchart^!rest_cond.png - -A request becomes conditional when it includes either of -the if-match header; the if-unmodified-since header; the -if-none-match header; or the if-modified-since header. - -If the condition fails, the request ends immediately -without any retrieval or modification of the resource. - -The `generate_etag` and `last_modified` are called as -needed. Cowboy will only call them once and then cache -the results for subsequent use. diff --git a/doc/src/guide/rest_handlers.asciidoc b/doc/src/guide/rest_handlers.asciidoc new file mode 100644 index 0000000..6bff18d --- /dev/null +++ b/doc/src/guide/rest_handlers.asciidoc @@ -0,0 +1,133 @@ +[[rest_handlers]] +== REST handlers + +REST is implemented in Cowboy as a sub protocol. The request +is handled as a state machine with many optional callbacks +describing the resource and modifying the machine's behavior. + +The REST handler is the recommended way to handle HTTP requests. + +=== Initialization + +First, the `init/2` callback is called. This callback is common +to all handlers. To use REST for the current request, this function +must return a `cowboy_rest` tuple. + +[source,erlang] +---- +init(Req, _Opts) -> + {cowboy_rest, Req, #state{}}. +---- + +Cowboy will then switch to the REST protocol and start executing +the state machine. + +After reaching the end of the flowchart, the `terminate/3` callback +will be called if it is defined. + +=== Methods + +The REST component has code for handling the following HTTP methods: +HEAD, GET, POST, PATCH, PUT, DELETE and OPTIONS. + +Other methods can be accepted, however they have no specific callback +defined for them at this time. + +=== Callbacks + +All callbacks are optional. Some may become mandatory depending +on what other defined callbacks return. The various flowcharts +in the next chapter should be a useful to determine which callbacks +you need. + +All callbacks take two arguments, the Req object and the State, +and return a three-element tuple of the form `{Value, Req, State}`. + +All callbacks can also return `{stop, Req, State}` to stop execution +of the request. + +The following table summarizes the callbacks and their default values. +If the callback isn't defined, then the default value will be used. +Please look at the flowcharts to find out the result of each return +value. + +In the following table, "skip" means the callback is entirely skipped +if it is undefined, moving directly to the next step. Similarly, +"none" means there is no default value for this callback. + +[cols="<,^",options="header"] +|=== +| Callback name | Default value +| allowed_methods | `[<<"GET">>, <<"HEAD">>, <<"OPTIONS">>]` +| allow_missing_post | `true` +| charsets_provided | skip +| content_types_accepted | none +| content_types_provided | `$$[{{<<"text">>, <<"html">>, '*'}, to_html}]$$` +| delete_completed | `true` +| delete_resource | `false` +| expires | `undefined` +| forbidden | `false` +| generate_etag | `undefined` +| is_authorized | `true` +| is_conflict | `false` +| known_methods | `[<<"GET">>, <<"HEAD">>, <<"POST">>, <<"PUT">>, <<"PATCH">>, <<"DELETE">>, <<"OPTIONS">>]` +| languages_provided | skip +| last_modified | `undefined` +| malformed_request | `false` +| moved_permanently | `false` +| moved_temporarily | `false` +| multiple_choices | `false` +| options | `ok` +| previously_existed | `false` +| resource_exists | `true` +| service_available | `true` +| uri_too_long | `false` +| valid_content_headers | `true` +| valid_entity_length | `true` +| variances | `[]` +|=== + +As you can see, Cowboy tries to move on with the request whenever +possible by using well thought out default values. + +In addition to these, there can be any number of user-defined +callbacks that are specified through `content_types_accepted/2` +and `content_types_provided/2`. They can take any name, however +it is recommended to use a separate prefix for the callbacks of +each function. For example, `from_html` and `to_html` indicate +in the first case that we're accepting a resource given as HTML, +and in the second case that we send one as HTML. + +=== Meta data + +Cowboy will set informative meta values at various points of the +execution. You can retrieve them using `cowboy_req:meta/{2,3}`. +The values are defined in the following table. + +[cols="<,<",options="header"] +|=== +| Meta key | Details +| media_type | The content-type negotiated for the response entity. +| language | The language negotiated for the response entity. +| charset | The charset negotiated for the response entity. +|=== + +They can be used to send a proper body with the response to a +request that used a method other than HEAD or GET. + +=== Response headers + +Cowboy will set response headers automatically over the execution +of the REST code. They are listed in the following table. + +[cols="<,<",options="header"] +|=== +| Header name | Details +| content-language | Language used in the response body +| content-type | Media type and charset of the response body +| etag | Etag of the resource +| expires | Expiration date of the resource +| last-modified | Last modification date for the resource +| location | Relative or absolute URI to the requested resource +| vary | List of headers that may change the representation of the resource +|=== diff --git a/doc/src/guide/rest_handlers.ezdoc b/doc/src/guide/rest_handlers.ezdoc deleted file mode 100644 index e6bb092..0000000 --- a/doc/src/guide/rest_handlers.ezdoc +++ /dev/null @@ -1,125 +0,0 @@ -::: REST handlers - -REST is implemented in Cowboy as a sub protocol. The request -is handled as a state machine with many optional callbacks -describing the resource and modifying the machine's behavior. - -The REST handler is the recommended way to handle HTTP requests. - -:: Initialization - -First, the `init/2` callback is called. This callback is common -to all handlers. To use REST for the current request, this function -must return a `cowboy_rest` tuple. - -``` erlang -init(Req, _Opts) -> - {cowboy_rest, Req, #state{}}. -``` - -Cowboy will then switch to the REST protocol and start executing -the state machine. - -After reaching the end of the flowchart, the `terminate/3` callback -will be called if it is defined. - -:: Methods - -The REST component has code for handling the following HTTP methods: -HEAD, GET, POST, PATCH, PUT, DELETE and OPTIONS. - -Other methods can be accepted, however they have no specific callback -defined for them at this time. - -:: Callbacks - -All callbacks are optional. Some may become mandatory depending -on what other defined callbacks return. The various flowcharts -in the next chapter should be a useful to determine which callbacks -you need. - -All callbacks take two arguments, the Req object and the State, -and return a three-element tuple of the form `{Value, Req, State}`. - -All callbacks can also return `{stop, Req, State}` to stop execution -of the request. - -The following table summarizes the callbacks and their default values. -If the callback isn't defined, then the default value will be used. -Please look at the flowcharts to find out the result of each return -value. - -In the following table, "skip" means the callback is entirely skipped -if it is undefined, moving directly to the next step. Similarly, -"none" means there is no default value for this callback. - -|| Callback name Default value -| -| allowed_methods `[<<"GET">>, <<"HEAD">>, <<"OPTIONS">>]` -| allow_missing_post `true` -| charsets_provided skip -| content_types_accepted none -| content_types_provided `[{{<<"text">>, <<"html">>, '*'}, to_html}] ` -| delete_completed `true` -| delete_resource `false` -| expires `undefined` -| forbidden `false` -| generate_etag `undefined` -| is_authorized `true` -| is_conflict `false` -| known_methods `[<<"GET">>, <<"HEAD">>, <<"POST">>, <<"PUT">>, <<"PATCH">>, <<"DELETE">>, <<"OPTIONS">>]` -| languages_provided skip -| last_modified `undefined` -| malformed_request `false` -| moved_permanently `false` -| moved_temporarily `false` -| multiple_choices `false` -| options `ok` -| previously_existed `false` -| resource_exists `true` -| service_available `true` -| uri_too_long `false` -| valid_content_headers `true` -| valid_entity_length `true` -| variances `[]` - -As you can see, Cowboy tries to move on with the request whenever -possible by using well thought out default values. - -In addition to these, there can be any number of user-defined -callbacks that are specified through `content_types_accepted/2` -and `content_types_provided/2`. They can take any name, however -it is recommended to use a separate prefix for the callbacks of -each function. For example, `from_html` and `to_html` indicate -in the first case that we're accepting a resource given as HTML, -and in the second case that we send one as HTML. - -:: Meta data - -Cowboy will set informative meta values at various points of the -execution. You can retrieve them using `cowboy_req:meta/{2,3}`. -The values are defined in the following table. - -|| Meta key Details -| -| media_type The content-type negotiated for the response entity. -| language The language negotiated for the response entity. -| charset The charset negotiated for the response entity. - -They can be used to send a proper body with the response to a -request that used a method other than HEAD or GET. - -:: Response headers - -Cowboy will set response headers automatically over the execution -of the REST code. They are listed in the following table. - -|| Header name Details -| -| content-language Language used in the response body -| content-type Media type and charset of the response body -| etag Etag of the resource -| expires Expiration date of the resource -| last-modified Last modification date for the resource -| location Relative or absolute URI to the requested resource -| vary List of headers that may change the representation of the resource diff --git a/doc/src/guide/rest_principles.asciidoc b/doc/src/guide/rest_principles.asciidoc new file mode 100644 index 0000000..6ae2063 --- /dev/null +++ b/doc/src/guide/rest_principles.asciidoc @@ -0,0 +1,160 @@ +[[rest_principles]] +== REST principles + +This chapter will attempt to define the concepts behind REST +and explain what makes a service RESTful. + +REST is often confused with performing a distinct operation +depending on the HTTP method, while using more than the GET +and POST methods. That's highly misguided at best. + +We will first attempt to define REST and will look at what +it means in the context of HTTP and the Web. +For a more in-depth explanation of REST, you can read +http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm[Roy T. Fielding's dissertation] +as it does a great job explaining where it comes from and +what it achieves. + +=== REST architecture + +REST is a *client-server* architecture. The client and the server +both have a different set of concerns. The server stores and/or +manipulates information and makes it available to the user in +an efficient manner. The client takes that information and +displays it to the user and/or uses it to perform subsequent +requests for information. This separation of concerns allows both +the client and the server to evolve independently as it only +requires that the interface stays the same. + +REST is *stateless*. That means the communication between the +client and the server always contains all the information needed +to perform the request. There is no session state in the server, +it is kept entirely on the client's side. If access to a resource +requires authentication, then the client needs to authenticate +itself with every request. + +REST is *cacheable*. The client, the server and any intermediary +components can all cache resources in order to improve performance. + +REST provides a *uniform interface* between components. This +simplifies the architecture, as all components follow the same +rules to speak to one another. It also makes it easier to understand +the interactions between the different components of the system. +A number of constraints are required to achieve this. They are +covered in the rest of the chapter. + +REST is a *layered system*. Individual components cannot see +beyond the immediate layer with which they are interacting. This +means that a client connecting to an intermediate component, like +a proxy, has no knowledge of what lies beyond. This allows +components to be independent and thus easily replaceable or +extendable. + +REST optionally provides *code on demand*. Code may be downloaded +to extend client functionality. This is optional however because +the client may not be able to download or run this code, and so +a REST component cannot rely on it being executed. + +=== Resources and resource identifiers + +A resource is an abstract concept. In a REST system, any information +that can be named may be a resource. This includes documents, images, +a collection of resources and any other information. Any information +that can be the target of an hypertext link can be a resource. + +A resource is a conceptual mapping to a set of entities. The set of +entities evolves over time; a resource doesn't. For example a resource +can map to "users who have logged in this past month" and another +to "all users". At some point in time they may map to the same set of +entities, because all users logged in this past month. But they are +still different resources. Similarly, if nobody logged in recently, +then the first resource may map to the empty set. This resource exists +regardless of the information it maps to. + +Resources are identified by uniform resource identifiers, also known +as URIs. Sometimes internationalized resource identifiers, or IRIs, +may also be used, but these can be directly translated into a URI. + +In practice we will identify two kinds of resources. Individual +resources map to a set of one element, for example "user Joe". +Collection of resources map to a set of 0 to N elements, +for example "all users". + +=== Resource representations + +The representation of a resource is a sequence of bytes associated +with metadata. + +The metadata comes as a list of key-value pairs, where the name +corresponds to a standard that defines the value's structure and +semantics. With HTTP, the metadata comes in the form of request +or response headers. The headers' structure and semantics are well +defined in the HTTP standard. Metadata includes representation +metadata, resource metadata and control data. + +The representation metadata gives information about the +representation, such as its media type, the date of last +modification, or even a checksum. + +Resource metadata could be link to related resources or +information about additional representations of the resource. + +Control data allows parameterizing the request or response. +For example, we may only want the representation returned if +it is more recent than the one we have in cache. Similarly, +we may want to instruct the client about how it should cache +the representation. This isn't restricted to caching. We may +for example want to store a new representation of a resource +only if it wasn't modified since we first retrieved it. + +The data format of a representation is also known as the media +type. Some media types are intended for direct rendering to the +user, while others are intended for automated processing. The +media type is a key component of the REST architecture. + +=== Self-descriptive messages + +Messages must be self-descriptive. That means that the data +format of a representation must always come with its media +type (and similarly requesting a resource involves choosing +the media type of the representation returned). If you are +sending HTML, then you must say it is HTML by sending the +media type with the representation. In HTTP this is done +using the content-type header. + +The media type is often an IANA registered media type, like +`text/html` or `image/png`, but does not need to be. Exactly +two things are important for respecting this constraint: that +the media type is well specified, and that the sender and +recipient agree about what the media type refers to. + +This means that you can create your own media types, like +`application/x-mine`, and that as long as you write the +specifications for it and that both endpoints agree about +it then the constraint is respected. + +=== Hypermedia as the engine of application state + +The last constraint is generally where services that claim +to be RESTful fail. Interactions with a server must be +entirely driven by hypermedia. The client does not need +any prior knowledge of the service in order to use it, +other than an entry point and of course basic understanding +of the media type of the representations, at the very least +enough to find and identify hyperlinks and link relations. + +To give a simple example, if your service only works with +the `application/json` media type then this constraint +cannot be respected (as there are no concept of links in +JSON) and thus your service isn't RESTful. This is the case +for the majority of self-proclaimed REST services. + +On the other hand if you create a JSON based media type +that has a concept of links and link relations, then +your service might be RESTful. + +Respecting this constraint means that the entirety of the +service becomes self-discoverable, not only the resources +in it, but also the operations you can perform on it. This +makes clients very thin as there is no need to implement +anything specific to the service to operate on it. diff --git a/doc/src/guide/rest_principles.ezdoc b/doc/src/guide/rest_principles.ezdoc deleted file mode 100644 index 1d54594..0000000 --- a/doc/src/guide/rest_principles.ezdoc +++ /dev/null @@ -1,159 +0,0 @@ -::: REST principles - -This chapter will attempt to define the concepts behind REST -and explain what makes a service RESTful. - -REST is often confused with performing a distinct operation -depending on the HTTP method, while using more than the GET -and POST methods. That's highly misguided at best. - -We will first attempt to define REST and will look at what -it means in the context of HTTP and the Web. -For a more in-depth explanation of REST, you can read -^"Roy T. Fielding's dissertation^http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm -as it does a great job explaining where it comes from and -what it achieves. - -:: REST architecture - -REST is a *client-server* architecture. The client and the server -both have a different set of concerns. The server stores and/or -manipulates information and makes it available to the user in -an efficient manner. The client takes that information and -displays it to the user and/or uses it to perform subsequent -requests for information. This separation of concerns allows both -the client and the server to evolve independently as it only -requires that the interface stays the same. - -REST is *stateless*. That means the communication between the -client and the server always contains all the information needed -to perform the request. There is no session state in the server, -it is kept entirely on the client's side. If access to a resource -requires authentication, then the client needs to authenticate -itself with every request. - -REST is *cacheable*. The client, the server and any intermediary -components can all cache resources in order to improve performance. - -REST provides a *uniform interface* between components. This -simplifies the architecture, as all components follow the same -rules to speak to one another. It also makes it easier to understand -the interactions between the different components of the system. -A number of constraints are required to achieve this. They are -covered in the rest of the chapter. - -REST is a *layered system*. Individual components cannot see -beyond the immediate layer with which they are interacting. This -means that a client connecting to an intermediate component, like -a proxy, has no knowledge of what lies beyond. This allows -components to be independent and thus easily replaceable or -extendable. - -REST optionally provides *code on demand*. Code may be downloaded -to extend client functionality. This is optional however because -the client may not be able to download or run this code, and so -a REST component cannot rely on it being executed. - -:: Resources and resource identifiers - -A resource is an abstract concept. In a REST system, any information -that can be named may be a resource. This includes documents, images, -a collection of resources and any other information. Any information -that can be the target of an hypertext link can be a resource. - -A resource is a conceptual mapping to a set of entities. The set of -entities evolves over time; a resource doesn't. For example a resource -can map to "users who have logged in this past month" and another -to "all users". At some point in time they may map to the same set of -entities, because all users logged in this past month. But they are -still different resources. Similarly, if nobody logged in recently, -then the first resource may map to the empty set. This resource exists -regardless of the information it maps to. - -Resources are identified by uniform resource identifiers, also known -as URIs. Sometimes internationalized resource identifiers, or IRIs, -may also be used, but these can be directly translated into a URI. - -In practice we will identify two kinds of resources. Individual -resources map to a set of one element, for example "user Joe". -Collection of resources map to a set of 0 to N elements, -for example "all users". - -:: Resource representations - -The representation of a resource is a sequence of bytes associated -with metadata. - -The metadata comes as a list of key-value pairs, where the name -corresponds to a standard that defines the value's structure and -semantics. With HTTP, the metadata comes in the form of request -or response headers. The headers' structure and semantics are well -defined in the HTTP standard. Metadata includes representation -metadata, resource metadata and control data. - -The representation metadata gives information about the -representation, such as its media type, the date of last -modification, or even a checksum. - -Resource metadata could be link to related resources or -information about additional representations of the resource. - -Control data allows parameterizing the request or response. -For example, we may only want the representation returned if -it is more recent than the one we have in cache. Similarly, -we may want to instruct the client about how it should cache -the representation. This isn't restricted to caching. We may -for example want to store a new representation of a resource -only if it wasn't modified since we first retrieved it. - -The data format of a representation is also known as the media -type. Some media types are intended for direct rendering to the -user, while others are intended for automated processing. The -media type is a key component of the REST architecture. - -:: Self-descriptive messages - -Messages must be self-descriptive. That means that the data -format of a representation must always come with its media -type (and similarly requesting a resource involves choosing -the media type of the representation returned). If you are -sending HTML, then you must say it is HTML by sending the -media type with the representation. In HTTP this is done -using the content-type header. - -The media type is often an IANA registered media type, like -`text/html` or `image/png`, but does not need to be. Exactly -two things are important for respecting this constraint: that -the media type is well specified, and that the sender and -recipient agree about what the media type refers to. - -This means that you can create your own media types, like -`application/x-mine`, and that as long as you write the -specifications for it and that both endpoints agree about -it then the constraint is respected. - -:: Hypermedia as the engine of application state - -The last constraint is generally where services that claim -to be RESTful fail. Interactions with a server must be -entirely driven by hypermedia. The client does not need -any prior knowledge of the service in order to use it, -other than an entry point and of course basic understanding -of the media type of the representations, at the very least -enough to find and identify hyperlinks and link relations. - -To give a simple example, if your service only works with -the `application/json` media type then this constraint -cannot be respected (as there are no concept of links in -JSON) and thus your service isn't RESTful. This is the case -for the majority of self-proclaimed REST services. - -On the other hand if you create a JSON based media type -that has a concept of links and link relations, then -your service might be RESTful. - -Respecting this constraint means that the entirety of the -service becomes self-discoverable, not only the resources -in it, but also the operations you can perform on it. This -makes clients very thin as there is no need to implement -anything specific to the service to operate on it. diff --git a/doc/src/guide/routing.asciidoc b/doc/src/guide/routing.asciidoc new file mode 100644 index 0000000..6ac2ebd --- /dev/null +++ b/doc/src/guide/routing.asciidoc @@ -0,0 +1,224 @@ +[[routing]] +== Routing + +Cowboy does nothing by default. + +To make Cowboy useful, you need to map URLs to Erlang modules that will +handle the requests. This is called routing. + +When Cowboy receives a request, it tries to match the requested host and +path to the resources given in the dispatch rules. If it matches, then +the associated Erlang code will be executed. + +Routing rules are given per host. Cowboy will first match on the host, +and then try to find a matching path. + +Routes need to be compiled before they can be used by Cowboy. + +=== Structure + +The general structure for the routes is defined as follow. + +[source,erlang] +Routes = [Host1, Host2, ... HostN]. + +Each host contains matching rules for the host along with optional +constraints, and a list of routes for the path component. + +[source,erlang] +Host1 = {HostMatch, PathsList}. +Host2 = {HostMatch, Constraints, PathsList}. + +The list of routes for the path component is defined similar to the +list of hosts. + +[source,erlang] +PathsList = [Path1, Path2, ... PathN]. + +Finally, each path contains matching rules for the path along with +optional constraints, and gives us the handler module to be used +along with options that will be given to it on initialization. + +[source,erlang] +Path1 = {PathMatch, Handler, Opts}. +Path2 = {PathMatch, Constraints, Handler, Opts}. + +Continue reading to learn more about the match syntax and the optional +constraints. + +=== Match syntax + +The match syntax is used to associate host names and paths with their +respective handlers. + +The match syntax is the same for host and path with a few subtleties. +Indeed, the segments separator is different, and the host is matched +starting from the last segment going to the first. All examples will +feature both host and path match rules and explain the differences +when encountered. + +Excluding special values that we will explain at the end of this section, +the simplest match value is a host or a path. It can be given as either +a `string()` or a `binary()`. + +[source,erlang] +---- +PathMatch1 = "/". +PathMatch2 = "/path/to/resource". + +HostMatch1 = "cowboy.example.org". +---- + +As you can see, all paths defined this way must start with a slash +character. Note that these two paths are identical as far as routing +is concerned. + +[source,erlang] +PathMatch2 = "/path/to/resource". +PathMatch3 = "/path/to/resource/". + +Hosts with and without a trailing dot are equivalent for routing. +Similarly, hosts with and without a leading dot are also equivalent. + +[source,erlang] +HostMatch1 = "cowboy.example.org". +HostMatch2 = "cowboy.example.org.". +HostMatch3 = ".cowboy.example.org". + +It is possible to extract segments of the host and path and to store +the values in the `Req` object for later use. We call these kind of +values bindings. + +The syntax for bindings is very simple. A segment that begins with +the `:` character means that what follows until the end of the segment +is the name of the binding in which the segment value will be stored. + +[source,erlang] +PathMatch = "/hats/:name/prices". +HostMatch = ":subdomain.example.org". + +If these two end up matching when routing, you will end up with two +bindings defined, `subdomain` and `name`, each containing the +segment value where they were defined. For example, the URL +`http://test.example.org/hats/wild_cowboy_legendary/prices` will +result in having the value `test` bound to the name `subdomain` +and the value `wild_cowboy_legendary` bound to the name `name`. +They can later be retrieved using `cowboy_req:binding/{2,3}`. The +binding name must be given as an atom. + +There is a special binding name you can use to mimic the underscore +variable in Erlang. Any match against the `_` binding will succeed +but the data will be discarded. This is especially useful for +matching against many domain names in one go. + +[source,erlang] +HostMatch = "ninenines.:_". + +Similarly, it is possible to have optional segments. Anything +between brackets is optional. + +[source,erlang] +PathMatch = "/hats/[page/:number]". +HostMatch = "[www.]ninenines.eu". + +You can also have imbricated optional segments. + +[source,erlang] +PathMatch = "/hats/[page/[:number]]". + +You can retrieve the rest of the host or path using `[...]`. +In the case of hosts it will match anything before, in the case +of paths anything after the previously matched segments. It is +a special case of optional segments, in that it can have +zero, one or many segments. You can then find the segments using +`cowboy_req:host_info/1` and `cowboy_req:path_info/1` respectively. +They will be represented as a list of segments. + +[source,erlang] +PathMatch = "/hats/[...]". +HostMatch = "[...]ninenines.eu". + +If a binding appears twice in the routing rules, then the match +will succeed only if they share the same value. This copies the +Erlang pattern matching behavior. + +[source,erlang] +PathMatch = "/hats/:name/:name". + +This is also true when an optional segment is present. In this +case the two values must be identical only if the segment is +available. + +[source,erlang] +PathMatch = "/hats/:name/[:name]". + +If a binding is defined in both the host and path, then they must +also share the same value. + +[source,erlang] +PathMatch = "/:user/[...]". +HostMatch = ":user.github.com". + +Finally, there are two special match values that can be used. The +first is the atom `'_'` which will match any host or path. + +[source,erlang] +PathMatch = '_'. +HostMatch = '_'. + +The second is the special host match `"*"` which will match the +wildcard path, generally used alongside the `OPTIONS` method. + +[source,erlang] +HostMatch = "*". + +=== Constraints + +After the matching has completed, the resulting bindings can be tested +against a set of constraints. Constraints are only tested when the +binding is defined. They run in the order you defined them. The match +will succeed only if they all succeed. If the match fails, then Cowboy +tries the next route in the list. + +The format used for constraints is the same as match functions in +`cowboy_req`: they are provided as a list of fields which may have +one or more constraints. While the router accepts the same format, +it will skip fields with no constraints and will also ignore default +values, if any. + +Read more about xref:constraints[constraints]. + +=== Compilation + +The structure defined in this chapter needs to be compiled before it is +passed to Cowboy. This allows Cowboy to efficiently lookup the correct +handler to run instead of having to parse the routes repeatedly. + +This can be done with a simple call to `cowboy_router:compile/1`. + +[source,erlang] +---- +Dispatch = cowboy_router:compile([ + %% {HostMatch, list({PathMatch, Handler, Opts})} + {'_', [{'_', my_handler, []}]} +]), +%% Name, NbAcceptors, TransOpts, ProtoOpts +cowboy:start_http(my_http_listener, 100, + [{port, 8080}], + [{env, [{dispatch, Dispatch}]}] +). +---- + +Note that this function will return `{error, badarg}` if the structure +given is incorrect. + +=== Live update + +You can use the `cowboy:set_env/3` function for updating the dispatch +list used by routing. This will apply to all new connections accepted +by the listener. + +[source,erlang] +cowboy:set_env(my_http_listener, dispatch, cowboy_router:compile(Dispatch)). + +Note that you need to compile the routes before updating. diff --git a/doc/src/guide/routing.ezdoc b/doc/src/guide/routing.ezdoc deleted file mode 100644 index 2482c12..0000000 --- a/doc/src/guide/routing.ezdoc +++ /dev/null @@ -1,239 +0,0 @@ -::: Routing - -Cowboy does nothing by default. - -To make Cowboy useful, you need to map URLs to Erlang modules that will -handle the requests. This is called routing. - -When Cowboy receives a request, it tries to match the requested host and -path to the resources given in the dispatch rules. If it matches, then -the associated Erlang code will be executed. - -Routing rules are given per host. Cowboy will first match on the host, -and then try to find a matching path. - -Routes need to be compiled before they can be used by Cowboy. - -:: Structure - -The general structure for the routes is defined as follow. - -``` erlang -Routes = [Host1, Host2, ... HostN]. -``` - -Each host contains matching rules for the host along with optional -constraints, and a list of routes for the path component. - -``` erlang -Host1 = {HostMatch, PathsList}. -Host2 = {HostMatch, Constraints, PathsList}. -``` - -The list of routes for the path component is defined similar to the -list of hosts. - -``` erlang -PathsList = [Path1, Path2, ... PathN]. -``` - -Finally, each path contains matching rules for the path along with -optional constraints, and gives us the handler module to be used -along with options that will be given to it on initialization. - -``` erlang -Path1 = {PathMatch, Handler, Opts}. -Path2 = {PathMatch, Constraints, Handler, Opts}. -``` - -Continue reading to learn more about the match syntax and the optional -constraints. - -:: Match syntax - -The match syntax is used to associate host names and paths with their -respective handlers. - -The match syntax is the same for host and path with a few subtleties. -Indeed, the segments separator is different, and the host is matched -starting from the last segment going to the first. All examples will -feature both host and path match rules and explain the differences -when encountered. - -Excluding special values that we will explain at the end of this section, -the simplest match value is a host or a path. It can be given as either -a `string()` or a `binary()`. - -``` erlang -PathMatch1 = "/". -PathMatch2 = "/path/to/resource". - -HostMatch1 = "cowboy.example.org". -``` - -As you can see, all paths defined this way must start with a slash -character. Note that these two paths are identical as far as routing -is concerned. - -``` erlang -PathMatch2 = "/path/to/resource". -PathMatch3 = "/path/to/resource/". -``` - -Hosts with and without a trailing dot are equivalent for routing. -Similarly, hosts with and without a leading dot are also equivalent. - -``` erlang -HostMatch1 = "cowboy.example.org". -HostMatch2 = "cowboy.example.org.". -HostMatch3 = ".cowboy.example.org". -``` - -It is possible to extract segments of the host and path and to store -the values in the `Req` object for later use. We call these kind of -values bindings. - -The syntax for bindings is very simple. A segment that begins with -the `:` character means that what follows until the end of the segment -is the name of the binding in which the segment value will be stored. - -``` erlang -PathMatch = "/hats/:name/prices". -HostMatch = ":subdomain.example.org". -``` - -If these two end up matching when routing, you will end up with two -bindings defined, `subdomain` and `name`, each containing the -segment value where they were defined. For example, the URL -`http://test.example.org/hats/wild_cowboy_legendary/prices` will -result in having the value `test` bound to the name `subdomain` -and the value `wild_cowboy_legendary` bound to the name `name`. -They can later be retrieved using `cowboy_req:binding/{2,3}`. The -binding name must be given as an atom. - -There is a special binding name you can use to mimic the underscore -variable in Erlang. Any match against the `_` binding will succeed -but the data will be discarded. This is especially useful for -matching against many domain names in one go. - -``` erlang -HostMatch = "ninenines.:_". -``` - -Similarly, it is possible to have optional segments. Anything -between brackets is optional. - -``` erlang -PathMatch = "/hats/[page/:number]". -HostMatch = "[www.]ninenines.eu". -``` - -You can also have imbricated optional segments. - -``` erlang -PathMatch = "/hats/[page/[:number]]". -``` - -You can retrieve the rest of the host or path using `[...]`. -In the case of hosts it will match anything before, in the case -of paths anything after the previously matched segments. It is -a special case of optional segments, in that it can have -zero, one or many segments. You can then find the segments using -`cowboy_req:host_info/1` and `cowboy_req:path_info/1` respectively. -They will be represented as a list of segments. - -``` erlang -PathMatch = "/hats/[...]". -HostMatch = "[...]ninenines.eu". -``` - -If a binding appears twice in the routing rules, then the match -will succeed only if they share the same value. This copies the -Erlang pattern matching behavior. - -``` erlang -PathMatch = "/hats/:name/:name". -``` - -This is also true when an optional segment is present. In this -case the two values must be identical only if the segment is -available. - -``` erlang -PathMatch = "/hats/:name/[:name]". -``` - -If a binding is defined in both the host and path, then they must -also share the same value. - -``` erlang -PathMatch = "/:user/[...]". -HostMatch = ":user.github.com". -``` - -Finally, there are two special match values that can be used. The -first is the atom `'_'` which will match any host or path. - -``` erlang -PathMatch = '_'. -HostMatch = '_'. -``` - -The second is the special host match `"*"` which will match the -wildcard path, generally used alongside the `OPTIONS` method. - -``` erlang -HostMatch = "*". -``` - -:: Constraints - -After the matching has completed, the resulting bindings can be tested -against a set of constraints. Constraints are only tested when the -binding is defined. They run in the order you defined them. The match -will succeed only if they all succeed. If the match fails, then Cowboy -tries the next route in the list. - -The format used for constraints is the same as match functions in -`cowboy_req`: they are provided as a list of fields which may have -one or more constraints. While the router accepts the same format, -it will skip fields with no constraints and will also ignore default -values, if any. - -Read more about ^constraints^. - -:: Compilation - -The structure defined in this chapter needs to be compiled before it is -passed to Cowboy. This allows Cowboy to efficiently lookup the correct -handler to run instead of having to parse the routes repeatedly. - -This can be done with a simple call to `cowboy_router:compile/1`. - -``` erlang -Dispatch = cowboy_router:compile([ - %% {HostMatch, list({PathMatch, Handler, Opts})} - {'_', [{'_', my_handler, []}]} -]), -%% Name, NbAcceptors, TransOpts, ProtoOpts -cowboy:start_http(my_http_listener, 100, - [{port, 8080}], - [{env, [{dispatch, Dispatch}]}] -). -``` - -Note that this function will return `{error, badarg}` if the structure -given is incorrect. - -:: Live update - -You can use the `cowboy:set_env/3` function for updating the dispatch -list used by routing. This will apply to all new connections accepted -by the listener. - -``` erlang -cowboy:set_env(my_http_listener, dispatch, - cowboy_router:compile(Dispatch)). -``` - -Note that you need to compile the routes before updating. diff --git a/doc/src/guide/static_files.asciidoc b/doc/src/guide/static_files.asciidoc new file mode 100644 index 0000000..39197a8 --- /dev/null +++ b/doc/src/guide/static_files.asciidoc @@ -0,0 +1,171 @@ +[[static_files]] +== Static files + +Cowboy comes with a special handler built as a REST handler +and designed specifically for serving static files. It is +provided as a convenience and provides a quick solution for +serving files during development. + +For systems in production, consider using one of the many +Content Distribution Network (CDN) available on the market, +as they are the best solution for serving files. They are +covered in the next chapter. If you decide against using a +CDN solution, then please look at the chapter after that, +as it explains how to efficiently serve static files on +your own. + +The static handler can serve either one file or all files +from a given directory. It can also send etag headers for +client-side caching. + +To use the static file handler, simply add routes for it +with the appropriate options. + +=== Serve one file + +You can use the static handler to serve one specific file +from an application's private directory. This is particularly +useful to serve an 'index.html' file when the client requests +the `/` path, for example. The path configured is relative +to the given application's private directory. + +The following rule will serve the file 'static/index.html' +from the application `my_app`'s priv directory whenever the +path `/` is accessed. + +[source,erlang] +{"/", cowboy_static, {priv_file, my_app, "static/index.html"}} + +You can also specify the absolute path to a file, or the +path to the file relative to the current directory. + +[source,erlang] +{"/", cowboy_static, {file, "/var/www/index.html"}} + +=== Serve all files from a directory + +You can also use the static handler to serve all files that +can be found in the configured directory. The handler will +use the `path_info` information to resolve the file location, +which means that your route must end with a `[...]` pattern +for it to work. All files are served, including the ones that +may be found in subfolders. + +You can specify the directory relative to an application's +private directory. + +The following rule will serve any file found in the application +`my_app`'s priv directory inside the `static/assets` folder +whenever the requested path begins with `/assets/`. + +[source,erlang] +{"/assets/[...]", cowboy_static, {priv_dir, my_app, "static/assets"}} + +You can also specify the absolute path to the directory or +set it relative to the current directory. + +[source,erlang] +{"/assets/[...]", cowboy_static, {dir, "/var/www/assets"}} + +=== Customize the mimetype detection + +By default, Cowboy will attempt to recognize the mimetype +of your static files by looking at the extension. + +You can override the function that figures out the mimetype +of the static files. It can be useful when Cowboy is missing +a mimetype you need to handle, or when you want to reduce +the list to make lookups faster. You can also give a +hard-coded mimetype that will be used unconditionally. + +Cowboy comes with two functions built-in. The default +function only handles common file types used when building +Web applications. The other function is an extensive list +of hundreds of mimetypes that should cover almost any need +you may have. You can of course create your own function. + +To use the default function, you should not have to configure +anything, as it is the default. If you insist, though, the +following will do the job. + +[source,erlang] +---- +{"/assets/[...]", cowboy_static, {priv_dir, my_app, "static/assets", + [{mimetypes, cow_mimetypes, web}]}} +---- + +As you can see, there is an optional field that may contain +a list of less used options, like mimetypes or etag. All option +types have this optional field. + +To use the function that will detect almost any mimetype, +the following configuration will do. + +[source,erlang] +---- +{"/assets/[...]", cowboy_static, {priv_dir, my_app, "static/assets", + [{mimetypes, cow_mimetypes, all}]}} +---- + +You probably noticed the pattern by now. The configuration +expects a module and a function name, so you can use any +of your own functions instead. + +[source,erlang] +---- +{"/assets/[...]", cowboy_static, {priv_dir, my_app, "static/assets", + [{mimetypes, Module, Function}]}} +---- + +The function that performs the mimetype detection receives +a single argument that is the path to the file on disk. It +is recommended to return the mimetype in tuple form, although +a binary string is also allowed (but will require extra +processing). If the function can't figure out the mimetype, +then it should return `{<<"application">>, <<"octet-stream">>, []}`. + +When the static handler fails to find the extension in the +list, it will send the file as `application/octet-stream`. +A browser receiving such file will attempt to download it +directly to disk. + +Finally, the mimetype can be hard-coded for all files. +This is especially useful in combination with the `file` +and `priv_file` options as it avoids needless computation. + +[source,erlang] +---- +{"/", cowboy_static, {priv_file, my_app, "static/index.html", + [{mimetypes, {<<"text">>, <<"html">>, []}}]}} +---- + +=== Generate an etag + +By default, the static handler will generate an etag header +value based on the size and modified time. This solution +can not be applied to all systems though. It would perform +rather poorly over a cluster of nodes, for example, as the +file metadata will vary from server to server, giving a +different etag on each server. + +You can however change the way the etag is calculated. + +[source,erlang] +---- +{"/assets/[...]", cowboy_static, {priv_dir, my_app, "static/assets", + [{etag, Module, Function}]}} +---- + +This function will receive three arguments: the path to the +file on disk, the size of the file and the last modification +time. In a distributed setup, you would typically use the +file path to retrieve an etag value that is identical across +all your servers. + +You can also completely disable etag handling. + +[source,erlang] +---- +{"/assets/[...]", cowboy_static, {priv_dir, my_app, "static/assets", + [{etag, false}]}} +---- diff --git a/doc/src/guide/static_files.ezdoc b/doc/src/guide/static_files.ezdoc deleted file mode 100644 index 5a289d0..0000000 --- a/doc/src/guide/static_files.ezdoc +++ /dev/null @@ -1,168 +0,0 @@ -::: Static files - -Cowboy comes with a special handler built as a REST handler -and designed specifically for serving static files. It is -provided as a convenience and provides a quick solution for -serving files during development. - -For systems in production, consider using one of the many -Content Distribution Network (CDN) available on the market, -as they are the best solution for serving files. They are -covered in the next chapter. If you decide against using a -CDN solution, then please look at the chapter after that, -as it explains how to efficiently serve static files on -your own. - -The static handler can serve either one file or all files -from a given directory. It can also send etag headers for -client-side caching. - -To use the static file handler, simply add routes for it -with the appropriate options. - -:: Serve one file - -You can use the static handler to serve one specific file -from an application's private directory. This is particularly -useful to serve an `index.html` file when the client requests -the `/` path, for example. The path configured is relative -to the given application's private directory. - -The following rule will serve the file `static/index.html` -from the application `my_app`'s priv directory whenever the -path `/` is accessed. - -``` erlang -{"/", cowboy_static, {priv_file, my_app, "static/index.html"}} -``` - -You can also specify the absolute path to a file, or the -path to the file relative to the current directory. - -``` erlang -{"/", cowboy_static, {file, "/var/www/index.html"}} -``` - -:: Serve all files from a directory - -You can also use the static handler to serve all files that -can be found in the configured directory. The handler will -use the `path_info` information to resolve the file location, -which means that your route must end with a `[...]` pattern -for it to work. All files are served, including the ones that -may be found in subfolders. - -You can specify the directory relative to an application's -private directory. - -The following rule will serve any file found in the application -`my_app`'s priv directory inside the `static/assets` folder -whenever the requested path begins with `/assets/`. - -``` erlang -{"/assets/[...]", cowboy_static, {priv_dir, my_app, "static/assets"}} -``` - -You can also specify the absolute path to the directory or -set it relative to the current directory. - -``` erlang -{"/assets/[...]", cowboy_static, {dir, "/var/www/assets"}} -``` - -:: Customize the mimetype detection - -By default, Cowboy will attempt to recognize the mimetype -of your static files by looking at the extension. - -You can override the function that figures out the mimetype -of the static files. It can be useful when Cowboy is missing -a mimetype you need to handle, or when you want to reduce -the list to make lookups faster. You can also give a -hard-coded mimetype that will be used unconditionally. - -Cowboy comes with two functions built-in. The default -function only handles common file types used when building -Web applications. The other function is an extensive list -of hundreds of mimetypes that should cover almost any need -you may have. You can of course create your own function. - -To use the default function, you should not have to configure -anything, as it is the default. If you insist, though, the -following will do the job. - -``` erlang -{"/assets/[...]", cowboy_static, {priv_dir, my_app, "static/assets", - [{mimetypes, cow_mimetypes, web}]}} -``` - -As you can see, there is an optional field that may contain -a list of less used options, like mimetypes or etag. All option -types have this optional field. - -To use the function that will detect almost any mimetype, -the following configuration will do. - -``` erlang -{"/assets/[...]", cowboy_static, {priv_dir, my_app, "static/assets", - [{mimetypes, cow_mimetypes, all}]}} -``` - -You probably noticed the pattern by now. The configuration -expects a module and a function name, so you can use any -of your own functions instead. - -``` erlang -{"/assets/[...]", cowboy_static, {priv_dir, my_app, "static/assets", - [{mimetypes, Module, Function}]}} -``` - -The function that performs the mimetype detection receives -a single argument that is the path to the file on disk. It -is recommended to return the mimetype in tuple form, although -a binary string is also allowed (but will require extra -processing). If the function can't figure out the mimetype, -then it should return `{<<"application">>, <<"octet-stream">>, []}`. - -When the static handler fails to find the extension in the -list, it will send the file as `application/octet-stream`. -A browser receiving such file will attempt to download it -directly to disk. - -Finally, the mimetype can be hard-coded for all files. -This is especially useful in combination with the `file` -and `priv_file` options as it avoids needless computation. - -``` erlang -{"/", cowboy_static, {priv_file, my_app, "static/index.html", - [{mimetypes, {<<"text">>, <<"html">>, []}}]}} -``` - -:: Generate an etag - -By default, the static handler will generate an etag header -value based on the size and modified time. This solution -can not be applied to all systems though. It would perform -rather poorly over a cluster of nodes, for example, as the -file metadata will vary from server to server, giving a -different etag on each server. - -You can however change the way the etag is calculated. - -``` erlang -{"/assets/[...]", cowboy_static, {priv_dir, my_app, "static/assets", - [{etag, Module, Function}]}} -``` - -This function will receive three arguments: the path to the -file on disk, the size of the file and the last modification -time. In a distributed setup, you would typically use the -file path to retrieve an etag value that is identical across -all your servers. - -You can also completely disable etag handling. - -``` erlang -{"/assets/[...]", cowboy_static, {priv_dir, my_app, "static/assets", - [{etag, false}]}} -``` diff --git a/doc/src/guide/sub_protocols.asciidoc b/doc/src/guide/sub_protocols.asciidoc new file mode 100644 index 0000000..63fd52b --- /dev/null +++ b/doc/src/guide/sub_protocols.asciidoc @@ -0,0 +1,68 @@ +[[sub_protocols]] +== Sub protocols + +Sub protocols are used for creating new types of handlers that +provide extra functionality in a reusable way. Cowboy uses this +mechanism to provide its loop, REST and Websocket handlers. + +This chapter will explain how to create your own sub protocols +and handler types. + +=== Usage + +To switch to a sub protocol, the `init/2` callback must return +the name of the sub protocol module. Everything past this point +is handled by the sub protocol. + +[source,erlang] +---- +init(Req, _Opts) -> + {cowboy_websocket, Req, #state{}}. +---- + +The return value may also have a `Timeout` value and/or the +atom `hibernate`. These options are useful for long living +connections. When they are not provided, the timeout value +defaults to `infinity` and the hibernate value to `run`. + +The following snippet switches to the `my_protocol` sub +protocol, sets the timeout value to 5 seconds and enables +hibernation: + +[source,erlang] +---- +init(Req, _Opts) -> + {my_protocol, Req, #state{}, 5000, hibernate}. +---- + +If a sub protocol does not make use of these options, it should +crash if it receives anything other than the default values. + +=== Upgrade + +After the `init/2` function returns, Cowboy will then call the +`upgrade/6` function. This is the only callback defined by the +`cowboy_sub_protocol` behavior. + +The function is named `upgrade` because it mimics the mechanism +of HTTP protocol upgrades. For some sub protocols, like Websocket, +an actual upgrade is performed. For others, like REST, this is +only an upgrade at Cowboy's level and the client has nothing to +do about it. + +The upgrade callback receives the Req object, the middleware +environment, the handler and its options, and the aforementioned +timeout and hibernate values. + +[source,erlang] +---- +upgrade(Req, Env, Handler, HandlerOpts, Timeout, Hibernate) -> + %% Sub protocol code here. +---- + +This callback is expected to behave like a middleware and to +return an updated environment and Req object. + +Sub protocols are expected to call the `cowboy_handler:terminate/4` +function when they terminate. This function will make sure that +the optional `terminate/3` callback is called, if present. diff --git a/doc/src/guide/sub_protocols.ezdoc b/doc/src/guide/sub_protocols.ezdoc deleted file mode 100644 index 54e57aa..0000000 --- a/doc/src/guide/sub_protocols.ezdoc +++ /dev/null @@ -1,64 +0,0 @@ -::: Sub protocols - -Sub protocols are used for creating new types of handlers that -provide extra functionality in a reusable way. Cowboy uses this -mechanism to provide its loop, REST and Websocket handlers. - -This chapter will explain how to create your own sub protocols -and handler types. - -:: Usage - -To switch to a sub protocol, the `init/2` callback must return -the name of the sub protocol module. Everything past this point -is handled by the sub protocol. - -``` erlang -init(Req, _Opts) -> - {cowboy_websocket, Req, #state{}}. -``` - -The return value may also have a `Timeout` value and/or the -atom `hibernate`. These options are useful for long living -connections. When they are not provided, the timeout value -defaults to `infinity` and the hibernate value to `run`. - -The following snippet switches to the `my_protocol` sub -protocol, sets the timeout value to 5 seconds and enables -hibernation: - -``` erlang -init(Req, _Opts) -> - {my_protocol, Req, #state{}, 5000, hibernate}. -``` - -If a sub protocol does not make use of these options, it should -crash if it receives anything other than the default values. - -:: Upgrade - -After the `init/2` function returns, Cowboy will then call the -`upgrade/6` function. This is the only callback defined by the -`cowboy_sub_protocol` behavior. - -The function is named `upgrade` because it mimics the mechanism -of HTTP protocol upgrades. For some sub protocols, like Websocket, -an actual upgrade is performed. For others, like REST, this is -only an upgrade at Cowboy's level and the client has nothing to -do about it. - -The upgrade callback receives the Req object, the middleware -environment, the handler and its options, and the aforementioned -timeout and hibernate values. - -``` erlang -upgrade(Req, Env, Handler, HandlerOpts, Timeout, Hibernate) -> - %% Sub protocol code here. -``` - -This callback is expected to behave like a middleware and to -return an updated environment and Req object. - -Sub protocols are expected to call the `cowboy_handler:terminate/4` -function when they terminate. This function will make sure that -the optional `terminate/3` callback is called, if present. diff --git a/doc/src/guide/ws_handlers.asciidoc b/doc/src/guide/ws_handlers.asciidoc new file mode 100644 index 0000000..9ddddf4 --- /dev/null +++ b/doc/src/guide/ws_handlers.asciidoc @@ -0,0 +1,196 @@ +[[ws_handlers]] +== Handling Websocket connections + +A special handler is required for handling Websocket connections. +Websocket handlers allow you to initialize the connection, +handle incoming frames from the socket, handle incoming Erlang +messages and then clean up on termination. + +Websocket handlers essentially act as a bridge between the client +and the Erlang system. They will typically do little more than +socket communication and decoding/encoding of frames. + +=== Initialization + +First, the `init/2` callback is called. This callback is common +to all handlers. To establish a Websocket connection, this function +must return a `ws` tuple. + +[source,erlang] +---- +init(Req, _Opts) -> + {cowboy_websocket, Req, #state{}}. +---- + +Upon receiving this tuple, Cowboy will switch to the code +that handles Websocket connections and perform the handshake +immediately. + +If the sec-websocket-protocol header was sent with the request +for establishing a Websocket connection, then the Websocket +handler *must* select one of these subprotocol and send it +back to the client, otherwise the client might decide to close +the connection, assuming no correct subprotocol was found. + +[source,erlang] +---- +init(Req, _Opts) -> + case cowboy_req:parse_header(<<"sec-websocket-protocol">>, Req) of + undefined -> + {ok, Req, #state{}}; + Subprotocols -> + case lists:keymember(<<"mychat2">>, 1, Subprotocols) of + true -> + Req2 = cowboy_req:set_resp_header(<<"sec-websocket-protocol">>, + <<"mychat2">>, Req), + {ok, Req2, #state{}}; + false -> + {stop, Req, undefined} + end + end. +---- + +It is not recommended to wait too long inside the `init/2` +function. Any extra initialization may be done after returning by +sending yourself a message before doing anything. Any message sent +to `self()` from `init/2` is guaranteed to arrive before +any frames from the client. + +It is also very easy to ensure that this message arrives before +any message from other processes by sending it before registering +or enabling timers. + +[source,erlang] +---- +init(Req, _Opts) -> + self() ! post_init, + %% Register process here... + {cowboy_websocket, Req, #state{}}. + +websocket_info(post_init, Req, State) -> + %% Perform post_init initialization here... + {ok, Req, State}. +---- + +=== Handling frames from the client + +Cowboy will call `websocket_handle/3` whenever a text, binary, +ping or pong frame arrives from the client. Note that in the +case of ping and pong frames, no action is expected as Cowboy +automatically replies to ping frames. + +The handler can decide to send frames to the socket, stop +or just continue without sending anything. + +The following snippet echoes back any text frame received and +ignores all others. + +[source,erlang] +---- +websocket_handle(Frame = {text, _}, Req, State) -> + {reply, Frame, Req, State}; +websocket_handle(_Frame, Req, State) -> + {ok, Req, State}. +---- + +=== Handling Erlang messages + +Cowboy will call `websocket_info/3` whenever an Erlang message +arrives. + +The handler can decide to send frames to the socket, stop +or just continue without sending anything. + +The following snippet forwards any `log` message to the socket +and ignores all others. + +[source,erlang] +---- +websocket_info({log, Text}, Req, State) -> + {reply, {text, Text}, Req, State}; +websocket_info(_Info, Req, State) -> + {ok, Req, State}. +---- + +=== Sending frames to the socket + +Cowboy allows sending either a single frame or a list of +frames to the socket, in which case the frames are sent +sequentially. Any frame can be sent: text, binary, ping, +pong or close frames. + +The following example sends three frames using a single `reply` +tuple. + +[source,erlang] +---- +websocket_info(hello_world, Req, State) -> + {reply, [ + {text, "Hello"}, + {text, <<"world!">>}, + {binary, <<0:8000>>} + ], Req, State}; +%% More websocket_info/3 clauses here... +---- + +Note that the payload for text and binary frames is of type +`iodata()`, meaning it can be either a `binary()` or an +`iolist()`. + +Sending a `close` frame will immediately initiate the closing +of the Websocket connection. Be aware that any additional +frames sent by the client or any Erlang messages waiting to +be received will not be processed. Also note that when replying +a list of frames that includes close, any frame found after the +close frame will not be sent. + +=== Ping and timeout + +The biggest performance improvement you can do when dealing +with a huge number of Websocket connections is to reduce the +number of timers that are started on the server. A common use +of timers when dealing with connections is for sending a ping +every once in a while. This should be done exclusively on the +client side. Indeed, a server handling one million Websocket +connections will perform a lot better when it doesn't have to +handle one million extra timers too! + +Cowboy will automatically respond to ping frames sent by the +client. It will still forward the frame to the handler for +informative purpose, but no further action is required. + +Cowboy can be configured to automatically close the Websocket +connection when no data arrives on the socket. It is highly +recommended to configure a timeout for it, as otherwise you +may end up with zombie "half-connected" sockets that may +leave the process alive forever. + +A good timeout value is 60 seconds. + +[source,erlang] +---- +init(Req, _Opts) -> + {cowboy_websocket, Req, #state{}, 60000}. +---- + +This value cannot be changed once it is set. It defaults to +`infinity`. + +=== Hibernate + +Most tuples returned from handler callbacks can include an +extra value `hibernate`. After doing any necessary operations +following the return of the callback, Cowboy will hibernate +the process. + +It is highly recommended to hibernate processes that do not +handle much traffic. It is a good idea to hibernate all +connections by default and investigate only when you start +noticing increased CPU usage. + +=== Supporting older browsers + +Unfortunately Websocket is a relatively recent technology, +which means that not all browsers support it. A library like +https://github.com/ninenines/bullet[Bullet] can be used to +emulate Websocket connections on older browsers. diff --git a/doc/src/guide/ws_handlers.ezdoc b/doc/src/guide/ws_handlers.ezdoc deleted file mode 100644 index a0cfc29..0000000 --- a/doc/src/guide/ws_handlers.ezdoc +++ /dev/null @@ -1,188 +0,0 @@ -::: Handling Websocket connections - -A special handler is required for handling Websocket connections. -Websocket handlers allow you to initialize the connection, -handle incoming frames from the socket, handle incoming Erlang -messages and then clean up on termination. - -Websocket handlers essentially act as a bridge between the client -and the Erlang system. They will typically do little more than -socket communication and decoding/encoding of frames. - -:: Initialization - -First, the `init/2` callback is called. This callback is common -to all handlers. To establish a Websocket connection, this function -must return a `ws` tuple. - -``` erlang -init(Req, _Opts) -> - {cowboy_websocket, Req, #state{}}. -``` - -Upon receiving this tuple, Cowboy will switch to the code -that handles Websocket connections and perform the handshake -immediately. - -If the sec-websocket-protocol header was sent with the request -for establishing a Websocket connection, then the Websocket -handler *must* select one of these subprotocol and send it -back to the client, otherwise the client might decide to close -the connection, assuming no correct subprotocol was found. - -``` erlang -init(Req, _Opts) -> - case cowboy_req:parse_header(<<"sec-websocket-protocol">>, Req) of - undefined -> - {ok, Req, #state{}}; - Subprotocols -> - case lists:keymember(<<"mychat2">>, 1, Subprotocols) of - true -> - Req2 = cowboy_req:set_resp_header(<<"sec-websocket-protocol">>, - <<"mychat2">>, Req), - {ok, Req2, #state{}}; - false -> - {stop, Req, undefined} - end - end. -``` - -It is not recommended to wait too long inside the `init/2` -function. Any extra initialization may be done after returning by -sending yourself a message before doing anything. Any message sent -to `self()` from `init/2` is guaranteed to arrive before -any frames from the client. - -It is also very easy to ensure that this message arrives before -any message from other processes by sending it before registering -or enabling timers. - -``` erlang -init(Req, _Opts) -> - self() ! post_init, - %% Register process here... - {cowboy_websocket, Req, #state{}}. - -websocket_info(post_init, Req, State) -> - %% Perform post_init initialization here... - {ok, Req, State}. -``` - -:: Handling frames from the client - -Cowboy will call `websocket_handle/3` whenever a text, binary, -ping or pong frame arrives from the client. Note that in the -case of ping and pong frames, no action is expected as Cowboy -automatically replies to ping frames. - -The handler can decide to send frames to the socket, stop -or just continue without sending anything. - -The following snippet echoes back any text frame received and -ignores all others. - -``` erlang -websocket_handle(Frame = {text, _}, Req, State) -> - {reply, Frame, Req, State}; -websocket_handle(_Frame, Req, State) -> - {ok, Req, State}. -``` - -:: Handling Erlang messages - -Cowboy will call `websocket_info/3` whenever an Erlang message -arrives. - -The handler can decide to send frames to the socket, stop -or just continue without sending anything. - -The following snippet forwards any `log` message to the socket -and ignores all others. - -``` erlang -websocket_info({log, Text}, Req, State) -> - {reply, {text, Text}, Req, State}; -websocket_info(_Info, Req, State) -> - {ok, Req, State}. -``` - -:: Sending frames to the socket - -Cowboy allows sending either a single frame or a list of -frames to the socket, in which case the frames are sent -sequentially. Any frame can be sent: text, binary, ping, -pong or close frames. - -The following example sends three frames using a single `reply` -tuple. - -``` erlang -websocket_info(hello_world, Req, State) -> - {reply, [ - {text, "Hello"}, - {text, <<"world!">>}, - {binary, <<0:8000>>} - ], Req, State}; -%% More websocket_info/3 clauses here... -``` - -Note that the payload for text and binary frames is of type -`iodata()`, meaning it can be either a `binary()` or an -`iolist()`. - -Sending a `close` frame will immediately initiate the closing -of the Websocket connection. Be aware that any additional -frames sent by the client or any Erlang messages waiting to -be received will not be processed. Also note that when replying -a list of frames that includes close, any frame found after the -close frame will not be sent. - -:: Ping and timeout - -The biggest performance improvement you can do when dealing -with a huge number of Websocket connections is to reduce the -number of timers that are started on the server. A common use -of timers when dealing with connections is for sending a ping -every once in a while. This should be done exclusively on the -client side. Indeed, a server handling one million Websocket -connections will perform a lot better when it doesn't have to -handle one million extra timers too! - -Cowboy will automatically respond to ping frames sent by the -client. It will still forward the frame to the handler for -informative purpose, but no further action is required. - -Cowboy can be configured to automatically close the Websocket -connection when no data arrives on the socket. It is highly -recommended to configure a timeout for it, as otherwise you -may end up with zombie "half-connected" sockets that may -leave the process alive forever. - -A good timeout value is 60 seconds. - -``` erlang -init(Req, _Opts) -> - {cowboy_websocket, Req, #state{}, 60000}. -``` - -This value cannot be changed once it is set. It defaults to -`infinity`. - -:: Hibernate - -Most tuples returned from handler callbacks can include an -extra value `hibernate`. After doing any necessary operations -following the return of the callback, Cowboy will hibernate -the process. - -It is highly recommended to hibernate processes that do not -handle much traffic. It is a good idea to hibernate all -connections by default and investigate only when you start -noticing increased CPU usage. - -:: Supporting older browsers - -Unfortunately Websocket is a relatively recent technology, -which means that not all browsers support it. A library like -^"Bullet^https://github.com/extend/bullet^ can be used to -emulate Websocket connections on older browsers. diff --git a/doc/src/guide/ws_protocol.asciidoc b/doc/src/guide/ws_protocol.asciidoc new file mode 100644 index 0000000..67b2cdf --- /dev/null +++ b/doc/src/guide/ws_protocol.asciidoc @@ -0,0 +1,43 @@ +[[ws_protocol]] +== The Websocket protocol + +This chapter explains what Websocket is and why it is +a vital component of soft realtime Web applications. + +=== Description + +Websocket is an extension to HTTP that emulates plain TCP +connections between the client, typically a Web browser, +and the server. It uses the HTTP Upgrade mechanism to +establish the connection. + +Websocket connections are asynchronous, unlike HTTP. This +means that not only can the client send frames to the server +at any time, but the server can also send frames to the client +without the client initiating anything other than the +Websocket connection itself. This allows the server to push +data to the client directly. + +Websocket is an IETF standard. Cowboy supports the standard +and all drafts that were previously implemented by browsers, +excluding the initial flawed draft sometimes known as +"version 0". + +=== Implementation + +Cowboy implements Websocket as a protocol upgrade. Once the +upgrade is performed from the `init/2` callback, Cowboy +switches to Websocket. Please consult the next chapter for +more information on initiating and handling Websocket +connections. + +The implementation of Websocket in Cowboy is validated using +the Autobahn test suite, which is an extensive suite of tests +covering all aspects of the protocol. Cowboy passes the +suite with 100% success, including all optional tests. + +Cowboy's Websocket implementation also includes the +x-webkit-deflate-frame compression draft which is being used +by some browsers to reduce the size of data being transmitted. +Cowboy will automatically use compression as long as the +`compress` protocol option is set when starting the listener. diff --git a/doc/src/guide/ws_protocol.ezdoc b/doc/src/guide/ws_protocol.ezdoc deleted file mode 100644 index d283ae3..0000000 --- a/doc/src/guide/ws_protocol.ezdoc +++ /dev/null @@ -1,42 +0,0 @@ -::: The Websocket protocol - -This chapter explains what Websocket is and why it is -a vital component of soft realtime Web applications. - -:: Description - -Websocket is an extension to HTTP that emulates plain TCP -connections between the client, typically a Web browser, -and the server. It uses the HTTP Upgrade mechanism to -establish the connection. - -Websocket connections are asynchronous, unlike HTTP. This -means that not only can the client send frames to the server -at any time, but the server can also send frames to the client -without the client initiating anything other than the -Websocket connection itself. This allows the server to push -data to the client directly. - -Websocket is an IETF standard. Cowboy supports the standard -and all drafts that were previously implemented by browsers, -excluding the initial flawed draft sometimes known as -"version 0". - -:: Implementation - -Cowboy implements Websocket as a protocol upgrade. Once the -upgrade is performed from the `init/2` callback, Cowboy -switches to Websocket. Please consult the next chapter for -more information on initiating and handling Websocket -connections. - -The implementation of Websocket in Cowboy is validated using -the Autobahn test suite, which is an extensive suite of tests -covering all aspects of the protocol. Cowboy passes the -suite with 100% success, including all optional tests. - -Cowboy's Websocket implementation also includes the -x-webkit-deflate-frame compression draft which is being used -by some browsers to reduce the size of data being transmitted. -Cowboy will automatically use compression as long as the -`compress` protocol option is set when starting the listener. diff --git a/doc/src/manual/cowboy.asciidoc b/doc/src/manual/cowboy.asciidoc new file mode 100644 index 0000000..cd83b3d --- /dev/null +++ b/doc/src/manual/cowboy.asciidoc @@ -0,0 +1,98 @@ += cowboy(3) + +== Name + +cowboy - HTTP server + +== Description + +The `cowboy` module provides convenience functions for +manipulating Ranch listeners. + +== Types + +=== fields() = [Field] + +[source,erlang] +---- +Field = atom() + | {atom(), cowboy_constraints:constraint() | [cowboy_constraints:constraint()]} + | {atom(), cowboy_constraints:constraint() | [cowboy_constraints:constraint()], any()}] +---- + +Fields for match operations. Constraint(s) and default value are optional. + +=== http_headers() = [{binary(), iodata()}] + +HTTP headers as a list of key/values. + +=== http_status() = non_neg_integer() | binary() + +HTTP status. + +A binary status can be used to set a custom message. + +=== http_version() = \'HTTP/1.1' | \'HTTP/1.0' + +HTTP version. + +=== `onresponse_fun() = fun((http_status(), http_headers(), iodata(), cowboy_req:req()) -> cowboy_req:req())` + +Fun called immediately before sending the response. + +It can perform any operation on the Req object, including +reading the request body or replying. If a reply is sent, it +overrides the reply initially sent. The callback will not be +called again for the new reply. + +== Exports + +=== start_http(Ref, NbAcceptors, TransOpts, ProtoOpts) -> {ok, pid()} + +Ref = ranch:ref():: Listener name. +NbAcceptors = non_neg_integer():: Number of acceptor processes. +TransOpts = ranch_tcp:opts():: TCP transport options. +ProtoOpts = cowboy_protocol:opts():: HTTP protocol options. + +Start listening for HTTP connections. Returns the pid for this +listener's supervisor. + +=== start_https(Ref, NbAcceptors, TransOpts, ProtoOpts) -> {ok, pid()} + +Ref = ranch:ref():: Listener name. +NbAcceptors = non_neg_integer():: Number of acceptor processes. +TransOpts = ranch_ssl:opts():: SSL transport options. +ProtoOpts = cowboy_protocol:opts():: HTTP protocol options. + +Start listening for HTTPS connections. Returns the pid for this +listener's supervisor. + +=== start_spdy(Ref, NbAcceptors, TransOpts, ProtoOpts) -> {ok, pid()} + +Ref = ranch:ref():: Listener name. +NbAcceptors = non_neg_integer():: Number of acceptor processes. +TransOpts = ranch_ssl:opts():: SSL transport options. +ProtoOpts = cowboy_spdy:opts():: SPDY protocol options. + +Start listening for SPDY connections. Returns the pid for this +listener's supervisor. + +=== stop_listener(Ref) -> ok | {error, not_found} + +Ref = ranch:ref():: Listener name. + +Stop a previously started listener. + +=== set_env(Ref, Name, Value) -> ok + +Ref = ranch:ref():: Listener name. +Name = atom():: Name of environment value. +Value = any():: Environment value. + +Set or update an environment value for an already running listener. +This will take effect on all subsequent connections. + +== See also + +The http://ninenines.eu/docs/en/ranch/HEAD/guide[Ranch guide] +provides detailed information about how listeners work. diff --git a/doc/src/manual/cowboy.ezdoc b/doc/src/manual/cowboy.ezdoc deleted file mode 100644 index c26ed37..0000000 --- a/doc/src/manual/cowboy.ezdoc +++ /dev/null @@ -1,98 +0,0 @@ -::: cowboy - -The `cowboy` module provides convenience functions for -manipulating Ranch listeners. - -:: Types - -: fields() = [atom() - | {atom(), cowboy_constraints:constraint() | [cowboy_constraints:constraint()]} - | {atom(), cowboy_constraints:constraint() | [cowboy_constraints:constraint()], any()}] - -Fields for match operations. Constraint(s) and default value are optional. - -: http_headers() = [{binary(), iodata()}] - -HTTP headers as a list of key/values. - -: http_status() = non_neg_integer() | binary() - -HTTP status. - -A binary status can be used to set a custom message. - -: http_version() = 'HTTP/1.1' | 'HTTP/1.0' - -HTTP version. - -: onresponse_fun() = fun((http_status(), http_headers(), - iodata(), cowboy_req:req()) -> cowboy_req:req()) - -Fun called immediately before sending the response. - -It can perform any operation on the Req object, including -reading the request body or replying. If a reply is sent, it -overrides the reply initially sent. The callback will not be -called again for the new reply. - -:: Exports - -: start_http(Ref, NbAcceptors, TransOpts, ProtoOpts) -> {ok, pid()} - -Types: - -* Ref = ranch:ref() -* NbAcceptors = non_neg_integer() -* TransOpts = ranch_tcp:opts() -* ProtoOpts = cowboy_protocol:opts() - -Start listening for HTTP connections. Returns the pid for this -listener's supervisor. - -: start_https(Ref, NbAcceptors, TransOpts, ProtoOpts) -> {ok, pid()} - -Types: - -* Ref = ranch:ref() -* NbAcceptors = non_neg_integer() -* TransOpts = ranch_ssl:opts() -* ProtoOpts = cowboy_protocol:opts() - -Start listening for HTTPS connections. Returns the pid for this -listener's supervisor. - -: start_spdy(Ref, NbAcceptors, TransOpts, ProtoOpts) -> {ok, pid()} - -Types: - -* Ref = ranch:ref() -* NbAcceptors = non_neg_integer() -* TransOpts = ranch_ssl:opts() -* ProtoOpts = cowboy_spdy:opts() - -Start listening for SPDY connections. Returns the pid for this -listener's supervisor. - -: stop_listener(Ref) -> ok | {error, not_found} - -Types: - -* Ref = ranch:ref() - -Stop a previously started listener. - -: set_env(Ref, Name, Value) -> ok - -Types: - -* Ref = ranch:ref() -* Name = atom() -* Value = any() - -Set or update an environment value for an already running listener. -This will take effect on all subsequent connections. - -:: See also - -The ^"Ranch guide^http://ninenines.eu/docs/en/ranch/HEAD/guide -provides detailed information about how listeners work. diff --git a/doc/src/manual/cowboy_app.asciidoc b/doc/src/manual/cowboy_app.asciidoc new file mode 100644 index 0000000..2213bd1 --- /dev/null +++ b/doc/src/manual/cowboy_app.asciidoc @@ -0,0 +1,25 @@ += cowboy(7) + +== Name + +cowboy - Small, fast, modular HTTP server. + +== Dependencies + +The `cowboy` application uses the Erlang applications `ranch` +for listening and accepting TCP connections, `crypto` for +establishing Websocket connections, and `cowlib` for parsing and +building messages for Web protocols. These dependencies must +be loaded for the `cowboy` application to work. In an embedded +environment this means that they need to be started with the +`application:start/{1,2}` function before the `cowboy` +application is started. + +The `cowboy` application also uses the Erlang applications +`asn1`, `public_key` and `ssl` when listening for HTTPS connections. +These are started automatically if they weren't before. + +== Environment + +The `cowboy` application does not define any application +environment configuration parameters. diff --git a/doc/src/manual/cowboy_app.ezdoc b/doc/src/manual/cowboy_app.ezdoc deleted file mode 100644 index 2e2b877..0000000 --- a/doc/src/manual/cowboy_app.ezdoc +++ /dev/null @@ -1,23 +0,0 @@ -::: The Cowboy Application - -Small, fast, modular HTTP server. - -:: Dependencies - -The `cowboy` application uses the Erlang applications `ranch` -for listening and accepting TCP connections, `crypto` for -establishing Websocket connections, and `cowlib` for parsing and -building messages for Web protocols. These dependencies must -be loaded for the `cowboy` application to work. In an embedded -environment this means that they need to be started with the -`application:start/{1,2}` function before the `cowboy` -application is started. - -The `cowboy` application also uses the Erlang applications -`asn1`, `public_key` and `ssl` when listening for HTTPS connections. -These are started automatically if they weren't before. - -:: Environment - -The `cowboy` application does not define any application -environment configuration parameters. diff --git a/doc/src/manual/cowboy_handler.asciidoc b/doc/src/manual/cowboy_handler.asciidoc new file mode 100644 index 0000000..fbbe761 --- /dev/null +++ b/doc/src/manual/cowboy_handler.asciidoc @@ -0,0 +1,99 @@ += cowboy_handler(3) + +== Name + +cowboy_handler - handler middleware and behaviour + +== Description + +The `cowboy_handler` middleware executes the handler passed +through the environment values `handler` and `handler_opts`, +and adds the result of this execution to the environment as +the value `result`, indicating that the request has been +handled and received a response. + +Environment input: + +handler = module():: Handler to be executed. +handler_opts = any():: Options to be passed to the handler. + +Environment output: + +result = ok:: Result of the request. + +This module also defines the `cowboy_handler` behaviour that +defines the basic interface for handlers. All Cowboy handlers +implement at least the `init/2` callback, and may implement +the `terminate/3` callback optionally. + +== Terminate reasons + +The following values may be received as the terminate reason +in the optional `terminate/3` callback. Different handler types +may define additional terminate reasons. + +normal:: + The connection was closed normally. + +{crash, Class, Reason}:: + A crash occurred in the handler. `Class` and `Reason` can be + used to obtain more information about the crash. The function + `erlang:get_stacktrace/0` can also be called to obtain the + stacktrace of the process when the crash occurred. + +== Callbacks + +=== init(Req, Opts) -> {ok, Req, State} | {Module, Req, State} | {Module, Req, State, hibernate | Timeout} | {Module, Req, State, Timeout, hibernate} + +Req = cowboy_req:req():: The Req object. +Opts = any():: Handler options. +State = any():: Handler state. +Module = module():: Module of the sub-protocol to use. +Timeout = timeout():: Timeout passed to the sub-protocol, when applicable. + +Process the request. + +This function can be used to switch to an alternate handler +type by returning the name of the module to be used, along +with a few options. + +For basic handlers this is the function where the response +should be sent. If no response is sent, Cowboy will ensure +that a `204 No Content` response is sent. + +A crash in this callback will result in `terminate/3` being +called if it is defined, with the `State` argument set to +the value of `Opts` originally given to the `init/2` callback. + +=== terminate(Reason, Req, State) -> ok + +Reason = any():: Reason for termination. +Req = cowboy_req:req():: The Req object. +State = any():: Handler state. + +Perform any necessary cleanup of the state. + +This callback should release any resource currently in use, +clear any active timer and reset the process to its original +state, as it might be reused for future requests sent on the +same connection. Typical plain HTTP handlers rarely need to +use it. + +A crash in this callback or an invalid return value will +result in the closing of the connection and the termination +of the process. + +== Exports + +=== terminate(Reason, Req, State, Handler) -> ok + +Reason = any():: Reason for termination. +Req = cowboy_req:req():: The Req object. +State = any():: Handler state. +Handler = module():: Handler module. + +Call the optional `terminate/3` callback if it exists. + +This function should always be called at the end of the execution +of a handler, to give it a chance to clean up or perform +miscellaneous operations. diff --git a/doc/src/manual/cowboy_handler.ezdoc b/doc/src/manual/cowboy_handler.ezdoc deleted file mode 100644 index 81615c2..0000000 --- a/doc/src/manual/cowboy_handler.ezdoc +++ /dev/null @@ -1,106 +0,0 @@ -::: cowboy_handler - -The `cowboy_handler` middleware executes the handler passed -through the environment values `handler` and `handler_opts`, -and adds the result of this execution to the environment as -the value `result`, indicating that the request has been -handled and received a response. - -Environment input: - -* handler = module() -* handler_opts = any() - -Environment output: - -* result = ok - -This module also defines the `cowboy_handler` behaviour that -defines the basic interface for handlers. All Cowboy handlers -implement at least the `init/2` callback, and may implement -the `terminate/3` callback optionally. - -:: Terminate reasons - -The following values may be received as the terminate reason -in the optional `terminate/3` callback. Different handler types -may define additional terminate reasons. - -: normal - -The connection was closed normally. - -: {crash, Class, Reason} - -A crash occurred in the handler. `Class` and `Reason` can be -used to obtain more information about the crash. The function -`erlang:get_stacktrace/0` can also be called to obtain the -stacktrace of the process when the crash occurred. - -:: Callbacks - -: init(Req, Opts) - -> {ok, Req, State} - | {Module, Req, State} - | {Module, Req, State, hibernate} - | {Module, Req, State, Timeout} - | {Module, Req, State, Timeout, hibernate} - -Types: - -* Req = cowboy_req:req() -* Opts = any() -* State = any() -* Module = module() -* Timeout = timeout() - -Process the request. - -This function can be used to switch to an alternate handler -type by returning the name of the module to be used, along -with a few options. - -For basic handlers this is the function where the response -should be sent. If no response is sent, Cowboy will ensure -that a `204 No Content` response is sent. - -A crash in this callback will result in `terminate/3` being -called if it is defined, with the `State` argument set to -the value of `Opts` originally given to the `init/2` callback. - -: terminate(Reason, Req, State) -> ok - -Types: - -* Reason = any() -* Req = cowboy_req:req() -* State = any() - -Perform any necessary cleanup of the state. - -This callback should release any resource currently in use, -clear any active timer and reset the process to its original -state, as it might be reused for future requests sent on the -same connection. Typical plain HTTP handlers rarely need to -use it. - -A crash in this callback or an invalid return value will -result in the closing of the connection and the termination -of the process. - -:: Exports - -: terminate(Reason, Req, State, Handler) -> ok - -Types: - -* Reason = any() -* Req = cowboy_req:req() -* State = any() -* Handler = module() - -Call the optional `terminate/3` callback if it exists. - -This function should always be called at the end of the execution -of a handler, to give it a chance to clean up or perform -miscellaneous operations. diff --git a/doc/src/manual/cowboy_loop.asciidoc b/doc/src/manual/cowboy_loop.asciidoc new file mode 100644 index 0000000..eb17b15 --- /dev/null +++ b/doc/src/manual/cowboy_loop.asciidoc @@ -0,0 +1,86 @@ += cowboy_loop(3) + +== Name + +cowboy_loop - loop handlers + +== Description + +The `cowboy_loop` module implements a handler interface for +long running HTTP connections. It is the recommended interface +for long polling and server-sent events, amongst others. + +This module is a sub protocol that defines three callbacks to +be implemented by handlers. The `init/2` and `terminate/3` +callbacks are common to all handler types and are documented +in the manual for the link:cowboy_handler.asciidoc[cowboy_handler] module. + +The `info/3` callback is specific to loop handlers and will be +called as many times as necessary until a reply is sent. + +It is highly recommended to return a timeout value from the +`init/2` callback to ensure that the process is terminated +when no data has been received during that timespan. The +default timeout is `infinity`, which should only be used if +you have alternate means of ending inactive connections. + +== Terminate reasons + +The following values may be received as the terminate reason +in the optional `terminate/3` callback. + +normal:: + The connection was closed normally before switching to the + loop sub protocol. This typically happens if an `ok` tuple is + returned from the `init/2` callback. + +stop:: + The handler requested to close the connection by returning + a `stop` tuple. + +timeout:: + The connection has been closed due to inactivity. The timeout + value can be configured from `init/2`. The response sent when + this happens is a `204 No Content`. + +{crash, Class, Reason}:: + A crash occurred in the handler. `Class` and `Reason` can be + used to obtain more information about the crash. The function + `erlang:get_stacktrace/0` can also be called to obtain the + stacktrace of the process when the crash occurred. + +{error, overflow}:: + The connection is being closed and the process terminated + because the buffer Cowboy uses to keep data sent by the + client has reached its maximum. The buffer size can be + configured through the environment value `loop_max_buffer` + and defaults to 5000 bytes. + + + If the long running request comes with a body it is recommended + to process this body before switching to the loop sub protocol. + +{error, closed}:: + The socket has been closed brutally without a close frame being + received first. + +{error, Reason}:: + A socket error ocurred. + +== Callbacks + +=== info(Info, Req, State) -> {ok, Req, State} | {ok, Req, State, hibernate} | {stop, Req, State} + +Info = any():: Message received by the process. +Req = cowboy_req:req():: The Req object. +State = any():: Handler state. + +Handle the Erlang message received. + +This function will be called every time an Erlang message +has been received. The message can be any Erlang term. + +The `stop` return value can be used to stop the receive loop, +typically because a response has been sent. + +The `hibernate` option will hibernate the process until +it receives another message. diff --git a/doc/src/manual/cowboy_loop.ezdoc b/doc/src/manual/cowboy_loop.ezdoc deleted file mode 100644 index 79b96f9..0000000 --- a/doc/src/manual/cowboy_loop.ezdoc +++ /dev/null @@ -1,92 +0,0 @@ -::: cowboy_loop - -The `cowboy_loop` module implements a handler interface for -long running HTTP connections. It is the recommended interface -for long polling and server-sent events, amongst others. - -This module is a sub protocol that defines three callbacks to -be implemented by handlers. The `init/2` and `terminate/3` -callbacks are common to all handler types and are documented -in the manual for the ^cowboy_handler module. - -The `info/3` callback is specific to loop handlers and will be -called as many times as necessary until a reply is sent. - -It is highly recommended to return a timeout value from the -`init/2` callback to ensure that the process is terminated -when no data has been received during that timespan. The -default timeout is `infinity`, which should only be used if -you have alternate means of ending inactive connections. - -:: Terminate reasons - -The following values may be received as the terminate reason -in the optional `terminate/3` callback. - -: normal - -The connection was closed normally before switching to the -loop sub protocol. This typically happens if an `ok` tuple is -returned from the `init/2` callback. - -: stop - -The handler requested to close the connection by returning -a `stop` tuple. - -: timeout - -The connection has been closed due to inactivity. The timeout -value can be configured from `init/2`. The response sent when -this happens is a `204 No Content`. - -: {crash, Class, Reason} - -A crash occurred in the handler. `Class` and `Reason` can be -used to obtain more information about the crash. The function -`erlang:get_stacktrace/0` can also be called to obtain the -stacktrace of the process when the crash occurred. - -: {error, overflow} - -The connection is being closed and the process terminated -because the buffer Cowboy uses to keep data sent by the -client has reached its maximum. The buffer size can be -configured through the environment value `loop_max_buffer` -and defaults to 5000 bytes. - -If the long running request comes with a body it is recommended -to process this body before switching to the loop sub protocol. - -: {error, closed} - -The socket has been closed brutally without a close frame being -received first. - -: {error, Reason} - -A socket error ocurred. - -:: Callbacks - -: info(Info, Req, State) - -> {ok, Req, State} - | {ok, Req, State, hibernate} - | {stop, Req, State} - -Types: - -* Info = any() -* Req = cowboy_req:req() -* State = any() - -Handle the Erlang message received. - -This function will be called every time an Erlang message -has been received. The message can be any Erlang term. - -The `stop` return value can be used to stop the receive loop, -typically because a response has been sent. - -The `hibernate` option will hibernate the process until -it receives another message. diff --git a/doc/src/manual/cowboy_middleware.asciidoc b/doc/src/manual/cowboy_middleware.asciidoc new file mode 100644 index 0000000..87cd5d2 --- /dev/null +++ b/doc/src/manual/cowboy_middleware.asciidoc @@ -0,0 +1,48 @@ += cowboy_middleware(3) + +== Name + +cowboy_middleware - behaviour for middlewares + +== Description + +The `cowboy_middleware` behaviour defines the interface used +by Cowboy middleware modules. + +Middlewares process the request sequentially in the order they +are configured. + +== Types + +=== env() = [{atom(), any()}] + +The environment variable. + +One is created for every request. It is passed to each +middleware module executed and subsequently returned, +optionally with its contents modified. + +== Callbacks + +=== execute(Req, Env) -> {ok, Req, Env} | {suspend, Module, Function, Args} | {stop, Req} + +Req = cowboy_req:req():: The Req object. +Env = env():: The request environment. +Module = module():: MFA to call when resuming the process. +Function = atom():: MFA to call when resuming the process. +Args = [any()]:: MFA to call when resuming the process. + +Execute the middleware. + +The `ok` return value indicates that everything went well +and that Cowboy should continue processing the request. A +response may or may not have been sent. + +The `suspend` return value will hibernate the process until +an Erlang message is received. Note that when resuming, any +previous stacktrace information will be gone. + +The `stop` return value stops Cowboy from doing any further +processing of the request, even if there are middlewares +that haven't been executed yet. The connection may be left +open to receive more requests from the client. diff --git a/doc/src/manual/cowboy_middleware.ezdoc b/doc/src/manual/cowboy_middleware.ezdoc deleted file mode 100644 index dacaf6c..0000000 --- a/doc/src/manual/cowboy_middleware.ezdoc +++ /dev/null @@ -1,47 +0,0 @@ -::: cowboy_middleware - -The `cowboy_middleware` behaviour defines the interface used -by Cowboy middleware modules. - -Middlewares process the request sequentially in the order they -are configured. - -:: Types - -: env() = [{atom(), any()}] - -The environment variable. - -One is created for every request. It is passed to each -middleware module executed and subsequently returned, -optionally with its contents modified. - -:: Callbacks - -: execute(Req, Env) - -> {ok, Req, Env} - | {suspend, Module, Function, Args} - | {stop, Req} - -Types: - -* Req = cowboy_req:req() -* Env = env() -* Module = module() -* Function = atom() -* Args = [any()] - -Execute the middleware. - -The `ok` return value indicates that everything went well -and that Cowboy should continue processing the request. A -response may or may not have been sent. - -The `suspend` return value will hibernate the process until -an Erlang message is received. Note that when resuming, any -previous stacktrace information will be gone. - -The `stop` return value stops Cowboy from doing any further -processing of the request, even if there are middlewares -that haven't been executed yet. The connection may be left -open to receive more requests from the client. diff --git a/doc/src/manual/cowboy_protocol.asciidoc b/doc/src/manual/cowboy_protocol.asciidoc new file mode 100644 index 0000000..e1e7501 --- /dev/null +++ b/doc/src/manual/cowboy_protocol.asciidoc @@ -0,0 +1,75 @@ += cowboy_protocol(3) + +== Name + +cowboy_protocol - HTTP protocol + +== Description + +The `cowboy_protocol` module implements HTTP/1.1 and HTTP/1.0 +as a Ranch protocol. + +== Types + +=== opts() = [Option] + +[source,erlang] +---- +Option = {compress, boolean()} + | {env, cowboy_middleware:env()} + | {max_empty_lines, non_neg_integer()} + | {max_header_name_length, non_neg_integer()} + | {max_header_value_length, non_neg_integer()} + | {max_headers, non_neg_integer()} + | {max_keepalive, non_neg_integer()} + | {max_request_line_length, non_neg_integer()} + | {middlewares, [module()]} + | {onresponse, cowboy:onresponse_fun()} + | {timeout, timeout()} +---- + +Configuration for the HTTP protocol handler. + +This configuration is passed to Cowboy when starting listeners +using `cowboy:start_http/4` or `cowboy:start_https/4` functions. + +It can be updated without restarting listeners using the +Ranch functions `ranch:get_protocol_options/1` and +`ranch:set_protocol_options/2`. + +=== Option descriptions + +The default value is given next to the option name. + +compress (false):: + When enabled, Cowboy will attempt to compress the response body. + +env ([{listener, Ref}]):: + Initial middleware environment. + +max_empty_lines (5):: + Maximum number of empty lines before a request. + +max_header_name_length (64):: + Maximum length of header names. + +max_header_value_length (4096):: + Maximum length of header values. + +max_headers (100):: + Maximum number of headers allowed per request. + +max_keepalive (100):: + Maximum number of requests allowed per connection. + +max_request_line_length (4096):: + Maximum length of the request line. + +middlewares ([cowboy_router, cowboy_handler]):: + List of middlewares to execute for every requests. + +onresponse (undefined):: + Fun called every time a response is sent. + +timeout (5000):: + Time in ms with no requests before Cowboy closes the connection. diff --git a/doc/src/manual/cowboy_protocol.ezdoc b/doc/src/manual/cowboy_protocol.ezdoc deleted file mode 100644 index 1d65a5c..0000000 --- a/doc/src/manual/cowboy_protocol.ezdoc +++ /dev/null @@ -1,75 +0,0 @@ -::: cowboy_protocol - -The `cowboy_protocol` module implements HTTP/1.1 and HTTP/1.0 -as a Ranch protocol. - -:: Types - -: opts() = [{compress, boolean()} - | {env, cowboy_middleware:env()} - | {max_empty_lines, non_neg_integer()} - | {max_header_name_length, non_neg_integer()} - | {max_header_value_length, non_neg_integer()} - | {max_headers, non_neg_integer()} - | {max_keepalive, non_neg_integer()} - | {max_request_line_length, non_neg_integer()} - | {middlewares, [module()]} - | {onresponse, cowboy:onresponse_fun()} - | {timeout, timeout()}] - -Configuration for the HTTP protocol handler. - -This configuration is passed to Cowboy when starting listeners -using `cowboy:start_http/4` or `cowboy:start_https/4` functions. - -It can be updated without restarting listeners using the -Ranch functions `ranch:get_protocol_options/1` and -`ranch:set_protocol_options/2`. - -:: Option descriptions - -The default value is given next to the option name. - -: compress (false) - -When enabled, Cowboy will attempt to compress the response body. - -: env ([{listener, Ref}]) - -Initial middleware environment. - -: max_empty_lines (5) - -Maximum number of empty lines before a request. - -: max_header_name_length (64) - -Maximum length of header names. - -: max_header_value_length (4096) - -Maximum length of header values. - -: max_headers (100) - -Maximum number of headers allowed per request. - -: max_keepalive (100) - -Maximum number of requests allowed per connection. - -: max_request_line_length (4096) - -Maximum length of the request line. - -: middlewares ([cowboy_router, cowboy_handler]) - -List of middlewares to execute for every requests. - -: onresponse (undefined) - -Fun called every time a response is sent. - -: timeout (5000) - -Time in ms with no requests before Cowboy closes the connection. diff --git a/doc/src/manual/cowboy_req.asciidoc b/doc/src/manual/cowboy_req.asciidoc new file mode 100644 index 0000000..558592b --- /dev/null +++ b/doc/src/manual/cowboy_req.asciidoc @@ -0,0 +1,690 @@ += cowboy_req(3) + +== Name + +cowboy_req - HTTP request and response + +== Description + +The `cowboy_req` module provides functions to access, manipulate +and respond to requests. + +The functions in this module follow patterns for their return types, +based on the kind of function. + +* access: `Value` +* action: `ok | {Result, Req} | {Result, Value, Req}` +* modification: `Req` +* question: `boolean()` + +Whenever `Req` is returned, you must use this returned value and +ignore any previous you may have had. This value contains various +values which are necessary for Cowboy to keep track of the request +and response states. + +All functions which perform an action should only be called once. +This includes reading the request body or replying. Cowboy will +throw an error on the second call when it detects suspicious behavior. + +It is highly discouraged to pass the Req object to another process. +Doing so and calling `cowboy_req` functions from it leads to +undefined behavior. + +== Types + +=== body_opts() = [Option] + +[source,erlang] +---- +Option = {continue, boolean()} + | {length, non_neg_integer()} + | {read_length, non_neg_integer()} + | {read_timeout, timeout()} + | {transfer_decode, transfer_decode_fun(), any()} + | {content_decode, content_decode_fun()} +---- + +Request body reading options. + +=== cookie_opts() = [Option] + +[source,erlang] +---- +Option = {max_age, non_neg_integer()} + | {domain, binary()} + | {path, binary()} + | {secure, boolean()} + | {http_only, boolean()} +---- + +Cookie options. + +=== req() - opaque to the user + +The Req object. + +All functions in this module receive a `Req` as argument, +and some of them return a new object labelled `Req2` in +the function descriptions below. + +== Request related exports + +=== binding(Name, Req) -> binding(Name, Req, undefined) + +Alias of `cowboy_req:binding/3`. + +=== binding(Name, Req, Default) -> Value + +Name = atom():: Binding name. +Default = any():: Default value. +Value = any() | Default:: Binding value. + +Return the value for the given binding. + +By default the value is a binary, however constraints may change +the type of this value (for example automatically converting +numbers to integer). + +=== bindings(Req) -> [{Name, Value}] + +Name = atom():: Binding name. +Value = any():: Binding value. + +Return all bindings. + +By default the value is a binary, however constraints may change +the type of this value (for example automatically converting +numbers to integer). + +=== header(Name, Req) -> header(Name, Req, undefined) + +Alias of `cowboy_req:header/3`. + +=== header(Name, Req, Default) -> Value + +Name = binary():: Request header name. +Default = any():: Default value. +Value = binary() | Default:: Request header value. + +Return the value for the given header. + +While header names are case insensitive, this function expects +the name to be a lowercase binary. + +=== headers(Req) -> Headers + +Headers = cowboy:http_headers():: Request headers. + +Return all headers. + +=== host(Req) -> Host + +Host = binary():: Requested host. + +Return the requested host. + +=== host_info(Req) -> HostInfo + +HostInfo = cowboy_router:tokens() | undefined:: Extra tokens for the host. + +Return the extra tokens from matching against `...` during routing. + +=== host_url(Req) -> HostURL + +HostURL = binary() | undefined:: Requested URL, without the path component. + +Return the requested URL excluding the path component. + +This function will always return `undefined` until the +`cowboy_router` middleware has been executed. + +=== match_cookies(Fields, Req) -> Map + +Fields = cowboy:fields():: Cookie fields match rules. +Map = map():: Cookie fields matched. + +Match cookies against the given fields. + +Cowboy will only return the cookie values specified in the +fields list, and ignore all others. Fields can be either +the name of the cookie requested; the name along with a +list of constraints; or the name, a list of constraints +and a default value in case the cookie is missing. + +This function will crash if the cookie is missing and no +default value is provided. This function will also crash +if a constraint fails. + +The name of the cookie must be provided as an atom. The +key of the returned map will be that atom. The value may +be converted through the use of constraints, making this +function able to extract, validate and convert values all +in one step. + +=== match_qs(Fields, Req) -> Map + +Fields = cowboy:fields():: Query string fields match rules. +Map = map():: Query string fields matched. + +Match the query string against the given fields. + +Cowboy will only return the query string values specified +in the fields list, and ignore all others. Fields can be +either the key requested; the key along with a list of +constraints; or the key, a list of constraints and a +default value in case the key is missing. + +This function will crash if the key is missing and no +default value is provided. This function will also crash +if a constraint fails. + +The key must be provided as an atom. The key of the +returned map will be that atom. The value may be converted +through the use of constraints, making this function able +to extract, validate and convert values all in one step. + +=== meta(Name, Req) -> meta(Name, Req, undefined) + +Alias for `cowboy_req:meta/3`. + +=== meta(Name, Req, Default) -> Value + +Name = atom():: Metadata name. +Default = any():: Default value. +Value = any():: Metadata value. + +Return metadata about the request. + +=== method(Req) -> Method + +Method = binary():: Request method. + +Return the method. + +Methods are case sensitive. Standard methods are always uppercase. + +=== parse_cookies(Req) -> [{Name, Value}] + +Name = binary():: Cookie name. +Value = binary():: Cookie value. + +Parse and return all cookies. + +Cookie names are case sensitive. + +=== parse_header(Name, Req) -> see below + +Alias of `cowboy_req:parse_header/3`. + +The `parse_header/2` function will call `parser_header/3` with a +different default value depending on the header being parsed. The +following table summarizes the default values used. + +[cols="<,^",options="header"] +|=== +| Header name | Header value +| content-length | `0` +| cookie | `[]` +| transfer-encoding | `[<<"identity">>]` +| Any other header | `undefined` +|=== + +=== parse_header(Name, Req, Default) -> ParsedValue | Default + +Name = binary():: Request header name. +Default = any():: Default value. +ParsedValue - see below:: Parsed request header value. + +Parse the given header. + +While header names are case insensitive, this function expects +the name to be a lowercase binary. + +The parsed value differs depending on the header being parsed. The +following table summarizes the different types returned. + +[cols="<,^",options="header"] +|=== +| Header name | Type of parsed header value +| accept | `[{{Type, SubType, Params}, Quality, AcceptExt}]` +| accept-charset | `[{Charset, Quality}]` +| accept-encoding | `[{Encoding, Quality}]` +| accept-language | `[{LanguageTag, Quality}]` +| authorization | `{AuthType, Credentials}` +| content-length | `non_neg_integer()` +| content-type | `{Type, SubType, ContentTypeParams}` +| cookie | `[{binary(), binary()}]` +| expect | `[Expect \| {Expect, ExpectValue, Params}]` +| if-match | `'*' \| [{weak \| strong, OpaqueTag}]` +| if-modified-since | `calendar:datetime()` +| if-none-match | `'*' \| [{weak \| strong, OpaqueTag}]` +| if-unmodified-since | `calendar:datetime()` +| range | `{Unit, [Range]}` +| sec-websocket-protocol | `[binary()]` +| transfer-encoding | `[binary()]` +| upgrade | `[binary()]` +| x-forwarded-for | `[binary()]` +|=== + +Types for the above table: + +* Type = SubType = Charset = Encoding = LanguageTag = binary() +* AuthType = Expect = OpaqueTag = Unit = binary() +* Params = ContentTypeParams = [{binary(), binary()}] +* Quality = 0..1000 +* AcceptExt = [{binary(), binary()} | binary()] +* Credentials - see below +* Range = {non_neg_integer(), non_neg_integer() | infinity} | neg_integer() + +The cookie names and values, the values of the sec-websocket-protocol +and x-forwarded-for headers, the values in `AcceptExt` and `Params`, +the authorization `Credentials`, the `ExpectValue` and `OpaqueTag` +are case sensitive. All values in `ContentTypeParams` are case sensitive +except the value of the charset parameter, which is case insensitive. +All other values are case insensitive and will be returned as lowercase. + +The headers accept, accept-encoding and cookie headers can return +an empty list. Some other headers are expected to have a value if provided +and may crash if the value is missing. + +The authorization header parsing code currently only supports basic +HTTP authentication. The `Credentials` type is thus `{Username, Password}` +with `Username` and `Password` being `binary()`. + +The range header value `Range` can take three forms: + +* `{From, To}`: from `From` to `To` units +* `{From, infinity}`: everything after `From` units +* `-Final`: the final `Final` units + +An `undefined` tuple will be returned if Cowboy doesn't know how +to parse the requested header. + +=== parse_qs(Req) -> [{Name, Value}] + +Name = binary():: Query string field name. +Value = binary() | true:: Query string field value. + +Return the request's query string as a list of tuples. + +The atom `true` is returned for keys which have no value. +Keys with no value are different from keys with an empty +value in that they do not have a `=` indicating the presence +of a value. + +=== path(Req) -> Path + +Path = binary():: Requested path. + +Return the requested path. + +=== path_info(Req) -> PathInfo + +PathInfo = cowboy_router:tokens() | undefined:: Extra tokens for the path. + +Return the extra tokens from matching against `...` during routing. + +=== peer(Req) -> Peer + +Peer = {inet:ip_address(), inet:port_number()}:: Peer IP address and port number. + +Return the client's IP address and port number. + +=== port(Req) -> Port + +Port = inet:port_number():: Requested port number. + +Return the request's port. + +The port returned by this function is obtained by parsing +the host header. It may be different than the actual port +the client used to connect to the Cowboy server. + +=== qs(Req) -> QueryString + +QueryString = binary():: Unprocessed query string. + +Return the request's query string. + +=== set_meta(Name, Value, Req) -> Req2 + +Name = atom():: Metadata name. +Value = any():: Metadata value. + +Set metadata about the request. + +An existing value will be overwritten. + +=== url(Req) -> URL + +URL = binary() | undefined:: Requested URL. + +Return the requested URL. + +This function will always return `undefined` until the +`cowboy_router` middleware has been executed. + +=== version(Req) -> Version + +Version = cowboy:http_version():: Client's advertised HTTP version. + +Return the HTTP version used for this request. + +== Request body related exports + +=== body(Req) -> body(Req, []) + +Alias of `cowboy_req:body/2`. + +=== body(Req, Opts) -> {ok, Data, Req2} | {more, Data, Req2} + +Opts = [body_opt()]:: Request body reading options. +Data = binary():: Data read from the body. + +Read the request body. + +This function will read a chunk of the request body. If there is +more data to be read after this function call, then a `more` tuple +is returned. Otherwise an `ok` tuple is returned. + +Cowboy will automatically send a `100 Continue` reply if +required. If this behavior is not desirable, it can be disabled +by setting the `continue` option to `false`. + +Cowboy will by default attempt to read up to 8MB of the body, +but in chunks of 1MB. It will use a timeout of 15s per chunk. +All these values can be changed using the `length`, `read_length` +and `read_timeout` options respectively. Note that the size +of the data may not be the same as requested as the decoding +functions may grow or shrink it, and Cowboy makes not attempt +at returning an exact amount. + +Cowboy will properly handle chunked transfer-encoding by +default. If any other transfer-encoding or content-encoding +has been used for the request, custom decoding functions +can be used. The `content_decode` and `transfer_decode` +options allow setting the decode functions manually. + +After the body has been streamed fully, Cowboy will remove +the transfer-encoding header from the Req object, and add +the content-length header if it wasn't already there. + +This function can only be called once. Cowboy will not cache +the result of this call. + +=== body_length(Req) -> Length + +Length = non_neg_integer() | undefined:: Length of the request body. + +Return the length of the request body. + +The length will only be returned if the request does not +use any transfer-encoding and if the content-length header +is present. + +=== body_qs(Req) -> body_qs(Req, [{length, 64000}, {read_length, 64000}, {read_timeout, 5000}]) + +Alias of `cowboy_req:body_qs/2`. + +=== body_qs(Req, Opts) -> {ok, [{Name, Value}], Req2} | {badlength, Req2} + +Opts = [body_opt()]:: Request body reading options. +Name = binary():: Field name. +Value = binary() | true:: Field value. + +Return the request body as a list of tuples. + +This function will parse the body assuming the content-type +application/x-www-form-urlencoded, commonly used for the +query string. + +This function calls `body/2` for reading the body, with the +same options it received. By default it will attempt to read +a body of 64KB in one chunk, with a timeout of 5s. If the +body is larger then a `badlength` tuple is returned. + +This function can only be called once. Cowboy will not cache +the result of this call. + +=== has_body(Req) -> boolean() + +Return whether the request has a body. + +=== part(Req) -> part(Req, [{length, 64000}, {read_length, 64000}, {read_timeout, 5000}]) + +Alias of `cowboy_req:part/2`. + +=== part(Req, Opts) -> {ok, Headers, Req2} | {done, Req2} + +Opts = [body_opt()]:: Request body reading options. +Headers = cow_multipart:headers():: Part's headers. + +Read the headers for the next part of the multipart message. + +Cowboy will skip any data remaining until the beginning of +the next part. This includes the preamble to the multipart +message but also the body of a previous part if it hasn't +been read. Both are skipped automatically when calling this +function. + +The headers returned are MIME headers, NOT HTTP headers. +They can be parsed using the functions from the `cow_multipart` +module. In addition, the `cow_multipart:form_data/1` function +can be used to quickly figure out `multipart/form-data` messages. +It takes the list of headers and returns whether this part is +a simple form field or a file being uploaded. + +Note that once a part has been read, or skipped, it cannot +be read again. + +This function calls `body/2` for reading the body, with the +same options it received. By default it will only read chunks +of 64KB with a timeout of 5s. This is tailored for reading +part headers, not for skipping the previous part's body. +You might want to consider skipping large parts manually. + +=== part_body(Req) -> part_body(Req, []) + +Alias of `cowboy_req:part_body/2`. + +=== part_body(Req, Opts) -> {ok, Data, Req2} | {more, Data, Req2} + +Opts = [body_opt()]:: Request body reading options. +Data = binary():: Part's body. + +Read the body of the current part of the multipart message. + +This function calls `body/2` for reading the body, with the +same options it received. It uses the same defaults. + +If there are more data to be read from the socket for this +part, the function will return what it could read inside a +`more` tuple. Otherwise, it will return an `ok` tuple. + +Calling this function again after receiving a `more` tuple +will return another chunk of body. The last chunk will be +returned inside an `ok` tuple. + +Note that once the body has been read, fully or partially, +it cannot be read again. + +== Response related exports + +=== chunk(Data, Req) -> ok + +Data = iodata():: Chunk data to be sent. + +Send a chunk of data. + +This function should be called as many times as needed +to send data chunks after calling `chunked_reply/{2,3}`. + +When the method is HEAD, no data will actually be sent. + +If the request uses HTTP/1.0, the data is sent directly +without wrapping it in an HTTP/1.1 chunk, providing +compatibility with older clients. + +=== chunked_reply(StatusCode, Req) -> chunked_reply(StatusCode, [], Req) + +Alias of `cowboy_req:chunked_reply/3`. + +=== chunked_reply(StatusCode, Headers, Req) -> Req2 + +StatusCode = cowboy:http_status():: Response status code. +Headers = cowboy:http_headers():: Response headers. + +Send a response using chunked transfer-encoding. + +This function effectively sends the response status line +and headers to the client. + +This function will not send any body set previously. After +this call the handler must use the `chunk/2` function +repeatedly to send the body in as many chunks as needed. + +If the request uses HTTP/1.0, the data is sent directly +without wrapping it in an HTTP/1.1 chunk, providing +compatibility with older clients. + +This function can only be called once, with the exception +of overriding the response in the `onresponse` hook. + +=== continue(Req) -> ok + +Send a 100 Continue intermediate reply. + +This reply is required before the client starts sending the +body when the request contains the `expect` header with the +`100-continue` value. + +Cowboy will send this automatically when required. However +you may want to do it manually by disabling this behavior +with the `continue` body option and then calling this +function. + +=== delete_resp_header(Name, Req) -> Req2 + +Name = binary():: Response header name. + +Delete the given response header. + +While header names are case insensitive, this function expects +the name to be a lowercase binary. + +=== has_resp_body(Req) -> boolean() + +Return whether a response body has been set. + +This function will return false if a response body has +been set with a length of 0. + +=== has_resp_header(Name, Req) -> boolean() + +Name = binary():: Response header name. + +Return whether the given response header has been set. + +While header names are case insensitive, this function expects +the name to be a lowercase binary. + +=== reply(StatusCode, Req) -> reply(StatusCode, [], Req) + +Alias of `cowboy_req:reply/3`. + +=== reply(StatusCode, Headers, Req) - see below + +Alias of `cowboy_req:reply/4`, with caveats. + +=== reply(StatusCode, Headers, Body, Req) -> Req2 + +StatusCode = cowboy:http_status():: Response status code. +Headers = cowboy:http_headers():: Response headers. +Body = iodata():: Response body. + +Send a response. + +This function effectively sends the response status line, +headers and body to the client, in a single send function +call. + +The `reply/2` and `reply/3` functions will send the body +set previously, if any. The `reply/4` function overrides +any body set previously and sends `Body` instead. + +If a body function was set, and `reply/2` or `reply/3` was +used, it will be called before returning. + +No more data can be sent to the client after this function +returns. + +This function can only be called once, with the exception +of overriding the response in the `onresponse` hook. + +=== set_resp_body(Body, Req) -> Req2 + +Body = iodata():: Response body. + +Set a response body. + +This body will not be sent if `chunked_reply/{2,3}` or +`reply/4` is used, as they override it. + +=== set_resp_body_fun(Fun, Req) -> Req2 + +Alias of `cowboy_req:set_resp_body_fun/3`. + +=== set_resp_body_fun(Length, Fun, Req) -> Req2 + +Fun = fun((Socket, Transport) -> ok):: Fun that will send the response body. +Socket = inet:socket():: Socket for this connection. +Transport = module():: Transport module for this socket. +Length = non_neg_integer():: Length of the response body. + +Set a fun for sending the response body. + +If a `Length` is provided, it will be sent in the +content-length header in the response. It is recommended +to set the length if it can be known in advance. Otherwise, +the transfer-encoding header will be set to identity. + +This function will only be called if the response is sent +using the `reply/2` or `reply/3` function. + +The fun will receive the Ranch `Socket` and `Transport` as +arguments. Only send and sendfile operations are supported. + +=== set_resp_body_fun(chunked, Fun, Req) -> Req2 + +Fun = fun((ChunkFun) -> ok):: Fun that will send the response body. +ChunkFun = fun((iodata()) -> ok):: Fun to call for every chunk to be sent. + +Set a fun for sending the response body using chunked transfer-encoding. + +This function will only be called if the response is sent +using the `reply/2` or `reply/3` function. + +The fun will receive another fun as argument. This fun is to +be used to send chunks in a similar way to the `chunk/2` function, +except the fun only takes one argument, the data to be sent in +the chunk. + +=== set_resp_cookie(Name, Value, Opts, Req) -> Req2 + +Name = iodata():: Cookie name. +Value = iodata():: Cookie value. +Opts = cookie_opts():: Cookie options. + +Set a cookie in the response. + +Cookie names are case sensitive. + +=== set_resp_header(Name, Value, Req) -> Req2 + +Name = binary():: Response header name. +Value = iodata():: Response header value. + +Set a response header. + +You should use `set_resp_cookie/4` instead of this function +to set cookies. diff --git a/doc/src/manual/cowboy_req.ezdoc b/doc/src/manual/cowboy_req.ezdoc deleted file mode 100644 index 9b73886..0000000 --- a/doc/src/manual/cowboy_req.ezdoc +++ /dev/null @@ -1,715 +0,0 @@ -::: cowboy_req - -The `cowboy_req` module provides functions to access, manipulate -and respond to requests. - -The functions in this module follow patterns for their return types, -based on the kind of function. - -* access: `Value` -* action: `ok | {Result, Req} | {Result, Value, Req}` -* modification: `Req` -* question: `boolean()` - -Whenever `Req` is returned, you must use this returned value and -ignore any previous you may have had. This value contains various -values which are necessary for Cowboy to keep track of the request -and response states. - -All functions which perform an action should only be called once. -This includes reading the request body or replying. Cowboy will -throw an error on the second call when it detects suspicious behavior. - -It is highly discouraged to pass the Req object to another process. -Doing so and calling `cowboy_req` functions from it leads to -undefined behavior. - -:: Types - -: body_opts() = [{continue, boolean()} - | {length, non_neg_integer()} - | {read_length, non_neg_integer()} - | {read_timeout, timeout()} - | {transfer_decode, transfer_decode_fun(), any()} - | {content_decode, content_decode_fun()}] - -Request body reading options. - -: cookie_opts() = [{max_age, non_neg_integer()} - | {domain, binary()} | {path, binary()} - | {secure, boolean()} | {http_only, boolean()}] - -Cookie options. - -: req() - opaque to the user - -The Req object. - -All functions in this module receive a `Req` as argument, -and most of them return a new object labelled `Req2` in -the function descriptions below. - -:: Request related exports - -: binding(Name, Req) -> binding(Name, Req, undefined) -: binding(Name, Req, Default) -> Value - -Types: - -* Name = atom() -* Default = any() -* Value = any() | Default - -Return the value for the given binding. - -By default the value is a binary, however constraints may change -the type of this value (for example automatically converting -numbers to integer). - -: bindings(Req) -> [{Name, Value}] - -Types: - -* Name = atom() -* Value = any() - -Return all bindings. - -By default the value is a binary, however constraints may change -the type of this value (for example automatically converting -numbers to integer). - -: header(Name, Req) -> header(Name, Req, undefined) -: header(Name, Req, Default) -> Value - -Types: - -* Name = binary() -* Default = any() -* Value = binary() | Default - -Return the value for the given header. - -While header names are case insensitive, this function expects -the name to be a lowercase binary. - -: headers(Req) -> Headers - -Types: - -* Headers = cowboy:http_headers() - -Return all headers. - -: host(Req) -> Host - -Types: - -* Host = binary() - -Return the requested host. - -: host_info(Req) -> HostInfo - -Types: - -* HostInfo = cowboy_router:tokens() | undefined - -Return the extra tokens from matching against `...` during routing. - -: host_url(Req) -> HostURL - -Types: - -* HostURL = binary() | undefined - -Return the requested URL excluding the path component. - -This function will always return `undefined` until the -`cowboy_router` middleware has been executed. - -: match_cookies(Fields, Req) -> Map - -Types: - -* Fields = cowboy:fields() -* Map = map() - -Match cookies against the given fields. - -Cowboy will only return the cookie values specified in the -fields list, and ignore all others. Fields can be either -the name of the cookie requested; the name along with a -list of constraints; or the name, a list of constraints -and a default value in case the cookie is missing. - -This function will crash if the cookie is missing and no -default value is provided. This function will also crash -if a constraint fails. - -The name of the cookie must be provided as an atom. The -key of the returned map will be that atom. The value may -be converted through the use of constraints, making this -function able to extract, validate and convert values all -in one step. - -: match_qs(Fields, Req) -> Map - -Types: - -* Fields = cowboy:fields() -* Map = map() - -Match the query string against the given fields. - -Cowboy will only return the query string values specified -in the fields list, and ignore all others. Fields can be -either the key requested; the key along with a list of -constraints; or the key, a list of constraints and a -default value in case the key is missing. - -This function will crash if the key is missing and no -default value is provided. This function will also crash -if a constraint fails. - -The key must be provided as an atom. The key of the -returned map will be that atom. The value may be converted -through the use of constraints, making this function able -to extract, validate and convert values all in one step. - -: meta(Name, Req) -> meta(Name, Req, undefined) -: meta(Name, Req, Default) -> Value - -Types: - -* Name = atom() -* Default = any() -* Value = any() - -Return metadata about the request. - -: method(Req) -> Method - -Types: - -* Method = binary() - -Return the method. - -Methods are case sensitive. Standard methods are always uppercase. - -: parse_cookies(Req) -> [{Name, Value}] - -Types: - -* Name = binary() -* Value = binary() - -Parse and return all cookies. - -Cookie names are case sensitive. - -: parse_header(Name, Req) -> see below -: parse_header(Name, Req, Default) -> ParsedValue | Default - -Types: - -* Name = binary() -* Default = any() -* ParsedValue - see below - -Parse the given header. - -While header names are case insensitive, this function expects -the name to be a lowercase binary. - -The `parse_header/2` function will call `parser_header/3` with a -different default value depending on the header being parsed. The -following table summarizes the default values used. - -|| Header name Default value -| -| content-length `0` -| cookie `[]` -| transfer-encoding `[<<"identity">>]` -| Any other header `undefined` - -The parsed value differs depending on the header being parsed. The -following table summarizes the different types returned. - -|| Header name Type -| -| accept `[{{Type, SubType, Params}, Quality, AcceptExt}]` -| accept-charset `[{Charset, Quality}]` -| accept-encoding `[{Encoding, Quality}]` -| accept-language `[{LanguageTag, Quality}]` -| authorization `{AuthType, Credentials}` -| content-length `non_neg_integer()` -| content-type `{Type, SubType, ContentTypeParams}` -| cookie `[{binary(), binary()}]` -| expect `[Expect | {Expect, ExpectValue, Params}]` -| if-match `'*' | [{weak | strong, OpaqueTag}]` -| if-modified-since `calendar:datetime()` -| if-none-match `'*' | [{weak | strong, OpaqueTag}]` -| if-unmodified-since `calendar:datetime()` -| range `{Unit, [Range]}` -| sec-websocket-protocol `[binary()]` -| transfer-encoding `[binary()]` -| upgrade `[binary()]` -| x-forwarded-for `[binary()]` - -Types for the above table: - -* Type = SubType = Charset = Encoding = LanguageTag = binary() -* AuthType = Expect = OpaqueTag = Unit = binary() -* Params = ContentTypeParams = [{binary(), binary()}] -* Quality = 0..1000 -* AcceptExt = [{binary(), binary()} | binary()] -* Credentials - see below -* Range = {non_neg_integer(), non_neg_integer() | infinity} | neg_integer() - -The cookie names and values, the values of the sec-websocket-protocol -and x-forwarded-for headers, the values in `AcceptExt` and `Params`, -the authorization `Credentials`, the `ExpectValue` and `OpaqueTag` -are case sensitive. All values in `ContentTypeParams` are case sensitive -except the value of the charset parameter, which is case insensitive. -All other values are case insensitive and will be returned as lowercase. - -The headers accept, accept-encoding and cookie headers can return -an empty list. Some other headers are expected to have a value if provided -and may crash if the value is missing. - -The authorization header parsing code currently only supports basic -HTTP authentication. The `Credentials` type is thus `{Username, Password}` -with `Username` and `Password` being `binary()`. - -The range header value `Range` can take three forms: - -* `{From, To}`: from `From` to `To` units -* `{From, infinity}`: everything after `From` units -* `-Final`: the final `Final` units - -An `undefined` tuple will be returned if Cowboy doesn't know how -to parse the requested header. - -: parse_qs(Req) -> [{Name, Value}] - -Types: - -* Name = binary() -* Value = binary() | true - -Return the request's query string as a list of tuples. - -The atom `true` is returned for keys which have no value. -Keys with no value are different from keys with an empty -value in that they do not have a `=` indicating the presence -of a value. - -: path(Req) -> Path - -Types: - -* Path = binary() - -Return the requested path. - -: path_info(Req) -> PathInfo - -Types: - -* PathInfo = cowboy_router:tokens() | undefined - -Return the extra tokens from matching against `...` during routing. - -: peer(Req) -> Peer - -Types: - -* Peer = {inet:ip_address(), inet:port_number()} - -Return the client's IP address and port number. - -: port(Req) -> Port - -Types: - -* Port = inet:port_number() - -Return the request's port. - -The port returned by this function is obtained by parsing -the host header. It may be different than the actual port -the client used to connect to the Cowboy server. - -: qs(Req) -> QueryString - -Types: - -* QueryString = binary() - -Return the request's query string. - -: set_meta(Name, Value, Req) -> Req2 - -Types: - -* Name = atom() -* Value = any() - -Set metadata about the request. - -An existing value will be overwritten. - -: url(Req) -> URL - -Types: - -* URL = binary() | undefined - -Return the requested URL. - -This function will always return `undefined` until the -`cowboy_router` middleware has been executed. - -: version(Req) -> Version - -Types: - -* Version = cowboy:http_version() - -Return the HTTP version used for this request. - -:: Request body related exports - -: body(Req) -> body(Req, []) -: body(Req, Opts) -> {ok, Data, Req2} | {more, Data, Req2} - -Types: - -* Opts = [body_opt()] -* Data = binary() -* Reason = atom() - -Read the request body. - -This function will read a chunk of the request body. If there is -more data to be read after this function call, then a `more` tuple -is returned. Otherwise an `ok` tuple is returned. - -Cowboy will automatically send a `100 Continue` reply if -required. If this behavior is not desirable, it can be disabled -by setting the `continue` option to `false`. - -Cowboy will by default attempt to read up to 8MB of the body, -but in chunks of 1MB. It will use a timeout of 15s per chunk. -All these values can be changed using the `length`, `read_length` -and `read_timeout` options respectively. Note that the size -of the data may not be the same as requested as the decoding -functions may grow or shrink it, and Cowboy makes not attempt -at returning an exact amount. - -Cowboy will properly handle chunked transfer-encoding by -default. If any other transfer-encoding or content-encoding -has been used for the request, custom decoding functions -can be used. The `content_decode` and `transfer_decode` -options allow setting the decode functions manually. - -After the body has been streamed fully, Cowboy will remove -the transfer-encoding header from the Req object, and add -the content-length header if it wasn't already there. - -This function can only be called once. Cowboy will not cache -the result of this call. - -: body_length(Req) -> Length - -Types: - -* Length = non_neg_integer() | undefined - -Return the length of the request body. - -The length will only be returned if the request does not -use any transfer-encoding and if the content-length header -is present. - -: body_qs(Req) -> body_qs(Req, - [{length, 64000}, {read_length, 64000}, {read_timeout, 5000}]) -: body_qs(Req, Opts) -> {ok, [{Name, Value}], Req2} | {badlength, Req2} - -Types: - -* Opts = [body_opt()] -* Name = binary() -* Value = binary() | true -* Reason = chunked | badlength | atom() - -Return the request body as a list of tuples. - -This function will parse the body assuming the content-type -application/x-www-form-urlencoded, commonly used for the -query string. - -This function calls `body/2` for reading the body, with the -same options it received. By default it will attempt to read -a body of 64KB in one chunk, with a timeout of 5s. If the -body is larger then a `badlength` tuple is returned. - -This function can only be called once. Cowboy will not cache -the result of this call. - -: has_body(Req) -> boolean() - -Return whether the request has a body. - -: part(Req) -> part(Req, - [{length, 64000}, {read_length, 64000}, {read_timeout, 5000}]) -: part(Req, Opts) -> {ok, Headers, Req2} | {done, Req2} - -Types: - -* Opts = [body_opt()] -* Headers = cow_multipart:headers() - -Read the headers for the next part of the multipart message. - -Cowboy will skip any data remaining until the beginning of -the next part. This includes the preamble to the multipart -message but also the body of a previous part if it hasn't -been read. Both are skipped automatically when calling this -function. - -The headers returned are MIME headers, NOT HTTP headers. -They can be parsed using the functions from the `cow_multipart` -module. In addition, the `cow_multipart:form_data/1` function -can be used to quickly figure out `multipart/form-data` messages. -It takes the list of headers and returns whether this part is -a simple form field or a file being uploaded. - -Note that once a part has been read, or skipped, it cannot -be read again. - -This function calls `body/2` for reading the body, with the -same options it received. By default it will only read chunks -of 64KB with a timeout of 5s. This is tailored for reading -part headers, not for skipping the previous part's body. -You might want to consider skipping large parts manually. - -: part_body(Req) -> part_body(Req, []) -: part_body(Req, Opts) -> {ok, Data, Req2} | {more, Data, Req2} - -Types: - -* Opts = [body_opt()] -* Data = binary() - -Read the body of the current part of the multipart message. - -This function calls `body/2` for reading the body, with the -same options it received. It uses the same defaults. - -If there are more data to be read from the socket for this -part, the function will return what it could read inside a -`more` tuple. Otherwise, it will return an `ok` tuple. - -Calling this function again after receiving a `more` tuple -will return another chunk of body. The last chunk will be -returned inside an `ok` tuple. - -Note that once the body has been read, fully or partially, -it cannot be read again. - -:: Response related exports - -: chunk(Data, Req) -> ok - -Types: - -* Data = iodata() -* Reason = atom() - -Send a chunk of data. - -This function should be called as many times as needed -to send data chunks after calling `chunked_reply/{2,3}`. - -When the method is HEAD, no data will actually be sent. - -If the request uses HTTP/1.0, the data is sent directly -without wrapping it in an HTTP/1.1 chunk, providing -compatibility with older clients. - -: chunked_reply(StatusCode, Req) -> chunked_reply(StatusCode, [], Req) -: chunked_reply(StatusCode, Headers, Req) -> Req2 - -Types: - -* StatusCode = cowboy:http_status() -* Headers = cowboy:http_headers() - -Send a response using chunked transfer-encoding. - -This function effectively sends the response status line -and headers to the client. - -This function will not send any body set previously. After -this call the handler must use the `chunk/2` function -repeatedly to send the body in as many chunks as needed. - -If the request uses HTTP/1.0, the data is sent directly -without wrapping it in an HTTP/1.1 chunk, providing -compatibility with older clients. - -This function can only be called once, with the exception -of overriding the response in the `onresponse` hook. - -: continue(Req) -> ok - -Types: - -* Reason = atom() - -Send a 100 Continue intermediate reply. - -This reply is required before the client starts sending the -body when the request contains the `expect` header with the -`100-continue` value. - -Cowboy will send this automatically when required. However -you may want to do it manually by disabling this behavior -with the `continue` body option and then calling this -function. - -: delete_resp_header(Name, Req) -> Req2 - -Types: - -* Name = binary() - -Delete the given response header. - -While header names are case insensitive, this function expects -the name to be a lowercase binary. - -: has_resp_body(Req) -> boolean() - -Return whether a response body has been set. - -This function will return false if a response body has -been set with a length of 0. - -: has_resp_header(Name, Req) -> boolean() - -Types: - -* Name = binary() - -Return whether the given response header has been set. - -While header names are case insensitive, this function expects -the name to be a lowercase binary. - -: reply(StatusCode, Req) -> reply(StatusCode, [], Req) -: reply(StatusCode, Headers, Req) - see below -: reply(StatusCode, Headers, Body, Req) -> Req2 - -Types: - -* StatusCode = cowboy:http_status() -* Headers = cowboy:http_headers() -* Body = iodata() - -Send a response. - -This function effectively sends the response status line, -headers and body to the client, in a single send function -call. - -The `reply/2` and `reply/3` functions will send the body -set previously, if any. The `reply/4` function overrides -any body set previously and sends `Body` instead. - -If a body function was set, and `reply/2` or `reply/3` was -used, it will be called before returning. - -No more data can be sent to the client after this function -returns. - -This function can only be called once, with the exception -of overriding the response in the `onresponse` hook. - -: set_resp_body(Body, Req) -> Req2 - -Types: - -* Body = iodata() - -Set a response body. - -This body will not be sent if `chunked_reply/{2,3}` or -`reply/4` is used, as they override it. - -: set_resp_body_fun(Fun, Req) -> Req2 -: set_resp_body_fun(Length, Fun, Req) -> Req2 - -Types: - -* Fun = fun((Socket, Transport) -> ok) -* Socket = inet:socket() -* Transport = module() -* Length = non_neg_integer() - -Set a fun for sending the response body. - -If a `Length` is provided, it will be sent in the -content-length header in the response. It is recommended -to set the length if it can be known in advance. Otherwise, -the transfer-encoding header will be set to identity. - -This function will only be called if the response is sent -using the `reply/2` or `reply/3` function. - -The fun will receive the Ranch `Socket` and `Transport` as -arguments. Only send and sendfile operations are supported. - -: set_resp_body_fun(chunked, Fun, Req) -> Req2 - -Types: - -* Fun = fun((ChunkFun) -> ok) -* ChunkFun = fun((iodata()) -> ok) - -Set a fun for sending the response body using chunked transfer-encoding. - -This function will only be called if the response is sent -using the `reply/2` or `reply/3` function. - -The fun will receive another fun as argument. This fun is to -be used to send chunks in a similar way to the `chunk/2` function, -except the fun only takes one argument, the data to be sent in -the chunk. - -: set_resp_cookie(Name, Value, Opts, Req) -> Req2 - -Types: - -* Name = iodata() -* Value = iodata() -* Opts = cookie_opts() - -Set a cookie in the response. - -Cookie names are case sensitive. - -: set_resp_header(Name, Value, Req) -> Req2 - -Types: - -* Name = binary() -* Value = iodata() - -Set a response header. - -You should use `set_resp_cookie/4` instead of this function -to set cookies. diff --git a/doc/src/manual/cowboy_rest.asciidoc b/doc/src/manual/cowboy_rest.asciidoc new file mode 100644 index 0000000..6380d41 --- /dev/null +++ b/doc/src/manual/cowboy_rest.asciidoc @@ -0,0 +1,529 @@ += cowboy_rest(3) + +== Name + +cowboy_rest - REST handlers + +== Description + +The `cowboy_rest` module implements REST semantics on top of +the HTTP protocol. + +This module is a sub protocol that defines many callbacks +be implemented by handlers. The `init/2` and `terminate/3` +callbacks are common to all handler types and are documented +in the manual for the link:cowboy_handler.asciidoc[cowboy_handler] module. + +All other callbacks are optional, though some may become +required depending on the return value of previous callbacks. + +== Meta values + +charset = binary():: + Negotiated charset. + + + This value may not be defined if no charset was negotiated. + +language = binary():: + Negotiated language. + + + This value may not be defined if no language was negotiated. + +media_type = {binary(), binary(), '*' | [{binary(), binary()}]}:: + Negotiated media-type. + + + The media-type is the content-type, excluding the charset. + + + This value is always defined after the call to + `content_types_provided/2`. + +== Terminate reasons + +The following values may be received as the terminate reason +in the optional `terminate/3` callback. + +normal:: + The connection was closed normally. + +{crash, Class, Reason}:: + A crash occurred in the handler. `Class` and `Reason` can be + used to obtain more information about the crash. The function + `erlang:get_stacktrace/0` can also be called to obtain the + stacktrace of the process when the crash occurred. + +== Callbacks + +=== Callback(Req, State) -> {Value, Req, State} | {stop, Req, State} + +Callback:: One of the REST callbacks described below. +Req = cowboy_req:req():: The Req object. +State = any():: Handler state. +Value:: See the REST callbacks description below. + +Please see the REST callbacks description below for details +on the `Value` type, the default value if the callback is +not defined, and more general information on when the +callback is called and what its intended use is. + +The `stop` tuple can be returned to stop REST processing. +It is up to the resource code to send a reply before that, +otherwise a `204 No Content` will be sent. + +== REST callbacks description + +=== allowed_methods + +Methods:: all +Value type:: [binary()] +Default value:: `[<<"GET">>, <<"HEAD">>, <<"OPTIONS">>]` + +Return the list of allowed methods. + +Methods are case sensitive. Standard methods are always uppercase. + +=== allow_missing_post + +Methods:: POST +Value type:: boolean() +Default value:: true + +Return whether POST is allowed when the resource doesn't exist. + +Returning `true` here means that a new resource will be +created. The URL to the created resource should also be +returned from the `AcceptResource` callback. + +=== charsets_provided + +Methods:: GET, HEAD, POST, PUT, PATCH, DELETE +Value type:: [binary()] +Default behavior:: Skip to the next step if undefined. + +Return the list of charsets the resource provides. + +The list must be ordered in order of preference. + +If the accept-charset header was not sent, the first charset +in the list will be selected. Otherwise Cowboy will select +the most appropriate charset from the list. + +The chosen charset will be set in the `Req` object as the meta +value `charset`. + +While charsets are case insensitive, this callback is expected +to return them as lowercase binary. + +=== content_types_accepted + +Methods:: POST, PUT, PATCH +Value type:: [{binary() | {Type, SubType, Params}, AcceptResource}] +Default behavior:: Crash if undefined. + +With types: + +* Type = SubType = binary() +* Params = '*' | [{binary(), binary()}] +* AcceptResource = atom() + +Return the list of content-types the resource accepts. + +The list must be ordered in order of preference. + +Each content-type can be given either as a binary string or as +a tuple containing the type, subtype and parameters. + +Cowboy will select the most appropriate content-type from the list. +If any parameter is acceptable, then the tuple form should be used +with parameters set to `'*'`. If the parameters value is set to `[]` +only content-type values with no parameters will be accepted. All +parameter values are treated in a case sensitive manner except the +`charset` parameter, if present, which is case insensitive. + +This function will be called for POST, PUT and PATCH requests. +It is entirely possible to define different callbacks for different +methods if the handling of the request differs. Simply verify +what the method is with `cowboy_req:method/1` and return a +different list for each methods. + +The `AcceptResource` value is the name of the callback that will +be called if the content-type matches. It is defined as follow. + +Value type:: true | {true, URL} | false +Default behavior:: Crash if undefined. + +Process the request body. + +This function should create or update the resource with the +information contained in the request body. This information +may be full or partial depending on the request method. + +If the request body was processed successfully, `true` must +be returned. If the request method is POST, `{true, URL}` may +be returned instead, and Cowboy will redirect the client to +the location of the newly created resource. + +If a response body must be sent, the appropriate media-type, charset +and language can be retrieved using the `cowboy_req:meta/{2,3}` +functions. The respective keys are `media_type`, `charset` +and `language`. The body can be set using `cowboy_req:set_resp_body/2`. + +=== content_types_provided + +Methods:: GET, HEAD, POST, PUT, PATCH, DELETE +Value type:: [{binary() | {Type, SubType, Params}, ProvideResource}] +Default value:: `[{{<<"text">>, <<"html">>, '*'}, to_html}]` + +With types: + +* Type = SubType = binary() +* Params = '*' | [{binary(), binary()}] +* ProvideResource = atom() + +Return the list of content-types the resource provides. + +The list must be ordered in order of preference. + +Each content-type can be given either as a binary string or as +a tuple containing the type, subtype and parameters. + +Cowboy will select the most appropriate content-type from the list. +If any parameter is acceptable, then the tuple form should be used +with parameters set to `'*'`. If the parameters value is set to `[]` +only content-type values with no parameters will be accepted. All +parameter values are treated in a case sensitive manner except the +`charset` parameter, if present, which is case insensitive. + +The `ProvideResource` value is the name of the callback that will +be called if the content-type matches. It will only be called when +a representation of the resource needs to be returned. It is defined +as follow. + +Methods:: GET, HEAD +Value type:: iodata() | {stream, Fun} | {stream, Len, Fun} | {chunked, ChunkedFun} +Default behavior:: Crash if undefined. + +Return the response body. + +The response body may be provided directly or through a fun. +If a fun tuple is returned, the appropriate `set_resp_body_fun` +function will be called. Please refer to the documentation for +these functions for more information about the types. + +The call to this callback happens a good time after the call to +`content_types_provided/2`, when it is time to start rendering +the response body. + +=== delete_completed + +Methods:: DELETE +Value type:: boolean() +Default value:: true + +Return whether the delete action has been completed. + +This function should return `false` if there is no guarantee +that the resource gets deleted immediately from the system, +including from any internal cache. + +When this function returns `false`, a `202 Accepted` +response will be sent instead of a `200 OK` or `204 No Content`. + +=== delete_resource + +Methods:: DELETE +Value type:: boolean() +Default value:: false + +Delete the resource. + +The value returned indicates if the action was successful, +regardless of whether the resource is immediately deleted +from the system. + +=== expires + +Methods:: GET, HEAD +Value type:: calendar:datetime() | binary() | undefined +Default value:: undefined + +Return the date of expiration of the resource. + +This date will be sent as the value of the expires header. + +=== forbidden + +Methods:: all +Value type:: boolean() +Default value:: false + +Return whether access to the resource is forbidden. + +A `403 Forbidden` response will be sent if this +function returns `true`. This status code means that +access is forbidden regardless of authentication, +and that the request shouldn't be repeated. + +=== generate_etag + +Methods:: GET, HEAD, POST, PUT, PATCH, DELETE +Value type:: binary() | {weak | strong, binary()} +Default value:: undefined + +Return the entity tag of the resource. + +This value will be sent as the value of the etag header. + +If a binary is returned, then the value will be parsed +to the tuple form automatically. The value must be in +the same format as the etag header, including quotes. + +=== is_authorized + +Methods:: all +Value type:: true | {false, AuthHeader} +Default value:: true + +With types: + +* AuthHead = iodata() + +Return whether the user is authorized to perform the action. + +This function should be used to perform any necessary +authentication of the user before attempting to perform +any action on the resource. + +If the authentication fails, the value returned will be sent +as the value for the www-authenticate header in the +`401 Unauthorized` response. + +=== is_conflict + +Methods:: PUT +Value type:: boolean() +Default value:: false + +Return whether the put action results in a conflict. + +A `409 Conflict` response will be sent if this function +returns `true`. + +=== known_methods + +Methods:: all +Value type:: [binary()] +Default value:: `[<<"GET">>, <<"HEAD">>, <<"POST">>, <<"PUT">>, <<"PATCH">>, <<"DELETE">>, <<"OPTIONS">>]` + +Return the list of known methods. + +The full list of methods known by the server should be +returned, regardless of their use in the resource. + +The default value lists the methods Cowboy knows and +implement in `cowboy_rest`. + +Methods are case sensitive. Standard methods are always uppercase. + +=== languages_provided + +Methods:: GET, HEAD, POST, PUT, PATCH, DELETE +Value type:: [binary()] +Default behavior:: Skip to the next step if undefined. + +Return the list of languages the resource provides. + +The list must be ordered in order of preference. + +If the accept-language header was not sent, the first language +in the list will be selected. Otherwise Cowboy will select +the most appropriate language from the list. + +The chosen language will be set in the `Req` object as the meta +value `language`. + +While languages are case insensitive, this callback is expected +to return them as lowercase binary. + +=== last_modified + +Methods:: GET, HEAD, POST, PUT, PATCH, DELETE +Value type:: calendar:datetime() +Default value:: undefined + +Return the date of last modification of the resource. + +This date will be used to test against the if-modified-since +and if-unmodified-since headers, and sent as the last-modified +header in the response of GET and HEAD requests. + +=== malformed_request + +Methods:: all +Value type:: boolean() +Default value:: false + +Return whether the request is malformed. + +Cowboy has already performed all the necessary checks +by the time this function is called, so few resources +are expected to implement it. + +The check is to be done on the request itself, not on +the request body, which is processed later. + +=== moved_permanently + +Methods:: GET, HEAD, POST, PUT, PATCH, DELETE +Value type:: {true, URL} | false +Default value:: false + +With types: + +* URL = iodata() + +Return whether the resource was permanently moved. + +If it was, its new URL is also returned and sent in the +location header in the response. + +=== moved_temporarily + +Methods:: GET, HEAD, POST, PATCH, DELETE +Value type:: {true, URL} | false +Default value:: false + +With types: + +* URL = iodata() + +Return whether the resource was temporarily moved. + +If it was, its new URL is also returned and sent in the +location header in the response. + +=== multiple_choices + +Methods:: GET, HEAD, POST, PUT, PATCH, DELETE +Value type:: boolean() +Default value:: false + +Return whether there are multiple representations of the resource. + +This function should be used to inform the client if there +are different representations of the resource, for example +different content-type. If this function returns `true`, +the response body should include information about these +different representations using `cowboy_req:set_resp_body/2`. +The content-type of the response should be the one previously +negociated and that can be obtained by calling +`cowboy_req:meta(media_type, Req)`. + +=== options + +Methods:: OPTIONS +Value type:: ok +Default value:: ok + +Handle a request for information. + +The response should inform the client the communication +options available for this resource. + +By default, Cowboy will send a `200 OK` response with the +allow header set. + +=== previously_existed + +Methods:: GET, HEAD, POST, PATCH, DELETE +Value type:: boolean() +Default value:: false + +Return whether the resource existed previously. + +=== resource_exists + +Methods:: GET, HEAD, POST, PUT, PATCH, DELETE +Value type:: boolean() +Default value:: true + +Return whether the resource exists. + +If it exists, conditional headers will be tested before +attempting to perform the action. Otherwise, Cowboy will +check if the resource previously existed first. + +=== service_available + +Methods:: all +Value type:: boolean() +Default value:: true + +Return whether the service is available. + +This function can be used to test that all relevant backend +systems are up and able to handle requests. + +A `503 Service Unavailable` response will be sent if this +function returns `false`. + +=== uri_too_long + +Methods:: all +Value type:: boolean() +Default value:: false + +Return whether the requested URI is too long. + +Cowboy has already performed all the necessary checks +by the time this function is called, so few resources +are expected to implement it. + +A `414 Request-URI Too Long` response will be sent if this +function returns `true`. + +=== valid_content_headers + +Methods:: all +Value type:: boolean() +Default value:: true + +Return whether the content-* headers are valid. + +This also applies to the transfer-encoding header. This +function must return `false` for any unknown content-* +headers, or if the headers can't be understood. The +function `cowboy_req:parse_header/2` can be used to +quickly check the headers can be parsed. + +A `501 Not Implemented` response will be sent if this +function returns `false`. + +=== valid_entity_length + +Methods:: all +Value type:: boolean() +Default value:: true + +Return whether the request body length is within acceptable boundaries. + +A `413 Request Entity Too Large` response will be sent if this +function returns `false`. + +=== variances + +Methods:: GET, HEAD, POST, PUT, PATCH, DELETE +Value type:: [binary()] +Default value:: [] + +Return the list of headers that affect the representation of the resource. + +These request headers return the same resource but with different +parameters, like another language or a different content-type. + +Cowboy will automatically add the accept, accept-language and +accept-charset headers to the list if the respective functions +were defined in the resource. + +This operation is performed right before the `resource_exists/2` +callback. All responses past that point will contain the vary +header which holds this list. diff --git a/doc/src/manual/cowboy_rest.ezdoc b/doc/src/manual/cowboy_rest.ezdoc deleted file mode 100644 index 35131c7..0000000 --- a/doc/src/manual/cowboy_rest.ezdoc +++ /dev/null @@ -1,536 +0,0 @@ -::: cowboy_rest - -The `cowboy_rest` module implements REST semantics on top of -the HTTP protocol. - -This module is a sub protocol that defines many callbacks -be implemented by handlers. The `init/2` and `terminate/3` -callbacks are common to all handler types and are documented -in the manual for the ^cowboy_handler module. - -All other callbacks are optional, though some may become -required depending on the return value of previous callbacks. - -:: Meta values - -: charset - -Type: binary() - -Negotiated charset. - -This value may not be defined if no charset was negotiated. - -: language - -Type: binary() - -Negotiated language. - -This value may not be defined if no language was negotiated. - -: media_type - -Type: {binary(), binary(), '*' | [{binary(), binary()}]} - -Negotiated media-type. - -The media-type is the content-type, excluding the charset. - -This value is always defined after the call to -`content_types_provided/2`. - -:: Terminate reasons - -The following values may be received as the terminate reason -in the optional `terminate/3` callback. - -: normal - -The connection was closed normally. - -: {crash, Class, Reason} - -A crash occurred in the handler. `Class` and `Reason` can be -used to obtain more information about the crash. The function -`erlang:get_stacktrace/0` can also be called to obtain the -stacktrace of the process when the crash occurred. - -:: Callbacks - -: Callback(Req, State) -> {Value, Req, State} | {stop, Req, State} - -Types: - -* Callback - one of the REST callbacks described below -* Req = cowboy_req:req() -* State = any() -* Value - see the REST callbacks description below - -Please see the REST callbacks description below for details -on the `Value` type, the default value if the callback is -not defined, and more general information on when the -callback is called and what its intended use is. - -The `stop` tuple can be returned to stop REST processing. -It is up to the resource code to send a reply before that, -otherwise a `204 No Content` will be sent. - -:: REST callbacks description - -: allowed_methods - -* Methods: all -* Value type: [binary()] -* Default value: [<<"GET">>, <<"HEAD">>, <<"OPTIONS">>] - -Return the list of allowed methods. - -Methods are case sensitive. Standard methods are always uppercase. - -: allow_missing_post - -* Methods: POST -* Value type: boolean() -* Default value: true - -Return whether POST is allowed when the resource doesn't exist. - -Returning `true` here means that a new resource will be -created. The URL to the created resource should also be -returned from the `AcceptResource` callback. - -: charsets_provided - -* Methods: GET, HEAD, POST, PUT, PATCH, DELETE -* Value type: [binary()] -* Skip to the next step if undefined - -Return the list of charsets the resource provides. - -The list must be ordered in order of preference. - -If the accept-charset header was not sent, the first charset -in the list will be selected. Otherwise Cowboy will select -the most appropriate charset from the list. - -The chosen charset will be set in the `Req` object as the meta -value `charset`. - -While charsets are case insensitive, this callback is expected -to return them as lowercase binary. - -: content_types_accepted - -* Methods: POST, PUT, PATCH -* No default - -Types: - -* Value = [{binary() | {Type, SubType, Params}, AcceptResource}] -* Type = SubType = binary() -* Params = '*' | [{binary(), binary()}] -* AcceptResource = atom() - -Return the list of content-types the resource accepts. - -The list must be ordered in order of preference. - -Each content-type can be given either as a binary string or as -a tuple containing the type, subtype and parameters. - -Cowboy will select the most appropriate content-type from the list. -If any parameter is acceptable, then the tuple form should be used -with parameters set to `'*'`. If the parameters value is set to `[]` -only content-type values with no parameters will be accepted. All -parameter values are treated in a case sensitive manner except the -`charset` parameter, if present, which is case insensitive. - -This function will be called for POST, PUT and PATCH requests. -It is entirely possible to define different callbacks for different -methods if the handling of the request differs. Simply verify -what the method is with `cowboy_req:method/1` and return a -different list for each methods. - -The `AcceptResource` value is the name of the callback that will -be called if the content-type matches. It is defined as follow. - -* Value type: true | {true, URL} | false -* No default - -Process the request body. - -This function should create or update the resource with the -information contained in the request body. This information -may be full or partial depending on the request method. - -If the request body was processed successfully, `true` must -be returned. If the request method is POST, `{true, URL}` may -be returned instead, and Cowboy will redirect the client to -the location of the newly created resource. - -If a response body must be sent, the appropriate media-type, charset -and language can be retrieved using the `cowboy_req:meta/{2,3}` -functions. The respective keys are `media_type`, `charset` -and `language`. The body can be set using `cowboy_req:set_resp_body/2`. - -: content_types_provided - -* Methods: GET, HEAD, POST, PUT, PATCH, DELETE -* Default value: [{{<<"text">>, <<"html">>, '*'}, to_html}] - -Types: - -* Value = [{binary() | {Type, SubType, Params}, ProvideResource}] -* Type = SubType = binary() -* Params = '*' | [{binary(), binary()}] -* ProvideResource = atom() - -Return the list of content-types the resource provides. - -The list must be ordered in order of preference. - -Each content-type can be given either as a binary string or as -a tuple containing the type, subtype and parameters. - -Cowboy will select the most appropriate content-type from the list. -If any parameter is acceptable, then the tuple form should be used -with parameters set to `'*'`. If the parameters value is set to `[]` -only content-type values with no parameters will be accepted. All -parameter values are treated in a case sensitive manner except the -`charset` parameter, if present, which is case insensitive. - -The `ProvideResource` value is the name of the callback that will -be called if the content-type matches. It will only be called when -a representation of the resource needs to be returned. It is defined -as follow. - -* Methods: GET, HEAD -* Value type: iodata() | {stream, Fun} | {stream, Len, Fun} | {chunked, ChunkedFun} -* No default - -Return the response body. - -The response body may be provided directly or through a fun. -If a fun tuple is returned, the appropriate `set_resp_body_fun` -function will be called. Please refer to the documentation for -these functions for more information about the types. - -The call to this callback happens a good time after the call to -`content_types_provided/2`, when it is time to start rendering -the response body. - -: delete_completed - -* Methods: DELETE -* Value type: boolean() -* Default value: true - -Return whether the delete action has been completed. - -This function should return `false` if there is no guarantee -that the resource gets deleted immediately from the system, -including from any internal cache. - -When this function returns `false`, a `202 Accepted` -response will be sent instead of a `200 OK` or `204 No Content`. - -: delete_resource - -* Methods: DELETE -* Value type: boolean() -* Default value: false - -Delete the resource. - -The value returned indicates if the action was successful, -regardless of whether the resource is immediately deleted -from the system. - -: expires - -* Methods: GET, HEAD -* Value type: calendar:datetime() | binary() | undefined -* Default value: undefined - -Return the date of expiration of the resource. - -This date will be sent as the value of the expires header. - -: forbidden - -* Methods: all -* Value type: boolean() -* Default value: false - -Return whether access to the resource is forbidden. - -A `403 Forbidden` response will be sent if this -function returns `true`. This status code means that -access is forbidden regardless of authentication, -and that the request shouldn't be repeated. - -: generate_etag - -* Methods: GET, HEAD, POST, PUT, PATCH, DELETE -* Value type: binary() | {weak | strong, binary()} -* Default value: undefined - -Return the entity tag of the resource. - -This value will be sent as the value of the etag header. - -If a binary is returned, then the value will be parsed -to the tuple form automatically. The value must be in -the same format as the etag header, including quotes. - -: is_authorized - -* Methods: all -* Value type: true | {false, AuthHeader} -* Default value: true - -Types: - -* AuthHead = iodata() - -Return whether the user is authorized to perform the action. - -This function should be used to perform any necessary -authentication of the user before attempting to perform -any action on the resource. - -If the authentication fails, the value returned will be sent -as the value for the www-authenticate header in the -`401 Unauthorized` response. - -: is_conflict - -* Methods: PUT -* Value type: boolean() -* Default value: false - -Return whether the put action results in a conflict. - -A `409 Conflict` response will be sent if this function -returns `true`. - -: known_methods - -* Methods: all -* Value type: [binary()] -* Default value: [<<"GET">>, <<"HEAD">>, <<"POST">>, <<"PUT">>, <<"PATCH">>, <<"DELETE">>, <<"OPTIONS">>] - -Return the list of known methods. - -The full list of methods known by the server should be -returned, regardless of their use in the resource. - -The default value lists the methods Cowboy knows and -implement in `cowboy_rest`. - -Methods are case sensitive. Standard methods are always uppercase. - -: languages_provided - -* Methods: GET, HEAD, POST, PUT, PATCH, DELETE -* Value type: [binary()] -* Skip to the next step if undefined - -Return the list of languages the resource provides. - -The list must be ordered in order of preference. - -If the accept-language header was not sent, the first language -in the list will be selected. Otherwise Cowboy will select -the most appropriate language from the list. - -The chosen language will be set in the `Req` object as the meta -value `language`. - -While languages are case insensitive, this callback is expected -to return them as lowercase binary. - -: last_modified - -* Methods: GET, HEAD, POST, PUT, PATCH, DELETE -* Value type: calendar:datetime() -* Default value: undefined - -Return the date of last modification of the resource. - -This date will be used to test against the if-modified-since -and if-unmodified-since headers, and sent as the last-modified -header in the response of GET and HEAD requests. - -: malformed_request - -* Methods: all -* Value type: boolean() -* Default value: false - -Return whether the request is malformed. - -Cowboy has already performed all the necessary checks -by the time this function is called, so few resources -are expected to implement it. - -The check is to be done on the request itself, not on -the request body, which is processed later. - -: moved_permanently - -* Methods: GET, HEAD, POST, PUT, PATCH, DELETE -* Value type: {true, URL} | false -* Default value: false - -Types: - -* URL = iodata() - -Return whether the resource was permanently moved. - -If it was, its new URL is also returned and sent in the -location header in the response. - -: moved_temporarily - -* Methods: GET, HEAD, POST, PATCH, DELETE -* Value type: {true, URL} | false -* Default value: false - -Types: - -* URL = iodata() - -Return whether the resource was temporarily moved. - -If it was, its new URL is also returned and sent in the -location header in the response. - -: multiple_choices - -* Methods: GET, HEAD, POST, PUT, PATCH, DELETE -* Value type: boolean() -* Default value: false - -Return whether there are multiple representations of the resource. - -This function should be used to inform the client if there -are different representations of the resource, for example -different content-type. If this function returns `true`, -the response body should include information about these -different representations using `cowboy_req:set_resp_body/2`. -The content-type of the response should be the one previously -negociated and that can be obtained by calling -`cowboy_req:meta(media_type, Req)`. - -: options - -* Methods: OPTIONS -* Value type: ok -* Default value: ok - -Handle a request for information. - -The response should inform the client the communication -options available for this resource. - -By default, Cowboy will send a `200 OK` response with the -allow header set. - -: previously_existed - -* Methods: GET, HEAD, POST, PATCH, DELETE -* Value type: boolean() -* Default value: false - -Return whether the resource existed previously. - -: resource_exists - -* Methods: GET, HEAD, POST, PUT, PATCH, DELETE -* Value type: boolean() -* Default value: true - -Return whether the resource exists. - -If it exists, conditional headers will be tested before -attempting to perform the action. Otherwise, Cowboy will -check if the resource previously existed first. - -: service_available - -* Methods: all -* Value type: boolean() -* Default value: true - -Return whether the service is available. - -This function can be used to test that all relevant backend -systems are up and able to handle requests. - -A `503 Service Unavailable` response will be sent if this -function returns `false`. - -: uri_too_long - -* Methods: all -* Value type: boolean() -* Default value: false - -Return whether the requested URI is too long. - -Cowboy has already performed all the necessary checks -by the time this function is called, so few resources -are expected to implement it. - -A `414 Request-URI Too Long` response will be sent if this -function returns `true`. - -: valid_content_headers - -* Methods: all -* Value type: boolean() -* Default value: true - -Return whether the content-* headers are valid. - -This also applies to the transfer-encoding header. This -function must return `false` for any unknown content-* -headers, or if the headers can't be understood. The -function `cowboy_req:parse_header/2` can be used to -quickly check the headers can be parsed. - -A `501 Not Implemented` response will be sent if this -function returns `false`. - -: valid_entity_length - -* Methods: all -* Value type: boolean() -* Default value: true - -Return whether the request body length is within acceptable boundaries. - -A `413 Request Entity Too Large` response will be sent if this -function returns `false`. - -: variances - -* Methods: GET, HEAD, POST, PUT, PATCH, DELETE -* Value type: [binary()] -* Default value: [] - -Return the list of headers that affect the representation of the resource. - -These request headers return the same resource but with different -parameters, like another language or a different content-type. - -Cowboy will automatically add the accept, accept-language and -accept-charset headers to the list if the respective functions -were defined in the resource. - -This operation is performed right before the `resource_exists/2` -callback. All responses past that point will contain the vary -header which holds this list. diff --git a/doc/src/manual/cowboy_router.asciidoc b/doc/src/manual/cowboy_router.asciidoc new file mode 100644 index 0000000..9142930 --- /dev/null +++ b/doc/src/manual/cowboy_router.asciidoc @@ -0,0 +1,56 @@ += cowboy_router(3) + +== Name + +cowboy_router - router middleware + +== Description + +The `cowboy_router` middleware maps the requested host and +path to the handler to be used for processing the request. +It uses the dispatch rules compiled from the routes given +to the `compile/1` function for this purpose. It adds the +handler name and options to the environment as the values +`handler` and `handler_opts` respectively. + +=== Environment input + +dispatch = dispatch_rules():: Dispatch table. + +=== Environment output + +handler = module():: Handler module. +handler_opts = any():: Handler options. + +== Types + +=== bindings() = [{atom(), binary()}] + +List of bindings found during routing. + +=== dispatch_rules() - opaque to the user + +Rules for dispatching request used by Cowboy. + +=== routes() = [{Host, Paths} | {Host, cowboy:fields(), Paths}] + +With types: + +* Host = Path = '_' | iodata() +* Paths = [{Path, Handler, Opts} | {Path, cowboy:fields(), Handler, HandlerOpts}] +* Handler = module() +* HandlerOpts = any() + +Human readable list of routes mapping hosts and paths to handlers. + +The syntax for routes is defined in the user guide. + +=== tokens() = [binary()] + +List of host_info and path_info tokens found during routing. + +== Exports + +=== compile(routes()) -> dispatch_rules() + +Compile the routes for use by Cowboy. diff --git a/doc/src/manual/cowboy_router.ezdoc b/doc/src/manual/cowboy_router.ezdoc deleted file mode 100644 index 8d45e67..0000000 --- a/doc/src/manual/cowboy_router.ezdoc +++ /dev/null @@ -1,55 +0,0 @@ -::: cowboy_router - -The `cowboy_router` middleware maps the requested host and -path to the handler to be used for processing the request. -It uses the dispatch rules compiled from the routes given -to the `compile/1` function for this purpose. It adds the -handler name and options to the environment as the values -`handler` and `handler_opts` respectively. - -Environment input: - -* dispatch = dispatch_rules() - -Environment output: - -* handler = module() -* handler_opts = any() - -:: Types - -: bindings() = [{atom(), binary()}] - -List of bindings found during routing. - -: dispatch_rules() - opaque to the user - -Rules for dispatching request used by Cowboy. - -: routes() = [{Host, Paths} | {Host, cowboy:fields(), Paths}] - -Types: - -* Host = Path = '_' | iodata() -* Paths = [{Path, Handler, Opts} | {Path, cowboy:fields(), Handler, Opts}] -* Handler = module() -* Opts = any() - -Human readable list of routes mapping hosts and paths to handlers. - -The syntax for routes is defined in the user guide. - -: tokens() = [binary()] - -List of host_info and path_info tokens found during routing. - -:: Exports - -: compile(Routes) -> Dispatch - -Types: - -* Routes = routes() -* Dispatch = dispatch_rules() - -Compile the routes for use by Cowboy. diff --git a/doc/src/manual/cowboy_spdy.asciidoc b/doc/src/manual/cowboy_spdy.asciidoc new file mode 100644 index 0000000..b0dcb70 --- /dev/null +++ b/doc/src/manual/cowboy_spdy.asciidoc @@ -0,0 +1,42 @@ += cowboy_spdy(3) + +== Name + +cowboy_spdy - SPDY protocol + +== Description + +The `cowboy_spdy` module implements SPDY/3 as a Ranch protocol. + +== Types + +=== opts() = [Option] + +[source,erlang] +---- +Option = {env, cowboy_middleware:env()} + | {middlewares, [module()]} + | {onresponse, cowboy:onresponse_fun()} +---- + +Configuration for the SPDY protocol handler. + +This configuration is passed to Cowboy when starting listeners +using the `cowboy:start_spdy/4` function. + +It can be updated without restarting listeners using the +Ranch functions `ranch:get_protocol_options/1` and +`ranch:set_protocol_options/2`. + +== Option descriptions + +The default value is given next to the option name. + +env ([{listener, Ref}]):: + Initial middleware environment. + +middlewares ([cowboy_router, cowboy_handler]):: + List of middlewares to execute for every requests. + +onresponse (undefined):: + Fun called every time a response is sent. diff --git a/doc/src/manual/cowboy_spdy.ezdoc b/doc/src/manual/cowboy_spdy.ezdoc deleted file mode 100644 index e88de20..0000000 --- a/doc/src/manual/cowboy_spdy.ezdoc +++ /dev/null @@ -1,34 +0,0 @@ -::: cowboy_spdy - -The `cowboy_spdy` module implements SPDY/3 as a Ranch protocol. - -:: Types - -: opts() = [{env, cowboy_middleware:env()} - | {middlewares, [module()]} - | {onresponse, cowboy:onresponse_fun()}] - -Configuration for the SPDY protocol handler. - -This configuration is passed to Cowboy when starting listeners -using the `cowboy:start_spdy/4` function. - -It can be updated without restarting listeners using the -Ranch functions `ranch:get_protocol_options/1` and -`ranch:set_protocol_options/2`. - -:: Option descriptions - -The default value is given next to the option name. - -: env ([{listener, Ref}]) - -Initial middleware environment. - -: middlewares ([cowboy_router, cowboy_handler]) - -List of middlewares to execute for every requests. - -: onresponse (undefined) - -Fun called every time a response is sent. diff --git a/doc/src/manual/cowboy_static.asciidoc b/doc/src/manual/cowboy_static.asciidoc new file mode 100644 index 0000000..658d93b --- /dev/null +++ b/doc/src/manual/cowboy_static.asciidoc @@ -0,0 +1,41 @@ += cowboy_static(3) + +== Name + +cowboy_static - static file handler + +== Description + +The `cowboy_static` module implements file serving capabilities +by using the REST semantics provided by `cowboy_rest`. + +== Types + +=== opts() = [Option] + +[source,erlang] +---- +Option = {priv_file, atom(), string() | binary()} + | {priv_file, atom(), string() | binary(), Extra} + | {file, string() | binary()} + | {file, string() | binary(), Extra} + | {priv_dir, atom(), string() | binary()} + | {priv_dir, atom(), string() | binary(), Extra} + | {dir, string() | binary()} + | {dir, string() | binary(), Extra} + +Extra = [ETag | Mimetypes] + +ETag = {etag, module(), function()} | {etag, false} + +Mimetypes = {mimetypes, module(), function()} + | {mimetypes, binary() | {binary(), binary(), [{binary(), binary()}]}} +---- + +Configuration for the static handler. + +The handler can be configured for sending either one file or +a directory (including its subdirectories). + +Extra options allow you to define how the etag should be calculated +and how the mimetype of files should be detected. diff --git a/doc/src/manual/cowboy_static.ezdoc b/doc/src/manual/cowboy_static.ezdoc deleted file mode 100644 index a827832..0000000 --- a/doc/src/manual/cowboy_static.ezdoc +++ /dev/null @@ -1,32 +0,0 @@ -::: cowboy_static - -The `cowboy_static` module implements file serving capabilities -by using the REST semantics provided by `cowboy_rest`. - -:: Types - -: opts() = {priv_file, atom(), string() | binary()} - | {priv_file, atom(), string() | binary(), extra()} - | {file, string() | binary()} - | {file, string() | binary(), extra()} - | {priv_dir, atom(), string() | binary()} - | {priv_dir, atom(), string() | binary(), extra()} - | {dir, string() | binary()} - | {dir, string() | binary(), extra()} - -Configuration for the static handler. - -The handler can be configured for sending either one file or -a directory (including its subdirectories). - -Extra options allow you to define how the etag should be calculated -and how the mimetype of files should be detected. They are defined -as follow, but do note that these types are not exported, only the -`opts/0` type is public. - -: extra() = [extra_etag() | extra_mimetypes()] - -: extra_etag() = {etag, module(), function()} | {etag, false} - -: extra_mimetypes() = {mimetypes, module(), function()} - | {mimetypes, binary() | {binary(), binary(), [{binary(), binary()}]}} diff --git a/doc/src/manual/cowboy_sub_protocol.asciidoc b/doc/src/manual/cowboy_sub_protocol.asciidoc new file mode 100644 index 0000000..8146a44 --- /dev/null +++ b/doc/src/manual/cowboy_sub_protocol.asciidoc @@ -0,0 +1,27 @@ += cowboy_sub_protocol(3) + +== Name + +cowboy_sub_protocol - sub protocol + +== Description + +The `cowboy_sub_protocol` behaviour defines the interface used +by modules that implement a protocol on top of HTTP. + +== Callbacks + +=== upgrade(Req, Env, Handler, HandlerOpts) -> {ok, Req, Env} | {suspend, Module, Function, Args} | {stop, Req} + +Req = cowboy_req:req():: The Req object. +Env = env():: The request environment. +Handler = module():: Handler module. +Opts = any():: Handler options. +Module = module():: MFA to call when resuming the process. +Function = atom():: MFA to call when resuming the process. +Args = [any()]:: MFA to call when resuming the process. + +Upgrade the protocol. + +Please refer to the `cowboy_middleware` manual for a +description of the return values. diff --git a/doc/src/manual/cowboy_sub_protocol.ezdoc b/doc/src/manual/cowboy_sub_protocol.ezdoc deleted file mode 100644 index ee57beb..0000000 --- a/doc/src/manual/cowboy_sub_protocol.ezdoc +++ /dev/null @@ -1,27 +0,0 @@ -::: cowboy_sub_protocol - -The `cowboy_sub_protocol` behaviour defines the interface used -by modules that implement a protocol on top of HTTP. - -:: Callbacks - -: upgrade(Req, Env, Handler, Opts) - -> {ok, Req, Env} - | {suspend, Module, Function, Args} - | {stop, Req} - -Types: - -* Req = cowboy_req:req() -* Env = env() -* Handler = module() -* Opts = any() -* Module = module() -* Function = atom() -* Args = [any()] -* StatusCode = cowboy:http_status() - -Upgrade the protocol. - -Please refer to the `cowboy_middleware` manual for a -description of the return values. diff --git a/doc/src/manual/cowboy_websocket.asciidoc b/doc/src/manual/cowboy_websocket.asciidoc new file mode 100644 index 0000000..ac9016b --- /dev/null +++ b/doc/src/manual/cowboy_websocket.asciidoc @@ -0,0 +1,143 @@ += cowboy_websocket(3) + +== Name + +cowboy_websocket - Websocket protocol + +== Description + +The `cowboy_websocket` module implements the Websocket protocol. + +This module is a sub protocol that defines four callbacks to +be implemented by handlers. The `init/2` and `terminate/3` +callbacks are common to all handler types and are documented +in the manual for the link:cowboy_handler.asciidoc[cowboy_handler] module. + +The `websocket_handle/3` and `websocket_info/3` callbacks are +specific to Websocket handlers and will be called as many times +as necessary until the Websocket connection is closed. + +The `init/2` callback can be used to negotiate Websocket protocol +extensions with the client. It is highly recommended to return a +timeout value from this callback to ensure that the process is +terminated when no data has been received during that timespan. +The default timeout is `infinity`, which should only be used if +you have alternate means of ending inactive connections. + +Cowboy will terminate the process right after closing the +Websocket connection. This means that there is no real need to +perform any cleanup in the optional `terminate/3` callback. + +== Meta values + +websocket_compress = boolean():: + Whether a websocket compression extension in in use. + +websocket_version = 7 | 8 | 13:: + The version of the Websocket protocol being used. + +== Terminate reasons + +The following values may be received as the terminate reason +in the optional `terminate/3` callback. + +normal:: + The connection was closed normally before establishing a Websocket + connection. This typically happens if an `ok` tuple is returned + from the `init/2` callback. + +remote:: + The remote endpoint closed the connection without giving any + further details. + +{remote, Code, Payload}:: + The remote endpoint closed the connection with the given + `Code` and `Payload` as the reason. + +stop:: + The handler requested to close the connection, either by returning + a `stop` tuple or by sending a `close` frame. + +timeout:: + The connection has been closed due to inactivity. The timeout + value can be configured from `init/2`. + +{crash, Class, Reason}:: + A crash occurred in the handler. `Class` and `Reason` can be + used to obtain more information about the crash. The function + `erlang:get_stacktrace/0` can also be called to obtain the + stacktrace of the process when the crash occurred. + +{error, badencoding}:: + A text frame was sent by the client with invalid encoding. All + text frames must be valid UTF-8. + +{error, badframe}:: + A protocol error has been detected. + +{error, closed}:: + The socket has been closed brutally without a close frame being + received first. + +{error, Reason}:: + A socket error ocurred. + +== Callbacks + +=== websocket_handle(InFrame, Req, State) -> Ret + +[source,erlang] +---- +Ret = {ok, Req, State} + | {ok, Req, State, hibernate} + | {reply, OutFrame | [OutFrame], Req, State} + | {reply, OutFrame | [OutFrame], Req, State, hibernate} + | {stop, Req, State} + +InFrame = {text | binary | ping | pong, binary()} +Req = cowboy_req:req() +State = any() +OutFrame = cow_ws:frame() +---- + +Handle the data received from the Websocket connection. + +This function will be called every time data is received +from the Websocket connection. + +The `stop` return value can be used to close the +connection. A close reply will also result in the connection +being closed. + +The `hibernate` option will hibernate the process until +it receives new data from the Websocket connection or an +Erlang message. + +=== websocket_info(Info, Req, State) -> Ret + +[source,erlang] +---- +Ret = {ok, Req, State} + | {ok, Req, State, hibernate} + | {reply, OutFrame | [OutFrame], Req, State} + | {reply, OutFrame | [OutFrame], Req, State, hibernate} + | {stop, Req, State} + +Info = any() +Req = cowboy_req:req() +State = any() +OutFrame = cow_ws:frame() +---- + +Handle the Erlang message received. + +This function will be called every time an Erlang message +has been received. The message can be any Erlang term. + +The `stop` return value can be used to close the +connection. A close reply will also result in the connection +being closed. + +The `hibernate` option will hibernate the process until +it receives another message or new data from the Websocket +connection. diff --git a/doc/src/manual/cowboy_websocket.ezdoc b/doc/src/manual/cowboy_websocket.ezdoc deleted file mode 100644 index 2519dba..0000000 --- a/doc/src/manual/cowboy_websocket.ezdoc +++ /dev/null @@ -1,149 +0,0 @@ -::: cowboy_websocket - -The `cowboy_websocket` module implements the Websocket protocol. - -This module is a sub protocol that defines four callbacks to -be implemented by handlers. The `init/2` and `terminate/3` -callbacks are common to all handler types and are documented -in the manual for the ^cowboy_handler module. - -The `websocket_handle/3` and `websocket_info/3` callbacks are -specific to Websocket handlers and will be called as many times -as necessary until the Websocket connection is closed. - -The `init/2` callback can be used to negotiate Websocket protocol -extensions with the client. It is highly recommended to return a -timeout value from this callback to ensure that the process is -terminated when no data has been received during that timespan. -The default timeout is `infinity`, which should only be used if -you have alternate means of ending inactive connections. - -Cowboy will terminate the process right after closing the -Websocket connection. This means that there is no real need to -perform any cleanup in the optional `terminate/3` callback. - -:: Meta values - -: websocket_compress - -Type: true | false - -Whether a websocket compression extension in in use. - -: websocket_version - -Type: 7 | 8 | 13 - -The version of the Websocket protocol being used. - -:: Terminate reasons - -The following values may be received as the terminate reason -in the optional `terminate/3` callback. - -: normal - -The connection was closed normally before establishing a Websocket -connection. This typically happens if an `ok` tuple is returned -from the `init/2` callback. - -: remote - -The remote endpoint closed the connection without giving any -further details. - -: {remote, Code, Payload} - -The remote endpoint closed the connection with the given -`Code` and `Payload` as the reason. - -: stop - -The handler requested to close the connection, either by returning -a `stop` tuple or by sending a `close` frame. - -: timeout - -The connection has been closed due to inactivity. The timeout -value can be configured from `init/2`. - -: {crash, Class, Reason} - -A crash occurred in the handler. `Class` and `Reason` can be -used to obtain more information about the crash. The function -`erlang:get_stacktrace/0` can also be called to obtain the -stacktrace of the process when the crash occurred. - -: {error, badencoding} - -A text frame was sent by the client with invalid encoding. All -text frames must be valid UTF-8. - -: {error, badframe} - -A protocol error has been detected. - -: {error, closed} - -The socket has been closed brutally without a close frame being -received first. - -: {error, Reason} - -A socket error ocurred. - -:: Callbacks - -: websocket_handle(InFrame, Req, State) - -> {ok, Req, State} - | {ok, Req, State, hibernate} - | {reply, OutFrame | [OutFrame], Req, State} - | {reply, OutFrame | [OutFrame], Req, State, hibernate} - | {stop, Req, State} - -Types: - -* InFrame = {text | binary | ping | pong, binary()} -* Req = cowboy_req:req() -* State = any() -* OutFrame = cow_ws:frame() - -Handle the data received from the Websocket connection. - -This function will be called every time data is received -from the Websocket connection. - -The `stop` return value can be used to close the -connection. A close reply will also result in the connection -being closed. - -The `hibernate` option will hibernate the process until -it receives new data from the Websocket connection or an -Erlang message. - -: websocket_info(Info, Req, State) - -> {ok, Req, State} - | {ok, Req, State, hibernate} - | {reply, OutFrame | [OutFrame], Req, State} - | {reply, OutFrame | [OutFrame], Req, State, hibernate} - | {stop, Req, State} - -Types: - -* Info = any() -* Req = cowboy_req:req() -* State = any() -* OutFrame = cow_ws:frame() - -Handle the Erlang message received. - -This function will be called every time an Erlang message -has been received. The message can be any Erlang term. - -The `stop` return value can be used to close the -connection. A close reply will also result in the connection -being closed. - -The `hibernate` option will hibernate the process until -it receives another message or new data from the Websocket -connection. diff --git a/doc/src/manual/http_status_codes.asciidoc b/doc/src/manual/http_status_codes.asciidoc new file mode 100644 index 0000000..172272a --- /dev/null +++ b/doc/src/manual/http_status_codes.asciidoc @@ -0,0 +1,157 @@ += HTTP status codes(7) + +== Name + +HTTP status codes - status codes used by Cowboy + +== Description + +This chapter aims to list all HTTP status codes that Cowboy +may return, with details on the reasons why. The list given +here only includes the replies that Cowboy sends, not user +replies. + +== 100 Continue + +When the client sends an `expect: 100-continue` header, +Cowboy automatically sends a this status code before +trying to read the request body. This behavior can be +disabled using the appropriate body option. + +== 101 Switching Protocols + +This is the status code sent when switching to the +Websocket protocol. + +== 200 OK + +This status code is sent by `cowboy_rest`. + +== 201 Created + +This status code is sent by `cowboy_rest`. + +== 202 Accepted + +This status code is sent by `cowboy_rest`. + +== 204 No Content + +This status code is sent when the processing of a request +ends without any reply having been sent. It may also be +sent by `cowboy_rest` under normal conditions. + +== 300 Multiple Choices + +This status code is sent by `cowboy_rest`. + +== 301 Moved Permanently + +This status code is sent by `cowboy_rest`. + +== 303 See Other + +This status code is sent by `cowboy_rest`. + +== 304 Not Modified + +This status code is sent by `cowboy_rest`. + +== 307 Temporary Redirect + +This status code is sent by `cowboy_rest`. + +== 400 Bad Request + +Cowboy will send this status code for any of the +following reasons: + +* Too many empty lines were sent before the request. +* The request-line could not be parsed. +* Too many headers were sent. +* A header name was too long. +* A header value was too long. +* The host header was missing from an HTTP/1.1 request. +* The host header could not be parsed. +* The requested host was not found. +* The requested path could not be parsed. +* The accept header could not be parsed when using REST. +* REST under normal conditions. +* A Websocket upgrade failed. + +== 401 Unauthorized + +This status code is sent by `cowboy_rest`. + +== 403 Forbidden + +This status code is sent by `cowboy_rest`. + +== 404 Not Found + +This status code is sent when the router successfully +resolved the host but didn't find a matching path for +the request. It may also be sent by `cowboy_rest` under +normal conditions. + +== 405 Method Not Allowed + +This status code is sent by `cowboy_rest`. + +== 406 Not Acceptable + +This status code is sent by `cowboy_rest`. + +== 408 Request Timeout + +Cowboy will send this status code to the client if the +client started to send a request, indicated by the +request-line being received fully, but failed to send +all headers in a reasonable time. + +== 409 Conflict + +This status code is sent by `cowboy_rest`. + +== 410 Gone + +This status code is sent by `cowboy_rest`. + +== 412 Precondition Failed + +This status code is sent by `cowboy_rest`. + +== 413 Request Entity Too Large + +This status code is sent by `cowboy_rest`. + +== 414 Request-URI Too Long + +Cowboy will send this status code to the client if the +request-line is too long. It may also be sent by +`cowboy_rest` under normal conditions. + +== 415 Unsupported Media Type + +This status code is sent by `cowboy_rest`. + +== 500 Internal Server Error + +This status code is sent when a crash occurs in HTTP, loop +or REST handlers, or when an invalid return value is +returned. It may also be sent by `cowboy_rest` under +normal conditions. + +== 501 Not Implemented + +This status code is sent by `cowboy_rest`. + +== 503 Service Unavailable + +This status code is sent by `cowboy_rest`. + +== 505 HTTP Version Not Supported + +Cowboy only supports the versions 1.0 and 1.1 of HTTP. +In all other cases this status code is sent back to the +client and the connection is closed. diff --git a/doc/src/manual/http_status_codes.ezdoc b/doc/src/manual/http_status_codes.ezdoc deleted file mode 100644 index 4d24b20..0000000 --- a/doc/src/manual/http_status_codes.ezdoc +++ /dev/null @@ -1,151 +0,0 @@ -::: HTTP status codes - -This chapter aims to list all HTTP status codes that Cowboy -may return, with details on the reasons why. The list given -here only includes the replies that Cowboy sends, not user -replies. - -: 100 Continue - -When the client sends an `expect: 100-continue` header, -Cowboy automatically sends a this status code before -trying to read the request body. This behavior can be -disabled using the appropriate body option. - -: 101 Switching Protocols - -This is the status code sent when switching to the -Websocket protocol. - -: 200 OK - -This status code is sent by `cowboy_rest`. - -: 201 Created - -This status code is sent by `cowboy_rest`. - -: 202 Accepted - -This status code is sent by `cowboy_rest`. - -: 204 No Content - -This status code is sent when the processing of a request -ends without any reply having been sent. It may also be -sent by `cowboy_rest` under normal conditions. - -: 300 Multiple Choices - -This status code is sent by `cowboy_rest`. - -: 301 Moved Permanently - -This status code is sent by `cowboy_rest`. - -: 303 See Other - -This status code is sent by `cowboy_rest`. - -: 304 Not Modified - -This status code is sent by `cowboy_rest`. - -: 307 Temporary Redirect - -This status code is sent by `cowboy_rest`. - -: 400 Bad Request - -Cowboy will send this status code for any of the -following reasons: - -* Too many empty lines were sent before the request. -* The request-line could not be parsed. -* Too many headers were sent. -* A header name was too long. -* A header value was too long. -* The host header was missing from an HTTP/1.1 request. -* The host header could not be parsed. -* The requested host was not found. -* The requested path could not be parsed. -* The accept header could not be parsed when using REST. -* REST under normal conditions. -* A Websocket upgrade failed. - -: 401 Unauthorized - -This status code is sent by `cowboy_rest`. - -: 403 Forbidden - -This status code is sent by `cowboy_rest`. - -: 404 Not Found - -This status code is sent when the router successfully -resolved the host but didn't find a matching path for -the request. It may also be sent by `cowboy_rest` under -normal conditions. - -: 405 Method Not Allowed - -This status code is sent by `cowboy_rest`. - -: 406 Not Acceptable - -This status code is sent by `cowboy_rest`. - -: 408 Request Timeout - -Cowboy will send this status code to the client if the -client started to send a request, indicated by the -request-line being received fully, but failed to send -all headers in a reasonable time. - -: 409 Conflict - -This status code is sent by `cowboy_rest`. - -: 410 Gone - -This status code is sent by `cowboy_rest`. - -: 412 Precondition Failed - -This status code is sent by `cowboy_rest`. - -: 413 Request Entity Too Large - -This status code is sent by `cowboy_rest`. - -: 414 Request-URI Too Long - -Cowboy will send this status code to the client if the -request-line is too long. It may also be sent by -`cowboy_rest` under normal conditions. - -: 415 Unsupported Media Type - -This status code is sent by `cowboy_rest`. - -: 500 Internal Server Error - -This status code is sent when a crash occurs in HTTP, loop -or REST handlers, or when an invalid return value is -returned. It may also be sent by `cowboy_rest` under -normal conditions. - -: 501 Not Implemented - -This status code is sent by `cowboy_rest`. - -: 503 Service Unavailable - -This status code is sent by `cowboy_rest`. - -: 505 HTTP Version Not Supported - -Cowboy only supports the versions 1.0 and 1.1 of HTTP. -In all other cases this status code is sent back to the -client and the connection is closed. diff --git a/doc/src/manual/index.ezdoc b/doc/src/manual/index.ezdoc deleted file mode 100644 index 133a341..0000000 --- a/doc/src/manual/index.ezdoc +++ /dev/null @@ -1,18 +0,0 @@ -::: Cowboy Function Reference - -The function reference documents the public interface of Cowboy. - -* ^"The Cowboy Application^cowboy_app -* ^cowboy -* ^cowboy_handler -* ^cowboy_loop -* ^cowboy_middleware -* ^cowboy_protocol -* ^cowboy_req -* ^cowboy_rest -* ^cowboy_router -* ^cowboy_spdy -* ^cowboy_static -* ^cowboy_sub_protocol -* ^cowboy_websocket -* ^"HTTP status codes^http_status_codes diff --git a/erlang.mk b/erlang.mk index 9ac51e1..a59b6ed 100644 --- a/erlang.mk +++ b/erlang.mk @@ -16,7 +16,7 @@ ERLANG_MK_FILENAME := $(realpath $(lastword $(MAKEFILE_LIST))) -ERLANG_MK_VERSION = 2.0.0-pre.1-38-gbd6e006-dirty +ERLANG_MK_VERSION = 2.0.0-pre.2-72-g86ddfb5 # Core configuration. @@ -30,9 +30,11 @@ PROJECT_VERSION ?= rolling V ?= 0 verbose_0 = @ +verbose_2 = set -x; verbose = $(verbose_$(V)) gen_verbose_0 = @echo " GEN " $@; +gen_verbose_2 = set -x; gen_verbose = $(gen_verbose_$(V)) # Temporary files directory. @@ -76,15 +78,13 @@ endif # Core targets. -.NOTPARALLEL: - all:: deps app rel # Noop to avoid a Make warning when there's nothing to do. rel:: - $(verbose) echo -n + $(verbose) : -check:: clean app tests +check:: tests clean:: clean-crashdump @@ -192,14 +192,3844 @@ ERLANG_MK_BUILD_DIR ?= .erlang.mk.build erlang-mk: git clone $(ERLANG_MK_REPO) $(ERLANG_MK_BUILD_DIR) +ifdef ERLANG_MK_COMMIT + cd $(ERLANG_MK_BUILD_DIR) && git checkout $(ERLANG_MK_COMMIT) +endif if [ -f $(ERLANG_MK_BUILD_CONFIG) ]; then cp $(ERLANG_MK_BUILD_CONFIG) $(ERLANG_MK_BUILD_DIR)/build.config; fi - cd $(ERLANG_MK_BUILD_DIR) && $(if $(ERLANG_MK_COMMIT),git checkout $(ERLANG_MK_COMMIT) &&) $(MAKE) + $(MAKE) -C $(ERLANG_MK_BUILD_DIR) cp $(ERLANG_MK_BUILD_DIR)/erlang.mk ./erlang.mk rm -rf $(ERLANG_MK_BUILD_DIR) # The erlang.mk package index is bundled in the default erlang.mk build. # Search for the string "copyright" to skip to the rest of the code. +PACKAGES += aberth +pkg_aberth_name = aberth +pkg_aberth_description = Generic BERT-RPC server in Erlang +pkg_aberth_homepage = https://github.com/a13x/aberth +pkg_aberth_fetch = git +pkg_aberth_repo = https://github.com/a13x/aberth +pkg_aberth_commit = master + +PACKAGES += active +pkg_active_name = active +pkg_active_description = Active development for Erlang: rebuild and reload source/binary files while the VM is running +pkg_active_homepage = https://github.com/proger/active +pkg_active_fetch = git +pkg_active_repo = https://github.com/proger/active +pkg_active_commit = master + +PACKAGES += actordb_core +pkg_actordb_core_name = actordb_core +pkg_actordb_core_description = ActorDB main source +pkg_actordb_core_homepage = http://www.actordb.com/ +pkg_actordb_core_fetch = git +pkg_actordb_core_repo = https://github.com/biokoda/actordb_core +pkg_actordb_core_commit = master + +PACKAGES += actordb_thrift +pkg_actordb_thrift_name = actordb_thrift +pkg_actordb_thrift_description = Thrift API for ActorDB +pkg_actordb_thrift_homepage = http://www.actordb.com/ +pkg_actordb_thrift_fetch = git +pkg_actordb_thrift_repo = https://github.com/biokoda/actordb_thrift +pkg_actordb_thrift_commit = master + +PACKAGES += aleppo +pkg_aleppo_name = aleppo +pkg_aleppo_description = Alternative Erlang Pre-Processor +pkg_aleppo_homepage = https://github.com/ErlyORM/aleppo +pkg_aleppo_fetch = git +pkg_aleppo_repo = https://github.com/ErlyORM/aleppo +pkg_aleppo_commit = master + +PACKAGES += alog +pkg_alog_name = alog +pkg_alog_description = Simply the best logging framework for Erlang +pkg_alog_homepage = https://github.com/siberian-fast-food/alogger +pkg_alog_fetch = git +pkg_alog_repo = https://github.com/siberian-fast-food/alogger +pkg_alog_commit = master + +PACKAGES += amqp_client +pkg_amqp_client_name = amqp_client +pkg_amqp_client_description = RabbitMQ Erlang AMQP client +pkg_amqp_client_homepage = https://www.rabbitmq.com/erlang-client-user-guide.html +pkg_amqp_client_fetch = git +pkg_amqp_client_repo = https://github.com/rabbitmq/rabbitmq-erlang-client.git +pkg_amqp_client_commit = master + +PACKAGES += annotations +pkg_annotations_name = annotations +pkg_annotations_description = Simple code instrumentation utilities +pkg_annotations_homepage = https://github.com/hyperthunk/annotations +pkg_annotations_fetch = git +pkg_annotations_repo = https://github.com/hyperthunk/annotations +pkg_annotations_commit = master + +PACKAGES += antidote +pkg_antidote_name = antidote +pkg_antidote_description = Large-scale computation without synchronisation +pkg_antidote_homepage = https://syncfree.lip6.fr/ +pkg_antidote_fetch = git +pkg_antidote_repo = https://github.com/SyncFree/antidote +pkg_antidote_commit = master + +PACKAGES += apns +pkg_apns_name = apns +pkg_apns_description = Apple Push Notification Server for Erlang +pkg_apns_homepage = http://inaka.github.com/apns4erl +pkg_apns_fetch = git +pkg_apns_repo = https://github.com/inaka/apns4erl +pkg_apns_commit = 1.0.4 + +PACKAGES += azdht +pkg_azdht_name = azdht +pkg_azdht_description = Azureus Distributed Hash Table (DHT) in Erlang +pkg_azdht_homepage = https://github.com/arcusfelis/azdht +pkg_azdht_fetch = git +pkg_azdht_repo = https://github.com/arcusfelis/azdht +pkg_azdht_commit = master + +PACKAGES += backoff +pkg_backoff_name = backoff +pkg_backoff_description = Simple exponential backoffs in Erlang +pkg_backoff_homepage = https://github.com/ferd/backoff +pkg_backoff_fetch = git +pkg_backoff_repo = https://github.com/ferd/backoff +pkg_backoff_commit = master + +PACKAGES += barrel_tcp +pkg_barrel_tcp_name = barrel_tcp +pkg_barrel_tcp_description = barrel is a generic TCP acceptor pool with low latency in Erlang. +pkg_barrel_tcp_homepage = https://github.com/benoitc-attic/barrel_tcp +pkg_barrel_tcp_fetch = git +pkg_barrel_tcp_repo = https://github.com/benoitc-attic/barrel_tcp +pkg_barrel_tcp_commit = master + +PACKAGES += basho_bench +pkg_basho_bench_name = basho_bench +pkg_basho_bench_description = A load-generation and testing tool for basically whatever you can write a returning Erlang function for. +pkg_basho_bench_homepage = https://github.com/basho/basho_bench +pkg_basho_bench_fetch = git +pkg_basho_bench_repo = https://github.com/basho/basho_bench +pkg_basho_bench_commit = master + +PACKAGES += bcrypt +pkg_bcrypt_name = bcrypt +pkg_bcrypt_description = Bcrypt Erlang / C library +pkg_bcrypt_homepage = https://github.com/riverrun/branglecrypt +pkg_bcrypt_fetch = git +pkg_bcrypt_repo = https://github.com/riverrun/branglecrypt +pkg_bcrypt_commit = master + +PACKAGES += beam +pkg_beam_name = beam +pkg_beam_description = BEAM emulator written in Erlang +pkg_beam_homepage = https://github.com/tonyrog/beam +pkg_beam_fetch = git +pkg_beam_repo = https://github.com/tonyrog/beam +pkg_beam_commit = master + +PACKAGES += beanstalk +pkg_beanstalk_name = beanstalk +pkg_beanstalk_description = An Erlang client for beanstalkd +pkg_beanstalk_homepage = https://github.com/tim/erlang-beanstalk +pkg_beanstalk_fetch = git +pkg_beanstalk_repo = https://github.com/tim/erlang-beanstalk +pkg_beanstalk_commit = master + +PACKAGES += bear +pkg_bear_name = bear +pkg_bear_description = a set of statistics functions for erlang +pkg_bear_homepage = https://github.com/boundary/bear +pkg_bear_fetch = git +pkg_bear_repo = https://github.com/boundary/bear +pkg_bear_commit = master + +PACKAGES += bertconf +pkg_bertconf_name = bertconf +pkg_bertconf_description = Make ETS tables out of statc BERT files that are auto-reloaded +pkg_bertconf_homepage = https://github.com/ferd/bertconf +pkg_bertconf_fetch = git +pkg_bertconf_repo = https://github.com/ferd/bertconf +pkg_bertconf_commit = master + +PACKAGES += bifrost +pkg_bifrost_name = bifrost +pkg_bifrost_description = Erlang FTP Server Framework +pkg_bifrost_homepage = https://github.com/thorstadt/bifrost +pkg_bifrost_fetch = git +pkg_bifrost_repo = https://github.com/thorstadt/bifrost +pkg_bifrost_commit = master + +PACKAGES += binpp +pkg_binpp_name = binpp +pkg_binpp_description = Erlang Binary Pretty Printer +pkg_binpp_homepage = https://github.com/jtendo/binpp +pkg_binpp_fetch = git +pkg_binpp_repo = https://github.com/jtendo/binpp +pkg_binpp_commit = master + +PACKAGES += bisect +pkg_bisect_name = bisect +pkg_bisect_description = Ordered fixed-size binary dictionary in Erlang +pkg_bisect_homepage = https://github.com/knutin/bisect +pkg_bisect_fetch = git +pkg_bisect_repo = https://github.com/knutin/bisect +pkg_bisect_commit = master + +PACKAGES += bitcask +pkg_bitcask_name = bitcask +pkg_bitcask_description = because you need another a key/value storage engine +pkg_bitcask_homepage = https://github.com/basho/bitcask +pkg_bitcask_fetch = git +pkg_bitcask_repo = https://github.com/basho/bitcask +pkg_bitcask_commit = master + +PACKAGES += bitstore +pkg_bitstore_name = bitstore +pkg_bitstore_description = A document based ontology development environment +pkg_bitstore_homepage = https://github.com/bdionne/bitstore +pkg_bitstore_fetch = git +pkg_bitstore_repo = https://github.com/bdionne/bitstore +pkg_bitstore_commit = master + +PACKAGES += bootstrap +pkg_bootstrap_name = bootstrap +pkg_bootstrap_description = A simple, yet powerful Erlang cluster bootstrapping application. +pkg_bootstrap_homepage = https://github.com/schlagert/bootstrap +pkg_bootstrap_fetch = git +pkg_bootstrap_repo = https://github.com/schlagert/bootstrap +pkg_bootstrap_commit = master + +PACKAGES += boss_db +pkg_boss_db_name = boss_db +pkg_boss_db_description = BossDB: a sharded, caching, pooling, evented ORM for Erlang +pkg_boss_db_homepage = https://github.com/ErlyORM/boss_db +pkg_boss_db_fetch = git +pkg_boss_db_repo = https://github.com/ErlyORM/boss_db +pkg_boss_db_commit = master + +PACKAGES += boss +pkg_boss_name = boss +pkg_boss_description = Erlang web MVC, now featuring Comet +pkg_boss_homepage = https://github.com/ChicagoBoss/ChicagoBoss +pkg_boss_fetch = git +pkg_boss_repo = https://github.com/ChicagoBoss/ChicagoBoss +pkg_boss_commit = master + +PACKAGES += brod +pkg_brod_name = brod +pkg_brod_description = Kafka client in Erlang +pkg_brod_homepage = https://github.com/klarna/brod +pkg_brod_fetch = git +pkg_brod_repo = https://github.com/klarna/brod.git +pkg_brod_commit = master + +PACKAGES += bson +pkg_bson_name = bson +pkg_bson_description = BSON documents in Erlang, see bsonspec.org +pkg_bson_homepage = https://github.com/comtihon/bson-erlang +pkg_bson_fetch = git +pkg_bson_repo = https://github.com/comtihon/bson-erlang +pkg_bson_commit = master + +PACKAGES += bullet +pkg_bullet_name = bullet +pkg_bullet_description = Simple, reliable, efficient streaming for Cowboy. +pkg_bullet_homepage = http://ninenines.eu +pkg_bullet_fetch = git +pkg_bullet_repo = https://github.com/ninenines/bullet +pkg_bullet_commit = master + +PACKAGES += cache +pkg_cache_name = cache +pkg_cache_description = Erlang in-memory cache +pkg_cache_homepage = https://github.com/fogfish/cache +pkg_cache_fetch = git +pkg_cache_repo = https://github.com/fogfish/cache +pkg_cache_commit = master + +PACKAGES += cake +pkg_cake_name = cake +pkg_cake_description = Really simple terminal colorization +pkg_cake_homepage = https://github.com/darach/cake-erl +pkg_cake_fetch = git +pkg_cake_repo = https://github.com/darach/cake-erl +pkg_cake_commit = v0.1.2 + +PACKAGES += carotene +pkg_carotene_name = carotene +pkg_carotene_description = Real-time server +pkg_carotene_homepage = https://github.com/carotene/carotene +pkg_carotene_fetch = git +pkg_carotene_repo = https://github.com/carotene/carotene +pkg_carotene_commit = master + +PACKAGES += cberl +pkg_cberl_name = cberl +pkg_cberl_description = NIF based Erlang bindings for Couchbase +pkg_cberl_homepage = https://github.com/chitika/cberl +pkg_cberl_fetch = git +pkg_cberl_repo = https://github.com/chitika/cberl +pkg_cberl_commit = master + +PACKAGES += cecho +pkg_cecho_name = cecho +pkg_cecho_description = An ncurses library for Erlang +pkg_cecho_homepage = https://github.com/mazenharake/cecho +pkg_cecho_fetch = git +pkg_cecho_repo = https://github.com/mazenharake/cecho +pkg_cecho_commit = master + +PACKAGES += cferl +pkg_cferl_name = cferl +pkg_cferl_description = Rackspace / Open Stack Cloud Files Erlang Client +pkg_cferl_homepage = https://github.com/ddossot/cferl +pkg_cferl_fetch = git +pkg_cferl_repo = https://github.com/ddossot/cferl +pkg_cferl_commit = master + +PACKAGES += chaos_monkey +pkg_chaos_monkey_name = chaos_monkey +pkg_chaos_monkey_description = This is The CHAOS MONKEY. It will kill your processes. +pkg_chaos_monkey_homepage = https://github.com/dLuna/chaos_monkey +pkg_chaos_monkey_fetch = git +pkg_chaos_monkey_repo = https://github.com/dLuna/chaos_monkey +pkg_chaos_monkey_commit = master + +PACKAGES += check_node +pkg_check_node_name = check_node +pkg_check_node_description = Nagios Scripts for monitoring Riak +pkg_check_node_homepage = https://github.com/basho-labs/riak_nagios +pkg_check_node_fetch = git +pkg_check_node_repo = https://github.com/basho-labs/riak_nagios +pkg_check_node_commit = master + +PACKAGES += chronos +pkg_chronos_name = chronos +pkg_chronos_description = Timer module for Erlang that makes it easy to abstact time out of the tests. +pkg_chronos_homepage = https://github.com/lehoff/chronos +pkg_chronos_fetch = git +pkg_chronos_repo = https://github.com/lehoff/chronos +pkg_chronos_commit = master + +PACKAGES += classifier +pkg_classifier_name = classifier +pkg_classifier_description = An Erlang Bayesian Filter and Text Classifier +pkg_classifier_homepage = https://github.com/inaka/classifier +pkg_classifier_fetch = git +pkg_classifier_repo = https://github.com/inaka/classifier +pkg_classifier_commit = master + +PACKAGES += clique +pkg_clique_name = clique +pkg_clique_description = CLI Framework for Erlang +pkg_clique_homepage = https://github.com/basho/clique +pkg_clique_fetch = git +pkg_clique_repo = https://github.com/basho/clique +pkg_clique_commit = develop + +PACKAGES += cl +pkg_cl_name = cl +pkg_cl_description = OpenCL binding for Erlang +pkg_cl_homepage = https://github.com/tonyrog/cl +pkg_cl_fetch = git +pkg_cl_repo = https://github.com/tonyrog/cl +pkg_cl_commit = master + +PACKAGES += cloudi_core +pkg_cloudi_core_name = cloudi_core +pkg_cloudi_core_description = CloudI internal service runtime +pkg_cloudi_core_homepage = http://cloudi.org/ +pkg_cloudi_core_fetch = git +pkg_cloudi_core_repo = https://github.com/CloudI/cloudi_core +pkg_cloudi_core_commit = master + +PACKAGES += cloudi_service_api_requests +pkg_cloudi_service_api_requests_name = cloudi_service_api_requests +pkg_cloudi_service_api_requests_description = CloudI Service API requests (JSON-RPC/Erlang-term support) +pkg_cloudi_service_api_requests_homepage = http://cloudi.org/ +pkg_cloudi_service_api_requests_fetch = git +pkg_cloudi_service_api_requests_repo = https://github.com/CloudI/cloudi_service_api_requests +pkg_cloudi_service_api_requests_commit = master + +PACKAGES += cloudi_service_db_cassandra_cql +pkg_cloudi_service_db_cassandra_cql_name = cloudi_service_db_cassandra_cql +pkg_cloudi_service_db_cassandra_cql_description = Cassandra CQL CloudI Service +pkg_cloudi_service_db_cassandra_cql_homepage = http://cloudi.org/ +pkg_cloudi_service_db_cassandra_cql_fetch = git +pkg_cloudi_service_db_cassandra_cql_repo = https://github.com/CloudI/cloudi_service_db_cassandra_cql +pkg_cloudi_service_db_cassandra_cql_commit = master + +PACKAGES += cloudi_service_db_cassandra +pkg_cloudi_service_db_cassandra_name = cloudi_service_db_cassandra +pkg_cloudi_service_db_cassandra_description = Cassandra CloudI Service +pkg_cloudi_service_db_cassandra_homepage = http://cloudi.org/ +pkg_cloudi_service_db_cassandra_fetch = git +pkg_cloudi_service_db_cassandra_repo = https://github.com/CloudI/cloudi_service_db_cassandra +pkg_cloudi_service_db_cassandra_commit = master + +PACKAGES += cloudi_service_db_couchdb +pkg_cloudi_service_db_couchdb_name = cloudi_service_db_couchdb +pkg_cloudi_service_db_couchdb_description = CouchDB CloudI Service +pkg_cloudi_service_db_couchdb_homepage = http://cloudi.org/ +pkg_cloudi_service_db_couchdb_fetch = git +pkg_cloudi_service_db_couchdb_repo = https://github.com/CloudI/cloudi_service_db_couchdb +pkg_cloudi_service_db_couchdb_commit = master + +PACKAGES += cloudi_service_db_elasticsearch +pkg_cloudi_service_db_elasticsearch_name = cloudi_service_db_elasticsearch +pkg_cloudi_service_db_elasticsearch_description = elasticsearch CloudI Service +pkg_cloudi_service_db_elasticsearch_homepage = http://cloudi.org/ +pkg_cloudi_service_db_elasticsearch_fetch = git +pkg_cloudi_service_db_elasticsearch_repo = https://github.com/CloudI/cloudi_service_db_elasticsearch +pkg_cloudi_service_db_elasticsearch_commit = master + +PACKAGES += cloudi_service_db_memcached +pkg_cloudi_service_db_memcached_name = cloudi_service_db_memcached +pkg_cloudi_service_db_memcached_description = memcached CloudI Service +pkg_cloudi_service_db_memcached_homepage = http://cloudi.org/ +pkg_cloudi_service_db_memcached_fetch = git +pkg_cloudi_service_db_memcached_repo = https://github.com/CloudI/cloudi_service_db_memcached +pkg_cloudi_service_db_memcached_commit = master + +PACKAGES += cloudi_service_db +pkg_cloudi_service_db_name = cloudi_service_db +pkg_cloudi_service_db_description = CloudI Database (in-memory/testing/generic) +pkg_cloudi_service_db_homepage = http://cloudi.org/ +pkg_cloudi_service_db_fetch = git +pkg_cloudi_service_db_repo = https://github.com/CloudI/cloudi_service_db +pkg_cloudi_service_db_commit = master + +PACKAGES += cloudi_service_db_mysql +pkg_cloudi_service_db_mysql_name = cloudi_service_db_mysql +pkg_cloudi_service_db_mysql_description = MySQL CloudI Service +pkg_cloudi_service_db_mysql_homepage = http://cloudi.org/ +pkg_cloudi_service_db_mysql_fetch = git +pkg_cloudi_service_db_mysql_repo = https://github.com/CloudI/cloudi_service_db_mysql +pkg_cloudi_service_db_mysql_commit = master + +PACKAGES += cloudi_service_db_pgsql +pkg_cloudi_service_db_pgsql_name = cloudi_service_db_pgsql +pkg_cloudi_service_db_pgsql_description = PostgreSQL CloudI Service +pkg_cloudi_service_db_pgsql_homepage = http://cloudi.org/ +pkg_cloudi_service_db_pgsql_fetch = git +pkg_cloudi_service_db_pgsql_repo = https://github.com/CloudI/cloudi_service_db_pgsql +pkg_cloudi_service_db_pgsql_commit = master + +PACKAGES += cloudi_service_db_riak +pkg_cloudi_service_db_riak_name = cloudi_service_db_riak +pkg_cloudi_service_db_riak_description = Riak CloudI Service +pkg_cloudi_service_db_riak_homepage = http://cloudi.org/ +pkg_cloudi_service_db_riak_fetch = git +pkg_cloudi_service_db_riak_repo = https://github.com/CloudI/cloudi_service_db_riak +pkg_cloudi_service_db_riak_commit = master + +PACKAGES += cloudi_service_db_tokyotyrant +pkg_cloudi_service_db_tokyotyrant_name = cloudi_service_db_tokyotyrant +pkg_cloudi_service_db_tokyotyrant_description = Tokyo Tyrant CloudI Service +pkg_cloudi_service_db_tokyotyrant_homepage = http://cloudi.org/ +pkg_cloudi_service_db_tokyotyrant_fetch = git +pkg_cloudi_service_db_tokyotyrant_repo = https://github.com/CloudI/cloudi_service_db_tokyotyrant +pkg_cloudi_service_db_tokyotyrant_commit = master + +PACKAGES += cloudi_service_filesystem +pkg_cloudi_service_filesystem_name = cloudi_service_filesystem +pkg_cloudi_service_filesystem_description = Filesystem CloudI Service +pkg_cloudi_service_filesystem_homepage = http://cloudi.org/ +pkg_cloudi_service_filesystem_fetch = git +pkg_cloudi_service_filesystem_repo = https://github.com/CloudI/cloudi_service_filesystem +pkg_cloudi_service_filesystem_commit = master + +PACKAGES += cloudi_service_http_client +pkg_cloudi_service_http_client_name = cloudi_service_http_client +pkg_cloudi_service_http_client_description = HTTP client CloudI Service +pkg_cloudi_service_http_client_homepage = http://cloudi.org/ +pkg_cloudi_service_http_client_fetch = git +pkg_cloudi_service_http_client_repo = https://github.com/CloudI/cloudi_service_http_client +pkg_cloudi_service_http_client_commit = master + +PACKAGES += cloudi_service_http_cowboy +pkg_cloudi_service_http_cowboy_name = cloudi_service_http_cowboy +pkg_cloudi_service_http_cowboy_description = cowboy HTTP/HTTPS CloudI Service +pkg_cloudi_service_http_cowboy_homepage = http://cloudi.org/ +pkg_cloudi_service_http_cowboy_fetch = git +pkg_cloudi_service_http_cowboy_repo = https://github.com/CloudI/cloudi_service_http_cowboy +pkg_cloudi_service_http_cowboy_commit = master + +PACKAGES += cloudi_service_http_elli +pkg_cloudi_service_http_elli_name = cloudi_service_http_elli +pkg_cloudi_service_http_elli_description = elli HTTP CloudI Service +pkg_cloudi_service_http_elli_homepage = http://cloudi.org/ +pkg_cloudi_service_http_elli_fetch = git +pkg_cloudi_service_http_elli_repo = https://github.com/CloudI/cloudi_service_http_elli +pkg_cloudi_service_http_elli_commit = master + +PACKAGES += cloudi_service_map_reduce +pkg_cloudi_service_map_reduce_name = cloudi_service_map_reduce +pkg_cloudi_service_map_reduce_description = Map/Reduce CloudI Service +pkg_cloudi_service_map_reduce_homepage = http://cloudi.org/ +pkg_cloudi_service_map_reduce_fetch = git +pkg_cloudi_service_map_reduce_repo = https://github.com/CloudI/cloudi_service_map_reduce +pkg_cloudi_service_map_reduce_commit = master + +PACKAGES += cloudi_service_oauth1 +pkg_cloudi_service_oauth1_name = cloudi_service_oauth1 +pkg_cloudi_service_oauth1_description = OAuth v1.0 CloudI Service +pkg_cloudi_service_oauth1_homepage = http://cloudi.org/ +pkg_cloudi_service_oauth1_fetch = git +pkg_cloudi_service_oauth1_repo = https://github.com/CloudI/cloudi_service_oauth1 +pkg_cloudi_service_oauth1_commit = master + +PACKAGES += cloudi_service_queue +pkg_cloudi_service_queue_name = cloudi_service_queue +pkg_cloudi_service_queue_description = Persistent Queue Service +pkg_cloudi_service_queue_homepage = http://cloudi.org/ +pkg_cloudi_service_queue_fetch = git +pkg_cloudi_service_queue_repo = https://github.com/CloudI/cloudi_service_queue +pkg_cloudi_service_queue_commit = master + +PACKAGES += cloudi_service_quorum +pkg_cloudi_service_quorum_name = cloudi_service_quorum +pkg_cloudi_service_quorum_description = CloudI Quorum Service +pkg_cloudi_service_quorum_homepage = http://cloudi.org/ +pkg_cloudi_service_quorum_fetch = git +pkg_cloudi_service_quorum_repo = https://github.com/CloudI/cloudi_service_quorum +pkg_cloudi_service_quorum_commit = master + +PACKAGES += cloudi_service_router +pkg_cloudi_service_router_name = cloudi_service_router +pkg_cloudi_service_router_description = CloudI Router Service +pkg_cloudi_service_router_homepage = http://cloudi.org/ +pkg_cloudi_service_router_fetch = git +pkg_cloudi_service_router_repo = https://github.com/CloudI/cloudi_service_router +pkg_cloudi_service_router_commit = master + +PACKAGES += cloudi_service_tcp +pkg_cloudi_service_tcp_name = cloudi_service_tcp +pkg_cloudi_service_tcp_description = TCP CloudI Service +pkg_cloudi_service_tcp_homepage = http://cloudi.org/ +pkg_cloudi_service_tcp_fetch = git +pkg_cloudi_service_tcp_repo = https://github.com/CloudI/cloudi_service_tcp +pkg_cloudi_service_tcp_commit = master + +PACKAGES += cloudi_service_timers +pkg_cloudi_service_timers_name = cloudi_service_timers +pkg_cloudi_service_timers_description = Timers CloudI Service +pkg_cloudi_service_timers_homepage = http://cloudi.org/ +pkg_cloudi_service_timers_fetch = git +pkg_cloudi_service_timers_repo = https://github.com/CloudI/cloudi_service_timers +pkg_cloudi_service_timers_commit = master + +PACKAGES += cloudi_service_udp +pkg_cloudi_service_udp_name = cloudi_service_udp +pkg_cloudi_service_udp_description = UDP CloudI Service +pkg_cloudi_service_udp_homepage = http://cloudi.org/ +pkg_cloudi_service_udp_fetch = git +pkg_cloudi_service_udp_repo = https://github.com/CloudI/cloudi_service_udp +pkg_cloudi_service_udp_commit = master + +PACKAGES += cloudi_service_validate +pkg_cloudi_service_validate_name = cloudi_service_validate +pkg_cloudi_service_validate_description = CloudI Validate Service +pkg_cloudi_service_validate_homepage = http://cloudi.org/ +pkg_cloudi_service_validate_fetch = git +pkg_cloudi_service_validate_repo = https://github.com/CloudI/cloudi_service_validate +pkg_cloudi_service_validate_commit = master + +PACKAGES += cloudi_service_zeromq +pkg_cloudi_service_zeromq_name = cloudi_service_zeromq +pkg_cloudi_service_zeromq_description = ZeroMQ CloudI Service +pkg_cloudi_service_zeromq_homepage = http://cloudi.org/ +pkg_cloudi_service_zeromq_fetch = git +pkg_cloudi_service_zeromq_repo = https://github.com/CloudI/cloudi_service_zeromq +pkg_cloudi_service_zeromq_commit = master + +PACKAGES += cluster_info +pkg_cluster_info_name = cluster_info +pkg_cluster_info_description = Fork of Hibari's nifty cluster_info OTP app +pkg_cluster_info_homepage = https://github.com/basho/cluster_info +pkg_cluster_info_fetch = git +pkg_cluster_info_repo = https://github.com/basho/cluster_info +pkg_cluster_info_commit = master + +PACKAGES += color +pkg_color_name = color +pkg_color_description = ANSI colors for your Erlang +pkg_color_homepage = https://github.com/julianduque/erlang-color +pkg_color_fetch = git +pkg_color_repo = https://github.com/julianduque/erlang-color +pkg_color_commit = master + +PACKAGES += confetti +pkg_confetti_name = confetti +pkg_confetti_description = Erlang configuration provider / application:get_env/2 on steroids +pkg_confetti_homepage = https://github.com/jtendo/confetti +pkg_confetti_fetch = git +pkg_confetti_repo = https://github.com/jtendo/confetti +pkg_confetti_commit = master + +PACKAGES += couchbeam +pkg_couchbeam_name = couchbeam +pkg_couchbeam_description = Apache CouchDB client in Erlang +pkg_couchbeam_homepage = https://github.com/benoitc/couchbeam +pkg_couchbeam_fetch = git +pkg_couchbeam_repo = https://github.com/benoitc/couchbeam +pkg_couchbeam_commit = master + +PACKAGES += covertool +pkg_covertool_name = covertool +pkg_covertool_description = Tool to convert Erlang cover data files into Cobertura XML reports +pkg_covertool_homepage = https://github.com/idubrov/covertool +pkg_covertool_fetch = git +pkg_covertool_repo = https://github.com/idubrov/covertool +pkg_covertool_commit = master + +PACKAGES += cowboy +pkg_cowboy_name = cowboy +pkg_cowboy_description = Small, fast and modular HTTP server. +pkg_cowboy_homepage = http://ninenines.eu +pkg_cowboy_fetch = git +pkg_cowboy_repo = https://github.com/ninenines/cowboy +pkg_cowboy_commit = 1.0.1 + +PACKAGES += cowdb +pkg_cowdb_name = cowdb +pkg_cowdb_description = Pure Key/Value database library for Erlang Applications +pkg_cowdb_homepage = https://github.com/refuge/cowdb +pkg_cowdb_fetch = git +pkg_cowdb_repo = https://github.com/refuge/cowdb +pkg_cowdb_commit = master + +PACKAGES += cowlib +pkg_cowlib_name = cowlib +pkg_cowlib_description = Support library for manipulating Web protocols. +pkg_cowlib_homepage = http://ninenines.eu +pkg_cowlib_fetch = git +pkg_cowlib_repo = https://github.com/ninenines/cowlib +pkg_cowlib_commit = 1.0.1 + +PACKAGES += cpg +pkg_cpg_name = cpg +pkg_cpg_description = CloudI Process Groups +pkg_cpg_homepage = https://github.com/okeuday/cpg +pkg_cpg_fetch = git +pkg_cpg_repo = https://github.com/okeuday/cpg +pkg_cpg_commit = master + +PACKAGES += cqerl +pkg_cqerl_name = cqerl +pkg_cqerl_description = Native Erlang CQL client for Cassandra +pkg_cqerl_homepage = https://matehat.github.io/cqerl/ +pkg_cqerl_fetch = git +pkg_cqerl_repo = https://github.com/matehat/cqerl +pkg_cqerl_commit = master + +PACKAGES += cr +pkg_cr_name = cr +pkg_cr_description = Chain Replication +pkg_cr_homepage = https://synrc.com/apps/cr/doc/cr.htm +pkg_cr_fetch = git +pkg_cr_repo = https://github.com/spawnproc/cr +pkg_cr_commit = master + +PACKAGES += cuttlefish +pkg_cuttlefish_name = cuttlefish +pkg_cuttlefish_description = never lose your childlike sense of wonder baby cuttlefish, promise me? +pkg_cuttlefish_homepage = https://github.com/basho/cuttlefish +pkg_cuttlefish_fetch = git +pkg_cuttlefish_repo = https://github.com/basho/cuttlefish +pkg_cuttlefish_commit = master + +PACKAGES += damocles +pkg_damocles_name = damocles +pkg_damocles_description = Erlang library for generating adversarial network conditions for QAing distributed applications/systems on a single Linux box. +pkg_damocles_homepage = https://github.com/lostcolony/damocles +pkg_damocles_fetch = git +pkg_damocles_repo = https://github.com/lostcolony/damocles +pkg_damocles_commit = master + +PACKAGES += debbie +pkg_debbie_name = debbie +pkg_debbie_description = .DEB Built In Erlang +pkg_debbie_homepage = https://github.com/crownedgrouse/debbie +pkg_debbie_fetch = git +pkg_debbie_repo = https://github.com/crownedgrouse/debbie +pkg_debbie_commit = master + +PACKAGES += decimal +pkg_decimal_name = decimal +pkg_decimal_description = An Erlang decimal arithmetic library +pkg_decimal_homepage = https://github.com/tim/erlang-decimal +pkg_decimal_fetch = git +pkg_decimal_repo = https://github.com/tim/erlang-decimal +pkg_decimal_commit = master + +PACKAGES += detergent +pkg_detergent_name = detergent +pkg_detergent_description = An emulsifying Erlang SOAP library +pkg_detergent_homepage = https://github.com/devinus/detergent +pkg_detergent_fetch = git +pkg_detergent_repo = https://github.com/devinus/detergent +pkg_detergent_commit = master + +PACKAGES += detest +pkg_detest_name = detest +pkg_detest_description = Tool for running tests on a cluster of erlang nodes +pkg_detest_homepage = https://github.com/biokoda/detest +pkg_detest_fetch = git +pkg_detest_repo = https://github.com/biokoda/detest +pkg_detest_commit = master + +PACKAGES += dh_date +pkg_dh_date_name = dh_date +pkg_dh_date_description = Date formatting / parsing library for erlang +pkg_dh_date_homepage = https://github.com/daleharvey/dh_date +pkg_dh_date_fetch = git +pkg_dh_date_repo = https://github.com/daleharvey/dh_date +pkg_dh_date_commit = master + +PACKAGES += dirbusterl +pkg_dirbusterl_name = dirbusterl +pkg_dirbusterl_description = DirBuster successor in Erlang +pkg_dirbusterl_homepage = https://github.com/silentsignal/DirBustErl +pkg_dirbusterl_fetch = git +pkg_dirbusterl_repo = https://github.com/silentsignal/DirBustErl +pkg_dirbusterl_commit = master + +PACKAGES += dispcount +pkg_dispcount_name = dispcount +pkg_dispcount_description = Erlang task dispatcher based on ETS counters. +pkg_dispcount_homepage = https://github.com/ferd/dispcount +pkg_dispcount_fetch = git +pkg_dispcount_repo = https://github.com/ferd/dispcount +pkg_dispcount_commit = master + +PACKAGES += dlhttpc +pkg_dlhttpc_name = dlhttpc +pkg_dlhttpc_description = dispcount-based lhttpc fork for massive amounts of requests to limited endpoints +pkg_dlhttpc_homepage = https://github.com/ferd/dlhttpc +pkg_dlhttpc_fetch = git +pkg_dlhttpc_repo = https://github.com/ferd/dlhttpc +pkg_dlhttpc_commit = master + +PACKAGES += dns +pkg_dns_name = dns +pkg_dns_description = Erlang DNS library +pkg_dns_homepage = https://github.com/aetrion/dns_erlang +pkg_dns_fetch = git +pkg_dns_repo = https://github.com/aetrion/dns_erlang +pkg_dns_commit = master + +PACKAGES += dnssd +pkg_dnssd_name = dnssd +pkg_dnssd_description = Erlang interface to Apple's Bonjour D NS Service Discovery implementation +pkg_dnssd_homepage = https://github.com/benoitc/dnssd_erlang +pkg_dnssd_fetch = git +pkg_dnssd_repo = https://github.com/benoitc/dnssd_erlang +pkg_dnssd_commit = master + +PACKAGES += dtl +pkg_dtl_name = dtl +pkg_dtl_description = Django Template Language: A full-featured port of the Django template engine to Erlang. +pkg_dtl_homepage = https://github.com/oinksoft/dtl +pkg_dtl_fetch = git +pkg_dtl_repo = https://github.com/oinksoft/dtl +pkg_dtl_commit = master + +PACKAGES += dynamic_compile +pkg_dynamic_compile_name = dynamic_compile +pkg_dynamic_compile_description = compile and load erlang modules from string input +pkg_dynamic_compile_homepage = https://github.com/jkvor/dynamic_compile +pkg_dynamic_compile_fetch = git +pkg_dynamic_compile_repo = https://github.com/jkvor/dynamic_compile +pkg_dynamic_compile_commit = master + +PACKAGES += e2 +pkg_e2_name = e2 +pkg_e2_description = Library to simply writing correct OTP applications. +pkg_e2_homepage = http://e2project.org +pkg_e2_fetch = git +pkg_e2_repo = https://github.com/gar1t/e2 +pkg_e2_commit = master + +PACKAGES += eamf +pkg_eamf_name = eamf +pkg_eamf_description = eAMF provides Action Message Format (AMF) support for Erlang +pkg_eamf_homepage = https://github.com/mrinalwadhwa/eamf +pkg_eamf_fetch = git +pkg_eamf_repo = https://github.com/mrinalwadhwa/eamf +pkg_eamf_commit = master + +PACKAGES += eavro +pkg_eavro_name = eavro +pkg_eavro_description = Apache Avro encoder/decoder +pkg_eavro_homepage = https://github.com/SIfoxDevTeam/eavro +pkg_eavro_fetch = git +pkg_eavro_repo = https://github.com/SIfoxDevTeam/eavro +pkg_eavro_commit = master + +PACKAGES += ecapnp +pkg_ecapnp_name = ecapnp +pkg_ecapnp_description = Cap'n Proto library for Erlang +pkg_ecapnp_homepage = https://github.com/kaos/ecapnp +pkg_ecapnp_fetch = git +pkg_ecapnp_repo = https://github.com/kaos/ecapnp +pkg_ecapnp_commit = master + +PACKAGES += econfig +pkg_econfig_name = econfig +pkg_econfig_description = simple Erlang config handler using INI files +pkg_econfig_homepage = https://github.com/benoitc/econfig +pkg_econfig_fetch = git +pkg_econfig_repo = https://github.com/benoitc/econfig +pkg_econfig_commit = master + +PACKAGES += edate +pkg_edate_name = edate +pkg_edate_description = date manipulation library for erlang +pkg_edate_homepage = https://github.com/dweldon/edate +pkg_edate_fetch = git +pkg_edate_repo = https://github.com/dweldon/edate +pkg_edate_commit = master + +PACKAGES += edgar +pkg_edgar_name = edgar +pkg_edgar_description = Erlang Does GNU AR +pkg_edgar_homepage = https://github.com/crownedgrouse/edgar +pkg_edgar_fetch = git +pkg_edgar_repo = https://github.com/crownedgrouse/edgar +pkg_edgar_commit = master + +PACKAGES += edis +pkg_edis_name = edis +pkg_edis_description = An Erlang implementation of Redis KV Store +pkg_edis_homepage = http://inaka.github.com/edis/ +pkg_edis_fetch = git +pkg_edis_repo = https://github.com/inaka/edis +pkg_edis_commit = master + +PACKAGES += edns +pkg_edns_name = edns +pkg_edns_description = Erlang/OTP DNS server +pkg_edns_homepage = https://github.com/hcvst/erlang-dns +pkg_edns_fetch = git +pkg_edns_repo = https://github.com/hcvst/erlang-dns +pkg_edns_commit = master + +PACKAGES += edown +pkg_edown_name = edown +pkg_edown_description = EDoc extension for generating Github-flavored Markdown +pkg_edown_homepage = https://github.com/uwiger/edown +pkg_edown_fetch = git +pkg_edown_repo = https://github.com/uwiger/edown +pkg_edown_commit = master + +PACKAGES += eep_app +pkg_eep_app_name = eep_app +pkg_eep_app_description = Embedded Event Processing +pkg_eep_app_homepage = https://github.com/darach/eep-erl +pkg_eep_app_fetch = git +pkg_eep_app_repo = https://github.com/darach/eep-erl +pkg_eep_app_commit = master + +PACKAGES += eep +pkg_eep_name = eep +pkg_eep_description = Erlang Easy Profiling (eep) application provides a way to analyze application performance and call hierarchy +pkg_eep_homepage = https://github.com/virtan/eep +pkg_eep_fetch = git +pkg_eep_repo = https://github.com/virtan/eep +pkg_eep_commit = master + +PACKAGES += efene +pkg_efene_name = efene +pkg_efene_description = Alternative syntax for the Erlang Programming Language focusing on simplicity, ease of use and programmer UX +pkg_efene_homepage = https://github.com/efene/efene +pkg_efene_fetch = git +pkg_efene_repo = https://github.com/efene/efene +pkg_efene_commit = master + +PACKAGES += eganglia +pkg_eganglia_name = eganglia +pkg_eganglia_description = Erlang library to interact with Ganglia +pkg_eganglia_homepage = https://github.com/inaka/eganglia +pkg_eganglia_fetch = git +pkg_eganglia_repo = https://github.com/inaka/eganglia +pkg_eganglia_commit = v0.9.1 + +PACKAGES += egeoip +pkg_egeoip_name = egeoip +pkg_egeoip_description = Erlang IP Geolocation module, currently supporting the MaxMind GeoLite City Database. +pkg_egeoip_homepage = https://github.com/mochi/egeoip +pkg_egeoip_fetch = git +pkg_egeoip_repo = https://github.com/mochi/egeoip +pkg_egeoip_commit = master + +PACKAGES += ehsa +pkg_ehsa_name = ehsa +pkg_ehsa_description = Erlang HTTP server basic and digest authentication modules +pkg_ehsa_homepage = https://bitbucket.org/a12n/ehsa +pkg_ehsa_fetch = hg +pkg_ehsa_repo = https://bitbucket.org/a12n/ehsa +pkg_ehsa_commit = 2.0.4 + +PACKAGES += ejabberd +pkg_ejabberd_name = ejabberd +pkg_ejabberd_description = Robust, ubiquitous and massively scalable Jabber / XMPP Instant Messaging platform +pkg_ejabberd_homepage = https://github.com/processone/ejabberd +pkg_ejabberd_fetch = git +pkg_ejabberd_repo = https://github.com/processone/ejabberd +pkg_ejabberd_commit = master + +PACKAGES += ej +pkg_ej_name = ej +pkg_ej_description = Helper module for working with Erlang terms representing JSON +pkg_ej_homepage = https://github.com/seth/ej +pkg_ej_fetch = git +pkg_ej_repo = https://github.com/seth/ej +pkg_ej_commit = master + +PACKAGES += ejwt +pkg_ejwt_name = ejwt +pkg_ejwt_description = erlang library for JSON Web Token +pkg_ejwt_homepage = https://github.com/artefactop/ejwt +pkg_ejwt_fetch = git +pkg_ejwt_repo = https://github.com/artefactop/ejwt +pkg_ejwt_commit = master + +PACKAGES += ekaf +pkg_ekaf_name = ekaf +pkg_ekaf_description = A minimal, high-performance Kafka client in Erlang. +pkg_ekaf_homepage = https://github.com/helpshift/ekaf +pkg_ekaf_fetch = git +pkg_ekaf_repo = https://github.com/helpshift/ekaf +pkg_ekaf_commit = master + +PACKAGES += elarm +pkg_elarm_name = elarm +pkg_elarm_description = Alarm Manager for Erlang. +pkg_elarm_homepage = https://github.com/esl/elarm +pkg_elarm_fetch = git +pkg_elarm_repo = https://github.com/esl/elarm +pkg_elarm_commit = master + +PACKAGES += eleveldb +pkg_eleveldb_name = eleveldb +pkg_eleveldb_description = Erlang LevelDB API +pkg_eleveldb_homepage = https://github.com/basho/eleveldb +pkg_eleveldb_fetch = git +pkg_eleveldb_repo = https://github.com/basho/eleveldb +pkg_eleveldb_commit = master + +PACKAGES += elli +pkg_elli_name = elli +pkg_elli_description = Simple, robust and performant Erlang web server +pkg_elli_homepage = https://github.com/knutin/elli +pkg_elli_fetch = git +pkg_elli_repo = https://github.com/knutin/elli +pkg_elli_commit = master + +PACKAGES += elvis +pkg_elvis_name = elvis +pkg_elvis_description = Erlang Style Reviewer +pkg_elvis_homepage = https://github.com/inaka/elvis +pkg_elvis_fetch = git +pkg_elvis_repo = https://github.com/inaka/elvis +pkg_elvis_commit = master + +PACKAGES += emagick +pkg_emagick_name = emagick +pkg_emagick_description = Wrapper for Graphics/ImageMagick command line tool. +pkg_emagick_homepage = https://github.com/kivra/emagick +pkg_emagick_fetch = git +pkg_emagick_repo = https://github.com/kivra/emagick +pkg_emagick_commit = master + +PACKAGES += emysql +pkg_emysql_name = emysql +pkg_emysql_description = Stable, pure Erlang MySQL driver. +pkg_emysql_homepage = https://github.com/Eonblast/Emysql +pkg_emysql_fetch = git +pkg_emysql_repo = https://github.com/Eonblast/Emysql +pkg_emysql_commit = master + +PACKAGES += enm +pkg_enm_name = enm +pkg_enm_description = Erlang driver for nanomsg +pkg_enm_homepage = https://github.com/basho/enm +pkg_enm_fetch = git +pkg_enm_repo = https://github.com/basho/enm +pkg_enm_commit = master + +PACKAGES += entop +pkg_entop_name = entop +pkg_entop_description = A top-like tool for monitoring an Erlang node +pkg_entop_homepage = https://github.com/mazenharake/entop +pkg_entop_fetch = git +pkg_entop_repo = https://github.com/mazenharake/entop +pkg_entop_commit = master + +PACKAGES += epcap +pkg_epcap_name = epcap +pkg_epcap_description = Erlang packet capture interface using pcap +pkg_epcap_homepage = https://github.com/msantos/epcap +pkg_epcap_fetch = git +pkg_epcap_repo = https://github.com/msantos/epcap +pkg_epcap_commit = master + +PACKAGES += eper +pkg_eper_name = eper +pkg_eper_description = Erlang performance and debugging tools. +pkg_eper_homepage = https://github.com/massemanet/eper +pkg_eper_fetch = git +pkg_eper_repo = https://github.com/massemanet/eper +pkg_eper_commit = master + +PACKAGES += epgsql +pkg_epgsql_name = epgsql +pkg_epgsql_description = Erlang PostgreSQL client library. +pkg_epgsql_homepage = https://github.com/epgsql/epgsql +pkg_epgsql_fetch = git +pkg_epgsql_repo = https://github.com/epgsql/epgsql +pkg_epgsql_commit = master + +PACKAGES += episcina +pkg_episcina_name = episcina +pkg_episcina_description = A simple non intrusive resource pool for connections +pkg_episcina_homepage = https://github.com/erlware/episcina +pkg_episcina_fetch = git +pkg_episcina_repo = https://github.com/erlware/episcina +pkg_episcina_commit = master + +PACKAGES += eplot +pkg_eplot_name = eplot +pkg_eplot_description = A plot engine written in erlang. +pkg_eplot_homepage = https://github.com/psyeugenic/eplot +pkg_eplot_fetch = git +pkg_eplot_repo = https://github.com/psyeugenic/eplot +pkg_eplot_commit = master + +PACKAGES += epocxy +pkg_epocxy_name = epocxy +pkg_epocxy_description = Erlang Patterns of Concurrency +pkg_epocxy_homepage = https://github.com/duomark/epocxy +pkg_epocxy_fetch = git +pkg_epocxy_repo = https://github.com/duomark/epocxy +pkg_epocxy_commit = master + +PACKAGES += epubnub +pkg_epubnub_name = epubnub +pkg_epubnub_description = Erlang PubNub API +pkg_epubnub_homepage = https://github.com/tsloughter/epubnub +pkg_epubnub_fetch = git +pkg_epubnub_repo = https://github.com/tsloughter/epubnub +pkg_epubnub_commit = master + +PACKAGES += eqm +pkg_eqm_name = eqm +pkg_eqm_description = Erlang pub sub with supply-demand channels +pkg_eqm_homepage = https://github.com/loucash/eqm +pkg_eqm_fetch = git +pkg_eqm_repo = https://github.com/loucash/eqm +pkg_eqm_commit = master + +PACKAGES += eredis +pkg_eredis_name = eredis +pkg_eredis_description = Erlang Redis client +pkg_eredis_homepage = https://github.com/wooga/eredis +pkg_eredis_fetch = git +pkg_eredis_repo = https://github.com/wooga/eredis +pkg_eredis_commit = master + +PACKAGES += eredis_pool +pkg_eredis_pool_name = eredis_pool +pkg_eredis_pool_description = eredis_pool is Pool of Redis clients, using eredis and poolboy. +pkg_eredis_pool_homepage = https://github.com/hiroeorz/eredis_pool +pkg_eredis_pool_fetch = git +pkg_eredis_pool_repo = https://github.com/hiroeorz/eredis_pool +pkg_eredis_pool_commit = master + +PACKAGES += erlang_cep +pkg_erlang_cep_name = erlang_cep +pkg_erlang_cep_description = A basic CEP package written in erlang +pkg_erlang_cep_homepage = https://github.com/danmacklin/erlang_cep +pkg_erlang_cep_fetch = git +pkg_erlang_cep_repo = https://github.com/danmacklin/erlang_cep +pkg_erlang_cep_commit = master + +PACKAGES += erlang_js +pkg_erlang_js_name = erlang_js +pkg_erlang_js_description = A linked-in driver for Erlang to Mozilla's Spidermonkey Javascript runtime. +pkg_erlang_js_homepage = https://github.com/basho/erlang_js +pkg_erlang_js_fetch = git +pkg_erlang_js_repo = https://github.com/basho/erlang_js +pkg_erlang_js_commit = master + +PACKAGES += erlang_localtime +pkg_erlang_localtime_name = erlang_localtime +pkg_erlang_localtime_description = Erlang library for conversion from one local time to another +pkg_erlang_localtime_homepage = https://github.com/dmitryme/erlang_localtime +pkg_erlang_localtime_fetch = git +pkg_erlang_localtime_repo = https://github.com/dmitryme/erlang_localtime +pkg_erlang_localtime_commit = master + +PACKAGES += erlang_smtp +pkg_erlang_smtp_name = erlang_smtp +pkg_erlang_smtp_description = Erlang SMTP and POP3 server code. +pkg_erlang_smtp_homepage = https://github.com/tonyg/erlang-smtp +pkg_erlang_smtp_fetch = git +pkg_erlang_smtp_repo = https://github.com/tonyg/erlang-smtp +pkg_erlang_smtp_commit = master + +PACKAGES += erlang_term +pkg_erlang_term_name = erlang_term +pkg_erlang_term_description = Erlang Term Info +pkg_erlang_term_homepage = https://github.com/okeuday/erlang_term +pkg_erlang_term_fetch = git +pkg_erlang_term_repo = https://github.com/okeuday/erlang_term +pkg_erlang_term_commit = master + +PACKAGES += erlastic_search +pkg_erlastic_search_name = erlastic_search +pkg_erlastic_search_description = An Erlang app for communicating with Elastic Search's rest interface. +pkg_erlastic_search_homepage = https://github.com/tsloughter/erlastic_search +pkg_erlastic_search_fetch = git +pkg_erlastic_search_repo = https://github.com/tsloughter/erlastic_search +pkg_erlastic_search_commit = master + +PACKAGES += erlasticsearch +pkg_erlasticsearch_name = erlasticsearch +pkg_erlasticsearch_description = Erlang thrift interface to elastic_search +pkg_erlasticsearch_homepage = https://github.com/dieswaytoofast/erlasticsearch +pkg_erlasticsearch_fetch = git +pkg_erlasticsearch_repo = https://github.com/dieswaytoofast/erlasticsearch +pkg_erlasticsearch_commit = master + +PACKAGES += erlbrake +pkg_erlbrake_name = erlbrake +pkg_erlbrake_description = Erlang Airbrake notification client +pkg_erlbrake_homepage = https://github.com/kenpratt/erlbrake +pkg_erlbrake_fetch = git +pkg_erlbrake_repo = https://github.com/kenpratt/erlbrake +pkg_erlbrake_commit = master + +PACKAGES += erlcloud +pkg_erlcloud_name = erlcloud +pkg_erlcloud_description = Cloud Computing library for erlang (Amazon EC2, S3, SQS, SimpleDB, Mechanical Turk, ELB) +pkg_erlcloud_homepage = https://github.com/gleber/erlcloud +pkg_erlcloud_fetch = git +pkg_erlcloud_repo = https://github.com/gleber/erlcloud +pkg_erlcloud_commit = master + +PACKAGES += erlcron +pkg_erlcron_name = erlcron +pkg_erlcron_description = Erlang cronish system +pkg_erlcron_homepage = https://github.com/erlware/erlcron +pkg_erlcron_fetch = git +pkg_erlcron_repo = https://github.com/erlware/erlcron +pkg_erlcron_commit = master + +PACKAGES += erldb +pkg_erldb_name = erldb +pkg_erldb_description = ORM (Object-relational mapping) application implemented in Erlang +pkg_erldb_homepage = http://erldb.org +pkg_erldb_fetch = git +pkg_erldb_repo = https://github.com/erldb/erldb +pkg_erldb_commit = master + +PACKAGES += erldis +pkg_erldis_name = erldis +pkg_erldis_description = redis erlang client library +pkg_erldis_homepage = https://github.com/cstar/erldis +pkg_erldis_fetch = git +pkg_erldis_repo = https://github.com/cstar/erldis +pkg_erldis_commit = master + +PACKAGES += erldns +pkg_erldns_name = erldns +pkg_erldns_description = DNS server, in erlang. +pkg_erldns_homepage = https://github.com/aetrion/erl-dns +pkg_erldns_fetch = git +pkg_erldns_repo = https://github.com/aetrion/erl-dns +pkg_erldns_commit = master + +PACKAGES += erldocker +pkg_erldocker_name = erldocker +pkg_erldocker_description = Docker Remote API client for Erlang +pkg_erldocker_homepage = https://github.com/proger/erldocker +pkg_erldocker_fetch = git +pkg_erldocker_repo = https://github.com/proger/erldocker +pkg_erldocker_commit = master + +PACKAGES += erlfsmon +pkg_erlfsmon_name = erlfsmon +pkg_erlfsmon_description = Erlang filesystem event watcher for Linux and OSX +pkg_erlfsmon_homepage = https://github.com/proger/erlfsmon +pkg_erlfsmon_fetch = git +pkg_erlfsmon_repo = https://github.com/proger/erlfsmon +pkg_erlfsmon_commit = master + +PACKAGES += erlgit +pkg_erlgit_name = erlgit +pkg_erlgit_description = Erlang convenience wrapper around git executable +pkg_erlgit_homepage = https://github.com/gleber/erlgit +pkg_erlgit_fetch = git +pkg_erlgit_repo = https://github.com/gleber/erlgit +pkg_erlgit_commit = master + +PACKAGES += erlguten +pkg_erlguten_name = erlguten +pkg_erlguten_description = ErlGuten is a system for high-quality typesetting, written purely in Erlang. +pkg_erlguten_homepage = https://github.com/richcarl/erlguten +pkg_erlguten_fetch = git +pkg_erlguten_repo = https://github.com/richcarl/erlguten +pkg_erlguten_commit = master + +PACKAGES += erlmc +pkg_erlmc_name = erlmc +pkg_erlmc_description = Erlang memcached binary protocol client +pkg_erlmc_homepage = https://github.com/jkvor/erlmc +pkg_erlmc_fetch = git +pkg_erlmc_repo = https://github.com/jkvor/erlmc +pkg_erlmc_commit = master + +PACKAGES += erlmongo +pkg_erlmongo_name = erlmongo +pkg_erlmongo_description = Record based Erlang driver for MongoDB with gridfs support +pkg_erlmongo_homepage = https://github.com/SergejJurecko/erlmongo +pkg_erlmongo_fetch = git +pkg_erlmongo_repo = https://github.com/SergejJurecko/erlmongo +pkg_erlmongo_commit = master + +PACKAGES += erlog +pkg_erlog_name = erlog +pkg_erlog_description = Prolog interpreter in and for Erlang +pkg_erlog_homepage = https://github.com/rvirding/erlog +pkg_erlog_fetch = git +pkg_erlog_repo = https://github.com/rvirding/erlog +pkg_erlog_commit = master + +PACKAGES += erlpass +pkg_erlpass_name = erlpass +pkg_erlpass_description = A library to handle password hashing and changing in a safe manner, independent from any kind of storage whatsoever. +pkg_erlpass_homepage = https://github.com/ferd/erlpass +pkg_erlpass_fetch = git +pkg_erlpass_repo = https://github.com/ferd/erlpass +pkg_erlpass_commit = master + +PACKAGES += erlport +pkg_erlport_name = erlport +pkg_erlport_description = ErlPort - connect Erlang to other languages +pkg_erlport_homepage = https://github.com/hdima/erlport +pkg_erlport_fetch = git +pkg_erlport_repo = https://github.com/hdima/erlport +pkg_erlport_commit = master + +PACKAGES += erlsha2 +pkg_erlsha2_name = erlsha2 +pkg_erlsha2_description = SHA-224, SHA-256, SHA-384, SHA-512 implemented in Erlang NIFs. +pkg_erlsha2_homepage = https://github.com/vinoski/erlsha2 +pkg_erlsha2_fetch = git +pkg_erlsha2_repo = https://github.com/vinoski/erlsha2 +pkg_erlsha2_commit = master + +PACKAGES += erlsh +pkg_erlsh_name = erlsh +pkg_erlsh_description = Erlang shell tools +pkg_erlsh_homepage = https://github.com/proger/erlsh +pkg_erlsh_fetch = git +pkg_erlsh_repo = https://github.com/proger/erlsh +pkg_erlsh_commit = master + +PACKAGES += erlsom +pkg_erlsom_name = erlsom +pkg_erlsom_description = XML parser for Erlang +pkg_erlsom_homepage = https://github.com/willemdj/erlsom +pkg_erlsom_fetch = git +pkg_erlsom_repo = https://github.com/willemdj/erlsom +pkg_erlsom_commit = master + +PACKAGES += erl_streams +pkg_erl_streams_name = erl_streams +pkg_erl_streams_description = Streams in Erlang +pkg_erl_streams_homepage = https://github.com/epappas/erl_streams +pkg_erl_streams_fetch = git +pkg_erl_streams_repo = https://github.com/epappas/erl_streams +pkg_erl_streams_commit = master + +PACKAGES += erlubi +pkg_erlubi_name = erlubi +pkg_erlubi_description = Ubigraph Erlang Client (and Process Visualizer) +pkg_erlubi_homepage = https://github.com/krestenkrab/erlubi +pkg_erlubi_fetch = git +pkg_erlubi_repo = https://github.com/krestenkrab/erlubi +pkg_erlubi_commit = master + +PACKAGES += erlvolt +pkg_erlvolt_name = erlvolt +pkg_erlvolt_description = VoltDB Erlang Client Driver +pkg_erlvolt_homepage = https://github.com/VoltDB/voltdb-client-erlang +pkg_erlvolt_fetch = git +pkg_erlvolt_repo = https://github.com/VoltDB/voltdb-client-erlang +pkg_erlvolt_commit = master + +PACKAGES += erlware_commons +pkg_erlware_commons_name = erlware_commons +pkg_erlware_commons_description = Erlware Commons is an Erlware project focused on all aspects of reusable Erlang components. +pkg_erlware_commons_homepage = https://github.com/erlware/erlware_commons +pkg_erlware_commons_fetch = git +pkg_erlware_commons_repo = https://github.com/erlware/erlware_commons +pkg_erlware_commons_commit = master + +PACKAGES += erlydtl +pkg_erlydtl_name = erlydtl +pkg_erlydtl_description = Django Template Language for Erlang. +pkg_erlydtl_homepage = https://github.com/erlydtl/erlydtl +pkg_erlydtl_fetch = git +pkg_erlydtl_repo = https://github.com/erlydtl/erlydtl +pkg_erlydtl_commit = master + +PACKAGES += errd +pkg_errd_name = errd +pkg_errd_description = Erlang RRDTool library +pkg_errd_homepage = https://github.com/archaelus/errd +pkg_errd_fetch = git +pkg_errd_repo = https://github.com/archaelus/errd +pkg_errd_commit = master + +PACKAGES += erserve +pkg_erserve_name = erserve +pkg_erserve_description = Erlang/Rserve communication interface +pkg_erserve_homepage = https://github.com/del/erserve +pkg_erserve_fetch = git +pkg_erserve_repo = https://github.com/del/erserve +pkg_erserve_commit = master + +PACKAGES += erwa +pkg_erwa_name = erwa +pkg_erwa_description = A WAMP router and client written in Erlang. +pkg_erwa_homepage = https://github.com/bwegh/erwa +pkg_erwa_fetch = git +pkg_erwa_repo = https://github.com/bwegh/erwa +pkg_erwa_commit = 0.1.1 + +PACKAGES += espec +pkg_espec_name = espec +pkg_espec_description = ESpec: Behaviour driven development framework for Erlang +pkg_espec_homepage = https://github.com/lucaspiller/espec +pkg_espec_fetch = git +pkg_espec_repo = https://github.com/lucaspiller/espec +pkg_espec_commit = master + +PACKAGES += estatsd +pkg_estatsd_name = estatsd +pkg_estatsd_description = Erlang stats aggregation app that periodically flushes data to graphite +pkg_estatsd_homepage = https://github.com/RJ/estatsd +pkg_estatsd_fetch = git +pkg_estatsd_repo = https://github.com/RJ/estatsd +pkg_estatsd_commit = master + +PACKAGES += etap +pkg_etap_name = etap +pkg_etap_description = etap is a simple erlang testing library that provides TAP compliant output. +pkg_etap_homepage = https://github.com/ngerakines/etap +pkg_etap_fetch = git +pkg_etap_repo = https://github.com/ngerakines/etap +pkg_etap_commit = master + +PACKAGES += etest_http +pkg_etest_http_name = etest_http +pkg_etest_http_description = etest Assertions around HTTP (client-side) +pkg_etest_http_homepage = https://github.com/wooga/etest_http +pkg_etest_http_fetch = git +pkg_etest_http_repo = https://github.com/wooga/etest_http +pkg_etest_http_commit = master + +PACKAGES += etest +pkg_etest_name = etest +pkg_etest_description = A lightweight, convention over configuration test framework for Erlang +pkg_etest_homepage = https://github.com/wooga/etest +pkg_etest_fetch = git +pkg_etest_repo = https://github.com/wooga/etest +pkg_etest_commit = master + +PACKAGES += etoml +pkg_etoml_name = etoml +pkg_etoml_description = TOML language erlang parser +pkg_etoml_homepage = https://github.com/kalta/etoml +pkg_etoml_fetch = git +pkg_etoml_repo = https://github.com/kalta/etoml +pkg_etoml_commit = master + +PACKAGES += eunit_formatters +pkg_eunit_formatters_name = eunit_formatters +pkg_eunit_formatters_description = Because eunit's output sucks. Let's make it better. +pkg_eunit_formatters_homepage = https://github.com/seancribbs/eunit_formatters +pkg_eunit_formatters_fetch = git +pkg_eunit_formatters_repo = https://github.com/seancribbs/eunit_formatters +pkg_eunit_formatters_commit = master + +PACKAGES += eunit +pkg_eunit_name = eunit +pkg_eunit_description = The EUnit lightweight unit testing framework for Erlang - this is the canonical development repository. +pkg_eunit_homepage = https://github.com/richcarl/eunit +pkg_eunit_fetch = git +pkg_eunit_repo = https://github.com/richcarl/eunit +pkg_eunit_commit = master + +PACKAGES += euthanasia +pkg_euthanasia_name = euthanasia +pkg_euthanasia_description = Merciful killer for your Erlang processes +pkg_euthanasia_homepage = https://github.com/doubleyou/euthanasia +pkg_euthanasia_fetch = git +pkg_euthanasia_repo = https://github.com/doubleyou/euthanasia +pkg_euthanasia_commit = master + +PACKAGES += evum +pkg_evum_name = evum +pkg_evum_description = Spawn Linux VMs as Erlang processes in the Erlang VM +pkg_evum_homepage = https://github.com/msantos/evum +pkg_evum_fetch = git +pkg_evum_repo = https://github.com/msantos/evum +pkg_evum_commit = master + +PACKAGES += exec +pkg_exec_name = exec +pkg_exec_description = Execute and control OS processes from Erlang/OTP. +pkg_exec_homepage = http://saleyn.github.com/erlexec +pkg_exec_fetch = git +pkg_exec_repo = https://github.com/saleyn/erlexec +pkg_exec_commit = master + +PACKAGES += exml +pkg_exml_name = exml +pkg_exml_description = XML parsing library in Erlang +pkg_exml_homepage = https://github.com/paulgray/exml +pkg_exml_fetch = git +pkg_exml_repo = https://github.com/paulgray/exml +pkg_exml_commit = master + +PACKAGES += exometer +pkg_exometer_name = exometer +pkg_exometer_description = Basic measurement objects and probe behavior +pkg_exometer_homepage = https://github.com/Feuerlabs/exometer +pkg_exometer_fetch = git +pkg_exometer_repo = https://github.com/Feuerlabs/exometer +pkg_exometer_commit = 1.2 + +PACKAGES += exs1024 +pkg_exs1024_name = exs1024 +pkg_exs1024_description = Xorshift1024star pseudo random number generator for Erlang. +pkg_exs1024_homepage = https://github.com/jj1bdx/exs1024 +pkg_exs1024_fetch = git +pkg_exs1024_repo = https://github.com/jj1bdx/exs1024 +pkg_exs1024_commit = master + +PACKAGES += exs64 +pkg_exs64_name = exs64 +pkg_exs64_description = Xorshift64star pseudo random number generator for Erlang. +pkg_exs64_homepage = https://github.com/jj1bdx/exs64 +pkg_exs64_fetch = git +pkg_exs64_repo = https://github.com/jj1bdx/exs64 +pkg_exs64_commit = master + +PACKAGES += exsplus116 +pkg_exsplus116_name = exsplus116 +pkg_exsplus116_description = Xorshift116plus for Erlang +pkg_exsplus116_homepage = https://github.com/jj1bdx/exsplus116 +pkg_exsplus116_fetch = git +pkg_exsplus116_repo = https://github.com/jj1bdx/exsplus116 +pkg_exsplus116_commit = master + +PACKAGES += exsplus128 +pkg_exsplus128_name = exsplus128 +pkg_exsplus128_description = Xorshift128plus pseudo random number generator for Erlang. +pkg_exsplus128_homepage = https://github.com/jj1bdx/exsplus128 +pkg_exsplus128_fetch = git +pkg_exsplus128_repo = https://github.com/jj1bdx/exsplus128 +pkg_exsplus128_commit = master + +PACKAGES += ezmq +pkg_ezmq_name = ezmq +pkg_ezmq_description = zMQ implemented in Erlang +pkg_ezmq_homepage = https://github.com/RoadRunnr/ezmq +pkg_ezmq_fetch = git +pkg_ezmq_repo = https://github.com/RoadRunnr/ezmq +pkg_ezmq_commit = master + +PACKAGES += ezmtp +pkg_ezmtp_name = ezmtp +pkg_ezmtp_description = ZMTP protocol in pure Erlang. +pkg_ezmtp_homepage = https://github.com/a13x/ezmtp +pkg_ezmtp_fetch = git +pkg_ezmtp_repo = https://github.com/a13x/ezmtp +pkg_ezmtp_commit = master + +PACKAGES += fast_disk_log +pkg_fast_disk_log_name = fast_disk_log +pkg_fast_disk_log_description = Pool-based asynchronous Erlang disk logger +pkg_fast_disk_log_homepage = https://github.com/lpgauth/fast_disk_log +pkg_fast_disk_log_fetch = git +pkg_fast_disk_log_repo = https://github.com/lpgauth/fast_disk_log +pkg_fast_disk_log_commit = master + +PACKAGES += feeder +pkg_feeder_name = feeder +pkg_feeder_description = Stream parse RSS and Atom formatted XML feeds. +pkg_feeder_homepage = https://github.com/michaelnisi/feeder +pkg_feeder_fetch = git +pkg_feeder_repo = https://github.com/michaelnisi/feeder +pkg_feeder_commit = v1.4.6 + +PACKAGES += fix +pkg_fix_name = fix +pkg_fix_description = http://fixprotocol.org/ implementation. +pkg_fix_homepage = https://github.com/maxlapshin/fix +pkg_fix_fetch = git +pkg_fix_repo = https://github.com/maxlapshin/fix +pkg_fix_commit = master + +PACKAGES += flower +pkg_flower_name = flower +pkg_flower_description = FlowER - a Erlang OpenFlow development platform +pkg_flower_homepage = https://github.com/travelping/flower +pkg_flower_fetch = git +pkg_flower_repo = https://github.com/travelping/flower +pkg_flower_commit = master + +PACKAGES += fn +pkg_fn_name = fn +pkg_fn_description = Function utilities for Erlang +pkg_fn_homepage = https://github.com/reiddraper/fn +pkg_fn_fetch = git +pkg_fn_repo = https://github.com/reiddraper/fn +pkg_fn_commit = master + +PACKAGES += folsom_cowboy +pkg_folsom_cowboy_name = folsom_cowboy +pkg_folsom_cowboy_description = A Cowboy based Folsom HTTP Wrapper. +pkg_folsom_cowboy_homepage = https://github.com/boundary/folsom_cowboy +pkg_folsom_cowboy_fetch = git +pkg_folsom_cowboy_repo = https://github.com/boundary/folsom_cowboy +pkg_folsom_cowboy_commit = master + +PACKAGES += folsomite +pkg_folsomite_name = folsomite +pkg_folsomite_description = blow up your graphite / riemann server with folsom metrics +pkg_folsomite_homepage = https://github.com/campanja/folsomite +pkg_folsomite_fetch = git +pkg_folsomite_repo = https://github.com/campanja/folsomite +pkg_folsomite_commit = master + +PACKAGES += folsom +pkg_folsom_name = folsom +pkg_folsom_description = Expose Erlang Events and Metrics +pkg_folsom_homepage = https://github.com/boundary/folsom +pkg_folsom_fetch = git +pkg_folsom_repo = https://github.com/boundary/folsom +pkg_folsom_commit = master + +PACKAGES += fs +pkg_fs_name = fs +pkg_fs_description = Erlang FileSystem Listener +pkg_fs_homepage = https://github.com/synrc/fs +pkg_fs_fetch = git +pkg_fs_repo = https://github.com/synrc/fs +pkg_fs_commit = master + +PACKAGES += fuse +pkg_fuse_name = fuse +pkg_fuse_description = A Circuit Breaker for Erlang +pkg_fuse_homepage = https://github.com/jlouis/fuse +pkg_fuse_fetch = git +pkg_fuse_repo = https://github.com/jlouis/fuse +pkg_fuse_commit = master + +PACKAGES += gcm +pkg_gcm_name = gcm +pkg_gcm_description = An Erlang application for Google Cloud Messaging +pkg_gcm_homepage = https://github.com/pdincau/gcm-erlang +pkg_gcm_fetch = git +pkg_gcm_repo = https://github.com/pdincau/gcm-erlang +pkg_gcm_commit = master + +PACKAGES += gcprof +pkg_gcprof_name = gcprof +pkg_gcprof_description = Garbage Collection profiler for Erlang +pkg_gcprof_homepage = https://github.com/knutin/gcprof +pkg_gcprof_fetch = git +pkg_gcprof_repo = https://github.com/knutin/gcprof +pkg_gcprof_commit = master + +PACKAGES += geas +pkg_geas_name = geas +pkg_geas_description = Guess Erlang Application Scattering +pkg_geas_homepage = https://github.com/crownedgrouse/geas +pkg_geas_fetch = git +pkg_geas_repo = https://github.com/crownedgrouse/geas +pkg_geas_commit = master + +PACKAGES += geef +pkg_geef_name = geef +pkg_geef_description = Git NEEEEF (Erlang NIF) +pkg_geef_homepage = https://github.com/carlosmn/geef +pkg_geef_fetch = git +pkg_geef_repo = https://github.com/carlosmn/geef +pkg_geef_commit = master + +PACKAGES += gen_coap +pkg_gen_coap_name = gen_coap +pkg_gen_coap_description = Generic Erlang CoAP Client/Server +pkg_gen_coap_homepage = https://github.com/gotthardp/gen_coap +pkg_gen_coap_fetch = git +pkg_gen_coap_repo = https://github.com/gotthardp/gen_coap +pkg_gen_coap_commit = master + +PACKAGES += gen_cycle +pkg_gen_cycle_name = gen_cycle +pkg_gen_cycle_description = Simple, generic OTP behaviour for recurring tasks +pkg_gen_cycle_homepage = https://github.com/aerosol/gen_cycle +pkg_gen_cycle_fetch = git +pkg_gen_cycle_repo = https://github.com/aerosol/gen_cycle +pkg_gen_cycle_commit = develop + +PACKAGES += gen_icmp +pkg_gen_icmp_name = gen_icmp +pkg_gen_icmp_description = Erlang interface to ICMP sockets +pkg_gen_icmp_homepage = https://github.com/msantos/gen_icmp +pkg_gen_icmp_fetch = git +pkg_gen_icmp_repo = https://github.com/msantos/gen_icmp +pkg_gen_icmp_commit = master + +PACKAGES += gen_nb_server +pkg_gen_nb_server_name = gen_nb_server +pkg_gen_nb_server_description = OTP behavior for writing non-blocking servers +pkg_gen_nb_server_homepage = https://github.com/kevsmith/gen_nb_server +pkg_gen_nb_server_fetch = git +pkg_gen_nb_server_repo = https://github.com/kevsmith/gen_nb_server +pkg_gen_nb_server_commit = master + +PACKAGES += gen_paxos +pkg_gen_paxos_name = gen_paxos +pkg_gen_paxos_description = An Erlang/OTP-style implementation of the PAXOS distributed consensus protocol +pkg_gen_paxos_homepage = https://github.com/gburd/gen_paxos +pkg_gen_paxos_fetch = git +pkg_gen_paxos_repo = https://github.com/gburd/gen_paxos +pkg_gen_paxos_commit = master + +PACKAGES += gen_smtp +pkg_gen_smtp_name = gen_smtp +pkg_gen_smtp_description = A generic Erlang SMTP server and client that can be extended via callback modules +pkg_gen_smtp_homepage = https://github.com/Vagabond/gen_smtp +pkg_gen_smtp_fetch = git +pkg_gen_smtp_repo = https://github.com/Vagabond/gen_smtp +pkg_gen_smtp_commit = master + +PACKAGES += gen_tracker +pkg_gen_tracker_name = gen_tracker +pkg_gen_tracker_description = supervisor with ets handling of children and their metadata +pkg_gen_tracker_homepage = https://github.com/erlyvideo/gen_tracker +pkg_gen_tracker_fetch = git +pkg_gen_tracker_repo = https://github.com/erlyvideo/gen_tracker +pkg_gen_tracker_commit = master + +PACKAGES += gen_unix +pkg_gen_unix_name = gen_unix +pkg_gen_unix_description = Erlang Unix socket interface +pkg_gen_unix_homepage = https://github.com/msantos/gen_unix +pkg_gen_unix_fetch = git +pkg_gen_unix_repo = https://github.com/msantos/gen_unix +pkg_gen_unix_commit = master + +PACKAGES += getopt +pkg_getopt_name = getopt +pkg_getopt_description = Module to parse command line arguments using the GNU getopt syntax +pkg_getopt_homepage = https://github.com/jcomellas/getopt +pkg_getopt_fetch = git +pkg_getopt_repo = https://github.com/jcomellas/getopt +pkg_getopt_commit = master + +PACKAGES += gettext +pkg_gettext_name = gettext +pkg_gettext_description = Erlang internationalization library. +pkg_gettext_homepage = https://github.com/etnt/gettext +pkg_gettext_fetch = git +pkg_gettext_repo = https://github.com/etnt/gettext +pkg_gettext_commit = master + +PACKAGES += giallo +pkg_giallo_name = giallo +pkg_giallo_description = Small and flexible web framework on top of Cowboy +pkg_giallo_homepage = https://github.com/kivra/giallo +pkg_giallo_fetch = git +pkg_giallo_repo = https://github.com/kivra/giallo +pkg_giallo_commit = master + +PACKAGES += gin +pkg_gin_name = gin +pkg_gin_description = The guards and for Erlang parse_transform +pkg_gin_homepage = https://github.com/mad-cocktail/gin +pkg_gin_fetch = git +pkg_gin_repo = https://github.com/mad-cocktail/gin +pkg_gin_commit = master + +PACKAGES += gitty +pkg_gitty_name = gitty +pkg_gitty_description = Git access in erlang +pkg_gitty_homepage = https://github.com/maxlapshin/gitty +pkg_gitty_fetch = git +pkg_gitty_repo = https://github.com/maxlapshin/gitty +pkg_gitty_commit = master + +PACKAGES += gold_fever +pkg_gold_fever_name = gold_fever +pkg_gold_fever_description = A Treasure Hunt for Erlangers +pkg_gold_fever_homepage = https://github.com/inaka/gold_fever +pkg_gold_fever_fetch = git +pkg_gold_fever_repo = https://github.com/inaka/gold_fever +pkg_gold_fever_commit = master + +PACKAGES += gossiperl +pkg_gossiperl_name = gossiperl +pkg_gossiperl_description = Gossip middleware in Erlang +pkg_gossiperl_homepage = http://gossiperl.com/ +pkg_gossiperl_fetch = git +pkg_gossiperl_repo = https://github.com/gossiperl/gossiperl +pkg_gossiperl_commit = master + +PACKAGES += gpb +pkg_gpb_name = gpb +pkg_gpb_description = A Google Protobuf implementation for Erlang +pkg_gpb_homepage = https://github.com/tomas-abrahamsson/gpb +pkg_gpb_fetch = git +pkg_gpb_repo = https://github.com/tomas-abrahamsson/gpb +pkg_gpb_commit = master + +PACKAGES += gproc +pkg_gproc_name = gproc +pkg_gproc_description = Extended process registry for Erlang +pkg_gproc_homepage = https://github.com/uwiger/gproc +pkg_gproc_fetch = git +pkg_gproc_repo = https://github.com/uwiger/gproc +pkg_gproc_commit = master + +PACKAGES += grapherl +pkg_grapherl_name = grapherl +pkg_grapherl_description = Create graphs of Erlang systems and programs +pkg_grapherl_homepage = https://github.com/eproxus/grapherl +pkg_grapherl_fetch = git +pkg_grapherl_repo = https://github.com/eproxus/grapherl +pkg_grapherl_commit = master + +PACKAGES += gun +pkg_gun_name = gun +pkg_gun_description = Asynchronous SPDY, HTTP and Websocket client written in Erlang. +pkg_gun_homepage = http//ninenines.eu +pkg_gun_fetch = git +pkg_gun_repo = https://github.com/ninenines/gun +pkg_gun_commit = master + +PACKAGES += gut +pkg_gut_name = gut +pkg_gut_description = gut is a template printing, aka scaffolding, tool for Erlang. Like rails generate or yeoman +pkg_gut_homepage = https://github.com/unbalancedparentheses/gut +pkg_gut_fetch = git +pkg_gut_repo = https://github.com/unbalancedparentheses/gut +pkg_gut_commit = master + +PACKAGES += hackney +pkg_hackney_name = hackney +pkg_hackney_description = simple HTTP client in Erlang +pkg_hackney_homepage = https://github.com/benoitc/hackney +pkg_hackney_fetch = git +pkg_hackney_repo = https://github.com/benoitc/hackney +pkg_hackney_commit = master + +PACKAGES += hamcrest +pkg_hamcrest_name = hamcrest +pkg_hamcrest_description = Erlang port of Hamcrest +pkg_hamcrest_homepage = https://github.com/hyperthunk/hamcrest-erlang +pkg_hamcrest_fetch = git +pkg_hamcrest_repo = https://github.com/hyperthunk/hamcrest-erlang +pkg_hamcrest_commit = master + +PACKAGES += hanoidb +pkg_hanoidb_name = hanoidb +pkg_hanoidb_description = Erlang LSM BTree Storage +pkg_hanoidb_homepage = https://github.com/krestenkrab/hanoidb +pkg_hanoidb_fetch = git +pkg_hanoidb_repo = https://github.com/krestenkrab/hanoidb +pkg_hanoidb_commit = master + +PACKAGES += hottub +pkg_hottub_name = hottub +pkg_hottub_description = Permanent Erlang Worker Pool +pkg_hottub_homepage = https://github.com/bfrog/hottub +pkg_hottub_fetch = git +pkg_hottub_repo = https://github.com/bfrog/hottub +pkg_hottub_commit = master + +PACKAGES += hpack +pkg_hpack_name = hpack +pkg_hpack_description = HPACK Implementation for Erlang +pkg_hpack_homepage = https://github.com/joedevivo/hpack +pkg_hpack_fetch = git +pkg_hpack_repo = https://github.com/joedevivo/hpack +pkg_hpack_commit = master + +PACKAGES += hyper +pkg_hyper_name = hyper +pkg_hyper_description = Erlang implementation of HyperLogLog +pkg_hyper_homepage = https://github.com/GameAnalytics/hyper +pkg_hyper_fetch = git +pkg_hyper_repo = https://github.com/GameAnalytics/hyper +pkg_hyper_commit = master + +PACKAGES += i18n +pkg_i18n_name = i18n +pkg_i18n_description = International components for unicode from Erlang (unicode, date, string, number, format, locale, localization, transliteration, icu4e) +pkg_i18n_homepage = https://github.com/erlang-unicode/i18n +pkg_i18n_fetch = git +pkg_i18n_repo = https://github.com/erlang-unicode/i18n +pkg_i18n_commit = master + +PACKAGES += ibrowse +pkg_ibrowse_name = ibrowse +pkg_ibrowse_description = Erlang HTTP client +pkg_ibrowse_homepage = https://github.com/cmullaparthi/ibrowse +pkg_ibrowse_fetch = git +pkg_ibrowse_repo = https://github.com/cmullaparthi/ibrowse +pkg_ibrowse_commit = v4.1.1 + +PACKAGES += ierlang +pkg_ierlang_name = ierlang +pkg_ierlang_description = An Erlang language kernel for IPython. +pkg_ierlang_homepage = https://github.com/robbielynch/ierlang +pkg_ierlang_fetch = git +pkg_ierlang_repo = https://github.com/robbielynch/ierlang +pkg_ierlang_commit = master + +PACKAGES += iota +pkg_iota_name = iota +pkg_iota_description = iota (Inter-dependency Objective Testing Apparatus) - a tool to enforce clean separation of responsibilities in Erlang code +pkg_iota_homepage = https://github.com/jpgneves/iota +pkg_iota_fetch = git +pkg_iota_repo = https://github.com/jpgneves/iota +pkg_iota_commit = master + +PACKAGES += ircd +pkg_ircd_name = ircd +pkg_ircd_description = A pluggable IRC daemon application/library for Erlang. +pkg_ircd_homepage = https://github.com/tonyg/erlang-ircd +pkg_ircd_fetch = git +pkg_ircd_repo = https://github.com/tonyg/erlang-ircd +pkg_ircd_commit = master + +PACKAGES += irc_lib +pkg_irc_lib_name = irc_lib +pkg_irc_lib_description = Erlang irc client library +pkg_irc_lib_homepage = https://github.com/OtpChatBot/irc_lib +pkg_irc_lib_fetch = git +pkg_irc_lib_repo = https://github.com/OtpChatBot/irc_lib +pkg_irc_lib_commit = master + +PACKAGES += iris +pkg_iris_name = iris +pkg_iris_description = Iris Erlang binding +pkg_iris_homepage = https://github.com/project-iris/iris-erl +pkg_iris_fetch = git +pkg_iris_repo = https://github.com/project-iris/iris-erl +pkg_iris_commit = master + +PACKAGES += iso8601 +pkg_iso8601_name = iso8601 +pkg_iso8601_description = Erlang ISO 8601 date formatter/parser +pkg_iso8601_homepage = https://github.com/seansawyer/erlang_iso8601 +pkg_iso8601_fetch = git +pkg_iso8601_repo = https://github.com/seansawyer/erlang_iso8601 +pkg_iso8601_commit = master + +PACKAGES += jamdb_sybase +pkg_jamdb_sybase_name = jamdb_sybase +pkg_jamdb_sybase_description = Erlang driver for SAP Sybase ASE +pkg_jamdb_sybase_homepage = https://github.com/erlangbureau/jamdb_sybase +pkg_jamdb_sybase_fetch = git +pkg_jamdb_sybase_repo = https://github.com/erlangbureau/jamdb_sybase +pkg_jamdb_sybase_commit = 0.6.0 + +PACKAGES += jerg +pkg_jerg_name = jerg +pkg_jerg_description = JSON Schema to Erlang Records Generator +pkg_jerg_homepage = https://github.com/ddossot/jerg +pkg_jerg_fetch = git +pkg_jerg_repo = https://github.com/ddossot/jerg +pkg_jerg_commit = master + +PACKAGES += jesse +pkg_jesse_name = jesse +pkg_jesse_description = jesse (JSon Schema Erlang) is an implementation of a json schema validator for Erlang. +pkg_jesse_homepage = https://github.com/klarna/jesse +pkg_jesse_fetch = git +pkg_jesse_repo = https://github.com/klarna/jesse +pkg_jesse_commit = master + +PACKAGES += jiffy +pkg_jiffy_name = jiffy +pkg_jiffy_description = JSON NIFs for Erlang. +pkg_jiffy_homepage = https://github.com/davisp/jiffy +pkg_jiffy_fetch = git +pkg_jiffy_repo = https://github.com/davisp/jiffy +pkg_jiffy_commit = master + +PACKAGES += jiffy_v +pkg_jiffy_v_name = jiffy_v +pkg_jiffy_v_description = JSON validation utility +pkg_jiffy_v_homepage = https://github.com/shizzard/jiffy-v +pkg_jiffy_v_fetch = git +pkg_jiffy_v_repo = https://github.com/shizzard/jiffy-v +pkg_jiffy_v_commit = 0.3.3 + +PACKAGES += jobs +pkg_jobs_name = jobs +pkg_jobs_description = a Job scheduler for load regulation +pkg_jobs_homepage = https://github.com/esl/jobs +pkg_jobs_fetch = git +pkg_jobs_repo = https://github.com/esl/jobs +pkg_jobs_commit = 0.3 + +PACKAGES += joxa +pkg_joxa_name = joxa +pkg_joxa_description = A Modern Lisp for the Erlang VM +pkg_joxa_homepage = https://github.com/joxa/joxa +pkg_joxa_fetch = git +pkg_joxa_repo = https://github.com/joxa/joxa +pkg_joxa_commit = master + +PACKAGES += jsonerl +pkg_jsonerl_name = jsonerl +pkg_jsonerl_description = yet another but slightly different erlang <-> json encoder/decoder +pkg_jsonerl_homepage = https://github.com/lambder/jsonerl +pkg_jsonerl_fetch = git +pkg_jsonerl_repo = https://github.com/lambder/jsonerl +pkg_jsonerl_commit = master + +PACKAGES += json +pkg_json_name = json +pkg_json_description = a high level json library for erlang (17.0+) +pkg_json_homepage = https://github.com/talentdeficit/json +pkg_json_fetch = git +pkg_json_repo = https://github.com/talentdeficit/json +pkg_json_commit = master + +PACKAGES += jsonpath +pkg_jsonpath_name = jsonpath +pkg_jsonpath_description = Fast Erlang JSON data retrieval and updates via javascript-like notation +pkg_jsonpath_homepage = https://github.com/GeneStevens/jsonpath +pkg_jsonpath_fetch = git +pkg_jsonpath_repo = https://github.com/GeneStevens/jsonpath +pkg_jsonpath_commit = master + +PACKAGES += json_rec +pkg_json_rec_name = json_rec +pkg_json_rec_description = JSON to erlang record +pkg_json_rec_homepage = https://github.com/justinkirby/json_rec +pkg_json_rec_fetch = git +pkg_json_rec_repo = https://github.com/justinkirby/json_rec +pkg_json_rec_commit = master + +PACKAGES += jsonx +pkg_jsonx_name = jsonx +pkg_jsonx_description = JSONX is an Erlang library for efficient decode and encode JSON, written in C. +pkg_jsonx_homepage = https://github.com/iskra/jsonx +pkg_jsonx_fetch = git +pkg_jsonx_repo = https://github.com/iskra/jsonx +pkg_jsonx_commit = master + +PACKAGES += jsx +pkg_jsx_name = jsx +pkg_jsx_description = An Erlang application for consuming, producing and manipulating JSON. +pkg_jsx_homepage = https://github.com/talentdeficit/jsx +pkg_jsx_fetch = git +pkg_jsx_repo = https://github.com/talentdeficit/jsx +pkg_jsx_commit = master + +PACKAGES += kafka +pkg_kafka_name = kafka +pkg_kafka_description = Kafka consumer and producer in Erlang +pkg_kafka_homepage = https://github.com/wooga/kafka-erlang +pkg_kafka_fetch = git +pkg_kafka_repo = https://github.com/wooga/kafka-erlang +pkg_kafka_commit = master + +PACKAGES += kai +pkg_kai_name = kai +pkg_kai_description = DHT storage by Takeshi Inoue +pkg_kai_homepage = https://github.com/synrc/kai +pkg_kai_fetch = git +pkg_kai_repo = https://github.com/synrc/kai +pkg_kai_commit = master + +PACKAGES += katja +pkg_katja_name = katja +pkg_katja_description = A simple Riemann client written in Erlang. +pkg_katja_homepage = https://github.com/nifoc/katja +pkg_katja_fetch = git +pkg_katja_repo = https://github.com/nifoc/katja +pkg_katja_commit = master + +PACKAGES += kdht +pkg_kdht_name = kdht +pkg_kdht_description = kdht is an erlang DHT implementation +pkg_kdht_homepage = https://github.com/kevinlynx/kdht +pkg_kdht_fetch = git +pkg_kdht_repo = https://github.com/kevinlynx/kdht +pkg_kdht_commit = master + +PACKAGES += key2value +pkg_key2value_name = key2value +pkg_key2value_description = Erlang 2-way map +pkg_key2value_homepage = https://github.com/okeuday/key2value +pkg_key2value_fetch = git +pkg_key2value_repo = https://github.com/okeuday/key2value +pkg_key2value_commit = master + +PACKAGES += keys1value +pkg_keys1value_name = keys1value +pkg_keys1value_description = Erlang set associative map for key lists +pkg_keys1value_homepage = https://github.com/okeuday/keys1value +pkg_keys1value_fetch = git +pkg_keys1value_repo = https://github.com/okeuday/keys1value +pkg_keys1value_commit = master + +PACKAGES += kinetic +pkg_kinetic_name = kinetic +pkg_kinetic_description = Erlang Kinesis Client +pkg_kinetic_homepage = https://github.com/AdRoll/kinetic +pkg_kinetic_fetch = git +pkg_kinetic_repo = https://github.com/AdRoll/kinetic +pkg_kinetic_commit = master + +PACKAGES += kjell +pkg_kjell_name = kjell +pkg_kjell_description = Erlang Shell +pkg_kjell_homepage = https://github.com/karlll/kjell +pkg_kjell_fetch = git +pkg_kjell_repo = https://github.com/karlll/kjell +pkg_kjell_commit = master + +PACKAGES += kraken +pkg_kraken_name = kraken +pkg_kraken_description = Distributed Pubsub Server for Realtime Apps +pkg_kraken_homepage = https://github.com/Asana/kraken +pkg_kraken_fetch = git +pkg_kraken_repo = https://github.com/Asana/kraken +pkg_kraken_commit = master + +PACKAGES += kucumberl +pkg_kucumberl_name = kucumberl +pkg_kucumberl_description = A pure-erlang, open-source, implementation of Cucumber +pkg_kucumberl_homepage = https://github.com/openshine/kucumberl +pkg_kucumberl_fetch = git +pkg_kucumberl_repo = https://github.com/openshine/kucumberl +pkg_kucumberl_commit = master + +PACKAGES += kvc +pkg_kvc_name = kvc +pkg_kvc_description = KVC - Key Value Coding for Erlang data structures +pkg_kvc_homepage = https://github.com/etrepum/kvc +pkg_kvc_fetch = git +pkg_kvc_repo = https://github.com/etrepum/kvc +pkg_kvc_commit = master + +PACKAGES += kvlists +pkg_kvlists_name = kvlists +pkg_kvlists_description = Lists of key-value pairs (decoded JSON) in Erlang +pkg_kvlists_homepage = https://github.com/jcomellas/kvlists +pkg_kvlists_fetch = git +pkg_kvlists_repo = https://github.com/jcomellas/kvlists +pkg_kvlists_commit = master + +PACKAGES += kvs +pkg_kvs_name = kvs +pkg_kvs_description = Container and Iterator +pkg_kvs_homepage = https://github.com/synrc/kvs +pkg_kvs_fetch = git +pkg_kvs_repo = https://github.com/synrc/kvs +pkg_kvs_commit = master + +PACKAGES += lager_amqp_backend +pkg_lager_amqp_backend_name = lager_amqp_backend +pkg_lager_amqp_backend_description = AMQP RabbitMQ Lager backend +pkg_lager_amqp_backend_homepage = https://github.com/jbrisbin/lager_amqp_backend +pkg_lager_amqp_backend_fetch = git +pkg_lager_amqp_backend_repo = https://github.com/jbrisbin/lager_amqp_backend +pkg_lager_amqp_backend_commit = master + +PACKAGES += lager +pkg_lager_name = lager +pkg_lager_description = A logging framework for Erlang/OTP. +pkg_lager_homepage = https://github.com/basho/lager +pkg_lager_fetch = git +pkg_lager_repo = https://github.com/basho/lager +pkg_lager_commit = master + +PACKAGES += lager_syslog +pkg_lager_syslog_name = lager_syslog +pkg_lager_syslog_description = Syslog backend for lager +pkg_lager_syslog_homepage = https://github.com/basho/lager_syslog +pkg_lager_syslog_fetch = git +pkg_lager_syslog_repo = https://github.com/basho/lager_syslog +pkg_lager_syslog_commit = master + +PACKAGES += lambdapad +pkg_lambdapad_name = lambdapad +pkg_lambdapad_description = Static site generator using Erlang. Yes, Erlang. +pkg_lambdapad_homepage = https://github.com/gar1t/lambdapad +pkg_lambdapad_fetch = git +pkg_lambdapad_repo = https://github.com/gar1t/lambdapad +pkg_lambdapad_commit = master + +PACKAGES += lasp +pkg_lasp_name = lasp +pkg_lasp_description = A Language for Distributed, Eventually Consistent Computations +pkg_lasp_homepage = http://lasp-lang.org/ +pkg_lasp_fetch = git +pkg_lasp_repo = https://github.com/lasp-lang/lasp +pkg_lasp_commit = master + +PACKAGES += lasse +pkg_lasse_name = lasse +pkg_lasse_description = SSE handler for Cowboy +pkg_lasse_homepage = https://github.com/inaka/lasse +pkg_lasse_fetch = git +pkg_lasse_repo = https://github.com/inaka/lasse +pkg_lasse_commit = 0.1.0 + +PACKAGES += ldap +pkg_ldap_name = ldap +pkg_ldap_description = LDAP server written in Erlang +pkg_ldap_homepage = https://github.com/spawnproc/ldap +pkg_ldap_fetch = git +pkg_ldap_repo = https://github.com/spawnproc/ldap +pkg_ldap_commit = master + +PACKAGES += lethink +pkg_lethink_name = lethink +pkg_lethink_description = erlang driver for rethinkdb +pkg_lethink_homepage = https://github.com/taybin/lethink +pkg_lethink_fetch = git +pkg_lethink_repo = https://github.com/taybin/lethink +pkg_lethink_commit = master + +PACKAGES += lfe +pkg_lfe_name = lfe +pkg_lfe_description = Lisp Flavoured Erlang (LFE) +pkg_lfe_homepage = https://github.com/rvirding/lfe +pkg_lfe_fetch = git +pkg_lfe_repo = https://github.com/rvirding/lfe +pkg_lfe_commit = master + +PACKAGES += ling +pkg_ling_name = ling +pkg_ling_description = Erlang on Xen +pkg_ling_homepage = https://github.com/cloudozer/ling +pkg_ling_fetch = git +pkg_ling_repo = https://github.com/cloudozer/ling +pkg_ling_commit = master + +PACKAGES += live +pkg_live_name = live +pkg_live_description = Automated module and configuration reloader. +pkg_live_homepage = http://ninenines.eu +pkg_live_fetch = git +pkg_live_repo = https://github.com/ninenines/live +pkg_live_commit = master + +PACKAGES += lmq +pkg_lmq_name = lmq +pkg_lmq_description = Lightweight Message Queue +pkg_lmq_homepage = https://github.com/iij/lmq +pkg_lmq_fetch = git +pkg_lmq_repo = https://github.com/iij/lmq +pkg_lmq_commit = master + +PACKAGES += locker +pkg_locker_name = locker +pkg_locker_description = Atomic distributed 'check and set' for short-lived keys +pkg_locker_homepage = https://github.com/wooga/locker +pkg_locker_fetch = git +pkg_locker_repo = https://github.com/wooga/locker +pkg_locker_commit = master + +PACKAGES += locks +pkg_locks_name = locks +pkg_locks_description = A scalable, deadlock-resolving resource locker +pkg_locks_homepage = https://github.com/uwiger/locks +pkg_locks_fetch = git +pkg_locks_repo = https://github.com/uwiger/locks +pkg_locks_commit = master + +PACKAGES += log4erl +pkg_log4erl_name = log4erl +pkg_log4erl_description = A logger for erlang in the spirit of Log4J. +pkg_log4erl_homepage = https://github.com/ahmednawras/log4erl +pkg_log4erl_fetch = git +pkg_log4erl_repo = https://github.com/ahmednawras/log4erl +pkg_log4erl_commit = master + +PACKAGES += lol +pkg_lol_name = lol +pkg_lol_description = Lisp on erLang, and programming is fun again +pkg_lol_homepage = https://github.com/b0oh/lol +pkg_lol_fetch = git +pkg_lol_repo = https://github.com/b0oh/lol +pkg_lol_commit = master + +PACKAGES += lucid +pkg_lucid_name = lucid +pkg_lucid_description = HTTP/2 server written in Erlang +pkg_lucid_homepage = https://github.com/tatsuhiro-t/lucid +pkg_lucid_fetch = git +pkg_lucid_repo = https://github.com/tatsuhiro-t/lucid +pkg_lucid_commit = master + +PACKAGES += luerl +pkg_luerl_name = luerl +pkg_luerl_description = Lua in Erlang +pkg_luerl_homepage = https://github.com/rvirding/luerl +pkg_luerl_fetch = git +pkg_luerl_repo = https://github.com/rvirding/luerl +pkg_luerl_commit = develop + +PACKAGES += luwak +pkg_luwak_name = luwak +pkg_luwak_description = Large-object storage interface for Riak +pkg_luwak_homepage = https://github.com/basho/luwak +pkg_luwak_fetch = git +pkg_luwak_repo = https://github.com/basho/luwak +pkg_luwak_commit = master + +PACKAGES += lux +pkg_lux_name = lux +pkg_lux_description = Lux (LUcid eXpect scripting) simplifies test automation and provides an Expect-style execution of commands +pkg_lux_homepage = https://github.com/hawk/lux +pkg_lux_fetch = git +pkg_lux_repo = https://github.com/hawk/lux +pkg_lux_commit = master + +PACKAGES += machi +pkg_machi_name = machi +pkg_machi_description = Machi file store +pkg_machi_homepage = https://github.com/basho/machi +pkg_machi_fetch = git +pkg_machi_repo = https://github.com/basho/machi +pkg_machi_commit = master + +PACKAGES += mad +pkg_mad_name = mad +pkg_mad_description = Small and Fast Rebar Replacement +pkg_mad_homepage = https://github.com/synrc/mad +pkg_mad_fetch = git +pkg_mad_repo = https://github.com/synrc/mad +pkg_mad_commit = master + +PACKAGES += marina +pkg_marina_name = marina +pkg_marina_description = Non-blocking Erlang Cassandra CQL3 client +pkg_marina_homepage = https://github.com/lpgauth/marina +pkg_marina_fetch = git +pkg_marina_repo = https://github.com/lpgauth/marina +pkg_marina_commit = master + +PACKAGES += mavg +pkg_mavg_name = mavg +pkg_mavg_description = Erlang :: Exponential moving average library +pkg_mavg_homepage = https://github.com/EchoTeam/mavg +pkg_mavg_fetch = git +pkg_mavg_repo = https://github.com/EchoTeam/mavg +pkg_mavg_commit = master + +PACKAGES += mcd +pkg_mcd_name = mcd +pkg_mcd_description = Fast memcached protocol client in pure Erlang +pkg_mcd_homepage = https://github.com/EchoTeam/mcd +pkg_mcd_fetch = git +pkg_mcd_repo = https://github.com/EchoTeam/mcd +pkg_mcd_commit = master + +PACKAGES += mcerlang +pkg_mcerlang_name = mcerlang +pkg_mcerlang_description = The McErlang model checker for Erlang +pkg_mcerlang_homepage = https://github.com/fredlund/McErlang +pkg_mcerlang_fetch = git +pkg_mcerlang_repo = https://github.com/fredlund/McErlang +pkg_mcerlang_commit = master + +PACKAGES += mc_erl +pkg_mc_erl_name = mc_erl +pkg_mc_erl_description = mc-erl is a server for Minecraft 1.4.7 written in Erlang. +pkg_mc_erl_homepage = https://github.com/clonejo/mc-erl +pkg_mc_erl_fetch = git +pkg_mc_erl_repo = https://github.com/clonejo/mc-erl +pkg_mc_erl_commit = master + +PACKAGES += meck +pkg_meck_name = meck +pkg_meck_description = A mocking library for Erlang +pkg_meck_homepage = https://github.com/eproxus/meck +pkg_meck_fetch = git +pkg_meck_repo = https://github.com/eproxus/meck +pkg_meck_commit = master + +PACKAGES += mekao +pkg_mekao_name = mekao +pkg_mekao_description = SQL constructor +pkg_mekao_homepage = https://github.com/ddosia/mekao +pkg_mekao_fetch = git +pkg_mekao_repo = https://github.com/ddosia/mekao +pkg_mekao_commit = master + +PACKAGES += memo +pkg_memo_name = memo +pkg_memo_description = Erlang memoization server +pkg_memo_homepage = https://github.com/tuncer/memo +pkg_memo_fetch = git +pkg_memo_repo = https://github.com/tuncer/memo +pkg_memo_commit = master + +PACKAGES += merge_index +pkg_merge_index_name = merge_index +pkg_merge_index_description = MergeIndex is an Erlang library for storing ordered sets on disk. It is very similar to an SSTable (in Google's Bigtable) or an HFile (in Hadoop). +pkg_merge_index_homepage = https://github.com/basho/merge_index +pkg_merge_index_fetch = git +pkg_merge_index_repo = https://github.com/basho/merge_index +pkg_merge_index_commit = master + +PACKAGES += merl +pkg_merl_name = merl +pkg_merl_description = Metaprogramming in Erlang +pkg_merl_homepage = https://github.com/richcarl/merl +pkg_merl_fetch = git +pkg_merl_repo = https://github.com/richcarl/merl +pkg_merl_commit = master + +PACKAGES += mimerl +pkg_mimerl_name = mimerl +pkg_mimerl_description = library to handle mimetypes +pkg_mimerl_homepage = https://github.com/benoitc/mimerl +pkg_mimerl_fetch = git +pkg_mimerl_repo = https://github.com/benoitc/mimerl +pkg_mimerl_commit = master + +PACKAGES += mimetypes +pkg_mimetypes_name = mimetypes +pkg_mimetypes_description = Erlang MIME types library +pkg_mimetypes_homepage = https://github.com/spawngrid/mimetypes +pkg_mimetypes_fetch = git +pkg_mimetypes_repo = https://github.com/spawngrid/mimetypes +pkg_mimetypes_commit = master + +PACKAGES += mixer +pkg_mixer_name = mixer +pkg_mixer_description = Mix in functions from other modules +pkg_mixer_homepage = https://github.com/chef/mixer +pkg_mixer_fetch = git +pkg_mixer_repo = https://github.com/chef/mixer +pkg_mixer_commit = master + +PACKAGES += mochiweb +pkg_mochiweb_name = mochiweb +pkg_mochiweb_description = MochiWeb is an Erlang library for building lightweight HTTP servers. +pkg_mochiweb_homepage = https://github.com/mochi/mochiweb +pkg_mochiweb_fetch = git +pkg_mochiweb_repo = https://github.com/mochi/mochiweb +pkg_mochiweb_commit = master + +PACKAGES += mochiweb_xpath +pkg_mochiweb_xpath_name = mochiweb_xpath +pkg_mochiweb_xpath_description = XPath support for mochiweb's html parser +pkg_mochiweb_xpath_homepage = https://github.com/retnuh/mochiweb_xpath +pkg_mochiweb_xpath_fetch = git +pkg_mochiweb_xpath_repo = https://github.com/retnuh/mochiweb_xpath +pkg_mochiweb_xpath_commit = master + +PACKAGES += mockgyver +pkg_mockgyver_name = mockgyver +pkg_mockgyver_description = A mocking library for Erlang +pkg_mockgyver_homepage = https://github.com/klajo/mockgyver +pkg_mockgyver_fetch = git +pkg_mockgyver_repo = https://github.com/klajo/mockgyver +pkg_mockgyver_commit = master + +PACKAGES += modlib +pkg_modlib_name = modlib +pkg_modlib_description = Web framework based on Erlang's inets httpd +pkg_modlib_homepage = https://github.com/gar1t/modlib +pkg_modlib_fetch = git +pkg_modlib_repo = https://github.com/gar1t/modlib +pkg_modlib_commit = master + +PACKAGES += mongodb +pkg_mongodb_name = mongodb +pkg_mongodb_description = MongoDB driver for Erlang +pkg_mongodb_homepage = https://github.com/comtihon/mongodb-erlang +pkg_mongodb_fetch = git +pkg_mongodb_repo = https://github.com/comtihon/mongodb-erlang +pkg_mongodb_commit = master + +PACKAGES += mongooseim +pkg_mongooseim_name = mongooseim +pkg_mongooseim_description = Jabber / XMPP server with focus on performance and scalability, by Erlang Solutions +pkg_mongooseim_homepage = https://www.erlang-solutions.com/products/mongooseim-massively-scalable-ejabberd-platform +pkg_mongooseim_fetch = git +pkg_mongooseim_repo = https://github.com/esl/MongooseIM +pkg_mongooseim_commit = master + +PACKAGES += moyo +pkg_moyo_name = moyo +pkg_moyo_description = Erlang utility functions library +pkg_moyo_homepage = https://github.com/dwango/moyo +pkg_moyo_fetch = git +pkg_moyo_repo = https://github.com/dwango/moyo +pkg_moyo_commit = master + +PACKAGES += msgpack +pkg_msgpack_name = msgpack +pkg_msgpack_description = MessagePack (de)serializer implementation for Erlang +pkg_msgpack_homepage = https://github.com/msgpack/msgpack-erlang +pkg_msgpack_fetch = git +pkg_msgpack_repo = https://github.com/msgpack/msgpack-erlang +pkg_msgpack_commit = master + +PACKAGES += mu2 +pkg_mu2_name = mu2 +pkg_mu2_description = Erlang mutation testing tool +pkg_mu2_homepage = https://github.com/ramsay-t/mu2 +pkg_mu2_fetch = git +pkg_mu2_repo = https://github.com/ramsay-t/mu2 +pkg_mu2_commit = master + +PACKAGES += mustache +pkg_mustache_name = mustache +pkg_mustache_description = Mustache template engine for Erlang. +pkg_mustache_homepage = https://github.com/mojombo/mustache.erl +pkg_mustache_fetch = git +pkg_mustache_repo = https://github.com/mojombo/mustache.erl +pkg_mustache_commit = master + +PACKAGES += myproto +pkg_myproto_name = myproto +pkg_myproto_description = MySQL Server Protocol in Erlang +pkg_myproto_homepage = https://github.com/altenwald/myproto +pkg_myproto_fetch = git +pkg_myproto_repo = https://github.com/altenwald/myproto +pkg_myproto_commit = master + +PACKAGES += mysql +pkg_mysql_name = mysql +pkg_mysql_description = Erlang MySQL Driver (from code.google.com) +pkg_mysql_homepage = https://github.com/dizzyd/erlang-mysql-driver +pkg_mysql_fetch = git +pkg_mysql_repo = https://github.com/dizzyd/erlang-mysql-driver +pkg_mysql_commit = master + +PACKAGES += n2o +pkg_n2o_name = n2o +pkg_n2o_description = WebSocket Application Server +pkg_n2o_homepage = https://github.com/5HT/n2o +pkg_n2o_fetch = git +pkg_n2o_repo = https://github.com/5HT/n2o +pkg_n2o_commit = master + +PACKAGES += nat_upnp +pkg_nat_upnp_name = nat_upnp +pkg_nat_upnp_description = Erlang library to map your internal port to an external using UNP IGD +pkg_nat_upnp_homepage = https://github.com/benoitc/nat_upnp +pkg_nat_upnp_fetch = git +pkg_nat_upnp_repo = https://github.com/benoitc/nat_upnp +pkg_nat_upnp_commit = master + +PACKAGES += neo4j +pkg_neo4j_name = neo4j +pkg_neo4j_description = Erlang client library for Neo4J. +pkg_neo4j_homepage = https://github.com/dmitriid/neo4j-erlang +pkg_neo4j_fetch = git +pkg_neo4j_repo = https://github.com/dmitriid/neo4j-erlang +pkg_neo4j_commit = master + +PACKAGES += neotoma +pkg_neotoma_name = neotoma +pkg_neotoma_description = Erlang library and packrat parser-generator for parsing expression grammars. +pkg_neotoma_homepage = https://github.com/seancribbs/neotoma +pkg_neotoma_fetch = git +pkg_neotoma_repo = https://github.com/seancribbs/neotoma +pkg_neotoma_commit = master + +PACKAGES += newrelic +pkg_newrelic_name = newrelic +pkg_newrelic_description = Erlang library for sending metrics to New Relic +pkg_newrelic_homepage = https://github.com/wooga/newrelic-erlang +pkg_newrelic_fetch = git +pkg_newrelic_repo = https://github.com/wooga/newrelic-erlang +pkg_newrelic_commit = master + +PACKAGES += nifty +pkg_nifty_name = nifty +pkg_nifty_description = Erlang NIF wrapper generator +pkg_nifty_homepage = https://github.com/parapluu/nifty +pkg_nifty_fetch = git +pkg_nifty_repo = https://github.com/parapluu/nifty +pkg_nifty_commit = master + +PACKAGES += nitrogen_core +pkg_nitrogen_core_name = nitrogen_core +pkg_nitrogen_core_description = The core Nitrogen library. +pkg_nitrogen_core_homepage = http://nitrogenproject.com/ +pkg_nitrogen_core_fetch = git +pkg_nitrogen_core_repo = https://github.com/nitrogen/nitrogen_core +pkg_nitrogen_core_commit = master + +PACKAGES += nkbase +pkg_nkbase_name = nkbase +pkg_nkbase_description = NkBASE distributed database +pkg_nkbase_homepage = https://github.com/Nekso/nkbase +pkg_nkbase_fetch = git +pkg_nkbase_repo = https://github.com/Nekso/nkbase +pkg_nkbase_commit = develop + +PACKAGES += nkdocker +pkg_nkdocker_name = nkdocker +pkg_nkdocker_description = Erlang Docker client +pkg_nkdocker_homepage = https://github.com/Nekso/nkdocker +pkg_nkdocker_fetch = git +pkg_nkdocker_repo = https://github.com/Nekso/nkdocker +pkg_nkdocker_commit = master + +PACKAGES += nkpacket +pkg_nkpacket_name = nkpacket +pkg_nkpacket_description = Generic Erlang transport layer +pkg_nkpacket_homepage = https://github.com/Nekso/nkpacket +pkg_nkpacket_fetch = git +pkg_nkpacket_repo = https://github.com/Nekso/nkpacket +pkg_nkpacket_commit = master + +PACKAGES += nksip +pkg_nksip_name = nksip +pkg_nksip_description = Erlang SIP application server +pkg_nksip_homepage = https://github.com/kalta/nksip +pkg_nksip_fetch = git +pkg_nksip_repo = https://github.com/kalta/nksip +pkg_nksip_commit = master + +PACKAGES += nodefinder +pkg_nodefinder_name = nodefinder +pkg_nodefinder_description = automatic node discovery via UDP multicast +pkg_nodefinder_homepage = https://github.com/erlanger/nodefinder +pkg_nodefinder_fetch = git +pkg_nodefinder_repo = https://github.com/okeuday/nodefinder +pkg_nodefinder_commit = master + +PACKAGES += nprocreg +pkg_nprocreg_name = nprocreg +pkg_nprocreg_description = Minimal Distributed Erlang Process Registry +pkg_nprocreg_homepage = http://nitrogenproject.com/ +pkg_nprocreg_fetch = git +pkg_nprocreg_repo = https://github.com/nitrogen/nprocreg +pkg_nprocreg_commit = master + +PACKAGES += oauth2 +pkg_oauth2_name = oauth2 +pkg_oauth2_description = Erlang Oauth2 implementation +pkg_oauth2_homepage = https://github.com/kivra/oauth2 +pkg_oauth2_fetch = git +pkg_oauth2_repo = https://github.com/kivra/oauth2 +pkg_oauth2_commit = master + +PACKAGES += oauth +pkg_oauth_name = oauth +pkg_oauth_description = An Erlang OAuth 1.0 implementation +pkg_oauth_homepage = https://github.com/tim/erlang-oauth +pkg_oauth_fetch = git +pkg_oauth_repo = https://github.com/tim/erlang-oauth +pkg_oauth_commit = master + +PACKAGES += octopus +pkg_octopus_name = octopus +pkg_octopus_description = Small and flexible pool manager written in Erlang +pkg_octopus_homepage = https://github.com/erlangbureau/octopus +pkg_octopus_fetch = git +pkg_octopus_repo = https://github.com/erlangbureau/octopus +pkg_octopus_commit = 1.0.0 + +PACKAGES += of_protocol +pkg_of_protocol_name = of_protocol +pkg_of_protocol_description = OpenFlow Protocol Library for Erlang +pkg_of_protocol_homepage = https://github.com/FlowForwarding/of_protocol +pkg_of_protocol_fetch = git +pkg_of_protocol_repo = https://github.com/FlowForwarding/of_protocol +pkg_of_protocol_commit = master + +PACKAGES += opencouch +pkg_opencouch_name = couch +pkg_opencouch_description = A embeddable document oriented database compatible with Apache CouchDB +pkg_opencouch_homepage = https://github.com/benoitc/opencouch +pkg_opencouch_fetch = git +pkg_opencouch_repo = https://github.com/benoitc/opencouch +pkg_opencouch_commit = master + +PACKAGES += openflow +pkg_openflow_name = openflow +pkg_openflow_description = An OpenFlow controller written in pure erlang +pkg_openflow_homepage = https://github.com/renatoaguiar/erlang-openflow +pkg_openflow_fetch = git +pkg_openflow_repo = https://github.com/renatoaguiar/erlang-openflow +pkg_openflow_commit = master + +PACKAGES += openid +pkg_openid_name = openid +pkg_openid_description = Erlang OpenID +pkg_openid_homepage = https://github.com/brendonh/erl_openid +pkg_openid_fetch = git +pkg_openid_repo = https://github.com/brendonh/erl_openid +pkg_openid_commit = master + +PACKAGES += openpoker +pkg_openpoker_name = openpoker +pkg_openpoker_description = Genesis Texas hold'em Game Server +pkg_openpoker_homepage = https://github.com/hpyhacking/openpoker +pkg_openpoker_fetch = git +pkg_openpoker_repo = https://github.com/hpyhacking/openpoker +pkg_openpoker_commit = master + +PACKAGES += pal +pkg_pal_name = pal +pkg_pal_description = Pragmatic Authentication Library +pkg_pal_homepage = https://github.com/manifest/pal +pkg_pal_fetch = git +pkg_pal_repo = https://github.com/manifest/pal +pkg_pal_commit = master + +PACKAGES += parse_trans +pkg_parse_trans_name = parse_trans +pkg_parse_trans_description = Parse transform utilities for Erlang +pkg_parse_trans_homepage = https://github.com/uwiger/parse_trans +pkg_parse_trans_fetch = git +pkg_parse_trans_repo = https://github.com/uwiger/parse_trans +pkg_parse_trans_commit = master + +PACKAGES += parsexml +pkg_parsexml_name = parsexml +pkg_parsexml_description = Simple DOM XML parser with convenient and very simple API +pkg_parsexml_homepage = https://github.com/maxlapshin/parsexml +pkg_parsexml_fetch = git +pkg_parsexml_repo = https://github.com/maxlapshin/parsexml +pkg_parsexml_commit = master + +PACKAGES += pegjs +pkg_pegjs_name = pegjs +pkg_pegjs_description = An implementation of PEG.js grammar for Erlang. +pkg_pegjs_homepage = https://github.com/dmitriid/pegjs +pkg_pegjs_fetch = git +pkg_pegjs_repo = https://github.com/dmitriid/pegjs +pkg_pegjs_commit = 0.3 + +PACKAGES += percept2 +pkg_percept2_name = percept2 +pkg_percept2_description = Concurrent profiling tool for Erlang +pkg_percept2_homepage = https://github.com/huiqing/percept2 +pkg_percept2_fetch = git +pkg_percept2_repo = https://github.com/huiqing/percept2 +pkg_percept2_commit = master + +PACKAGES += pgsql +pkg_pgsql_name = pgsql +pkg_pgsql_description = Erlang PostgreSQL driver +pkg_pgsql_homepage = https://github.com/semiocast/pgsql +pkg_pgsql_fetch = git +pkg_pgsql_repo = https://github.com/semiocast/pgsql +pkg_pgsql_commit = master + +PACKAGES += pkgx +pkg_pkgx_name = pkgx +pkg_pkgx_description = Build .deb packages from Erlang releases +pkg_pkgx_homepage = https://github.com/arjan/pkgx +pkg_pkgx_fetch = git +pkg_pkgx_repo = https://github.com/arjan/pkgx +pkg_pkgx_commit = master + +PACKAGES += pkt +pkg_pkt_name = pkt +pkg_pkt_description = Erlang network protocol library +pkg_pkt_homepage = https://github.com/msantos/pkt +pkg_pkt_fetch = git +pkg_pkt_repo = https://github.com/msantos/pkt +pkg_pkt_commit = master + +PACKAGES += plain_fsm +pkg_plain_fsm_name = plain_fsm +pkg_plain_fsm_description = A behaviour/support library for writing plain Erlang FSMs. +pkg_plain_fsm_homepage = https://github.com/uwiger/plain_fsm +pkg_plain_fsm_fetch = git +pkg_plain_fsm_repo = https://github.com/uwiger/plain_fsm +pkg_plain_fsm_commit = master + +PACKAGES += plumtree +pkg_plumtree_name = plumtree +pkg_plumtree_description = Epidemic Broadcast Trees +pkg_plumtree_homepage = https://github.com/helium/plumtree +pkg_plumtree_fetch = git +pkg_plumtree_repo = https://github.com/helium/plumtree +pkg_plumtree_commit = master + +PACKAGES += pmod_transform +pkg_pmod_transform_name = pmod_transform +pkg_pmod_transform_description = Parse transform for parameterized modules +pkg_pmod_transform_homepage = https://github.com/erlang/pmod_transform +pkg_pmod_transform_fetch = git +pkg_pmod_transform_repo = https://github.com/erlang/pmod_transform +pkg_pmod_transform_commit = master + +PACKAGES += pobox +pkg_pobox_name = pobox +pkg_pobox_description = External buffer processes to protect against mailbox overflow in Erlang +pkg_pobox_homepage = https://github.com/ferd/pobox +pkg_pobox_fetch = git +pkg_pobox_repo = https://github.com/ferd/pobox +pkg_pobox_commit = master + +PACKAGES += ponos +pkg_ponos_name = ponos +pkg_ponos_description = ponos is a simple yet powerful load generator written in erlang +pkg_ponos_homepage = https://github.com/klarna/ponos +pkg_ponos_fetch = git +pkg_ponos_repo = https://github.com/klarna/ponos +pkg_ponos_commit = master + +PACKAGES += poolboy +pkg_poolboy_name = poolboy +pkg_poolboy_description = A hunky Erlang worker pool factory +pkg_poolboy_homepage = https://github.com/devinus/poolboy +pkg_poolboy_fetch = git +pkg_poolboy_repo = https://github.com/devinus/poolboy +pkg_poolboy_commit = master + +PACKAGES += pooler +pkg_pooler_name = pooler +pkg_pooler_description = An OTP Process Pool Application +pkg_pooler_homepage = https://github.com/seth/pooler +pkg_pooler_fetch = git +pkg_pooler_repo = https://github.com/seth/pooler +pkg_pooler_commit = master + +PACKAGES += pqueue +pkg_pqueue_name = pqueue +pkg_pqueue_description = Erlang Priority Queues +pkg_pqueue_homepage = https://github.com/okeuday/pqueue +pkg_pqueue_fetch = git +pkg_pqueue_repo = https://github.com/okeuday/pqueue +pkg_pqueue_commit = master + +PACKAGES += procket +pkg_procket_name = procket +pkg_procket_description = Erlang interface to low level socket operations +pkg_procket_homepage = http://blog.listincomprehension.com/search/label/procket +pkg_procket_fetch = git +pkg_procket_repo = https://github.com/msantos/procket +pkg_procket_commit = master + +PACKAGES += proper +pkg_proper_name = proper +pkg_proper_description = PropEr: a QuickCheck-inspired property-based testing tool for Erlang. +pkg_proper_homepage = http://proper.softlab.ntua.gr +pkg_proper_fetch = git +pkg_proper_repo = https://github.com/manopapad/proper +pkg_proper_commit = master + +PACKAGES += prop +pkg_prop_name = prop +pkg_prop_description = An Erlang code scaffolding and generator system. +pkg_prop_homepage = https://github.com/nuex/prop +pkg_prop_fetch = git +pkg_prop_repo = https://github.com/nuex/prop +pkg_prop_commit = master + +PACKAGES += props +pkg_props_name = props +pkg_props_description = Property structure library +pkg_props_homepage = https://github.com/greyarea/props +pkg_props_fetch = git +pkg_props_repo = https://github.com/greyarea/props +pkg_props_commit = master + +PACKAGES += protobuffs +pkg_protobuffs_name = protobuffs +pkg_protobuffs_description = An implementation of Google's Protocol Buffers for Erlang, based on ngerakines/erlang_protobuffs. +pkg_protobuffs_homepage = https://github.com/basho/erlang_protobuffs +pkg_protobuffs_fetch = git +pkg_protobuffs_repo = https://github.com/basho/erlang_protobuffs +pkg_protobuffs_commit = master + +PACKAGES += psycho +pkg_psycho_name = psycho +pkg_psycho_description = HTTP server that provides a WSGI-like interface for applications and middleware. +pkg_psycho_homepage = https://github.com/gar1t/psycho +pkg_psycho_fetch = git +pkg_psycho_repo = https://github.com/gar1t/psycho +pkg_psycho_commit = master + +PACKAGES += purity +pkg_purity_name = purity +pkg_purity_description = A side-effect analyzer for Erlang +pkg_purity_homepage = https://github.com/mpitid/purity +pkg_purity_fetch = git +pkg_purity_repo = https://github.com/mpitid/purity +pkg_purity_commit = master + +PACKAGES += push_service +pkg_push_service_name = push_service +pkg_push_service_description = Push service +pkg_push_service_homepage = https://github.com/hairyhum/push_service +pkg_push_service_fetch = git +pkg_push_service_repo = https://github.com/hairyhum/push_service +pkg_push_service_commit = master + +PACKAGES += qdate +pkg_qdate_name = qdate +pkg_qdate_description = Date, time, and timezone parsing, formatting, and conversion for Erlang. +pkg_qdate_homepage = https://github.com/choptastic/qdate +pkg_qdate_fetch = git +pkg_qdate_repo = https://github.com/choptastic/qdate +pkg_qdate_commit = 0.4.0 + +PACKAGES += qrcode +pkg_qrcode_name = qrcode +pkg_qrcode_description = QR Code encoder in Erlang +pkg_qrcode_homepage = https://github.com/komone/qrcode +pkg_qrcode_fetch = git +pkg_qrcode_repo = https://github.com/komone/qrcode +pkg_qrcode_commit = master + +PACKAGES += quest +pkg_quest_name = quest +pkg_quest_description = Learn Erlang through this set of challenges. An interactive system for getting to know Erlang. +pkg_quest_homepage = https://github.com/eriksoe/ErlangQuest +pkg_quest_fetch = git +pkg_quest_repo = https://github.com/eriksoe/ErlangQuest +pkg_quest_commit = master + +PACKAGES += quickrand +pkg_quickrand_name = quickrand +pkg_quickrand_description = Quick Erlang Random Number Generation +pkg_quickrand_homepage = https://github.com/okeuday/quickrand +pkg_quickrand_fetch = git +pkg_quickrand_repo = https://github.com/okeuday/quickrand +pkg_quickrand_commit = master + +PACKAGES += rabbit_exchange_type_riak +pkg_rabbit_exchange_type_riak_name = rabbit_exchange_type_riak +pkg_rabbit_exchange_type_riak_description = Custom RabbitMQ exchange type for sticking messages in Riak +pkg_rabbit_exchange_type_riak_homepage = https://github.com/jbrisbin/riak-exchange +pkg_rabbit_exchange_type_riak_fetch = git +pkg_rabbit_exchange_type_riak_repo = https://github.com/jbrisbin/riak-exchange +pkg_rabbit_exchange_type_riak_commit = master + +PACKAGES += rabbit +pkg_rabbit_name = rabbit +pkg_rabbit_description = RabbitMQ Server +pkg_rabbit_homepage = https://www.rabbitmq.com/ +pkg_rabbit_fetch = git +pkg_rabbit_repo = https://github.com/rabbitmq/rabbitmq-server.git +pkg_rabbit_commit = master + +PACKAGES += rack +pkg_rack_name = rack +pkg_rack_description = Rack handler for erlang +pkg_rack_homepage = https://github.com/erlyvideo/rack +pkg_rack_fetch = git +pkg_rack_repo = https://github.com/erlyvideo/rack +pkg_rack_commit = master + +PACKAGES += radierl +pkg_radierl_name = radierl +pkg_radierl_description = RADIUS protocol stack implemented in Erlang. +pkg_radierl_homepage = https://github.com/vances/radierl +pkg_radierl_fetch = git +pkg_radierl_repo = https://github.com/vances/radierl +pkg_radierl_commit = master + +PACKAGES += rafter +pkg_rafter_name = rafter +pkg_rafter_description = An Erlang library application which implements the Raft consensus protocol +pkg_rafter_homepage = https://github.com/andrewjstone/rafter +pkg_rafter_fetch = git +pkg_rafter_repo = https://github.com/andrewjstone/rafter +pkg_rafter_commit = master + +PACKAGES += ranch +pkg_ranch_name = ranch +pkg_ranch_description = Socket acceptor pool for TCP protocols. +pkg_ranch_homepage = http://ninenines.eu +pkg_ranch_fetch = git +pkg_ranch_repo = https://github.com/ninenines/ranch +pkg_ranch_commit = 1.1.0 + +PACKAGES += rbeacon +pkg_rbeacon_name = rbeacon +pkg_rbeacon_description = LAN discovery and presence in Erlang. +pkg_rbeacon_homepage = https://github.com/refuge/rbeacon +pkg_rbeacon_fetch = git +pkg_rbeacon_repo = https://github.com/refuge/rbeacon +pkg_rbeacon_commit = master + +PACKAGES += rebar +pkg_rebar_name = rebar +pkg_rebar_description = Erlang build tool that makes it easy to compile and test Erlang applications, port drivers and releases. +pkg_rebar_homepage = http://www.rebar3.org +pkg_rebar_fetch = git +pkg_rebar_repo = https://github.com/rebar/rebar3 +pkg_rebar_commit = master + +PACKAGES += rebus +pkg_rebus_name = rebus +pkg_rebus_description = A stupid simple, internal, pub/sub event bus written in- and for Erlang. +pkg_rebus_homepage = https://github.com/olle/rebus +pkg_rebus_fetch = git +pkg_rebus_repo = https://github.com/olle/rebus +pkg_rebus_commit = master + +PACKAGES += rec2json +pkg_rec2json_name = rec2json +pkg_rec2json_description = Compile erlang record definitions into modules to convert them to/from json easily. +pkg_rec2json_homepage = https://github.com/lordnull/rec2json +pkg_rec2json_fetch = git +pkg_rec2json_repo = https://github.com/lordnull/rec2json +pkg_rec2json_commit = master + +PACKAGES += recon +pkg_recon_name = recon +pkg_recon_description = Collection of functions and scripts to debug Erlang in production. +pkg_recon_homepage = https://github.com/ferd/recon +pkg_recon_fetch = git +pkg_recon_repo = https://github.com/ferd/recon +pkg_recon_commit = 2.2.1 + +PACKAGES += record_info +pkg_record_info_name = record_info +pkg_record_info_description = Convert between record and proplist +pkg_record_info_homepage = https://github.com/bipthelin/erlang-record_info +pkg_record_info_fetch = git +pkg_record_info_repo = https://github.com/bipthelin/erlang-record_info +pkg_record_info_commit = master + +PACKAGES += redgrid +pkg_redgrid_name = redgrid +pkg_redgrid_description = automatic Erlang node discovery via redis +pkg_redgrid_homepage = https://github.com/jkvor/redgrid +pkg_redgrid_fetch = git +pkg_redgrid_repo = https://github.com/jkvor/redgrid +pkg_redgrid_commit = master + +PACKAGES += redo +pkg_redo_name = redo +pkg_redo_description = pipelined erlang redis client +pkg_redo_homepage = https://github.com/jkvor/redo +pkg_redo_fetch = git +pkg_redo_repo = https://github.com/jkvor/redo +pkg_redo_commit = master + +PACKAGES += reload_mk +pkg_reload_mk_name = reload_mk +pkg_reload_mk_description = Live reload plugin for erlang.mk. +pkg_reload_mk_homepage = https://github.com/bullno1/reload.mk +pkg_reload_mk_fetch = git +pkg_reload_mk_repo = https://github.com/bullno1/reload.mk +pkg_reload_mk_commit = master + +PACKAGES += reltool_util +pkg_reltool_util_name = reltool_util +pkg_reltool_util_description = Erlang reltool utility functionality application +pkg_reltool_util_homepage = https://github.com/okeuday/reltool_util +pkg_reltool_util_fetch = git +pkg_reltool_util_repo = https://github.com/okeuday/reltool_util +pkg_reltool_util_commit = master + +PACKAGES += relx +pkg_relx_name = relx +pkg_relx_description = Sane, simple release creation for Erlang +pkg_relx_homepage = https://github.com/erlware/relx +pkg_relx_fetch = git +pkg_relx_repo = https://github.com/erlware/relx +pkg_relx_commit = master + +PACKAGES += resource_discovery +pkg_resource_discovery_name = resource_discovery +pkg_resource_discovery_description = An application used to dynamically discover resources present in an Erlang node cluster. +pkg_resource_discovery_homepage = http://erlware.org/ +pkg_resource_discovery_fetch = git +pkg_resource_discovery_repo = https://github.com/erlware/resource_discovery +pkg_resource_discovery_commit = master + +PACKAGES += restc +pkg_restc_name = restc +pkg_restc_description = Erlang Rest Client +pkg_restc_homepage = https://github.com/kivra/restclient +pkg_restc_fetch = git +pkg_restc_repo = https://github.com/kivra/restclient +pkg_restc_commit = master + +PACKAGES += rfc4627_jsonrpc +pkg_rfc4627_jsonrpc_name = rfc4627_jsonrpc +pkg_rfc4627_jsonrpc_description = Erlang RFC4627 (JSON) codec and JSON-RPC server implementation. +pkg_rfc4627_jsonrpc_homepage = https://github.com/tonyg/erlang-rfc4627 +pkg_rfc4627_jsonrpc_fetch = git +pkg_rfc4627_jsonrpc_repo = https://github.com/tonyg/erlang-rfc4627 +pkg_rfc4627_jsonrpc_commit = master + +PACKAGES += riakc +pkg_riakc_name = riakc +pkg_riakc_description = Erlang clients for Riak. +pkg_riakc_homepage = https://github.com/basho/riak-erlang-client +pkg_riakc_fetch = git +pkg_riakc_repo = https://github.com/basho/riak-erlang-client +pkg_riakc_commit = master + +PACKAGES += riak_control +pkg_riak_control_name = riak_control +pkg_riak_control_description = Webmachine-based administration interface for Riak. +pkg_riak_control_homepage = https://github.com/basho/riak_control +pkg_riak_control_fetch = git +pkg_riak_control_repo = https://github.com/basho/riak_control +pkg_riak_control_commit = master + +PACKAGES += riak_core +pkg_riak_core_name = riak_core +pkg_riak_core_description = Distributed systems infrastructure used by Riak. +pkg_riak_core_homepage = https://github.com/basho/riak_core +pkg_riak_core_fetch = git +pkg_riak_core_repo = https://github.com/basho/riak_core +pkg_riak_core_commit = master + +PACKAGES += riak_dt +pkg_riak_dt_name = riak_dt +pkg_riak_dt_description = Convergent replicated datatypes in Erlang +pkg_riak_dt_homepage = https://github.com/basho/riak_dt +pkg_riak_dt_fetch = git +pkg_riak_dt_repo = https://github.com/basho/riak_dt +pkg_riak_dt_commit = master + +PACKAGES += riak_ensemble +pkg_riak_ensemble_name = riak_ensemble +pkg_riak_ensemble_description = Multi-Paxos framework in Erlang +pkg_riak_ensemble_homepage = https://github.com/basho/riak_ensemble +pkg_riak_ensemble_fetch = git +pkg_riak_ensemble_repo = https://github.com/basho/riak_ensemble +pkg_riak_ensemble_commit = master + +PACKAGES += riakhttpc +pkg_riakhttpc_name = riakhttpc +pkg_riakhttpc_description = Riak Erlang client using the HTTP interface +pkg_riakhttpc_homepage = https://github.com/basho/riak-erlang-http-client +pkg_riakhttpc_fetch = git +pkg_riakhttpc_repo = https://github.com/basho/riak-erlang-http-client +pkg_riakhttpc_commit = master + +PACKAGES += riak_kv +pkg_riak_kv_name = riak_kv +pkg_riak_kv_description = Riak Key/Value Store +pkg_riak_kv_homepage = https://github.com/basho/riak_kv +pkg_riak_kv_fetch = git +pkg_riak_kv_repo = https://github.com/basho/riak_kv +pkg_riak_kv_commit = master + +PACKAGES += riaknostic +pkg_riaknostic_name = riaknostic +pkg_riaknostic_description = A diagnostic tool for Riak installations, to find common errors asap +pkg_riaknostic_homepage = https://github.com/basho/riaknostic +pkg_riaknostic_fetch = git +pkg_riaknostic_repo = https://github.com/basho/riaknostic +pkg_riaknostic_commit = master + +PACKAGES += riak_pg +pkg_riak_pg_name = riak_pg +pkg_riak_pg_description = Distributed process groups with riak_core. +pkg_riak_pg_homepage = https://github.com/cmeiklejohn/riak_pg +pkg_riak_pg_fetch = git +pkg_riak_pg_repo = https://github.com/cmeiklejohn/riak_pg +pkg_riak_pg_commit = master + +PACKAGES += riak_pipe +pkg_riak_pipe_name = riak_pipe +pkg_riak_pipe_description = Riak Pipelines +pkg_riak_pipe_homepage = https://github.com/basho/riak_pipe +pkg_riak_pipe_fetch = git +pkg_riak_pipe_repo = https://github.com/basho/riak_pipe +pkg_riak_pipe_commit = master + +PACKAGES += riakpool +pkg_riakpool_name = riakpool +pkg_riakpool_description = erlang riak client pool +pkg_riakpool_homepage = https://github.com/dweldon/riakpool +pkg_riakpool_fetch = git +pkg_riakpool_repo = https://github.com/dweldon/riakpool +pkg_riakpool_commit = master + +PACKAGES += riak_sysmon +pkg_riak_sysmon_name = riak_sysmon +pkg_riak_sysmon_description = Simple OTP app for managing Erlang VM system_monitor event messages +pkg_riak_sysmon_homepage = https://github.com/basho/riak_sysmon +pkg_riak_sysmon_fetch = git +pkg_riak_sysmon_repo = https://github.com/basho/riak_sysmon +pkg_riak_sysmon_commit = master + +PACKAGES += riak_test +pkg_riak_test_name = riak_test +pkg_riak_test_description = I'm in your cluster, testing your riaks +pkg_riak_test_homepage = https://github.com/basho/riak_test +pkg_riak_test_fetch = git +pkg_riak_test_repo = https://github.com/basho/riak_test +pkg_riak_test_commit = master + +PACKAGES += rivus_cep +pkg_rivus_cep_name = rivus_cep +pkg_rivus_cep_description = Complex event processing in Erlang +pkg_rivus_cep_homepage = https://github.com/vascokk/rivus_cep +pkg_rivus_cep_fetch = git +pkg_rivus_cep_repo = https://github.com/vascokk/rivus_cep +pkg_rivus_cep_commit = master + +PACKAGES += rlimit +pkg_rlimit_name = rlimit +pkg_rlimit_description = Magnus Klaar's rate limiter code from etorrent +pkg_rlimit_homepage = https://github.com/jlouis/rlimit +pkg_rlimit_fetch = git +pkg_rlimit_repo = https://github.com/jlouis/rlimit +pkg_rlimit_commit = master + +PACKAGES += safetyvalve +pkg_safetyvalve_name = safetyvalve +pkg_safetyvalve_description = A safety valve for your erlang node +pkg_safetyvalve_homepage = https://github.com/jlouis/safetyvalve +pkg_safetyvalve_fetch = git +pkg_safetyvalve_repo = https://github.com/jlouis/safetyvalve +pkg_safetyvalve_commit = master + +PACKAGES += seestar +pkg_seestar_name = seestar +pkg_seestar_description = The Erlang client for Cassandra 1.2+ binary protocol +pkg_seestar_homepage = https://github.com/iamaleksey/seestar +pkg_seestar_fetch = git +pkg_seestar_repo = https://github.com/iamaleksey/seestar +pkg_seestar_commit = master + +PACKAGES += service +pkg_service_name = service +pkg_service_description = A minimal Erlang behavior for creating CloudI internal services +pkg_service_homepage = http://cloudi.org/ +pkg_service_fetch = git +pkg_service_repo = https://github.com/CloudI/service +pkg_service_commit = master + +PACKAGES += setup +pkg_setup_name = setup +pkg_setup_description = Generic setup utility for Erlang-based systems +pkg_setup_homepage = https://github.com/uwiger/setup +pkg_setup_fetch = git +pkg_setup_repo = https://github.com/uwiger/setup +pkg_setup_commit = master + +PACKAGES += sext +pkg_sext_name = sext +pkg_sext_description = Sortable Erlang Term Serialization +pkg_sext_homepage = https://github.com/uwiger/sext +pkg_sext_fetch = git +pkg_sext_repo = https://github.com/uwiger/sext +pkg_sext_commit = master + +PACKAGES += sfmt +pkg_sfmt_name = sfmt +pkg_sfmt_description = SFMT pseudo random number generator for Erlang. +pkg_sfmt_homepage = https://github.com/jj1bdx/sfmt-erlang +pkg_sfmt_fetch = git +pkg_sfmt_repo = https://github.com/jj1bdx/sfmt-erlang +pkg_sfmt_commit = master + +PACKAGES += sgte +pkg_sgte_name = sgte +pkg_sgte_description = A simple Erlang Template Engine +pkg_sgte_homepage = https://github.com/filippo/sgte +pkg_sgte_fetch = git +pkg_sgte_repo = https://github.com/filippo/sgte +pkg_sgte_commit = master + +PACKAGES += sheriff +pkg_sheriff_name = sheriff +pkg_sheriff_description = Parse transform for type based validation. +pkg_sheriff_homepage = http://ninenines.eu +pkg_sheriff_fetch = git +pkg_sheriff_repo = https://github.com/extend/sheriff +pkg_sheriff_commit = master + +PACKAGES += shotgun +pkg_shotgun_name = shotgun +pkg_shotgun_description = better than just a gun +pkg_shotgun_homepage = https://github.com/inaka/shotgun +pkg_shotgun_fetch = git +pkg_shotgun_repo = https://github.com/inaka/shotgun +pkg_shotgun_commit = 0.1.0 + +PACKAGES += sidejob +pkg_sidejob_name = sidejob +pkg_sidejob_description = Parallel worker and capacity limiting library for Erlang +pkg_sidejob_homepage = https://github.com/basho/sidejob +pkg_sidejob_fetch = git +pkg_sidejob_repo = https://github.com/basho/sidejob +pkg_sidejob_commit = master + +PACKAGES += sieve +pkg_sieve_name = sieve +pkg_sieve_description = sieve is a simple TCP routing proxy (layer 7) in erlang +pkg_sieve_homepage = https://github.com/benoitc/sieve +pkg_sieve_fetch = git +pkg_sieve_repo = https://github.com/benoitc/sieve +pkg_sieve_commit = master + +PACKAGES += sighandler +pkg_sighandler_name = sighandler +pkg_sighandler_description = Handle UNIX signals in Er lang +pkg_sighandler_homepage = https://github.com/jkingsbery/sighandler +pkg_sighandler_fetch = git +pkg_sighandler_repo = https://github.com/jkingsbery/sighandler +pkg_sighandler_commit = master + +PACKAGES += simhash +pkg_simhash_name = simhash +pkg_simhash_description = Simhashing for Erlang -- hashing algorithm to find near-duplicates in binary data. +pkg_simhash_homepage = https://github.com/ferd/simhash +pkg_simhash_fetch = git +pkg_simhash_repo = https://github.com/ferd/simhash +pkg_simhash_commit = master + +PACKAGES += simple_bridge +pkg_simple_bridge_name = simple_bridge +pkg_simple_bridge_description = A simple, standardized interface library to Erlang HTTP Servers. +pkg_simple_bridge_homepage = https://github.com/nitrogen/simple_bridge +pkg_simple_bridge_fetch = git +pkg_simple_bridge_repo = https://github.com/nitrogen/simple_bridge +pkg_simple_bridge_commit = master + +PACKAGES += simple_oauth2 +pkg_simple_oauth2_name = simple_oauth2 +pkg_simple_oauth2_description = Simple erlang OAuth2 client module for any http server framework (Google, Facebook, Yandex, Vkontakte are preconfigured) +pkg_simple_oauth2_homepage = https://github.com/virtan/simple_oauth2 +pkg_simple_oauth2_fetch = git +pkg_simple_oauth2_repo = https://github.com/virtan/simple_oauth2 +pkg_simple_oauth2_commit = master + +PACKAGES += skel +pkg_skel_name = skel +pkg_skel_description = A Streaming Process-based Skeleton Library for Erlang +pkg_skel_homepage = https://github.com/ParaPhrase/skel +pkg_skel_fetch = git +pkg_skel_repo = https://github.com/ParaPhrase/skel +pkg_skel_commit = master + +PACKAGES += smother +pkg_smother_name = smother +pkg_smother_description = Extended code coverage metrics for Erlang. +pkg_smother_homepage = https://ramsay-t.github.io/Smother/ +pkg_smother_fetch = git +pkg_smother_repo = https://github.com/ramsay-t/Smother +pkg_smother_commit = master + +PACKAGES += social +pkg_social_name = social +pkg_social_description = Cowboy handler for social login via OAuth2 providers +pkg_social_homepage = https://github.com/dvv/social +pkg_social_fetch = git +pkg_social_repo = https://github.com/dvv/social +pkg_social_commit = master + +PACKAGES += spapi_router +pkg_spapi_router_name = spapi_router +pkg_spapi_router_description = Partially-connected Erlang clustering +pkg_spapi_router_homepage = https://github.com/spilgames/spapi-router +pkg_spapi_router_fetch = git +pkg_spapi_router_repo = https://github.com/spilgames/spapi-router +pkg_spapi_router_commit = master + +PACKAGES += sqerl +pkg_sqerl_name = sqerl +pkg_sqerl_description = An Erlang-flavoured SQL DSL +pkg_sqerl_homepage = https://github.com/hairyhum/sqerl +pkg_sqerl_fetch = git +pkg_sqerl_repo = https://github.com/hairyhum/sqerl +pkg_sqerl_commit = master + +PACKAGES += srly +pkg_srly_name = srly +pkg_srly_description = Native Erlang Unix serial interface +pkg_srly_homepage = https://github.com/msantos/srly +pkg_srly_fetch = git +pkg_srly_repo = https://github.com/msantos/srly +pkg_srly_commit = master + +PACKAGES += sshrpc +pkg_sshrpc_name = sshrpc +pkg_sshrpc_description = Erlang SSH RPC module (experimental) +pkg_sshrpc_homepage = https://github.com/jj1bdx/sshrpc +pkg_sshrpc_fetch = git +pkg_sshrpc_repo = https://github.com/jj1bdx/sshrpc +pkg_sshrpc_commit = master + +PACKAGES += stable +pkg_stable_name = stable +pkg_stable_description = Library of assorted helpers for Cowboy web server. +pkg_stable_homepage = https://github.com/dvv/stable +pkg_stable_fetch = git +pkg_stable_repo = https://github.com/dvv/stable +pkg_stable_commit = master + +PACKAGES += statebox +pkg_statebox_name = statebox +pkg_statebox_description = Erlang state monad with merge/conflict-resolution capabilities. Useful for Riak. +pkg_statebox_homepage = https://github.com/mochi/statebox +pkg_statebox_fetch = git +pkg_statebox_repo = https://github.com/mochi/statebox +pkg_statebox_commit = master + +PACKAGES += statebox_riak +pkg_statebox_riak_name = statebox_riak +pkg_statebox_riak_description = Convenience library that makes it easier to use statebox with riak, extracted from best practices in our production code at Mochi Media. +pkg_statebox_riak_homepage = https://github.com/mochi/statebox_riak +pkg_statebox_riak_fetch = git +pkg_statebox_riak_repo = https://github.com/mochi/statebox_riak +pkg_statebox_riak_commit = master + +PACKAGES += statman +pkg_statman_name = statman +pkg_statman_description = Efficiently collect massive volumes of metrics inside the Erlang VM +pkg_statman_homepage = https://github.com/knutin/statman +pkg_statman_fetch = git +pkg_statman_repo = https://github.com/knutin/statman +pkg_statman_commit = master + +PACKAGES += statsderl +pkg_statsderl_name = statsderl +pkg_statsderl_description = StatsD client (erlang) +pkg_statsderl_homepage = https://github.com/lpgauth/statsderl +pkg_statsderl_fetch = git +pkg_statsderl_repo = https://github.com/lpgauth/statsderl +pkg_statsderl_commit = master + +PACKAGES += stdinout_pool +pkg_stdinout_pool_name = stdinout_pool +pkg_stdinout_pool_description = stdinout_pool : stuff goes in, stuff goes out. there's never any miscommunication. +pkg_stdinout_pool_homepage = https://github.com/mattsta/erlang-stdinout-pool +pkg_stdinout_pool_fetch = git +pkg_stdinout_pool_repo = https://github.com/mattsta/erlang-stdinout-pool +pkg_stdinout_pool_commit = master + +PACKAGES += stockdb +pkg_stockdb_name = stockdb +pkg_stockdb_description = Database for storing Stock Exchange quotes in erlang +pkg_stockdb_homepage = https://github.com/maxlapshin/stockdb +pkg_stockdb_fetch = git +pkg_stockdb_repo = https://github.com/maxlapshin/stockdb +pkg_stockdb_commit = master + +PACKAGES += stripe +pkg_stripe_name = stripe +pkg_stripe_description = Erlang interface to the stripe.com API +pkg_stripe_homepage = https://github.com/mattsta/stripe-erlang +pkg_stripe_fetch = git +pkg_stripe_repo = https://github.com/mattsta/stripe-erlang +pkg_stripe_commit = v1 + +PACKAGES += supervisor3 +pkg_supervisor3_name = supervisor3 +pkg_supervisor3_description = OTP supervisor with additional strategies +pkg_supervisor3_homepage = https://github.com/klarna/supervisor3 +pkg_supervisor3_fetch = git +pkg_supervisor3_repo = https://github.com/klarna/supervisor3.git +pkg_supervisor3_commit = master + +PACKAGES += surrogate +pkg_surrogate_name = surrogate +pkg_surrogate_description = Proxy server written in erlang. Supports reverse proxy load balancing and forward proxy with http (including CONNECT), socks4, socks5, and transparent proxy modes. +pkg_surrogate_homepage = https://github.com/skruger/Surrogate +pkg_surrogate_fetch = git +pkg_surrogate_repo = https://github.com/skruger/Surrogate +pkg_surrogate_commit = master + +PACKAGES += swab +pkg_swab_name = swab +pkg_swab_description = General purpose buffer handling module +pkg_swab_homepage = https://github.com/crownedgrouse/swab +pkg_swab_fetch = git +pkg_swab_repo = https://github.com/crownedgrouse/swab +pkg_swab_commit = master + +PACKAGES += swarm +pkg_swarm_name = swarm +pkg_swarm_description = Fast and simple acceptor pool for Erlang +pkg_swarm_homepage = https://github.com/jeremey/swarm +pkg_swarm_fetch = git +pkg_swarm_repo = https://github.com/jeremey/swarm +pkg_swarm_commit = master + +PACKAGES += switchboard +pkg_switchboard_name = switchboard +pkg_switchboard_description = A framework for processing email using worker plugins. +pkg_switchboard_homepage = https://github.com/thusfresh/switchboard +pkg_switchboard_fetch = git +pkg_switchboard_repo = https://github.com/thusfresh/switchboard +pkg_switchboard_commit = master + +PACKAGES += sync +pkg_sync_name = sync +pkg_sync_description = On-the-fly recompiling and reloading in Erlang. +pkg_sync_homepage = https://github.com/rustyio/sync +pkg_sync_fetch = git +pkg_sync_repo = https://github.com/rustyio/sync +pkg_sync_commit = master + +PACKAGES += syn +pkg_syn_name = syn +pkg_syn_description = A global process registry for Erlang. +pkg_syn_homepage = https://github.com/ostinelli/syn +pkg_syn_fetch = git +pkg_syn_repo = https://github.com/ostinelli/syn +pkg_syn_commit = master + +PACKAGES += syntaxerl +pkg_syntaxerl_name = syntaxerl +pkg_syntaxerl_description = Syntax checker for Erlang +pkg_syntaxerl_homepage = https://github.com/ten0s/syntaxerl +pkg_syntaxerl_fetch = git +pkg_syntaxerl_repo = https://github.com/ten0s/syntaxerl +pkg_syntaxerl_commit = master + +PACKAGES += syslog +pkg_syslog_name = syslog +pkg_syslog_description = Erlang port driver for interacting with syslog via syslog(3) +pkg_syslog_homepage = https://github.com/Vagabond/erlang-syslog +pkg_syslog_fetch = git +pkg_syslog_repo = https://github.com/Vagabond/erlang-syslog +pkg_syslog_commit = master + +PACKAGES += taskforce +pkg_taskforce_name = taskforce +pkg_taskforce_description = Erlang worker pools for controlled parallelisation of arbitrary tasks. +pkg_taskforce_homepage = https://github.com/g-andrade/taskforce +pkg_taskforce_fetch = git +pkg_taskforce_repo = https://github.com/g-andrade/taskforce +pkg_taskforce_commit = master + +PACKAGES += tddreloader +pkg_tddreloader_name = tddreloader +pkg_tddreloader_description = Shell utility for recompiling, reloading, and testing code as it changes +pkg_tddreloader_homepage = https://github.com/version2beta/tddreloader +pkg_tddreloader_fetch = git +pkg_tddreloader_repo = https://github.com/version2beta/tddreloader +pkg_tddreloader_commit = master + +PACKAGES += tempo +pkg_tempo_name = tempo +pkg_tempo_description = NIF-based date and time parsing and formatting for Erlang. +pkg_tempo_homepage = https://github.com/selectel/tempo +pkg_tempo_fetch = git +pkg_tempo_repo = https://github.com/selectel/tempo +pkg_tempo_commit = master + +PACKAGES += ticktick +pkg_ticktick_name = ticktick +pkg_ticktick_description = Ticktick is an id generator for message service. +pkg_ticktick_homepage = https://github.com/ericliang/ticktick +pkg_ticktick_fetch = git +pkg_ticktick_repo = https://github.com/ericliang/ticktick +pkg_ticktick_commit = master + +PACKAGES += tinymq +pkg_tinymq_name = tinymq +pkg_tinymq_description = TinyMQ - a diminutive, in-memory message queue +pkg_tinymq_homepage = https://github.com/ChicagoBoss/tinymq +pkg_tinymq_fetch = git +pkg_tinymq_repo = https://github.com/ChicagoBoss/tinymq +pkg_tinymq_commit = master + +PACKAGES += tinymt +pkg_tinymt_name = tinymt +pkg_tinymt_description = TinyMT pseudo random number generator for Erlang. +pkg_tinymt_homepage = https://github.com/jj1bdx/tinymt-erlang +pkg_tinymt_fetch = git +pkg_tinymt_repo = https://github.com/jj1bdx/tinymt-erlang +pkg_tinymt_commit = master + +PACKAGES += tirerl +pkg_tirerl_name = tirerl +pkg_tirerl_description = Erlang interface to Elastic Search +pkg_tirerl_homepage = https://github.com/inaka/tirerl +pkg_tirerl_fetch = git +pkg_tirerl_repo = https://github.com/inaka/tirerl +pkg_tirerl_commit = master + +PACKAGES += traffic_tools +pkg_traffic_tools_name = traffic_tools +pkg_traffic_tools_description = Simple traffic limiting library +pkg_traffic_tools_homepage = https://github.com/systra/traffic_tools +pkg_traffic_tools_fetch = git +pkg_traffic_tools_repo = https://github.com/systra/traffic_tools +pkg_traffic_tools_commit = master + +PACKAGES += trails +pkg_trails_name = trails +pkg_trails_description = A couple of improvements over Cowboy Routes +pkg_trails_homepage = http://inaka.github.io/cowboy-trails/ +pkg_trails_fetch = git +pkg_trails_repo = https://github.com/inaka/cowboy-trails +pkg_trails_commit = master + +PACKAGES += trane +pkg_trane_name = trane +pkg_trane_description = SAX style broken HTML parser in Erlang +pkg_trane_homepage = https://github.com/massemanet/trane +pkg_trane_fetch = git +pkg_trane_repo = https://github.com/massemanet/trane +pkg_trane_commit = master + +PACKAGES += transit +pkg_transit_name = transit +pkg_transit_description = transit format for erlang +pkg_transit_homepage = https://github.com/isaiah/transit-erlang +pkg_transit_fetch = git +pkg_transit_repo = https://github.com/isaiah/transit-erlang +pkg_transit_commit = master + +PACKAGES += trie +pkg_trie_name = trie +pkg_trie_description = Erlang Trie Implementation +pkg_trie_homepage = https://github.com/okeuday/trie +pkg_trie_fetch = git +pkg_trie_repo = https://github.com/okeuday/trie +pkg_trie_commit = master + +PACKAGES += triq +pkg_triq_name = triq +pkg_triq_description = Trifork QuickCheck +pkg_triq_homepage = https://github.com/krestenkrab/triq +pkg_triq_fetch = git +pkg_triq_repo = https://github.com/krestenkrab/triq +pkg_triq_commit = master + +PACKAGES += tunctl +pkg_tunctl_name = tunctl +pkg_tunctl_description = Erlang TUN/TAP interface +pkg_tunctl_homepage = https://github.com/msantos/tunctl +pkg_tunctl_fetch = git +pkg_tunctl_repo = https://github.com/msantos/tunctl +pkg_tunctl_commit = master + +PACKAGES += twerl +pkg_twerl_name = twerl +pkg_twerl_description = Erlang client for the Twitter Streaming API +pkg_twerl_homepage = https://github.com/lucaspiller/twerl +pkg_twerl_fetch = git +pkg_twerl_repo = https://github.com/lucaspiller/twerl +pkg_twerl_commit = oauth + +PACKAGES += twitter_erlang +pkg_twitter_erlang_name = twitter_erlang +pkg_twitter_erlang_description = An Erlang twitter client +pkg_twitter_erlang_homepage = https://github.com/ngerakines/erlang_twitter +pkg_twitter_erlang_fetch = git +pkg_twitter_erlang_repo = https://github.com/ngerakines/erlang_twitter +pkg_twitter_erlang_commit = master + +PACKAGES += ucol_nif +pkg_ucol_nif_name = ucol_nif +pkg_ucol_nif_description = ICU based collation Erlang module +pkg_ucol_nif_homepage = https://github.com/refuge/ucol_nif +pkg_ucol_nif_fetch = git +pkg_ucol_nif_repo = https://github.com/refuge/ucol_nif +pkg_ucol_nif_commit = master + +PACKAGES += unicorn +pkg_unicorn_name = unicorn +pkg_unicorn_description = Generic configuration server +pkg_unicorn_homepage = https://github.com/shizzard/unicorn +pkg_unicorn_fetch = git +pkg_unicorn_repo = https://github.com/shizzard/unicorn +pkg_unicorn_commit = 0.3.0 + +PACKAGES += unsplit +pkg_unsplit_name = unsplit +pkg_unsplit_description = Resolves conflicts in Mnesia after network splits +pkg_unsplit_homepage = https://github.com/uwiger/unsplit +pkg_unsplit_fetch = git +pkg_unsplit_repo = https://github.com/uwiger/unsplit +pkg_unsplit_commit = master + +PACKAGES += uuid +pkg_uuid_name = uuid +pkg_uuid_description = Erlang UUID Implementation +pkg_uuid_homepage = https://github.com/okeuday/uuid +pkg_uuid_fetch = git +pkg_uuid_repo = https://github.com/okeuday/uuid +pkg_uuid_commit = v1.4.0 + +PACKAGES += ux +pkg_ux_name = ux +pkg_ux_description = Unicode eXtention for Erlang (Strings, Collation) +pkg_ux_homepage = https://github.com/erlang-unicode/ux +pkg_ux_fetch = git +pkg_ux_repo = https://github.com/erlang-unicode/ux +pkg_ux_commit = master + +PACKAGES += vert +pkg_vert_name = vert +pkg_vert_description = Erlang binding to libvirt virtualization API +pkg_vert_homepage = https://github.com/msantos/erlang-libvirt +pkg_vert_fetch = git +pkg_vert_repo = https://github.com/msantos/erlang-libvirt +pkg_vert_commit = master + +PACKAGES += verx +pkg_verx_name = verx +pkg_verx_description = Erlang implementation of the libvirtd remote protocol +pkg_verx_homepage = https://github.com/msantos/verx +pkg_verx_fetch = git +pkg_verx_repo = https://github.com/msantos/verx +pkg_verx_commit = master + +PACKAGES += vmq_acl +pkg_vmq_acl_name = vmq_acl +pkg_vmq_acl_description = Component of VerneMQ: A distributed MQTT message broker +pkg_vmq_acl_homepage = https://verne.mq/ +pkg_vmq_acl_fetch = git +pkg_vmq_acl_repo = https://github.com/erlio/vmq_acl +pkg_vmq_acl_commit = master + +PACKAGES += vmq_bridge +pkg_vmq_bridge_name = vmq_bridge +pkg_vmq_bridge_description = Component of VerneMQ: A distributed MQTT message broker +pkg_vmq_bridge_homepage = https://verne.mq/ +pkg_vmq_bridge_fetch = git +pkg_vmq_bridge_repo = https://github.com/erlio/vmq_bridge +pkg_vmq_bridge_commit = master + +PACKAGES += vmq_graphite +pkg_vmq_graphite_name = vmq_graphite +pkg_vmq_graphite_description = Component of VerneMQ: A distributed MQTT message broker +pkg_vmq_graphite_homepage = https://verne.mq/ +pkg_vmq_graphite_fetch = git +pkg_vmq_graphite_repo = https://github.com/erlio/vmq_graphite +pkg_vmq_graphite_commit = master + +PACKAGES += vmq_passwd +pkg_vmq_passwd_name = vmq_passwd +pkg_vmq_passwd_description = Component of VerneMQ: A distributed MQTT message broker +pkg_vmq_passwd_homepage = https://verne.mq/ +pkg_vmq_passwd_fetch = git +pkg_vmq_passwd_repo = https://github.com/erlio/vmq_passwd +pkg_vmq_passwd_commit = master + +PACKAGES += vmq_server +pkg_vmq_server_name = vmq_server +pkg_vmq_server_description = Component of VerneMQ: A distributed MQTT message broker +pkg_vmq_server_homepage = https://verne.mq/ +pkg_vmq_server_fetch = git +pkg_vmq_server_repo = https://github.com/erlio/vmq_server +pkg_vmq_server_commit = master + +PACKAGES += vmq_snmp +pkg_vmq_snmp_name = vmq_snmp +pkg_vmq_snmp_description = Component of VerneMQ: A distributed MQTT message broker +pkg_vmq_snmp_homepage = https://verne.mq/ +pkg_vmq_snmp_fetch = git +pkg_vmq_snmp_repo = https://github.com/erlio/vmq_snmp +pkg_vmq_snmp_commit = master + +PACKAGES += vmq_systree +pkg_vmq_systree_name = vmq_systree +pkg_vmq_systree_description = Component of VerneMQ: A distributed MQTT message broker +pkg_vmq_systree_homepage = https://verne.mq/ +pkg_vmq_systree_fetch = git +pkg_vmq_systree_repo = https://github.com/erlio/vmq_systree +pkg_vmq_systree_commit = master + +PACKAGES += vmstats +pkg_vmstats_name = vmstats +pkg_vmstats_description = tiny Erlang app that works in conjunction with statsderl in order to generate information on the Erlang VM for graphite logs. +pkg_vmstats_homepage = https://github.com/ferd/vmstats +pkg_vmstats_fetch = git +pkg_vmstats_repo = https://github.com/ferd/vmstats +pkg_vmstats_commit = master + +PACKAGES += walrus +pkg_walrus_name = walrus +pkg_walrus_description = Walrus - Mustache-like Templating +pkg_walrus_homepage = https://github.com/devinus/walrus +pkg_walrus_fetch = git +pkg_walrus_repo = https://github.com/devinus/walrus +pkg_walrus_commit = master + +PACKAGES += webmachine +pkg_webmachine_name = webmachine +pkg_webmachine_description = A REST-based system for building web applications. +pkg_webmachine_homepage = https://github.com/basho/webmachine +pkg_webmachine_fetch = git +pkg_webmachine_repo = https://github.com/basho/webmachine +pkg_webmachine_commit = master + +PACKAGES += websocket_client +pkg_websocket_client_name = websocket_client +pkg_websocket_client_description = Erlang websocket client (ws and wss supported) +pkg_websocket_client_homepage = https://github.com/jeremyong/websocket_client +pkg_websocket_client_fetch = git +pkg_websocket_client_repo = https://github.com/jeremyong/websocket_client +pkg_websocket_client_commit = master + +PACKAGES += worker_pool +pkg_worker_pool_name = worker_pool +pkg_worker_pool_description = a simple erlang worker pool +pkg_worker_pool_homepage = https://github.com/inaka/worker_pool +pkg_worker_pool_fetch = git +pkg_worker_pool_repo = https://github.com/inaka/worker_pool +pkg_worker_pool_commit = 1.0.3 + +PACKAGES += wrangler +pkg_wrangler_name = wrangler +pkg_wrangler_description = Import of the Wrangler svn repository. +pkg_wrangler_homepage = http://www.cs.kent.ac.uk/projects/wrangler/Home.html +pkg_wrangler_fetch = git +pkg_wrangler_repo = https://github.com/RefactoringTools/wrangler +pkg_wrangler_commit = master + +PACKAGES += wsock +pkg_wsock_name = wsock +pkg_wsock_description = Erlang library to build WebSocket clients and servers +pkg_wsock_homepage = https://github.com/madtrick/wsock +pkg_wsock_fetch = git +pkg_wsock_repo = https://github.com/madtrick/wsock +pkg_wsock_commit = master + +PACKAGES += xhttpc +pkg_xhttpc_name = xhttpc +pkg_xhttpc_description = Extensible HTTP Client for Erlang +pkg_xhttpc_homepage = https://github.com/seriyps/xhttpc +pkg_xhttpc_fetch = git +pkg_xhttpc_repo = https://github.com/seriyps/xhttpc +pkg_xhttpc_commit = master + +PACKAGES += xref_runner +pkg_xref_runner_name = xref_runner +pkg_xref_runner_description = Erlang Xref Runner (inspired in rebar xref) +pkg_xref_runner_homepage = https://github.com/inaka/xref_runner +pkg_xref_runner_fetch = git +pkg_xref_runner_repo = https://github.com/inaka/xref_runner +pkg_xref_runner_commit = 0.2.3 + +PACKAGES += yamerl +pkg_yamerl_name = yamerl +pkg_yamerl_description = YAML 1.2 parser in pure Erlang +pkg_yamerl_homepage = https://github.com/yakaz/yamerl +pkg_yamerl_fetch = git +pkg_yamerl_repo = https://github.com/yakaz/yamerl +pkg_yamerl_commit = master + +PACKAGES += yamler +pkg_yamler_name = yamler +pkg_yamler_description = libyaml-based yaml loader for Erlang +pkg_yamler_homepage = https://github.com/goertzenator/yamler +pkg_yamler_fetch = git +pkg_yamler_repo = https://github.com/goertzenator/yamler +pkg_yamler_commit = master + +PACKAGES += yaws +pkg_yaws_name = yaws +pkg_yaws_description = Yaws webserver +pkg_yaws_homepage = http://yaws.hyber.org +pkg_yaws_fetch = git +pkg_yaws_repo = https://github.com/klacke/yaws +pkg_yaws_commit = master + +PACKAGES += zab_engine +pkg_zab_engine_name = zab_engine +pkg_zab_engine_description = zab propotocol implement by erlang +pkg_zab_engine_homepage = https://github.com/xinmingyao/zab_engine +pkg_zab_engine_fetch = git +pkg_zab_engine_repo = https://github.com/xinmingyao/zab_engine +pkg_zab_engine_commit = master + +PACKAGES += zeta +pkg_zeta_name = zeta +pkg_zeta_description = HTTP access log parser in Erlang +pkg_zeta_homepage = https://github.com/s1n4/zeta +pkg_zeta_fetch = git +pkg_zeta_repo = https://github.com/s1n4/zeta +pkg_zeta_commit = + +PACKAGES += zippers +pkg_zippers_name = zippers +pkg_zippers_description = A library for functional zipper data structures in Erlang. Read more on zippers +pkg_zippers_homepage = https://github.com/ferd/zippers +pkg_zippers_fetch = git +pkg_zippers_repo = https://github.com/ferd/zippers +pkg_zippers_commit = master + +PACKAGES += zlists +pkg_zlists_name = zlists +pkg_zlists_description = Erlang lazy lists library. +pkg_zlists_homepage = https://github.com/vjache/erlang-zlists +pkg_zlists_fetch = git +pkg_zlists_repo = https://github.com/vjache/erlang-zlists +pkg_zlists_commit = master + +PACKAGES += zraft_lib +pkg_zraft_lib_name = zraft_lib +pkg_zraft_lib_description = Erlang raft consensus protocol implementation +pkg_zraft_lib_homepage = https://github.com/dreyk/zraft_lib +pkg_zraft_lib_fetch = git +pkg_zraft_lib_repo = https://github.com/dreyk/zraft_lib +pkg_zraft_lib_commit = master + +PACKAGES += zucchini +pkg_zucchini_name = zucchini +pkg_zucchini_description = An Erlang INI parser +pkg_zucchini_homepage = https://github.com/devinus/zucchini +pkg_zucchini_fetch = git +pkg_zucchini_repo = https://github.com/devinus/zucchini +pkg_zucchini_commit = master + +# Copyright (c) 2015, Loïc Hoguin +# This file is part of erlang.mk and subject to the terms of the ISC License. + +.PHONY: search + +define pkg_print + $(verbose) printf "%s\n" \ + $(if $(call core_eq,$(1),$(pkg_$(1)_name)),,"Pkg name: $(1)") \ + "App name: $(pkg_$(1)_name)" \ + "Description: $(pkg_$(1)_description)" \ + "Home page: $(pkg_$(1)_homepage)" \ + "Fetch with: $(pkg_$(1)_fetch)" \ + "Repository: $(pkg_$(1)_repo)" \ + "Commit: $(pkg_$(1)_commit)" \ + "" + +endef + +search: +ifdef q + $(foreach p,$(PACKAGES), \ + $(if $(findstring $(call core_lc,$(q)),$(call core_lc,$(pkg_$(p)_name) $(pkg_$(p)_description))), \ + $(call pkg_print,$(p)))) +else + $(foreach p,$(PACKAGES),$(call pkg_print,$(p))) +endif + # Copyright (c) 2013-2015, Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. @@ -245,6 +4075,7 @@ export NO_AUTOPATCH # Verbosity. dep_verbose_0 = @echo " DEP " $(1); +dep_verbose_2 = set -x; dep_verbose = $(dep_verbose_$(V)) # Core targets. @@ -264,7 +4095,7 @@ endif $(verbose) mkdir -p $(ERLANG_MK_TMP) $(verbose) for dep in $(ALL_DEPS_DIRS) ; do \ if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/deps.log; then \ - echo -n; \ + :; \ else \ echo $$dep >> $(ERLANG_MK_TMP)/deps.log; \ if [ -f $$dep/GNUmakefile ] || [ -f $$dep/makefile ] || [ -f $$dep/Makefile ]; then \ @@ -283,7 +4114,10 @@ endif # While Makefile file could be GNUmakefile or makefile, # in practice only Makefile is needed so far. define dep_autopatch - if [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \ + if [ -f $(DEPS_DIR)/$(1)/erlang.mk ]; then \ + $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \ + $(call dep_autopatch_erlang_mk,$(1)); \ + elif [ -f $(DEPS_DIR)/$(1)/Makefile ]; then \ if [ 0 != `grep -c "include ../\w*\.mk" $(DEPS_DIR)/$(1)/Makefile` ]; then \ $(call dep_autopatch2,$(1)); \ elif [ 0 != `grep -ci rebar $(DEPS_DIR)/$(1)/Makefile` ]; then \ @@ -291,12 +4125,7 @@ define dep_autopatch elif [ -n "`find $(DEPS_DIR)/$(1)/ -type f -name \*.mk -not -name erlang.mk -exec grep -i rebar '{}' \;`" ]; then \ $(call dep_autopatch2,$(1)); \ else \ - if [ -f $(DEPS_DIR)/$(1)/erlang.mk ]; then \ - $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \ - $(call dep_autopatch_erlang_mk,$(1)); \ - else \ - $(call erlang,$(call dep_autopatch_app.erl,$(1))); \ - fi \ + $(call erlang,$(call dep_autopatch_app.erl,$(1))); \ fi \ else \ if [ ! -d $(DEPS_DIR)/$(1)/src/ ]; then \ @@ -308,8 +4137,11 @@ define dep_autopatch endef define dep_autopatch2 + if [ -f $(DEPS_DIR)/$1/src/$1.app.src.script ]; then \ + $(call erlang,$(call dep_autopatch_appsrc_script.erl,$(1))); \ + fi; \ $(call erlang,$(call dep_autopatch_appsrc.erl,$(1))); \ - if [ -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script ]; then \ + if [ -f $(DEPS_DIR)/$(1)/rebar -o -f $(DEPS_DIR)/$(1)/rebar.config -o -f $(DEPS_DIR)/$(1)/rebar.config.script ]; then \ $(call dep_autopatch_fetch_rebar); \ $(call dep_autopatch_rebar,$(1)); \ else \ @@ -329,7 +4161,7 @@ define dep_autopatch_erlang_mk endef else define dep_autopatch_erlang_mk - echo -n + : endef endif @@ -381,7 +4213,7 @@ define dep_autopatch_rebar.erl file:write_file("$(call core_native_path,$(DEPS_DIR)/$1/Makefile)", Text, [append]) end, Escape = fun (Text) -> - re:replace(Text, "\\\\$$$$", "\$$$$$$$$", [global, {return, list}]) + re:replace(Text, "\\\\$$", "\$$$$", [global, {return, list}]) end, Write("IGNORE_DEPS += edown eper eunit_formatters meck node_package " "rebar_lock_deps_plugin rebar_vsn_plugin reltool_util\n"), @@ -447,67 +4279,16 @@ define dep_autopatch_rebar.erl Write(io_lib:format("COMPILE_FIRST +=~s\n", [Names])) end end(), - FindFirst = fun(F, Fd) -> - case io:parse_erl_form(Fd, undefined) of - {ok, {attribute, _, compile, {parse_transform, PT}}, _} -> - [PT, F(F, Fd)]; - {ok, {attribute, _, compile, CompileOpts}, _} when is_list(CompileOpts) -> - case proplists:get_value(parse_transform, CompileOpts) of - undefined -> [F(F, Fd)]; - PT -> [PT, F(F, Fd)] - end; - {ok, {attribute, _, include, Hrl}, _} -> - case file:open("$(call core_native_path,$(DEPS_DIR)/$1/include/)" ++ Hrl, [read]) of - {ok, HrlFd} -> [F(F, HrlFd), F(F, Fd)]; - _ -> - case file:open("$(call core_native_path,$(DEPS_DIR)/$1/src/)" ++ Hrl, [read]) of - {ok, HrlFd} -> [F(F, HrlFd), F(F, Fd)]; - _ -> [F(F, Fd)] - end - end; - {ok, {attribute, _, include_lib, "$(1)/include/" ++ Hrl}, _} -> - {ok, HrlFd} = file:open("$(call core_native_path,$(DEPS_DIR)/$1/include/)" ++ Hrl, [read]), - [F(F, HrlFd), F(F, Fd)]; - {ok, {attribute, _, include_lib, Hrl}, _} -> - case file:open("$(call core_native_path,$(DEPS_DIR)/$1/include/)" ++ Hrl, [read]) of - {ok, HrlFd} -> [F(F, HrlFd), F(F, Fd)]; - _ -> [F(F, Fd)] - end; - {ok, {attribute, _, import, {Imp, _}}, _} -> - case file:open("$(call core_native_path,$(DEPS_DIR)/$1/src/)" ++ atom_to_list(Imp) ++ ".erl", [read]) of - {ok, ImpFd} -> [Imp, F(F, ImpFd), F(F, Fd)]; - _ -> [F(F, Fd)] - end; - {eof, _} -> - file:close(Fd), - []; - _ -> - F(F, Fd) - end - end, - fun() -> - ErlFiles = filelib:wildcard("$(call core_native_path,$(DEPS_DIR)/$1/src/)*.erl"), - First0 = lists:usort(lists:flatten([begin - {ok, Fd} = file:open(F, [read]), - FindFirst(FindFirst, Fd) - end || F <- ErlFiles])), - First = lists:flatten([begin - {ok, Fd} = file:open("$(call core_native_path,$(DEPS_DIR)/$1/src/)" ++ atom_to_list(M) ++ ".erl", [read]), - FindFirst(FindFirst, Fd) - end || M <- First0, lists:member("$(call core_native_path,$(DEPS_DIR)/$1/src/)" ++ atom_to_list(M) ++ ".erl", ErlFiles)]) ++ First0, - Write(["COMPILE_FIRST +=", [[" ", atom_to_list(M)] || M <- First, - lists:member("$(call core_native_path,$(DEPS_DIR)/$1/src/)" ++ atom_to_list(M) ++ ".erl", ErlFiles)], "\n"]) - end(), Write("\n\nrebar_dep: preprocess pre-deps deps pre-app app\n"), Write("\npreprocess::\n"), Write("\npre-deps::\n"), Write("\npre-app::\n"), PatchHook = fun(Cmd) -> case Cmd of - "make -C" ++ Cmd1 -> "$$$$\(MAKE) -C" ++ Escape(Cmd1); - "gmake -C" ++ Cmd1 -> "$$$$\(MAKE) -C" ++ Escape(Cmd1); - "make " ++ Cmd1 -> "$$$$\(MAKE) -f Makefile.orig.mk " ++ Escape(Cmd1); - "gmake " ++ Cmd1 -> "$$$$\(MAKE) -f Makefile.orig.mk " ++ Escape(Cmd1); + "make -C" ++ Cmd1 -> "$$\(MAKE) -C" ++ Escape(Cmd1); + "gmake -C" ++ Cmd1 -> "$$\(MAKE) -C" ++ Escape(Cmd1); + "make " ++ Cmd1 -> "$$\(MAKE) -f Makefile.orig.mk " ++ Escape(Cmd1); + "gmake " ++ Cmd1 -> "$$\(MAKE) -f Makefile.orig.mk " ++ Escape(Cmd1); _ -> Escape(Cmd) end end, @@ -519,10 +4300,10 @@ define dep_autopatch_rebar.erl {'get-deps', Cmd} -> Write("\npre-deps::\n\t" ++ PatchHook(Cmd) ++ "\n"); {compile, Cmd} -> - Write("\npre-app::\n\tCC=$$$$\(CC) " ++ PatchHook(Cmd) ++ "\n"); + Write("\npre-app::\n\tCC=$$\(CC) " ++ PatchHook(Cmd) ++ "\n"); {Regex, compile, Cmd} -> case rebar_utils:is_arch(Regex) of - true -> Write("\npre-app::\n\tCC=$$$$\(CC) " ++ PatchHook(Cmd) ++ "\n"); + true -> Write("\npre-app::\n\tCC=$$\(CC) " ++ PatchHook(Cmd) ++ "\n"); false -> ok end; _ -> ok @@ -530,7 +4311,7 @@ define dep_autopatch_rebar.erl end end(), ShellToMk = fun(V) -> - re:replace(re:replace(V, "(\\\\$$$$)(\\\\w*)", "\\\\1(\\\\2)", [global]), + re:replace(re:replace(V, "(\\\\$$)(\\\\w*)", "\\\\1(\\\\2)", [global]), "-Werror\\\\b", "", [{return, list}, global]) end, PortSpecs = fun() -> @@ -564,7 +4345,7 @@ define dep_autopatch_rebar.erl case PortSpecs of [] -> ok; _ -> - Write("\npre-app::\n\t$$$$\(MAKE) -f c_src/Makefile.erlang.mk\n"), + Write("\npre-app::\n\t$$\(MAKE) -f c_src/Makefile.erlang.mk\n"), PortSpecWrite(io_lib:format("ERL_CFLAGS = -finline-functions -Wall -fPIC -I ~s/erts-~s/include -I ~s\n", [code:root_dir(), erlang:system_info(version), code:lib_dir(erl_interface, include)])), PortSpecWrite(io_lib:format("ERL_LDFLAGS = -L ~s -lerl_interface -lei\n", @@ -602,17 +4383,18 @@ define dep_autopatch_rebar.erl _ -> "" end, "\n\nall:: ", Output, "\n\n", - "%.o: %.c\n\t$$$$\(CC) -c -o $$$$\@ $$$$\< $$$$\(CFLAGS) $$$$\(ERL_CFLAGS) $$$$\(DRV_CFLAGS) $$$$\(EXE_CFLAGS)\n\n", - "%.o: %.C\n\t$$$$\(CXX) -c -o $$$$\@ $$$$\< $$$$\(CXXFLAGS) $$$$\(ERL_CFLAGS) $$$$\(DRV_CFLAGS) $$$$\(EXE_CFLAGS)\n\n", - "%.o: %.cc\n\t$$$$\(CXX) -c -o $$$$\@ $$$$\< $$$$\(CXXFLAGS) $$$$\(ERL_CFLAGS) $$$$\(DRV_CFLAGS) $$$$\(EXE_CFLAGS)\n\n", - "%.o: %.cpp\n\t$$$$\(CXX) -c -o $$$$\@ $$$$\< $$$$\(CXXFLAGS) $$$$\(ERL_CFLAGS) $$$$\(DRV_CFLAGS) $$$$\(EXE_CFLAGS)\n\n", + "%.o: %.c\n\t$$\(CC) -c -o $$\@ $$\< $$\(CFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n", + "%.o: %.C\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n", + "%.o: %.cc\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n", + "%.o: %.cpp\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n", [[Output, ": ", K, " = ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))], - Output, ": $$$$\(foreach ext,.c .C .cc .cpp,", - "$$$$\(patsubst %$$$$\(ext),%.o,$$$$\(filter %$$$$\(ext),$$$$\(wildcard", Input, "))))\n", - "\t$$$$\(CC) -o $$$$\@ $$$$\? $$$$\(LDFLAGS) $$$$\(ERL_LDFLAGS) $$$$\(DRV_LDFLAGS) $$$$\(EXE_LDFLAGS)", - case filename:extension(Output) of - [] -> "\n"; - _ -> " -shared\n" + Output, ": $$\(foreach ext,.c .C .cc .cpp,", + "$$\(patsubst %$$\(ext),%.o,$$\(filter %$$\(ext),$$\(wildcard", Input, "))))\n", + "\t$$\(CC) -o $$\@ $$\? $$\(LDFLAGS) $$\(ERL_LDFLAGS) $$\(DRV_LDFLAGS) $$\(EXE_LDFLAGS)", + case {filename:extension(Output), $(PLATFORM)} of + {[], _} -> "\n"; + {_, darwin} -> "\n"; + _ -> " -shared\n" end]) end, [PortSpec(S) || S <- PortSpecs] @@ -671,7 +4453,7 @@ define dep_autopatch_app.erl false -> ok; true -> {ok, [{application, '$(1)', L0}]} = file:consult(App), - Mods = filelib:fold_files("$(call core_native_path,$(DEPS_DIR)/$1/src)", "\\\\.erl$$$$", true, + Mods = filelib:fold_files("$(call core_native_path,$(DEPS_DIR)/$1/src)", "\\\\.erl$$", true, fun (F, Acc) -> [list_to_atom(filename:rootname(filename:basename(F)))|Acc] end, []), L = lists:keystore(modules, 1, L0, {modules, Mods}), ok = file:write_file(App, io_lib:format("~p.~n", [{application, '$(1)', L}])) @@ -681,6 +4463,15 @@ define dep_autopatch_app.erl halt() endef +define dep_autopatch_appsrc_script.erl + AppSrc = "$(call core_native_path,$(DEPS_DIR)/$1/src/$1.app.src)", + AppSrcScript = AppSrc ++ ".script", + Bindings = erl_eval:new_bindings(), + {ok, Conf} = file:script(AppSrcScript, Bindings), + ok = file:write_file(AppSrc, io_lib:format("~p.~n", [Conf])), + halt() +endef + define dep_autopatch_appsrc.erl AppSrcOut = "$(call core_native_path,$(DEPS_DIR)/$1/src/$1.app.src)", AppSrcIn = case filelib:is_regular(AppSrcOut) of false -> "$(call core_native_path,$(DEPS_DIR)/$1/ebin/$1.app)"; true -> AppSrcOut end, @@ -767,10 +4558,11 @@ $(DEPS_DIR)/$(call dep_name,$1): exit 17; \ fi $(verbose) mkdir -p $(DEPS_DIR) - $(dep_verbose) $(call dep_fetch_$(strip $(call dep_fetch,$1)),$1) - $(verbose) if [ -f $(DEPS_DIR)/$(DEP_NAME)/configure.ac -o -f $(DEPS_DIR)/$(DEP_NAME)/configure.in ]; then \ - echo " AUTO " $(DEP_STR); \ - cd $(DEPS_DIR)/$(DEP_NAME) && autoreconf -Wall -vif -I m4; \ + $(dep_verbose) $(call dep_fetch_$(strip $(call dep_fetch,$(1))),$(1)) + $(verbose) if [ -f $(DEPS_DIR)/$(1)/configure.ac -o -f $(DEPS_DIR)/$(1)/configure.in ] \ + && [ ! -f $(DEPS_DIR)/$(1)/configure ]; then \ + echo " AUTO " $(1); \ + cd $(DEPS_DIR)/$(1) && autoreconf -Wall -vif -I m4; \ fi - $(verbose) if [ -f $(DEPS_DIR)/$(DEP_NAME)/configure ]; then \ echo " CONF " $(DEP_STR); \ @@ -793,7 +4585,7 @@ ifeq ($(filter $(1),$(NO_AUTOPATCH)),) git clone https://github.com/rabbitmq/rabbitmq-codegen.git $(DEPS_DIR)/rabbitmq-codegen; \ fi \ else \ - $(call dep_autopatch,$(DEP_NAME)) \ + $$(call dep_autopatch,$(DEP_NAME)) \ fi endif endef @@ -838,6 +4630,60 @@ $(foreach p,$(DEP_PLUGINS),\ $(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\ $(call core_dep_plugin,$p/plugins.mk,$p)))) +# Copyright (c) 2013-2015, Loïc Hoguin +# This file is part of erlang.mk and subject to the terms of the ISC License. + +# Configuration. + +DTL_FULL_PATH ?= +DTL_PATH ?= templates/ +DTL_SUFFIX ?= _dtl + +# Verbosity. + +dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F)); +dtl_verbose = $(dtl_verbose_$(V)) + +# Core targets. + +DTL_FILES = $(sort $(call core_find,$(DTL_PATH),*.dtl)) + +ifneq ($(DTL_FILES),) + +ifdef DTL_FULL_PATH +BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(subst /,_,$(DTL_FILES:$(DTL_PATH)%=%)))) +else +BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(notdir $(DTL_FILES)))) +endif + +# Rebuild templates when the Makefile changes. +$(DTL_FILES): $(MAKEFILE_LIST) + @touch $@ + +define erlydtl_compile.erl + [begin + Module0 = case "$(strip $(DTL_FULL_PATH))" of + "" -> + filename:basename(F, ".dtl"); + _ -> + "$(DTL_PATH)" ++ F2 = filename:rootname(F, ".dtl"), + re:replace(F2, "/", "_", [{return, list}, global]) + end, + Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"), + case erlydtl:compile(F, Module, [{out_dir, "ebin/"}, return_errors, {doc_root, "templates"}]) of + ok -> ok; + {ok, _} -> ok + end + end || F <- string:tokens("$(1)", " ")], + halt(). +endef + +ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/ + $(if $(strip $?),\ + $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$?),-pa ebin/ $(DEPS_DIR)/erlydtl/ebin/)) + +endif + # Copyright (c) 2015, Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. @@ -891,25 +4737,32 @@ COMPILE_MIB_FIRST_PATHS = $(addprefix mibs/,$(addsuffix .mib,$(COMPILE_MIB_FIRST # Verbosity. app_verbose_0 = @echo " APP " $(PROJECT); +app_verbose_2 = set -x; app_verbose = $(app_verbose_$(V)) appsrc_verbose_0 = @echo " APP " $(PROJECT).app.src; +appsrc_verbose_2 = set -x; appsrc_verbose = $(appsrc_verbose_$(V)) makedep_verbose_0 = @echo " DEPEND" $(PROJECT).d; +makedep_verbose_2 = set -x; makedep_verbose = $(makedep_verbose_$(V)) erlc_verbose_0 = @echo " ERLC " $(filter-out $(patsubst %,%.erl,$(ERLC_EXCLUDE)),\ $(filter %.erl %.core,$(?F))); +erlc_verbose_2 = set -x; erlc_verbose = $(erlc_verbose_$(V)) xyrl_verbose_0 = @echo " XYRL " $(filter %.xrl %.yrl,$(?F)); +xyrl_verbose_2 = set -x; xyrl_verbose = $(xyrl_verbose_$(V)) asn1_verbose_0 = @echo " ASN1 " $(filter %.asn1,$(?F)); +asn1_verbose_2 = set -x; asn1_verbose = $(asn1_verbose_$(V)) mib_verbose_0 = @echo " MIB " $(filter %.bin %.mib,$(?F)); +mib_verbose_2 = set -x; mib_verbose = $(mib_verbose_$(V)) ifneq ($(wildcard src/),) @@ -917,10 +4770,10 @@ ifneq ($(wildcard src/),) # Targets. ifeq ($(wildcard ebin/test),) -app:: $(PROJECT).d +app:: deps $(PROJECT).d $(verbose) $(MAKE) --no-print-directory app-build else -app:: clean $(PROJECT).d +app:: clean deps $(PROJECT).d $(verbose) $(MAKE) --no-print-directory app-build endif @@ -950,7 +4803,7 @@ endef endif app-build: ebin/$(PROJECT).app - $(verbose) echo -n + $(verbose) : # Source files. @@ -1002,51 +4855,79 @@ $(PROJECT).d:: $(XRL_FILES) $(YRL_FILES) # Erlang and Core Erlang files. define makedep.erl + E = ets:new(makedep, [bag]), + G = digraph:new([acyclic]), ErlFiles = lists:usort(string:tokens("$(ERL_FILES)", " ")), - Modules = [{filename:basename(F, ".erl"), F} || F <- ErlFiles], - Add = fun (Dep, Acc) -> - case lists:keyfind(atom_to_list(Dep), 1, Modules) of - {_, DepFile} -> [DepFile|Acc]; - false -> Acc + Modules = [{list_to_atom(filename:basename(F, ".erl")), F} || F <- ErlFiles], + Add = fun (Mod, Dep) -> + case lists:keyfind(Dep, 1, Modules) of + false -> ok; + {_, DepFile} -> + {_, ModFile} = lists:keyfind(Mod, 1, Modules), + ets:insert(E, {ModFile, DepFile}), + digraph:add_vertex(G, Mod), + digraph:add_vertex(G, Dep), + digraph:add_edge(G, Mod, Dep) end end, - AddHd = fun (Dep, Acc) -> - case {Dep, lists:keymember(Dep, 2, Modules)} of - {"src/" ++ _, false} -> [Dep|Acc]; - {"include/" ++ _, false} -> [Dep|Acc]; - _ -> Acc + AddHd = fun (F, Mod, DepFile) -> + case file:open(DepFile, [read]) of + {error, enoent} -> ok; + {ok, Fd} -> + F(F, Fd, Mod), + {_, ModFile} = lists:keyfind(Mod, 1, Modules), + ets:insert(E, {ModFile, DepFile}) end end, - CompileFirst = fun (Deps) -> - First0 = [case filename:extension(D) of - ".erl" -> filename:basename(D, ".erl"); - _ -> [] - end || D <- Deps], - case lists:usort(First0) of - [] -> []; - [[]] -> []; - First -> ["COMPILE_FIRST +=", [[" ", F] || F <- First], "\n"] - end + Attr = fun + (F, Mod, behavior, Dep) -> Add(Mod, Dep); + (F, Mod, behaviour, Dep) -> Add(Mod, Dep); + (F, Mod, compile, {parse_transform, Dep}) -> Add(Mod, Dep); + (F, Mod, compile, Opts) when is_list(Opts) -> + case proplists:get_value(parse_transform, Opts) of + undefined -> ok; + Dep -> Add(Mod, Dep) + end; + (F, Mod, include, Hrl) -> + case filelib:is_file("include/" ++ Hrl) of + true -> AddHd(F, Mod, "include/" ++ Hrl); + false -> + case filelib:is_file("src/" ++ Hrl) of + true -> AddHd(F, Mod, "src/" ++ Hrl); + false -> false + end + end; + (F, Mod, include_lib, "$1/include/" ++ Hrl) -> AddHd(F, Mod, "include/" ++ Hrl); + (F, Mod, include_lib, Hrl) -> AddHd(F, Mod, "include/" ++ Hrl); + (F, Mod, import, {Imp, _}) -> + case filelib:is_file("src/" ++ atom_to_list(Imp) ++ ".erl") of + false -> ok; + true -> Add(Mod, Imp) + end; + (_, _, _, _) -> ok end, - Depend = [begin - case epp:parse_file(F, ["include/"], []) of - {ok, Forms} -> - Deps = lists:usort(lists:foldl(fun - ({attribute, _, behavior, Dep}, Acc) -> Add(Dep, Acc); - ({attribute, _, behaviour, Dep}, Acc) -> Add(Dep, Acc); - ({attribute, _, compile, {parse_transform, Dep}}, Acc) -> Add(Dep, Acc); - ({attribute, _, file, {Dep, _}}, Acc) -> AddHd(Dep, Acc); - (_, Acc) -> Acc - end, [], Forms)), - case Deps of - [] -> ""; - _ -> [F, "::", [[" ", D] || D <- Deps], "; @touch \$$@\n", CompileFirst(Deps)] - end; - {error, enoent} -> - [] + MakeDepend = fun(F, Fd, Mod) -> + case io:parse_erl_form(Fd, undefined) of + {ok, {attribute, _, Key, Value}, _} -> + Attr(F, Mod, Key, Value), + F(F, Fd, Mod); + {eof, _} -> + file:close(Fd); + _ -> + F(F, Fd, Mod) end + end, + [begin + Mod = list_to_atom(filename:basename(F, ".erl")), + {ok, Fd} = file:open(F, [read]), + MakeDepend(MakeDepend, Fd, Mod) end || F <- ErlFiles], - ok = file:write_file("$(1)", Depend), + Depend = sofs:to_external(sofs:relation_to_family(sofs:relation(ets:tab2list(E)))), + CompileFirst = [X || X <- lists:reverse(digraph_utils:topsort(G)), [] =/= digraph:in_neighbours(G, X)], + ok = file:write_file("$(1)", [ + [[F, "::", [[" ", D] || D <- Deps], "; @touch \$$@\n"] || {F, Deps} <- Depend], + "\nCOMPILE_FIRST +=", [[" ", atom_to_list(CF)] || CF <- CompileFirst], "\n" + ]), halt() endef @@ -1125,6 +5006,26 @@ endif # Copyright (c) 2015, Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. +.PHONY: rel-deps + +# Configuration. + +ALL_REL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(REL_DEPS)) + +# Targets. + +$(foreach dep,$(REL_DEPS),$(eval $(call dep_target,$(dep)))) + +ifneq ($(SKIP_DEPS),) +rel-deps: +else +rel-deps: $(ALL_REL_DEPS_DIRS) + $(verbose) for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done +endif + +# Copyright (c) 2015, Loïc Hoguin +# This file is part of erlang.mk and subject to the terms of the ISC License. + .PHONY: test-deps test-dir test-build clean-test-dir # Configuration. @@ -1153,6 +5054,11 @@ test-dir: $(call core_find,$(TEST_DIR)/,*.erl) -pa ebin/ endif +ifeq ($(wildcard src),) +test-build:: ERLC_OPTS=$(TEST_ERLC_OPTS) +test-build:: clean deps test-deps + $(verbose) $(MAKE) --no-print-directory test-dir ERLC_OPTS="$(TEST_ERLC_OPTS)" +else ifeq ($(wildcard ebin/test),) test-build:: ERLC_OPTS=$(TEST_ERLC_OPTS) test-build:: clean deps test-deps $(PROJECT).d @@ -1170,6 +5076,39 @@ clean-test-dir: ifneq ($(wildcard $(TEST_DIR)/*.beam),) $(gen_verbose) rm -f $(TEST_DIR)/*.beam endif +endif + +# Copyright (c) 2015, Loïc Hoguin +# This file is part of erlang.mk and subject to the terms of the ISC License. + +.PHONY: rebar.config + +# We strip out -Werror because we don't want to fail due to +# warnings when used as a dependency. + +compat_prepare_erlc_opts = $(shell echo "$1" | sed 's/, */,/') + +define compat_convert_erlc_opts +$(if $(filter-out -Werror,$1),\ + $(if $(findstring +,$1),\ + $(shell echo $1 | cut -b 2-))) +endef + +define compat_erlc_opts_to_list + [$(call comma_list,$(foreach o,$(call compat_prepare_erlc_opts,$1),$(call compat_convert_erlc_opts,$o)))] +endef + +define compat_rebar_config +{deps, [$(call comma_list,$(foreach d,$(DEPS),\ + {$(call dep_name,$d),".*",{git,"$(call dep_repo,$d)","$(call dep_commit,$d)"}}))]}. +{erl_opts, $(call compat_erlc_opts_to_list,$(ERLC_OPTS))}. +endef + +$(eval _compat_rebar_config = $$(compat_rebar_config)) +$(eval export _compat_rebar_config) + +rebar.config: + $(gen_verbose) echo "$${_compat_rebar_config}" > rebar.config # Copyright (c) 2015, Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. @@ -1181,12 +5120,12 @@ MAN_SECTIONS ?= 3 7 docs:: asciidoc -asciidoc: distclean-asciidoc doc-deps asciidoc-guide asciidoc-manual +asciidoc: asciidoc-guide asciidoc-manual ifeq ($(wildcard doc/src/guide/book.asciidoc),) asciidoc-guide: else -asciidoc-guide: +asciidoc-guide: distclean-asciidoc doc-deps a2x -v -f pdf doc/src/guide/book.asciidoc && mv doc/src/guide/book.pdf doc/guide.pdf a2x -v -f chunked doc/src/guide/book.asciidoc && mv doc/src/guide/book.chunked/ doc/html/ endif @@ -1194,7 +5133,7 @@ endif ifeq ($(wildcard doc/src/manual/*.asciidoc),) asciidoc-manual: else -asciidoc-manual: +asciidoc-manual: distclean-asciidoc doc-deps for f in doc/src/manual/*.asciidoc ; do \ a2x -v -f manpage $$f ; \ done @@ -1209,7 +5148,7 @@ install-docs:: install-asciidoc install-asciidoc: asciidoc-manual for s in $(MAN_SECTIONS); do \ mkdir -p $(MAN_INSTALL_PATH)/man$$s/ ; \ - install -g 0 -o 0 -m 0644 doc/man$$s/*.gz $(MAN_INSTALL_PATH)/man$$s/ ; \ + install -g `id -u` -o `id -g` -m 0644 doc/man$$s/*.gz $(MAN_INSTALL_PATH)/man$$s/ ; \ done endif @@ -1269,6 +5208,8 @@ define bs_appsrc_lib ]}. endef +# To prevent autocompletion issues with ZSH, we add "include erlang.mk" +# separately during the actual bootstrap. ifdef SP define bs_Makefile PROJECT = $p @@ -1278,17 +5219,21 @@ PROJECT_VERSION = 0.0.1 # Whitespace to be used when creating files from templates. SP = $(SP) -include erlang.mk endef else define bs_Makefile PROJECT = $p -include erlang.mk +PROJECT_DESCRIPTION = New project +PROJECT_VERSION = 0.0.1 + endef endif define bs_apps_Makefile PROJECT = $p +PROJECT_DESCRIPTION = New project +PROJECT_VERSION = 0.0.1 + include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(APPS_DIR)/app)/erlang.mk endef @@ -1564,7 +5509,7 @@ endef # Plugin-specific targets. define render_template - $(verbose) echo "$${_$(1)}" > $(2) + $(verbose) printf -- '$(subst $(newline),\n,$(subst %,%%,$(subst ','\'',$(subst $(tab),$(WS),$(call $(1))))))\n' > $(2) endef ifndef WS @@ -1575,10 +5520,6 @@ WS = $(tab) endif endif -$(foreach template,$(filter bs_% tpl_%,$(.VARIABLES)), \ - $(eval _$(template) = $$(subst $$(tab),$$(WS),$$($(template)))) \ - $(eval export _$(template))) - bootstrap: ifneq ($(wildcard src/),) $(error Error: src/ directory already exists) @@ -1586,6 +5527,7 @@ endif $(eval p := $(PROJECT)) $(eval n := $(PROJECT)_sup) $(call render_template,bs_Makefile,Makefile) + $(verbose) echo "include erlang.mk" >> Makefile $(verbose) mkdir src/ ifdef LEGACY $(call render_template,bs_appsrc,src/$(PROJECT).app.src) @@ -1599,6 +5541,7 @@ ifneq ($(wildcard src/),) endif $(eval p := $(PROJECT)) $(call render_template,bs_Makefile,Makefile) + $(verbose) echo "include erlang.mk" >> Makefile $(verbose) mkdir src/ ifdef LEGACY $(call render_template,bs_appsrc_lib,src/$(PROJECT).app.src) @@ -1679,12 +5622,32 @@ list-templates: C_SRC_DIR ?= $(CURDIR)/c_src C_SRC_ENV ?= $(C_SRC_DIR)/env.mk -C_SRC_OUTPUT ?= $(CURDIR)/priv/$(PROJECT).so +C_SRC_OUTPUT ?= $(CURDIR)/priv/$(PROJECT) C_SRC_TYPE ?= shared # System type and C compiler/flags. -ifeq ($(PLATFORM),darwin) +ifeq ($(PLATFORM),msys2) + C_SRC_OUTPUT_EXECUTABLE_EXTENSION ?= .exe + C_SRC_OUTPUT_SHARED_EXTENSION ?= .dll +else + C_SRC_OUTPUT_EXECUTABLE_EXTENSION ?= + C_SRC_OUTPUT_SHARED_EXTENSION ?= .so +endif + +ifeq ($(C_SRC_TYPE),shared) + C_SRC_OUTPUT_FILE = $(C_SRC_OUTPUT)$(C_SRC_OUTPUT_SHARED_EXTENSION) +else + C_SRC_OUTPUT_FILE = $(C_SRC_OUTPUT)$(C_SRC_OUTPUT_EXECUTABLE_EXTENSION) +endif + +ifeq ($(PLATFORM),msys2) +# We hardcode the compiler used on MSYS2. The default CC=cc does +# not produce working code. The "gcc" MSYS2 package also doesn't. + CC = /mingw64/bin/gcc + CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes + CXXFLAGS ?= -O3 -finline-functions -Wall +else ifeq ($(PLATFORM),darwin) CC ?= cc CFLAGS ?= -O3 -std=c99 -arch x86_64 -finline-functions -Wall -Wmissing-prototypes CXXFLAGS ?= -O3 -arch x86_64 -finline-functions -Wall @@ -1699,10 +5662,15 @@ else ifeq ($(PLATFORM),linux) CXXFLAGS ?= -O3 -finline-functions -Wall endif -CFLAGS += -fPIC -I $(ERTS_INCLUDE_DIR) -I $(ERL_INTERFACE_INCLUDE_DIR) -CXXFLAGS += -fPIC -I $(ERTS_INCLUDE_DIR) -I $(ERL_INTERFACE_INCLUDE_DIR) +ifneq ($(PLATFORM),msys2) + CFLAGS += -fPIC + CXXFLAGS += -fPIC +endif + +CFLAGS += -I"$(ERTS_INCLUDE_DIR)" -I"$(ERL_INTERFACE_INCLUDE_DIR)" +CXXFLAGS += -I"$(ERTS_INCLUDE_DIR)" -I"$(ERL_INTERFACE_INCLUDE_DIR)" -LDLIBS += -L $(ERL_INTERFACE_LIB_DIR) -lerl_interface -lei +LDLIBS += -L"$(ERL_INTERFACE_LIB_DIR)" -lerl_interface -lei # Verbosity. @@ -1739,15 +5707,15 @@ OBJECTS = $(addsuffix .o, $(basename $(SOURCES))) COMPILE_C = $(c_verbose) $(CC) $(CFLAGS) $(CPPFLAGS) -c COMPILE_CPP = $(cpp_verbose) $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -app:: $(C_SRC_ENV) $(C_SRC_OUTPUT) +app:: $(C_SRC_ENV) $(C_SRC_OUTPUT_FILE) -test-build:: $(C_SRC_ENV) $(C_SRC_OUTPUT) +test-build:: $(C_SRC_ENV) $(C_SRC_OUTPUT_FILE) -$(C_SRC_OUTPUT): $(OBJECTS) +$(C_SRC_OUTPUT_FILE): $(OBJECTS) $(verbose) mkdir -p priv/ $(link_verbose) $(CC) $(OBJECTS) \ $(LDFLAGS) $(if $(filter $(C_SRC_TYPE),shared),-shared) $(LDLIBS) \ - -o $(C_SRC_OUTPUT) + -o $(C_SRC_OUTPUT_FILE) %.o: %.c $(COMPILE_C) $(OUTPUT_OPTION) $< @@ -1764,13 +5732,13 @@ $(C_SRC_OUTPUT): $(OBJECTS) clean:: clean-c_src clean-c_src: - $(gen_verbose) rm -f $(C_SRC_OUTPUT) $(OBJECTS) + $(gen_verbose) rm -f $(C_SRC_OUTPUT_FILE) $(OBJECTS) endif ifneq ($(wildcard $(C_SRC_DIR)),) $(C_SRC_ENV): - $(verbose) $(ERL) -eval "file:write_file(\"$(C_SRC_ENV)\", \ + $(verbose) $(ERL) -eval "file:write_file(\"$(call core_native_path,$(C_SRC_ENV))\", \ io_lib:format( \ \"ERTS_INCLUDE_DIR ?= ~s/erts-~s/include/~n\" \ \"ERL_INTERFACE_INCLUDE_DIR ?= ~s~n\" \ @@ -1864,10 +5832,6 @@ hello(_) -> erlang:nif_error({not_loaded, ?MODULE}). endef -$(foreach template,bs_c_nif bs_erl_nif, \ - $(eval _$(template) = $$(subst $$(tab),$$(WS),$$($(template)))) \ - $(eval export _$(template))) - new-nif: ifneq ($(wildcard $(C_SRC_DIR)/$n.c),) $(error Error: $(C_SRC_DIR)/$n.c already exists) @@ -1952,7 +5916,7 @@ endif # Copyright (c) 2013-2015, Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. -.PHONY: ct distclean-ct +.PHONY: ct apps-ct distclean-ct # Configuration. @@ -1987,17 +5951,33 @@ CT_RUN = ct_run \ -logdir $(CURDIR)/logs ifeq ($(CT_SUITES),) -ct: +ct: $(if $(IS_APP),,apps-ct) else -ct: test-build +ct: test-build $(if $(IS_APP),,apps-ct) $(verbose) mkdir -p $(CURDIR)/logs/ $(gen_verbose) $(CT_RUN) -suite $(addsuffix _SUITE,$(CT_SUITES)) $(CT_OPTS) endif +ifneq ($(ALL_APPS_DIRS),) +apps-ct: + $(verbose) for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app ct IS_APP=1; done +endif + +ifndef t +CT_EXTRA = +else +ifeq (,$(findstring :,$t)) +CT_EXTRA = -group $t +else +t_words = $(subst :, ,$t) +CT_EXTRA = -group $(firstword $(t_words)) -case $(lastword $(t_words)) +endif +endif + define ct_suite_target ct-$(1): test-build $(verbose) mkdir -p $(CURDIR)/logs/ - $(gen_verbose) $(CT_RUN) -suite $(addsuffix _SUITE,$(1)) $(CT_OPTS) + $(gen_verbose) $(CT_RUN) -suite $(addsuffix _SUITE,$(1)) $(CT_EXTRA) $(CT_OPTS) endef $(foreach test,$(CT_SUITES),$(eval $(call ct_suite_target,$(test)))) @@ -2016,9 +5996,8 @@ DIALYZER_PLT ?= $(CURDIR)/.$(PROJECT).plt export DIALYZER_PLT PLT_APPS ?= -DIALYZER_DIRS ?= --src -r src -DIALYZER_OPTS ?= -Werror_handling -Wrace_conditions \ - -Wunmatched_returns # -Wunderspecs +DIALYZER_DIRS ?= --src -r $(wildcard src) $(ALL_APPS_DIRS) +DIALYZER_OPTS ?= -Werror_handling -Wrace_conditions -Wunmatched_returns # -Wunderspecs # Core targets. @@ -2034,6 +6013,18 @@ help:: # Plugin-specific targets. +define filter_opts.erl + Opts = binary:split(<<"$1">>, <<"-">>, [global]), + Filtered = lists:reverse(lists:foldl(fun + (O = <<"pa ", _/bits>>, Acc) -> [O|Acc]; + (O = <<"D ", _/bits>>, Acc) -> [O|Acc]; + (O = <<"I ", _/bits>>, Acc) -> [O|Acc]; + (_, Acc) -> Acc + end, [], Opts)), + io:format("~s~n", [[["-", O] || O <- Filtered]]), + halt(). +endef + $(DIALYZER_PLT): deps app $(verbose) dialyzer --build_plt --apps erts kernel stdlib $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS) @@ -2047,100 +6038,32 @@ dialyze: else dialyze: $(DIALYZER_PLT) endif - $(verbose) dialyzer --no_native $(DIALYZER_DIRS) $(DIALYZER_OPTS) + $(verbose) dialyzer --no_native `$(call erlang,$(call filter_opts.erl,$(ERLC_OPTS)))` $(DIALYZER_DIRS) $(DIALYZER_OPTS) -# Copyright (c) 2015, Erlang Solutions Ltd. +# Copyright (c) 2013-2015, Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. -.PHONY: elvis distclean-elvis +.PHONY: distclean-edoc edoc # Configuration. -ELVIS_CONFIG ?= $(CURDIR)/elvis.config - -ELVIS ?= $(CURDIR)/elvis -export ELVIS - -ELVIS_URL ?= https://github.com/inaka/elvis/releases/download/0.2.5/elvis -ELVIS_CONFIG_URL ?= https://github.com/inaka/elvis/releases/download/0.2.5/elvis.config -ELVIS_OPTS ?= +EDOC_OPTS ?= # Core targets. -help:: - $(verbose) printf "%s\n" "" \ - "Elvis targets:" \ - " elvis Run Elvis using the local elvis.config or download the default otherwise" +ifneq ($(wildcard doc/overview.edoc),) +docs:: edoc +endif -distclean:: distclean-elvis +distclean:: distclean-edoc # Plugin-specific targets. -$(ELVIS): - $(gen_verbose) $(call core_http_get,$(ELVIS),$(ELVIS_URL)) - $(verbose) chmod +x $(ELVIS) - -$(ELVIS_CONFIG): - $(verbose) $(call core_http_get,$(ELVIS_CONFIG),$(ELVIS_CONFIG_URL)) - -elvis: $(ELVIS) $(ELVIS_CONFIG) - $(verbose) $(ELVIS) rock -c $(ELVIS_CONFIG) $(ELVIS_OPTS) - -distclean-elvis: - $(gen_verbose) rm -rf $(ELVIS) - -# Copyright (c) 2013-2015, Loïc Hoguin -# This file is part of erlang.mk and subject to the terms of the ISC License. - -# Configuration. - -DTL_FULL_PATH ?= -DTL_PATH ?= templates/ -DTL_SUFFIX ?= _dtl - -# Verbosity. - -dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F)); -dtl_verbose = $(dtl_verbose_$(V)) - -# Core targets. - -define erlydtl_compile.erl - [begin - Module0 = case "$(strip $(DTL_FULL_PATH))" of - "" -> - filename:basename(F, ".dtl"); - _ -> - "$(DTL_PATH)" ++ F2 = filename:rootname(F, ".dtl"), - re:replace(F2, "/", "_", [{return, list}, global]) - end, - Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"), - case erlydtl:compile(F, Module, [{out_dir, "ebin/"}, return_errors, {doc_root, "templates"}]) of - ok -> ok; - {ok, _} -> ok - end - end || F <- string:tokens("$(1)", " ")], - halt(). -endef - -ifneq ($(wildcard src/),) +edoc: distclean-edoc doc-deps + $(gen_verbose) $(ERL) -eval 'edoc:application($(PROJECT), ".", [$(EDOC_OPTS)]), halt().' -DTL_FILES = $(sort $(call core_find,$(DTL_PATH),*.dtl)) - -ifdef DTL_FULL_PATH -BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(subst /,_,$(DTL_FILES:$(DTL_PATH)%=%)))) -else -BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(notdir $(DTL_FILES)))) -endif - -# Rebuild templates when the Makefile changes. -$(DTL_FILES): $(MAKEFILE_LIST) - @touch $@ - -ebin/$(PROJECT).app:: $(DTL_FILES) - $(if $(strip $?),\ - $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$?,-pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))) -endif +distclean-edoc: + $(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info # Copyright (c) 2014 Dave Cottlehuber # This file is part of erlang.mk and subject to the terms of the ISC License. @@ -2211,11 +6134,12 @@ distclean-escript: # Copyright (c) 2015, Loïc Hoguin # This file is contributed to erlang.mk and subject to the terms of the ISC License. -.PHONY: eunit +.PHONY: eunit apps-eunit # Configuration EUNIT_OPTS ?= +EUNIT_ERL_OPTS ?= # Core targets. @@ -2237,7 +6161,7 @@ define eunit.erl _ -> ok end end, - case eunit:test([$(call comma_list,$(1))], [$(EUNIT_OPTS)]) of + case eunit:test($1, [$(EUNIT_OPTS)]) of ok -> ok; error -> halt(2) end, @@ -2249,14 +6173,30 @@ define eunit.erl halt() endef +EUNIT_ERL_OPTS += -pa $(TEST_DIR) $(DEPS_DIR)/*/ebin $(APPS_DIR)/*/ebin ebin + +ifdef t +ifeq (,$(findstring :,$(t))) +eunit: test-build + $(gen_verbose) $(call erlang,$(call eunit.erl,['$(t)']),$(EUNIT_ERL_OPTS)) +else +eunit: test-build + $(gen_verbose) $(call erlang,$(call eunit.erl,fun $(t)/0),$(EUNIT_ERL_OPTS)) +endif +else EUNIT_EBIN_MODS = $(notdir $(basename $(call core_find,ebin/,*.beam))) EUNIT_TEST_MODS = $(notdir $(basename $(call core_find,$(TEST_DIR)/,*.beam))) EUNIT_MODS = $(foreach mod,$(EUNIT_EBIN_MODS) $(filter-out \ - $(patsubst %,%_tests,$(EUNIT_EBIN_MODS)),$(EUNIT_TEST_MODS)),{module,'$(mod)'}) + $(patsubst %,%_tests,$(EUNIT_EBIN_MODS)),$(EUNIT_TEST_MODS)),'$(mod)') -eunit: test-build - $(gen_verbose) $(ERL) -pa $(TEST_DIR) $(DEPS_DIR)/*/ebin ebin \ - -eval "$(subst $(newline),,$(subst ",\",$(call eunit.erl,$(EUNIT_MODS))))" +eunit: test-build $(if $(IS_APP),,apps-eunit) + $(gen_verbose) $(call erlang,$(call eunit.erl,[$(call comma_list,$(EUNIT_MODS))]),$(EUNIT_ERL_OPTS)) + +ifneq ($(ALL_APPS_DIRS),) +apps-eunit: + $(verbose) for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app eunit IS_APP=1; done +endif +endif # Copyright (c) 2013-2015, Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. @@ -2282,7 +6222,7 @@ endif ifeq ($(IS_DEP),) ifneq ($(wildcard $(RELX_CONFIG)),) -rel:: distclean-relx-rel relx-rel +rel:: relx-rel endif endif @@ -2294,7 +6234,7 @@ $(RELX): $(gen_verbose) $(call core_http_get,$(RELX),$(RELX_URL)) $(verbose) chmod +x $(RELX) -relx-rel: $(RELX) rel-deps +relx-rel: $(RELX) rel-deps app $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) distclean-relx-rel: -- cgit v1.2.3