├── .gitattributes ├── test ├── cover.spec ├── http_SUITE_data │ ├── rest_empty_resource.erl │ ├── http_handler.erl │ ├── rest_simple_resource.erl │ ├── http_streamed.erl │ ├── http_chunked.erl │ ├── rest_postonly_resource.erl │ ├── rest_expires_binary.erl │ ├── http_req_attr.erl │ ├── rest_post_charset_resource.erl │ ├── rest_nodelete_resource.erl │ ├── http_multipart.erl │ ├── http_errors.erl │ ├── rest_expires.erl │ ├── http_echo_body.erl │ ├── rest_missing_callbacks.erl │ ├── http_multipart_stream.erl │ ├── http_loop_stream_recv.erl │ ├── http_set_resp.erl │ ├── rest_forbidden_resource.erl │ ├── http_stream_body.erl │ ├── rest_param_all.erl │ ├── http_body_qs.erl │ ├── rest_patch_resource.erl │ └── rest_resource_etags.erl ├── handlers │ ├── hello_h.erl │ ├── input_crash_h.erl │ ├── asterisk_h.erl │ ├── loop_handler_body_h.erl │ ├── loop_handler_timeout_h.erl │ ├── long_polling_h.erl │ ├── ws_init_h.erl │ ├── multipart_h.erl │ └── echo_h.erl ├── ws_SUITE_data │ ├── ws_init_shutdown.erl │ ├── client.json │ ├── ws_timeout_hibernate.erl │ ├── ws_echo.erl │ ├── ws_send_many.erl │ ├── ws_subprotocol.erl │ ├── ws_timeout_cancel.erl │ └── ws_echo_timer.erl ├── cowboy_ct_hook.erl └── loop_handler_SUITE.erl ├── examples ├── file_server │ ├── priv │ │ ├── test.txt │ │ ├── small.mp4 │ │ ├── small.ogv │ │ └── video.html │ ├── relx.config │ ├── Makefile │ ├── src │ │ ├── file_server_sup.erl │ │ ├── file_server_app.erl │ │ ├── directory_lister.erl │ │ └── directory_handler.erl │ └── README.asciidoc ├── cookie │ ├── relx.config │ ├── Makefile │ ├── README.asciidoc │ ├── src │ │ ├── cookie_sup.erl │ │ ├── cookie_app.erl │ │ └── toppage_handler.erl │ └── templates │ │ └── toppage.dtl ├── upload │ ├── relx.config │ ├── Makefile │ ├── README.asciidoc │ ├── priv │ │ └── index.html │ └── src │ │ ├── upload_sup.erl │ │ ├── upload_handler.erl │ │ └── upload_app.erl ├── echo_get │ ├── relx.config │ ├── Makefile │ ├── src │ │ ├── echo_get_sup.erl │ │ ├── echo_get_app.erl │ │ └── toppage_handler.erl │ └── README.asciidoc ├── echo_post │ ├── relx.config │ ├── Makefile │ ├── src │ │ ├── echo_post_sup.erl │ │ ├── echo_post_app.erl │ │ └── toppage_handler.erl │ └── README.asciidoc ├── error_hook │ ├── relx.config │ ├── Makefile │ ├── src │ │ ├── error_hook_sup.erl │ │ └── error_hook_app.erl │ └── README.asciidoc ├── websocket │ ├── relx.config │ ├── Makefile │ ├── README.asciidoc │ ├── src │ │ ├── websocket_sup.erl │ │ ├── websocket_app.erl │ │ └── ws_handler.erl │ └── priv │ │ └── index.html ├── eventsource │ ├── relx.config │ ├── Makefile │ ├── README.asciidoc │ ├── src │ │ ├── eventsource_sup.erl │ │ ├── eventsource_app.erl │ │ └── eventsource_handler.erl │ └── priv │ │ └── index.html ├── hello_world │ ├── relx.config │ ├── Makefile │ ├── src │ │ ├── toppage_handler.erl │ │ ├── hello_world_sup.erl │ │ └── hello_world_app.erl │ └── README.asciidoc ├── rest_pastebin │ ├── relx.config │ ├── Makefile │ ├── priv │ │ ├── index.txt │ │ └── index.html │ ├── src │ │ ├── rest_pastebin_sup.erl │ │ └── rest_pastebin_app.erl │ └── README.asciidoc ├── markdown_middleware │ ├── priv │ │ ├── small.mp4 │ │ ├── small.ogv │ │ └── video.md │ ├── relx.config │ ├── Makefile │ ├── src │ │ ├── markdown_middleware_sup.erl │ │ ├── markdown_middleware_app.erl │ │ └── markdown_converter.erl │ └── README.asciidoc ├── rest_basic_auth │ ├── relx.config │ ├── Makefile │ └── src │ │ ├── rest_basic_auth_sup.erl │ │ ├── rest_basic_auth_app.erl │ │ └── toppage_handler.erl ├── ssl_hello_world │ ├── relx.config │ ├── Makefile │ ├── src │ │ ├── toppage_handler.erl │ │ ├── ssl_hello_world_sup.erl │ │ └── ssl_hello_world_app.erl │ ├── priv │ │ └── ssl │ │ │ ├── server.key │ │ │ ├── cowboy-ca.crt │ │ │ └── server.crt │ └── README.asciidoc ├── compress_response │ ├── relx.config │ ├── Makefile │ ├── src │ │ ├── compress_response_sup.erl │ │ ├── compress_response_app.erl │ │ └── toppage_handler.erl │ └── README.asciidoc ├── rest_hello_world │ ├── relx.config │ ├── Makefile │ └── src │ │ ├── rest_hello_world_sup.erl │ │ ├── rest_hello_world_app.erl │ │ └── toppage_handler.erl ├── chunked_hello_world │ ├── relx.config │ ├── Makefile │ ├── src │ │ ├── toppage_handler.erl │ │ ├── chunked_hello_world_sup.erl │ │ └── chunked_hello_world_app.erl │ └── README.asciidoc ├── rest_stream_response │ ├── relx.config │ ├── Makefile │ ├── src │ │ ├── rest_stream_response_sup.erl │ │ ├── rest_stream_response_app.erl │ │ └── toppage_handler.erl │ └── README.asciidoc └── README.asciidoc ├── doc └── src │ ├── guide │ ├── flow_diagram.asciidoc │ ├── rest_cond.png │ ├── rest_start.png │ ├── rest_conneg.png │ ├── rest_delete.png │ ├── rest_options.png │ ├── http_req_resp.png │ ├── rest_get_head.png │ ├── rest_put_post_patch.png │ ├── cowboy.sty │ ├── hooks.asciidoc │ ├── architecture.asciidoc │ ├── broken_clients.asciidoc │ ├── book.asciidoc │ ├── sub_protocols.asciidoc │ ├── middlewares.asciidoc │ ├── ws_protocol.asciidoc │ ├── introduction.asciidoc │ ├── constraints.asciidoc │ └── handlers.asciidoc │ ├── specs │ ├── index.ezdoc │ └── rfc6585.ezdoc │ └── manual │ ├── cowboy_sub_protocol.asciidoc │ ├── cowboy_static.asciidoc │ ├── cowboy.stop_listener.asciidoc │ ├── cowboy_router.asciidoc │ ├── cowboy_middleware.asciidoc │ ├── cowboy.set_env.asciidoc │ ├── cowboy_protocol.asciidoc │ ├── cowboy_app.asciidoc │ ├── cowboy.asciidoc │ ├── cowboy_loop.asciidoc │ ├── cowboy_handler.asciidoc │ └── cowboy.start_clear.asciidoc ├── .gitignore ├── rebar.config ├── ebin └── cowboy.app ├── LICENSE ├── src ├── cowboy_app.erl ├── cowboy_middleware.erl ├── cowboy_sub_protocol.erl ├── cowboy_sup.erl ├── cowboy_clear.erl ├── cowboy_constraints.erl ├── cowboy_tls.erl ├── cowboy_stream.erl ├── cowboy_handler.erl └── cowboy.erl ├── Makefile ├── AUTHORS └── README.asciidoc /.gitattributes: -------------------------------------------------------------------------------- 1 | erlang.mk -diff 2 | -------------------------------------------------------------------------------- /test/cover.spec: -------------------------------------------------------------------------------- 1 | {incl_app, cowboy, details}. 2 | -------------------------------------------------------------------------------- /examples/file_server/priv/test.txt: -------------------------------------------------------------------------------- 1 | If you read this then the static file server works! 2 | -------------------------------------------------------------------------------- /doc/src/guide/flow_diagram.asciidoc: -------------------------------------------------------------------------------- 1 | [[flow_diagram]] 2 | == Flow diagram 3 | 4 | Placeholder chapter. 5 | -------------------------------------------------------------------------------- /doc/src/guide/rest_cond.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wujunze/cowboy/master/doc/src/guide/rest_cond.png -------------------------------------------------------------------------------- /doc/src/guide/rest_start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wujunze/cowboy/master/doc/src/guide/rest_start.png -------------------------------------------------------------------------------- /doc/src/guide/rest_conneg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wujunze/cowboy/master/doc/src/guide/rest_conneg.png -------------------------------------------------------------------------------- /doc/src/guide/rest_delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wujunze/cowboy/master/doc/src/guide/rest_delete.png -------------------------------------------------------------------------------- /doc/src/guide/rest_options.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wujunze/cowboy/master/doc/src/guide/rest_options.png -------------------------------------------------------------------------------- /examples/cookie/relx.config: -------------------------------------------------------------------------------- 1 | {release, {cookie_example, "1"}, [cookie]}. 2 | {extended_start_script, true}. 3 | -------------------------------------------------------------------------------- /examples/upload/relx.config: -------------------------------------------------------------------------------- 1 | {release, {upload_example, "1"}, [upload]}. 2 | {extended_start_script, true}. 3 | -------------------------------------------------------------------------------- /doc/src/guide/http_req_resp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wujunze/cowboy/master/doc/src/guide/http_req_resp.png -------------------------------------------------------------------------------- /doc/src/guide/rest_get_head.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wujunze/cowboy/master/doc/src/guide/rest_get_head.png -------------------------------------------------------------------------------- /examples/echo_get/relx.config: -------------------------------------------------------------------------------- 1 | {release, {echo_get_example, "1"}, [echo_get]}. 2 | {extended_start_script, true}. 3 | -------------------------------------------------------------------------------- /examples/echo_post/relx.config: -------------------------------------------------------------------------------- 1 | {release, {echo_post_example, "1"}, [echo_post]}. 2 | {extended_start_script, true}. 3 | -------------------------------------------------------------------------------- /examples/error_hook/relx.config: -------------------------------------------------------------------------------- 1 | {release, {error_hook_example, "1"}, [error_hook]}. 2 | {extended_start_script, true}. 3 | -------------------------------------------------------------------------------- /examples/file_server/priv/small.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wujunze/cowboy/master/examples/file_server/priv/small.mp4 -------------------------------------------------------------------------------- /examples/file_server/priv/small.ogv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wujunze/cowboy/master/examples/file_server/priv/small.ogv -------------------------------------------------------------------------------- /examples/websocket/relx.config: -------------------------------------------------------------------------------- 1 | {release, {websocket_example, "1"}, [websocket]}. 2 | {extended_start_script, true}. 3 | -------------------------------------------------------------------------------- /doc/src/guide/rest_put_post_patch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wujunze/cowboy/master/doc/src/guide/rest_put_post_patch.png -------------------------------------------------------------------------------- /examples/eventsource/relx.config: -------------------------------------------------------------------------------- 1 | {release, {eventsource_example, "1"}, [eventsource]}. 2 | {extended_start_script, true}. 3 | -------------------------------------------------------------------------------- /examples/file_server/relx.config: -------------------------------------------------------------------------------- 1 | {release, {file_server_example, "1"}, [file_server]}. 2 | {extended_start_script, true}. 3 | -------------------------------------------------------------------------------- /examples/hello_world/relx.config: -------------------------------------------------------------------------------- 1 | {release, {hello_world_example, "1"}, [hello_world]}. 2 | {extended_start_script, true}. 3 | -------------------------------------------------------------------------------- /examples/rest_pastebin/relx.config: -------------------------------------------------------------------------------- 1 | {release, {rest_pastebin_example, "1"}, [rest_pastebin]}. 2 | {extended_start_script, true}. 3 | -------------------------------------------------------------------------------- /examples/markdown_middleware/priv/small.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wujunze/cowboy/master/examples/markdown_middleware/priv/small.mp4 -------------------------------------------------------------------------------- /examples/markdown_middleware/priv/small.ogv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wujunze/cowboy/master/examples/markdown_middleware/priv/small.ogv -------------------------------------------------------------------------------- /examples/rest_basic_auth/relx.config: -------------------------------------------------------------------------------- 1 | {release, {rest_basic_auth_example, "1"}, [rest_basic_auth]}. 2 | {extended_start_script, true}. 3 | -------------------------------------------------------------------------------- /examples/ssl_hello_world/relx.config: -------------------------------------------------------------------------------- 1 | {release, {ssl_hello_world_example, "1"}, [ssl_hello_world]}. 2 | {extended_start_script, true}. 3 | -------------------------------------------------------------------------------- /examples/compress_response/relx.config: -------------------------------------------------------------------------------- 1 | {release, {compress_response_example, "1"}, [compress_response]}. 2 | {extended_start_script, true}. 3 | -------------------------------------------------------------------------------- /examples/rest_hello_world/relx.config: -------------------------------------------------------------------------------- 1 | {release, {rest_hello_world_example, "1"}, [rest_hello_world]}. 2 | {extended_start_script, true}. 3 | -------------------------------------------------------------------------------- /examples/chunked_hello_world/relx.config: -------------------------------------------------------------------------------- 1 | {release, {chunked_hello_world_example, "1"}, [chunked_hello_world]}. 2 | {extended_start_script, true}. 3 | -------------------------------------------------------------------------------- /examples/markdown_middleware/relx.config: -------------------------------------------------------------------------------- 1 | {release, {markdown_middleware_example, "1"}, [markdown_middleware]}. 2 | {extended_start_script, true}. 3 | -------------------------------------------------------------------------------- /examples/rest_stream_response/relx.config: -------------------------------------------------------------------------------- 1 | {release, {rest_stream_response_example, "1"}, [rest_stream_response]}. 2 | {extended_start_script, true}. 3 | -------------------------------------------------------------------------------- /test/http_SUITE_data/rest_empty_resource.erl: -------------------------------------------------------------------------------- 1 | -module(rest_empty_resource). 2 | 3 | -export([init/2]). 4 | 5 | init(Req, Opts) -> 6 | {cowboy_rest, Req, Opts}. 7 | -------------------------------------------------------------------------------- /examples/cookie/Makefile: -------------------------------------------------------------------------------- 1 | PROJECT = cookie 2 | PROJECT_DESCRIPTION = Cowboy Cookie example 3 | PROJECT_VERSION = 1 4 | 5 | DEPS = cowboy erlydtl 6 | dep_cowboy_commit = master 7 | 8 | include ../../erlang.mk 9 | -------------------------------------------------------------------------------- /examples/echo_get/Makefile: -------------------------------------------------------------------------------- 1 | PROJECT = echo_get 2 | PROJECT_DESCRIPTION = Cowboy GET echo example 3 | PROJECT_VERSION = 1 4 | 5 | DEPS = cowboy 6 | dep_cowboy_commit = master 7 | 8 | include ../../erlang.mk 9 | -------------------------------------------------------------------------------- /examples/echo_post/Makefile: -------------------------------------------------------------------------------- 1 | PROJECT = echo_post 2 | PROJECT_DESCRIPTION = Cowboy POST echo example 3 | PROJECT_VERSION = 1 4 | 5 | DEPS = cowboy 6 | dep_cowboy_commit = master 7 | 8 | include ../../erlang.mk 9 | -------------------------------------------------------------------------------- /examples/websocket/Makefile: -------------------------------------------------------------------------------- 1 | PROJECT = websocket 2 | PROJECT_DESCRIPTION = Cowboy Websocket example 3 | PROJECT_VERSION = 1 4 | 5 | DEPS = cowboy 6 | dep_cowboy_commit = master 7 | 8 | include ../../erlang.mk 9 | -------------------------------------------------------------------------------- /examples/upload/Makefile: -------------------------------------------------------------------------------- 1 | PROJECT = upload 2 | PROJECT_DESCRIPTION = Cowboy multipart upload example 3 | PROJECT_VERSION = 1 4 | 5 | DEPS = cowboy 6 | dep_cowboy_commit = master 7 | 8 | include ../../erlang.mk 9 | -------------------------------------------------------------------------------- /examples/error_hook/Makefile: -------------------------------------------------------------------------------- 1 | PROJECT = error_hook 2 | PROJECT_DESCRIPTION = Cowboy error handler example 3 | PROJECT_VERSION = 1 4 | 5 | DEPS = cowboy 6 | dep_cowboy_commit = master 7 | 8 | include ../../erlang.mk 9 | -------------------------------------------------------------------------------- /examples/eventsource/Makefile: -------------------------------------------------------------------------------- 1 | PROJECT = eventsource 2 | PROJECT_DESCRIPTION = Cowboy EventSource example 3 | PROJECT_VERSION = 1 4 | 5 | DEPS = cowboy 6 | dep_cowboy_commit = master 7 | 8 | include ../../erlang.mk 9 | -------------------------------------------------------------------------------- /examples/hello_world/Makefile: -------------------------------------------------------------------------------- 1 | PROJECT = hello_world 2 | PROJECT_DESCRIPTION = Cowboy Hello World example 3 | PROJECT_VERSION = 1 4 | 5 | DEPS = cowboy 6 | dep_cowboy_commit = master 7 | 8 | include ../../erlang.mk 9 | -------------------------------------------------------------------------------- /examples/rest_pastebin/Makefile: -------------------------------------------------------------------------------- 1 | PROJECT = rest_pastebin 2 | PROJECT_DESCRIPTION = Cowboy REST Pastebin example 3 | PROJECT_VERSION = 1 4 | 5 | DEPS = cowboy 6 | dep_cowboy_commit = master 7 | 8 | include ../../erlang.mk 9 | -------------------------------------------------------------------------------- /test/handlers/hello_h.erl: -------------------------------------------------------------------------------- 1 | %% This module sends a hello world response. 2 | 3 | -module(hello_h). 4 | 5 | -export([init/2]). 6 | 7 | init(Req, Opts) -> 8 | {ok, cowboy_req:reply(200, #{}, <<"Hello world!">>, Req), Opts}. 9 | -------------------------------------------------------------------------------- /examples/rest_hello_world/Makefile: -------------------------------------------------------------------------------- 1 | PROJECT = rest_hello_world 2 | PROJECT_DESCRIPTION = Cowboy REST Hello World example 3 | PROJECT_VERSION = 1 4 | 5 | DEPS = cowboy 6 | dep_cowboy_commit = master 7 | 8 | include ../../erlang.mk 9 | -------------------------------------------------------------------------------- /examples/chunked_hello_world/Makefile: -------------------------------------------------------------------------------- 1 | PROJECT = chunked_hello_world 2 | PROJECT_DESCRIPTION = Cowboy chunked Hello World example 3 | PROJECT_VERSION = 1 4 | 5 | DEPS = cowboy 6 | dep_cowboy_commit = master 7 | 8 | include ../../erlang.mk 9 | -------------------------------------------------------------------------------- /examples/compress_response/Makefile: -------------------------------------------------------------------------------- 1 | PROJECT = compress_response 2 | PROJECT_DESCRIPTION = Cowboy compressed response example 3 | PROJECT_VERSION = 1 4 | 5 | DEPS = cowboy 6 | dep_cowboy_commit = master 7 | 8 | include ../../erlang.mk 9 | -------------------------------------------------------------------------------- /examples/rest_basic_auth/Makefile: -------------------------------------------------------------------------------- 1 | PROJECT = rest_basic_auth 2 | PROJECT_DESCRIPTION = Cowboy Basic HTTP Authorization example 3 | PROJECT_VERSION = 1 4 | 5 | DEPS = cowboy 6 | dep_cowboy_commit = master 7 | 8 | include ../../erlang.mk 9 | -------------------------------------------------------------------------------- /test/ws_SUITE_data/ws_init_shutdown.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | -module(ws_init_shutdown). 4 | 5 | -export([init/2]). 6 | 7 | init(Req, Opts) -> 8 | {ok, cowboy_req:reply(403, Req), Opts}. 9 | -------------------------------------------------------------------------------- /examples/file_server/Makefile: -------------------------------------------------------------------------------- 1 | PROJECT = file_server 2 | PROJECT_DESCRIPTION = Cowboy file server example with directory listing 3 | PROJECT_VERSION = 1 4 | 5 | DEPS = cowboy jsx 6 | dep_cowboy_commit = master 7 | 8 | include ../../erlang.mk 9 | -------------------------------------------------------------------------------- /examples/rest_stream_response/Makefile: -------------------------------------------------------------------------------- 1 | PROJECT = rest_stream_response 2 | PROJECT_DESCRIPTION = Cowboy REST example with streaming 3 | PROJECT_VERSION = 1 4 | 5 | DEPS = cowboy 6 | dep_cowboy_commit = master 7 | 8 | include ../../erlang.mk 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .cowboy.plt 2 | .erlang.mk 3 | _rel 4 | cowboy.d 5 | deps 6 | doc/guide.pdf 7 | doc/html 8 | doc/man3 9 | doc/man7 10 | ebin/*.beam 11 | ebin/test 12 | examples/*/ebin 13 | examples/*/*.d 14 | logs 15 | relx 16 | test/*.beam 17 | -------------------------------------------------------------------------------- /examples/rest_pastebin/priv/index.txt: -------------------------------------------------------------------------------- 1 | Simple Pastebin 2 | --------------- 3 | 4 | You can paste your text into the text field to submit, or you can capture the 5 | output of a command with: 6 | 7 | | curl -i --data-urlencode paste@- localhost:8080 8 | -------------------------------------------------------------------------------- /examples/ssl_hello_world/Makefile: -------------------------------------------------------------------------------- 1 | PROJECT = ssl_hello_world 2 | PROJECT_DESCRIPTION = Cowboy SSL Hello World example 3 | PROJECT_VERSION = 1 4 | 5 | DEPS = cowboy 6 | LOCAL_DEPS = ssl 7 | dep_cowboy_commit = master 8 | 9 | include ../../erlang.mk 10 | -------------------------------------------------------------------------------- /examples/markdown_middleware/Makefile: -------------------------------------------------------------------------------- 1 | PROJECT = markdown_middleware 2 | PROJECT_DESCRIPTION = Cowboy static file handler example with middleware component 3 | PROJECT_VERSION = 1 4 | 5 | DEPS = cowboy 6 | dep_cowboy_commit = master 7 | 8 | include ../../erlang.mk 9 | -------------------------------------------------------------------------------- /doc/src/specs/index.ezdoc: -------------------------------------------------------------------------------- 1 | ::: Cowboy Implementation Reference 2 | 3 | The implementation reference documents the behavior of Cowboy 4 | with regards to various standards and specifications. 5 | 6 | * ^"RFC6585 status codes^rfc6585 7 | * ^"RFC7230 HTTP/1.1 server^rfc7230_server 8 | -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | {deps, [{cowlib,".*",{git,"https://github.com/ninenines/cowlib","master"}},{ranch,".*",{git,"https://github.com/ninenines/ranch","1.2.1"}}]}. 2 | {erl_opts, [debug_info,warn_export_vars,warn_shadow_vars,warn_obsolete_guard,warn_export_all,warn_missing_spec,warn_untyped_record]}. 3 | -------------------------------------------------------------------------------- /examples/websocket/README.asciidoc: -------------------------------------------------------------------------------- 1 | = Websocket example 2 | 3 | To try this example, you need GNU `make` and `git` in your PATH. 4 | 5 | To build and run the example, use the following command: 6 | 7 | [source,bash] 8 | $ make run 9 | 10 | Then point your browser to http://localhost:8080 11 | -------------------------------------------------------------------------------- /examples/eventsource/README.asciidoc: -------------------------------------------------------------------------------- 1 | = EventSource example 2 | 3 | To try this example, you need GNU `make` and `git` in your PATH. 4 | 5 | To build and run the example, use the following command: 6 | 7 | [source,bash] 8 | $ make run 9 | 10 | Then point your browser to http://localhost:8080 11 | -------------------------------------------------------------------------------- /test/ws_SUITE_data/client.json: -------------------------------------------------------------------------------- 1 | { 2 | "options": {"failByDrop": false}, 3 | "enable-ssl": false, 4 | 5 | "servers": [{ 6 | "agent": "Cowboy", 7 | "url": "ws://localhost:33080/ws_echo", 8 | "options": {"version": 18} 9 | }], 10 | 11 | "cases": ["*"], 12 | "exclude-cases": [], 13 | "exclude-agent-cases": {} 14 | } 15 | -------------------------------------------------------------------------------- /test/handlers/input_crash_h.erl: -------------------------------------------------------------------------------- 1 | %% This module crashes on request input data 2 | %% depending on the given option. 3 | 4 | -module(input_crash_h). 5 | 6 | -export([init/2]). 7 | 8 | init(Req, content_length) -> 9 | ct_helper_error_h:ignore(erlang, binary_to_integer, 1), 10 | cowboy_req:parse_header(<<"content-length">>, Req). 11 | -------------------------------------------------------------------------------- /doc/src/guide/cowboy.sty: -------------------------------------------------------------------------------- 1 | \NeedsTeXFormat{LaTeX2e} 2 | \ProvidesPackage{asciidoc-dblatex}[2012/10/24 AsciiDoc DocBook Style] 3 | 4 | %% Just use the original package and pass the options. 5 | \RequirePackageWithOptions{docbook} 6 | 7 | %% Define an alias for make snippets to be compatible with source-highlighter. 8 | \lstalias{makefile}{make} 9 | -------------------------------------------------------------------------------- /examples/markdown_middleware/priv/video.md: -------------------------------------------------------------------------------- 1 | HTML5 Video With Markdown 2 | ========================= 3 | 4 | 8 | 9 | Videos taken from [TechSlides](http://techslides.com/sample-webm-ogg-and-mp4-video-files-for-html5/) 10 | -------------------------------------------------------------------------------- /test/http_SUITE_data/http_handler.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | -module(http_handler). 4 | 5 | -export([init/2]). 6 | 7 | init(Req, Opts) -> 8 | Headers = proplists:get_value(headers, Opts, #{}), 9 | Body = proplists:get_value(body, Opts, "http_handler"), 10 | {ok, cowboy_req:reply(200, Headers, Body, Req), Opts}. 11 | -------------------------------------------------------------------------------- /examples/upload/README.asciidoc: -------------------------------------------------------------------------------- 1 | = Multipart upload example 2 | 3 | To try this example, you need GNU `make` and `git` in your PATH. 4 | 5 | To build and run the example, use the following command: 6 | 7 | [source,bash] 8 | $ make run 9 | 10 | Then point your browser to http://localhost:8080 11 | 12 | The uploaded file will be displayed in the shell directly. 13 | -------------------------------------------------------------------------------- /examples/hello_world/src/toppage_handler.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @doc Hello world handler. 4 | -module(toppage_handler). 5 | 6 | -export([init/2]). 7 | 8 | init(Req0, Opts) -> 9 | Req = cowboy_req:reply(200, #{ 10 | <<"content-type">> => <<"text/plain">> 11 | }, <<"Hello world!">>, Req0), 12 | {ok, Req, Opts}. 13 | -------------------------------------------------------------------------------- /examples/ssl_hello_world/src/toppage_handler.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @doc Hello world handler. 4 | -module(toppage_handler). 5 | 6 | -export([init/2]). 7 | 8 | init(Req0, Opts) -> 9 | Req = cowboy_req:reply(200, #{ 10 | <<"content-type">> => <<"text/plain">> 11 | }, <<"Hello world!">>, Req0), 12 | {ok, Req, Opts}. 13 | -------------------------------------------------------------------------------- /examples/file_server/priv/video.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |

HTML5 Video Example

5 | 9 |

Videos taken from TechSlides

10 | 11 | 12 | -------------------------------------------------------------------------------- /examples/cookie/README.asciidoc: -------------------------------------------------------------------------------- 1 | = Cookie example 2 | 3 | To try this example, you need GNU `make` and `git` in your PATH. 4 | 5 | To build and run the example, use the following command: 6 | 7 | [source,bash] 8 | $ make run 9 | 10 | Then point your browser to http://localhost:8080 11 | 12 | This example allows you to use any path to show that the cookies 13 | are defined site-wide. Try it! 14 | -------------------------------------------------------------------------------- /examples/upload/priv/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Multipart upload example 5 | 6 | 7 | 8 |
9 | 10 | 11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /test/http_SUITE_data/rest_simple_resource.erl: -------------------------------------------------------------------------------- 1 | -module(rest_simple_resource). 2 | 3 | -export([init/2]). 4 | -export([content_types_provided/2]). 5 | -export([get_text_plain/2]). 6 | 7 | init(Req, Opts) -> 8 | {cowboy_rest, Req, Opts}. 9 | 10 | content_types_provided(Req, State) -> 11 | {[{{<<"text">>, <<"plain">>, []}, get_text_plain}], Req, State}. 12 | 13 | get_text_plain(Req, State) -> 14 | {<<"This is REST!">>, Req, State}. 15 | -------------------------------------------------------------------------------- /test/http_SUITE_data/http_streamed.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | -module(http_streamed). 4 | 5 | -export([init/2]). 6 | 7 | init(Req, Opts) -> 8 | Req2 = cowboy_req:set([{resp_state, waiting_stream}], Req), 9 | Req3 = cowboy_req:chunked_reply(200, Req2), 10 | timer:sleep(100), 11 | cowboy_req:chunk("streamed_handler\r\n", Req3), 12 | timer:sleep(100), 13 | cowboy_req:chunk("works fine!", Req3), 14 | {ok, Req3, Opts}. 15 | -------------------------------------------------------------------------------- /test/ws_SUITE_data/ws_timeout_hibernate.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | -module(ws_timeout_hibernate). 4 | 5 | -export([init/2]). 6 | -export([websocket_handle/2]). 7 | -export([websocket_info/2]). 8 | 9 | init(Req, _) -> 10 | {cowboy_websocket, Req, undefined, 1000, hibernate}. 11 | 12 | websocket_handle(_Frame, State) -> 13 | {ok, State, hibernate}. 14 | 15 | websocket_info(_Info, State) -> 16 | {ok, State, hibernate}. 17 | -------------------------------------------------------------------------------- /test/handlers/asterisk_h.erl: -------------------------------------------------------------------------------- 1 | %% This module echoes back the value the test is interested in. 2 | 3 | -module(asterisk_h). 4 | 5 | -export([init/2]). 6 | 7 | init(Req, Opts) -> 8 | echo(cowboy_req:header(<<"x-echo">>, Req), Req, Opts). 9 | 10 | echo(What, Req, Opts) -> 11 | F = binary_to_atom(What, latin1), 12 | Value = case cowboy_req:F(Req) of 13 | V when is_integer(V) -> integer_to_binary(V); 14 | V -> V 15 | end, 16 | {ok, cowboy_req:reply(200, #{}, Value, Req), Opts}. 17 | -------------------------------------------------------------------------------- /test/http_SUITE_data/http_chunked.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | -module(http_chunked). 4 | 5 | -export([init/2]). 6 | 7 | init(Req, Opts) -> 8 | Req2 = cowboy_req:chunked_reply(200, Req), 9 | %% Try an empty chunk to make sure the stream doesn't get closed. 10 | cowboy_req:chunk([<<>>], Req2), 11 | timer:sleep(100), 12 | cowboy_req:chunk("chunked_handler\r\n", Req2), 13 | timer:sleep(100), 14 | cowboy_req:chunk("works fine!", Req2), 15 | {ok, Req2, Opts}. 16 | -------------------------------------------------------------------------------- /examples/cookie/src/cookie_sup.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @private 4 | -module(cookie_sup). 5 | -behaviour(supervisor). 6 | 7 | %% API. 8 | -export([start_link/0]). 9 | 10 | %% supervisor. 11 | -export([init/1]). 12 | 13 | %% API. 14 | 15 | -spec start_link() -> {ok, pid()}. 16 | start_link() -> 17 | supervisor:start_link({local, ?MODULE}, ?MODULE, []). 18 | 19 | %% supervisor. 20 | 21 | init([]) -> 22 | Procs = [], 23 | {ok, {{one_for_one, 10, 10}, Procs}}. 24 | -------------------------------------------------------------------------------- /examples/upload/src/upload_sup.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @private 4 | -module(upload_sup). 5 | -behaviour(supervisor). 6 | 7 | %% API. 8 | -export([start_link/0]). 9 | 10 | %% supervisor. 11 | -export([init/1]). 12 | 13 | %% API. 14 | 15 | -spec start_link() -> {ok, pid()}. 16 | start_link() -> 17 | supervisor:start_link({local, ?MODULE}, ?MODULE, []). 18 | 19 | %% supervisor. 20 | 21 | init([]) -> 22 | Procs = [], 23 | {ok, {{one_for_one, 10, 10}, Procs}}. 24 | -------------------------------------------------------------------------------- /test/http_SUITE_data/rest_postonly_resource.erl: -------------------------------------------------------------------------------- 1 | -module(rest_postonly_resource). 2 | 3 | -export([init/2]). 4 | -export([allowed_methods/2]). 5 | -export([content_types_accepted/2]). 6 | -export([from_text/2]). 7 | 8 | init(Req, Opts) -> 9 | {cowboy_rest, Req, Opts}. 10 | 11 | allowed_methods(Req, State) -> 12 | {[<<"POST">>], Req, State}. 13 | 14 | content_types_accepted(Req, State) -> 15 | {[{{<<"text">>, <<"plain">>, '*'}, from_text}], Req, State}. 16 | 17 | from_text(Req, State) -> 18 | {true, Req, State}. 19 | -------------------------------------------------------------------------------- /examples/echo_get/src/echo_get_sup.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @private 4 | -module(echo_get_sup). 5 | -behaviour(supervisor). 6 | 7 | %% API. 8 | -export([start_link/0]). 9 | 10 | %% supervisor. 11 | -export([init/1]). 12 | 13 | %% API. 14 | 15 | -spec start_link() -> {ok, pid()}. 16 | start_link() -> 17 | supervisor:start_link({local, ?MODULE}, ?MODULE, []). 18 | 19 | %% supervisor. 20 | 21 | init([]) -> 22 | Procs = [], 23 | {ok, {{one_for_one, 10, 10}, Procs}}. 24 | -------------------------------------------------------------------------------- /examples/echo_post/src/echo_post_sup.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @private 4 | -module(echo_post_sup). 5 | -behaviour(supervisor). 6 | 7 | %% API. 8 | -export([start_link/0]). 9 | 10 | %% supervisor. 11 | -export([init/1]). 12 | 13 | %% API. 14 | 15 | -spec start_link() -> {ok, pid()}. 16 | start_link() -> 17 | supervisor:start_link({local, ?MODULE}, ?MODULE, []). 18 | 19 | %% supervisor. 20 | 21 | init([]) -> 22 | Procs = [], 23 | {ok, {{one_for_one, 10, 10}, Procs}}. 24 | -------------------------------------------------------------------------------- /examples/websocket/src/websocket_sup.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @private 4 | -module(websocket_sup). 5 | -behaviour(supervisor). 6 | 7 | %% API. 8 | -export([start_link/0]). 9 | 10 | %% supervisor. 11 | -export([init/1]). 12 | 13 | %% API. 14 | 15 | -spec start_link() -> {ok, pid()}. 16 | start_link() -> 17 | supervisor:start_link({local, ?MODULE}, ?MODULE, []). 18 | 19 | %% supervisor. 20 | 21 | init([]) -> 22 | Procs = [], 23 | {ok, {{one_for_one, 10, 10}, Procs}}. 24 | -------------------------------------------------------------------------------- /test/http_SUITE_data/rest_expires_binary.erl: -------------------------------------------------------------------------------- 1 | -module(rest_expires_binary). 2 | 3 | -export([init/2]). 4 | -export([content_types_provided/2]). 5 | -export([get_text_plain/2]). 6 | -export([expires/2]). 7 | 8 | init(Req, Opts) -> 9 | {cowboy_rest, Req, Opts}. 10 | 11 | content_types_provided(Req, State) -> 12 | {[{{<<"text">>, <<"plain">>, []}, get_text_plain}], Req, State}. 13 | 14 | get_text_plain(Req, State) -> 15 | {<<"This is REST!">>, Req, State}. 16 | 17 | expires(Req, State) -> 18 | {<<"0">>, Req, State}. 19 | -------------------------------------------------------------------------------- /examples/chunked_hello_world/src/toppage_handler.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @doc Chunked hello world handler. 4 | -module(toppage_handler). 5 | 6 | -export([init/2]). 7 | 8 | init(Req0, Opts) -> 9 | Req = cowboy_req:stream_reply(200, Req0), 10 | cowboy_req:stream_body("Hello\r\n", nofin, Req), 11 | timer:sleep(1000), 12 | cowboy_req:stream_body("World\r\n", nofin, Req), 13 | timer:sleep(1000), 14 | cowboy_req:stream_body("Chunked!\r\n", fin, Req), 15 | {ok, Req, Opts}. 16 | -------------------------------------------------------------------------------- /examples/error_hook/src/error_hook_sup.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @private 4 | -module(error_hook_sup). 5 | -behaviour(supervisor). 6 | 7 | %% API. 8 | -export([start_link/0]). 9 | 10 | %% supervisor. 11 | -export([init/1]). 12 | 13 | %% API. 14 | 15 | -spec start_link() -> {ok, pid()}. 16 | start_link() -> 17 | supervisor:start_link({local, ?MODULE}, ?MODULE, []). 18 | 19 | %% supervisor. 20 | 21 | init([]) -> 22 | Procs = [], 23 | {ok, {{one_for_one, 10, 10}, Procs}}. 24 | -------------------------------------------------------------------------------- /examples/eventsource/src/eventsource_sup.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @private 4 | -module(eventsource_sup). 5 | -behaviour(supervisor). 6 | 7 | %% API. 8 | -export([start_link/0]). 9 | 10 | %% supervisor. 11 | -export([init/1]). 12 | 13 | %% API. 14 | 15 | -spec start_link() -> {ok, pid()}. 16 | start_link() -> 17 | supervisor:start_link({local, ?MODULE}, ?MODULE, []). 18 | 19 | %% supervisor. 20 | 21 | init([]) -> 22 | Procs = [], 23 | {ok, {{one_for_one, 10, 10}, Procs}}. 24 | -------------------------------------------------------------------------------- /examples/file_server/src/file_server_sup.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @private 4 | -module(file_server_sup). 5 | -behaviour(supervisor). 6 | 7 | %% API. 8 | -export([start_link/0]). 9 | 10 | %% supervisor. 11 | -export([init/1]). 12 | 13 | %% API. 14 | 15 | -spec start_link() -> {ok, pid()}. 16 | start_link() -> 17 | supervisor:start_link({local, ?MODULE}, ?MODULE, []). 18 | 19 | %% supervisor. 20 | 21 | init([]) -> 22 | Procs = [], 23 | {ok, {{one_for_one, 10, 10}, Procs}}. 24 | -------------------------------------------------------------------------------- /examples/hello_world/src/hello_world_sup.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @private 4 | -module(hello_world_sup). 5 | -behaviour(supervisor). 6 | 7 | %% API. 8 | -export([start_link/0]). 9 | 10 | %% supervisor. 11 | -export([init/1]). 12 | 13 | %% API. 14 | 15 | -spec start_link() -> {ok, pid()}. 16 | start_link() -> 17 | supervisor:start_link({local, ?MODULE}, ?MODULE, []). 18 | 19 | %% supervisor. 20 | 21 | init([]) -> 22 | Procs = [], 23 | {ok, {{one_for_one, 10, 10}, Procs}}. 24 | -------------------------------------------------------------------------------- /examples/rest_pastebin/src/rest_pastebin_sup.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @private 4 | -module(rest_pastebin_sup). 5 | -behaviour(supervisor). 6 | 7 | %% API. 8 | -export([start_link/0]). 9 | 10 | %% supervisor. 11 | -export([init/1]). 12 | 13 | %% API. 14 | 15 | -spec start_link() -> {ok, pid()}. 16 | start_link() -> 17 | supervisor:start_link({local, ?MODULE}, ?MODULE, []). 18 | 19 | %% supervisor. 20 | 21 | init([]) -> 22 | Procs = [], 23 | {ok, {{one_for_one, 10, 10}, Procs}}. 24 | -------------------------------------------------------------------------------- /examples/rest_basic_auth/src/rest_basic_auth_sup.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @private 4 | -module(rest_basic_auth_sup). 5 | -behaviour(supervisor). 6 | 7 | %% API. 8 | -export([start_link/0]). 9 | 10 | %% supervisor. 11 | -export([init/1]). 12 | 13 | %% API. 14 | 15 | -spec start_link() -> {ok, pid()}. 16 | start_link() -> 17 | supervisor:start_link({local, ?MODULE}, ?MODULE, []). 18 | 19 | %% supervisor. 20 | 21 | init([]) -> 22 | Procs = [], 23 | {ok, {{one_for_one, 10, 10}, Procs}}. 24 | -------------------------------------------------------------------------------- /examples/ssl_hello_world/src/ssl_hello_world_sup.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @private 4 | -module(ssl_hello_world_sup). 5 | -behaviour(supervisor). 6 | 7 | %% API. 8 | -export([start_link/0]). 9 | 10 | %% supervisor. 11 | -export([init/1]). 12 | 13 | %% API. 14 | 15 | -spec start_link() -> {ok, pid()}. 16 | start_link() -> 17 | supervisor:start_link({local, ?MODULE}, ?MODULE, []). 18 | 19 | %% supervisor. 20 | 21 | init([]) -> 22 | Procs = [], 23 | {ok, {{one_for_one, 10, 10}, Procs}}. 24 | -------------------------------------------------------------------------------- /examples/compress_response/src/compress_response_sup.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @private 4 | -module(compress_response_sup). 5 | -behaviour(supervisor). 6 | 7 | %% API. 8 | -export([start_link/0]). 9 | 10 | %% supervisor. 11 | -export([init/1]). 12 | 13 | %% API. 14 | 15 | -spec start_link() -> {ok, pid()}. 16 | start_link() -> 17 | supervisor:start_link({local, ?MODULE}, ?MODULE, []). 18 | 19 | %% supervisor. 20 | 21 | init([]) -> 22 | Procs = [], 23 | {ok, {{one_for_one, 10, 10}, Procs}}. 24 | -------------------------------------------------------------------------------- /examples/rest_hello_world/src/rest_hello_world_sup.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @private 4 | -module(rest_hello_world_sup). 5 | -behaviour(supervisor). 6 | 7 | %% API. 8 | -export([start_link/0]). 9 | 10 | %% supervisor. 11 | -export([init/1]). 12 | 13 | %% API. 14 | 15 | -spec start_link() -> {ok, pid()}. 16 | start_link() -> 17 | supervisor:start_link({local, ?MODULE}, ?MODULE, []). 18 | 19 | %% supervisor. 20 | 21 | init([]) -> 22 | Procs = [], 23 | {ok, {{one_for_one, 10, 10}, Procs}}. 24 | -------------------------------------------------------------------------------- /test/http_SUITE_data/http_req_attr.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @todo That module was clearly meant to do more than one 4 | %% thing and yet doesn't. 5 | -module(http_req_attr). 6 | 7 | -export([init/2]). 8 | 9 | init(Req, Opts) -> 10 | #{attr := Attr} = cowboy_req:match_qs([attr], Req), 11 | <<"host_and_port">> = Attr, 12 | Host = cowboy_req:host(Req), 13 | Port = cowboy_req:port(Req), 14 | Value = [Host, "\n", integer_to_list(Port)], 15 | {ok, cowboy_req:reply(200, #{}, Value, Req), Opts}. 16 | -------------------------------------------------------------------------------- /examples/chunked_hello_world/src/chunked_hello_world_sup.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @private 4 | -module(chunked_hello_world_sup). 5 | -behaviour(supervisor). 6 | 7 | %% API. 8 | -export([start_link/0]). 9 | 10 | %% supervisor. 11 | -export([init/1]). 12 | 13 | %% API. 14 | 15 | -spec start_link() -> {ok, pid()}. 16 | start_link() -> 17 | supervisor:start_link({local, ?MODULE}, ?MODULE, []). 18 | 19 | %% supervisor. 20 | 21 | init([]) -> 22 | Procs = [], 23 | {ok, {{one_for_one, 10, 10}, Procs}}. 24 | -------------------------------------------------------------------------------- /examples/markdown_middleware/src/markdown_middleware_sup.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @private 4 | -module(markdown_middleware_sup). 5 | -behaviour(supervisor). 6 | 7 | %% API. 8 | -export([start_link/0]). 9 | 10 | %% supervisor. 11 | -export([init/1]). 12 | 13 | %% API. 14 | 15 | -spec start_link() -> {ok, pid()}. 16 | start_link() -> 17 | supervisor:start_link({local, ?MODULE}, ?MODULE, []). 18 | 19 | %% supervisor. 20 | 21 | init([]) -> 22 | Procs = [], 23 | {ok, {{one_for_one, 10, 10}, Procs}}. 24 | -------------------------------------------------------------------------------- /examples/rest_stream_response/src/rest_stream_response_sup.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @private 4 | -module(rest_stream_response_sup). 5 | -behaviour(supervisor). 6 | 7 | %% API. 8 | -export([start_link/0]). 9 | 10 | %% supervisor. 11 | -export([init/1]). 12 | 13 | %% API. 14 | 15 | -spec start_link() -> {ok, pid()}. 16 | start_link() -> 17 | supervisor:start_link({local, ?MODULE}, ?MODULE, []). 18 | 19 | %% supervisor. 20 | 21 | init([]) -> 22 | Procs = [], 23 | {ok, {{one_for_one, 10, 10}, Procs}}. 24 | -------------------------------------------------------------------------------- /examples/cookie/templates/toppage.dtl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Cowboy Cookie Example 6 | 7 | 8 | 9 |

Cowboy Cookie Example

10 |

Refresh the page to see the next cookie.

11 | 12 |

Cookie Set Server-Side

13 |

{{ server }}

14 | 15 |

Cookie Set Client-Side

16 |

{{ client }}

17 | 18 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /examples/upload/src/upload_handler.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @doc Upload handler. 4 | -module(upload_handler). 5 | 6 | -export([init/2]). 7 | 8 | init(Req, Opts) -> 9 | {ok, Headers, Req2} = cowboy_req:read_part(Req), 10 | {ok, Data, Req3} = cowboy_req:read_part_body(Req2), 11 | {file, <<"inputfile">>, Filename, ContentType, _TE} 12 | = cow_multipart:form_data(Headers), 13 | io:format("Received file ~p of content-type ~p as follow:~n~p~n~n", 14 | [Filename, ContentType, Data]), 15 | {ok, Req3, Opts}. 16 | -------------------------------------------------------------------------------- /test/http_SUITE_data/rest_post_charset_resource.erl: -------------------------------------------------------------------------------- 1 | -module(rest_post_charset_resource). 2 | 3 | -export([init/2]). 4 | -export([allowed_methods/2]). 5 | -export([content_types_accepted/2]). 6 | -export([from_text/2]). 7 | 8 | init(Req, Opts) -> 9 | {cowboy_rest, Req, Opts}. 10 | 11 | allowed_methods(Req, State) -> 12 | {[<<"POST">>], Req, State}. 13 | 14 | content_types_accepted(Req, State) -> 15 | {[{{<<"text">>, <<"plain">>, [{<<"charset">>, <<"utf-8">>}]}, 16 | from_text}], Req, State}. 17 | 18 | from_text(Req, State) -> 19 | {true, Req, State}. 20 | -------------------------------------------------------------------------------- /examples/cookie/src/cookie_app.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @private 4 | -module(cookie_app). 5 | -behaviour(application). 6 | 7 | %% API. 8 | -export([start/2]). 9 | -export([stop/1]). 10 | 11 | %% API. 12 | 13 | start(_Type, _Args) -> 14 | Dispatch = cowboy_router:compile([ 15 | {'_', [ 16 | {'_', toppage_handler, []} 17 | ]} 18 | ]), 19 | {ok, _} = cowboy:start_clear(http, 100, [{port, 8080}], #{ 20 | env => #{dispatch => Dispatch} 21 | }), 22 | cookie_sup:start_link(). 23 | 24 | stop(_State) -> 25 | ok. 26 | -------------------------------------------------------------------------------- /test/http_SUITE_data/rest_nodelete_resource.erl: -------------------------------------------------------------------------------- 1 | -module(rest_nodelete_resource). 2 | 3 | -export([init/2]). 4 | -export([allowed_methods/2]). 5 | -export([content_types_provided/2]). 6 | -export([get_text_plain/2]). 7 | 8 | init(Req, Opts) -> 9 | {cowboy_rest, Req, Opts}. 10 | 11 | allowed_methods(Req, State) -> 12 | {[<<"GET">>, <<"HEAD">>, <<"DELETE">>], Req, State}. 13 | 14 | content_types_provided(Req, State) -> 15 | {[{{<<"text">>, <<"plain">>, []}, get_text_plain}], Req, State}. 16 | 17 | get_text_plain(Req, State) -> 18 | {<<"This is REST!">>, Req, State}. 19 | -------------------------------------------------------------------------------- /test/ws_SUITE_data/ws_echo.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | -module(ws_echo). 4 | 5 | -export([init/2]). 6 | -export([websocket_handle/2]). 7 | -export([websocket_info/2]). 8 | 9 | init(Req, _) -> 10 | {cowboy_websocket, Req, undefined}. 11 | 12 | websocket_handle({text, Data}, State) -> 13 | {reply, {text, Data}, State}; 14 | websocket_handle({binary, Data}, State) -> 15 | {reply, {binary, Data}, State}; 16 | websocket_handle(_Frame, State) -> 17 | {ok, State}. 18 | 19 | websocket_info(_Info, State) -> 20 | {ok, State}. 21 | -------------------------------------------------------------------------------- /examples/echo_get/src/echo_get_app.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @private 4 | -module(echo_get_app). 5 | -behaviour(application). 6 | 7 | %% API. 8 | -export([start/2]). 9 | -export([stop/1]). 10 | 11 | %% API. 12 | 13 | start(_Type, _Args) -> 14 | Dispatch = cowboy_router:compile([ 15 | {'_', [ 16 | {"/", toppage_handler, []} 17 | ]} 18 | ]), 19 | {ok, _} = cowboy:start_clear(http, 100, [{port, 8080}], #{ 20 | env => #{dispatch => Dispatch} 21 | }), 22 | echo_get_sup:start_link(). 23 | 24 | stop(_State) -> 25 | ok. 26 | -------------------------------------------------------------------------------- /ebin/cowboy.app: -------------------------------------------------------------------------------- 1 | {application, cowboy, [ 2 | {description, "Small, fast, modern HTTP server."}, 3 | {vsn, "2.0.0-pre.2"}, 4 | {modules, ['cowboy','cowboy_app','cowboy_bstr','cowboy_clear','cowboy_clock','cowboy_constraints','cowboy_handler','cowboy_http','cowboy_http2','cowboy_loop','cowboy_middleware','cowboy_req','cowboy_rest','cowboy_router','cowboy_static','cowboy_stream','cowboy_stream_h','cowboy_sub_protocol','cowboy_sup','cowboy_tls','cowboy_websocket']}, 5 | {registered, [cowboy_sup,cowboy_clock]}, 6 | {applications, [kernel,stdlib,crypto,cowlib,ranch]}, 7 | {mod, {cowboy_app, []}} 8 | ]}. -------------------------------------------------------------------------------- /examples/echo_post/src/echo_post_app.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @private 4 | -module(echo_post_app). 5 | -behaviour(application). 6 | 7 | %% API. 8 | -export([start/2]). 9 | -export([stop/1]). 10 | 11 | %% API. 12 | 13 | start(_Type, _Args) -> 14 | Dispatch = cowboy_router:compile([ 15 | {'_', [ 16 | {"/", toppage_handler, []} 17 | ]} 18 | ]), 19 | {ok, _} = cowboy:start_clear(http, 100, [{port, 8080}], #{ 20 | env => #{dispatch => Dispatch} 21 | }), 22 | echo_post_sup:start_link(). 23 | 24 | stop(_State) -> 25 | ok. 26 | -------------------------------------------------------------------------------- /test/ws_SUITE_data/ws_send_many.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | -module(ws_send_many). 4 | 5 | -export([init/2]). 6 | -export([websocket_init/1]). 7 | -export([websocket_handle/2]). 8 | -export([websocket_info/2]). 9 | 10 | init(Req, Opts) -> 11 | {cowboy_websocket, Req, Opts}. 12 | 13 | websocket_init(State) -> 14 | erlang:send_after(10, self(), send_many), 15 | {ok, State}. 16 | 17 | websocket_handle(_Frame, State) -> 18 | {ok, State}. 19 | 20 | websocket_info(send_many, State = [{sequence, Sequence}]) -> 21 | {reply, Sequence, State}. 22 | -------------------------------------------------------------------------------- /examples/hello_world/src/hello_world_app.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @private 4 | -module(hello_world_app). 5 | -behaviour(application). 6 | 7 | %% API. 8 | -export([start/2]). 9 | -export([stop/1]). 10 | 11 | %% API. 12 | 13 | start(_Type, _Args) -> 14 | Dispatch = cowboy_router:compile([ 15 | {'_', [ 16 | {"/", toppage_handler, []} 17 | ]} 18 | ]), 19 | {ok, _} = cowboy:start_clear(http, 100, [{port, 8080}], #{ 20 | env => #{dispatch => Dispatch} 21 | }), 22 | hello_world_sup:start_link(). 23 | 24 | stop(_State) -> 25 | ok. 26 | -------------------------------------------------------------------------------- /examples/markdown_middleware/README.asciidoc: -------------------------------------------------------------------------------- 1 | = Middleware example 2 | 3 | To try this example, you need GNU `make` and `git` in your PATH. 4 | 5 | To build and run the example, use the following command: 6 | 7 | [source,bash] 8 | $ make run 9 | 10 | Then point your browser to http://localhost:8080/video.html 11 | 12 | Cowboy will serve all the files you put in the `priv` directory. 13 | If you request a `.html` file that has a corresponding `.md` file 14 | that has been modified more recently than the `.html` file, the 15 | Markdown file will be converted to HTML and served by Cowboy. 16 | -------------------------------------------------------------------------------- /test/http_SUITE_data/http_multipart.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | -module(http_multipart). 4 | 5 | -export([init/2]). 6 | 7 | init(Req, Opts) -> 8 | {Result, Req2} = acc_multipart(Req, []), 9 | {ok, cowboy_req:reply(200, #{}, term_to_binary(Result), Req2), Opts}. 10 | 11 | acc_multipart(Req, Acc) -> 12 | case cowboy_req:read_part(Req) of 13 | {ok, Headers, Req2} -> 14 | {ok, Body, Req3} = cowboy_req:read_part_body(Req2), 15 | acc_multipart(Req3, [{Headers, Body}|Acc]); 16 | {done, Req2} -> 17 | {lists:reverse(Acc), Req2} 18 | end. 19 | -------------------------------------------------------------------------------- /test/ws_SUITE_data/ws_subprotocol.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | -module(ws_subprotocol). 4 | 5 | -export([init/2]). 6 | -export([websocket_handle/2]). 7 | -export([websocket_info/2]). 8 | 9 | init(Req, Opts) -> 10 | [Protocol | _] = cowboy_req:parse_header(<<"sec-websocket-protocol">>, Req), 11 | Req2 = cowboy_req:set_resp_header(<<"sec-websocket-protocol">>, Protocol, Req), 12 | {cowboy_websocket, Req2, Opts, 1000}. 13 | 14 | websocket_handle(_Frame, State) -> 15 | {ok, State}. 16 | 17 | websocket_info(_Info, State) -> 18 | {ok, State}. 19 | -------------------------------------------------------------------------------- /examples/rest_basic_auth/src/rest_basic_auth_app.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @private 4 | -module(rest_basic_auth_app). 5 | -behaviour(application). 6 | 7 | %% API. 8 | -export([start/2]). 9 | -export([stop/1]). 10 | 11 | %% API. 12 | 13 | start(_Type, _Args) -> 14 | Dispatch = cowboy_router:compile([ 15 | {'_', [ 16 | {"/", toppage_handler, []} 17 | ]} 18 | ]), 19 | {ok, _} = cowboy:start_clear(http, 100, [{port, 8080}], #{ 20 | env => #{dispatch => Dispatch} 21 | }), 22 | rest_basic_auth_sup:start_link(). 23 | 24 | stop(_State) -> 25 | ok. 26 | -------------------------------------------------------------------------------- /examples/rest_hello_world/src/rest_hello_world_app.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @private 4 | -module(rest_hello_world_app). 5 | -behaviour(application). 6 | 7 | %% API. 8 | -export([start/2]). 9 | -export([stop/1]). 10 | 11 | %% API. 12 | 13 | start(_Type, _Args) -> 14 | Dispatch = cowboy_router:compile([ 15 | {'_', [ 16 | {"/", toppage_handler, []} 17 | ]} 18 | ]), 19 | {ok, _} = cowboy:start_clear(http, 100, [{port, 8080}], #{ 20 | env => #{dispatch => Dispatch} 21 | }), 22 | rest_hello_world_sup:start_link(). 23 | 24 | stop(_State) -> 25 | ok. 26 | -------------------------------------------------------------------------------- /examples/rest_pastebin/src/rest_pastebin_app.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @private 4 | -module(rest_pastebin_app). 5 | -behaviour(application). 6 | 7 | %% API. 8 | -export([start/2]). 9 | -export([stop/1]). 10 | 11 | %% API. 12 | 13 | start(_Type, _Args) -> 14 | Dispatch = cowboy_router:compile([ 15 | {'_', [ 16 | {"/[:paste_id]", toppage_handler, []} 17 | ]} 18 | ]), 19 | {ok, _} = cowboy:start_clear(http, 100, [{port, 8080}], #{ 20 | env => #{dispatch => Dispatch} 21 | }), 22 | rest_pastebin_sup:start_link(). 23 | 24 | stop(_State) -> 25 | ok. 26 | -------------------------------------------------------------------------------- /test/http_SUITE_data/http_errors.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | -module(http_errors). 4 | 5 | -export([init/2]). 6 | 7 | init(Req, _Opts) -> 8 | #{'case' := Case} = cowboy_req:match_qs(['case'], Req), 9 | case_init(Case, Req). 10 | 11 | case_init(<<"init_before_reply">> = Case, _Req) -> 12 | ct_helper_error_h:ignore(?MODULE, case_init, 2), 13 | error(Case); 14 | case_init(<<"init_after_reply">> = Case, Req) -> 15 | ct_helper_error_h:ignore(?MODULE, case_init, 2), 16 | _ = cowboy_req:reply(200, #{}, "http_handler_crashes", Req), 17 | error(Case). 18 | -------------------------------------------------------------------------------- /examples/chunked_hello_world/src/chunked_hello_world_app.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @private 4 | -module(chunked_hello_world_app). 5 | -behaviour(application). 6 | 7 | %% API. 8 | -export([start/2]). 9 | -export([stop/1]). 10 | 11 | %% API. 12 | 13 | start(_Type, _Args) -> 14 | Dispatch = cowboy_router:compile([ 15 | {'_', [ 16 | {"/", toppage_handler, []} 17 | ]} 18 | ]), 19 | {ok, _} = cowboy:start_clear(http, 100, [{port, 8080}], #{ 20 | env => #{dispatch => Dispatch} 21 | }), 22 | chunked_hello_world_sup:start_link(). 23 | 24 | stop(_State) -> 25 | ok. 26 | -------------------------------------------------------------------------------- /examples/compress_response/src/compress_response_app.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @private 4 | -module(compress_response_app). 5 | -behaviour(application). 6 | 7 | %% API. 8 | -export([start/2]). 9 | -export([stop/1]). 10 | 11 | %% API. 12 | 13 | start(_Type, _Args) -> 14 | Dispatch = cowboy_router:compile([ 15 | {'_', [ 16 | {"/", toppage_handler, []} 17 | ]} 18 | ]), 19 | {ok, _} = cowboy:start_http(http, 100, [{port, 8080}], [ 20 | {compress, true}, 21 | {env, [{dispatch, Dispatch}]} 22 | ]), 23 | compress_response_sup:start_link(). 24 | 25 | stop(_State) -> 26 | ok. 27 | -------------------------------------------------------------------------------- /examples/rest_pastebin/priv/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Simple Pastebin 5 | 6 | 7 |

Simple Pastebin

8 |

9 | You can paste your text into the text field to submit, or you can 10 | capture the output of a command with: 11 |

12 | 13 | command | curl -i --data-urlencode paste@- localhost:8080 14 | 15 |
16 | 17 |
18 | 19 |
20 |
21 | 22 | 23 | -------------------------------------------------------------------------------- /examples/upload/src/upload_app.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @private 4 | -module(upload_app). 5 | -behaviour(application). 6 | 7 | %% API. 8 | -export([start/2]). 9 | -export([stop/1]). 10 | 11 | %% API. 12 | start(_Type, _Args) -> 13 | Dispatch = cowboy_router:compile([ 14 | {'_', [ 15 | {"/", cowboy_static, {priv_file, upload, "index.html"}}, 16 | {"/upload", upload_handler, []} 17 | ]} 18 | ]), 19 | {ok, _} = cowboy:start_clear(http, 100, [{port, 8080}], #{ 20 | env => #{dispatch => Dispatch} 21 | }), 22 | upload_sup:start_link(). 23 | 24 | stop(_State) -> 25 | ok. 26 | -------------------------------------------------------------------------------- /test/http_SUITE_data/rest_expires.erl: -------------------------------------------------------------------------------- 1 | -module(rest_expires). 2 | 3 | -export([init/2]). 4 | -export([content_types_provided/2]). 5 | -export([get_text_plain/2]). 6 | -export([expires/2]). 7 | -export([last_modified/2]). 8 | 9 | init(Req, Opts) -> 10 | {cowboy_rest, Req, Opts}. 11 | 12 | content_types_provided(Req, State) -> 13 | {[{{<<"text">>, <<"plain">>, []}, get_text_plain}], Req, State}. 14 | 15 | get_text_plain(Req, State) -> 16 | {<<"This is REST!">>, Req, State}. 17 | 18 | expires(Req, State) -> 19 | {{{2012, 9, 21}, {22, 36, 14}}, Req, State}. 20 | 21 | last_modified(Req, State) -> 22 | {{{2012, 9, 21}, {22, 36, 14}}, Req, State}. 23 | -------------------------------------------------------------------------------- /test/ws_SUITE_data/ws_timeout_cancel.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | -module(ws_timeout_cancel). 4 | 5 | -export([init/2]). 6 | -export([websocket_handle/2]). 7 | -export([websocket_info/2]). 8 | 9 | init(Req, _) -> 10 | erlang:start_timer(500, self(), should_not_cancel_timer), 11 | {cowboy_websocket, Req, undefined, 1000}. 12 | 13 | websocket_handle({text, Data}, State) -> 14 | {reply, {text, Data}, State}; 15 | websocket_handle({binary, Data}, State) -> 16 | {reply, {binary, Data}, State}. 17 | 18 | websocket_info(_Info, State) -> 19 | erlang:start_timer(500, self(), should_not_cancel_timer), 20 | {ok, State}. 21 | -------------------------------------------------------------------------------- /test/handlers/loop_handler_body_h.erl: -------------------------------------------------------------------------------- 1 | %% This module implements a loop handler that reads 2 | %% the request body after sending itself a message, 3 | %% checks that its size is exactly 100000 bytes, 4 | %% then sends a 200 reply back. 5 | 6 | -module(loop_handler_body_h). 7 | 8 | -export([init/2]). 9 | -export([info/3]). 10 | -export([terminate/3]). 11 | 12 | init(Req, _) -> 13 | self() ! timeout, 14 | {cowboy_loop, Req, undefined, 5000, hibernate}. 15 | 16 | info(timeout, Req0, State) -> 17 | {ok, Body, Req} = cowboy_req:read_body(Req0), 18 | 100000 = byte_size(Body), 19 | {stop, cowboy_req:reply(200, Req), State}. 20 | 21 | terminate(stop, _, _) -> 22 | ok. 23 | -------------------------------------------------------------------------------- /examples/eventsource/src/eventsource_app.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @private 4 | -module(eventsource_app). 5 | -behaviour(application). 6 | 7 | %% API. 8 | -export([start/2]). 9 | -export([stop/1]). 10 | 11 | %% API. 12 | 13 | start(_Type, _Args) -> 14 | Dispatch = cowboy_router:compile([ 15 | {'_', [ 16 | {"/eventsource", eventsource_handler, []}, 17 | {"/", cowboy_static, {priv_file, eventsource, "index.html"}} 18 | ]} 19 | ]), 20 | {ok, _} = cowboy:start_clear(http, 100, [{port, 8080}], #{ 21 | env => #{dispatch => Dispatch} 22 | }), 23 | eventsource_sup:start_link(). 24 | 25 | stop(_State) -> 26 | ok. 27 | -------------------------------------------------------------------------------- /test/http_SUITE_data/http_echo_body.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | -module(http_echo_body). 4 | 5 | -export([init/2]). 6 | 7 | init(Req, Opts) -> 8 | true = cowboy_req:has_body(Req), 9 | Req3 = case cowboy_req:read_body(Req, [{length, 1000000}]) of 10 | {ok, Body, Req2} -> handle_body(Req2, Body); 11 | {more, _, Req2} -> handle_badlength(Req2) 12 | end, 13 | {ok, Req3, Opts}. 14 | 15 | handle_badlength(Req) -> 16 | cowboy_req:reply(413, #{}, <<"Request entity too large">>, Req). 17 | 18 | handle_body(Req, Body) -> 19 | Size = cowboy_req:body_length(Req), 20 | Size = byte_size(Body), 21 | cowboy_req:reply(200, #{}, Body, Req). 22 | -------------------------------------------------------------------------------- /test/handlers/loop_handler_timeout_h.erl: -------------------------------------------------------------------------------- 1 | %% This module implements a loop handler that sends 2 | %% itself a timeout that will intentionally arrive 3 | %% too late, as it configures itself to only wait 4 | %% 200ms before closing the connection in init/2. 5 | %% This results in a 204 reply being sent back by Cowboy. 6 | 7 | -module(loop_handler_timeout_h). 8 | 9 | -export([init/2]). 10 | -export([info/3]). 11 | -export([terminate/3]). 12 | 13 | init(Req, _) -> 14 | erlang:send_after(1000, self(), timeout), 15 | {cowboy_loop, Req, undefined, 200, hibernate}. 16 | 17 | info(timeout, Req, State) -> 18 | {stop, cowboy_req:reply(500, Req), State}. 19 | 20 | terminate(timeout, _, _) -> 21 | ok. 22 | -------------------------------------------------------------------------------- /examples/websocket/src/websocket_app.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @private 4 | -module(websocket_app). 5 | -behaviour(application). 6 | 7 | %% API. 8 | -export([start/2]). 9 | -export([stop/1]). 10 | 11 | %% API. 12 | start(_Type, _Args) -> 13 | Dispatch = cowboy_router:compile([ 14 | {'_', [ 15 | {"/", cowboy_static, {priv_file, websocket, "index.html"}}, 16 | {"/websocket", ws_handler, []}, 17 | {"/static/[...]", cowboy_static, {priv_dir, websocket, "static"}} 18 | ]} 19 | ]), 20 | {ok, _} = cowboy:start_clear(http, 100, [{port, 8080}], #{ 21 | env => #{dispatch => Dispatch} 22 | }), 23 | websocket_sup:start_link(). 24 | 25 | stop(_State) -> 26 | ok. 27 | -------------------------------------------------------------------------------- /examples/markdown_middleware/src/markdown_middleware_app.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @private 4 | -module(markdown_middleware_app). 5 | -behaviour(application). 6 | 7 | %% API. 8 | -export([start/2]). 9 | -export([stop/1]). 10 | 11 | %% API. 12 | 13 | start(_Type, _Args) -> 14 | Dispatch = cowboy_router:compile([ 15 | {'_', [ 16 | {"/[...]", cowboy_static, {priv_dir, markdown_middleware, ""}} 17 | ]} 18 | ]), 19 | {ok, _} = cowboy:start_clear(http, 100, [{port, 8080}], #{ 20 | env => #{dispatch => Dispatch}, 21 | middlewares => [cowboy_router, markdown_converter, cowboy_handler] 22 | }), 23 | markdown_middleware_sup:start_link(). 24 | 25 | stop(_State) -> 26 | ok. 27 | -------------------------------------------------------------------------------- /examples/cookie/src/toppage_handler.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @doc Cookie handler. 4 | -module(toppage_handler). 5 | 6 | -export([init/2]). 7 | 8 | init(Req0, Opts) -> 9 | NewValue = integer_to_list(rand:uniform(1000000)), 10 | Req1 = cowboy_req:set_resp_cookie(<<"server">>, NewValue, 11 | #{path => <<"/">>}, Req0), 12 | #{client := ClientCookie, server := ServerCookie} 13 | = cowboy_req:match_cookies([{client, [], <<>>}, {server, [], <<>>}], Req1), 14 | {ok, Body} = toppage_dtl:render([ 15 | {client, ClientCookie}, 16 | {server, ServerCookie} 17 | ]), 18 | Req = cowboy_req:reply(200, #{ 19 | <<"content-type">> => <<"text/html">> 20 | }, Body, Req1), 21 | {ok, Req, Opts}. 22 | -------------------------------------------------------------------------------- /examples/echo_get/src/toppage_handler.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @doc GET echo handler. 4 | -module(toppage_handler). 5 | 6 | -export([init/2]). 7 | 8 | init(Req0, Opts) -> 9 | Method = cowboy_req:method(Req0), 10 | #{echo := Echo} = cowboy_req:match_qs([{echo, [], undefined}], Req0), 11 | Req = echo(Method, Echo, Req0), 12 | {ok, Req, Opts}. 13 | 14 | echo(<<"GET">>, undefined, Req) -> 15 | cowboy_req:reply(400, #{}, <<"Missing echo parameter.">>, Req); 16 | echo(<<"GET">>, Echo, Req) -> 17 | cowboy_req:reply(200, #{ 18 | <<"content-type">> => <<"text/plain; charset=utf-8">> 19 | }, Echo, Req); 20 | echo(_, _, Req) -> 21 | %% Method not allowed. 22 | cowboy_req:reply(405, Req). 23 | -------------------------------------------------------------------------------- /examples/websocket/src/ws_handler.erl: -------------------------------------------------------------------------------- 1 | -module(ws_handler). 2 | 3 | -export([init/2]). 4 | -export([websocket_init/1]). 5 | -export([websocket_handle/2]). 6 | -export([websocket_info/2]). 7 | 8 | init(Req, Opts) -> 9 | {cowboy_websocket, Req, Opts}. 10 | 11 | websocket_init(State) -> 12 | erlang:start_timer(1000, self(), <<"Hello!">>), 13 | {ok, State}. 14 | 15 | websocket_handle({text, Msg}, State) -> 16 | {reply, {text, << "That's what she said! ", Msg/binary >>}, State}; 17 | websocket_handle(_Data, State) -> 18 | {ok, State}. 19 | 20 | websocket_info({timeout, _Ref, Msg}, State) -> 21 | erlang:start_timer(1000, self(), <<"How' you doin'?">>), 22 | {reply, {text, Msg}, State}; 23 | websocket_info(_Info, State) -> 24 | {ok, State}. 25 | -------------------------------------------------------------------------------- /test/http_SUITE_data/rest_missing_callbacks.erl: -------------------------------------------------------------------------------- 1 | -module(rest_missing_callbacks). 2 | 3 | -export([init/2]). 4 | -export([allowed_methods/2]). 5 | -export([content_types_accepted/2]). 6 | -export([content_types_provided/2]). 7 | 8 | init(Req, Opts) -> 9 | {cowboy_rest, Req, Opts}. 10 | 11 | allowed_methods(Req, State) -> 12 | {[<<"GET">>, <<"PUT">>], Req, State}. 13 | 14 | content_types_accepted(Req, State) -> 15 | ct_helper_error_h:ignore(cowboy_rest, process_content_type, 3), 16 | {[ 17 | {<<"application/json">>, put_application_json} 18 | ], Req, State}. 19 | 20 | content_types_provided(Req, State) -> 21 | ct_helper_error_h:ignore(cowboy_rest, set_resp_body, 2), 22 | {[ 23 | {<<"text/plain">>, get_text_plain} 24 | ], Req, State}. 25 | -------------------------------------------------------------------------------- /examples/eventsource/src/eventsource_handler.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @doc EventSource emitter. 4 | -module(eventsource_handler). 5 | 6 | -export([init/2]). 7 | -export([info/3]). 8 | 9 | init(Req0, Opts) -> 10 | Req = cowboy_req:stream_reply(200, #{ 11 | <<"content-type">> => <<"text/event-stream">> 12 | }, Req0), 13 | erlang:send_after(1000, self(), {message, "Tick"}), 14 | {cowboy_loop, Req, Opts, 5000}. 15 | 16 | info({message, Msg}, Req, State) -> 17 | cowboy_req:stream_body(["id: ", id(), "\ndata: ", Msg, "\n\n"], nofin, Req), 18 | erlang:send_after(1000, self(), {message, "Tick"}), 19 | {ok, Req, State}. 20 | 21 | id() -> 22 | integer_to_list(erlang:unique_integer([positive, monotonic]), 16). 23 | -------------------------------------------------------------------------------- /examples/file_server/src/file_server_app.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @private 4 | -module(file_server_app). 5 | -behaviour(application). 6 | 7 | %% API. 8 | -export([start/2]). 9 | -export([stop/1]). 10 | 11 | %% API. 12 | 13 | start(_Type, _Args) -> 14 | Dispatch = cowboy_router:compile([ 15 | {'_', [ 16 | {"/[...]", cowboy_static, {priv_dir, file_server, "", [ 17 | {mimetypes, cow_mimetypes, all}, 18 | {dir_handler, directory_handler} 19 | ]}} 20 | ]} 21 | ]), 22 | {ok, _} = cowboy:start_clear(http, 100, [{port, 8080}], #{ 23 | env => #{dispatch => Dispatch}, 24 | middlewares => [cowboy_router, directory_lister, cowboy_handler] 25 | }), 26 | file_server_sup:start_link(). 27 | 28 | stop(_State) -> 29 | ok. 30 | -------------------------------------------------------------------------------- /test/http_SUITE_data/http_multipart_stream.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | -module(http_multipart_stream). 4 | 5 | -export([init/2]). 6 | 7 | init(Req, Opts) -> 8 | Req2 = multipart(Req), 9 | {ok, cowboy_req:reply(200, Req2), Opts}. 10 | 11 | multipart(Req) -> 12 | case cowboy_req:read_part(Req) of 13 | {ok, [{<<"content-length">>, BinLength}], Req2} -> 14 | Length = binary_to_integer(BinLength), 15 | {Length, Req3} = stream_body(Req2, 0), 16 | multipart(Req3); 17 | {done, Req2} -> 18 | Req2 19 | end. 20 | 21 | stream_body(Req, N) -> 22 | case cowboy_req:read_part_body(Req) of 23 | {ok, Data, Req2} -> 24 | {N + byte_size(Data), Req2}; 25 | {more, Data, Req2} -> 26 | stream_body(Req2, N + byte_size(Data)) 27 | end. 28 | -------------------------------------------------------------------------------- /examples/ssl_hello_world/src/ssl_hello_world_app.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @private 4 | -module(ssl_hello_world_app). 5 | -behaviour(application). 6 | 7 | %% API. 8 | -export([start/2]). 9 | -export([stop/1]). 10 | 11 | %% API. 12 | 13 | start(_Type, _Args) -> 14 | Dispatch = cowboy_router:compile([ 15 | {'_', [ 16 | {"/", toppage_handler, []} 17 | ]} 18 | ]), 19 | PrivDir = code:priv_dir(ssl_hello_world), 20 | {ok, _} = cowboy:start_tls(https, 100, [ 21 | {port, 8443}, 22 | {cacertfile, PrivDir ++ "/ssl/cowboy-ca.crt"}, 23 | {certfile, PrivDir ++ "/ssl/server.crt"}, 24 | {keyfile, PrivDir ++ "/ssl/server.key"} 25 | ], #{env => #{dispatch => Dispatch}}), 26 | ssl_hello_world_sup:start_link(). 27 | 28 | stop(_State) -> 29 | ok. 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011-2014, Loïc Hoguin 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /test/handlers/long_polling_h.erl: -------------------------------------------------------------------------------- 1 | %% This module implements a loop handler for long-polling. 2 | %% It starts by sending itself a message after 200ms, 3 | %% then sends another after that for a total of 3 messages. 4 | %% When it receives the last message, it sends a 102 reply back. 5 | 6 | -module(long_polling_h). 7 | 8 | -export([init/2]). 9 | -export([info/3]). 10 | -export([terminate/3]). 11 | 12 | init(Req, _) -> 13 | erlang:send_after(200, self(), timeout), 14 | {cowboy_loop, Req, 2, 5000, hibernate}. 15 | 16 | info(timeout, Req, 0) -> 17 | %% @todo Why 102? 18 | {stop, cowboy_req:reply(102, Req), 0}; 19 | info(timeout, Req, Count) -> 20 | erlang:send_after(200, self(), timeout), 21 | {ok, Req, Count - 1, hibernate}. 22 | 23 | terminate(stop, _, 0) -> 24 | ok; 25 | terminate({error, overflow}, _, _) -> 26 | ok. 27 | -------------------------------------------------------------------------------- /examples/rest_basic_auth/src/toppage_handler.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @doc Handler with basic HTTP authorization. 4 | -module(toppage_handler). 5 | 6 | -export([init/2]). 7 | -export([content_types_provided/2]). 8 | -export([is_authorized/2]). 9 | -export([to_text/2]). 10 | 11 | init(Req, Opts) -> 12 | {cowboy_rest, Req, Opts}. 13 | 14 | is_authorized(Req, State) -> 15 | case cowboy_req:parse_header(<<"authorization">>, Req) of 16 | {basic, User = <<"Alladin">>, <<"open sesame">>} -> 17 | {true, Req, User}; 18 | _ -> 19 | {{false, <<"Basic realm=\"cowboy\"">>}, Req, State} 20 | end. 21 | 22 | content_types_provided(Req, State) -> 23 | {[ 24 | {<<"text/plain">>, to_text} 25 | ], Req, State}. 26 | 27 | to_text(Req, User) -> 28 | {<< "Hello, ", User/binary, "!\n" >>, Req, User}. 29 | -------------------------------------------------------------------------------- /test/ws_SUITE_data/ws_echo_timer.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | -module(ws_echo_timer). 4 | 5 | -export([init/2]). 6 | -export([websocket_init/1]). 7 | -export([websocket_handle/2]). 8 | -export([websocket_info/2]). 9 | 10 | init(Req, _) -> 11 | {cowboy_websocket, Req, undefined}. 12 | 13 | websocket_init(State) -> 14 | erlang:start_timer(1000, self(), <<"websocket_init">>), 15 | {ok, State}. 16 | 17 | websocket_handle({text, Data}, State) -> 18 | {reply, {text, Data}, State}; 19 | websocket_handle({binary, Data}, State) -> 20 | {reply, {binary, Data}, State}; 21 | websocket_handle(_Frame, State) -> 22 | {ok, State}. 23 | 24 | websocket_info({timeout, _Ref, Msg}, State) -> 25 | erlang:start_timer(1000, self(), <<"websocket_handle">>), 26 | {reply, {text, Msg}, State}; 27 | websocket_info(_Info, State) -> 28 | {ok, State}. 29 | -------------------------------------------------------------------------------- /test/http_SUITE_data/http_loop_stream_recv.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | -module(http_loop_stream_recv). 4 | 5 | -export([init/2]). 6 | -export([info/3]). 7 | -export([terminate/3]). 8 | 9 | init(Req, _) -> 10 | receive after 100 -> ok end, 11 | self() ! stream, 12 | {cowboy_loop, Req, undefined, 100}. 13 | 14 | info(stream, Req, undefined) -> 15 | stream(Req, 1, <<>>). 16 | 17 | stream(Req, ID, Acc) -> 18 | case cowboy_req:read_body(Req) of 19 | {ok, <<>>, Req2} -> 20 | {stop, cowboy_req:reply(200, Req2), undefined}; 21 | {_, Data, Req2} -> 22 | parse_id(Req2, ID, << Acc/binary, Data/binary >>) 23 | end. 24 | 25 | parse_id(Req, ID, Data) -> 26 | case Data of 27 | << ID:32, Rest/bits >> -> 28 | parse_id(Req, ID + 1, Rest); 29 | _ -> 30 | stream(Req, ID, Data) 31 | end. 32 | 33 | terminate(stop, _, _) -> 34 | ok. 35 | -------------------------------------------------------------------------------- /doc/src/manual/cowboy_sub_protocol.asciidoc: -------------------------------------------------------------------------------- 1 | = cowboy_sub_protocol(3) 2 | 3 | == Name 4 | 5 | cowboy_sub_protocol - sub protocol 6 | 7 | == Description 8 | 9 | The `cowboy_sub_protocol` behaviour defines the interface used 10 | by modules that implement a protocol on top of HTTP. 11 | 12 | == Callbacks 13 | 14 | === upgrade(Req, Env, Handler, HandlerOpts) -> {ok, Req, Env} | {suspend, Module, Function, Args} | {stop, Req} 15 | 16 | Req = cowboy_req:req():: The Req object. 17 | Env = env():: The request environment. 18 | Handler = module():: Handler module. 19 | Opts = any():: Handler options. 20 | Module = module():: MFA to call when resuming the process. 21 | Function = atom():: MFA to call when resuming the process. 22 | Args = [any()]:: MFA to call when resuming the process. 23 | 24 | Upgrade the protocol. 25 | 26 | Please refer to the `cowboy_middleware` manual for a 27 | description of the return values. 28 | -------------------------------------------------------------------------------- /examples/markdown_middleware/src/markdown_converter.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | -module(markdown_converter). 4 | -behaviour(cowboy_middleware). 5 | 6 | -export([execute/2]). 7 | 8 | execute(Req, Env) -> 9 | [Path] = cowboy_req:path_info(Req), 10 | case filename:extension(Path) of 11 | <<".html">> -> maybe_generate_markdown(resource_path(Path)); 12 | _Ext -> ok 13 | end, 14 | {ok, Req, Env}. 15 | 16 | maybe_generate_markdown(Path) -> 17 | ModifiedAt = filelib:last_modified(source_path(Path)), 18 | GeneratedAt = filelib:last_modified(Path), 19 | case ModifiedAt > GeneratedAt of 20 | true -> erlmarkdown:conv_file(source_path(Path), Path); 21 | false -> ok 22 | end. 23 | 24 | resource_path(Path) -> 25 | PrivDir = code:priv_dir(markdown_middleware), 26 | filename:join([PrivDir, Path]). 27 | 28 | source_path(Path) -> 29 | << (filename:rootname(Path))/binary, ".md" >>. 30 | -------------------------------------------------------------------------------- /test/http_SUITE_data/http_set_resp.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | -module(http_set_resp). 4 | 5 | -export([init/2]). 6 | 7 | init(Req, Opts) -> 8 | Headers = proplists:get_value(headers, Opts, #{}), 9 | Body = proplists:get_value(body, Opts, <<"http_handler_set_resp">>), 10 | Req2 = lists:foldl(fun({Name, Value}, R) -> 11 | cowboy_req:set_resp_header(Name, Value, R) 12 | end, Req, maps:to_list(Headers)), 13 | Req3 = cowboy_req:set_resp_body(Body, Req2), 14 | Req4 = cowboy_req:set_resp_header(<<"x-cowboy-test">>, <<"ok">>, Req3), 15 | Req5 = cowboy_req:set_resp_cookie(<<"cake">>, <<"lie">>, [], Req4), 16 | case cowboy_req:has_resp_header(<<"x-cowboy-test">>, Req5) of 17 | false -> {ok, Req5, Opts}; 18 | true -> 19 | case cowboy_req:has_resp_body(Req5) of 20 | false -> 21 | {ok, Req5, Opts}; 22 | true -> 23 | {ok, cowboy_req:reply(200, Req5), Opts} 24 | end 25 | end. 26 | -------------------------------------------------------------------------------- /examples/ssl_hello_world/priv/ssl/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICXQIBAAKBgQDNtbUaMQLMdRz9ZJOouIAaqMI1xxHmxpVL7KjPZI9GGmjJ/T+o 3 | GtfkFjS3OaCjOhOJF8TjAKJUP30Jz4Oun8Uzj2vgSll2hwii+muY6a/+DCSiP3nN 4 | oDo8o2fU52YOnaHAmxfZmbeSlsZRlPGMOSRxyaBRvgSMvuo0eruxpC2K9QIDAQAB 5 | AoGAaD85c/h6bpq7Aj7CBbLaWKhFI3OqwsTITB22vsM7SE+B4zsP02UnG1OVi3UM 6 | zytTUxpUkKV1njQ+bYZYOVqGWF4Up8tTqUglHn0FTPok1AIemELWtz3sXvdSHC1T 7 | lqvFBAZ9kibn13qGyVOiyCFaMwfOM/05RvV7p3jfUMTWnNECQQDs7yCJZ8Ol8MyH 8 | TGZzvkjoN2zg1KwmTbSD1hkP6QAJtPdRuqFbjlEru0/PefgOXsWLRIa3/3v0qw2G 9 | xGkV6AXTAkEA3kNbFisqUydjPnZIYv/P6SvPdUimHJEjXbAbfNfzS9dzszrOVJd2 10 | XqGH7z5yzjoH3IyaIMW8GnubVzGDSjrHFwJAKSU5vELlygpwKkrNO+pelN0TLlQg 11 | dSJnZ8GlZorq88SWcn37iX/EftivenNO7YftvEqxLoDSkOGnnrC7Iw/A+wJBAIEe 12 | L/QY72WPJCBNJpAce/PA96vyoE1II3txqwZDjZspdpVQPDz4IFOpEwbxCFC1dYuy 13 | Qnd3Z2cbF4r3wIWGz9ECQQCJGNhUNtY+Om1ELdqPcquxE2VRV/pucnvJSTKwyo2C 14 | Rvm6H7kFDwPDuN23YnTOlTiho0zzCkclcIukhIVJ+dKz 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /test/http_SUITE_data/rest_forbidden_resource.erl: -------------------------------------------------------------------------------- 1 | -module(rest_forbidden_resource). 2 | 3 | -export([init/2]). 4 | -export([allowed_methods/2]). 5 | -export([forbidden/2]). 6 | -export([content_types_provided/2]). 7 | -export([content_types_accepted/2]). 8 | -export([to_text/2]). 9 | -export([from_text/2]). 10 | 11 | init(Req, [Forbidden]) -> 12 | {cowboy_rest, Req, Forbidden}. 13 | 14 | allowed_methods(Req, State) -> 15 | {[<<"GET">>, <<"HEAD">>, <<"POST">>], Req, State}. 16 | 17 | forbidden(Req, State=true) -> 18 | {true, Req, State}; 19 | forbidden(Req, State=false) -> 20 | {false, Req, State}. 21 | 22 | content_types_provided(Req, State) -> 23 | {[{{<<"text">>, <<"plain">>, []}, to_text}], Req, State}. 24 | 25 | content_types_accepted(Req, State) -> 26 | {[{{<<"text">>, <<"plain">>, []}, from_text}], Req, State}. 27 | 28 | to_text(Req, State) -> 29 | {<<"This is REST!">>, Req, State}. 30 | 31 | from_text(Req, State) -> 32 | {{true, cowboy_req:path(Req)}, Req, State}. 33 | -------------------------------------------------------------------------------- /examples/echo_post/src/toppage_handler.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @doc POST echo handler. 4 | -module(toppage_handler). 5 | 6 | -export([init/2]). 7 | 8 | init(Req0, Opts) -> 9 | Method = cowboy_req:method(Req0), 10 | HasBody = cowboy_req:has_body(Req0), 11 | Req = maybe_echo(Method, HasBody, Req0), 12 | {ok, Req, Opts}. 13 | 14 | maybe_echo(<<"POST">>, true, Req0) -> 15 | {ok, PostVals, Req} = cowboy_req:read_urlencoded_body(Req0), 16 | Echo = proplists:get_value(<<"echo">>, PostVals), 17 | echo(Echo, Req); 18 | maybe_echo(<<"POST">>, false, Req) -> 19 | cowboy_req:reply(400, [], <<"Missing body.">>, Req); 20 | maybe_echo(_, _, Req) -> 21 | %% Method not allowed. 22 | cowboy_req:reply(405, Req). 23 | 24 | echo(undefined, Req) -> 25 | cowboy_req:reply(400, [], <<"Missing echo parameter.">>, Req); 26 | echo(Echo, Req) -> 27 | cowboy_req:reply(200, #{ 28 | <<"content-type">> => <<"text/plain; charset=utf-8">> 29 | }, Echo, Req). 30 | -------------------------------------------------------------------------------- /examples/ssl_hello_world/priv/ssl/cowboy-ca.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICeDCCAeGgAwIBAgIJAOvpU0y2e5J4MA0GCSqGSIb3DQEBBQUAMFUxCzAJBgNV 3 | BAYTAlVTMQ4wDAYDVQQIDAVUZXhhczETMBEGA1UECgwKTmluZSBOaW5lczEPMA0G 4 | A1UECwwGQ293Ym95MRAwDgYDVQQDDAdST09UIENBMB4XDTEzMDIyODA1MTAwMVoX 5 | DTMzMDIyMzA1MTAwMVowVTELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVRleGFzMRMw 6 | EQYDVQQKDApOaW5lIE5pbmVzMQ8wDQYDVQQLDAZDb3dib3kxEDAOBgNVBAMMB1JP 7 | T1QgQ0EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMzmY7Us06yjyUbpqwPx 8 | Iv+xh/g3V7we07ClC9GEYnvr3OQvdA1jFEHccMBUUjRoQ8DPd6uSyK5UkixABs08 9 | Tt5B3VsnGKr0DIN+IO4SN2PkmBqIU/BN3KdcwN65YNr3iM0KsKWeFtAZdYx4CakX 10 | 7REbO0wjK20AH3xSBn3uFGiBAgMBAAGjUDBOMB0GA1UdDgQWBBRKfZ8KF2jlLBDm 11 | NL6IuEuGY0pdbzAfBgNVHSMEGDAWgBRKfZ8KF2jlLBDmNL6IuEuGY0pdbzAMBgNV 12 | HRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAG1I0kBxXiLkM1b7rl2zPLizREYg 13 | 1m+ajb6rWzPOBg6TXjv58Be+H4tqoHIL/M/crixew5emftBkuAGjiKMhbIokjvan 14 | aPTCV8U6HHvNvz9c68HpESWbd+56cHqfsS5XCKp1OpW5tbL2UQYpFKMP4qmbv3Ea 15 | pBfPPmSFMBb1i2AI 16 | -----END CERTIFICATE----- 17 | -------------------------------------------------------------------------------- /test/http_SUITE_data/http_stream_body.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | -module(http_stream_body). 4 | 5 | -export([init/2]). 6 | 7 | init(Req, Opts) -> 8 | Body = proplists:get_value(body, Opts, "http_handler_stream_body"), 9 | Reply = proplists:get_value(reply, Opts), 10 | SFun = fun () -> 11 | cowboy_req:send_body(Body, nofin, Req) 12 | end, 13 | Req2 = case Reply of 14 | set_resp -> 15 | SLen = iolist_size(Body), 16 | cowboy_req:set_resp_body({stream, SLen, SFun}, Req); 17 | %% @todo Hmm that one will be sent as chunked now. 18 | %% We need an option to disable chunked. 19 | set_resp_close -> 20 | cowboy_req:set_resp_body({stream, undefined, SFun}, Req); 21 | set_resp_chunked -> 22 | %% Here Body should be a list of chunks, not a binary. 23 | SFun2 = fun () -> 24 | lists:foreach(fun (Data) -> cowboy_req:send_body(Data, nofin, Req) end, Body) 25 | end, 26 | cowboy_req:set_resp_body({stream, undefined, SFun2}, Req) 27 | end, 28 | {ok, cowboy_req:reply(200, Req2), Opts}. 29 | -------------------------------------------------------------------------------- /test/http_SUITE_data/rest_param_all.erl: -------------------------------------------------------------------------------- 1 | -module(rest_param_all). 2 | 3 | -export([init/2]). 4 | -export([allowed_methods/2]). 5 | -export([content_types_provided/2]). 6 | -export([get_text_plain/2]). 7 | -export([content_types_accepted/2]). 8 | -export([put_text_plain/2]). 9 | 10 | init(Req, Opts) -> 11 | {cowboy_rest, Req, Opts}. 12 | 13 | allowed_methods(Req, State) -> 14 | {[<<"GET">>, <<"PUT">>], Req, State}. 15 | 16 | content_types_provided(Req, State) -> 17 | {[{{<<"text">>, <<"plain">>, '*'}, get_text_plain}], Req, State}. 18 | 19 | get_text_plain(Req, State) -> 20 | {_, _, Param} = maps:get(media_type, Req, {{<<"text">>, <<"plain">>}, []}), 21 | Body = if 22 | Param == '*' -> 23 | <<"'*'">>; 24 | Param == [] -> 25 | <<"[]">>; 26 | Param /= [] -> 27 | iolist_to_binary([[Key, $=, Value] || {Key, Value} <- Param]) 28 | end, 29 | {Body, Req, State}. 30 | 31 | content_types_accepted(Req, State) -> 32 | {[{{<<"text">>, <<"plain">>, '*'}, put_text_plain}], Req, State}. 33 | 34 | put_text_plain(Req, State) -> 35 | {true, Req, State}. 36 | -------------------------------------------------------------------------------- /examples/rest_hello_world/src/toppage_handler.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @doc Hello world handler. 4 | -module(toppage_handler). 5 | 6 | -export([init/2]). 7 | -export([content_types_provided/2]). 8 | -export([hello_to_html/2]). 9 | -export([hello_to_json/2]). 10 | -export([hello_to_text/2]). 11 | 12 | init(Req, Opts) -> 13 | {cowboy_rest, Req, Opts}. 14 | 15 | content_types_provided(Req, State) -> 16 | {[ 17 | {<<"text/html">>, hello_to_html}, 18 | {<<"application/json">>, hello_to_json}, 19 | {<<"text/plain">>, hello_to_text} 20 | ], Req, State}. 21 | 22 | hello_to_html(Req, State) -> 23 | Body = <<" 24 | 25 | 26 | REST Hello World! 27 | 28 | 29 |

REST Hello World as HTML!

30 | 31 | ">>, 32 | {Body, Req, State}. 33 | 34 | hello_to_json(Req, State) -> 35 | Body = <<"{\"rest\": \"Hello World!\"}">>, 36 | {Body, Req, State}. 37 | 38 | hello_to_text(Req, State) -> 39 | {<<"REST Hello World as text!">>, Req, State}. 40 | -------------------------------------------------------------------------------- /examples/ssl_hello_world/priv/ssl/server.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICpTCCAg6gAwIBAgIJAOvpU0y2e5J5MA0GCSqGSIb3DQEBBQUAMFUxCzAJBgNV 3 | BAYTAlVTMQ4wDAYDVQQIDAVUZXhhczETMBEGA1UECgwKTmluZSBOaW5lczEPMA0G 4 | A1UECwwGQ293Ym95MRAwDgYDVQQDDAdST09UIENBMB4XDTEzMDIyODA1MjMzNFoX 5 | DTMzMDIyMzA1MjMzNFowVzELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVRleGFzMRMw 6 | EQYDVQQKDApOaW5lIE5pbmVzMQ8wDQYDVQQLDAZDb3dib3kxEjAQBgNVBAMMCWxv 7 | Y2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAzbW1GjECzHUc/WST 8 | qLiAGqjCNccR5saVS+yoz2SPRhpoyf0/qBrX5BY0tzmgozoTiRfE4wCiVD99Cc+D 9 | rp/FM49r4EpZdocIovprmOmv/gwkoj95zaA6PKNn1OdmDp2hwJsX2Zm3kpbGUZTx 10 | jDkkccmgUb4EjL7qNHq7saQtivUCAwEAAaN7MHkwCQYDVR0TBAIwADAsBglghkgB 11 | hvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYE 12 | FB6jTEIWI8T1ckORA4GezbyYxtbvMB8GA1UdIwQYMBaAFEp9nwoXaOUsEOY0voi4 13 | S4ZjSl1vMA0GCSqGSIb3DQEBBQUAA4GBACMboVQjrx8u/fk3gl/sR0tbA0Wf/NcS 14 | 2Dzsy2czndgVUAG4Sqb+hfgn0dqAyUKghRrj3JDcYxYksGPIklDfPzZb7yJ39l16 15 | 6x5ZiIzhp8CAVdPvRxRznw5rZwaXesryXu1jVSZxTr3MYZdkG6KaAM0t90+YlGLZ 16 | UG8fAicx0Bf+ 17 | -----END CERTIFICATE----- 18 | -------------------------------------------------------------------------------- /test/cowboy_ct_hook.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2014, Loïc Hoguin 2 | %% 3 | %% Permission to use, copy, modify, and/or distribute this software for any 4 | %% purpose with or without fee is hereby granted, provided that the above 5 | %% copyright notice and this permission notice appear in all copies. 6 | %% 7 | %% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | %% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | %% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | %% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | %% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | %% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | -module(cowboy_ct_hook). 16 | 17 | -export([init/2]). 18 | 19 | init(_, _) -> 20 | ct_helper:start([cowboy, gun]), 21 | ct_helper:make_certs_in_ets(), 22 | error_logger:add_report_handler(ct_helper_error_h), 23 | {ok, undefined}. 24 | -------------------------------------------------------------------------------- /test/http_SUITE_data/http_body_qs.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | -module(http_body_qs). 4 | 5 | -export([init/2]). 6 | 7 | init(Req, Opts) -> 8 | Method = cowboy_req:method(Req), 9 | HasBody = cowboy_req:has_body(Req), 10 | {ok, maybe_echo(Method, HasBody, Req), Opts}. 11 | 12 | maybe_echo(<<"POST">>, true, Req) -> 13 | case cowboy_req:read_urlencoded_body(Req) of 14 | {badlength, Req2} -> 15 | echo(badlength, Req2); 16 | {ok, PostVals, Req2} -> 17 | echo(proplists:get_value(<<"echo">>, PostVals), Req2) 18 | end; 19 | maybe_echo(<<"POST">>, false, Req) -> 20 | cowboy_req:reply(400, #{}, <<"Missing body.">>, Req); 21 | maybe_echo(_, _, Req) -> 22 | %% Method not allowed. 23 | cowboy_req:reply(405, Req). 24 | 25 | echo(badlength, Req) -> 26 | cowboy_req:reply(413, #{}, <<"POST body bigger than 16000 bytes">>, Req); 27 | echo(undefined, Req) -> 28 | cowboy_req:reply(400, #{}, <<"Missing echo parameter.">>, Req); 29 | echo(Echo, Req) -> 30 | cowboy_req:reply(200, #{ 31 | <<"content-type">> => <<"text/plain; charset=utf-8">> 32 | }, Echo, Req). 33 | -------------------------------------------------------------------------------- /src/cowboy_app.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2011-2014, Loïc Hoguin 2 | %% 3 | %% Permission to use, copy, modify, and/or distribute this software for any 4 | %% purpose with or without fee is hereby granted, provided that the above 5 | %% copyright notice and this permission notice appear in all copies. 6 | %% 7 | %% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | %% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | %% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | %% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | %% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | %% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | -module(cowboy_app). 16 | -behaviour(application). 17 | 18 | -export([start/2]). 19 | -export([stop/1]). 20 | 21 | -spec start(_, _) -> {ok, pid()}. 22 | start(_, _) -> 23 | cowboy_sup:start_link(). 24 | 25 | -spec stop(_) -> ok. 26 | stop(_) -> 27 | ok. 28 | -------------------------------------------------------------------------------- /examples/error_hook/README.asciidoc: -------------------------------------------------------------------------------- 1 | = Error hook example 2 | 3 | *This example is currently broken on master.* 4 | 5 | To try this example, you need GNU `make` and `git` in your PATH. 6 | 7 | To build and run the example, use the following command: 8 | 9 | [source,bash] 10 | $ make run 11 | 12 | Then point your browser to http://localhost:8080 13 | 14 | == Example output 15 | 16 | Not found: 17 | 18 | [source,bash] 19 | ---- 20 | $ curl -i http://localhost:8080 21 | HTTP/1.1 404 Not Found 22 | connection: keep-alive 23 | server: Cowboy 24 | date: Wed, 27 Feb 2013 23:32:55 GMT 25 | content-length: 56 26 | 27 | 404 Not Found: "/" is not the path you are looking for. 28 | ---- 29 | 30 | Bad request: 31 | 32 | [source,bash] 33 | ---- 34 | $ telnet localhost 8080 35 | Trying ::1... 36 | Connection failed: Connection refused 37 | Trying 127.0.0.1... 38 | Connected to localhost. 39 | Escape character is '^]'. 40 | bad 41 | HTTP/1.1 400 Bad Request 42 | connection: close 43 | server: Cowboy 44 | date: Sun, 08 Sep 2013 09:29:27 GMT 45 | content-length: 15 46 | 47 | HTTP Error 400 48 | Connection closed by foreign host. 49 | ---- 50 | -------------------------------------------------------------------------------- /examples/rest_stream_response/src/rest_stream_response_app.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @private 4 | -module(rest_stream_response_app). 5 | -behaviour(application). 6 | 7 | %% API. 8 | -export([start/2]). 9 | -export([stop/1]). 10 | 11 | %% API. 12 | 13 | start(_Type, _Args) -> 14 | Table = ets:new(stream_tab, []), 15 | generate_rows(Table, 1000), 16 | Dispatch = cowboy_router:compile([ 17 | {'_', [ 18 | {"/[:v1]", [{v1, int}], toppage_handler, Table} 19 | ]} 20 | ]), 21 | {ok, _} = cowboy:start_http(http, 100, [{port, 8080}], [ 22 | {env, [{dispatch, Dispatch}]} 23 | ]), 24 | rest_stream_response_sup:start_link(). 25 | 26 | stop(_State) -> 27 | ok. 28 | 29 | generate_rows(_Table, 0) -> 30 | ok; 31 | generate_rows(Table, N) -> 32 | ets:insert(Table, {key(), val(), val()}), 33 | generate_rows(Table, N - 1). 34 | 35 | key() -> key(10). 36 | key(N) -> key(<< (random:uniform(26) - 1) >>, N - 1). 37 | key(Acc, 0) -> binary_part(base64:encode(Acc), 0, 8); 38 | key(Acc, N) -> key(<< Acc/binary, (random:uniform(26) - 1) >>, N - 1). 39 | val() -> random:uniform(50). 40 | -------------------------------------------------------------------------------- /src/cowboy_middleware.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2013-2014, Loïc Hoguin 2 | %% 3 | %% Permission to use, copy, modify, and/or distribute this software for any 4 | %% purpose with or without fee is hereby granted, provided that the above 5 | %% copyright notice and this permission notice appear in all copies. 6 | %% 7 | %% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | %% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | %% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | %% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | %% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | %% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | -module(cowboy_middleware). 16 | 17 | -type env() :: [{atom(), any()}]. 18 | -export_type([env/0]). 19 | 20 | -callback execute(Req, Env) 21 | -> {ok, Req, Env} 22 | | {suspend, module(), atom(), [any()]} 23 | | {stop, Req} 24 | when Req::cowboy_req:req(), Env::env(). 25 | -------------------------------------------------------------------------------- /test/http_SUITE_data/rest_patch_resource.erl: -------------------------------------------------------------------------------- 1 | -module(rest_patch_resource). 2 | 3 | -export([init/2]). 4 | -export([allowed_methods/2]). 5 | -export([content_types_provided/2]). 6 | -export([get_text_plain/2]). 7 | -export([content_types_accepted/2]). 8 | -export([patch_text_plain/2]). 9 | 10 | init(Req, Opts) -> 11 | {cowboy_rest, Req, Opts}. 12 | 13 | allowed_methods(Req, State) -> 14 | {[<<"HEAD">>, <<"GET">>, <<"PATCH">>], Req, State}. 15 | 16 | content_types_provided(Req, State) -> 17 | {[{{<<"text">>, <<"plain">>, []}, get_text_plain}], Req, State}. 18 | 19 | get_text_plain(Req, State) -> 20 | {<<"This is REST!">>, Req, State}. 21 | 22 | content_types_accepted(Req, State) -> 23 | case cowboy_req:method(Req) of 24 | <<"PATCH">> -> 25 | {[{{<<"text">>, <<"plain">>, []}, patch_text_plain}], Req, State}; 26 | _ -> 27 | {[], Req, State} 28 | end. 29 | 30 | patch_text_plain(Req, State) -> 31 | case cowboy_req:read_body(Req) of 32 | {ok, <<"stop">>, Req0} -> 33 | {stop, cowboy_req:reply(400, Req0), State}; 34 | {ok, <<"false">>, Req0} -> 35 | {false, Req0, State}; 36 | {ok, _Body, Req0} -> 37 | {true, Req0, State} 38 | end. 39 | -------------------------------------------------------------------------------- /src/cowboy_sub_protocol.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2013, James Fish 2 | %% Copyright (c) 2013-2014, Loïc Hoguin 3 | %% 4 | %% Permission to use, copy, modify, and/or distribute this software for any 5 | %% purpose with or without fee is hereby granted, provided that the above 6 | %% copyright notice and this permission notice appear in all copies. 7 | %% 8 | %% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | %% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | %% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | %% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | %% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | %% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | 16 | -module(cowboy_sub_protocol). 17 | 18 | -callback upgrade(Req, Env, module(), any(), timeout(), run | hibernate) 19 | -> {ok, Req, Env} | {suspend, module(), atom(), [any()]} | {stop, Req} 20 | when Req::cowboy_req:req(), Env::cowboy_middleware:env(). 21 | -------------------------------------------------------------------------------- /examples/file_server/src/directory_lister.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | -module(directory_lister). 4 | -behaviour(cowboy_middleware). 5 | 6 | -export([execute/2]). 7 | 8 | execute(Req, Env=#{handler := cowboy_static}) -> 9 | redirect_directory(Req, Env); 10 | execute(Req, Env) -> 11 | {ok, Req, Env}. 12 | 13 | redirect_directory(Req, Env=#{handler_opts := {_, _, _, Extra}}) -> 14 | Path = cowboy_req:path_info(Req), 15 | Path1 = << <> || S <- Path >>, 16 | {dir_handler, DirHandler} = lists:keyfind(dir_handler, 1, Extra), 17 | FullPath = resource_path(Path1), 18 | case valid_path(Path) and filelib:is_dir(FullPath) of 19 | true -> handle_directory(Req, Env, Path1, FullPath, DirHandler); 20 | false -> {ok, Req, Env} 21 | end. 22 | 23 | handle_directory(Req, Env, Prefix, Path, DirHandler) -> 24 | {ok, Req, Env#{handler => DirHandler, handler_opts => {Prefix, Path}}}. 25 | 26 | valid_path([]) -> true; 27 | valid_path([<<"..">> | _T]) -> false; 28 | valid_path([<<"/", _/binary>> | _T]) -> false; 29 | valid_path([_H | Rest]) -> valid_path(Rest). 30 | 31 | resource_path(Path) -> 32 | filename:join([code:priv_dir(file_server), Path]). 33 | -------------------------------------------------------------------------------- /examples/rest_stream_response/src/toppage_handler.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @doc Streaming handler. 4 | -module(toppage_handler). 5 | 6 | -export([init/2]). 7 | -export([content_types_provided/2]). 8 | -export([streaming_csv/2]). 9 | 10 | init(Req, Table) -> 11 | {cowboy_rest, Req, Table}. 12 | 13 | content_types_provided(Req, State) -> 14 | {[ 15 | {{<<"text">>, <<"csv">>, []}, streaming_csv} 16 | ], Req, State}. 17 | 18 | streaming_csv(Req, Table) -> 19 | N = cowboy_req:binding(v1, Req, 1), 20 | MS = [{{'$1', '$2', '$3'}, [{'==', '$2', N}], ['$$']}], 21 | {{stream, result_streamer(Table, MS)}, Req, Table}. 22 | 23 | result_streamer(Table, MS) -> 24 | fun (Socket, Transport) -> 25 | send_records(Socket, Transport, ets:select(Table, MS, 1)) 26 | end. 27 | 28 | send_records(Socket, Transport, {[Rec], Cont}) -> 29 | timer:sleep(500), 30 | send_line(Socket, Transport, Rec), 31 | send_records(Socket, Transport, ets:select(Cont)); 32 | send_records(_Socket, _Transport, '$end_of_table') -> 33 | ok. 34 | 35 | send_line(Socket, Transport, [Key, V1, V2]) -> 36 | Transport:send(Socket, 37 | [Key, $,, integer_to_list(V1), $,, integer_to_list(V2), $\r, $\n]). 38 | -------------------------------------------------------------------------------- /doc/src/manual/cowboy_static.asciidoc: -------------------------------------------------------------------------------- 1 | = cowboy_static(3) 2 | 3 | == Name 4 | 5 | cowboy_static - static file handler 6 | 7 | == Description 8 | 9 | The `cowboy_static` module implements file serving capabilities 10 | by using the REST semantics provided by `cowboy_rest`. 11 | 12 | == Types 13 | 14 | === opts() = [Option] 15 | 16 | [source,erlang] 17 | ---- 18 | Option = {priv_file, atom(), string() | binary()} 19 | | {priv_file, atom(), string() | binary(), Extra} 20 | | {file, string() | binary()} 21 | | {file, string() | binary(), Extra} 22 | | {priv_dir, atom(), string() | binary()} 23 | | {priv_dir, atom(), string() | binary(), Extra} 24 | | {dir, string() | binary()} 25 | | {dir, string() | binary(), Extra} 26 | 27 | Extra = [ETag | Mimetypes] 28 | 29 | ETag = {etag, module(), function()} | {etag, false} 30 | 31 | Mimetypes = {mimetypes, module(), function()} 32 | | {mimetypes, binary() | {binary(), binary(), [{binary(), binary()}]}} 33 | ---- 34 | 35 | Configuration for the static handler. 36 | 37 | The handler can be configured for sending either one file or 38 | a directory (including its subdirectories). 39 | 40 | Extra options allow you to define how the etag should be calculated 41 | and how the mimetype of files should be detected. 42 | -------------------------------------------------------------------------------- /examples/compress_response/src/toppage_handler.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @doc Compress response handler. 4 | -module(toppage_handler). 5 | 6 | -export([init/2]). 7 | 8 | init(Req0, Opts) -> 9 | BigBody = 10 | <<"A cowboy is an animal herder who tends cattle on ranches in North America, 11 | traditionally on horseback, and often performs a multitude of other ranch- 12 | related tasks. The historic American cowboy of the late 19th century arose 13 | from the vaquero traditions of northern Mexico and became a figure of special 14 | significance and legend. A subtype, called a wrangler, specifically tends the 15 | horses used to work cattle. In addition to ranch work, some cowboys work for 16 | or participate in rodeos. Cowgirls, first defined as such in the late 19th 17 | century, had a less-well documented historical role, but in the modern world 18 | have established the ability to work at virtually identical tasks and obtained 19 | considerable respect for their achievements. There are also cattle handlers 20 | in many other parts of the world, particularly South America and Australia, 21 | who perform work similar to the cowboy in their respective nations.\n">>, 22 | Req = cowboy_req:reply(200, [], BigBody, Req0), 23 | {ok, Req, Opts}. 24 | -------------------------------------------------------------------------------- /examples/error_hook/src/error_hook_app.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @private 4 | -module(error_hook_app). 5 | -behaviour(application). 6 | 7 | %% API. 8 | -export([start/2]). 9 | -export([stop/1]). 10 | 11 | %% API. 12 | 13 | start(_Type, _Args) -> 14 | Dispatch = cowboy_router:compile([ 15 | {'_', []} 16 | ]), 17 | {ok, _} = cowboy:start_http(http, 100, [{port, 8080}], [ 18 | {env, [{dispatch, Dispatch}]}, 19 | {onresponse, fun error_hook/4} 20 | ]), 21 | error_hook_sup:start_link(). 22 | 23 | stop(_State) -> 24 | ok. 25 | 26 | error_hook(404, Headers, <<>>, Req) -> 27 | Path = cowboy_req:path(Req), 28 | Body = ["404 Not Found: \"", Path, 29 | "\" is not the path you are looking for.\n"], 30 | Headers2 = lists:keyreplace(<<"content-length">>, 1, Headers, 31 | {<<"content-length">>, integer_to_list(iolist_size(Body))}), 32 | cowboy_req:reply(404, Headers2, Body, Req); 33 | error_hook(Code, Headers, <<>>, Req) when is_integer(Code), Code >= 400 -> 34 | Body = ["HTTP Error ", integer_to_list(Code), $\n], 35 | Headers2 = lists:keyreplace(<<"content-length">>, 1, Headers, 36 | {<<"content-length">>, integer_to_list(iolist_size(Body))}), 37 | cowboy_req:reply(Code, Headers2, Body, Req); 38 | error_hook(_Code, _Headers, _Body, Req) -> 39 | Req. 40 | -------------------------------------------------------------------------------- /examples/eventsource/priv/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 39 | 40 | 41 | Hi! 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /src/cowboy_sup.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2011-2014, Loïc Hoguin 2 | %% 3 | %% Permission to use, copy, modify, and/or distribute this software for any 4 | %% purpose with or without fee is hereby granted, provided that the above 5 | %% copyright notice and this permission notice appear in all copies. 6 | %% 7 | %% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | %% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | %% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | %% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | %% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | %% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | -module(cowboy_sup). 16 | -behaviour(supervisor). 17 | 18 | -export([start_link/0]). 19 | -export([init/1]). 20 | 21 | -spec start_link() -> {ok, pid()}. 22 | start_link() -> 23 | supervisor:start_link({local, ?MODULE}, ?MODULE, []). 24 | 25 | -spec init([]) 26 | -> {ok, {{supervisor:strategy(), 10, 10}, [supervisor:child_spec()]}}. 27 | init([]) -> 28 | Procs = [{cowboy_clock, {cowboy_clock, start_link, []}, 29 | permanent, 5000, worker, [cowboy_clock]}], 30 | {ok, {{one_for_one, 10, 10}, Procs}}. 31 | -------------------------------------------------------------------------------- /doc/src/manual/cowboy.stop_listener.asciidoc: -------------------------------------------------------------------------------- 1 | = cowboy:stop_listener(3) 2 | 3 | == Name 4 | 5 | cowboy:stop_listener - Stop the given listener 6 | 7 | == Description 8 | 9 | [source,erlang] 10 | ---- 11 | stop_listener(Name :: ranch:ref()) 12 | -> ok | {error, not_found}. 13 | ---- 14 | 15 | Stop a previously started listener. 16 | 17 | Alias of link:man:ranch:stop_listener(3)[ranch:stop_listener(3)]. 18 | 19 | == Arguments 20 | 21 | Name:: 22 | 23 | The name of the listener to be stopped. 24 | + 25 | The name of the listener is the first argument given to the 26 | link:man:cowboy:start_clear(3)[cowboy:start_clear(3)], 27 | link:man:cowboy:start_tls(3)[cowboy:start_tls(3)] or 28 | link:man:ranch:start_listener(3)[ranch:start_listener(3)] function. 29 | 30 | == Return value 31 | 32 | The atom `ok` is returned on success. 33 | 34 | The `{error, not_found}` tuple is returned when the listener 35 | does not exist. 36 | 37 | == Changelog 38 | 39 | * *1.0*: Function introduced. 40 | 41 | == Examples 42 | 43 | .Stop a listener 44 | [source,erlang] 45 | ---- 46 | ok = cowboy:stop_listener(example). 47 | ---- 48 | 49 | == See also 50 | 51 | link:man:cowboy(3)[cowboy(3)], 52 | link:man:cowboy:start_clear(3)[cowboy:start_clear(3)], 53 | link:man:cowboy:start_tls(3)[cowboy:start_tls(3)], 54 | link:man:ranch(3)[ranch(3)], 55 | link:man:ranch:start_listener(3)[ranch:start_listener(3)] 56 | -------------------------------------------------------------------------------- /test/http_SUITE_data/rest_resource_etags.erl: -------------------------------------------------------------------------------- 1 | -module(rest_resource_etags). 2 | 3 | -export([init/2]). 4 | -export([generate_etag/2]). 5 | -export([content_types_provided/2]). 6 | -export([get_text_plain/2]). 7 | 8 | init(Req, Opts) -> 9 | {cowboy_rest, Req, Opts}. 10 | 11 | generate_etag(Req, State) -> 12 | #{type := Type} = cowboy_req:match_qs([type], Req), 13 | case Type of 14 | %% Correct return values from generate_etag/2. 15 | <<"tuple-weak">> -> 16 | {{weak, <<"etag-header-value">>}, Req, State}; 17 | <<"tuple-strong">> -> 18 | {{strong, <<"etag-header-value">>}, Req, State}; 19 | %% Backwards compatible return values from generate_etag/2. 20 | <<"binary-weak-quoted">> -> 21 | {<<"W/\"etag-header-value\"">>, Req, State}; 22 | <<"binary-strong-quoted">> -> 23 | {<<"\"etag-header-value\"">>, Req, State}; 24 | %% Invalid return values from generate_etag/2. 25 | <<"binary-strong-unquoted">> -> 26 | ct_helper_error_h:ignore(cow_http_hd, parse_etag, 1), 27 | {<<"etag-header-value">>, Req, State}; 28 | <<"binary-weak-unquoted">> -> 29 | ct_helper_error_h:ignore(cow_http_hd, parse_etag, 1), 30 | {<<"W/etag-header-value">>, Req, State} 31 | end. 32 | 33 | content_types_provided(Req, State) -> 34 | {[{{<<"text">>, <<"plain">>, []}, get_text_plain}], Req, State}. 35 | 36 | get_text_plain(Req, State) -> 37 | {<<"This is REST!">>, Req, State}. 38 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # See LICENSE for licensing information. 2 | 3 | PROJECT = cowboy 4 | PROJECT_DESCRIPTION = Small, fast, modern HTTP server. 5 | PROJECT_VERSION = 2.0.0-pre.4 6 | PROJECT_REGISTERED = cowboy_clock 7 | 8 | # Options. 9 | 10 | COMPILE_FIRST = cowboy_middleware cowboy_stream cowboy_sub_protocol 11 | PLT_APPS = public_key ssl 12 | CT_OPTS += -ct_hooks cowboy_ct_hook [] # -boot start_sasl 13 | 14 | CI_OTP ?= OTP-18.0.3 OTP-18.1.5 OTP-18.2.4.1 OTP-18.3.4.4 OTP-19.0.7 OTP-19.1.5 15 | 16 | # Dependencies. 17 | 18 | LOCAL_DEPS = crypto 19 | 20 | DEPS = cowlib ranch 21 | dep_cowlib = git https://github.com/ninenines/cowlib master 22 | dep_ranch = git https://github.com/ninenines/ranch 1.2.1 23 | 24 | TEST_DEPS = ct_helper gun 25 | dep_ct_helper = git https://github.com/extend/ct_helper master 26 | dep_gun = git https://github.com/ninenines/gun master 27 | 28 | # Standard targets. 29 | 30 | include erlang.mk 31 | 32 | # Compile options. 33 | 34 | ERLC_OPTS += +warn_export_all +warn_missing_spec +warn_untyped_record 35 | TEST_ERLC_OPTS += +'{parse_transform, eunit_autoexport}' 36 | 37 | # Generate rebar.config on build. 38 | 39 | app:: rebar.config 40 | 41 | # Also dialyze the tests. 42 | 43 | # DIALYZER_OPTS += --src -r test 44 | 45 | # Use erl_make_certs from the tested release. 46 | 47 | ci-setup:: clean deps test-deps 48 | $(gen_verbose) cp ~/.kerl/builds/$(CI_OTP_RELEASE)/otp_src_git/lib/ssl/test/erl_make_certs.erl deps/ct_helper/src/ 49 | -------------------------------------------------------------------------------- /doc/src/specs/rfc6585.ezdoc: -------------------------------------------------------------------------------- 1 | ::: RFC6585 2 | 3 | This document lists status codes that Cowboy implements as 4 | defined in the RFC6585 specifications. 5 | 6 | :: Status codes 7 | 8 | : 428 Precondition Required (RFC6585 3) 9 | 10 | The server requires the request to this resource to be conditional. 11 | 12 | The response should explain how to resubmit the request successfully. 13 | 14 | : 429 Too Many Requests (RFC6585 4, RFC6585 7.2) 15 | 16 | The user has sent too many requests in a given amount of time. 17 | 18 | The response should detail the rates allowed. 19 | 20 | The retry-after header can be used to indicate how long the 21 | user has to wait before making a new request. 22 | 23 | When an attack is detected it is recommended to drop the 24 | connection directly instead of sending this response. 25 | 26 | : 431 Request Header Fields Too Large (RFC6585 5, RFC6585 7.3) 27 | 28 | The request's header fields are too large. 29 | 30 | When rejecting a single header, the response should detail 31 | which header was at fault. 32 | 33 | When an attack is detected it is recommended to drop the 34 | connection directly instead of sending this response. 35 | 36 | : 511 Network Authentication Required (RFC6585 6) 37 | 38 | The user needs to authenticate into the network to gain access. 39 | 40 | This status code is meant to be used by proxies only, not by 41 | origin servers. 42 | 43 | The response should contain a link to the resource allowing 44 | the user to log in. 45 | -------------------------------------------------------------------------------- /examples/file_server/src/directory_handler.erl: -------------------------------------------------------------------------------- 1 | %% Feel free to use, reuse and abuse the code in this file. 2 | 3 | %% @doc Directory handler. 4 | -module(directory_handler). 5 | 6 | %% REST Callbacks 7 | -export([init/2]). 8 | -export([allowed_methods/2]). 9 | -export([resource_exists/2]). 10 | -export([content_types_provided/2]). 11 | 12 | %% Callback Callbacks 13 | -export([list_json/2]). 14 | -export([list_html/2]). 15 | 16 | init(Req, Paths) -> 17 | {cowboy_rest, Req, Paths}. 18 | 19 | allowed_methods(Req, State) -> 20 | {[<<"GET">>], Req, State}. 21 | 22 | resource_exists(Req, {ReqPath, FilePath}) -> 23 | case file:list_dir(FilePath) of 24 | {ok, Fs} -> {true, Req, {ReqPath, lists:sort(Fs)}}; 25 | _Err -> {false, Req, {ReqPath, FilePath}} 26 | end. 27 | 28 | content_types_provided(Req, State) -> 29 | {[ 30 | {{<<"text">>, <<"html">>, []}, list_html}, 31 | {{<<"application">>, <<"json">>, []}, list_json} 32 | ], Req, State}. 33 | 34 | list_json(Req, {Path, Fs}) -> 35 | Files = [ <<(list_to_binary(F))/binary>> || F <- Fs ], 36 | {jsx:encode(Files), Req, Path}. 37 | 38 | list_html(Req, {Path, Fs}) -> 39 | Body = [[ links(Path, F) || F <- [".."|Fs] ]], 40 | HTML = [<<"Index", 41 | "">>, Body, <<"\n">>], 42 | {HTML, Req, Path}. 43 | 44 | links(<<>>, File) -> 45 | ["", File, "
\n"]; 46 | links(Prefix, File) -> 47 | ["", File, "
\n"]. 48 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Cowboy is available thanks to the work of: 2 | 3 | Loïc Hoguin 4 | Magnus Klaar 5 | Ali Sabil 6 | Anthony Ramine 7 | Adam Cammack 8 | Tom Burdick 9 | James Fish 10 | Paul Oliver 11 | Slava Yurin 12 | Vladimir Dronnikov 13 | YAMAMOTO Takashi 14 | Yurii Rashkovskii 15 | Andrew Majorov 16 | Eduardo Gurgel 17 | Egobrain 18 | Josh Toft 19 | Steven Gravell 20 | Andrew Thompson 21 | Hunter Morris 22 | Ivan Lisenkov 23 | Matthias Endler 24 | Seletskiy Stanislav 25 | Sina Samavati 26 | Tristan Sloughter 27 | 0x00F6 28 | 0xAX 29 | Adam Cammmack 30 | Adrian Roe 31 | Alexei Sholik 32 | Andre Graf 33 | Andrew Houghton 34 | Andrzej Sliwa 35 | Blake Gentry 36 | Bob Ippolito 37 | Boris Faure 38 | Cameron Bytheway 39 | Cristian Hancila 40 | Danielle Sucher 41 | Dave Peticolas 42 | David Kelly 43 | DeadZen 44 | Dmitry Groshev 45 | Drew 46 | Drew Varner 47 | Eiichi Tsukata 48 | Fred Hebert 49 | Hans Ulrich Niedermann 50 | Ivan Blinkov 51 | Jeremy Ong 52 | Jesper Louis Andersen 53 | Josh Allmann 54 | Josh Marchán 55 | José Valim 56 | Julian Squires 57 | Klaus Trainer 58 | Kuk-Hyun Lee 59 | Mathieu Lecarme 60 | Max Lapshin 61 | Michael Truog 62 | Michiel Hakvoort 63 | Nakai Ryosuke 64 | Ori Bar 65 | Pablo Vieytes 66 | Paulo Oliveira 67 | Peter Ericson 68 | RJ 69 | Radosław Szymczyszyn 70 | Richard Ramsden 71 | Roberto Ostinelli 72 | Sergey Prokhorov 73 | Sergey Rublev 74 | Sergey Urbanovich 75 | Seven Du 76 | Thomas Nordström 77 | Tim Dysinger 78 | Tomas Morstein 79 | Unix1 80 | alisdair sullivan 81 | dbmercer 82 | derdesign 83 | pmyarchon 84 | rambocoder 85 | serge 86 | -------------------------------------------------------------------------------- /README.asciidoc: -------------------------------------------------------------------------------- 1 | = Cowboy 2 | 3 | Cowboy is a small, fast and modern HTTP server for Erlang/OTP. 4 | 5 | == Goals 6 | 7 | Cowboy aims to provide a *complete* HTTP stack in a *small* code base. 8 | It is optimized for *low latency* and *low memory usage*, in part 9 | because it uses *binary strings*. 10 | 11 | Cowboy provides *routing* capabilities, selectively dispatching requests 12 | to handlers written in Erlang. 13 | 14 | Because it uses Ranch for managing connections, Cowboy can easily be 15 | *embedded* in any other application. 16 | 17 | Cowboy is *clean* and *well tested* Erlang code. 18 | 19 | == Sponsors 20 | 21 | The project is currently sponsored by https://sameroom.io[Sameroom]. 22 | 23 | The original SPDY implementation was sponsored by 24 | http://leo-project.net/leofs/[LeoFS Cloud Storage]. 25 | It has since been superseded by HTTP/2. 26 | 27 | == Online documentation 28 | 29 | * http://ninenines.eu/docs/en/cowboy/2.0/guide[User guide] 30 | * http://ninenines.eu/docs/en/cowboy/2.0/manual[Function reference] 31 | 32 | == Offline documentation 33 | 34 | * While still online, run `make docs` 35 | * User guide available in `doc/` in PDF and HTML formats 36 | * Function reference man pages available in `doc/man3/` and `doc/man7/` 37 | * Run `make install-docs` to install man pages on your system 38 | * Full documentation in Asciidoc available in `doc/src/` 39 | * Examples available in `examples/` 40 | 41 | == Getting help 42 | 43 | * Official IRC Channel: #ninenines on irc.freenode.net 44 | * http://ninenines.eu/services[Commercial Support] 45 | -------------------------------------------------------------------------------- /doc/src/guide/hooks.asciidoc: -------------------------------------------------------------------------------- 1 | [[hooks]] 2 | == Hooks 3 | 4 | Hooks allow the user to customize Cowboy's behavior during specific 5 | operations. 6 | 7 | === Onresponse 8 | 9 | The `onresponse` hook is called right before sending the response 10 | to the socket. It can be used for the purposes of logging responses, 11 | or for modifying the response headers or body. The best example is 12 | providing custom error pages. 13 | 14 | Note that this function MUST NOT crash. Cowboy may or may not send a 15 | reply if this function crashes. If a reply is sent, the hook MUST 16 | explicitly provide all headers that are needed. 17 | 18 | You can specify the `onresponse` hook when creating the listener. 19 | 20 | [source,erlang] 21 | ---- 22 | cowboy:start_http(my_http_listener, 100, 23 | [{port, 8080}], 24 | [ 25 | {env, [{dispatch, Dispatch}]}, 26 | {onresponse, fun ?MODULE:custom_404_hook/4} 27 | ] 28 | ). 29 | ---- 30 | 31 | The following hook function will provide a custom body for 404 errors 32 | when it has not been provided before, and will let Cowboy proceed with 33 | the default response otherwise. 34 | 35 | [source,erlang] 36 | ---- 37 | custom_404_hook(404, Headers, <<>>, Req) -> 38 | Body = <<"404 Not Found.">>, 39 | Headers2 = lists:keyreplace(<<"content-length">>, 1, Headers, 40 | {<<"content-length">>, integer_to_list(byte_size(Body))}), 41 | cowboy_req:reply(404, Headers2, Body, Req); 42 | custom_404_hook(_, _, _, Req) -> 43 | Req. 44 | ---- 45 | 46 | Again, make sure to always return the last request object obtained. 47 | -------------------------------------------------------------------------------- /doc/src/manual/cowboy_router.asciidoc: -------------------------------------------------------------------------------- 1 | = cowboy_router(3) 2 | 3 | == Name 4 | 5 | cowboy_router - router middleware 6 | 7 | == Description 8 | 9 | The `cowboy_router` middleware maps the requested host and 10 | path to the handler to be used for processing the request. 11 | It uses the dispatch rules compiled from the routes given 12 | to the `compile/1` function for this purpose. It adds the 13 | handler name and options to the environment as the values 14 | `handler` and `handler_opts` respectively. 15 | 16 | === Environment input 17 | 18 | dispatch = dispatch_rules():: Dispatch table. 19 | 20 | === Environment output 21 | 22 | handler = module():: Handler module. 23 | handler_opts = any():: Handler options. 24 | 25 | == Types 26 | 27 | === bindings() = [{atom(), binary()}] 28 | 29 | List of bindings found during routing. 30 | 31 | === dispatch_rules() - opaque to the user 32 | 33 | Rules for dispatching request used by Cowboy. 34 | 35 | === routes() = [{Host, Paths} | {Host, cowboy:fields(), Paths}] 36 | 37 | With types: 38 | 39 | * Host = Path = '_' | iodata() 40 | * Paths = [{Path, Handler, Opts} | {Path, cowboy:fields(), Handler, HandlerOpts}] 41 | * Handler = module() 42 | * HandlerOpts = any() 43 | 44 | Human readable list of routes mapping hosts and paths to handlers. 45 | 46 | The syntax for routes is defined in the user guide. 47 | 48 | === tokens() = [binary()] 49 | 50 | List of host_info and path_info tokens found during routing. 51 | 52 | == Exports 53 | 54 | === compile(routes()) -> dispatch_rules() 55 | 56 | Compile the routes for use by Cowboy. 57 | -------------------------------------------------------------------------------- /examples/README.asciidoc: -------------------------------------------------------------------------------- 1 | = Cowboy examples 2 | 3 | * link:chunked_hello_world[]: 4 | demonstrate chunked data transfer with two one-second delays 5 | 6 | * link:compress_response[]: 7 | send a response body compressed if the client supports it 8 | 9 | * link:cookie[]: 10 | set cookies from server and client side 11 | 12 | * link:echo_get[]: 13 | parse and echo a GET query string 14 | 15 | * link:echo_post[]: 16 | parse and echo a POST parameter 17 | 18 | * link:error_hook[]: 19 | provide custom error pages 20 | 21 | * link:eventsource[]: 22 | eventsource emitter and consumer 23 | 24 | * link:file_server[]: 25 | file server with directory listing 26 | 27 | * link:hello_world[]: 28 | simplest example application 29 | 30 | * link:markdown_middleware[]: 31 | static file handler with markdown preprocessor 32 | 33 | * link:rest_basic_auth[]: 34 | basic HTTP authorization with REST 35 | 36 | * link:rest_hello_world[]: 37 | return the data type that matches the request type (ex: html, text, json) 38 | 39 | * link:rest_pastebin[]: 40 | create text objects and return the data type that matches the request type (html, text) 41 | 42 | * link:rest_stream_response[]: 43 | stream results from a data store 44 | 45 | * link:ssl_hello_world[]: 46 | simplest SSL application 47 | 48 | * link:upload[]: 49 | multipart/form-data upload 50 | 51 | * link:websocket[]: 52 | websocket example 53 | 54 | == Other languages 55 | 56 | * https://github.com/joshrotenberg/elixir_cowboy_examples[Elixir] 57 | * https://github.com/quasiquoting/lfe-cowboy-examples[LFE] 58 | -------------------------------------------------------------------------------- /examples/rest_stream_response/README.asciidoc: -------------------------------------------------------------------------------- 1 | = REST streaming example 2 | 3 | *This example is currently broken on master.* 4 | 5 | To try this example, you need GNU `make` and `git` in your PATH. 6 | 7 | To build and run the example, use the following command: 8 | 9 | [source,bash] 10 | $ make run 11 | 12 | Then point your browser to http://localhost:8080 13 | 14 | == About 15 | 16 | This example simulates streaming a large amount of data from a data store one 17 | record at a time in CSV format. It also uses a constraint to ensure that the 18 | last segment of the route is an integer. 19 | 20 | == Example output 21 | 22 | Fetch records with the second field with value 1: 23 | 24 | [source,bash] 25 | ---- 26 | $ curl -i localhost:8080 27 | HTTP/1.1 200 OK 28 | transfer-encoding: identity 29 | server: Cowboy 30 | date: Sun, 10 Feb 2013 19:32:16 GMT 31 | connection: close 32 | content-type: text/csv 33 | 34 | DBUZGQ0C,1,28 35 | BgoQAxMV,1,6 36 | DAYEFxER,1,18 37 | ... 38 | ---- 39 | 40 | Fetch records with the second field with value 4: 41 | 42 | [source,bash] 43 | ---- 44 | $ curl -i localhost:8080/4 45 | HTTP/1.1 200 OK 46 | transfer-encoding: identity 47 | server: Cowboy 48 | date: Sun, 10 Feb 2013 19:34:31 GMT 49 | connection: close 50 | content-type: text/csv 51 | 52 | ABcFDxcE,4,42 53 | DgYQCgEE,4,5 54 | CA8BBhYD,4,10 55 | ... 56 | ---- 57 | 58 | Fail to use a proper integer and get an error: 59 | 60 | [source,bash] 61 | ---- 62 | $ curl -i localhost:8080/foo 63 | HTTP/1.1 404 Not Found 64 | connection: keep-alive 65 | server: Cowboy 66 | date: Sun, 10 Feb 2013 19:36:16 GMT 67 | content-length: 0 68 | 69 | ---- 70 | -------------------------------------------------------------------------------- /doc/src/manual/cowboy_middleware.asciidoc: -------------------------------------------------------------------------------- 1 | = cowboy_middleware(3) 2 | 3 | == Name 4 | 5 | cowboy_middleware - behaviour for middlewares 6 | 7 | == Description 8 | 9 | The `cowboy_middleware` behaviour defines the interface used 10 | by Cowboy middleware modules. 11 | 12 | Middlewares process the request sequentially in the order they 13 | are configured. 14 | 15 | == Types 16 | 17 | === env() = [{atom(), any()}] 18 | 19 | The environment variable. 20 | 21 | One is created for every request. It is passed to each 22 | middleware module executed and subsequently returned, 23 | optionally with its contents modified. 24 | 25 | == Callbacks 26 | 27 | === execute(Req, Env) -> {ok, Req, Env} | {suspend, Module, Function, Args} | {stop, Req} 28 | 29 | Req = cowboy_req:req():: The Req object. 30 | Env = env():: The request environment. 31 | Module = module():: MFA to call when resuming the process. 32 | Function = atom():: MFA to call when resuming the process. 33 | Args = [any()]:: MFA to call when resuming the process. 34 | 35 | Execute the middleware. 36 | 37 | The `ok` return value indicates that everything went well 38 | and that Cowboy should continue processing the request. A 39 | response may or may not have been sent. 40 | 41 | The `suspend` return value will hibernate the process until 42 | an Erlang message is received. Note that when resuming, any 43 | previous stacktrace information will be gone. 44 | 45 | The `stop` return value stops Cowboy from doing any further 46 | processing of the request, even if there are middlewares 47 | that haven't been executed yet. The connection may be left 48 | open to receive more requests from the client. 49 | -------------------------------------------------------------------------------- /test/handlers/ws_init_h.erl: -------------------------------------------------------------------------------- 1 | %% This module returns a different value in websocket_init/1 depending on the query string. 2 | 3 | -module(ws_init_h). 4 | -behavior(cowboy_websocket). 5 | 6 | -export([init/2]). 7 | -export([websocket_init/1]). 8 | -export([websocket_handle/2]). 9 | -export([websocket_info/2]). 10 | 11 | init(Req, _) -> 12 | State = binary_to_atom(cowboy_req:qs(Req), latin1), 13 | {cowboy_websocket, Req, State}. 14 | 15 | %% Sleep to make sure the HTTP response was sent. 16 | websocket_init(State) -> 17 | timer:sleep(100), 18 | do_websocket_init(State). 19 | 20 | do_websocket_init(State=ok) -> 21 | {ok, State}; 22 | do_websocket_init(State=ok_hibernate) -> 23 | {ok, State, hibernate}; 24 | do_websocket_init(State=reply) -> 25 | {reply, {text, "Hello"}, State}; 26 | do_websocket_init(State=reply_hibernate) -> 27 | {reply, {text, "Hello"}, State, hibernate}; 28 | do_websocket_init(State=reply_close) -> 29 | {reply, close, State}; 30 | do_websocket_init(State=reply_close_hibernate) -> 31 | {reply, close, State, hibernate}; 32 | do_websocket_init(State=reply_many) -> 33 | {reply, [{text, "Hello"}, {binary, "World"}], State}; 34 | do_websocket_init(State=reply_many_hibernate) -> 35 | {reply, [{text, "Hello"}, {binary, "World"}], State, hibernate}; 36 | do_websocket_init(State=reply_many_close) -> 37 | {reply, [{text, "Hello"}, close], State}; 38 | do_websocket_init(State=reply_many_close_hibernate) -> 39 | {reply, [{text, "Hello"}, close], State, hibernate}; 40 | do_websocket_init(State=stop) -> 41 | {stop, State}. 42 | 43 | websocket_handle(_, State) -> 44 | {ok, State}. 45 | 46 | websocket_info(_, State) -> 47 | {ok, State}. 48 | -------------------------------------------------------------------------------- /examples/rest_pastebin/README.asciidoc: -------------------------------------------------------------------------------- 1 | = REST pastebin example 2 | 3 | To try this example, you need GNU `make` and `git` in your PATH. 4 | 5 | To build and run the example, use the following command: 6 | 7 | [source,bash] 8 | $ make run 9 | 10 | Then point your browser to http://localhost:8080 11 | 12 | == Usage 13 | 14 | To upload something to the paste application, you can use `curl`: 15 | 16 | [source,bash] 17 | $ | curl -i --data-urlencode paste@- localhost:8080 18 | 19 | Or, to upload the file `my_file`: 20 | 21 | [source,bash] 22 | curl -i --data-urlencode paste@my_file localhost:8080 23 | 24 | The URL of your data will be in the location header. Alternately, you can visit 25 | http://localhost:8080 with your favorite web browser and submit your paste via 26 | the form. 27 | 28 | Code that has been pasted can be highlighted with ?lang= option if 29 | you have http://www.andre-simon.de/doku/highlight/en/highlight.html[highlight] 30 | installed (although `pygments` or any other should work just fine). 31 | 32 | This will show the contents of the HTML file: 33 | 34 | [source,bash] 35 | curl -i --data-urlencode paste@priv/index.html localhost:8080 36 | curl 37 | 38 | If your terminal supports color sequences and `highlight` is installed, 39 | the following command will show the same contents but with HTML syntax 40 | highlighting. 41 | 42 | [source,bash] 43 | curl ?lang=html 44 | 45 | If you open the same URL in your web browser and your web browser tells 46 | Cowboy that it prefers HTML files, you will see the file highlighted 47 | with special HTML markup and CSS. Firefox is known to work. 48 | -------------------------------------------------------------------------------- /doc/src/manual/cowboy.set_env.asciidoc: -------------------------------------------------------------------------------- 1 | = cowboy:set_env(3) 2 | 3 | == Name 4 | 5 | cowboy:set_env - Update a listener's environment value 6 | 7 | == Description 8 | 9 | [source,erlang] 10 | ---- 11 | set_env(Name :: ranch:ref(), 12 | Key :: atom(), 13 | Value :: any()) 14 | -> ok 15 | ---- 16 | 17 | Set or update an environment value for a previously started 18 | listener. 19 | 20 | This is most useful for updating the routes dynamically, 21 | without having to restart the listener. 22 | 23 | The new value will only be available to new connections. 24 | Pre-existing connections will still use the old value. 25 | 26 | == Arguments 27 | 28 | Name:: 29 | 30 | The name of the listener to update. 31 | + 32 | The name of the listener is the first argument given to the 33 | link:man:cowboy:start_clear(3)[cowboy:start_clear(3)], 34 | link:man:cowboy:start_tls(3)[cowboy:start_tls(3)] or 35 | link:man:ranch:start_listener(3)[ranch:start_listener(3)] function. 36 | 37 | Key:: 38 | 39 | The key in the environment map. Common keys include `dispatch` 40 | and `middlewares`. 41 | 42 | Value:: 43 | 44 | The new value. 45 | 46 | The type of the value differs depending on the key. 47 | 48 | == Return value 49 | 50 | The atom `ok` is returned on success. 51 | 52 | An `exit:badarg` exception is thrown when the listener does 53 | not exist. 54 | 55 | == Changelog 56 | 57 | * *1.0*: Function introduced. 58 | 59 | == Examples 60 | 61 | .Update a listener's routes 62 | [source,erlang] 63 | ---- 64 | Dispatch = cowboy_router:compile([ 65 | {'_', [ 66 | {"/", toppage_h, []}, 67 | {"/ws", websocket_h, []} 68 | ]} 69 | ]), 70 | 71 | cowboy:set_env(example, dispatch, Dispatch). 72 | ---- 73 | 74 | == See also 75 | 76 | link:man:cowboy(3)[cowboy(3)], 77 | link:man:cowboy:start_clear(3)[cowboy:start_clear(3)], 78 | link:man:cowboy:start_tls(3)[cowboy:start_tls(3)], 79 | link:man:ranch:set_protocol_options(3)[ranch:set_protocol_options(3)] 80 | -------------------------------------------------------------------------------- /doc/src/guide/architecture.asciidoc: -------------------------------------------------------------------------------- 1 | [[architecture]] 2 | == Architecture 3 | 4 | Cowboy is a lightweight HTTP server. 5 | 6 | It is built on top of Ranch. Please see the Ranch guide for more 7 | information. 8 | 9 | === One process per connection 10 | 11 | It uses only one process per connection. The process where your 12 | code runs is the process controlling the socket. Using one process 13 | instead of two allows for lower memory usage. 14 | 15 | Because there can be more than one request per connection with the 16 | keepalive feature of HTTP/1.1, that means the same process will be 17 | used to handle many requests. 18 | 19 | Because of this, you are expected to make sure your process cleans 20 | up before terminating the handling of the current request. This may 21 | include cleaning up the process dictionary, timers, monitoring and 22 | more. 23 | 24 | === Binaries 25 | 26 | It uses binaries. Binaries are more efficient than lists for 27 | representing strings because they take less memory space. Processing 28 | performance can vary depending on the operation. Binaries are known 29 | for generally getting a great boost if the code is compiled natively. 30 | Please see the HiPE documentation for more details. 31 | 32 | === Date header 33 | 34 | Because querying for the current date and time can be expensive, 35 | Cowboy generates one `Date` header value every second, shares it 36 | to all other processes, which then simply copy it in the response. 37 | This allows compliance with HTTP/1.1 with no actual performance loss. 38 | 39 | === Max connections 40 | 41 | By default the maximum number of active connections is set to a 42 | generally accepted big enough number. This is meant to prevent having 43 | too many processes performing potentially heavy work and slowing 44 | everything else down, or taking up all the memory. 45 | 46 | Disabling this feature, by setting the `{max_connections, infinity}` 47 | protocol option, would give you greater performance when you are 48 | only processing short-lived requests. 49 | -------------------------------------------------------------------------------- /test/handlers/multipart_h.erl: -------------------------------------------------------------------------------- 1 | %% This module reads a multipart body and echoes it back as an Erlang term. 2 | 3 | -module(multipart_h). 4 | 5 | -export([init/2]). 6 | 7 | init(Req0, State) -> 8 | {Result, Req} = case cowboy_req:binding(key, Req0) of 9 | undefined -> acc_multipart(Req0, []); 10 | <<"skip_body">> -> skip_body_multipart(Req0, []); 11 | <<"read_part2">> -> read_part2_multipart(Req0, []); 12 | <<"read_part_body2">> -> read_part_body2_multipart(Req0, []) 13 | end, 14 | {ok, cowboy_req:reply(200, #{}, term_to_binary(Result), Req), State}. 15 | 16 | acc_multipart(Req0, Acc) -> 17 | case cowboy_req:read_part(Req0) of 18 | {ok, Headers, Req1} -> 19 | {ok, Body, Req} = stream_body(Req1, <<>>), 20 | acc_multipart(Req, [{Headers, Body}|Acc]); 21 | {done, Req} -> 22 | {lists:reverse(Acc), Req} 23 | end. 24 | 25 | stream_body(Req0, Acc) -> 26 | case cowboy_req:read_part_body(Req0) of 27 | {more, Data, Req} -> 28 | stream_body(Req, << Acc/binary, Data/binary >>); 29 | {ok, Data, Req} -> 30 | {ok, << Acc/binary, Data/binary >>, Req} 31 | end. 32 | 33 | skip_body_multipart(Req0, Acc) -> 34 | case cowboy_req:read_part(Req0) of 35 | {ok, Headers, Req} -> 36 | skip_body_multipart(Req, [Headers|Acc]); 37 | {done, Req} -> 38 | {lists:reverse(Acc), Req} 39 | end. 40 | 41 | read_part2_multipart(Req0, Acc) -> 42 | case cowboy_req:read_part(Req0, #{length => 1, period => 1}) of 43 | {ok, Headers, Req1} -> 44 | {ok, Body, Req} = stream_body(Req1, <<>>), 45 | acc_multipart(Req, [{Headers, Body}|Acc]); 46 | {done, Req} -> 47 | {lists:reverse(Acc), Req} 48 | end. 49 | 50 | read_part_body2_multipart(Req0, Acc) -> 51 | case cowboy_req:read_part(Req0) of 52 | {ok, Headers, Req1} -> 53 | {ok, Body, Req} = stream_body2(Req1, <<>>), 54 | acc_multipart(Req, [{Headers, Body}|Acc]); 55 | {done, Req} -> 56 | {lists:reverse(Acc), Req} 57 | end. 58 | 59 | stream_body2(Req0, Acc) -> 60 | case cowboy_req:read_part_body(Req0, #{length => 1, period => 1}) of 61 | {more, Data, Req} -> 62 | stream_body(Req, << Acc/binary, Data/binary >>); 63 | {ok, Data, Req} -> 64 | {ok, << Acc/binary, Data/binary >>, Req} 65 | end. 66 | -------------------------------------------------------------------------------- /src/cowboy_clear.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2016, Loïc Hoguin 2 | %% 3 | %% Permission to use, copy, modify, and/or distribute this software for any 4 | %% purpose with or without fee is hereby granted, provided that the above 5 | %% copyright notice and this permission notice appear in all copies. 6 | %% 7 | %% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | %% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | %% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | %% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | %% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | %% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | -module(cowboy_clear). 16 | -behavior(ranch_protocol). 17 | 18 | -export([start_link/4]). 19 | -export([proc_lib_hack/5]). 20 | 21 | -spec start_link(ranch:ref(), inet:socket(), module(), cowboy:opts()) -> {ok, pid()}. 22 | start_link(Ref, Socket, Transport, Opts) -> 23 | Pid = proc_lib:spawn_link(?MODULE, proc_lib_hack, [self(), Ref, Socket, Transport, Opts]), 24 | {ok, Pid}. 25 | 26 | -spec proc_lib_hack(pid(), ranch:ref(), inet:socket(), module(), cowboy:opts()) -> ok. 27 | proc_lib_hack(Parent, Ref, Socket, Transport, Opts) -> 28 | try 29 | init(Parent, Ref, Socket, Transport, Opts) 30 | catch 31 | _:normal -> exit(normal); 32 | _:shutdown -> exit(shutdown); 33 | _:Reason = {shutdown, _} -> exit(Reason); 34 | _:Reason -> exit({Reason, erlang:get_stacktrace()}) 35 | end. 36 | 37 | -spec init(pid(), ranch:ref(), inet:socket(), module(), cowboy:opts()) -> ok. 38 | init(Parent, Ref, Socket, Transport, Opts) -> 39 | ok = ranch:accept_ack(Ref), 40 | init(Parent, Ref, Socket, Transport, Opts, cowboy_http). 41 | 42 | init(Parent, Ref, Socket, Transport, Opts, Protocol) -> 43 | {Handler, Type} = maps:get(stream_handler, Opts, {cowboy_stream_h, supervisor}), 44 | _ = case Type of 45 | worker -> ok; 46 | supervisor -> process_flag(trap_exit, true) 47 | end, 48 | Protocol:init(Parent, Ref, Socket, Transport, Opts, Handler). 49 | -------------------------------------------------------------------------------- /doc/src/manual/cowboy_protocol.asciidoc: -------------------------------------------------------------------------------- 1 | = cowboy_protocol(3) 2 | 3 | == Name 4 | 5 | cowboy_protocol - HTTP protocol 6 | 7 | == Description 8 | 9 | The `cowboy_protocol` module implements HTTP/1.1 and HTTP/1.0 10 | as a Ranch protocol. 11 | 12 | == Types 13 | 14 | === opts() = [Option] 15 | 16 | [source,erlang] 17 | ---- 18 | Option = {compress, boolean()} 19 | | {env, cowboy_middleware:env()} 20 | | {max_empty_lines, non_neg_integer()} 21 | | {max_header_name_length, non_neg_integer()} 22 | | {max_header_value_length, non_neg_integer()} 23 | | {max_headers, non_neg_integer()} 24 | | {max_keepalive, non_neg_integer()} 25 | | {max_request_line_length, non_neg_integer()} 26 | | {middlewares, [module()]} 27 | | {onresponse, cowboy:onresponse_fun()} 28 | | {timeout, timeout()} 29 | ---- 30 | 31 | Configuration for the HTTP protocol handler. 32 | 33 | This configuration is passed to Cowboy when starting listeners 34 | using `cowboy:start_http/4` or `cowboy:start_https/4` functions. 35 | 36 | It can be updated without restarting listeners using the 37 | Ranch functions `ranch:get_protocol_options/1` and 38 | `ranch:set_protocol_options/2`. 39 | 40 | === Option descriptions 41 | 42 | The default value is given next to the option name. 43 | 44 | compress (false):: 45 | When enabled, Cowboy will attempt to compress the response body. 46 | 47 | env ([{listener, Ref}]):: 48 | Initial middleware environment. 49 | 50 | max_empty_lines (5):: 51 | Maximum number of empty lines before a request. 52 | 53 | max_header_name_length (64):: 54 | Maximum length of header names. 55 | 56 | max_header_value_length (4096):: 57 | Maximum length of header values. 58 | 59 | max_headers (100):: 60 | Maximum number of headers allowed per request. 61 | 62 | max_keepalive (100):: 63 | Maximum number of requests allowed per connection. 64 | 65 | max_request_line_length (4096):: 66 | Maximum length of the request line. 67 | 68 | middlewares ([cowboy_router, cowboy_handler]):: 69 | List of middlewares to execute for every requests. 70 | 71 | onresponse (undefined):: 72 | Fun called every time a response is sent. 73 | 74 | timeout (5000):: 75 | Time in ms with no requests before Cowboy closes the connection. 76 | -------------------------------------------------------------------------------- /src/cowboy_constraints.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2014, Loïc Hoguin 2 | %% 3 | %% Permission to use, copy, modify, and/or distribute this software for any 4 | %% purpose with or without fee is hereby granted, provided that the above 5 | %% copyright notice and this permission notice appear in all copies. 6 | %% 7 | %% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | %% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | %% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | %% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | %% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | %% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | -module(cowboy_constraints). 16 | 17 | -export([validate/2]). 18 | 19 | -type constraint() :: int | nonempty | fun(). 20 | -export_type([constraint/0]). 21 | 22 | -spec validate(binary(), [constraint()]) -> true | {true, any()} | false. 23 | validate(Value, [Constraint]) -> 24 | apply_constraint(Value, Constraint); 25 | validate(Value, Constraints) when is_list(Constraints) -> 26 | validate_list(Value, Constraints, original); 27 | validate(Value, Constraint) -> 28 | apply_constraint(Value, Constraint). 29 | 30 | validate_list(_, [], original) -> 31 | true; 32 | validate_list(Value, [], modified) -> 33 | {true, Value}; 34 | validate_list(Value, [Constraint|Tail], State) -> 35 | case apply_constraint(Value, Constraint) of 36 | true -> 37 | validate_list(Value, Tail, State); 38 | {true, Value2} -> 39 | validate_list(Value2, Tail, modified); 40 | false -> 41 | false 42 | end. 43 | 44 | %% @todo {int, From, To}, etc. 45 | apply_constraint(Value, int) -> 46 | int(Value); 47 | apply_constraint(Value, nonempty) -> 48 | nonempty(Value); 49 | apply_constraint(Value, F) when is_function(F) -> 50 | F(Value). 51 | 52 | %% Constraint functions. 53 | 54 | int(Value) when is_binary(Value) -> 55 | try {true, binary_to_integer(Value)} 56 | catch _:_ -> false 57 | end. 58 | 59 | nonempty(<<>>) -> false; 60 | nonempty(Value) when is_binary(Value) -> true. 61 | -------------------------------------------------------------------------------- /src/cowboy_tls.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2015, Loïc Hoguin 2 | %% 3 | %% Permission to use, copy, modify, and/or distribute this software for any 4 | %% purpose with or without fee is hereby granted, provided that the above 5 | %% copyright notice and this permission notice appear in all copies. 6 | %% 7 | %% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | %% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | %% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | %% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | %% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | %% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | -module(cowboy_tls). 16 | -behavior(ranch_protocol). 17 | 18 | -export([start_link/4]). 19 | -export([proc_lib_hack/5]). 20 | 21 | -spec start_link(ranch:ref(), inet:socket(), module(), cowboy:opts()) -> {ok, pid()}. 22 | start_link(Ref, Socket, Transport, Opts) -> 23 | Pid = proc_lib:spawn_link(?MODULE, proc_lib_hack, [self(), Ref, Socket, Transport, Opts]), 24 | {ok, Pid}. 25 | 26 | -spec proc_lib_hack(pid(), ranch:ref(), inet:socket(), module(), cowboy:opts()) -> ok. 27 | proc_lib_hack(Parent, Ref, Socket, Transport, Opts) -> 28 | try 29 | init(Parent, Ref, Socket, Transport, Opts) 30 | catch 31 | _:normal -> exit(normal); 32 | _:shutdown -> exit(shutdown); 33 | _:Reason = {shutdown, _} -> exit(Reason); 34 | _:Reason -> exit({Reason, erlang:get_stacktrace()}) 35 | end. 36 | 37 | -spec init(pid(), ranch:ref(), inet:socket(), module(), cowboy:opts()) -> ok. 38 | init(Parent, Ref, Socket, Transport, Opts) -> 39 | ok = ranch:accept_ack(Ref), 40 | case ssl:negotiated_protocol(Socket) of 41 | {ok, <<"h2">>} -> 42 | init(Parent, Ref, Socket, Transport, Opts, cowboy_http2); 43 | _ -> %% http/1.1 or no protocol negotiated. 44 | init(Parent, Ref, Socket, Transport, Opts, cowboy_http) 45 | end. 46 | 47 | init(Parent, Ref, Socket, Transport, Opts, Protocol) -> 48 | {Handler, Type} = maps:get(stream_handler, Opts, {cowboy_stream_h, supervisor}), 49 | _ = case Type of 50 | worker -> ok; 51 | supervisor -> process_flag(trap_exit, true) 52 | end, 53 | Protocol:init(Parent, Ref, Socket, Transport, Opts, Handler). 54 | -------------------------------------------------------------------------------- /doc/src/guide/broken_clients.asciidoc: -------------------------------------------------------------------------------- 1 | [[broken_clients]] 2 | == Dealing with broken clients 3 | 4 | There exists a very large number of implementations for the 5 | HTTP protocol. Most widely used clients, like browsers, 6 | follow the standard quite well, but others may not. In 7 | particular custom enterprise clients tend to be very badly 8 | written. 9 | 10 | Cowboy tries to follow the standard as much as possible, 11 | but is not trying to handle every possible special cases. 12 | Instead Cowboy focuses on the cases reported in the wild, 13 | on the public Web. 14 | 15 | That means clients that ignore the HTTP standard completely 16 | may fail to understand Cowboy's responses. There are of 17 | course workarounds. This chapter aims to cover them. 18 | 19 | === Lowercase headers 20 | 21 | Cowboy converts all headers it receives to lowercase, and 22 | similarly sends back headers all in lowercase. Some broken 23 | HTTP clients have issues with that. 24 | 25 | A simple way to solve this is to create an `onresponse` hook 26 | that will format the header names with the expected case. 27 | 28 | [source,erlang] 29 | ---- 30 | capitalize_hook(Status, Headers, Body, Req) -> 31 | Headers2 = [{cowboy_bstr:capitalize_token(N), V} 32 | || {N, V} <- Headers], 33 | cowboy_req:reply(Status, Headers2, Body, Req). 34 | ---- 35 | 36 | Note that HTTP/2 clients do not have that particular issue 37 | because the specification explicitly says all headers are 38 | lowercase, unlike HTTP which allows any case but treats 39 | them as case insensitive. 40 | 41 | === Camel-case headers 42 | 43 | Sometimes it is desirable to keep the actual case used by 44 | clients, for example when acting as a proxy between two broken 45 | implementations. There is no easy solution for this other than 46 | forking the project and editing the `cowboy_protocol` file 47 | directly. 48 | 49 | === Chunked transfer-encoding 50 | 51 | Sometimes an HTTP client advertises itself as HTTP/1.1 but 52 | does not support chunked transfer-encoding. This is invalid 53 | behavior, as HTTP/1.1 clients are required to support it. 54 | 55 | A simple workaround exists in these cases. By changing the 56 | Req object response state to `waiting_stream`, Cowboy will 57 | understand that it must use the identity transfer-encoding 58 | when replying, just like if it was an HTTP/1.0 client. 59 | 60 | [source,erlang] 61 | Req2 = cowboy_req:set(resp_state, waiting_stream). 62 | -------------------------------------------------------------------------------- /doc/src/guide/book.asciidoc: -------------------------------------------------------------------------------- 1 | // a2x: --dblatex-opts "-P latex.output.revhistory=0 -P doc.publisher.show=0 -P index.numbered=0" 2 | // a2x: --dblatex-opts "-s cowboy" 3 | // a2x: -d book --attribute tabsize=4 4 | 5 | = Cowboy User Guide 6 | 7 | // REST: where should i handle bindings? init, probably. qs? in media type functions 8 | // REST: explain how a module per media type is good; module may be shared between client/server 9 | 10 | // @todo Put the list of RFCs and other documents supported somewhere. 11 | 12 | = Rationale 13 | 14 | include::modern_web.asciidoc[The modern Web] 15 | 16 | include::erlang_web.asciidoc[Erlang and the Web] 17 | 18 | = Introduction 19 | 20 | include::introduction.asciidoc[Introduction] 21 | 22 | include::getting_started.asciidoc[Getting started] 23 | 24 | // NEW! Flow diagram here 25 | // MERGE include::overview.asciidoc[Request overview] 26 | include::flow_diagram.asciidoc[Flow diagram] 27 | 28 | = Configuration 29 | 30 | include::listeners.asciidoc[Listeners] 31 | 32 | include::streams.asciidoc[Streams] 33 | 34 | include::routing.asciidoc[Routing] 35 | 36 | include::constraints.asciidoc[Constraints] 37 | 38 | = Handlers 39 | 40 | include::handlers.asciidoc[Handlers] 41 | 42 | include::loop_handlers.asciidoc[Loop handlers] 43 | 44 | include::static_files.asciidoc[Static files] 45 | 46 | = Request and response 47 | 48 | include::req.asciidoc[Request details] 49 | 50 | include::req_body.asciidoc[Reading the request body] 51 | 52 | include::resp.asciidoc[Sending a response] 53 | 54 | include::cookies.asciidoc[Using cookies] 55 | 56 | include::multipart.asciidoc[Multipart] 57 | 58 | = REST 59 | 60 | include::rest_principles.asciidoc[REST principles] 61 | 62 | include::rest_handlers.asciidoc[Handling REST requests] 63 | 64 | include::rest_flowcharts.asciidoc[REST flowcharts] 65 | 66 | include::resource_design.asciidoc[Designing a resource handler] 67 | 68 | = Websocket 69 | 70 | include::ws_protocol.asciidoc[The Websocket protocol] 71 | 72 | include::ws_handlers.asciidoc[Handling Websocket connections] 73 | 74 | = Internals 75 | 76 | // TODO: shouldn't be needed anymore? 77 | include::architecture.asciidoc[Architecture] 78 | 79 | // TODO: Move into Common scenarios or something; switch to streams 80 | include::broken_clients.asciidoc[Dealing with broken clients] 81 | 82 | include::middlewares.asciidoc[Middlewares] 83 | 84 | include::sub_protocols.asciidoc[Sub protocols] 85 | 86 | // TODO: they're gone 87 | include::hooks.asciidoc[Hooks] 88 | -------------------------------------------------------------------------------- /doc/src/guide/sub_protocols.asciidoc: -------------------------------------------------------------------------------- 1 | [[sub_protocols]] 2 | == Sub protocols 3 | 4 | Sub protocols are used for creating new types of handlers that 5 | provide extra functionality in a reusable way. Cowboy uses this 6 | mechanism to provide its loop, REST and Websocket handlers. 7 | 8 | This chapter will explain how to create your own sub protocols 9 | and handler types. 10 | 11 | === Usage 12 | 13 | To switch to a sub protocol, the `init/2` callback must return 14 | the name of the sub protocol module. Everything past this point 15 | is handled by the sub protocol. 16 | 17 | [source,erlang] 18 | ---- 19 | init(Req, State) -> 20 | {cowboy_websocket, Req, State}. 21 | ---- 22 | 23 | The return value may also have a `Timeout` value and/or the 24 | atom `hibernate`. These options are useful for long living 25 | connections. When they are not provided, the timeout value 26 | defaults to `infinity` and the hibernate value to `run`. 27 | 28 | The following snippet switches to the `my_protocol` sub 29 | protocol, sets the timeout value to 5 seconds and enables 30 | hibernation: 31 | 32 | // @todo Yeah maybe what we really need is an Opts map. 33 | 34 | [source,erlang] 35 | ---- 36 | init(Req, State) -> 37 | {my_protocol, Req, State, 5000, hibernate}. 38 | ---- 39 | 40 | If a sub protocol does not make use of these options, it should 41 | crash if it receives anything other than the default values. 42 | 43 | === Upgrade 44 | 45 | After the `init/2` function returns, Cowboy will then call the 46 | `upgrade/6` function. This is the only callback defined by the 47 | `cowboy_sub_protocol` behavior. 48 | 49 | The function is named `upgrade` because it mimics the mechanism 50 | of HTTP protocol upgrades. For some sub protocols, like Websocket, 51 | an actual upgrade is performed. For others, like REST, this is 52 | only an upgrade at Cowboy's level and the client has nothing to 53 | do about it. 54 | 55 | The upgrade callback receives the Req object, the middleware 56 | environment, the handler and its options, and the aforementioned 57 | timeout and hibernate values. 58 | 59 | [source,erlang] 60 | ---- 61 | upgrade(Req, Env, Handler, HandlerOpts, Timeout, Hibernate) -> 62 | %% Sub protocol code here. 63 | ---- 64 | 65 | This callback is expected to behave like a middleware and to 66 | return an updated environment and Req object. 67 | 68 | Sub protocols are expected to call the `cowboy_handler:terminate/4` 69 | function when they terminate. This function will make sure that 70 | the optional `terminate/3` callback is called, if present. 71 | -------------------------------------------------------------------------------- /doc/src/manual/cowboy_app.asciidoc: -------------------------------------------------------------------------------- 1 | = cowboy(7) 2 | 3 | == Name 4 | 5 | cowboy - Small, fast, modern HTTP server for Erlang/OTP 6 | 7 | == Description 8 | 9 | Cowboy is an HTTP server for Erlang/OTP with support for the 10 | HTTP/1.1, HTTP/2 and Websocket protocols. 11 | 12 | Cowboy aims to provide a complete HTTP stack. This includes 13 | the implementation of the HTTP RFCs but also any directly 14 | related standards, like Websocket or Server-Sent Events. 15 | 16 | == Modules 17 | 18 | Functions: 19 | 20 | * link:man:cowboy(3)[cowboy(3)] - Listener management 21 | * link:man:cowboy_req(3)[cowboy_req(3)] - Request and response 22 | * link:man:cowboy_router(3)[cowboy_router(3)] - Router 23 | 24 | // @todo What about cowboy_constraints? 25 | 26 | Protocols: 27 | 28 | * link:man:cowboy_http(3)[cowboy_http(3)] - HTTP/1.1 29 | * link:man:cowboy_http2(3)[cowboy_http2(3)] - HTTP/2 30 | * link:man:cowboy_websocket(3)[cowboy_websocket(3)] - Websocket 31 | 32 | Handlers: 33 | 34 | * link:man:cowboy_static(3)[cowboy_static(3)] - Static file handler 35 | 36 | // @todo What about cowboy_stream_h? 37 | 38 | Behaviors: 39 | 40 | * link:man:cowboy_handler(3)[cowboy_handler(3)] - Plain HTTP handlers 41 | * link:man:cowboy_loop(3)[cowboy_loop(3)] - Loop handlers 42 | * link:man:cowboy_middleware(3)[cowboy_middleware(3)] - Middlewares 43 | * link:man:cowboy_rest(3)[cowboy_rest(3)] - REST handlers 44 | * link:man:cowboy_stream(3)[cowboy_stream(3)] - Stream handlers 45 | * link:man:cowboy_sub_protocol(3)[cowboy_sub_protocol(3)] - Sub protocols 46 | * link:man:cowboy_websocket(3)[cowboy_websocket(3)] - Websocket handlers 47 | 48 | Middlewares: 49 | 50 | * link:man:cowboy_router(3)[cowboy_router(3)] - Router middleware 51 | * link:man:cowboy_handler(3)[cowboy_handler(3)] - Handler middleware 52 | 53 | == Dependencies 54 | 55 | * link:man:ranch(7)[ranch(7)] - Socket acceptor pool for TCP protocols 56 | * link:man:cowlib(7)[cowlib(7)] - Support library for manipulating Web protocols 57 | * ssl - Secure communication over sockets 58 | * crypto - Crypto functions 59 | 60 | // @todo Explicitly depend on ssl. 61 | 62 | All these applications must be started before the `cowboy` 63 | application. To start Cowboy and all dependencies at once: 64 | 65 | [source,erlang] 66 | ---- 67 | {ok, _} = application:ensure_all_started(cowboy). 68 | ---- 69 | 70 | == Environment 71 | 72 | The `cowboy` application does not define any application 73 | environment configuration parameters. 74 | 75 | == See also 76 | 77 | link:man:ranch(7)[ranch(7)], 78 | link:man:cowlib(7)[cowlib(7)] 79 | -------------------------------------------------------------------------------- /doc/src/manual/cowboy.asciidoc: -------------------------------------------------------------------------------- 1 | = cowboy(3) 2 | 3 | == Name 4 | 5 | cowboy - HTTP server 6 | 7 | == Description 8 | 9 | The module `cowboy` provides convenience functions for 10 | manipulating Ranch listeners. 11 | 12 | == Exports 13 | 14 | * link:man:cowboy:start_clear(3)[cowboy:start_clear(3)] - Listen for connections using plain TCP 15 | * link:man:cowboy:start_tls(3)[cowboy:start_tls(3)] - Listen for connections using TLS 16 | * link:man:cowboy:stop_listener(3)[cowboy:stop_listener(3)] - Stop the given listener 17 | * link:man:cowboy:set_env(3)[cowboy:set_env(3)] - Update a listener's environment value 18 | 19 | == Types 20 | 21 | === fields() 22 | 23 | [source,erlang] 24 | ---- 25 | fields() :: [Name 26 | | {Name, Constraints} 27 | | {Name, Constraints, Default}] 28 | 29 | Name :: atom() 30 | Constraints :: Constraint | [Constraint] 31 | Constraint :: cowboy_constraints:constraint() 32 | Default :: any() 33 | ---- 34 | 35 | Fields description for match operations. 36 | 37 | This type is used in link:man:cowboy_router(3)[cowboy_router(3)] 38 | for matching bindings and in the match functions found in 39 | link:man:cowboy_req(3)[cowboy_req(3)]. 40 | 41 | === http_headers() 42 | 43 | [source,erlang] 44 | ---- 45 | http_headers() :: #{binary() => iodata()} 46 | ---- 47 | 48 | HTTP headers. 49 | 50 | === http_status() 51 | 52 | [source,erlang] 53 | ---- 54 | http_status() :: non_neg_integer() | binary() 55 | ---- 56 | 57 | HTTP response status. 58 | 59 | A binary status can be used to set a reason phrase. Note 60 | however that HTTP/2 only sends the status code and drops 61 | the reason phrase entirely. 62 | 63 | === http_version() 64 | 65 | [source,erlang] 66 | ---- 67 | http_version() :: 'HTTP/2' | 'HTTP/1.1' | 'HTTP/1.0' 68 | ---- 69 | 70 | HTTP version. 71 | 72 | Note that semantically, HTTP/1.1 and HTTP/2 are equivalent. 73 | 74 | === opts() 75 | 76 | [source,erlang] 77 | ---- 78 | opts() :: map() 79 | ---- 80 | 81 | Options for the HTTP/1.1, HTTP/2 and Websocket protocols. 82 | 83 | The protocol options are in a map containing all the options for 84 | the different protocols that may be involved when connecting 85 | to the listener, including HTTP/1.1 and HTTP/2 but also 86 | subprotocols like Websocket. 87 | // @todo For Websocket this might change in the future. 88 | 89 | The HTTP/1.1 options are documented in the 90 | link:man:cowboy_http(3)[cowboy_http(3)] manual; 91 | the HTTP/2 options in 92 | link:man:cowboy_http2(3)[cowboy_http2(3)]; 93 | and the Websocket options in 94 | link:man:cowboy_websocket(3)[cowboy_websocket(3)]. 95 | 96 | == See also 97 | 98 | link:man:cowboy(7)[cowboy(7)], 99 | link:man:ranch(3)[ranch(3)] 100 | -------------------------------------------------------------------------------- /src/cowboy_stream.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2015, Loïc Hoguin 2 | %% 3 | %% Permission to use, copy, modify, and/or distribute this software for any 4 | %% purpose with or without fee is hereby granted, provided that the above 5 | %% copyright notice and this permission notice appear in all copies. 6 | %% 7 | %% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | %% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | %% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | %% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | %% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | %% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | -module(cowboy_stream). 16 | 17 | -type streamid() :: any(). 18 | -type fin() :: fin | nofin. 19 | -type headers() :: map(). %% @todo cowboy:http_headers() when they're maps 20 | 21 | -type status_code() :: 100..999. %% @todo cowboy:http_status() when not binary 22 | -type state() :: any(). 23 | 24 | -type commands() :: [{response, fin(), status_code(), headers()} 25 | | {data, fin(), iodata()} 26 | | {promise, binary(), binary(), binary(), binary(), headers()} 27 | | {flow, auto | integer()} 28 | | {spawn, pid()} 29 | | {upgrade, module(), state()}]. 30 | 31 | -type human_reason() :: atom(). 32 | -type reason() :: [{internal_error, timeout | {error | exit | throw, any()}, human_reason()} 33 | | {socket_error, closed | atom(), human_reason()} 34 | | {stream_error, cow_http2:error_reason(), human_reason()} 35 | | {connection_error, cow_http2:error_reason(), human_reason()} 36 | | {stop, cow_http2:frame(), human_reason()}]. 37 | 38 | -callback init(streamid(), fin(), binary(), binary(), binary(), binary(), 39 | headers(), cowboy:opts()) -> {commands(), state()}. 40 | -callback data(streamid(), fin(), binary(), State) -> {commands(), State} when State::state(). 41 | -callback info(streamid(), any(), state()) -> {commands(), State} when State::state(). 42 | -callback terminate(streamid(), reason(), state()) -> any(). 43 | 44 | %% @todo To optimize the number of active timers we could have a command 45 | %% that enables a timeout that is called in the absence of any other call, 46 | %% similar to what gen_server does. However the nice thing about this is 47 | %% that the connection process can keep a single timer around (the same 48 | %% one that would be used to detect half-closed sockets) and use this 49 | %% timer and other events to trigger the timeout in streams at their 50 | %% intended time. 51 | %% 52 | %% This same timer can be used to try and send PING frames to help detect 53 | %% that the connection is indeed unresponsive. 54 | -------------------------------------------------------------------------------- /src/cowboy_handler.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2011-2014, Loïc Hoguin 2 | %% 3 | %% Permission to use, copy, modify, and/or distribute this software for any 4 | %% purpose with or without fee is hereby granted, provided that the above 5 | %% copyright notice and this permission notice appear in all copies. 6 | %% 7 | %% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | %% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | %% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | %% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | %% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | %% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | %% Handler middleware. 16 | %% 17 | %% Execute the handler given by the handler and handler_opts 18 | %% environment values. The result of this execution is added to the 19 | %% environment under the result value. 20 | -module(cowboy_handler). 21 | -behaviour(cowboy_middleware). 22 | 23 | -export([execute/2]). 24 | -export([terminate/4]). 25 | 26 | -callback init(Req, any()) 27 | -> {ok | module(), Req, any()} 28 | | {module(), Req, any(), hibernate} 29 | | {module(), Req, any(), timeout()} 30 | | {module(), Req, any(), timeout(), hibernate} 31 | when Req::cowboy_req:req(). 32 | 33 | -callback terminate(any(), cowboy_req:req(), any()) -> ok. 34 | -optional_callbacks([terminate/3]). 35 | 36 | -spec execute(Req, Env) -> {ok, Req, Env} 37 | when Req::cowboy_req:req(), Env::cowboy_middleware:env(). 38 | execute(Req, Env=#{handler := Handler, handler_opts := HandlerOpts}) -> 39 | try Handler:init(Req, HandlerOpts) of 40 | {ok, Req2, State} -> 41 | Result = terminate(normal, Req2, State, Handler), 42 | {ok, Req2, [{result, Result}|Env]}; 43 | {Mod, Req2, State} -> 44 | Mod:upgrade(Req2, Env, Handler, State, infinity, run); 45 | {Mod, Req2, State, hibernate} -> 46 | Mod:upgrade(Req2, Env, Handler, State, infinity, hibernate); 47 | {Mod, Req2, State, Timeout} -> 48 | Mod:upgrade(Req2, Env, Handler, State, Timeout, run); 49 | {Mod, Req2, State, Timeout, hibernate} -> 50 | Mod:upgrade(Req2, Env, Handler, State, Timeout, hibernate) 51 | catch Class:Reason -> 52 | terminate({crash, Class, Reason}, Req, HandlerOpts, Handler), 53 | erlang:raise(Class, Reason, erlang:get_stacktrace()) 54 | end. 55 | 56 | -spec terminate(any(), Req, any(), module()) -> ok when Req::cowboy_req:req(). 57 | terminate(Reason, Req, State, Handler) -> 58 | case erlang:function_exported(Handler, terminate, 3) of 59 | true -> 60 | Handler:terminate(Reason, Req, State); 61 | false -> 62 | ok 63 | end. 64 | -------------------------------------------------------------------------------- /examples/compress_response/README.asciidoc: -------------------------------------------------------------------------------- 1 | = Compressed response example 2 | 3 | *This example is currently broken on master.* 4 | 5 | To try this example, you need GNU `make` and `git` in your PATH. 6 | 7 | To build and run the example, use the following command: 8 | 9 | [source,bash] 10 | $ make run 11 | 12 | Then point your browser to http://localhost:8080 13 | 14 | == Example output 15 | 16 | Without compression: 17 | 18 | [source,bash] 19 | ---- 20 | $ curl -i http://localhost:8080 21 | HTTP/1.1 200 OK 22 | connection: keep-alive 23 | server: Cowboy 24 | date: Mon, 07 Jan 2013 18:42:29 GMT 25 | content-length: 909 26 | 27 | A cowboy is an animal herder who tends cattle on ranches in North America, 28 | traditionally on horseback, and often performs a multitude of other ranch- 29 | related tasks. The historic American cowboy of the late 19th century arose 30 | from the vaquero traditions of northern Mexico and became a figure of special 31 | significance and legend. A subtype, called a wrangler, specifically tends the 32 | horses used to work cattle. In addition to ranch work, some cowboys work for 33 | or participate in rodeos. Cowgirls, first defined as such in the late 19th 34 | century, had a less-well documented historical role, but in the modern world 35 | have established the ability to work at virtually identical tasks and obtained 36 | considerable respect for their achievements. There are also cattle handlers 37 | in many other parts of the world, particularly South America and Australia, 38 | who perform work similar to the cowboy in their respective nations. 39 | ---- 40 | 41 | With compression: 42 | 43 | [source,bash] 44 | ---- 45 | $ curl -i --compressed http://localhost:8080 46 | HTTP/1.1 200 OK 47 | connection: keep-alive 48 | server: Cowboy 49 | date: Mon, 07 Jan 2013 18:42:30 GMT 50 | content-encoding: gzip 51 | content-length: 510 52 | 53 | A cowboy is an animal herder who tends cattle on ranches in North America, 54 | traditionally on horseback, and often performs a multitude of other ranch- 55 | related tasks. The historic American cowboy of the late 19th century arose 56 | from the vaquero traditions of northern Mexico and became a figure of special 57 | significance and legend. A subtype, called a wrangler, specifically tends the 58 | horses used to work cattle. In addition to ranch work, some cowboys work for 59 | or participate in rodeos. Cowgirls, first defined as such in the late 19th 60 | century, had a less-well documented historical role, but in the modern world 61 | have established the ability to work at virtually identical tasks and obtained 62 | considerable respect for their achievements. There are also cattle handlers 63 | in many other parts of the world, particularly South America and Australia, 64 | who perform work similar to the cowboy in their respective nations. 65 | ---- 66 | -------------------------------------------------------------------------------- /doc/src/guide/middlewares.asciidoc: -------------------------------------------------------------------------------- 1 | [[middlewares]] 2 | == Middlewares 3 | 4 | Cowboy delegates the request processing to middleware components. 5 | By default, two middlewares are defined, for the routing and handling 6 | of the request, as is detailed in most of this guide. 7 | 8 | Middlewares give you complete control over how requests are to be 9 | processed. You can add your own middlewares to the mix or completely 10 | change the chain of middlewares as needed. 11 | 12 | Cowboy will execute all middlewares in the given order, unless one 13 | of them decides to stop processing. 14 | 15 | === Usage 16 | 17 | Middlewares only need to implement a single callback: `execute/2`. 18 | It is defined in the `cowboy_middleware` behavior. 19 | 20 | This callback has two arguments. The first is the `Req` object. 21 | The second is the environment. 22 | 23 | Middlewares can return one of three different values: 24 | 25 | * `{ok, Req, Env}` to continue the request processing 26 | * `{suspend, Module, Function, Args}` to hibernate 27 | * `{stop, Req}` to stop processing and move on to the next request 28 | 29 | Of note is that when hibernating, processing will resume on the given 30 | MFA, discarding all previous stacktrace. Make sure you keep the `Req` 31 | and `Env` in the arguments of this MFA for later use. 32 | 33 | If an error happens during middleware processing, Cowboy will not try 34 | to send an error back to the socket, the process will just crash. It 35 | is up to the middleware to make sure that a reply is sent if something 36 | goes wrong. 37 | 38 | === Configuration 39 | 40 | The middleware environment is defined as the `env` protocol option. 41 | In the previous chapters we saw it briefly when we needed to pass 42 | the routing information. It is a list of tuples with the first 43 | element being an atom and the second any Erlang term. 44 | 45 | Two values in the environment are reserved: 46 | 47 | * `listener` contains the name of the listener 48 | * `result` contains the result of the processing 49 | 50 | The `listener` value is always defined. The `result` value can be 51 | set by any middleware. If set to anything other than `ok`, Cowboy 52 | will not process any subsequent requests on this connection. 53 | 54 | The middlewares that come with Cowboy may define or require other 55 | environment values to perform. 56 | 57 | You can update the environment by calling the `cowboy:set_env/3` 58 | convenience function, adding or replacing a value in the environment. 59 | 60 | === Routing middleware 61 | 62 | The routing middleware requires the `dispatch` value. If routing 63 | succeeds, it will put the handler name and options in the `handler` 64 | and `handler_opts` values of the environment, respectively. 65 | 66 | === Handler middleware 67 | 68 | The handler middleware requires the `handler` and `handler_opts` 69 | values. It puts the result of the request handling into `result`. 70 | -------------------------------------------------------------------------------- /doc/src/guide/ws_protocol.asciidoc: -------------------------------------------------------------------------------- 1 | [[ws_protocol]] 2 | == The Websocket protocol 3 | 4 | This chapter explains what Websocket is and why it is 5 | a vital component of soft realtime Web applications. 6 | 7 | === Description 8 | 9 | Websocket is an extension to HTTP that emulates plain TCP 10 | connections between the client, typically a Web browser, 11 | and the server. It uses the HTTP Upgrade mechanism to 12 | establish the connection. 13 | 14 | Websocket connections are fully asynchronous, unlike 15 | HTTP/1.1 (synchronous) and HTTP/2 (asynchronous, but the 16 | server can only initiate streams in response to requests). 17 | With Websocket, the client and the server can both send 18 | frames at any time without any restriction. It is closer 19 | to TCP than any of the HTTP protocols. 20 | 21 | Websocket is an IETF standard. Cowboy supports the standard 22 | and all drafts that were previously implemented by browsers, 23 | excluding the initial flawed draft sometimes known as 24 | "version 0". 25 | 26 | === Websocket vs HTTP/2 27 | 28 | For a few years Websocket was the only way to have a 29 | bidirectional asynchronous connection with the server. 30 | This changed when HTTP/2 was introduced. While HTTP/2 31 | requires the client to first perform a request before 32 | the server can push data, this is only a minor restriction 33 | as the client can do so just as it connects. 34 | 35 | Websocket was designed as a kind-of-TCP channel to a 36 | server. It only defines the framing and connection 37 | management and lets the developer implement a protocol 38 | on top of it. For example you could implement IRC over 39 | Websocket and use a Javascript IRC client to speak to 40 | the server. 41 | 42 | HTTP/2 on the other hand is just an improvement over 43 | the HTTP/1.1 connection and request/response mechanism. 44 | It has the same semantics as HTTP/1.1. 45 | 46 | If all you need is to access an HTTP API, then HTTP/2 47 | should be your first choice. On the other hand, if what 48 | you need is a different protocol, then you can use 49 | Websocket to implement it. 50 | 51 | === Implementation 52 | 53 | Cowboy implements Websocket as a protocol upgrade. Once the 54 | upgrade is performed from the `init/2` callback, Cowboy 55 | switches to Websocket. Please consult the next chapter for 56 | more information on initiating and handling Websocket 57 | connections. 58 | 59 | The implementation of Websocket in Cowboy is validated using 60 | the Autobahn test suite, which is an extensive suite of tests 61 | covering all aspects of the protocol. Cowboy passes the 62 | suite with 100% success, including all optional tests. 63 | 64 | Cowboy's Websocket implementation also includes the 65 | permessage-deflate and x-webkit-deflate-frame compression 66 | extensions. 67 | 68 | Cowboy will automatically use compression as long as the 69 | `websocket_compress` protocol option is set when starting 70 | the listener. 71 | -------------------------------------------------------------------------------- /examples/hello_world/README.asciidoc: -------------------------------------------------------------------------------- 1 | = Hello world example 2 | 3 | To try this example, you need GNU `make` and `git` in your PATH. 4 | 5 | To build and run the example, use the following command: 6 | 7 | [source,bash] 8 | $ make run 9 | 10 | Then point your browser to http://localhost:8080 11 | 12 | == HTTP/1.1 example output 13 | 14 | [source,bash] 15 | ---- 16 | $ curl -i http://localhost:8080 17 | HTTP/1.1 200 OK 18 | connection: keep-alive 19 | server: Cowboy 20 | date: Fri, 28 Sep 2012 04:10:25 GMT 21 | content-length: 12 22 | content-type: text/plain 23 | 24 | Hello world! 25 | ---- 26 | 27 | == HTTP/2 example output 28 | 29 | [source,bash] 30 | ---- 31 | $ nghttp -v http://localhost:8080 32 | [ 0.000] Connected 33 | [ 0.000] send SETTINGS frame 34 | (niv=2) 35 | [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100] 36 | [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535] 37 | [ 0.000] send PRIORITY frame 38 | (dep_stream_id=0, weight=201, exclusive=0) 39 | [ 0.000] send PRIORITY frame 40 | (dep_stream_id=0, weight=101, exclusive=0) 41 | [ 0.000] send PRIORITY frame 42 | (dep_stream_id=0, weight=1, exclusive=0) 43 | [ 0.000] send PRIORITY frame 44 | (dep_stream_id=7, weight=1, exclusive=0) 45 | [ 0.000] send PRIORITY frame 46 | (dep_stream_id=3, weight=1, exclusive=0) 47 | [ 0.000] send HEADERS frame 48 | ; END_STREAM | END_HEADERS | PRIORITY 49 | (padlen=0, dep_stream_id=11, weight=16, exclusive=0) 50 | ; Open new stream 51 | :method: GET 52 | :path: / 53 | :scheme: http 54 | :authority: localhost:8080 55 | accept: */* 56 | accept-encoding: gzip, deflate 57 | user-agent: nghttp2/1.7.1 58 | [ 0.008] recv SETTINGS frame 59 | (niv=0) 60 | [ 0.008] recv SETTINGS frame 61 | ; ACK 62 | (niv=0) 63 | [ 0.008] send SETTINGS frame 64 | ; ACK 65 | (niv=0) 66 | [ 0.013] recv (stream_id=13) :status: 200 67 | [ 0.013] recv (stream_id=13) content-length: 12 68 | [ 0.013] recv (stream_id=13) content-type: text/plain 69 | [ 0.013] recv (stream_id=13) date: Thu, 09 Jun 2016 08:56:56 GMT 70 | [ 0.013] recv (stream_id=13) server: Cowboy 71 | [ 0.013] recv HEADERS frame 72 | ; END_HEADERS 73 | (padlen=0) 74 | ; First response header 75 | Hello world![ 0.013] recv DATA frame 76 | ; END_STREAM 77 | [ 0.013] send GOAWAY frame 78 | (last_stream_id=0, error_code=NO_ERROR(0x00), opaque_data(0)=[]) 79 | ---- 80 | -------------------------------------------------------------------------------- /doc/src/guide/introduction.asciidoc: -------------------------------------------------------------------------------- 1 | [[introduction]] 2 | == Introduction 3 | 4 | Cowboy is a small, fast and modern HTTP server for Erlang/OTP. 5 | 6 | Cowboy aims to provide a complete xref:modern_web[modern Web stack]. 7 | This includes HTTP/1.1, HTTP/2, Websocket, Server-Sent Events and 8 | Webmachine-based REST. 9 | 10 | Cowboy comes with functions for introspection and tracing, enabling 11 | developers to know precisely what is happening at any time. Its modular 12 | design also easily enable developers to add instrumentation. 13 | 14 | Cowboy is a high quality project. It has a small code base, is very 15 | efficient (both in latency and memory use) and can easily be embedded 16 | in another application. 17 | 18 | Cowboy is clean Erlang code. It includes hundreds of tests and its code 19 | is fully compliant with the Dialyzer. It is also well documented and 20 | features a Function Reference, a User Guide and numerous Tutorials. 21 | 22 | === Prerequisites 23 | 24 | Beginner Erlang knowledge is recommended for reading this guide. 25 | 26 | Knowledge of the HTTP protocol is recommended but not required, as it 27 | will be detailed throughout the guide. 28 | 29 | === Supported platforms 30 | 31 | Cowboy is tested and supported on Linux, FreeBSD, Windows and OSX. 32 | 33 | Cowboy has been reported to work on other platforms, but we make no 34 | guarantee that the experience will be safe and smooth. You are advised 35 | to perform the necessary testing and security audits prior to deploying 36 | on other platforms. 37 | 38 | Cowboy is developed for Erlang/OTP 19.0 and newer. 39 | 40 | === License 41 | 42 | Cowboy uses the ISC License. 43 | 44 | ---- 45 | Copyright (c) 2011-2016, Loïc Hoguin 46 | 47 | Permission to use, copy, modify, and/or distribute this software for any 48 | purpose with or without fee is hereby granted, provided that the above 49 | copyright notice and this permission notice appear in all copies. 50 | 51 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 52 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 53 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 54 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 55 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 56 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 57 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 58 | ---- 59 | 60 | === Versioning 61 | 62 | Cowboy uses http://semver.org/[Semantic Versioning 2.0.0]. 63 | 64 | === Conventions 65 | 66 | In the HTTP protocol, the method name is case sensitive. All standard 67 | method names are uppercase. 68 | 69 | Header names are case insensitive. When using HTTP/1.1, Cowboy converts 70 | all the request header names to lowercase. HTTP/2 requires clients to 71 | send them as lowercase. Any other header name is expected to be provided 72 | lowercased, including when querying information about the request or 73 | when sending responses. 74 | 75 | The same applies to any other case insensitive value. 76 | -------------------------------------------------------------------------------- /doc/src/guide/constraints.asciidoc: -------------------------------------------------------------------------------- 1 | [[constraints]] 2 | == Constraints 3 | 4 | Constraints are validation and conversion functions applied 5 | to user input. 6 | 7 | They are used in various places in Cowboy, including the 8 | router and the request match functions. 9 | 10 | === Syntax 11 | 12 | Constraints are provided as a list of fields. For each field 13 | in the list, specific constraints can be applied, as well as 14 | a default value if the field is missing. 15 | 16 | A field can take the form of an atom `field`, a tuple with 17 | constraints `{field, Constraints}` or a tuple with constraints 18 | and a default value `{field, Constraints, Default}`. 19 | The `field` form indicates the field is mandatory. 20 | 21 | Note that when used with the router, only the second form 22 | makes sense, as it does not use the default and the field 23 | is always defined. 24 | 25 | Constraints for each field are provided as an ordered list 26 | of atoms or funs to apply. Built-in constraints are provided 27 | as atoms, while custom constraints are provided as funs. 28 | 29 | When multiple constraints are provided, they are applied in 30 | the order given. If the value has been modified by a constraint 31 | then the next one receives the new value. 32 | 33 | For example, the following constraints will first validate 34 | and convert the field `my_value` to an integer, and then 35 | check that the integer is positive: 36 | 37 | [source,erlang] 38 | ---- 39 | PositiveFun = fun(V) when V > 0 -> true; (_) -> false end, 40 | {my_value, [int, PositiveFun]}. 41 | ---- 42 | 43 | When there's only one constraint, it can be provided directly 44 | without wrapping it into a list: 45 | 46 | [source,erlang] 47 | ---- 48 | {my_value, int} 49 | ---- 50 | 51 | === Built-in constraints 52 | 53 | Built-in constraints are specified as an atom: 54 | 55 | [cols="<,<",options="header"] 56 | |=== 57 | | Constraint | Description 58 | | int | Converts binary value to integer. 59 | | nonempty | Ensures the binary value is non-empty. 60 | |=== 61 | 62 | === Custom constraints 63 | 64 | Custom constraints are specified as a fun. This fun takes 65 | a single argument and must return one of `true`, `{true, NewValue}` 66 | or `false`. 67 | 68 | `true` indicates the input is valid, `false` otherwise. 69 | The `{true, NewValue}` tuple is returned when the input 70 | is valid and the value has been converted. For example, 71 | the following constraint will convert the binary input 72 | to an integer: 73 | 74 | [source,erlang] 75 | ---- 76 | fun (Value0) when is_binary(Value0) -> 77 | try binary_to_integer(Value0) of 78 | Value -> {true, Value} 79 | catch _:_ -> 80 | false 81 | end. 82 | ---- 83 | 84 | Constraint functions should only crash because the programmer 85 | made an error when chaining constraints incorrectly (for example 86 | if the constraints were `[int, int]`, and not because of input. 87 | If the input is invalid then `false` must be returned. 88 | 89 | In our snippet, the `is_binary/1` guard will crash only 90 | because of a programmer error, and the try block is there 91 | to ensure that we do not crash when the input is invalid. 92 | -------------------------------------------------------------------------------- /src/cowboy.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2011-2014, Loïc Hoguin 2 | %% 3 | %% Permission to use, copy, modify, and/or distribute this software for any 4 | %% purpose with or without fee is hereby granted, provided that the above 5 | %% copyright notice and this permission notice appear in all copies. 6 | %% 7 | %% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | %% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | %% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | %% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | %% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | %% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | -module(cowboy). 16 | 17 | -export([start_clear/4]). 18 | -export([start_tls/4]). 19 | -export([stop_listener/1]). 20 | -export([set_env/3]). 21 | 22 | %% @todo Detailed opts. 23 | -type opts() :: map(). 24 | -export_type([opts/0]). 25 | 26 | -type fields() :: [atom() 27 | | {atom(), cowboy_constraints:constraint() | [cowboy_constraints:constraint()]} 28 | | {atom(), cowboy_constraints:constraint() | [cowboy_constraints:constraint()], any()}]. 29 | -export_type([fields/0]). 30 | 31 | -type http_headers() :: #{binary() => iodata()}. 32 | -export_type([http_headers/0]). 33 | 34 | -type http_status() :: non_neg_integer() | binary(). 35 | -export_type([http_status/0]). 36 | 37 | -type http_version() :: 'HTTP/2' | 'HTTP/1.1' | 'HTTP/1.0'. 38 | -export_type([http_version/0]). 39 | 40 | -spec start_clear(ranch:ref(), non_neg_integer(), ranch_tcp:opts(), 41 | cowboy_protocol:opts()) -> {ok, pid()} | {error, any()}. 42 | start_clear(Ref, NbAcceptors, TransOpts0, ProtoOpts) 43 | when is_integer(NbAcceptors), NbAcceptors > 0 -> 44 | TransOpts = [connection_type(ProtoOpts)|TransOpts0], 45 | ranch:start_listener(Ref, NbAcceptors, ranch_tcp, TransOpts, cowboy_clear, ProtoOpts). 46 | 47 | -spec start_tls(ranch:ref(), non_neg_integer(), ranch_ssl:opts(), opts()) -> {ok, pid()} | {error, any()}. 48 | start_tls(Ref, NbAcceptors, TransOpts0, ProtoOpts) 49 | when is_integer(NbAcceptors), NbAcceptors > 0 -> 50 | TransOpts = [ 51 | connection_type(ProtoOpts), 52 | {next_protocols_advertised, [<<"h2">>, <<"http/1.1">>]}, 53 | {alpn_preferred_protocols, [<<"h2">>, <<"http/1.1">>]} 54 | |TransOpts0], 55 | ranch:start_listener(Ref, NbAcceptors, ranch_ssl, TransOpts, cowboy_tls, ProtoOpts). 56 | 57 | -spec connection_type(opts()) -> {connection_type, worker | supervisor}. 58 | connection_type(ProtoOpts) -> 59 | {_, Type} = maps:get(stream_handler, ProtoOpts, {cowboy_stream_h, supervisor}), 60 | {connection_type, Type}. 61 | 62 | -spec stop_listener(ranch:ref()) -> ok | {error, not_found}. 63 | stop_listener(Ref) -> 64 | ranch:stop_listener(Ref). 65 | 66 | -spec set_env(ranch:ref(), atom(), any()) -> ok. 67 | set_env(Ref, Name, Value) -> 68 | Opts = ranch:get_protocol_options(Ref), 69 | {_, Env} = lists:keyfind(env, 1, Opts), 70 | Opts2 = lists:keyreplace(env, 1, Opts, 71 | {env, lists:keystore(Name, 1, Env, {Name, Value})}), 72 | ok = ranch:set_protocol_options(Ref, Opts2). 73 | -------------------------------------------------------------------------------- /examples/echo_get/README.asciidoc: -------------------------------------------------------------------------------- 1 | = GET parameter echo example 2 | 3 | To try this example, you need GNU `make` and `git` in your PATH. 4 | 5 | To build and run the example, use the following command: 6 | 7 | [source,bash] 8 | $ make run 9 | 10 | Then point your browser to http://localhost:8080/?echo=hello 11 | 12 | You can replace the `echo` parameter with another to check 13 | that the handler is echoing it back properly. 14 | 15 | == HTTP/1.1 example output 16 | 17 | [source,bash] 18 | ---- 19 | $ curl -i "http://localhost:8080/?echo=saymyname" 20 | HTTP/1.1 200 OK 21 | connection: keep-alive 22 | server: Cowboy 23 | date: Fri, 28 Sep 2012 04:09:04 GMT 24 | content-length: 9 25 | content-type: text/plain; charset=utf-8 26 | 27 | saymyname 28 | ---- 29 | 30 | == HTTP/2 example output 31 | 32 | [source,bash] 33 | ---- 34 | $ nghttp -v "http://localhost:8080/?echo=saymyname" 35 | [ 0.000] Connected 36 | [ 0.000] send SETTINGS frame 37 | (niv=2) 38 | [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100] 39 | [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535] 40 | [ 0.000] send PRIORITY frame 41 | (dep_stream_id=0, weight=201, exclusive=0) 42 | [ 0.000] send PRIORITY frame 43 | (dep_stream_id=0, weight=101, exclusive=0) 44 | [ 0.000] send PRIORITY frame 45 | (dep_stream_id=0, weight=1, exclusive=0) 46 | [ 0.000] send PRIORITY frame 47 | (dep_stream_id=7, weight=1, exclusive=0) 48 | [ 0.000] send PRIORITY frame 49 | (dep_stream_id=3, weight=1, exclusive=0) 50 | [ 0.000] send HEADERS frame 51 | ; END_STREAM | END_HEADERS | PRIORITY 52 | (padlen=0, dep_stream_id=11, weight=16, exclusive=0) 53 | ; Open new stream 54 | :method: GET 55 | :path: /?echo=saymyname 56 | :scheme: http 57 | :authority: localhost:8080 58 | accept: */* 59 | accept-encoding: gzip, deflate 60 | user-agent: nghttp2/1.7.1 61 | [ 0.000] recv SETTINGS frame 62 | (niv=0) 63 | [ 0.000] send SETTINGS frame 64 | ; ACK 65 | (niv=0) 66 | [ 0.000] recv SETTINGS frame 67 | ; ACK 68 | (niv=0) 69 | [ 0.001] recv (stream_id=13) :status: 200 70 | [ 0.001] recv (stream_id=13) content-length: 9 71 | [ 0.001] recv (stream_id=13) content-type: text/plain; charset=utf-8 72 | [ 0.001] recv (stream_id=13) date: Thu, 09 Jun 2016 09:06:05 GMT 73 | [ 0.001] recv (stream_id=13) server: Cowboy 74 | [ 0.001] recv HEADERS frame 75 | ; END_HEADERS 76 | (padlen=0) 77 | ; First response header 78 | saymyname[ 0.001] recv DATA frame 79 | ; END_STREAM 80 | [ 0.001] send GOAWAY frame 81 | (last_stream_id=0, error_code=NO_ERROR(0x00), opaque_data(0)=[]) 82 | ---- 83 | -------------------------------------------------------------------------------- /test/loop_handler_SUITE.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2011-2014, Loïc Hoguin 2 | %% 3 | %% Permission to use, copy, modify, and/or distribute this software for any 4 | %% purpose with or without fee is hereby granted, provided that the above 5 | %% copyright notice and this permission notice appear in all copies. 6 | %% 7 | %% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | %% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | %% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | %% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | %% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | %% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | -module(loop_handler_SUITE). 16 | -compile(export_all). 17 | 18 | -import(ct_helper, [config/2]). 19 | -import(ct_helper, [doc/1]). 20 | -import(cowboy_test, [gun_open/1]). 21 | 22 | %% ct. 23 | 24 | all() -> 25 | cowboy_test:common_all(). 26 | 27 | groups() -> 28 | cowboy_test:common_groups(ct_helper:all(?MODULE)). 29 | 30 | init_per_group(Name, Config) -> 31 | cowboy_test:init_common_groups(Name, Config, ?MODULE). 32 | 33 | end_per_group(Name, _) -> 34 | cowboy:stop_listener(Name). 35 | 36 | %% Dispatch configuration. 37 | 38 | init_dispatch(_) -> 39 | cowboy_router:compile([{'_', [ 40 | {"/long_polling", long_polling_h, []}, 41 | {"/loop_body", loop_handler_body_h, []}, 42 | {"/loop_timeout", loop_handler_timeout_h, []} 43 | ]}]). 44 | 45 | %% Tests. 46 | 47 | long_polling(Config) -> 48 | doc("Simple long-polling."), 49 | ConnPid = gun_open(Config), 50 | Ref = gun:get(ConnPid, "/long_polling"), 51 | {response, fin, 102, _} = gun:await(ConnPid, Ref), 52 | ok. 53 | 54 | long_polling_body(Config) -> 55 | doc("Long-polling with a body that falls within the configurable limits."), 56 | ConnPid = gun_open(Config), 57 | Ref = gun:post(ConnPid, "/long_polling", [], << 0:5000/unit:8 >>), 58 | {response, fin, 102, _} = gun:await(ConnPid, Ref), 59 | ok. 60 | 61 | long_polling_body_too_large(Config) -> 62 | doc("Long-polling with a body that exceeds the configurable limits."), 63 | ConnPid = gun_open(Config), 64 | Ref = gun:post(ConnPid, "/long_polling", [], << 0:100000/unit:8 >>), 65 | {response, fin, 500, _} = gun:await(ConnPid, Ref), 66 | ok. 67 | 68 | long_polling_pipeline(Config) -> 69 | doc("Pipeline of long-polling calls."), 70 | ConnPid = gun_open(Config), 71 | Refs = [gun:get(ConnPid, "/long_polling") || _ <- lists:seq(1, 2)], 72 | _ = [{response, fin, 102, _} = gun:await(ConnPid, Ref) || Ref <- Refs], 73 | ok. 74 | 75 | loop_body(Config) -> 76 | doc("Check that a loop handler can read the request body in info/3."), 77 | ConnPid = gun_open(Config), 78 | Ref = gun:post(ConnPid, "/loop_body", [], << 0:100000/unit:8 >>), 79 | {response, fin, 200, _} = gun:await(ConnPid, Ref), 80 | ok. 81 | 82 | loop_timeout(Config) -> 83 | doc("Ensure that the loop handler timeout results in a 204 response."), 84 | ConnPid = gun_open(Config), 85 | Ref = gun:get(ConnPid, "/loop_timeout"), 86 | {response, fin, 204, _} = gun:await(ConnPid, Ref), 87 | ok. 88 | -------------------------------------------------------------------------------- /doc/src/manual/cowboy_loop.asciidoc: -------------------------------------------------------------------------------- 1 | = cowboy_loop(3) 2 | 3 | == Name 4 | 5 | cowboy_loop - loop handlers 6 | 7 | == Description 8 | 9 | The `cowboy_loop` module implements a handler interface for 10 | long running HTTP connections. It is the recommended interface 11 | for long polling and server-sent events, amongst others. 12 | 13 | This module is a sub protocol that defines three callbacks to 14 | be implemented by handlers. The `init/2` and `terminate/3` 15 | callbacks are common to all handler types and are documented 16 | in the manual for the link:cowboy_handler.asciidoc[cowboy_handler] module. 17 | 18 | The `info/3` callback is specific to loop handlers and will be 19 | called as many times as necessary until a reply is sent. 20 | 21 | It is highly recommended to return a timeout value from the 22 | `init/2` callback to ensure that the process is terminated 23 | when no data has been received during that timespan. The 24 | default timeout is `infinity`, which should only be used if 25 | you have alternate means of ending inactive connections. 26 | 27 | == Terminate reasons 28 | 29 | The following values may be received as the terminate reason 30 | in the optional `terminate/3` callback. 31 | 32 | normal:: 33 | The connection was closed normally before switching to the 34 | loop sub protocol. This typically happens if an `ok` tuple is 35 | returned from the `init/2` callback. 36 | 37 | stop:: 38 | The handler requested to close the connection by returning 39 | a `stop` tuple. 40 | 41 | timeout:: 42 | The connection has been closed due to inactivity. The timeout 43 | value can be configured from `init/2`. The response sent when 44 | this happens is a `204 No Content`. 45 | 46 | {crash, Class, Reason}:: 47 | A crash occurred in the handler. `Class` and `Reason` can be 48 | used to obtain more information about the crash. The function 49 | `erlang:get_stacktrace/0` can also be called to obtain the 50 | stacktrace of the process when the crash occurred. 51 | 52 | {error, overflow}:: 53 | The connection is being closed and the process terminated 54 | because the buffer Cowboy uses to keep data sent by the 55 | client has reached its maximum. The buffer size can be 56 | configured through the environment value `loop_max_buffer` 57 | and defaults to 5000 bytes. 58 | + 59 | If the long running request comes with a body it is recommended 60 | to process this body before switching to the loop sub protocol. 61 | 62 | {error, closed}:: 63 | The socket has been closed brutally without a close frame being 64 | received first. 65 | 66 | {error, Reason}:: 67 | A socket error ocurred. 68 | 69 | == Callbacks 70 | 71 | === info(Info, Req, State) -> {ok, Req, State} | {ok, Req, State, hibernate} | {stop, Req, State} 72 | 73 | Info = any():: Message received by the process. 74 | Req = cowboy_req:req():: The Req object. 75 | State = any():: Handler state. 76 | 77 | Handle the Erlang message received. 78 | 79 | This function will be called every time an Erlang message 80 | has been received. The message can be any Erlang term. 81 | 82 | The `stop` return value can be used to stop the receive loop, 83 | typically because a response has been sent. 84 | 85 | The `hibernate` option will hibernate the process until 86 | it receives another message. 87 | -------------------------------------------------------------------------------- /examples/chunked_hello_world/README.asciidoc: -------------------------------------------------------------------------------- 1 | = Chunked hello world example 2 | 3 | To try this example, you need GNU `make` and `git` in your PATH. 4 | 5 | To build and run the example, use the following command: 6 | 7 | [source,bash] 8 | $ make run 9 | 10 | Then point your browser to http://localhost:8080 11 | or use `curl` to see the chunks arriving one at a time every second. 12 | 13 | == HTTP/1.1 example output 14 | 15 | [source,bash] 16 | ---- 17 | $ time curl -i http://localhost:8080 18 | HTTP/1.1 200 OK 19 | transfer-encoding: chunked 20 | connection: keep-alive 21 | server: Cowboy 22 | date: Fri, 28 Sep 2012 04:24:16 GMT 23 | 24 | Hello 25 | World 26 | Chunked! 27 | curl -i http://localhost:8080 0.01s user 0.00s system 0% cpu 2.015 total 28 | ---- 29 | 30 | == HTTP/2 example output 31 | 32 | [source,bash] 33 | ---- 34 | $ nghttp -v http://localhost:8080 35 | [ 0.000] Connected 36 | [ 0.000] send SETTINGS frame 37 | (niv=2) 38 | [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100] 39 | [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535] 40 | [ 0.000] send PRIORITY frame 41 | (dep_stream_id=0, weight=201, exclusive=0) 42 | [ 0.000] send PRIORITY frame 43 | (dep_stream_id=0, weight=101, exclusive=0) 44 | [ 0.000] send PRIORITY frame 45 | (dep_stream_id=0, weight=1, exclusive=0) 46 | [ 0.000] send PRIORITY frame 47 | (dep_stream_id=7, weight=1, exclusive=0) 48 | [ 0.000] send PRIORITY frame 49 | (dep_stream_id=3, weight=1, exclusive=0) 50 | [ 0.000] send HEADERS frame 51 | ; END_STREAM | END_HEADERS | PRIORITY 52 | (padlen=0, dep_stream_id=11, weight=16, exclusive=0) 53 | ; Open new stream 54 | :method: GET 55 | :path: / 56 | :scheme: http 57 | :authority: localhost:8080 58 | accept: */* 59 | accept-encoding: gzip, deflate 60 | user-agent: nghttp2/1.7.1 61 | [ 0.006] recv SETTINGS frame 62 | (niv=0) 63 | [ 0.006] recv SETTINGS frame 64 | ; ACK 65 | (niv=0) 66 | [ 0.006] send SETTINGS frame 67 | ; ACK 68 | (niv=0) 69 | [ 0.010] recv (stream_id=13) :status: 200 70 | [ 0.010] recv (stream_id=13) date: Mon, 13 Jun 2016 14:16:26 GMT 71 | [ 0.010] recv (stream_id=13) server: Cowboy 72 | [ 0.010] recv HEADERS frame 73 | ; END_HEADERS 74 | (padlen=0) 75 | ; First response header 76 | Hello 77 | [ 0.010] recv DATA frame 78 | World 79 | [ 1.012] recv DATA frame 80 | Chunked! 81 | [ 2.013] recv DATA frame 82 | [ 2.013] recv DATA frame 83 | ; END_STREAM 84 | [ 2.013] send GOAWAY frame 85 | (last_stream_id=0, error_code=NO_ERROR(0x00), opaque_data(0)=[]) 86 | ---- 87 | -------------------------------------------------------------------------------- /examples/ssl_hello_world/README.asciidoc: -------------------------------------------------------------------------------- 1 | = Secure hello world example 2 | 3 | To try this example, you need GNU `make` and `git` in your PATH. 4 | 5 | To build and run the example, use the following command: 6 | 7 | [source,bash] 8 | $ make run 9 | 10 | Then point your browser to https://localhost:8443 11 | 12 | You will need to temporarily trust the root certificate authority, 13 | which can also be found in `priv/ssl/cowboy-ca.crt`. 14 | 15 | Recent browsers will communicate using HTTP/2. Older browsers 16 | will use HTTP/1.1. 17 | 18 | == HTTP/1.1 example output 19 | 20 | [source,bash] 21 | ---- 22 | $ curl --cacert priv/ssl/cowboy-ca.crt -i https://localhost:8443 23 | HTTP/1.1 200 OK 24 | connection: keep-alive 25 | server: Cowboy 26 | date: Fri, 28 Sep 2012 04:10:25 GMT 27 | content-length: 12 28 | 29 | Hello world! 30 | ---- 31 | 32 | == HTTP/2 example output 33 | 34 | [source,bash] 35 | ---- 36 | $ nghttp -v https://localhost:8443 37 | [ 0.001] Connected 38 | The negotiated protocol: h2 39 | [ 0.009] recv SETTINGS frame 40 | (niv=0) 41 | [ 0.009] send SETTINGS frame 42 | (niv=2) 43 | [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100] 44 | [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535] 45 | [ 0.009] send SETTINGS frame 46 | ; ACK 47 | (niv=0) 48 | [ 0.009] send PRIORITY frame 49 | (dep_stream_id=0, weight=201, exclusive=0) 50 | [ 0.009] send PRIORITY frame 51 | (dep_stream_id=0, weight=101, exclusive=0) 52 | [ 0.009] send PRIORITY frame 53 | (dep_stream_id=0, weight=1, exclusive=0) 54 | [ 0.009] send PRIORITY frame 55 | (dep_stream_id=7, weight=1, exclusive=0) 56 | [ 0.009] send PRIORITY frame 57 | (dep_stream_id=3, weight=1, exclusive=0) 58 | [ 0.009] send HEADERS frame 59 | ; END_STREAM | END_HEADERS | PRIORITY 60 | (padlen=0, dep_stream_id=11, weight=16, exclusive=0) 61 | ; Open new stream 62 | :method: GET 63 | :path: / 64 | :scheme: https 65 | :authority: localhost:8443 66 | accept: */* 67 | accept-encoding: gzip, deflate 68 | user-agent: nghttp2/1.7.1 69 | [ 0.010] recv SETTINGS frame 70 | ; ACK 71 | (niv=0) 72 | [ 0.010] recv (stream_id=13) :status: 200 73 | [ 0.010] recv (stream_id=13) content-length: 12 74 | [ 0.010] recv (stream_id=13) content-type: text/plain 75 | [ 0.010] recv (stream_id=13) date: Sat, 30 Apr 2016 12:54:32 GMT 76 | [ 0.010] recv (stream_id=13) server: Cowboy 77 | [ 0.010] recv HEADERS frame 78 | ; END_HEADERS 79 | (padlen=0) 80 | ; First response header 81 | Hello world![ 0.010] recv DATA frame 82 | ; END_STREAM 83 | [ 0.010] send GOAWAY frame 84 | (last_stream_id=0, error_code=NO_ERROR(0x00), opaque_data(0)=[]) 85 | ---- 86 | -------------------------------------------------------------------------------- /test/handlers/echo_h.erl: -------------------------------------------------------------------------------- 1 | %% This module echoes back the value the test is interested in. 2 | 3 | -module(echo_h). 4 | 5 | -export([init/2]). 6 | 7 | init(Req, Opts) -> 8 | case cowboy_req:binding(arg, Req) of 9 | undefined -> 10 | echo(cowboy_req:binding(key, Req), Req, Opts); 11 | Arg -> 12 | echo_arg(Arg, Req, Opts) 13 | end. 14 | 15 | echo(<<"read_body">>, Req0, Opts) -> 16 | case Opts of 17 | #{crash := true} -> ct_helper:ignore(cowboy_req, read_body, 2); 18 | _ -> ok 19 | end, 20 | {_, Body, Req} = case cowboy_req:path(Req0) of 21 | <<"/full", _/bits>> -> read_body(Req0, <<>>); 22 | <<"/opts", _/bits>> -> cowboy_req:read_body(Req0, Opts); 23 | _ -> cowboy_req:read_body(Req0) 24 | end, 25 | {ok, cowboy_req:reply(200, #{}, Body, Req), Opts}; 26 | echo(<<"read_urlencoded_body">>, Req0, Opts) -> 27 | Path = cowboy_req:path(Req0), 28 | case {Path, Opts} of 29 | {<<"/opts", _/bits>>, #{crash := true}} -> ct_helper:ignore(cowboy_req, read_body, 2); 30 | {_, #{crash := true}} -> ct_helper:ignore(cowboy_req, read_urlencoded_body, 2); 31 | _ -> ok 32 | end, 33 | {ok, Body, Req} = case Path of 34 | <<"/opts", _/bits>> -> cowboy_req:read_urlencoded_body(Req0, Opts); 35 | <<"/crash", _/bits>> -> cowboy_req:read_urlencoded_body(Req0, Opts); 36 | _ -> cowboy_req:read_urlencoded_body(Req0) 37 | end, 38 | {ok, cowboy_req:reply(200, #{}, value_to_iodata(Body), Req), Opts}; 39 | echo(<<"uri">>, Req, Opts) -> 40 | Value = case cowboy_req:path_info(Req) of 41 | [<<"origin">>] -> cowboy_req:uri(Req, #{host => undefined}); 42 | [<<"protocol-relative">>] -> cowboy_req:uri(Req, #{scheme => undefined}); 43 | [<<"no-qs">>] -> cowboy_req:uri(Req, #{qs => undefined}); 44 | [<<"no-path">>] -> cowboy_req:uri(Req, #{path => undefined, qs => undefined}); 45 | [<<"set-port">>] -> cowboy_req:uri(Req, #{port => 123}); 46 | [] -> cowboy_req:uri(Req) 47 | end, 48 | {ok, cowboy_req:reply(200, #{}, Value, Req), Opts}; 49 | echo(<<"match">>, Req, Opts) -> 50 | [Type|Fields0] = cowboy_req:path_info(Req), 51 | Fields = [binary_to_atom(F, latin1) || F <- Fields0], 52 | Value = case Type of 53 | <<"qs">> -> cowboy_req:match_qs(Fields, Req); 54 | <<"cookies">> -> cowboy_req:match_cookies(Fields, Req) 55 | end, 56 | {ok, cowboy_req:reply(200, #{}, value_to_iodata(Value), Req), Opts}; 57 | echo(What, Req, Opts) -> 58 | F = binary_to_atom(What, latin1), 59 | Value = cowboy_req:F(Req), 60 | {ok, cowboy_req:reply(200, #{}, value_to_iodata(Value), Req), Opts}. 61 | 62 | echo_arg(Arg0, Req, Opts) -> 63 | F = binary_to_atom(cowboy_req:binding(key, Req), latin1), 64 | Arg = case F of 65 | binding -> binary_to_atom(Arg0, latin1); 66 | _ -> Arg0 67 | end, 68 | Value = case cowboy_req:binding(default, Req) of 69 | undefined -> cowboy_req:F(Arg, Req); 70 | Default -> cowboy_req:F(Arg, Req, Default) 71 | end, 72 | {ok, cowboy_req:reply(200, #{}, value_to_iodata(Value), Req), Opts}. 73 | 74 | read_body(Req0, Acc) -> 75 | case cowboy_req:read_body(Req0) of 76 | {ok, Data, Req} -> {ok, << Acc/binary, Data/binary >>, Req}; 77 | {more, Data, Req} -> read_body(Req, << Acc/binary, Data/binary >>) 78 | end. 79 | 80 | value_to_iodata(V) when is_integer(V) -> integer_to_binary(V); 81 | value_to_iodata(V) when is_atom(V) -> atom_to_binary(V, latin1); 82 | value_to_iodata(V) when is_list(V); is_tuple(V); is_map(V) -> io_lib:format("~p", [V]); 83 | value_to_iodata(V) -> V. 84 | -------------------------------------------------------------------------------- /doc/src/guide/handlers.asciidoc: -------------------------------------------------------------------------------- 1 | [[handlers]] 2 | == Handlers 3 | 4 | Handlers are Erlang modules that handle HTTP requests. 5 | 6 | === Plain HTTP handlers 7 | 8 | The most basic handler in Cowboy implements the mandatory 9 | `init/2` callback, manipulates the request, optionally 10 | sends a response and then returns. 11 | 12 | This callback receives the xref:req[Req object] and the initial 13 | state defined in the xref:routing[router configuration]. 14 | 15 | A handler that does nothing would look like this: 16 | 17 | [source,erlang] 18 | ---- 19 | init(Req, State) -> 20 | {ok, Req, State}. 21 | ---- 22 | 23 | Despite sending no reply, a `204 No Content` response will be 24 | sent to the client, as Cowboy makes sure that a response is 25 | sent for every request. 26 | 27 | We need to use the Req object to reply. 28 | 29 | [source,erlang] 30 | ---- 31 | init(Req0, State) -> 32 | Req = cowboy_req:reply(200, [ 33 | {<<"content-type">>, <<"text/plain">>} 34 | ], <<"Hello World!">>, Req0), 35 | {ok, Req, State}. 36 | ---- 37 | 38 | Cowboy will immediately send a response when `cowboy:reply/4` 39 | is called. 40 | 41 | We then return a 3-tuple. `ok` means that the handler ran 42 | successfully. We also give the modified Req back to Cowboy. 43 | 44 | The last value of the tuple is a state that will be used 45 | in every subsequent callbacks to this handler. Plain HTTP 46 | handlers only have one additional callback, the optional 47 | and rarely used `terminate/3`. 48 | 49 | === Other handlers 50 | 51 | The `init/2` callback can also be used to inform Cowboy 52 | that this is a different kind of handler and that Cowboy 53 | should switch to it. To do this you simply need to return 54 | the module name of the handler type you want to switch to. 55 | 56 | Cowboy comes with three handler types you can switch to: 57 | xref:rest_handlers[cowboy_rest], xref:ws_handlers[cowboy_websocket] 58 | and xref:loop_handlers[cowboy_loop]. In addition to those you 59 | can define your own handler types. 60 | 61 | Switching is simple. Instead of returning `ok`, you simply 62 | return the name of the handler type you want to use. The 63 | following snippet switches to a Websocket handler: 64 | 65 | [source,erlang] 66 | ---- 67 | init(Req, State) -> 68 | {cowboy_websocket, Req, State}. 69 | ---- 70 | 71 | You can also switch to your own custom handler type: 72 | 73 | [source,erlang] 74 | ---- 75 | init(Req, State) -> 76 | {my_handler_type, Req, State}. 77 | ---- 78 | 79 | How to implement a custom handler type is described in the 80 | xref:sub_protocols[Sub protocols] chapter. 81 | 82 | === Cleaning up 83 | 84 | With the exception of Websocket handlers, all handler types 85 | provide the optional `terminate/3` callback. 86 | 87 | [source,erlang] 88 | ---- 89 | terminate(_Reason, _Req, _State) -> 90 | ok. 91 | ---- 92 | 93 | This callback is strictly reserved for any required cleanup. 94 | You cannot send a response from this function. There is no 95 | other return value. 96 | 97 | This callback is optional because it is rarely necessary. 98 | Cleanup should be done in separate processes directly (by 99 | monitoring the handler process to detect when it exits). 100 | 101 | Cowboy does not reuse processes for different requests. The 102 | process will terminate soon after this call returns. 103 | -------------------------------------------------------------------------------- /examples/echo_post/README.asciidoc: -------------------------------------------------------------------------------- 1 | = POST parameter echo example 2 | 3 | To try this example, you need GNU `make` and `git` in your PATH. 4 | 5 | To build and run the example, use the following command: 6 | 7 | [source,bash] 8 | $ make run 9 | 10 | As this example echoes a POST parameter, it is a little more 11 | complex to test. Some browsers feature tools that allow you 12 | to perform one such request, or you can use the command line 13 | tool `curl` as we will demonstrate. 14 | 15 | == HTTP/1.1 example output 16 | 17 | [source,bash] 18 | ---- 19 | $ curl -i -d echo=echomeplz http://localhost:8080 20 | HTTP/1.1 200 OK 21 | connection: keep-alive 22 | server: Cowboy 23 | date: Fri, 28 Sep 2012 04:12:36 GMT 24 | content-length: 9 25 | content-type: text/plain; charset=utf-8 26 | 27 | echomeplz 28 | ---- 29 | 30 | == HTTP/2 example output 31 | 32 | [source,bash] 33 | ---- 34 | $ echo echo=echomeplz | nghttp -v -d - http://localhost:8080 35 | [ 0.000] Connected 36 | [ 0.000] send SETTINGS frame 37 | (niv=2) 38 | [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100] 39 | [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535] 40 | [ 0.000] send PRIORITY frame 41 | (dep_stream_id=0, weight=201, exclusive=0) 42 | [ 0.000] send PRIORITY frame 43 | (dep_stream_id=0, weight=101, exclusive=0) 44 | [ 0.001] send PRIORITY frame 45 | (dep_stream_id=0, weight=1, exclusive=0) 46 | [ 0.001] send PRIORITY frame 47 | (dep_stream_id=7, weight=1, exclusive=0) 48 | [ 0.001] send PRIORITY frame 49 | (dep_stream_id=3, weight=1, exclusive=0) 50 | [ 0.001] send HEADERS frame 51 | ; END_HEADERS | PRIORITY 52 | (padlen=0, dep_stream_id=11, weight=16, exclusive=0) 53 | ; Open new stream 54 | :method: POST 55 | :path: / 56 | :scheme: http 57 | :authority: localhost:8080 58 | accept: */* 59 | accept-encoding: gzip, deflate 60 | user-agent: nghttp2/1.7.1 61 | content-length: 15 62 | [ 0.001] send DATA frame 63 | [ 0.001] send DATA frame 64 | ; END_STREAM 65 | [ 0.012] recv SETTINGS frame 66 | (niv=0) 67 | [ 0.012] recv SETTINGS frame 68 | ; ACK 69 | (niv=0) 70 | [ 0.012] send SETTINGS frame 71 | ; ACK 72 | (niv=0) 73 | [ 0.020] recv (stream_id=13) :status: 200 74 | [ 0.020] recv (stream_id=13) content-length: 10 75 | [ 0.020] recv (stream_id=13) content-type: text/plain; charset=utf-8 76 | [ 0.020] recv (stream_id=13) date: Thu, 09 Jun 2016 09:19:35 GMT 77 | [ 0.020] recv (stream_id=13) server: Cowboy 78 | [ 0.020] recv HEADERS frame 79 | ; END_HEADERS 80 | (padlen=0) 81 | ; First response header 82 | echomeplz 83 | [ 0.020] recv DATA frame 84 | ; END_STREAM 85 | [ 0.020] send GOAWAY frame 86 | (last_stream_id=0, error_code=NO_ERROR(0x00), opaque_data(0)=[]) 87 | ---- 88 | -------------------------------------------------------------------------------- /examples/file_server/README.asciidoc: -------------------------------------------------------------------------------- 1 | = File server example with directory listing 2 | 3 | To try this example, you need GNU `make` and `git` in your PATH. 4 | 5 | To build and run the example, use the following command: 6 | 7 | [source,bash] 8 | $ make run 9 | 10 | Then point your browser to http://localhost:8080 11 | to browse the contents of the `priv` directory. 12 | 13 | Interesting examples include: 14 | 15 | * http://localhost:8080/test.txt[Plain text file] 16 | * http://localhost:8080/video.html[HTML5 video demo] 17 | 18 | == HTTP/1.1 example output 19 | 20 | [source,bash] 21 | ---- 22 | $ curl -i http://localhost:8080/test.txt 23 | HTTP/1.1 200 OK 24 | connection: keep-alive 25 | server: Cowboy 26 | date: Mon, 09 Sep 2013 13:49:50 GMT 27 | content-length: 52 28 | content-type: text/plain 29 | last-modified: Fri, 18 Jan 2013 16:33:31 GMT 30 | 31 | If you read this then the static file server works! 32 | ---- 33 | 34 | == HTTP/2 example output 35 | 36 | [source,bash] 37 | ---- 38 | $ nghttp -v http://localhost:8080/test.txt 39 | [ 0.000] Connected 40 | [ 0.000] send SETTINGS frame 41 | (niv=2) 42 | [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100] 43 | [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535] 44 | [ 0.000] send PRIORITY frame 45 | (dep_stream_id=0, weight=201, exclusive=0) 46 | [ 0.000] send PRIORITY frame 47 | (dep_stream_id=0, weight=101, exclusive=0) 48 | [ 0.000] send PRIORITY frame 49 | (dep_stream_id=0, weight=1, exclusive=0) 50 | [ 0.000] send PRIORITY frame 51 | (dep_stream_id=7, weight=1, exclusive=0) 52 | [ 0.000] send PRIORITY frame 53 | (dep_stream_id=3, weight=1, exclusive=0) 54 | [ 0.000] send HEADERS frame 55 | ; END_STREAM | END_HEADERS | PRIORITY 56 | (padlen=0, dep_stream_id=11, weight=16, exclusive=0) 57 | ; Open new stream 58 | :method: GET 59 | :path: /test.txt 60 | :scheme: http 61 | :authority: localhost:8080 62 | accept: */* 63 | accept-encoding: gzip, deflate 64 | user-agent: nghttp2/1.7.1 65 | [ 0.001] recv SETTINGS frame 66 | (niv=0) 67 | [ 0.001] recv SETTINGS frame 68 | ; ACK 69 | (niv=0) 70 | [ 0.001] send SETTINGS frame 71 | ; ACK 72 | (niv=0) 73 | [ 0.007] recv (stream_id=13) :status: 200 74 | [ 0.007] recv (stream_id=13) content-length: 52 75 | [ 0.007] recv (stream_id=13) content-type: text/plain 76 | [ 0.007] recv (stream_id=13) date: Mon, 13 Jun 2016 11:25:20 GMT 77 | [ 0.007] recv (stream_id=13) etag: "1371478245" 78 | [ 0.007] recv (stream_id=13) last-modified: Tue, 12 Aug 2014 17:00:17 GMT 79 | [ 0.007] recv (stream_id=13) server: Cowboy 80 | [ 0.007] recv HEADERS frame 81 | ; END_HEADERS 82 | (padlen=0) 83 | ; First response header 84 | If you read this then the static file server works! 85 | [ 0.007] recv DATA frame 86 | ; END_STREAM 87 | [ 0.007] send GOAWAY frame 88 | (last_stream_id=0, error_code=NO_ERROR(0x00), opaque_data(0)=[]) 89 | ---- 90 | -------------------------------------------------------------------------------- /doc/src/manual/cowboy_handler.asciidoc: -------------------------------------------------------------------------------- 1 | = cowboy_handler(3) 2 | 3 | == Name 4 | 5 | cowboy_handler - handler middleware and behaviour 6 | 7 | == Description 8 | 9 | The `cowboy_handler` middleware executes the handler passed 10 | through the environment values `handler` and `handler_opts`, 11 | and adds the result of this execution to the environment as 12 | the value `result`, indicating that the request has been 13 | handled and received a response. 14 | 15 | Environment input: 16 | 17 | handler = module():: Handler to be executed. 18 | handler_opts = any():: Options to be passed to the handler. 19 | 20 | Environment output: 21 | 22 | result = ok:: Result of the request. 23 | 24 | This module also defines the `cowboy_handler` behaviour that 25 | defines the basic interface for handlers. All Cowboy handlers 26 | implement at least the `init/2` callback, and may implement 27 | the `terminate/3` callback optionally. 28 | 29 | == Terminate reasons 30 | 31 | The following values may be received as the terminate reason 32 | in the optional `terminate/3` callback. Different handler types 33 | may define additional terminate reasons. 34 | 35 | normal:: 36 | The connection was closed normally. 37 | 38 | {crash, Class, Reason}:: 39 | A crash occurred in the handler. `Class` and `Reason` can be 40 | used to obtain more information about the crash. The function 41 | `erlang:get_stacktrace/0` can also be called to obtain the 42 | stacktrace of the process when the crash occurred. 43 | 44 | == Callbacks 45 | 46 | === init(Req, Opts) -> {ok, Req, State} | {Module, Req, State} | {Module, Req, State, hibernate | Timeout} | {Module, Req, State, Timeout, hibernate} 47 | 48 | Req = cowboy_req:req():: The Req object. 49 | Opts = any():: Handler options. 50 | State = any():: Handler state. 51 | Module = module():: Module of the sub-protocol to use. 52 | Timeout = timeout():: Timeout passed to the sub-protocol, when applicable. 53 | 54 | Process the request. 55 | 56 | This function can be used to switch to an alternate handler 57 | type by returning the name of the module to be used, along 58 | with a few options. 59 | 60 | For basic handlers this is the function where the response 61 | should be sent. If no response is sent, Cowboy will ensure 62 | that a `204 No Content` response is sent. 63 | 64 | A crash in this callback will result in `terminate/3` being 65 | called if it is defined, with the `State` argument set to 66 | the value of `Opts` originally given to the `init/2` callback. 67 | 68 | === terminate(Reason, Req, State) -> ok 69 | 70 | Reason = any():: Reason for termination. 71 | Req = cowboy_req:req():: The Req object. 72 | State = any():: Handler state. 73 | 74 | Perform any necessary cleanup of the state. 75 | 76 | This callback should release any resource currently in use, 77 | clear any active timer and reset the process to its original 78 | state, as it might be reused for future requests sent on the 79 | same connection. Typical plain HTTP handlers rarely need to 80 | use it. 81 | 82 | A crash in this callback or an invalid return value will 83 | result in the closing of the connection and the termination 84 | of the process. 85 | 86 | == Exports 87 | 88 | === terminate(Reason, Req, State, Handler) -> ok 89 | 90 | Reason = any():: Reason for termination. 91 | Req = cowboy_req:req():: The Req object. 92 | State = any():: Handler state. 93 | Handler = module():: Handler module. 94 | 95 | Call the optional `terminate/3` callback if it exists. 96 | 97 | This function should always be called at the end of the execution 98 | of a handler, to give it a chance to clean up or perform 99 | miscellaneous operations. 100 | -------------------------------------------------------------------------------- /examples/websocket/priv/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Websocket client 5 | 6 | 84 | 85 | 86 | 87 | 91 | 92 | 93 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /doc/src/manual/cowboy.start_clear.asciidoc: -------------------------------------------------------------------------------- 1 | = cowboy:start_clear(3) 2 | 3 | == Name 4 | 5 | cowboy:start_clear - Listen for connections using plain TCP 6 | 7 | == Description 8 | 9 | [source,erlang] 10 | ---- 11 | start_clear(Name :: ranch:ref(), 12 | NumAcceptors :: non_neg_integer(), 13 | TransportOpts :: ranch_tcp:opts(), 14 | ProtocolOpts :: opts()) 15 | -> {ok, ListenerPid :: pid()} 16 | | {error, any()} 17 | ---- 18 | 19 | Start listening for connections over a clear TCP channel. 20 | 21 | Both HTTP/1.1 and HTTP/2 are supported on this listener. 22 | HTTP/2 has two methods of establishing a connection over 23 | a clear TCP channel. Both the upgrade and the prior knowledge 24 | methods are supported. 25 | 26 | == Arguments 27 | 28 | Name:: 29 | 30 | The listener name is used to refer to this listener in 31 | future calls, for example when stopping it or when 32 | updating the routes defined. 33 | + 34 | It can be any Erlang term. An atom is generally good enough, 35 | for example `api`, `my_app_clear` or `my_app_tls`. 36 | 37 | NumAcceptors:: 38 | 39 | The number of acceptors is the number of processes that 40 | will accept connections. Tweak this value to improve the 41 | accept rate for incoming connections. 42 | + 43 | The ideal value is between 10 and 100 on most systems. 44 | Larger values may have the opposite effect and reduce the 45 | accept rate. It's generally safe to start with a value of 46 | 100 (or 10 on low memory systems). Then, when accept rates 47 | become a concern, measure the performance and update the 48 | value accordingly. 49 | + 50 | This value is unrelated to the maximum number of concurrent 51 | connections. 52 | 53 | TransportOpts:: 54 | 55 | The transport options are where the TCP options, including 56 | the listener's port number, are defined. Transport options 57 | are provided as a list of keys and values, for example 58 | `[{port, 8080}]`. 59 | + 60 | The available options are documented in the 61 | link:man:ranch_tcp(3)[ranch_tcp(3)] manual. 62 | 63 | ProtocolOpts:: 64 | 65 | The protocol options are in a map containing all the options for 66 | the different protocols that may be involved when connecting 67 | to the listener, including HTTP/1.1 and HTTP/2 but also 68 | subprotocols like Websocket. 69 | // @todo For Websocket this might change in the future. 70 | + 71 | The HTTP/1.1 options are documented in the 72 | link:man:cowboy_http(3)[cowboy_http(3)] manual; 73 | the HTTP/2 options in 74 | link:man:cowboy_http2(3)[cowboy_http2(3)]; 75 | and the Websocket options in 76 | link:man:cowboy_websocket(3)[cowboy_websocket(3)]. 77 | 78 | == Return value 79 | 80 | An ok tuple is returned on success. It contains the pid of 81 | the top-level supervisor for the listener. 82 | 83 | An error tuple is returned on error. The error reason may 84 | be any Erlang term. 85 | 86 | A common error is `eaddrinuse`. It indicates that the port 87 | configured for Cowboy is already in use. 88 | 89 | == Changelog 90 | 91 | * *2.0*: HTTP/2 support added. 92 | * *2.0*: Function introduced. Replaces `cowboy:start_http/4`. 93 | 94 | == Examples 95 | 96 | .Start a listener 97 | [source,erlang] 98 | ---- 99 | Dispatch = cowboy_router:compile([ 100 | {'_', [ 101 | {"/", toppage_h, []} 102 | ]} 103 | ]), 104 | 105 | {ok, _} = cowboy:start_clear(example, 100, [{port, 8080}], #{ 106 | env => #{dispatch => Dispatch} 107 | }). 108 | ---- 109 | 110 | .Start a listener on a random port 111 | [source,erlang] 112 | ---- 113 | Name = example, 114 | 115 | {ok, _} = cowboy:start_clear(Name, 100, [], #{ 116 | env => #{dispatch => Dispatch} 117 | }), 118 | 119 | Port = ranch:get_port(Name). 120 | ---- 121 | 122 | == See also 123 | 124 | link:man:cowboy(3)[cowboy(3)], 125 | link:man:cowboy:start_tls(3)[cowboy:start_tls(3)], 126 | link:man:cowboy:stop_listener(3)[cowboy:stop_listener(3)], 127 | link:man:ranch(3)[ranch(3)] 128 | --------------------------------------------------------------------------------