From 078d686a0ac0aed212db97d73bd1e4a9387a4956 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= Date: Sun, 6 Jul 2014 13:10:35 +0200 Subject: Provide installable man pages make docs: generate Markdown and man pages in doc/ make install-docs: install man pages to be usable directly Docs are generated from the ezdoc files in doc/src/. --- .gitignore | 3 + Makefile | 52 + README.md | 22 +- doc/src/guide/architecture.ezdoc | 47 + doc/src/guide/broken_clients.ezdoc | 61 + doc/src/guide/cookies.ezdoc | 137 ++ doc/src/guide/erlang_beginners.ezdoc | 40 + doc/src/guide/erlang_web.ezdoc | 175 ++ doc/src/guide/getting_started.ezdoc | 306 +++ doc/src/guide/hooks.ezdoc | 82 + doc/src/guide/http_handlers.ezdoc | 145 ++ doc/src/guide/http_req_life.ezdoc | 147 ++ doc/src/guide/http_req_resp.png | Bin 0 -> 33228 bytes doc/src/guide/http_req_resp.svg | 558 +++++ doc/src/guide/index.ezdoc | 58 + doc/src/guide/introduction.ezdoc | 49 + doc/src/guide/loop_handlers.ezdoc | 141 ++ doc/src/guide/middlewares.ezdoc | 69 + doc/src/guide/modern_web.ezdoc | 215 ++ doc/src/guide/multipart_intro.ezdoc | 50 + doc/src/guide/multipart_req.ezdoc | 115 + doc/src/guide/req.ezdoc | 301 +++ doc/src/guide/req_body.ezdoc | 158 ++ doc/src/guide/resource_design.ezdoc | 220 ++ doc/src/guide/resp.ezdoc | 197 ++ doc/src/guide/rest_cond.png | Bin 0 -> 111628 bytes doc/src/guide/rest_cond.svg | 1656 ++++++++++++++ doc/src/guide/rest_conneg.png | Bin 0 -> 78133 bytes doc/src/guide/rest_conneg.svg | 1135 ++++++++++ doc/src/guide/rest_delete.png | Bin 0 -> 122185 bytes doc/src/guide/rest_delete.svg | 1718 +++++++++++++++ doc/src/guide/rest_flowcharts.ezdoc | 247 +++ doc/src/guide/rest_get_head.png | Bin 0 -> 99942 bytes doc/src/guide/rest_get_head.svg | 1523 +++++++++++++ doc/src/guide/rest_handlers.ezdoc | 135 ++ doc/src/guide/rest_options.png | Bin 0 -> 8539 bytes doc/src/guide/rest_options.svg | 387 ++++ doc/src/guide/rest_principles.ezdoc | 159 ++ doc/src/guide/rest_put_post_patch.png | Bin 0 -> 218656 bytes doc/src/guide/rest_put_post_patch.svg | 2856 +++++++++++++++++++++++++ doc/src/guide/rest_start.png | Bin 0 -> 118210 bytes doc/src/guide/rest_start.svg | 1468 +++++++++++++ doc/src/guide/routing.ezdoc | 249 +++ doc/src/guide/static_handlers.ezdoc | 167 ++ doc/src/guide/upgrade_protocol.ezdoc | 36 + doc/src/guide/ws_handlers.ezdoc | 222 ++ doc/src/guide/ws_protocol.ezdoc | 42 + doc/src/manual/cowboy.ezdoc | 101 + doc/src/manual/cowboy_app.ezdoc | 23 + doc/src/manual/cowboy_handler.ezdoc | 24 + doc/src/manual/cowboy_http_handler.ezdoc | 57 + doc/src/manual/cowboy_loop_handler.ezdoc | 91 + doc/src/manual/cowboy_middleware.ezdoc | 54 + doc/src/manual/cowboy_protocol.ezdoc | 84 + doc/src/manual/cowboy_req.ezdoc | 704 ++++++ doc/src/manual/cowboy_rest.ezdoc | 561 +++++ doc/src/manual/cowboy_router.ezdoc | 70 + doc/src/manual/cowboy_spdy.ezdoc | 43 + doc/src/manual/cowboy_static.ezdoc | 32 + doc/src/manual/cowboy_sub_protocol.ezdoc | 32 + doc/src/manual/cowboy_websocket.ezdoc | 36 + doc/src/manual/cowboy_websocket_handler.ezdoc | 133 ++ doc/src/manual/http_status_codes.ezdoc | 151 ++ doc/src/manual/index.ezdoc | 20 + erlang.mk | 11 - guide/architecture.md | 52 - guide/broken_clients.md | 65 - guide/cookies.md | 140 -- guide/erlang_beginners.md | 43 - guide/erlang_web.md | 181 -- guide/getting_started.md | 312 --- guide/hooks.md | 85 - guide/http_handlers.md | 149 -- guide/http_req_life.md | 153 -- guide/http_req_resp.png | Bin 33228 -> 0 bytes guide/http_req_resp.svg | 558 ----- guide/introduction.md | 53 - guide/loop_handlers.md | 148 -- guide/middlewares.md | 72 - guide/modern_web.md | 226 -- guide/multipart_intro.md | 53 - guide/multipart_req.md | 119 -- guide/req.md | 312 --- guide/req_body.md | 166 -- guide/resource_design.md | 233 -- guide/resp.md | 203 -- guide/rest_cond.png | Bin 111628 -> 0 bytes guide/rest_cond.svg | 1656 -------------- guide/rest_conneg.png | Bin 78133 -> 0 bytes guide/rest_conneg.svg | 1135 ---------- guide/rest_delete.png | Bin 122185 -> 0 bytes guide/rest_delete.svg | 1718 --------------- guide/rest_flowcharts.md | 255 --- guide/rest_get_head.png | Bin 99942 -> 0 bytes guide/rest_get_head.svg | 1523 ------------- guide/rest_handlers.md | 141 -- guide/rest_options.png | Bin 8539 -> 0 bytes guide/rest_options.svg | 387 ---- guide/rest_principles.md | 165 -- guide/rest_put_post_patch.png | Bin 218656 -> 0 bytes guide/rest_put_post_patch.svg | 2856 ------------------------- guide/rest_start.png | Bin 118210 -> 0 bytes guide/rest_start.svg | 1468 ------------- guide/routing.md | 255 --- guide/static_handlers.md | 172 -- guide/toc.md | 68 - guide/upgrade_protocol.md | 37 - guide/ws_handlers.md | 230 -- guide/ws_protocol.md | 45 - manual/cowboy.md | 100 - manual/cowboy_app.md | 26 - manual/cowboy_handler.md | 25 - manual/cowboy_http_handler.md | 57 - manual/cowboy_loop_handler.md | 91 - manual/cowboy_middleware.md | 56 - manual/cowboy_protocol.md | 65 - manual/cowboy_req.md | 670 ------ manual/cowboy_rest.md | 560 ----- manual/cowboy_router.md | 68 - manual/cowboy_spdy.md | 40 - manual/cowboy_static.md | 34 - manual/cowboy_sub_protocol.md | 34 - manual/cowboy_websocket.md | 40 - manual/cowboy_websocket_handler.md | 131 -- manual/http_status_codes.md | 180 -- manual/toc.md | 21 - 126 files changed, 17557 insertions(+), 17650 deletions(-) create mode 100644 doc/src/guide/architecture.ezdoc create mode 100644 doc/src/guide/broken_clients.ezdoc create mode 100644 doc/src/guide/cookies.ezdoc create mode 100644 doc/src/guide/erlang_beginners.ezdoc create mode 100644 doc/src/guide/erlang_web.ezdoc create mode 100644 doc/src/guide/getting_started.ezdoc create mode 100644 doc/src/guide/hooks.ezdoc create mode 100644 doc/src/guide/http_handlers.ezdoc create mode 100644 doc/src/guide/http_req_life.ezdoc create mode 100644 doc/src/guide/http_req_resp.png create mode 100644 doc/src/guide/http_req_resp.svg create mode 100644 doc/src/guide/index.ezdoc create mode 100644 doc/src/guide/introduction.ezdoc create mode 100644 doc/src/guide/loop_handlers.ezdoc create mode 100644 doc/src/guide/middlewares.ezdoc create mode 100644 doc/src/guide/modern_web.ezdoc create mode 100644 doc/src/guide/multipart_intro.ezdoc create mode 100644 doc/src/guide/multipart_req.ezdoc create mode 100644 doc/src/guide/req.ezdoc create mode 100644 doc/src/guide/req_body.ezdoc create mode 100644 doc/src/guide/resource_design.ezdoc create mode 100644 doc/src/guide/resp.ezdoc create mode 100644 doc/src/guide/rest_cond.png create mode 100644 doc/src/guide/rest_cond.svg create mode 100644 doc/src/guide/rest_conneg.png create mode 100644 doc/src/guide/rest_conneg.svg create mode 100644 doc/src/guide/rest_delete.png create mode 100644 doc/src/guide/rest_delete.svg create mode 100644 doc/src/guide/rest_flowcharts.ezdoc create mode 100644 doc/src/guide/rest_get_head.png create mode 100644 doc/src/guide/rest_get_head.svg create mode 100644 doc/src/guide/rest_handlers.ezdoc create mode 100644 doc/src/guide/rest_options.png create mode 100644 doc/src/guide/rest_options.svg create mode 100644 doc/src/guide/rest_principles.ezdoc create mode 100644 doc/src/guide/rest_put_post_patch.png create mode 100644 doc/src/guide/rest_put_post_patch.svg create mode 100644 doc/src/guide/rest_start.png create mode 100644 doc/src/guide/rest_start.svg create mode 100644 doc/src/guide/routing.ezdoc create mode 100644 doc/src/guide/static_handlers.ezdoc create mode 100644 doc/src/guide/upgrade_protocol.ezdoc create mode 100644 doc/src/guide/ws_handlers.ezdoc create mode 100644 doc/src/guide/ws_protocol.ezdoc create mode 100644 doc/src/manual/cowboy.ezdoc create mode 100644 doc/src/manual/cowboy_app.ezdoc create mode 100644 doc/src/manual/cowboy_handler.ezdoc create mode 100644 doc/src/manual/cowboy_http_handler.ezdoc create mode 100644 doc/src/manual/cowboy_loop_handler.ezdoc create mode 100644 doc/src/manual/cowboy_middleware.ezdoc create mode 100644 doc/src/manual/cowboy_protocol.ezdoc create mode 100644 doc/src/manual/cowboy_req.ezdoc create mode 100644 doc/src/manual/cowboy_rest.ezdoc create mode 100644 doc/src/manual/cowboy_router.ezdoc create mode 100644 doc/src/manual/cowboy_spdy.ezdoc create mode 100644 doc/src/manual/cowboy_static.ezdoc create mode 100644 doc/src/manual/cowboy_sub_protocol.ezdoc create mode 100644 doc/src/manual/cowboy_websocket.ezdoc create mode 100644 doc/src/manual/cowboy_websocket_handler.ezdoc create mode 100644 doc/src/manual/http_status_codes.ezdoc create mode 100644 doc/src/manual/index.ezdoc delete mode 100644 guide/architecture.md delete mode 100644 guide/broken_clients.md delete mode 100644 guide/cookies.md delete mode 100644 guide/erlang_beginners.md delete mode 100644 guide/erlang_web.md delete mode 100644 guide/getting_started.md delete mode 100644 guide/hooks.md delete mode 100644 guide/http_handlers.md delete mode 100644 guide/http_req_life.md delete mode 100644 guide/http_req_resp.png delete mode 100644 guide/http_req_resp.svg delete mode 100644 guide/introduction.md delete mode 100644 guide/loop_handlers.md delete mode 100644 guide/middlewares.md delete mode 100644 guide/modern_web.md delete mode 100644 guide/multipart_intro.md delete mode 100644 guide/multipart_req.md delete mode 100644 guide/req.md delete mode 100644 guide/req_body.md delete mode 100644 guide/resource_design.md delete mode 100644 guide/resp.md delete mode 100644 guide/rest_cond.png delete mode 100644 guide/rest_cond.svg delete mode 100644 guide/rest_conneg.png delete mode 100644 guide/rest_conneg.svg delete mode 100644 guide/rest_delete.png delete mode 100644 guide/rest_delete.svg delete mode 100644 guide/rest_flowcharts.md delete mode 100644 guide/rest_get_head.png delete mode 100644 guide/rest_get_head.svg delete mode 100644 guide/rest_handlers.md delete mode 100644 guide/rest_options.png delete mode 100644 guide/rest_options.svg delete mode 100644 guide/rest_principles.md delete mode 100644 guide/rest_put_post_patch.png delete mode 100644 guide/rest_put_post_patch.svg delete mode 100644 guide/rest_start.png delete mode 100644 guide/rest_start.svg delete mode 100644 guide/routing.md delete mode 100644 guide/static_handlers.md delete mode 100644 guide/toc.md delete mode 100644 guide/upgrade_protocol.md delete mode 100644 guide/ws_handlers.md delete mode 100644 guide/ws_protocol.md delete mode 100644 manual/cowboy.md delete mode 100644 manual/cowboy_app.md delete mode 100644 manual/cowboy_handler.md delete mode 100644 manual/cowboy_http_handler.md delete mode 100644 manual/cowboy_loop_handler.md delete mode 100644 manual/cowboy_middleware.md delete mode 100644 manual/cowboy_protocol.md delete mode 100644 manual/cowboy_req.md delete mode 100644 manual/cowboy_rest.md delete mode 100644 manual/cowboy_router.md delete mode 100644 manual/cowboy_spdy.md delete mode 100644 manual/cowboy_static.md delete mode 100644 manual/cowboy_sub_protocol.md delete mode 100644 manual/cowboy_websocket.md delete mode 100644 manual/cowboy_websocket_handler.md delete mode 100644 manual/http_status_codes.md delete mode 100644 manual/toc.md diff --git a/.gitignore b/.gitignore index d708ffc..b1fa0b5 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,9 @@ .erlang.mk.packages.* _rel deps +doc/man3 +doc/man7 +doc/markdown ebin logs relx diff --git a/Makefile b/Makefile index 168d8af..70eec4f 100644 --- a/Makefile +++ b/Makefile @@ -24,3 +24,55 @@ dep_gun = pkg://gun master # Standard targets. include erlang.mk + +# Documentation. + +dep_ezdoc = 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/README.md b/README.md index 8313bc6..2919e63 100644 --- a/README.md +++ b/README.md @@ -27,15 +27,23 @@ The SPDY implementation was sponsored by The project is currently sponsored by [Kato.im](https://kato.im). -Getting Started ---------------- +Online documentation +-------------------- - * [Read the guide](http://ninenines.eu/docs/en/cowboy/HEAD/guide) - * [Check the manual](http://ninenines.eu/docs/en/cowboy/HEAD/manual) - * Look at the examples in the `examples/` directory + * [User guide](http://ninenines.eu/docs/en/cowboy/HEAD/guide) + * [Function reference](http://ninenines.eu/docs/en/cowboy/HEAD/manual) -Support -------- +Offline documentation +--------------------- + + * While still online, run `make docs` + * Function reference man pages available in `doc/man3/` and `doc/man7/` + * Run `make install-docs` to install man pages on your system + * Full documentation in Markdown available in `doc/markdown/` + * Examples available in `examples/` + +Getting help +------------ * Official IRC Channel: #ninenines on irc.freenode.net * [Mailing Lists](http://lists.ninenines.eu) diff --git a/doc/src/guide/architecture.ezdoc b/doc/src/guide/architecture.ezdoc new file mode 100644 index 0000000..745505a --- /dev/null +++ b/doc/src/guide/architecture.ezdoc @@ -0,0 +1,47 @@ +::: 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/broken_clients.ezdoc b/doc/src/guide/broken_clients.ezdoc new file mode 100644 index 0000000..26568a3 --- /dev/null +++ b/doc/src/guide/broken_clients.ezdoc @@ -0,0 +1,61 @@ +::: 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], + {ok, Req2} = cowboy_req:reply(Status, Headers2, Body, Req), + Req2. +``` + +Note that SPDY clients do not have that particular issue +because the specification explicitly says all headers are +lowercase, unlike HTTP which allows any case but treats +them as case insensitive. + +:: Camel-case headers + +Sometimes it is desirable to keep the actual case used by +clients, for example when acting as a proxy between two broken +implementations. There is no easy solution for this other than +forking the project and editing the `cowboy_protocol` file +directly. + +:: Chunked transfer-encoding + +Sometimes an HTTP client advertises itself as HTTP/1.1 but +does not support chunked transfer-encoding. This is invalid +behavior, as HTTP/1.1 clients are required to support it. + +A simple workaround exists in these cases. By changing the +Req object response state to `waiting_stream`, Cowboy will +understand that it must use the identity transfer-encoding +when replying, just like if it was an HTTP/1.0 client. + +``` erlang +Req2 = cowboy_req:set(resp_state, waiting_stream). +``` diff --git a/doc/src/guide/cookies.ezdoc b/doc/src/guide/cookies.ezdoc new file mode 100644 index 0000000..fe9246c --- /dev/null +++ b/doc/src/guide/cookies.ezdoc @@ -0,0 +1,137 @@ +::: 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. + +You can read the value of a cookie. + +``` erlang +{CookieVal, Req2} = cowboy_req:cookie(<<"lang">>, Req). +``` + +You can also get a default value returned when the cookie +isn't set. + +``` erlang +{CookieVal, Req2} = cowboy_req:cookie(<<"lang">>, Req, <<"fr">>). +``` + +And you can obtain all cookies at once as a list of +key/value tuples. + +``` erlang +{AllCookies, Req2} = cowboy_req:cookies(Req). +``` diff --git a/doc/src/guide/erlang_beginners.ezdoc b/doc/src/guide/erlang_beginners.ezdoc new file mode 100644 index 0000000..f62543f --- /dev/null +++ b/doc/src/guide/erlang_beginners.ezdoc @@ -0,0 +1,40 @@ +::: 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. + +At the time of writing, the 2nd edition of the book is in beta, +and includes a few details about upcoming Erlang features that +cannot be used today. Choose the edition you want, then get +reading! diff --git a/doc/src/guide/erlang_web.ezdoc b/doc/src/guide/erlang_web.ezdoc new file mode 100644 index 0000000..42fcd34 --- /dev/null +++ b/doc/src/guide/erlang_web.ezdoc @@ -0,0 +1,175 @@ +::: 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.ezdoc b/doc/src/guide/getting_started.ezdoc new file mode 100644 index 0000000..ff34699 --- /dev/null +++ b/doc/src/guide/getting_started.ezdoc @@ -0,0 +1,306 @@ +::: 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. + +:: Application skeleton + +Let's start by creating this application. We will simply call it +`hello_erlang`. This application will have the following directory +structure: + +``` bash +hello_erlang/ + src/ + hello_erlang.app.src + hello_erlang_app.erl + hello_erlang_sup.erl + hello_handler.erl + erlang.mk + Makefile + relx.config +``` + +Once the release is generated, we will also have the following +files added: + +``` bash +hello_erlang/ + ebin/ + hello_erlang.app + hello_erlang_app.beam + hello_erlang_sup.beam + hello_handler.beam + _rel/ + relx +``` + +As you can probably guess, the `.app.src` file end up becoming +the `.app` file, and the `.erl` files are compiled into `.beam`. +Then, the whole release will be copied into the `_rel/` directory. + +The `.app` file contains various informations about the application. +It contains its name, a description, a version, a list of modules, +default configuration and more. + +Using a build system like ^"erlang.mk^https://github.com/extend/erlang.mk^, +the list of modules will be included automatically in the `.app` file, +so you don't need to manually put them in your `.app.src` file. + +For generating the release, we will use ^"relx^https://github.com/erlware/relx +as it is a much simpler alternative to the tool coming with Erlang. + +First, create the `hello_erlang` directory. It should have the same name +as the application within it. Then we create the `src` directory inside +it, which will contain the source code for our application. + +``` bash +$ mkdir hello_erlang +$ cd hello_erlang +$ mkdir src +``` + +Let's first create the `hello_erlang.app.src` file. It should be pretty +straightforward for the most part. You can use the following template +and change what you like in it. + +``` erlang +{application, hello_erlang, [ + {description, "Hello world with Cowboy!"}, + {vsn, "0.1.0"}, + {modules, []}, + {registered, [hello_erlang_sup]}, + {applications, [ + kernel, + stdlib, + cowboy + ]}, + {mod, {hello_erlang_app, []}}, + {env, []} +]}. +``` + +The `modules` line will be replaced with the list of modules during +compilation. Make sure to leave this line even if you do not use it +directly. + +The `registered` value indicates which processes are registered by this +application. You will often only register the top-level supervisor +of the application. + +The `applications` value lists the applications that must be started +for this application to work. The Erlang release will start all the +applications listed here automatically. + +The `mod` value defines how the application should be started. Erlang +will use the `hello_erlang_app` module for starting the application. + +The `hello_erlang_app` module is what we call an application behavior. +The application behavior must define two functions: `start/2` and +`stop/1`, for starting and stopping the application. A typical +application module would look like this: + +``` erlang +-module(hello_erlang_app). +-behavior(application). + +-export([start/2]). +-export([stop/1]). + +start(_Type, _Args) -> + hello_erlang_sup:start_link(). + +stop(_State) -> + ok. +``` + +That's not enough however. Since we are building a Cowboy based +application, we also need to initialize Cowboy when we start our +application. + +:: Setting up Cowboy + +Cowboy does nothing by default. + +Cowboy uses Ranch for handling the connections and provides convenience +functions to start Ranch listeners. + +The `cowboy:start_http/4` function starts a listener for HTTP connections +using the TCP transport. The `cowboy:start_https/4` function starts a +listener for HTTPS connections using the SSL transport. + +Listeners are a group of processes that are used to accept and manage +connections. The processes used specifically for accepting connections +are called acceptors. The number of acceptor processes is unrelated to +the maximum number of connections Cowboy can handle. Please refer to +the ^"Ranch guide^http://ninenines.eu/docs/en/ranch/HEAD/guide/ +for in-depth information. + +Listeners are named. They spawn a given number of acceptors, listen for +connections using the given transport options and pass along the protocol +options to the connection processes. The protocol options must include +the dispatch list for routing requests to handlers. + +The dispatch list is explained in greater details in the +^"Routing^routing^ chapter. For the purpose of this example +we will simply map all URLs to our handler `hello_handler`, +using the wildcard `_` for both the hostname and path parts +of the URL. + +This is what the `hello_erlang_app:start/2` function looks like +with Cowboy initialized. + +``` erlang +start(_Type, _Args) -> + Dispatch = cowboy_router:compile([ + %% {URIHost, list({URIPath, Handler, Opts})} + {'_', [{'_', hello_handler, []}]} + ]), + %% Name, NbAcceptors, TransOpts, ProtoOpts + cowboy:start_http(my_http_listener, 100, + [{port, 8080}], + [{env, [{dispatch, Dispatch}]}] + ), + hello_erlang_sup:start_link(). +``` + +Do note that we told Cowboy to start listening on port 8080. +You can change this value if needed. + +Our application doesn't need to start any process, as Cowboy +will automatically start processes for every incoming +connections. We are still required to have a top-level supervisor +however, albeit a fairly small one. + +``` erlang +-module(hello_erlang_sup). +-behavior(supervisor). + +-export([start_link/0]). +-export([init/1]). + +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +init([]) -> + {ok, {{one_for_one, 10, 10}, []}}. +``` + +Finally, we need to write the code for handling incoming requests. + +:: Handling HTTP requests + +Cowboy features many kinds of handlers. For this simple example, +we will just use the plain HTTP handler, which has three callback +functions: `init/3`, `handle/2` and `terminate/3`. You can find more +information about the arguments and possible return values of these +callbacks in the +^"cowboy_http_handler function reference^http://ninenines.eu/docs/en/cowboy/HEAD/manual/cowboy_http_handler^. + +Our handler will only send a friendly hello back to the client. + +``` erlang +-module(hello_handler). +-behavior(cowboy_http_handler). + +-export([init/3]). +-export([handle/2]). +-export([terminate/3]). + +init(_Type, Req, _Opts) -> + {ok, Req, undefined_state}. + +handle(Req, State) -> + {ok, Req2} = cowboy_req:reply(200, [ + {<<"content-type">>, <<"text/plain">>} + ], <<"Hello World!">>, Req), + {ok, Req2, State}. + +terminate(_Reason, _Req, _State) -> + ok. +``` + +The `Req` variable above is the Req object, which allows the developer +to obtain information about the request and to perform a reply. +Its usage is documented in the +^"cowboy_req function reference^http://ninenines.eu/docs/en/cowboy/HEAD/manual/cowboy_req^. + +The code for our application is ready, so let's build a release! + +:: Compiling + +First we need to download `erlang.mk`. + +``` bash +$ wget https://raw.github.com/extend/erlang.mk/master/erlang.mk +$ ls +src/ +erlang.mk +``` + +Then we need to create a Makefile that will include `erlang.mk` +for building our application. We need to define the Cowboy +dependency in the Makefile. Thankfully `erlang.mk` already +knows where to find Cowboy as it features a package index, +so we can just tell it to look there. + +``` Makefile +PROJECT = hello_erlang + +DEPS = cowboy +dep_cowboy = pkg://cowboy master + +include erlang.mk +``` + +Note that when creating production nodes you will most likely +want to use a specific version of Cowboy instead of `master`, +and properly test your release every time you update Cowboy. + +If you type `make` in a shell now, your application should build +as expected. If you get compilation errors, double check that you +haven't made any typo when creating the previous files. + +``` bash +$ make +``` + +:: Generating the release + +That's not all however, as we want to create a working release. +For that purpose, we need to create a `relx.config` file. When +this file exists, `erlang.mk` will automatically download `relx` +and build the release when you type `make`. + +In the `relx.config` file, we only need to tell `relx` that +we want the release to include the `hello_erlang` application, +and that we want an extended start script for convenience. +`relx` will figure out which other applications are required +by looking into the `.app` files for dependencies. + +``` erlang +{release, {hello_erlang, "1"}, [hello_erlang]}. +{extended_start_script, true}. +``` + +The `release` value is used to specify the release name, its +version, and the applications to be included. + +We can now build and start the release. + +``` bash +$ make +$ ./_rel/hello_erlang/bin/hello_erlang console +``` + +If you then access `http://localhost:8080` using your browser, +you should receive a nice greet! diff --git a/doc/src/guide/hooks.ezdoc b/doc/src/guide/hooks.ezdoc new file mode 100644 index 0000000..edef971 --- /dev/null +++ b/doc/src/guide/hooks.ezdoc @@ -0,0 +1,82 @@ +::: Hooks + +Cowboy provides two hooks. `onrequest` is called once the request +line and headers have been received. `onresponse` is called just +before sending the response. + +:: Onrequest + +The `onrequest` hook is called as soon as Cowboy finishes fetching +the request headers. It occurs before any other processing, including +routing. It can be used to perform any modification needed on the +request object before continuing with the processing. If a reply is +sent inside this hook, then Cowboy will move on to the next request, +skipping any subsequent handling. + +This hook is a function that takes a request object as argument, +and returns a request object. This function MUST NOT crash. Cowboy +will not send any reply if a crash occurs in this function. + +You can specify the `onrequest` hook when creating the listener, +inside the request options. + +``` erlang +cowboy:start_http(my_http_listener, 100, + [{port, 8080}], + [ + {env, [{dispatch, Dispatch}]}, + {onrequest, fun ?MODULE:debug_hook/1} + ] +). +``` + +The following hook function prints the request object everytime a +request is received. This can be useful for debugging, for example. + +``` erlang +debug_hook(Req) -> + erlang:display(Req), + Req. +``` + +Make sure to always return the last request object obtained. + +:: 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 like the `onrequest` hook, this function MUST NOT crash. +Cowboy may or may not send a reply if this function crashes. If a reply +is sent, the hook MUST explicitly provide all headers that are needed. + +You can specify the `onresponse` hook when creating the listener. + +``` 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))}), + {ok, Req2} = cowboy_req:reply(404, Headers2, Body, Req), + Req2; +custom_404_hook(_, _, _, Req) -> + Req. +``` + +Again, make sure to always return the last request object obtained. diff --git a/doc/src/guide/http_handlers.ezdoc b/doc/src/guide/http_handlers.ezdoc new file mode 100644 index 0000000..9a450a6 --- /dev/null +++ b/doc/src/guide/http_handlers.ezdoc @@ -0,0 +1,145 @@ +::: Handling plain HTTP requests + +The simplest way to handle a request is by writing a +plain HTTP handler. It is modeled after Erlang/OTP's +gen_server behaviour, although simplified, as Cowboy +will simply call the three callbacks sequentially. + +:: Initialization + +The first callback, `init/3`, is common to all handlers, +as it is used to identify the type of handler. Plain +HTTP handlers just return `ok`. + +``` erlang +init(_Type, Req, _Opts) -> + {ok, Req, no_state}. +``` + +This function receives the name of the transport and +protocol modules used for processing the request. +They can be used to quickly dismiss requests. For +example the following handler will crash when accessed +using TCP instead of SSL. + +``` erlang +init({ssl, _}, Req, _Opts) -> + {ok, Req, no_state}. +``` + +This function also receives the options associated with +this route that you configured previously. If your +handler does not use options, then it is recommended +you match the value `[]` directly to quickly detect +configuration errors. + +``` erlang +init(_Type, Req, []) -> + {ok, Req, no_state}. +``` + +You do not need to validate the options unless they +are user configured. If they are, and there's a +configuration error, you may choose to crash. For +example, this will crash if the required `lang` +option is not found. + +``` erlang +init(_Type, Req, Opts) -> + {_, _Lang} = lists:keyfind(lang, 1, Opts), + {ok, Req, no_state}. +``` + +If your users are unlikely to figure out the issue +without explanations, then you should send a more +meaningful error back to the user. Since we already +replied to the user, there's no need for us to +continue with the handler code, so we use the +`shutdown` return value to stop early. + +``` erlang +init(_Type, Req, Opts) -> + case lists:keyfind(lang, 1, Opts) of + false -> + {ok, Req2} = cowboy_req:reply(500, [ + {<<"content-type">>, <<"text/plain">>} + ], "Missing option 'lang'.", Req), + {shutdown, Req2, no_state}; + _ -> + {ok, Req, no_state} + end. +``` + +Once the options have been validated, we can use them +safely. So we need to pass them onward to the rest of +the handler. That's what the third element of the return +tuple, the state, is for. + +We recommend that you create a state record for this. +The record will make your handler code clearer and +will allow you to better use Dialyzer for type checking. + +``` erlang +-record(state, { + lang :: en | fr + %% More fields here. +}). + +init(_Type, Req, Opts) -> + {_, Lang} = lists:keyfind(lang, 1, Opts), + {ok, Req, #state{lang=Lang}}. +``` + +:: Handling the request + +The second callback, `handle/2`, is specific to plain HTTP +handlers. It's where you, wait for it, handle the request. + +A handle function that does nothing would look like this: + +``` erlang +handle(Req, State) -> + {ok, Req, State}. +``` + +There's no other return value. To obtain information about +the request, or send a response, you would use the Req object +here. The Req object is documented in its own chapter. + +The following handle function will send a fairly original response. + +``` erlang +handle(Req, State) -> + {ok, Req2} = cowboy_req:reply(200, [ + {<<"content-type">>, <<"text/plain">>} + ], <<"Hello World!">>, Req), + {ok, Req2, State}. +``` + +:: Cleaning up + +The third and last callback, `terminate/3`, will most likely +be empty in your handler. + +``` 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. + +The chances of any of this happening in your handler are pretty +thin however. The use of the process dictionary is discouraged +in Erlang code in general. And if you need to use timers, monitors +or to receive messages, you are better off with a loop handler, +a different kind of handler meant specifically for this use. + +This function is still available should you need it. It will +always be called. diff --git a/doc/src/guide/http_req_life.ezdoc b/doc/src/guide/http_req_life.ezdoc new file mode 100644 index 0000000..5fd8486 --- /dev/null +++ b/doc/src/guide/http_req_life.ezdoc @@ -0,0 +1,147 @@ +::: The life of a request + +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 +{ok, 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/http_req_resp.png b/doc/src/guide/http_req_resp.png new file mode 100644 index 0000000..e38935f Binary files /dev/null and b/doc/src/guide/http_req_resp.png differ diff --git a/doc/src/guide/http_req_resp.svg b/doc/src/guide/http_req_resp.svg new file mode 100644 index 0000000..0cfa0ae --- /dev/null +++ b/doc/src/guide/http_req_resp.svg @@ -0,0 +1,558 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + some text + acceptor + parser + router + some text + onrequest + handler + middlewares + some text + client + + + + + reply + onresponse + + diff --git a/doc/src/guide/index.ezdoc b/doc/src/guide/index.ezdoc new file mode 100644 index 0000000..38b2ac0 --- /dev/null +++ b/doc/src/guide/index.ezdoc @@ -0,0 +1,58 @@ +::: Cowboy User Guide + +The Cowboy User Guide explores the modern Web and how to make +best use of Cowboy for writing powerful web applications. + +:: Introducing Cowboy + +* ^"Introduction^introduction +* ^"The modern Web^modern_web +* ^"Erlang and the Web^erlang_web +* ^"Erlang for beginners^erlang_beginners +* ^"Getting started^getting_started + +:: HTTP + +* ^"The life of a request^http_req_life +* ^"Routing^routing +* ^"Handling plain HTTP requests^http_handlers +* ^"The Req object^req +* ^"Reading the request body^req_body +* ^"Sending a response^resp +* ^"Using cookies^cookies + +:: Multipart + +* ^"Introduction to multipart^multipart_intro +* ^"Multipart requests^multipart_req + +:: Static files + +* ^"Static handler^static_handlers + +:: 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 + +:: Server push + +* ^"Loop handlers^loop_handlers + +:: Pluggable interface + +* ^"Middlewares^middlewares +* ^"Protocol upgrades^upgrade_protocol +* ^"Hooks^hooks + +:: Internals + +* ^"Architecture^architecture +* ^"Dealing with broken clients^broken_clients diff --git a/doc/src/guide/introduction.ezdoc b/doc/src/guide/introduction.ezdoc new file mode 100644 index 0000000..7f77fcc --- /dev/null +++ b/doc/src/guide/introduction.ezdoc @@ -0,0 +1,49 @@ +::: 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 + +No Erlang knowledge is required for reading this guide. The reader will +be introduced to Erlang concepts and redirected to reference material +whenever necessary. + +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 R15B+. + +Cowboy may be compiled on earlier Erlang versions with small source code +modifications but there is no guarantee that it will work as expected. + +:: 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.ezdoc b/doc/src/guide/loop_handlers.ezdoc new file mode 100644 index 0000000..fba4feb --- /dev/null +++ b/doc/src/guide/loop_handlers.ezdoc @@ -0,0 +1,141 @@ +::: 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/3` and `terminate/3` callbacks which work the same as +for plain HTTP handlers. + +:: Initialization + +The `init/3` function must return a `loop` tuple to enable +loop handler behavior. This tuple may optionally contain +a timeout value and/or the atom `hibernate` to make the +process enter hibernation until a message is received. + +This snippet enables the loop handler. + +``` erlang +init(_Type, Req, _Opts) -> + {loop, Req, undefined_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(_Type, Req, _Opts) -> + {loop, Req, undefined_state, 30000, hibernate}. +``` + +:: Receive loop + +Once initialized, Cowboy will wait for messages to arrive +in the process' mailbox. When a message arrives, Cowboy +calls the `info/3` function with the message, the Req object +and the handler's state. + +The following snippet sends a reply when it receives a +`reply` message from another process, or waits for another +message otherwise. + +``` erlang +info({reply, Body}, Req, State) -> + {ok, Req2} = cowboy_req:reply(200, [], Body, Req), + {ok, Req2, State}; +info(_Msg, Req, State) -> + {loop, 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 `ok` tuple should be returned. +This will instruct Cowboy to end the request. + +Otherwise a `loop` tuple should be returned. + +:: Streaming loop + +Another common case well suited for loop handlers is +streaming data received in the form of Erlang messages. +This can be done by initiating a chunked reply in the +`init/3` callback and then using `cowboy_req:chunk/2` +every time a message is received. + +The following snippet does exactly that. As you can see +a chunk is sent every time a `chunk` message is received, +and the loop is stopped by sending an `eof` message. + +``` erlang +init(_Type, Req, _Opts) -> + {ok, Req2} = cowboy_req:chunked_reply(200, [], Req), + {loop, Req2, undefined_state}. + +info(eof, Req, State) -> + {ok, Req, State}; +info({chunk, Chunk}, Req, State) -> + ok = cowboy_req:chunk(Chunk, Req), + {loop, Req, State}; +info(_Msg, Req, State) -> + {loop, Req, State}. +``` + +:: Cleaning up + +It is recommended that you set the connection header to +`close` when replying, as this process may be reused for +a subsequent request. + +Please refer to the ^"HTTP handlers chapter^http_handlers +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.ezdoc b/doc/src/guide/middlewares.ezdoc new file mode 100644 index 0000000..e33abfb --- /dev/null +++ b/doc/src/guide/middlewares.ezdoc @@ -0,0 +1,69 @@ +::: 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 four different values: + +* `{ok, Req, Env}` to continue the request processing +* `{suspend, Module, Function, Args}` to hibernate +* `{halt, Req}` to stop processing and move on to the next request +* `{error, StatusCode, Req}` to reply an error and close the socket + +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.ezdoc b/doc/src/guide/modern_web.ezdoc new file mode 100644 index 0000000..1c2c342 --- /dev/null +++ b/doc/src/guide/modern_web.ezdoc @@ -0,0 +1,215 @@ +::: 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_intro.ezdoc b/doc/src/guide/multipart_intro.ezdoc new file mode 100644 index 0000000..b9a7fa9 --- /dev/null +++ b/doc/src/guide/multipart_intro.ezdoc @@ -0,0 +1,50 @@ +::: Introduction to multipart + +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. + +:: 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. diff --git a/doc/src/guide/multipart_req.ezdoc b/doc/src/guide/multipart_req.ezdoc new file mode 100644 index 0000000..a807e48 --- /dev/null +++ b/doc/src/guide/multipart_req.ezdoc @@ -0,0 +1,115 @@ +::: Multipart requests + +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. + +:: 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 +{ok, {<<"multipart">>, <<"form-data">>, _}, Req2} + = 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/req.ezdoc b/doc/src/guide/req.ezdoc new file mode 100644 index 0000000..9501158 --- /dev/null +++ b/doc/src/guide/req.ezdoc @@ -0,0 +1,301 @@ +::: 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. + +It also caches the result of operations performed +on the immutable state. That means that some calls +will give a result much faster when called many times. + +:: Overview of the cowboy_req interface + +The `cowboy_req` interface is divided in four groups +of functions, each having a well defined return type +signature common to the entire group. + +The first group, access functions, will always return +`{Value, Req}`. The group includes all the following +functions: `binding/{2,3}`, `bindings/1`, `body_length/1`, +`cookie/{2,3}`, `cookies/1`, `header/{2,3}`, `headers/1`, +`host/1`, `host_info/1`, `host_url/1`, `meta/{2,3}`, +`method/1`, `path/1`, `path_info/1`, `peer/1`, `port/1`, +`qs/1`, `qs_val/{2,3}`, `qs_vals/1`, `url/1`, `version/1`. + +The second group, question functions, will always return +a `boolean()`. The group includes the following three +functions: `has_body/1`, `has_resp_body/1`, `has_resp_header/2`. + +The third group contains the functions that manipulate +the socket or perform operations that may legitimately fail. +They may return `{Result, Req}`, `{Result, Value, Req}` +or `{error, atom()}`. This includes the following functions: +`body/{1,2}`, `body_qs/{1,2}`, `chunked_reply/{2,3}`, +`parse_header/{2,3}`, `part/{1,2}`, `part_body/{1,2}` +and `reply/{2,3,4}`. Finally, the group also includes the +`chunk/2` and `continue/1` functions which always return `ok`. + +The final group modifies the Req object state without +performing any immediate operations. As these functions +can't fail, they always return a new `Req` directly. +This includes the following functions: `compact/1`, +`delete_resp_header/2`, `set_meta/3`, `set_resp_body/2`, +`set_resp_body_fun/{2,3}`, `set_resp_cookie/4`, `set_resp_header/3`. + +This chapter covers most of the first group, plus a few other +functions. The next few chapters cover cookies handling, reading +the request body and sending a response. + +:: 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, Req2} = 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, Req2} = cowboy_req:host(Req), +{Port, Req3} = cowboy_req:port(Req2), +{Path, Req4} = cowboy_req:path(Req3). +``` + +The version used by the client can of course also be obtained. + +``` erlang +{Version, Req2} = 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, Req2} = 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, Req2} = 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, Req2} = 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, Req2} = 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, Req2} = cowboy_req:path_info(Req). +``` + +:: Query string + +The query string can be obtained directly. + +``` erlang +{Qs, Req2} = cowboy_req:qs(Req). +``` + +You can also requests only one value. + +``` erlang +{QsVal, Req2} = cowboy_req:qs_val(<<"lang">>, Req). +``` + +If that value is optional, you can define a default to simplify +your task. + +``` erlang +{QsVal, Req2} = cowboy_req:qs_val(<<"lang">>, Req, <<"en">>). +``` + +Finally, you can obtain all query string values. + +``` erlang +{AllValues, Req2} = cowboy_req:qs_vals(Req). +``` + +:: Request URL + +You can reconstruct the full URL of the resource. + +``` erlang +{URL, Req2} = cowboy_req:url(Req). +``` + +You can also obtain only the base of the URL, excluding the +path and query string. + +``` erlang +{BaseURL, Req2} = 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, Req2} = cowboy_req:header(<<"content-type">>, Req). +``` + +You can of course set a default in case the header is missing. + +``` erlang +{HeaderVal, Req2} + = cowboy_req:header(<<"content-type">>, Req, <<"text/plain">>). +``` + +And also obtain all headers. + +``` erlang +{AllHeaders, Req2} = cowboy_req:headers(Req). +``` + +To parse the previous header, simply call `parse_header/{2,3}` +where you would call `header/{2,3}` otherwise. Note that the +return value changes and includes the result of the operation +as the first element of the returned tuple. A successful parse +returns `ok`. + +``` erlang +{ok, ParsedVal, Req2} = cowboy_req:parse_header(<<"content-type">>, Req). +``` + +When Cowboy doesn't know how to parse the given header, the +result of the operation will be `undefined` and the string value +will be returned instead. + +``` erlang +{undefined, HeaderVal, Req2} + = cowboy_req:parse_header(<<"unicorn-header">>, Req). +``` + +When parsing fails, `{error, Reason}` is returned instead. + +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 +{ok, ParsedVal, Req2} + = cowboy_req:parse_header(<<"content-type">>, Req, + {<<"text">>, <<"plain">>, []}). +``` + +The list of known headers and default values is defined in the +manual. Also note that the result of parsing is cached, so +calling this function multiple times for the same values will +not have a significant performance impact. + +:: 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, Req2} = cowboy_req:meta(websocket_version, Req). +``` + +You can change the default value if needed. + +``` erlang +{MetaVal, Req2} = 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}, Req2} = cowboy_req:peer(Req). +``` + +:: Reducing the memory footprint + +When you are done reading information from the request object +and know you are not going to access it anymore, for example +when using long-polling or Websocket, you can use the `compact/1` +function to remove most of the data from the request object and +free memory. + +``` erlang +Req2 = cowboy_req:compact(Req). +``` + +You will still be able to send a reply if needed. diff --git a/doc/src/guide/req_body.ezdoc b/doc/src/guide/req_body.ezdoc new file mode 100644 index 0000000..44f32f8 --- /dev/null +++ b/doc/src/guide/req_body.ezdoc @@ -0,0 +1,158 @@ +::: 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, Req2} = 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, 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.ezdoc b/doc/src/guide/resource_design.ezdoc new file mode 100644 index 0000000..67cb09b --- /dev/null +++ b/doc/src/guide/resource_design.ezdoc @@ -0,0 +1,220 @@ +::: 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.ezdoc b/doc/src/guide/resp.ezdoc new file mode 100644 index 0000000..28f2544 --- /dev/null +++ b/doc/src/guide/resp.ezdoc @@ -0,0 +1,197 @@ +::: 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 +{ok, 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 +{ok, 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 +{ok, 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 +{ok, Req2} = cowboy_req:reply(200, [ + {<<"content-type">>, <<"text/plain">>} +], "Hello world!", Req). +``` + +Here is the same example but sending HTML this time. + +``` erlang +{ok, 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 +{ok, Req2} = cowboy_req:chunked_reply(200, Req), +ok = cowboy_req:chunk("Hello...", Req2), +ok = cowboy_req:chunk("chunked...", Req2), +ok = 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 +{ok, Req2} = cowboy_req:chunked_reply(200, [ + {<<"content-type">>, <<"text/html">>} +], Req), +ok = cowboy_req:chunk("Hello world!", Req2), +ok = 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_cond.png b/doc/src/guide/rest_cond.png new file mode 100644 index 0000000..64cda34 Binary files /dev/null and b/doc/src/guide/rest_cond.png differ diff --git a/doc/src/guide/rest_cond.svg b/doc/src/guide/rest_cond.svg new file mode 100644 index 0000000..542ae17 --- /dev/null +++ b/doc/src/guide/rest_cond.svg @@ -0,0 +1,1656 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + some text + some text + has if-unmodified-since? + has if-none-match? + some text + ... + generate_etag + has if-modified-since? + has if-match? + generate_etag + last_modified + + true + match* + true + not modified* + true + no match* + + + + + false + false, orinvalid + modified* + false + + + + + + 412 precondition failed + + middlewares + + + + + + + + + + + + + + + + + no match* + + + + + + date is in the future? + + + + + + + + + + last_modified + + + + + + 304 not modified + + ... + false, orinvalid + match* + + method is GET/HEAD? + true + false + true + false + true + modified* + not modified* + + + + + + generate_etag + + + + + + expires + + diff --git a/doc/src/guide/rest_conneg.png b/doc/src/guide/rest_conneg.png new file mode 100644 index 0000000..65ecdcf Binary files /dev/null and b/doc/src/guide/rest_conneg.png differ diff --git a/doc/src/guide/rest_conneg.svg b/doc/src/guide/rest_conneg.svg new file mode 100644 index 0000000..247567a --- /dev/null +++ b/doc/src/guide/rest_conneg.svg @@ -0,0 +1,1135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + some text + some text + has accept-language? + has accept-charset? + some text + start + charsets_provided + variances + has accept? + content_types_provided + languages_provided + + true + provided* + true + provided* + true + provided* + + + + + false + false + not provided* + false + not provided* + + + + + + 406 not acceptable + + middlewares + + + + + + + + + + + + + + + + + not provided* + + ... + + diff --git a/doc/src/guide/rest_delete.png b/doc/src/guide/rest_delete.png new file mode 100644 index 0000000..56a861c Binary files /dev/null and b/doc/src/guide/rest_delete.png differ diff --git a/doc/src/guide/rest_delete.svg b/doc/src/guide/rest_delete.svg new file mode 100644 index 0000000..2f5513c --- /dev/null +++ b/doc/src/guide/rest_delete.svg @@ -0,0 +1,1718 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + some text + some text + delete_completed + has response body? + some text + conneg + multiple_choices + resource_exists + delete_resource + + true + false + + + + + false + + + + + + middlewares + + + + + true + true + + + + + + cond + + 300 multiple choices + + 200 OK + + + + + + has if-match? + false + + + + + + + + + + previously_existed + + 404 not found + false + + + + + + + + + + moved_permanently + + + + + + 412 precondition failed + true + true* + false + + 301 moved permanently + + + + + + + + + + moved_temporarily + true* + false + + 307 moved temporarily + + 410 gone + + + + + false + + 202 accepted + + 204 no content + true + true + + 500 internal server error + false + true + false + + diff --git a/doc/src/guide/rest_flowcharts.ezdoc b/doc/src/guide/rest_flowcharts.ezdoc new file mode 100644 index 0000000..7da3721 --- /dev/null +++ b/doc/src/guide/rest_flowcharts.ezdoc @@ -0,0 +1,247 @@ +::: 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 `accept_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_get_head.png b/doc/src/guide/rest_get_head.png new file mode 100644 index 0000000..efee892 Binary files /dev/null and b/doc/src/guide/rest_get_head.png differ diff --git a/doc/src/guide/rest_get_head.svg b/doc/src/guide/rest_get_head.svg new file mode 100644 index 0000000..c78e939 --- /dev/null +++ b/doc/src/guide/rest_get_head.svg @@ -0,0 +1,1523 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + some text + some text + last_modified + ProvideResource + some text + conneg + multiple_choices + resource_exists + generate_etag + expires + + true + false + + + + + false + + + + + + middlewares + + + + + true + true + + + + + + cond + + 300 multiple choices + + 200 OK + + + + + + has if-match? + false + + + + + + + + + + previously_existed + + 404 not found + false + + + + + + + + + + moved_permanently + + + + + + 412 precondition failed + true + true* + false + + 301 moved permanently + + + + + + + + + + moved_temporarily + true* + false + + 307 moved temporarily + + 410 gone + + + + + + diff --git a/doc/src/guide/rest_handlers.ezdoc b/doc/src/guide/rest_handlers.ezdoc new file mode 100644 index 0000000..ee3e5aa --- /dev/null +++ b/doc/src/guide/rest_handlers.ezdoc @@ -0,0 +1,135 @@ +::: REST handlers + +REST is implemented in Cowboy as a protocol upgrade. Once upgraded, +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 requests. + +:: Initialization + +First, the `init/3` callback is called. This callback is common +to all handlers. To use REST for the current request, this function +must return an `upgrade` tuple. + +``` erlang +init({tcp, http}, Req, Opts) -> + {upgrade, protocol, cowboy_rest}. +``` + +Cowboy will then switch to the REST protocol and start executing +the state machine, starting from `rest_init/2` if it's defined, +and ending with `rest_terminate/2` also if 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. + +When the request starts being processed, Cowboy will call the +`rest_init/2` function if it is defined, with the Req object +and the handler options as arguments. This function must return +`{ok, Req, State}` where `State` is the handler's state that all +subsequent callbacks will receive. + +At the end of every request, the special callback `rest_terminate/2` +will be called if it is defined. It cannot be used to send a reply, +and must always return `ok`. + +All other callbacks are resource callbacks. They all take two +arguments, the Req object and the State, and return a three-element +tuple of the form `{Value, Req, State}`. + +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. + +All callbacks can also return `{halt, Req, State}` to stop execution +of the request, at which point `rest_terminate/2` will be called. + +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_content_type `true` +| 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_options.png b/doc/src/guide/rest_options.png new file mode 100644 index 0000000..90fd6f0 Binary files /dev/null and b/doc/src/guide/rest_options.png differ diff --git a/doc/src/guide/rest_options.svg b/doc/src/guide/rest_options.svg new file mode 100644 index 0000000..496c050 --- /dev/null +++ b/doc/src/guide/rest_options.svg @@ -0,0 +1,387 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + some text + some text + some text + start + options + 200 OK + + + + + + + middlewares + + diff --git a/doc/src/guide/rest_principles.ezdoc b/doc/src/guide/rest_principles.ezdoc new file mode 100644 index 0000000..1d54594 --- /dev/null +++ b/doc/src/guide/rest_principles.ezdoc @@ -0,0 +1,159 @@ +::: 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/rest_put_post_patch.png b/doc/src/guide/rest_put_post_patch.png new file mode 100644 index 0000000..4afca9e Binary files /dev/null and b/doc/src/guide/rest_put_post_patch.png differ diff --git a/doc/src/guide/rest_put_post_patch.svg b/doc/src/guide/rest_put_post_patch.svg new file mode 100644 index 0000000..263cc94 --- /dev/null +++ b/doc/src/guide/rest_put_post_patch.svg @@ -0,0 +1,2856 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + some text + some text + some text + conneg + resource_exists + + true + + + + + false + + + + + + middlewares + + + + + true + + + + + + + + + + + + + + + cond + + + + + + has if-match? + false + + + + + + method is POST/PATCH? + true + + + + + + + + + + + + + + + + + + + method is POST? + + 412 precondition failed + + + + + + + + + + + + + + previously_existed + + + + + + 404 not found + false + + + + + + + + + true* + false + + 301 moved permanently + + + + + + + + + + moved_temporarily + true* + false + + 307 moved temporarily + + 400 bad request + + + + + true + + + + + + allow_missing_post + + method is POST? + allow_missing_post + + + + + + method is PUT? + + + + + + + + + + is_conflict + true + + 409 conflict + + + + + + content_types_accepted + + AcceptResource + + + + + + + + + + new resource? + + + + + + + + + + new resource? + + 201 created + + 303 see other + + + + + + + + + + has resp location? + + + + + + + + + + + has resp body? + + + + + + + + + + multiple_choices + false + + 300 multiple choices + + 200 OK + 204 no content + true + + + + + true + + moved_permanently + + 410 gone + false + true + false + false + false + false + + + + + true + + + + + true, URI* + + + + + true + false + true + true + false + true + false + true + false + false + false + + + + + + + + true + + + + + false + true + + diff --git a/doc/src/guide/rest_start.png b/doc/src/guide/rest_start.png new file mode 100644 index 0000000..7f26464 Binary files /dev/null and b/doc/src/guide/rest_start.png differ diff --git a/doc/src/guide/rest_start.svg b/doc/src/guide/rest_start.svg new file mode 100644 index 0000000..d75e1cc --- /dev/null +++ b/doc/src/guide/rest_start.svg @@ -0,0 +1,1468 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + some text + some text + uri_too_long + malformed_request + some text + init + is_authorized + forbidden + valid_content_headers + known_content_type + valid_entity_length + ... + service_available + known_methods + allowed_methods + + true + known* + false + allowed* + false + true + false + true + true + true + + + + + false + unknown* + true + unallowed* + true + false* + true + false + false + false + + 503 service unavailable + + + + + + + + + + 501 not implemented + 414 request URI too long + 405 method not allowed + 400 bad request + 401 unauthorized + 403 forbidden + 501 not implemented + 415 unsupported media type + 413 request entity too large + + middlewares + + diff --git a/doc/src/guide/routing.ezdoc b/doc/src/guide/routing.ezdoc new file mode 100644 index 0000000..e7b43f2 --- /dev/null +++ b/doc/src/guide/routing.ezdoc @@ -0,0 +1,249 @@ +::: 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. + +They are always given as a two or three elements tuple, where the first +element is the name of the binding, the second element is the constraint's +name, and the optional third element is the constraint's arguments. + +The following constraints are currently defined: + +* {Name, int} +* {Name, function, fun ((Value) -> true | {true, NewValue} | false)} + +The `int` constraint will check if the binding is a binary string +representing an integer, and if it is, will convert the value to integer. + +The `function` constraint will pass the binding value to a user specified +function that receives the binary value as its only argument and must +return whether it fulfills the constraint, optionally modifying the value. +The value thus returned can be of any type. + +Note that constraint functions SHOULD be pure and MUST NOT crash. + +:: 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_handlers.ezdoc b/doc/src/guide/static_handlers.ezdoc new file mode 100644 index 0000000..f5eaac3 --- /dev/null +++ b/doc/src/guide/static_handlers.ezdoc @@ -0,0 +1,167 @@ +::: Static handler + +The static handler is a built-in REST handler for serving files. +It is available 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/upgrade_protocol.ezdoc b/doc/src/guide/upgrade_protocol.ezdoc new file mode 100644 index 0000000..eebce74 --- /dev/null +++ b/doc/src/guide/upgrade_protocol.ezdoc @@ -0,0 +1,36 @@ +::: Protocol upgrades + +Cowboy features many different handlers, each for different purposes. +All handlers have a common entry point: the `init/3` function. + +The default handler type is the simple HTTP handler. + +To switch to a different protocol, you must perform a protocol +upgrade. This is what is done for Websocket and REST and is +explained in details in the respective chapters. + +You can also create your own protocol on top of Cowboy and use +the protocol upgrade mechanism to switch to it. + +For example, if you create the `my_protocol` module implementing +the `cowboy_sub_protocol` behavior, then you can upgrade to it +by simply returning the module name from `init/3`. + +``` erlang +init(_, _, _Opts) -> + {upgrade, protocol, my_protocol}. +``` + +The `cowboy_sub_protocol` behavior only requires one callback, +`upgrade/4`. It receives the Req object, the middleware environment, +and the handler and options for this request. This is the same +module as the `init/3` function and the same options that were +passed to it. + +``` erlang +upgrade(Req, Env, Handler, HandlerOpts) -> + %% ... +``` + +This callback is expected to behave like a middleware. Please +see the corresponding chapter for more information. diff --git a/doc/src/guide/ws_handlers.ezdoc b/doc/src/guide/ws_handlers.ezdoc new file mode 100644 index 0000000..0de7910 --- /dev/null +++ b/doc/src/guide/ws_handlers.ezdoc @@ -0,0 +1,222 @@ +::: 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/3` callback is called. This callback is common +to all handlers. To establish a Websocket connection, this function +must return an `upgrade` tuple. + +``` erlang +init(_, Req, Opts) -> + {upgrade, protocol, cowboy_websocket}. +``` + +It is also possible to return an update Req object and options +using the longer form of this tuple. + +``` erlang +init(_Type, Req, Opts) -> + {upgrade, protocol, cowboy_websocket, Req, Opts}. +``` + +Upon receiving this tuple, Cowboy will switch to the code +that handles Websocket connections. It does not immediately +perform the handshake however. First, it calls the `websocket_init/3` +callback. + +This function must be used to initialize the state, and can +also be used to register the process, start a timer, etc. +As long as the function returns an `ok` tuple, then Cowboy +performs the Websocket handshake. + +``` erlang +websocket_init(_Type, Req, _Opts) -> + {ok, Req, #state{}}. +``` + +A `shutdown` tuple can be returned to refuse to perform the +handshake. When doing so, Cowboy will send a `400 Bad Request` +response to the client and close the connection. + +``` erlang +websocket_init(_Type, Req, _Opts) -> + {shutdown, Req}. +``` + +It is also possible to perform a `cowboy_req:reply/{2,3,4}` +before returning a `shutdown` tuple, allowing you to override +the response sent back to the client. + +Note that browser support for handling Websocket connection +failures may vary. + +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 +websocket_init(_Type, Req, _Opts) -> + case cowboy_req:parse_header(<<"sec-websocket-protocol">>, Req) of + {ok, undefined, Req2} -> + {ok, Req, #state{}}; + {ok, Subprotocols, Req2} -> + case lists:keymember(<<"mychat2">>, 1, Subprotocols) of + true -> + Req3 = cowboy:set_resp_header(<<"sec-websocket-protocol">>, + <<"mychat2">>, Req2), + {ok, Req3, #state{}}; + false -> + {shutdown, Req2} + end + end. +``` + +It is not recommended to wait too long inside the `websocket_init/3` +function. Any extra initialization may be done after returning by +sending yourself a message before doing anything. Any message sent +to `self()` from `websocket_init/3` 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 +websocket_init(_Type, Req, _Opts) -> + self() ! post_init, + %% Register process here... + {ok, 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, shutdown +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, shutdown +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. 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 +websocket_init(_Type, Req, _Opts) -> + {ok, 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.ezdoc b/doc/src/guide/ws_protocol.ezdoc new file mode 100644 index 0000000..15aea2c --- /dev/null +++ b/doc/src/guide/ws_protocol.ezdoc @@ -0,0 +1,42 @@ +::: 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/3` 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.ezdoc b/doc/src/manual/cowboy.ezdoc new file mode 100644 index 0000000..a207afe --- /dev/null +++ b/doc/src/manual/cowboy.ezdoc @@ -0,0 +1,101 @@ +::: cowboy + +The `cowboy` module provides convenience functions for +manipulating Ranch listeners. + +:: Types + +: 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. + +: onrequest_fun() = fun((cowboy_req:req()) -> cowboy_req:req()) + +Fun called immediately after receiving a request. + +It can perform any operation on the Req object, including +reading the request body or replying. If a reply is sent, +the processing of the request ends here, before any middleware +is executed. + +: 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.ezdoc b/doc/src/manual/cowboy_app.ezdoc new file mode 100644 index 0000000..2e2b877 --- /dev/null +++ b/doc/src/manual/cowboy_app.ezdoc @@ -0,0 +1,23 @@ +::: 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.ezdoc b/doc/src/manual/cowboy_handler.ezdoc new file mode 100644 index 0000000..0495f28 --- /dev/null +++ b/doc/src/manual/cowboy_handler.ezdoc @@ -0,0 +1,24 @@ +::: 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 + +:: Types + +None. + +:: Exports + +None. diff --git a/doc/src/manual/cowboy_http_handler.ezdoc b/doc/src/manual/cowboy_http_handler.ezdoc new file mode 100644 index 0000000..6776598 --- /dev/null +++ b/doc/src/manual/cowboy_http_handler.ezdoc @@ -0,0 +1,57 @@ +::: cowboy_http_handler + +The `cowboy_http_handler` behaviour defines the interface used +by plain HTTP handlers. + +Unless noted otherwise, the callbacks will be executed sequentially. + +:: Types + +None. + +:: Callbacks + +: init({TransportName, ProtocolName}, Req, Opts) + -> {ok, Req, State} | {shutdown, Req, State} + +Types: + +* TransportName = tcp | ssl | atom() +* ProtocolName = http | atom() +* Req = cowboy_req:req() +* Opts = any() +* State = any() + +Initialize the state for this request. + +The `shutdown` return value can be used to skip the `handle/2` +call entirely. + +: handle(Req, State) -> {ok, Req, State} + +Types: + +* Req = cowboy_req:req() +* State = any() + +Handle the request. + +This callback is where the request is handled and a response +should be sent. If a response is not sent, Cowboy will send +a `204 No Content` response automatically. + +: terminate(Reason, Req, State) -> ok + +Types: + +* Reason = {normal, shutdown} | {error, atom()} +* 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. diff --git a/doc/src/manual/cowboy_loop_handler.ezdoc b/doc/src/manual/cowboy_loop_handler.ezdoc new file mode 100644 index 0000000..0811a9a --- /dev/null +++ b/doc/src/manual/cowboy_loop_handler.ezdoc @@ -0,0 +1,91 @@ +::: cowboy_loop_handler + +The `cowboy_loop_handler` behaviour defines the interface used +by HTTP handlers that do not send a response directly, instead +requiring a receive loop to process Erlang messages. + +This interface is best fit for long-polling types of requests. + +The `init/3` callback will always be called, followed by zero +or more calls to `info/3`. The `terminate/3` callback will +always be called last. + +:: Types + +None. + +:: Callbacks + +: init({TransportName, ProtocolName}, Req, Opts) + -> {loop, Req, State} + | {loop, Req, State, hibernate} + | {loop, Req, State, Timeout} + | {loop, Req, State, Timeout, hibernate} + | {shutdown, Req, State} + +Types: + +* TransportName = tcp | ssl | atom() +* ProtocolName = http | atom() +* Req = cowboy_req:req() +* Opts = any() +* State = any() +* Timeout = timeout() + +Initialize the state for this request. + +This callback will typically be used to register this process +to an event manager or a message queue in order to receive +the messages the handler wants to process. + +The receive loop will run for a duration of up to `Timeout` +milliseconds after it last received data from the socket, +at which point it will stop and send a `204 No Content` reply. +By default this value is set to `infinity`. It is recommended +to either set this value or ensure by any other mechanism +that the handler will be closed after a certain period of +inactivity. + +The `hibernate` option will hibernate the process until it +starts receiving messages. + +The `shutdown` return value can be used to skip the receive +loop entirely. + +: info(Info, Req, State) -> {ok, Req, State} | {loop, Req, State} + | {loop, Req, State, hibernate} + +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 `ok` 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. + +: terminate(Reason, Req, State) -> ok + +Types: + +* Reason = {normal, shutdown} | {normal, timeout} | {error, closed} | {error, overflow} | {error, atom()} +* Req = cowboy_req:req() +* State = any() + +Perform any necessary cleanup of the state. + +This callback will typically unregister from any event manager +or message queue it registered to in `init/3`. + +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. diff --git a/doc/src/manual/cowboy_middleware.ezdoc b/doc/src/manual/cowboy_middleware.ezdoc new file mode 100644 index 0000000..065139c --- /dev/null +++ b/doc/src/manual/cowboy_middleware.ezdoc @@ -0,0 +1,54 @@ +::: 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} + | {halt, Req} + | {error, StatusCode, Req} + +Types: + +* Req = cowboy_req:req() +* Env = env() +* Module = module() +* Function = atom() +* Args = [any()] +* StatusCode = cowboy:http_status() + +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 `halt` 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. + +The `error` return value sends an error response identified +by the `StatusCode` and then proceeds to terminate the +connection. Middlewares that haven't been executed yet +will not be called. diff --git a/doc/src/manual/cowboy_protocol.ezdoc b/doc/src/manual/cowboy_protocol.ezdoc new file mode 100644 index 0000000..6813295 --- /dev/null +++ b/doc/src/manual/cowboy_protocol.ezdoc @@ -0,0 +1,84 @@ +::: 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()]} + | {onrequest, cowboy:onrequest_fun()} + | {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. + +: onrequest (undefined) + +Fun called every time a request is received. + +: onresponse (undefined) + +Fun called every time a response is sent. + +: timeout (5000) + +Time in ms with no requests before Cowboy closes the connection. + +:: Exports + +None. diff --git a/doc/src/manual/cowboy_req.ezdoc b/doc/src/manual/cowboy_req.ezdoc new file mode 100644 index 0000000..beac1f4 --- /dev/null +++ b/doc/src/manual/cowboy_req.ezdoc @@ -0,0 +1,704 @@ +::: 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, Req}` +* action: `{Result, Req} | {Result, Value, Req} | {error, atom()}` +* modification: `Req` +* question: `boolean()` + +The only exception is the `chunk/2` function which may return `ok`. + +Whenever `Req` is returned, you must use this returned value and +ignore any previous you may have had. This value contains various +state informations which are necessary for Cowboy to do some lazy +evaluation or cache results where appropriate. + +All functions which perform an action should only be called once. +This includes reading the request body or replying. Cowboy will +generally throw an error on the second call. + +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, Req2} + +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}], Req2} + +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). + +: cookie(Name, Req) -> cookie(Name, Req, undefined) +: cookie(Name, Req, Default) -> {Value, Req2} + +Types: + +* Name = binary() +* Default = any() +* Value = binary() | Default + +Return the value for the given cookie. + +Cookie names are case sensitive. + +: cookies(Req) -> {[{Name, Value}], Req2} + +Types: + +* Name = binary() +* Value = binary() + +Return all cookies. + +: header(Name, Req) -> header(Name, Req, undefined) +: header(Name, Req, Default) -> {Value, Req2} + +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, Req2} + +Types: + +* Headers = cowboy:http_headers() + +Return all headers. + +: host(Req) -> {Host, Req2} + +Types: + +* Host = binary() + +Return the requested host. + +: host_info(Req) -> {HostInfo, Req2} + +Types: + +* HostInfo = cowboy_router:tokens() | undefined + +Return the extra tokens from matching against `...` during routing. + +: host_url(Req) -> {HostURL, Req2} + +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. This includes +the `onrequest` hook. + +: meta(Name, Req) -> meta(Name, Req, undefined) +: meta(Name, Req, Default) -> {Value, Req2} + +Types: + +* Name = atom() +* Default = any() +* Value = any() + +Return metadata about the request. + +: method(Req) -> {Method, Req2} + +Types: + +* Method = binary() + +Return the method. + +Methods are case sensitive. Standard methods are always uppercase. + +: parse_header(Name, Req) -> +: parse_header(Name, Req, Default) -> {ok, ParsedValue, Req2} + | {undefined, Value, Req2} | {error, badarg} + +Types: + +* Name = binary() +* Default = any() +* ParsedValue - see below +* Value = any() + +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 +| +| 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. Others will return `{error, badarg}` if the header +value is empty. + +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. + +: path(Req) -> {Path, Req2} + +Types: + +* Path = binary() + +Return the requested path. + +: path_info(Req) -> {PathInfo, Req2} + +Types: + +* PathInfo = cowboy_router:tokens() | undefined + +Return the extra tokens from matching against `...` during routing. + +: peer(Req) -> {Peer, Req2} + +Types: + +* Peer = {inet:ip_address(), inet:port_number()} + +Return the client's IP address and port number. + +: port(Req) -> {Port, Req2} + +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, Req2} + +Types: + +* QueryString = binary() + +Return the request's query string. + +: qs_val(Name, Req) -> qs_val(Name, Req, undefined) +: qs_val(Name, Req, Default) -> {Value, Req2} + +Types: + +* Name = binary() +* Default = any() +* Value = binary() | true + +Return a value from the request's query string. + +The value `true` will be returned when the name was found +in the query string without an associated value. + +: qs_vals(Req) -> {[{Name, Value}], Req2} + +Types: + +* Name = binary() +* Value = binary() | true + +Return the request's query string as a list of tuples. + +The value `true` will be returned when a name was found +in the query string without an associated value. + +: 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, Req2} + +Types: + +* URL = binary() | undefined + +Return the requested URL. + +This function will always return `undefined` until the +`cowboy_router` middleware has been executed. This includes +the `onrequest` hook. + +: version(Req) -> {Version, Req2} + +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} | {error, Reason} + +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, Req2} + +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} | {error, Reason} + +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 | {error, Reason} + +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) -> {ok, 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 | {error, Reason} + +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) -> {ok, 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 | {error, atom()}) + +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. + +:: Misc. exports + +: compact(Req) -> Req2 + +Remove any non-essential data from the Req object. + +Long-lived connections usually only need to manipulate the +Req object at initialization. Compacting allows saving up +memory by discarding extraneous information. diff --git a/doc/src/manual/cowboy_rest.ezdoc b/doc/src/manual/cowboy_rest.ezdoc new file mode 100644 index 0000000..4d13530 --- /dev/null +++ b/doc/src/manual/cowboy_rest.ezdoc @@ -0,0 +1,561 @@ +::: cowboy_rest + +The `cowboy_rest` module implements REST semantics on top of +the HTTP protocol. + +This module cannot be described as a behaviour due to most of +the callbacks it defines being optional. It has the same +semantics as a behaviour otherwise. + +The only mandatory callback is `init/3`, needed to perform +the protocol upgrade. + +:: Types + +None. + +:: 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`. + +:: Callbacks + +: init({TransportName, ProtocolName}, Req, Opts) + -> {upgrade, protocol, cowboy_rest} + | {upgrade, protocol, cowboy_rest, Req, Opts} + +Types: + +* TransportName = tcp | ssl | atom() +* ProtocolName = http | atom() +* Req = cowboy_req:req() +* Opts = any() + +Upgrade the protocol to `cowboy_rest`. + +This is the only mandatory callback. + +: rest_init(Req, Opts) -> {ok, Req, State} + +Types: + +* Req = cowboy_req:req() +* Opts = any() +* State = any() + +Initialize the state for this request. + +: rest_terminate(Req, State) -> ok + +Types: + +* 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. + +: Callback(Req, State) -> {Value, Req, State} | {halt, 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 `halt` 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` or +`{true, URL}` may be returned. If an URL is provided, the +response will redirect the client to the location of the +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 + +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_content_type + +* Methods: all +* Value type: boolean() +* Default value: true + +Return whether the content-type is known. + +This function determines if the server understands the +content-type, regardless of its use by the resource. + +: 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 + +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 + +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.ezdoc b/doc/src/manual/cowboy_router.ezdoc new file mode 100644 index 0000000..f76acf6 --- /dev/null +++ b/doc/src/manual/cowboy_router.ezdoc @@ -0,0 +1,70 @@ +::: 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. + +: constraints() = [IntConstraint | FunConstraint] + +Types: + +* IntConstraint = {atom(), int} +* FunConstraint = {atom(), function, Fun} +* Fun = fun((binary()) -> true | {true, any()} | false) + +List of constraints to apply to the bindings. + +The int constraint will convert the binding to an integer. +The fun constraint allows writing custom code for checking +the bindings. Returning a new value from that fun allows +replacing the current binding with a new value. + +: dispatch_rules() - opaque to the user + +Rules for dispatching request used by Cowboy. + +: routes() = [{Host, Paths} | {Host, constraints(), Paths}] + +Types: + +* Host = Path = '_' | iodata() +* Paths = [{Path, Handler, Opts} | {Path, constraints(), 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.ezdoc b/doc/src/manual/cowboy_spdy.ezdoc new file mode 100644 index 0000000..51a2110 --- /dev/null +++ b/doc/src/manual/cowboy_spdy.ezdoc @@ -0,0 +1,43 @@ +::: cowboy_spdy + +The `cowboy_spdy` module implements SPDY/3 as a Ranch protocol. + +:: Types + +: opts() = [{env, cowboy_middleware:env()} + | {middlewares, [module()]} + | {onrequest, cowboy:onrequest_fun()} + | {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. + +: onrequest (undefined) + +Fun called every time a request is received. + +: onresponse (undefined) + +Fun called every time a response is sent. + +:: Exports + +None. diff --git a/doc/src/manual/cowboy_static.ezdoc b/doc/src/manual/cowboy_static.ezdoc new file mode 100644 index 0000000..ee122c1 --- /dev/null +++ b/doc/src/manual/cowboy_static.ezdoc @@ -0,0 +1,32 @@ +::: 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, atom(), string() | binary()} + | {dir, atom(), 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.ezdoc b/doc/src/manual/cowboy_sub_protocol.ezdoc new file mode 100644 index 0000000..2ad0cf7 --- /dev/null +++ b/doc/src/manual/cowboy_sub_protocol.ezdoc @@ -0,0 +1,32 @@ +::: cowboy_sub_protocol + +The `cowboy_sub_protocol` behaviour defines the interface used +by modules that implement a protocol on top of HTTP. + +:: Types + +None. + +:: Callbacks + +: upgrade(Req, Env, Handler, Opts) + -> {ok, Req, Env} + | {suspend, Module, Function, Args} + | {halt, Req} + | {error, StatusCode, 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.ezdoc b/doc/src/manual/cowboy_websocket.ezdoc new file mode 100644 index 0000000..59a6248 --- /dev/null +++ b/doc/src/manual/cowboy_websocket.ezdoc @@ -0,0 +1,36 @@ +::: cowboy_websocket + +The `cowboy_websocket` module implements the Websocket protocol. + +The callbacks for websocket handlers are defined in the manual +for the `cowboy_websocket_handler` behaviour. + +:: Types + +: close_code() = 1000..4999 + +Reason for closing the connection. + +: frame() = close | ping | pong + | {text | binary | close | ping | pong, iodata()} + | {close, close_code(), iodata()} + +Frames that can be sent to the client. + +:: 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. + +:: Exports + +None. diff --git a/doc/src/manual/cowboy_websocket_handler.ezdoc b/doc/src/manual/cowboy_websocket_handler.ezdoc new file mode 100644 index 0000000..0d31a54 --- /dev/null +++ b/doc/src/manual/cowboy_websocket_handler.ezdoc @@ -0,0 +1,133 @@ +::: cowboy_websocket_handler + +The `cowboy_websocket_handler` behaviour defines the interface used +by Websocket handlers. + +The `init/3` and `websocket_init/3` callbacks will always be called, +followed by zero or more calls to `websocket_handle/3` and +`websocket_info/3`. The `websocket_terminate/3` will always +be called last. + +:: Types + +None. + +:: Callbacks + +: init({TransportName, ProtocolName}, Req, Opts) + -> {upgrade, protocol, cowboy_websocket} + | {upgrade, protocol, cowboy_websocket, Req, Opts} + +Types: + +* TransportName = tcp | ssl | atom() +* ProtocolName = http | atom() +* Req = cowboy_req:req() +* Opts = any() + +Upgrade the protocol to `cowboy_websocket`. + +: websocket_init(TransportName, Req, Opts) + -> {ok, Req, State} + | {ok, Req, State, hibernate} + | {ok, Req, State, Timeout} + | {ok, Req, State, Timeout, hibernate} + | {shutdown, Req} + +Types: + +* TransportName = tcp | ssl | atom() +* Req = cowboy_req:req() +* Opts = any() +* State = any() +* Timeout = timeout() + +Initialize the state for this session. + +This function is called before the upgrade to Websocket occurs. +It can be used to negotiate Websocket protocol extensions +with the client. It will typically be used to register this process +to an event manager or a message queue in order to receive +the messages the handler wants to process. + +The connection will stay up for a duration of up to `Timeout` +milliseconds after it last received data from the socket, +at which point it will stop and close the connection. +By default this value is set to `infinity`. It is recommended +to either set this value or ensure by any other mechanism +that the handler will be closed after a certain period of +inactivity. + +The `hibernate` option will hibernate the process until it +starts receiving either data from the Websocket connection +or Erlang messages. + +The `shutdown` return value can be used to close the connection +before upgrading to Websocket. + +: websocket_handle(InFrame, Req, State) + -> {ok, Req, State} + | {ok, Req, State, hibernate} + | {reply, OutFrame | [OutFrame], Req, State} + | {reply, OutFrame | [OutFrame], Req, State, hibernate} + | {shutdown, Req, State} + +Types: + +* InFrame = {text | binary | ping | pong, binary()} +* Req = cowboy_req:req() +* State = any() +* OutFrame = cowboy_websocket:frame() + +Handle the data received from the Websocket connection. + +This function will be called every time data is received +from the Websocket connection. + +The `shutdown` 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} + | {shutdown, Req, State} + +Types: + +* Info = any() +* Req = cowboy_req:req() +* State = any() +* OutFrame = cowboy_websocket: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 `shutdown` 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. + +: websocket_terminate(Reason, Req, State) -> ok + +Types: + +* Reason = {normal, shutdown | timeout} | {remote, closed} | {remote, cowboy_websocket:close_code(), binary()} | {error, badencoding | badframe | closed | atom()} +* Req = cowboy_req:req() +* State = any() + +Perform any necessary cleanup of the state. + +The connection will be closed and the process stopped right +after this call. diff --git a/doc/src/manual/http_status_codes.ezdoc b/doc/src/manual/http_status_codes.ezdoc new file mode 100644 index 0000000..4d24b20 --- /dev/null +++ b/doc/src/manual/http_status_codes.ezdoc @@ -0,0 +1,151 @@ +::: 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 new file mode 100644 index 0000000..e364e90 --- /dev/null +++ b/doc/src/manual/index.ezdoc @@ -0,0 +1,20 @@ +::: Cowboy Function Reference + +The function reference documents the public interface of Cowboy. + +* ^"The Cowboy Application^cowboy_app +* ^cowboy +* ^cowboy_handler +* ^cowboy_http_handler +* ^cowboy_loop_handler +* ^cowboy_middleware +* ^cowboy_protocol +* ^cowboy_req +* ^cowboy_rest +* ^cowboy_router +* ^cowboy_spdy +* ^cowboy_static +* ^cowboy_sub_protocol +* ^cowboy_websocket +* ^cowboy_websocket_handler +* ^"HTTP status codes^http_status_codes diff --git a/erlang.mk b/erlang.mk index d29632c..8c62069 100644 --- a/erlang.mk +++ b/erlang.mk @@ -199,17 +199,6 @@ clean-deps: fi ; \ done -# Documentation. - -EDOC_OPTS ?= - -docs: clean-docs - $(gen_verbose) erl -noshell \ - -eval 'edoc:application($(PROJECT), ".", [$(EDOC_OPTS)]), init:stop().' - -clean-docs: - $(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info - # Tests. $(foreach dep,$(TEST_DEPS),$(eval $(call dep_target,$(dep)))) diff --git a/guide/architecture.md b/guide/architecture.md deleted file mode 100644 index b799a37..0000000 --- a/guide/architecture.md +++ /dev/null @@ -1,52 +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/guide/broken_clients.md b/guide/broken_clients.md deleted file mode 100644 index ac9924c..0000000 --- a/guide/broken_clients.md +++ /dev/null @@ -1,65 +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], - {ok, Req2} = cowboy_req:reply(Status, Headers2, Body, Req), - Req2. -``` - -Note that SPDY clients do not have that particular issue -because the specification explicitly says all headers are -lowercase, unlike HTTP which allows any case but treats -them as case insensitive. - -Camel-case headers ------------------- - -Sometimes it is desirable to keep the actual case used by -clients, for example when acting as a proxy between two broken -implementations. There is no easy solution for this other than -forking the project and editing the `cowboy_protocol` file -directly. - -Chunked transfer-encoding -------------------------- - -Sometimes an HTTP client advertises itself as HTTP/1.1 but -does not support chunked transfer-encoding. This is invalid -behavior, as HTTP/1.1 clients are required to support it. - -A simple workaround exists in these cases. By changing the -Req object response state to `waiting_stream`, Cowboy will -understand that it must use the identity transfer-encoding -when replying, just like if it was an HTTP/1.0 client. - -``` erlang -Req2 = cowboy_req:set(resp_state, waiting_stream). -``` diff --git a/guide/cookies.md b/guide/cookies.md deleted file mode 100644 index bfc8651..0000000 --- a/guide/cookies.md +++ /dev/null @@ -1,140 +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. - -You can read the value of a cookie. - -``` erlang -{CookieVal, Req2} = cowboy_req:cookie(<<"lang">>, Req). -``` - -You can also get a default value returned when the cookie -isn't set. - -``` erlang -{CookieVal, Req2} = cowboy_req:cookie(<<"lang">>, Req, <<"fr">>). -``` - -And you can obtain all cookies at once as a list of -key/value tuples. - -``` erlang -{AllCookies, Req2} = cowboy_req:cookies(Req). -``` diff --git a/guide/erlang_beginners.md b/guide/erlang_beginners.md deleted file mode 100644 index 7778dee..0000000 --- a/guide/erlang_beginners.md +++ /dev/null @@ -1,43 +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. - -At the time of writing, the 2nd edition of the book is in beta, -and includes a few details about upcoming Erlang features that -cannot be used today. Choose the edition you want, then get -reading! diff --git a/guide/erlang_web.md b/guide/erlang_web.md deleted file mode 100644 index fa3d922..0000000 --- a/guide/erlang_web.md +++ /dev/null @@ -1,181 +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/guide/getting_started.md b/guide/getting_started.md deleted file mode 100644 index a5f811d..0000000 --- a/guide/getting_started.md +++ /dev/null @@ -1,312 +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. - -Application skeleton --------------------- - -Let's start by creating this application. We will simply call it -`hello_erlang`. This application will have the following directory -structure: - -``` -hello_erlang/ - src/ - hello_erlang.app.src - hello_erlang_app.erl - hello_erlang_sup.erl - hello_handler.erl - erlang.mk - Makefile - relx.config -``` - -Once the release is generated, we will also have the following -files added: - -``` -hello_erlang/ - ebin/ - hello_erlang.app - hello_erlang_app.beam - hello_erlang_sup.beam - hello_handler.beam - _rel/ - relx -``` - -As you can probably guess, the `.app.src` file end up becoming -the `.app` file, and the `.erl` files are compiled into `.beam`. -Then, the whole release will be copied into the `_rel/` directory. - -The `.app` file contains various informations about the application. -It contains its name, a description, a version, a list of modules, -default configuration and more. - -Using a build system like [erlang.mk](https://github.com/extend/erlang.mk), -the list of modules will be included automatically in the `.app` file, -so you don't need to manually put them in your `.app.src` file. - -For generating the release, we will use [relx](https://github.com/erlware/relx) -as it is a much simpler alternative to the tool coming with Erlang. - -First, create the `hello_erlang` directory. It should have the same name -as the application within it. Then we create the `src` directory inside -it, which will contain the source code for our application. - -``` bash -$ mkdir hello_erlang -$ cd hello_erlang -$ mkdir src -``` - -Let's first create the `hello_erlang.app.src` file. It should be pretty -straightforward for the most part. You can use the following template -and change what you like in it. - -``` erlang -{application, hello_erlang, [ - {description, "Hello world with Cowboy!"}, - {vsn, "0.1.0"}, - {modules, []}, - {registered, [hello_erlang_sup]}, - {applications, [ - kernel, - stdlib, - cowboy - ]}, - {mod, {hello_erlang_app, []}}, - {env, []} -]}. -``` - -The `modules` line will be replaced with the list of modules during -compilation. Make sure to leave this line even if you do not use it -directly. - -The `registered` value indicates which processes are registered by this -application. You will often only register the top-level supervisor -of the application. - -The `applications` value lists the applications that must be started -for this application to work. The Erlang release will start all the -applications listed here automatically. - -The `mod` value defines how the application should be started. Erlang -will use the `hello_erlang_app` module for starting the application. - -The `hello_erlang_app` module is what we call an application behavior. -The application behavior must define two functions: `start/2` and -`stop/1`, for starting and stopping the application. A typical -application module would look like this: - -``` erlang --module(hello_erlang_app). --behavior(application). - --export([start/2]). --export([stop/1]). - -start(_Type, _Args) -> - hello_erlang_sup:start_link(). - -stop(_State) -> - ok. -``` - -That's not enough however. Since we are building a Cowboy based -application, we also need to initialize Cowboy when we start our -application. - -Setting up Cowboy ------------------ - -Cowboy does nothing by default. - -Cowboy uses Ranch for handling the connections and provides convenience -functions to start Ranch listeners. - -The `cowboy:start_http/4` function starts a listener for HTTP connections -using the TCP transport. The `cowboy:start_https/4` function starts a -listener for HTTPS connections using the SSL transport. - -Listeners are a group of processes that are used to accept and manage -connections. The processes used specifically for accepting connections -are called acceptors. The number of acceptor processes is unrelated to -the maximum number of connections Cowboy can handle. Please refer to -the [Ranch guide](http://ninenines.eu/docs/en/ranch/HEAD/guide/) -for in-depth information. - -Listeners are named. They spawn a given number of acceptors, listen for -connections using the given transport options and pass along the protocol -options to the connection processes. The protocol options must include -the dispatch list for routing requests to handlers. - -The dispatch list is explained in greater details in the -[Routing](routing.md) chapter. For the purpose of this example -we will simply map all URLs to our handler `hello_handler`, -using the wildcard `_` for both the hostname and path parts -of the URL. - -This is what the `hello_erlang_app:start/2` function looks like -with Cowboy initialized. - -``` erlang -start(_Type, _Args) -> - Dispatch = cowboy_router:compile([ - %% {URIHost, list({URIPath, Handler, Opts})} - {'_', [{'_', hello_handler, []}]} - ]), - %% Name, NbAcceptors, TransOpts, ProtoOpts - cowboy:start_http(my_http_listener, 100, - [{port, 8080}], - [{env, [{dispatch, Dispatch}]}] - ), - hello_erlang_sup:start_link(). -``` - -Do note that we told Cowboy to start listening on port 8080. -You can change this value if needed. - -Our application doesn't need to start any process, as Cowboy -will automatically start processes for every incoming -connections. We are still required to have a top-level supervisor -however, albeit a fairly small one. - -``` erlang --module(hello_erlang_sup). --behavior(supervisor). - --export([start_link/0]). --export([init/1]). - -start_link() -> - supervisor:start_link({local, ?MODULE}, ?MODULE, []). - -init([]) -> - {ok, {{one_for_one, 10, 10}, []}}. -``` - -Finally, we need to write the code for handling incoming requests. - -Handling HTTP requests ----------------------- - -Cowboy features many kinds of handlers. For this simple example, -we will just use the plain HTTP handler, which has three callback -functions: `init/3`, `handle/2` and `terminate/3`. You can find more -information about the arguments and possible return values of these -callbacks in the -[cowboy_http_handler function reference](http://ninenines.eu/docs/en/cowboy/HEAD/manual/cowboy_http_handler). - -Our handler will only send a friendly hello back to the client. - -``` erlang --module(hello_handler). --behavior(cowboy_http_handler). - --export([init/3]). --export([handle/2]). --export([terminate/3]). - -init(_Type, Req, _Opts) -> - {ok, Req, undefined_state}. - -handle(Req, State) -> - {ok, Req2} = cowboy_req:reply(200, [ - {<<"content-type">>, <<"text/plain">>} - ], <<"Hello World!">>, Req), - {ok, Req2, State}. - -terminate(_Reason, _Req, _State) -> - ok. -``` - -The `Req` variable above is the Req object, which allows the developer -to obtain information about the request and to perform a reply. -Its usage is documented in the -[cowboy_req function reference](http://ninenines.eu/docs/en/cowboy/HEAD/manual/cowboy_req). - -The code for our application is ready, so let's build a release! - -Compiling ---------- - -First we need to download `erlang.mk`. - -``` bash -$ wget https://raw.github.com/extend/erlang.mk/master/erlang.mk -$ ls -src/ -erlang.mk -``` - -Then we need to create a Makefile that will include `erlang.mk` -for building our application. We need to define the Cowboy -dependency in the Makefile. Thankfully `erlang.mk` already -knows where to find Cowboy as it features a package index, -so we can just tell it to look there. - -``` Makefile -PROJECT = hello_erlang - -DEPS = cowboy -dep_cowboy = pkg://cowboy master - -include erlang.mk -``` - -Note that when creating production nodes you will most likely -want to use a specific version of Cowboy instead of `master`, -and properly test your release every time you update Cowboy. - -If you type `make` in a shell now, your application should build -as expected. If you get compilation errors, double check that you -haven't made any typo when creating the previous files. - -``` bash -$ make -``` - -Generating the release ----------------------- - -That's not all however, as we want to create a working release. -For that purpose, we need to create a `relx.config` file. When -this file exists, `erlang.mk` will automatically download `relx` -and build the release when you type `make`. - -In the `relx.config` file, we only need to tell `relx` that -we want the release to include the `hello_erlang` application, -and that we want an extended start script for convenience. -`relx` will figure out which other applications are required -by looking into the `.app` files for dependencies. - -``` erlang -{release, {hello_erlang, "1"}, [hello_erlang]}. -{extended_start_script, true}. -``` - -The `release` value is used to specify the release name, its -version, and the applications to be included. - -We can now build and start the release. - -``` bash -$ make -$ ./_rel/hello_erlang/bin/hello_erlang console -``` - -If you then access `http://localhost:8080` using your browser, -you should receive a nice greet! diff --git a/guide/hooks.md b/guide/hooks.md deleted file mode 100644 index b2e0c50..0000000 --- a/guide/hooks.md +++ /dev/null @@ -1,85 +0,0 @@ -Hooks -===== - -Cowboy provides two hooks. `onrequest` is called once the request -line and headers have been received. `onresponse` is called just -before sending the response. - -Onrequest ---------- - -The `onrequest` hook is called as soon as Cowboy finishes fetching -the request headers. It occurs before any other processing, including -routing. It can be used to perform any modification needed on the -request object before continuing with the processing. If a reply is -sent inside this hook, then Cowboy will move on to the next request, -skipping any subsequent handling. - -This hook is a function that takes a request object as argument, -and returns a request object. This function MUST NOT crash. Cowboy -will not send any reply if a crash occurs in this function. - -You can specify the `onrequest` hook when creating the listener, -inside the request options. - -``` erlang -cowboy:start_http(my_http_listener, 100, - [{port, 8080}], - [ - {env, [{dispatch, Dispatch}]}, - {onrequest, fun ?MODULE:debug_hook/1} - ] -). -``` - -The following hook function prints the request object everytime a -request is received. This can be useful for debugging, for example. - -``` erlang -debug_hook(Req) -> - erlang:display(Req), - Req. -``` - -Make sure to always return the last request object obtained. - -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 like the `onrequest` hook, this function MUST NOT crash. -Cowboy may or may not send a reply if this function crashes. If a reply -is sent, the hook MUST explicitly provide all headers that are needed. - -You can specify the `onresponse` hook when creating the listener. - -``` 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))}), - {ok, Req2} = cowboy_req:reply(404, Headers2, Body, Req), - Req2; -custom_404_hook(_, _, _, Req) -> - Req. -``` - -Again, make sure to always return the last request object obtained. diff --git a/guide/http_handlers.md b/guide/http_handlers.md deleted file mode 100644 index b1e2d5c..0000000 --- a/guide/http_handlers.md +++ /dev/null @@ -1,149 +0,0 @@ -Handling plain HTTP requests -============================ - -The simplest way to handle a request is by writing a -plain HTTP handler. It is modeled after Erlang/OTP's -gen_server behaviour, although simplified, as Cowboy -will simply call the three callbacks sequentially. - -Initialization --------------- - -The first callback, `init/3`, is common to all handlers, -as it is used to identify the type of handler. Plain -HTTP handlers just return `ok`. - -``` erlang -init(_Type, Req, _Opts) -> - {ok, Req, no_state}. -``` - -This function receives the name of the transport and -protocol modules used for processing the request. -They can be used to quickly dismiss requests. For -example the following handler will crash when accessed -using TCP instead of SSL. - -``` erlang -init({ssl, _}, Req, _Opts) -> - {ok, Req, no_state}. -``` - -This function also receives the options associated with -this route that you configured previously. If your -handler does not use options, then it is recommended -you match the value `[]` directly to quickly detect -configuration errors. - -``` erlang -init(_Type, Req, []) -> - {ok, Req, no_state}. -``` - -You do not need to validate the options unless they -are user configured. If they are, and there's a -configuration error, you may choose to crash. For -example, this will crash if the required `lang` -option is not found. - -``` erlang -init(_Type, Req, Opts) -> - {_, _Lang} = lists:keyfind(lang, 1, Opts), - {ok, Req, no_state}. -``` - -If your users are unlikely to figure out the issue -without explanations, then you should send a more -meaningful error back to the user. Since we already -replied to the user, there's no need for us to -continue with the handler code, so we use the -`shutdown` return value to stop early. - -``` erlang -init(_Type, Req, Opts) -> - case lists:keyfind(lang, 1, Opts) of - false -> - {ok, Req2} = cowboy_req:reply(500, [ - {<<"content-type">>, <<"text/plain">>} - ], "Missing option 'lang'.", Req), - {shutdown, Req2, no_state}; - _ -> - {ok, Req, no_state} - end. -``` - -Once the options have been validated, we can use them -safely. So we need to pass them onward to the rest of -the handler. That's what the third element of the return -tuple, the state, is for. - -We recommend that you create a state record for this. -The record will make your handler code clearer and -will allow you to better use Dialyzer for type checking. - -``` erlang --record(state, { - lang :: en | fr - %% More fields here. -}). - -init(_Type, Req, Opts) -> - {_, Lang} = lists:keyfind(lang, 1, Opts), - {ok, Req, #state{lang=Lang}}. -``` - -Handling the request --------------------- - -The second callback, `handle/2`, is specific to plain HTTP -handlers. It's where you, wait for it, handle the request. - -A handle function that does nothing would look like this: - -``` erlang -handle(Req, State) -> - {ok, Req, State}. -``` - -There's no other return value. To obtain information about -the request, or send a response, you would use the Req object -here. The Req object is documented in its own chapter. - -The following handle function will send a fairly original response. - -``` erlang -handle(Req, State) -> - {ok, Req2} = cowboy_req:reply(200, [ - {<<"content-type">>, <<"text/plain">>} - ], <<"Hello World!">>, Req), - {ok, Req2, State}. -``` - -Cleaning up ------------ - -The third and last callback, `terminate/3`, will most likely -be empty in your handler. - -``` 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. - -The chances of any of this happening in your handler are pretty -thin however. The use of the process dictionary is discouraged -in Erlang code in general. And if you need to use timers, monitors -or to receive messages, you are better off with a loop handler, -a different kind of handler meant specifically for this use. - -This function is still available should you need it. It will -always be called. diff --git a/guide/http_req_life.md b/guide/http_req_life.md deleted file mode 100644 index 1462b59..0000000 --- a/guide/http_req_life.md +++ /dev/null @@ -1,153 +0,0 @@ -The life of a request -===================== - -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 -{ok, 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/guide/http_req_resp.png b/guide/http_req_resp.png deleted file mode 100644 index e38935f..0000000 Binary files a/guide/http_req_resp.png and /dev/null differ diff --git a/guide/http_req_resp.svg b/guide/http_req_resp.svg deleted file mode 100644 index 0cfa0ae..0000000 --- a/guide/http_req_resp.svg +++ /dev/null @@ -1,558 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - some text - acceptor - parser - router - some text - onrequest - handler - middlewares - some text - client - - - - - reply - onresponse - - diff --git a/guide/introduction.md b/guide/introduction.md deleted file mode 100644 index 0af9039..0000000 --- a/guide/introduction.md +++ /dev/null @@ -1,53 +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 -------------- - -No Erlang knowledge is required for reading this guide. The reader will -be introduced to Erlang concepts and redirected to reference material -whenever necessary. - -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 R15B+. - -Cowboy may be compiled on earlier Erlang versions with small source code -modifications but there is no guarantee that it will work as expected. - -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/guide/loop_handlers.md b/guide/loop_handlers.md deleted file mode 100644 index 8689453..0000000 --- a/guide/loop_handlers.md +++ /dev/null @@ -1,148 +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/3` and `terminate/3` callbacks which work the same as -for plain HTTP handlers. - -Initialization --------------- - -The `init/3` function must return a `loop` tuple to enable -loop handler behavior. This tuple may optionally contain -a timeout value and/or the atom `hibernate` to make the -process enter hibernation until a message is received. - -This snippet enables the loop handler. - -``` erlang -init(_Type, Req, _Opts) -> - {loop, Req, undefined_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(_Type, Req, _Opts) -> - {loop, Req, undefined_state, 30000, hibernate}. -``` - -Receive loop ------------- - -Once initialized, Cowboy will wait for messages to arrive -in the process' mailbox. When a message arrives, Cowboy -calls the `info/3` function with the message, the Req object -and the handler's state. - -The following snippet sends a reply when it receives a -`reply` message from another process, or waits for another -message otherwise. - -``` erlang -info({reply, Body}, Req, State) -> - {ok, Req2} = cowboy_req:reply(200, [], Body, Req), - {ok, Req2, State}; -info(_Msg, Req, State) -> - {loop, 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 `ok` tuple should be returned. -This will instruct Cowboy to end the request. - -Otherwise a `loop` tuple should be returned. - -Streaming loop --------------- - -Another common case well suited for loop handlers is -streaming data received in the form of Erlang messages. -This can be done by initiating a chunked reply in the -`init/3` callback and then using `cowboy_req:chunk/2` -every time a message is received. - -The following snippet does exactly that. As you can see -a chunk is sent every time a `chunk` message is received, -and the loop is stopped by sending an `eof` message. - -``` erlang -init(_Type, Req, _Opts) -> - {ok, Req2} = cowboy_req:chunked_reply(200, [], Req), - {loop, Req2, undefined_state}. - -info(eof, Req, State) -> - {ok, Req, State}; -info({chunk, Chunk}, Req, State) -> - ok = cowboy_req:chunk(Chunk, Req), - {loop, Req, State}; -info(_Msg, Req, State) -> - {loop, Req, State}. -``` - -Cleaning up ------------ - -It is recommended that you set the connection header to -`close` when replying, as this process may be reused for -a subsequent request. - -Please refer to the [HTTP handlers chapter](http_handlers.md) -for general instructions about cleaning up. - -Timeout -------- - -By default Cowboy will not attempt to close the connection -if there is no activity from the client. This is not always -desirable, which is why you can set a timeout. Cowboy will -close the connection if no data was received from the client -after the configured time. The timeout only needs to be set -once and can't be modified afterwards. - -Because the request may have had a body, or may be followed -by another request, Cowboy is forced to buffer all data it -receives. This data may grow to become too large though, -so there is a configurable limit for it. The default buffer -size is of 5000 bytes, but it may be changed by setting the -`loop_max_buffer` middleware environment value. - -Hibernate ---------- - -To save memory, you may hibernate the process in between -messages received. This is done by returning the atom -`hibernate` as part of the `loop` tuple callbacks normally -return. Just add the atom at the end and Cowboy will hibernate -accordingly. diff --git a/guide/middlewares.md b/guide/middlewares.md deleted file mode 100644 index 341a0e2..0000000 --- a/guide/middlewares.md +++ /dev/null @@ -1,72 +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 four different values: - * `{ok, Req, Env}` to continue the request processing - * `{suspend, Module, Function, Args}` to hibernate - * `{halt, Req}` to stop processing and move on to the next request - * `{error, StatusCode, Req}` to reply an error and close the socket - -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/guide/modern_web.md b/guide/modern_web.md deleted file mode 100644 index 53670a4..0000000 --- a/guide/modern_web.md +++ /dev/null @@ -1,226 +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/guide/multipart_intro.md b/guide/multipart_intro.md deleted file mode 100644 index dde4099..0000000 --- a/guide/multipart_intro.md +++ /dev/null @@ -1,53 +0,0 @@ -Introduction to multipart -========================= - -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. - -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. diff --git a/guide/multipart_req.md b/guide/multipart_req.md deleted file mode 100644 index a56c70e..0000000 --- a/guide/multipart_req.md +++ /dev/null @@ -1,119 +0,0 @@ -Multipart requests -================== - -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. - -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 -{ok, {<<"multipart">>, <<"form-data">>, _}, Req2} - = 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/guide/req.md b/guide/req.md deleted file mode 100644 index 074f325..0000000 --- a/guide/req.md +++ /dev/null @@ -1,312 +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. - -It also caches the result of operations performed -on the immutable state. That means that some calls -will give a result much faster when called many times. - -Overview of the cowboy_req interface ------------------------------------- - -The `cowboy_req` interface is divided in four groups -of functions, each having a well defined return type -signature common to the entire group. - -The first group, access functions, will always return -`{Value, Req}`. The group includes all the following -functions: `binding/{2,3}`, `bindings/1`, `body_length/1`, -`cookie/{2,3}`, `cookies/1`, `header/{2,3}`, `headers/1`, -`host/1`, `host_info/1`, `host_url/1`, `meta/{2,3}`, -`method/1`, `path/1`, `path_info/1`, `peer/1`, `port/1`, -`qs/1`, `qs_val/{2,3}`, `qs_vals/1`, `url/1`, `version/1`. - -The second group, question functions, will always return -a `boolean()`. The group includes the following three -functions: `has_body/1`, `has_resp_body/1`, `has_resp_header/2`. - -The third group contains the functions that manipulate -the socket or perform operations that may legitimately fail. -They may return `{Result, Req}`, `{Result, Value, Req}` -or `{error, atom()}`. This includes the following functions: -`body/{1,2}`, `body_qs/{1,2}`, `chunked_reply/{2,3}`, -`parse_header/{2,3}`, `part/{1,2}`, `part_body/{1,2}` -and `reply/{2,3,4}`. Finally, the group also includes the -`chunk/2` and `continue/1` functions which always return `ok`. - -The final group modifies the Req object state without -performing any immediate operations. As these functions -can't fail, they always return a new `Req` directly. -This includes the following functions: `compact/1`, -`delete_resp_header/2`, `set_meta/3`, `set_resp_body/2`, -`set_resp_body_fun/{2,3}`, `set_resp_cookie/4`, `set_resp_header/3`. - -This chapter covers most of the first group, plus a few other -functions. The next few chapters cover cookies handling, reading -the request body and sending a response. - -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, Req2} = 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, Req2} = cowboy_req:host(Req), -{Port, Req3} = cowboy_req:port(Req2), -{Path, Req4} = cowboy_req:path(Req3). -``` - -The version used by the client can of course also be obtained. - -``` erlang -{Version, Req2} = 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, Req2} = 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, Req2} = 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, Req2} = 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, Req2} = 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, Req2} = cowboy_req:path_info(Req). -``` - -Query string ------------- - -The query string can be obtained directly. - -``` erlang -{Qs, Req2} = cowboy_req:qs(Req). -``` - -You can also requests only one value. - -``` erlang -{QsVal, Req2} = cowboy_req:qs_val(<<"lang">>, Req). -``` - -If that value is optional, you can define a default to simplify -your task. - -``` erlang -{QsVal, Req2} = cowboy_req:qs_val(<<"lang">>, Req, <<"en">>). -``` - -Finally, you can obtain all query string values. - -``` erlang -{AllValues, Req2} = cowboy_req:qs_vals(Req). -``` - -Request URL ------------ - -You can reconstruct the full URL of the resource. - -``` erlang -{URL, Req2} = cowboy_req:url(Req). -``` - -You can also obtain only the base of the URL, excluding the -path and query string. - -``` erlang -{BaseURL, Req2} = 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, Req2} = cowboy_req:header(<<"content-type">>, Req). -``` - -You can of course set a default in case the header is missing. - -``` erlang -{HeaderVal, Req2} - = cowboy_req:header(<<"content-type">>, Req, <<"text/plain">>). -``` - -And also obtain all headers. - -``` erlang -{AllHeaders, Req2} = cowboy_req:headers(Req). -``` - -To parse the previous header, simply call `parse_header/{2,3}` -where you would call `header/{2,3}` otherwise. Note that the -return value changes and includes the result of the operation -as the first element of the returned tuple. A successful parse -returns `ok`. - -``` erlang -{ok, ParsedVal, Req2} = cowboy_req:parse_header(<<"content-type">>, Req). -``` - -When Cowboy doesn't know how to parse the given header, the -result of the operation will be `undefined` and the string value -will be returned instead. - -``` erlang -{undefined, HeaderVal, Req2} - = cowboy_req:parse_header(<<"unicorn-header">>, Req). -``` - -When parsing fails, `{error, Reason}` is returned instead. - -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 -{ok, ParsedVal, Req2} - = cowboy_req:parse_header(<<"content-type">>, Req, - {<<"text">>, <<"plain">>, []}). -``` - -The list of known headers and default values is defined in the -manual. Also note that the result of parsing is cached, so -calling this function multiple times for the same values will -not have a significant performance impact. - -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, Req2} = cowboy_req:meta(websocket_version, Req). -``` - -You can change the default value if needed. - -``` erlang -{MetaVal, Req2} = 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}, Req2} = cowboy_req:peer(Req). -``` - -Reducing the memory footprint ------------------------------ - -When you are done reading information from the request object -and know you are not going to access it anymore, for example -when using long-polling or Websocket, you can use the `compact/1` -function to remove most of the data from the request object and -free memory. - -``` erlang -Req2 = cowboy_req:compact(Req). -``` - -You will still be able to send a reply if needed. diff --git a/guide/req_body.md b/guide/req_body.md deleted file mode 100644 index 5e07fbe..0000000 --- a/guide/req_body.md +++ /dev/null @@ -1,166 +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, Req2} = 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, 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/guide/resource_design.md b/guide/resource_design.md deleted file mode 100644 index 744e4ee..0000000 --- a/guide/resource_design.md +++ /dev/null @@ -1,233 +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/guide/resp.md b/guide/resp.md deleted file mode 100644 index b3da72d..0000000 --- a/guide/resp.md +++ /dev/null @@ -1,203 +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 -{ok, 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 -{ok, 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 -{ok, 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 -{ok, Req2} = cowboy_req:reply(200, [ - {<<"content-type">>, <<"text/plain">>} -], "Hello world!", Req). -``` - -Here is the same example but sending HTML this time. - -``` erlang -{ok, 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 -{ok, Req2} = cowboy_req:chunked_reply(200, Req), -ok = cowboy_req:chunk("Hello...", Req2), -ok = cowboy_req:chunk("chunked...", Req2), -ok = 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 -{ok, Req2} = cowboy_req:chunked_reply(200, [ - {<<"content-type">>, <<"text/html">>} -], Req), -ok = cowboy_req:chunk("Hello world!", Req2), -ok = 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/guide/rest_cond.png b/guide/rest_cond.png deleted file mode 100644 index 64cda34..0000000 Binary files a/guide/rest_cond.png and /dev/null differ diff --git a/guide/rest_cond.svg b/guide/rest_cond.svg deleted file mode 100644 index 542ae17..0000000 --- a/guide/rest_cond.svg +++ /dev/null @@ -1,1656 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - some text - some text - has if-unmodified-since? - has if-none-match? - some text - ... - generate_etag - has if-modified-since? - has if-match? - generate_etag - last_modified - - true - match* - true - not modified* - true - no match* - - - - - false - false, orinvalid - modified* - false - - - - - - 412 precondition failed - - middlewares - - - - - - - - - - - - - - - - - no match* - - - - - - date is in the future? - - - - - - - - - - last_modified - - - - - - 304 not modified - - ... - false, orinvalid - match* - - method is GET/HEAD? - true - false - true - false - true - modified* - not modified* - - - - - - generate_etag - - - - - - expires - - diff --git a/guide/rest_conneg.png b/guide/rest_conneg.png deleted file mode 100644 index 65ecdcf..0000000 Binary files a/guide/rest_conneg.png and /dev/null differ diff --git a/guide/rest_conneg.svg b/guide/rest_conneg.svg deleted file mode 100644 index 247567a..0000000 --- a/guide/rest_conneg.svg +++ /dev/null @@ -1,1135 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - some text - some text - has accept-language? - has accept-charset? - some text - start - charsets_provided - variances - has accept? - content_types_provided - languages_provided - - true - provided* - true - provided* - true - provided* - - - - - false - false - not provided* - false - not provided* - - - - - - 406 not acceptable - - middlewares - - - - - - - - - - - - - - - - - not provided* - - ... - - diff --git a/guide/rest_delete.png b/guide/rest_delete.png deleted file mode 100644 index 56a861c..0000000 Binary files a/guide/rest_delete.png and /dev/null differ diff --git a/guide/rest_delete.svg b/guide/rest_delete.svg deleted file mode 100644 index 2f5513c..0000000 --- a/guide/rest_delete.svg +++ /dev/null @@ -1,1718 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - some text - some text - delete_completed - has response body? - some text - conneg - multiple_choices - resource_exists - delete_resource - - true - false - - - - - false - - - - - - middlewares - - - - - true - true - - - - - - cond - - 300 multiple choices - - 200 OK - - - - - - has if-match? - false - - - - - - - - - - previously_existed - - 404 not found - false - - - - - - - - - - moved_permanently - - - - - - 412 precondition failed - true - true* - false - - 301 moved permanently - - - - - - - - - - moved_temporarily - true* - false - - 307 moved temporarily - - 410 gone - - - - - false - - 202 accepted - - 204 no content - true - true - - 500 internal server error - false - true - false - - diff --git a/guide/rest_flowcharts.md b/guide/rest_flowcharts.md deleted file mode 100644 index 08b56d2..0000000 --- a/guide/rest_flowcharts.md +++ /dev/null @@ -1,255 +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 `accept_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/guide/rest_get_head.png b/guide/rest_get_head.png deleted file mode 100644 index efee892..0000000 Binary files a/guide/rest_get_head.png and /dev/null differ diff --git a/guide/rest_get_head.svg b/guide/rest_get_head.svg deleted file mode 100644 index c78e939..0000000 --- a/guide/rest_get_head.svg +++ /dev/null @@ -1,1523 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - some text - some text - last_modified - ProvideResource - some text - conneg - multiple_choices - resource_exists - generate_etag - expires - - true - false - - - - - false - - - - - - middlewares - - - - - true - true - - - - - - cond - - 300 multiple choices - - 200 OK - - - - - - has if-match? - false - - - - - - - - - - previously_existed - - 404 not found - false - - - - - - - - - - moved_permanently - - - - - - 412 precondition failed - true - true* - false - - 301 moved permanently - - - - - - - - - - moved_temporarily - true* - false - - 307 moved temporarily - - 410 gone - - - - - - diff --git a/guide/rest_handlers.md b/guide/rest_handlers.md deleted file mode 100644 index 92b0bc5..0000000 --- a/guide/rest_handlers.md +++ /dev/null @@ -1,141 +0,0 @@ -REST handlers -============= - -REST is implemented in Cowboy as a protocol upgrade. Once upgraded, -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 requests. - -Initialization --------------- - -First, the `init/3` callback is called. This callback is common -to all handlers. To use REST for the current request, this function -must return an `upgrade` tuple. - -``` erlang -init({tcp, http}, Req, Opts) -> - {upgrade, protocol, cowboy_rest}. -``` - -Cowboy will then switch to the REST protocol and start executing -the state machine, starting from `rest_init/2` if it's defined, -and ending with `rest_terminate/2` also if 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. - -When the request starts being processed, Cowboy will call the -`rest_init/2` function if it is defined, with the Req object -and the handler options as arguments. This function must return -`{ok, Req, State}` where `State` is the handler's state that all -subsequent callbacks will receive. - -At the end of every request, the special callback `rest_terminate/2` -will be called if it is defined. It cannot be used to send a reply, -and must always return `ok`. - -All other callbacks are resource callbacks. They all take two -arguments, the Req object and the State, and return a three-element -tuple of the form `{Value, Req, State}`. - -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. - -All callbacks can also return `{halt, Req, State}` to stop execution -of the request, at which point `rest_terminate/2` will be called. - -In the following table, "skip" means the callback is entirely skipped -if it is undefined, moving directly to the next step. Similarly, an -empty column 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 | | -| 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_content_type | `true` | -| 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/guide/rest_options.png b/guide/rest_options.png deleted file mode 100644 index 90fd6f0..0000000 Binary files a/guide/rest_options.png and /dev/null differ diff --git a/guide/rest_options.svg b/guide/rest_options.svg deleted file mode 100644 index 496c050..0000000 --- a/guide/rest_options.svg +++ /dev/null @@ -1,387 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - some text - some text - some text - start - options - 200 OK - - - - - - - middlewares - - diff --git a/guide/rest_principles.md b/guide/rest_principles.md deleted file mode 100644 index 922f158..0000000 --- a/guide/rest_principles.md +++ /dev/null @@ -1,165 +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/guide/rest_put_post_patch.png b/guide/rest_put_post_patch.png deleted file mode 100644 index 4afca9e..0000000 Binary files a/guide/rest_put_post_patch.png and /dev/null differ diff --git a/guide/rest_put_post_patch.svg b/guide/rest_put_post_patch.svg deleted file mode 100644 index 263cc94..0000000 --- a/guide/rest_put_post_patch.svg +++ /dev/null @@ -1,2856 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - some text - some text - some text - conneg - resource_exists - - true - - - - - false - - - - - - middlewares - - - - - true - - - - - - - - - - - - - - - cond - - - - - - has if-match? - false - - - - - - method is POST/PATCH? - true - - - - - - - - - - - - - - - - - - - method is POST? - - 412 precondition failed - - - - - - - - - - - - - - previously_existed - - - - - - 404 not found - false - - - - - - - - - true* - false - - 301 moved permanently - - - - - - - - - - moved_temporarily - true* - false - - 307 moved temporarily - - 400 bad request - - - - - true - - - - - - allow_missing_post - - method is POST? - allow_missing_post - - - - - - method is PUT? - - - - - - - - - - is_conflict - true - - 409 conflict - - - - - - content_types_accepted - - AcceptResource - - - - - - - - - - new resource? - - - - - - - - - - new resource? - - 201 created - - 303 see other - - - - - - - - - - has resp location? - - - - - - - - - - - has resp body? - - - - - - - - - - multiple_choices - false - - 300 multiple choices - - 200 OK - 204 no content - true - - - - - true - - moved_permanently - - 410 gone - false - true - false - false - false - false - - - - - true - - - - - true, URI* - - - - - true - false - true - true - false - true - false - true - false - false - false - - - - - - - - true - - - - - false - true - - diff --git a/guide/rest_start.png b/guide/rest_start.png deleted file mode 100644 index 7f26464..0000000 Binary files a/guide/rest_start.png and /dev/null differ diff --git a/guide/rest_start.svg b/guide/rest_start.svg deleted file mode 100644 index d75e1cc..0000000 --- a/guide/rest_start.svg +++ /dev/null @@ -1,1468 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - some text - some text - uri_too_long - malformed_request - some text - init - is_authorized - forbidden - valid_content_headers - known_content_type - valid_entity_length - ... - service_available - known_methods - allowed_methods - - true - known* - false - allowed* - false - true - false - true - true - true - - - - - false - unknown* - true - unallowed* - true - false* - true - false - false - false - - 503 service unavailable - - - - - - - - - - 501 not implemented - 414 request URI too long - 405 method not allowed - 400 bad request - 401 unauthorized - 403 forbidden - 501 not implemented - 415 unsupported media type - 413 request entity too large - - middlewares - - diff --git a/guide/routing.md b/guide/routing.md deleted file mode 100644 index 9357424..0000000 --- a/guide/routing.md +++ /dev/null @@ -1,255 +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. - -They are always given as a two or three elements tuple, where the first -element is the name of the binding, the second element is the constraint's -name, and the optional third element is the constraint's arguments. - -The following constraints are currently defined: - - * {Name, int} - * {Name, function, fun ((Value) -> true | {true, NewValue} | false)} - -The `int` constraint will check if the binding is a binary string -representing an integer, and if it is, will convert the value to integer. - -The `function` constraint will pass the binding value to a user specified -function that receives the binary value as its only argument and must -return whether it fulfills the constraint, optionally modifying the value. -The value thus returned can be of any type. - -Note that constraint functions SHOULD be pure and MUST NOT crash. - -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/guide/static_handlers.md b/guide/static_handlers.md deleted file mode 100644 index 4e0bcfc..0000000 --- a/guide/static_handlers.md +++ /dev/null @@ -1,172 +0,0 @@ -Static handler -============== - -The static handler is a built-in REST handler for serving files. -It is available 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/guide/toc.md b/guide/toc.md deleted file mode 100644 index 5f652f1..0000000 --- a/guide/toc.md +++ /dev/null @@ -1,68 +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. - -Introducing Cowboy ------------------- - - * [Introduction](introduction.md) - * [The modern Web](modern_web.md) - * [Erlang and the Web](erlang_web.md) - * [Erlang for beginners](erlang_beginners.md) - * [Getting started](getting_started.md) - -HTTP ----- - - * [The life of a request](http_req_life.md) - * [Routing](routing.md) - * [Handling plain HTTP requests](http_handlers.md) - * [The Req object](req.md) - * [Reading the request body](req_body.md) - * [Sending a response](resp.md) - * [Using cookies](cookies.md) - -Multipart ---------- - - * [Introduction to multipart](multipart_intro.md) - * [Multipart requests](multipart_req.md) - -Static files ------------- - - * [Static handler](static_handlers.md) - -REST ----- - - * [REST principles](rest_principles.md) - * [Handling REST requests](rest_handlers.md) - * [REST flowcharts](rest_flowcharts.md) - * [Designing a resource handler](resource_design.md) - -Websocket ---------- - - * [The Websocket protocol](ws_protocol.md) - * [Handling Websocket connections](ws_handlers.md) - -Server push ------------ - - * [Loop handlers](loop_handlers.md) - -Pluggable interface -------------------- - - * [Middlewares](middlewares.md) - * [Protocol upgrades](upgrade_protocol.md) - * [Hooks](hooks.md) - -Internals ---------- - - * [Architecture](architecture.md) - * [Dealing with broken clients](broken_clients.md) diff --git a/guide/upgrade_protocol.md b/guide/upgrade_protocol.md deleted file mode 100644 index db7a453..0000000 --- a/guide/upgrade_protocol.md +++ /dev/null @@ -1,37 +0,0 @@ -Protocol upgrades -================= - -Cowboy features many different handlers, each for different purposes. -All handlers have a common entry point: the `init/3` function. - -The default handler type is the simple HTTP handler. - -To switch to a different protocol, you must perform a protocol -upgrade. This is what is done for Websocket and REST and is -explained in details in the respective chapters. - -You can also create your own protocol on top of Cowboy and use -the protocol upgrade mechanism to switch to it. - -For example, if you create the `my_protocol` module implementing -the `cowboy_sub_protocol` behavior, then you can upgrade to it -by simply returning the module name from `init/3`. - -``` erlang -init(_, _, _Opts) -> - {upgrade, protocol, my_protocol}. -``` - -The `cowboy_sub_protocol` behavior only requires one callback, -`upgrade/4`. It receives the Req object, the middleware environment, -and the handler and options for this request. This is the same -module as the `init/3` function and the same options that were -passed to it. - -``` erlang -upgrade(Req, Env, Handler, HandlerOpts) -> - %% ... -``` - -This callback is expected to behave like a middleware. Please -see the corresponding chapter for more information. diff --git a/guide/ws_handlers.md b/guide/ws_handlers.md deleted file mode 100644 index 99f69dc..0000000 --- a/guide/ws_handlers.md +++ /dev/null @@ -1,230 +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/3` callback is called. This callback is common -to all handlers. To establish a Websocket connection, this function -must return an `upgrade` tuple. - -``` erlang -init(_, Req, Opts) -> - {upgrade, protocol, cowboy_websocket}. -``` - -It is also possible to return an update Req object and options -using the longer form of this tuple. - -``` erlang -init(_Type, Req, Opts) -> - {upgrade, protocol, cowboy_websocket, Req, Opts}. -``` - -Upon receiving this tuple, Cowboy will switch to the code -that handles Websocket connections. It does not immediately -perform the handshake however. First, it calls the `websocket_init/3` -callback. - -This function must be used to initialize the state, and can -also be used to register the process, start a timer, etc. -As long as the function returns an `ok` tuple, then Cowboy -performs the Websocket handshake. - -``` erlang -websocket_init(_Type, Req, _Opts) -> - {ok, Req, #state{}}. -``` - -A `shutdown` tuple can be returned to refuse to perform the -handshake. When doing so, Cowboy will send a `400 Bad Request` -response to the client and close the connection. - -``` erlang -websocket_init(_Type, Req, _Opts) -> - {shutdown, Req}. -``` - -It is also possible to perform a `cowboy_req:reply/{2,3,4}` -before returning a `shutdown` tuple, allowing you to override -the response sent back to the client. - -Note that browser support for handling Websocket connection -failures may vary. - -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 -websocket_init(_Type, Req, _Opts) -> - case cowboy_req:parse_header(<<"sec-websocket-protocol">>, Req) of - {ok, undefined, Req2} -> - {ok, Req, #state{}}; - {ok, Subprotocols, Req2} -> - case lists:keymember(<<"mychat2">>, 1, Subprotocols) of - true -> - Req3 = cowboy:set_resp_header(<<"sec-websocket-protocol">>, - <<"mychat2">>, Req2), - {ok, Req3, #state{}}; - false -> - {shutdown, Req2} - end - end. -``` - -It is not recommended to wait too long inside the `websocket_init/3` -function. Any extra initialization may be done after returning by -sending yourself a message before doing anything. Any message sent -to `self()` from `websocket_init/3` 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 -websocket_init(_Type, Req, _Opts) -> - self() ! post_init, - %% Register process here... - {ok, 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, shutdown -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, shutdown -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. 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 -websocket_init(_Type, Req, _Opts) -> - {ok, 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/guide/ws_protocol.md b/guide/ws_protocol.md deleted file mode 100644 index 390751e..0000000 --- a/guide/ws_protocol.md +++ /dev/null @@ -1,45 +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/3` 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/manual/cowboy.md b/manual/cowboy.md deleted file mode 100644 index 0eab0f8..0000000 --- a/manual/cowboy.md +++ /dev/null @@ -1,100 +0,0 @@ -cowboy -====== - -The `cowboy` module provides convenience functions for -manipulating Ranch listeners. - -Types ------ - -### 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. - -### onrequest_fun() = fun((cowboy_req:req()) -> cowboy_req:req()) - -> Fun called immediately after receiving a request. -> -> It can perform any operation on the `Req` object, including -> reading the request body or replying. If a reply is sent, -> the processing of the request ends here, before any middleware -> is executed. - -### 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/manual/cowboy_app.md b/manual/cowboy_app.md deleted file mode 100644 index 2a086de..0000000 --- a/manual/cowboy_app.md +++ /dev/null @@ -1,26 +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/manual/cowboy_handler.md b/manual/cowboy_handler.md deleted file mode 100644 index 8d13492..0000000 --- a/manual/cowboy_handler.md +++ /dev/null @@ -1,25 +0,0 @@ -cowboy_handler -============== - -The `cowboy_handler` middleware executes the handler passed -through the environment values `handler` and `handler_opts`, -and add 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 - -Types ------ - -None. - -Exports -------- - -None. diff --git a/manual/cowboy_http_handler.md b/manual/cowboy_http_handler.md deleted file mode 100644 index 9d283e7..0000000 --- a/manual/cowboy_http_handler.md +++ /dev/null @@ -1,57 +0,0 @@ -cowboy_http_handler -=================== - -The `cowboy_http_handler` behaviour defines the interface used -by plain HTTP handlers. - -Unless noted otherwise, the callbacks will be executed sequentially. - -Types ------ - -None. - -Callbacks ---------- - -### init({TransportName, ProtocolName}, Req, Opts) - -> {ok, Req, State} | {shutdown, Req, State} - -> Types: -> * TransportName = tcp | ssl | atom() -> * ProtocolName = http | atom() -> * Req = cowboy_req:req() -> * Opts = any() -> * State = any() -> -> Initialize the state for this request. -> -> The `shutdown` return value can be used to skip the `handle/2` -> call entirely. - -### handle(Req, State) -> {ok, Req, State} - -> Types: -> * Req = cowboy_req:req() -> * State = any() -> -> Handle the request. -> -> This callback is where the request is handled and a response -> should be sent. If a response is not sent, Cowboy will send -> a `204 No Content` response automatically. - -### terminate(Reason, Req, State) -> ok - -> Types: -> * Reason = {normal, shutdown} | {error, atom()} -> * 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. diff --git a/manual/cowboy_loop_handler.md b/manual/cowboy_loop_handler.md deleted file mode 100644 index ccbb9b0..0000000 --- a/manual/cowboy_loop_handler.md +++ /dev/null @@ -1,91 +0,0 @@ -cowboy_loop_handler -=================== - -The `cowboy_loop_handler` behaviour defines the interface used -by HTTP handlers that do not send a response directly, instead -requiring a receive loop to process Erlang messages. - -This interface is best fit for long-polling types of requests. - -The `init/3` callback will always be called, followed by zero -or more calls to `info/3`. The `terminate/3` will always be -called last. - -Types ------ - -None. - -Callbacks ---------- - -### init({TransportName, ProtocolName}, Req, Opts) - -> {loop, Req, State} - | {loop, Req, State, hibernate} - | {loop, Req, State, Timeout} - | {loop, Req, State, Timeout, hibernate} - | {shutdown, Req, State} - -> Types: -> * TransportName = tcp | ssl | atom() -> * ProtocolName = http | atom() -> * Req = cowboy_req:req() -> * Opts = any() -> * State = any() -> * Timeout = timeout() -> -> Initialize the state for this request. -> -> This callback will typically be used to register this process -> to an event manager or a message queue in order to receive -> the messages the handler wants to process. -> -> The receive loop will run for a duration of up to `Timeout` -> milliseconds after it last received data from the socket, -> at which point it will stop and send a `204 No Content` reply. -> By default this value is set to `infinity`. It is recommended -> to either set this value or ensure by any other mechanism -> that the handler will be closed after a certain period of -> inactivity. -> -> The `hibernate` option will hibernate the process until it -> starts receiving messages. -> -> The `shutdown` return value can be used to skip the receive -> loop entirely. - -### info(Info, Req, State) -> {ok, Req, State} | {loop, Req, State} - | {loop, Req, State, hibernate} - -> 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 `ok` 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. - -### terminate(Reason, Req, State) -> ok - -> Types: -> * Reason = {normal, shutdown} | {normal, timeout} | {error, closed} | {error, overflow} | {error, atom()} -> * Req = cowboy_req:req() -> * State = any() -> -> Perform any necessary cleanup of the state. -> -> This callback will typically unregister from any event manager -> or message queue it registered to in `init/3`. -> -> 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. diff --git a/manual/cowboy_middleware.md b/manual/cowboy_middleware.md deleted file mode 100644 index dd28ff8..0000000 --- a/manual/cowboy_middleware.md +++ /dev/null @@ -1,56 +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} - | {halt, Req} - | {error, StatusCode, Req} - -> Types: -> * Req = cowboy_req:req() -> * Env = env() -> * Module = module() -> * Function = atom() -> * Args = [any()] -> * StatusCode = cowboy:http_status() -> -> 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 `halt` 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. -> -> The `error` return value sends an error response identified -> by the `StatusCode` and then proceeds to terminate the -> connection. Middlewares that haven't been executed yet -> will not be called. diff --git a/manual/cowboy_protocol.md b/manual/cowboy_protocol.md deleted file mode 100644 index 86aee9d..0000000 --- a/manual/cowboy_protocol.md +++ /dev/null @@ -1,65 +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()]} - | {onrequest, cowboy:onrequest_fun()} - | {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. - - onrequest (undefined) - - Fun called every time a request is received. - - onresponse (undefined) - - Fun called every time a response is sent. - - timeout (5000) - - Time in ms with no requests before Cowboy closes the connection. - -Exports -------- - -None. diff --git a/manual/cowboy_req.md b/manual/cowboy_req.md deleted file mode 100644 index 00bfd64..0000000 --- a/manual/cowboy_req.md +++ /dev/null @@ -1,670 +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, Req}` - * action: `{Result, Req} | {Result, Value, Req} | {error, atom()}` - * modification: `Req` - * question: `boolean()` - -The only exception is the `chunk/2` function which may return `ok`. - -Whenever `Req` is returned, you must use this returned value and -ignore any previous you may have had. This value contains various -state informations which are necessary for Cowboy to do some lazy -evaluation or cache results where appropriate. - -All functions which perform an action should only be called once. -This includes reading the request body or replying. Cowboy will -generally throw an error on the second call. - -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, Req2} - -> 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}], Req2} - -> 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). - -### cookie(Name, Req) -> cookie(Name, Req, undefined) -### cookie(Name, Req, Default) -> {Value, Req2} - -> Types: -> * Name = binary() -> * Default = any() -> * Value = binary() | Default -> -> Return the value for the given cookie. -> -> Cookie names are case sensitive. - -### cookies(Req) -> {[{Name, Value}], Req2} - -> Types: -> * Name = binary() -> * Value = binary() -> -> Return all cookies. - -### header(Name, Req) -> header(Name, Req, undefined) -### header(Name, Req, Default) -> {Value, Req2} - -> 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, Req2} - -> Types: -> * Headers = cowboy:http_headers() -> -> Return all headers. - -### host(Req) -> {Host, Req2} - -> Types: -> * Host = binary() -> -> Return the requested host. - -### host_info(Req) -> {HostInfo, Req2} - -> Types: -> * HostInfo = cowboy_router:tokens() | undefined -> -> Return the extra tokens from matching against `...` during routing. - -### host_url(Req) -> {HostURL, Req2} - -> 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. This includes -> the `onrequest` hook. - -### meta(Name, Req) -> meta(Name, Req, undefined) -### meta(Name, Req, Default) -> {Value, Req2} - -> Types: -> * Name = atom() -> * Default = any() -> * Value = any() -> -> Return metadata about the request. - -### method(Req) -> {Method, Req2} - -> Types: -> * Method = binary() -> -> Return the method. -> -> Methods are case sensitive. Standard methods are always uppercase. - -### parse_header(Name, Req) -> -### parse_header(Name, Req, Default) -> {ok, ParsedValue, Req2} - | {undefined, Value, Req2} | {error, badarg} - -> Types: -> * Name = binary() -> * Default = any() -> * ParsedValue - see below -> * Value = any() -> -> 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 | -> | ----------------- | ------------------ | -> | 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. Others will return `{error, badarg}` if the header -> value is empty. -> -> 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. - -### path(Req) -> {Path, Req2} - -> Types: -> * Path = binary() -> -> Return the requested path. - -### path_info(Req) -> {PathInfo, Req2} - -> Types: -> * PathInfo = cowboy_router:tokens() | undefined -> -> Return the extra tokens from matching against `...` during routing. - -### peer(Req) -> {Peer, Req2} - -> Types: -> * Peer = {inet:ip_address(), inet:port_number()} -> -> Return the client's IP address and port number. - -### port(Req) -> {Port, Req2} - -> 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, Req2} - -> Types: -> * QueryString = binary() -> -> Return the request's query string. - -### qs_val(Name, Req) -> qs_val(Name, Req, undefined) -### qs_val(Name, Req, Default) -> {Value, Req2} - -> Types: -> * Name = binary() -> * Default = any() -> * Value = binary() | true -> -> Return a value from the request's query string. -> -> The value `true` will be returned when the name was found -> in the query string without an associated value. - -### qs_vals(Req) -> {[{Name, Value}], Req2} - -> Types: -> * Name = binary() -> * Value = binary() | true -> -> Return the request's query string as a list of tuples. -> -> The value `true` will be returned when a name was found -> in the query string without an associated value. - -### 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, Req2} - -> Types: -> * URL = binary() | undefined -> -> Return the requested URL. -> -> This function will always return `undefined` until the -> `cowboy_router` middleware has been executed. This includes -> the `onrequest` hook. - -### version(Req) -> {Version, Req2} - -> 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} | {error, Reason} - -> 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, Req2} - -> 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} | {error, Reason} - -> 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 | {error, Reason} - -> 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) -> {ok, 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 | {error, Reason} - -> 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) -> {ok, 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 | {error, atom()}) -> -> 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. - -Misc. exports -------------- - -### compact(Req) -> Req2 - -> Remove any non-essential data from the `Req` object. -> -> Long-lived connections usually only need to manipulate the -> `Req` object at initialization. Compacting allows saving up -> memory by discarding extraneous information. diff --git a/manual/cowboy_rest.md b/manual/cowboy_rest.md deleted file mode 100644 index a2abf3a..0000000 --- a/manual/cowboy_rest.md +++ /dev/null @@ -1,560 +0,0 @@ -cowboy_rest -=========== - -The `cowboy_rest` module implements REST semantics on top of -the HTTP protocol. - -This module cannot be described as a behaviour due to most of -the callbacks it defines being optional. It has the same -semantics as a behaviour otherwise. - -The only mandatory callback is `init/3`, needed to perform -the protocol upgrade. - -Types ------ - -None. - -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`. - -Callbacks ---------- - -### init({TransportName, ProtocolName}, Req, Opts) - -> {upgrade, protocol, cowboy_rest} - | {upgrade, protocol, cowboy_rest, Req, Opts} - -> Types: -> * TransportName = tcp | ssl | atom() -> * ProtocolName = http | atom() -> * Req = cowboy_req:req() -> * Opts = any() -> -> Upgrade the protocol to `cowboy_rest`. -> -> This is the only mandatory callback. - -### rest_init(Req, Opts) -> {ok, Req, State} - -> Types: -> * Req = cowboy_req:req() -> * Opts = any() -> * State = any() -> -> Initialize the state for this request. - -### rest_terminate(Req, State) -> ok - -> Types: -> * 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. - -### Callback(Req, State) -> {Value, Req, State} | {halt, 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 `halt` 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` or -> `{true, URL}` may be returned. If an URL is provided, the -> response will redirect the client to the location of the -> 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 -> -> 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_content_type - -> * Methods: all -> * Value type: boolean() -> * Default value: true -> -> Return whether the content-type is known. -> -> This function determines if the server understands the -> content-type, regardless of its use by the resource. - -### 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 -> -> 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 -> -> 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/manual/cowboy_router.md b/manual/cowboy_router.md deleted file mode 100644 index 1c6dc04..0000000 --- a/manual/cowboy_router.md +++ /dev/null @@ -1,68 +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. - -### constraints() = [IntConstraint | FunConstraint] - -> Types: -> * IntConstraint = {atom(), int} -> * FunConstraint = {atom(), function, Fun} -> * Fun = fun((binary()) -> true | {true, any()} | false) -> -> List of constraints to apply to the bindings. -> -> The int constraint will convert the binding to an integer. -> The fun constraint allows writing custom code for checking -> the bindings. Returning a new value from that fun allows -> replacing the current binding with a new value. - -### dispatch_rules() - opaque to the user - -> Rules for dispatching request used by Cowboy. - -### routes() = [{Host, Paths} | {Host, constraints(), Paths}] - -> Types: -> * Host = Path = '_' | iodata() -> * Paths = [{Path, Handler, Opts} | {Path, constraints(), 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/manual/cowboy_spdy.md b/manual/cowboy_spdy.md deleted file mode 100644 index 527dbb1..0000000 --- a/manual/cowboy_spdy.md +++ /dev/null @@ -1,40 +0,0 @@ -cowboy_spdy -=========== - -The `cowboy_spdy` module implements SPDY/3 as a Ranch protocol. - -Types ------ - -### opts() = [{env, cowboy_middleware:env()} - | {middlewares, [module()]} - | {onrequest, cowboy:onrequest_fun()} - | {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. - - onrequest (undefined) - - Fun called every time a request is received. - - onresponse (undefined) - - Fun called every time a response is sent. - -Exports -------- - -None. diff --git a/manual/cowboy_static.md b/manual/cowboy_static.md deleted file mode 100644 index 01aa2bf..0000000 --- a/manual/cowboy_static.md +++ /dev/null @@ -1,34 +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, atom(), string() | binary()} - | {dir, atom(), 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/manual/cowboy_sub_protocol.md b/manual/cowboy_sub_protocol.md deleted file mode 100644 index a8ecae1..0000000 --- a/manual/cowboy_sub_protocol.md +++ /dev/null @@ -1,34 +0,0 @@ -cowboy_sub_protocol -=================== - -The `cowboy_sub_protocol` behaviour defines the interface used -by modules that implement a protocol on top of HTTP. - -Types ------ - -None. - -Callbacks ---------- - -### upgrade(Req, Env, Handler, Opts) - -> {ok, Req, Env} - | {suspend, Module, Function, Args} - | {halt, Req} - | {error, StatusCode, 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/manual/cowboy_websocket.md b/manual/cowboy_websocket.md deleted file mode 100644 index 9a81878..0000000 --- a/manual/cowboy_websocket.md +++ /dev/null @@ -1,40 +0,0 @@ -cowboy_websocket -================ - -The `cowboy_websocket` module implements the Websocket protocol. - -The callbacks for websocket handlers are defined in the manual -for the `cowboy_websocket_handler` behaviour. - -Types ------ - -### close_code() = 1000..4999 - -> Reason for closing the connection. - -### frame() = close | ping | pong - | {text | binary | close | ping | pong, iodata()} - | {close, close_code(), iodata()} - -> Frames that can be sent to the client. - -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. - -Exports -------- - -None. diff --git a/manual/cowboy_websocket_handler.md b/manual/cowboy_websocket_handler.md deleted file mode 100644 index f0480b1..0000000 --- a/manual/cowboy_websocket_handler.md +++ /dev/null @@ -1,131 +0,0 @@ -cowboy_websocket_handler -======================== - -The `cowboy_websocket_handler` behaviour defines the interface used -by Websocket handlers. - -The `init/3` and `websocket_init/3` callbacks will always be called, -followed by zero or more calls to `websocket_handle/3` and -`websocket_info/3`. The `websocket_terminate/3` will always -be called last. - -Types ------ - -None. - -Callbacks ---------- - -### init({TransportName, ProtocolName}, Req, Opts) - -> {upgrade, protocol, cowboy_websocket} - | {upgrade, protocol, cowboy_websocket, Req, Opts} - -> Types: -> * TransportName = tcp | ssl | atom() -> * ProtocolName = http | atom() -> * Req = cowboy_req:req() -> * Opts = any() -> -> Upgrade the protocol to `cowboy_websocket`. - -### websocket_init(TransportName, Req, Opts) - -> {ok, Req, State} - | {ok, Req, State, hibernate} - | {ok, Req, State, Timeout} - | {ok, Req, State, Timeout, hibernate} - | {shutdown, Req} - -> Types: -> * TransportName = tcp | ssl | atom() -> * Req = cowboy_req:req() -> * Opts = any() -> * State = any() -> * Timeout = timeout() -> -> Initialize the state for this session. -> -> This function is called before the upgrade to Websocket occurs. -> It can be used to negotiate Websocket protocol extensions -> with the client. It will typically be used to register this process -> to an event manager or a message queue in order to receive -> the messages the handler wants to process. -> -> The connection will stay up for a duration of up to `Timeout` -> milliseconds after it last received data from the socket, -> at which point it will stop and close the connection. -> By default this value is set to `infinity`. It is recommended -> to either set this value or ensure by any other mechanism -> that the handler will be closed after a certain period of -> inactivity. -> -> The `hibernate` option will hibernate the process until it -> starts receiving either data from the Websocket connection -> or Erlang messages. -> -> The `shutdown` return value can be used to close the connection -> before upgrading to Websocket. - -### websocket_handle(InFrame, Req, State) - -> {ok, Req, State} - | {ok, Req, State, hibernate} - | {reply, OutFrame | [OutFrame], Req, State} - | {reply, OutFrame | [OutFrame], Req, State, hibernate} - | {shutdown, Req, State} - -> Types: -> * InFrame = {text | binary | ping | pong, binary()} -> * Req = cowboy_req:req() -> * State = any() -> * OutFrame = cowboy_websocket:frame() -> -> Handle the data received from the Websocket connection. -> -> This function will be called every time data is received -> from the Websocket connection. -> -> The `shutdown` 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} - | {shutdown, Req, State} - -> Types: -> * Info = any() -> * Req = cowboy_req:req() -> * State = any() -> * OutFrame = cowboy_websocket: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 `shutdown` 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. - -### websocket_terminate(Reason, Req, State) -> ok - -> Types: -> * Reason = {normal, shutdown | timeout} | {remote, closed} | {remote, cowboy_websocket:close_code(), binary()} | {error, badencoding | badframe | closed | atom()} -> * Req = cowboy_req:req() -> * State = any() -> -> Perform any necessary cleanup of the state. -> -> The connection will be closed and the process stopped right -> after this call. diff --git a/manual/http_status_codes.md b/manual/http_status_codes.md deleted file mode 100644 index 070dce8..0000000 --- a/manual/http_status_codes.md +++ /dev/null @@ -1,180 +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/manual/toc.md b/manual/toc.md deleted file mode 100644 index 165470e..0000000 --- a/manual/toc.md +++ /dev/null @@ -1,21 +0,0 @@ -Cowboy Function Reference -========================= - -The function reference documents the public interface of Cowboy. - - * [The Cowboy Application](cowboy_app.md) - * [cowboy](cowboy.md) - * [cowboy_handler](cowboy_handler.md) - * [cowboy_http_handler](cowboy_http_handler.md) - * [cowboy_loop_handler](cowboy_loop_handler.md) - * [cowboy_middleware](cowboy_middleware.md) - * [cowboy_protocol](cowboy_protocol.md) - * [cowboy_req](cowboy_req.md) - * [cowboy_rest](cowboy_rest.md) - * [cowboy_router](cowboy_router.md) - * [cowboy_spdy](cowboy_spdy.md) - * [cowboy_static](cowboy_static.md) - * [cowboy_sub_protocol](cowboy_sub_protocol.md) - * [cowboy_websocket](cowboy_websocket.md) - * [cowboy_websocket_handler](cowboy_websocket_handler.md) - * [HTTP status codes](http_status_codes.md) -- cgit v1.2.3