diff options
82 files changed, 773 insertions, 1032 deletions
diff --git a/doc/src/guide/erlang_beginners.ezdoc b/doc/src/guide/erlang_beginners.ezdoc index f62543f..74d3470 100644 --- a/doc/src/guide/erlang_beginners.ezdoc +++ b/doc/src/guide/erlang_beginners.ezdoc @@ -33,8 +33,3 @@ Instead of going into every single details of the language, Joe focuses on the central concepts behind Erlang, and shows you how they can be used to write a variety of different applications. - -At the time of writing, the 2nd edition of the book is in beta, -and includes a few details about upcoming Erlang features that -cannot be used today. Choose the edition you want, then get -reading! diff --git a/doc/src/guide/getting_started.ezdoc b/doc/src/guide/getting_started.ezdoc index 34f02dc..a959b45 100644 --- a/doc/src/guide/getting_started.ezdoc +++ b/doc/src/guide/getting_started.ezdoc @@ -150,15 +150,15 @@ $ make new t=cowboy_http n=hello_handler ``` You can then open the `src/hello_handler.erl` file and modify -the `handle/2` function like this to send a reply. +the `init/2` function like this to send a reply. ``` erlang -handle(Req, State=#state{}) -> +init(Req, Opts) -> Req2 = cowboy_req:reply(200, [{<<"content-type">>, <<"text/plain">>}], <<"Hello Erlang!">>, Req), - {ok, Req2, State}. + {ok, Req2, Opts}. ``` What the above code does is send a `200 OK` reply, with the diff --git a/doc/src/guide/handlers.ezdoc b/doc/src/guide/handlers.ezdoc new file mode 100644 index 0000000..c0fb97e --- /dev/null +++ b/doc/src/guide/handlers.ezdoc @@ -0,0 +1,99 @@ +::: Handlers + +Handlers are Erlang modules that handle HTTP requests. + +:: Plain HTTP handlers + +The most basic handler in Cowboy implements the mandatory +`init/2` callback, manipulates the request, optionally +sends a response and then returns. + +This callback receives the ^"Req object^req and the options +defined during the ^"router configuration^routing^. + +A handler that does nothing would look like this: + +``` erlang +init(Req, Opts) -> + {ok, Req, Opts}. +``` + +Despite sending no reply, a `204 No Content` reply will be +sent to the client, as Cowboy makes sure that a reply is +sent for every request. + +We need to use the Req object for sending a reply. + +``` erlang +init(Req, Opts) -> + Req2 = cowboy_req:reply(200, [ + {<<"content-type">>, <<"text/plain">>} + ], <<"Hello World!">>, Req), + {ok, Req2, Opts}. +``` + +As you can see we return a 3-tuple. `ok` means that the +handler ran successfully. The Req object is returned as +it may have been modified as is the case here: replying +returns a modified Req object that you need to return +back to Cowboy for proper operations. + +The last value of the tuple is a state that will be used +in every subsequent callbacks to this handler. Plain HTTP +handlers only have one additional callback, the optional +`terminate/3`. + +:: Other handlers + +The `init/2` callback can also be used to inform Cowboy +that this is a different kind of handler and that Cowboy +should switch to it. To do this you simply need to return +the module name of the handler type you want to switch to. + +Cowboy comes with three handler types you can switch to: +^"cowboy_rest^rest_handlers^, ^"cowboy_websocket^ws_handlers^ +and ^"cowboy_loop^loop_handlers^. In addition to those you +can define your own handler types. + +Switching is simple. Instead of returning `ok`, you simply +return the name of the handler type you want to use. The +following snippet switches to a Websocket handler: + +``` erlang +init(Req, Opts) -> + {cowboy_websocket, Req, Opts}. +``` + +You can also switch to your own custom handler type: + +``` erlang +init(Req, Opts) -> + {my_handler_type, Req, Opts}. +``` + +How to implement a custom handler type is described in the +^"Sub protocols^sub_protocols chapter. + +:: Cleaning up + +All handlers coming with Cowboy allow the use of the optional +`terminate/3` callback. + +``` erlang +terminate(_Reason, Req, State) -> + ok. +``` + +This callback is strictly reserved for any required cleanup. +You cannot send a response from this function. There is no +other return value. + +If you used the process dictionary, timers, monitors or may +be receiving messages, then you can use this function to clean +them up, as Cowboy might reuse the process for the next +keep-alive request. + +Note that while this function may be called in a Websocket +handler, it is generally not useful to do any clean up as +the process terminates immediately after calling this callback +when using Websocket. diff --git a/doc/src/guide/http_handlers.ezdoc b/doc/src/guide/http_handlers.ezdoc deleted file mode 100644 index d846ffe..0000000 --- a/doc/src/guide/http_handlers.ezdoc +++ /dev/null @@ -1,132 +0,0 @@ -::: Handling plain HTTP requests - -The simplest way to handle a request is by writing a -plain HTTP handler. It is modeled after Erlang/OTP's -gen_server behaviour, although simplified, as Cowboy -will simply call the three callbacks sequentially. - -:: Initialization - -The first callback, `init/2`, is common to all handlers, -as it is used to identify the type of handler. Plain -HTTP handlers just return `http`. - -``` erlang -init(Req, Opts) -> - {http, Req, Opts}. -``` - -This function receives the options associated with -this route that you configured previously. - -You do not need to validate the options unless they -are user configured. If they are, and there's a -configuration error, you may choose to crash. For -example, this will crash if the required `lang` -option is not found. - -``` erlang -init(Req, Opts) -> - {_, Lang} = lists:keyfind(lang, 1, Opts), - {http, Req, Lang}. -``` - -If your users are unlikely to figure out the issue -without explanations, then you should send a more -meaningful error back to the user. Since we already -replied to the user, there's no need for us to -continue with the handler code, so we use the -`shutdown` return value to stop early. - -``` erlang -init(Req, Opts) -> - case lists:keyfind(lang, 1, Opts) of - false -> - Req2 = cowboy_req:reply(500, [ - {<<"content-type">>, <<"text/plain">>} - ], "Missing option 'lang'.", Req), - {shutdown, Req2, undefined}; - {_, Lang} -> - {http, Req, Lang} - end. -``` - -Once the options have been validated, we can use them -safely. So we need to pass them onward to the rest of -the handler. That's what the third element of the return -tuple, the state, is for. - -You may use a state record for this. The record will make -your handler code clearer and will allow Dialyzer to better -type check your code. - -``` erlang --record(state, { - lang :: en | fr - %% More fields here. -}). - -init(Req, Opts) -> - {_, Lang} = lists:keyfind(lang, 1, Opts), - {http, Req, #state{lang=Lang}}. -``` - -You may also use a map. A map is interesting in that you -do not need to define it beforehand, but is a little less -efficient and not as well supported by Dialyzer. - -``` erlang -init(Req, Opts) -> - {_, Lang} = lists:keyfind(lang, 1, Opts), - {http, Req, #{lang => Lang}. -``` - -:: Handling the request - -The second callback, `handle/2`, is specific to plain HTTP -handlers. It's where you handle the request. - -A handle function that does nothing would look like this: - -``` erlang -handle(Req, State) -> - {ok, Req, State}. -``` - -There's no other return value. To obtain information about -the request, or send a response, you would use the Req object -here. The Req object is documented in its own chapter. - -The following handle function will send a fairly original response. - -``` erlang -handle(Req, State) -> - Req2 = cowboy_req:reply(200, [ - {<<"content-type">>, <<"text/plain">>} - ], <<"Hello World!">>, Req), - {ok, Req2, State}. -``` - -:: Cleaning up - -The third and last callback, `terminate/3`, is optional. - -``` erlang -terminate(_Reason, Req, State) -> - ok. -``` - -This callback is strictly reserved for any required cleanup. -You cannot send a response from this function. There is no -other return value. - -If you used the process dictionary, timers, monitors or may -be receiving messages, then you can use this function to clean -them up, as Cowboy might reuse the process for the next -keep-alive request. - -The chances of any of this happening in your handler are pretty -thin however. The use of the process dictionary is discouraged -in Erlang code in general. And if you need to use timers, monitors -or to receive messages, you are better off with a loop handler, -a different kind of handler meant specifically for this use. diff --git a/doc/src/guide/index.ezdoc b/doc/src/guide/index.ezdoc index 300cea8..dbe75ff 100644 --- a/doc/src/guide/index.ezdoc +++ b/doc/src/guide/index.ezdoc @@ -1,35 +1,34 @@ ::: Cowboy User Guide The Cowboy User Guide explores the modern Web and how to make -best use of Cowboy for writing powerful web applications. +best use of Cowboy for writing powerful Web applications. -:: Introducing Cowboy +:: Rationale -* ^"Introduction^introduction * ^"The modern Web^modern_web * ^"Erlang and the Web^erlang_web -* ^"Erlang for beginners^erlang_beginners + +:: Introduction + +* ^"Introduction^introduction * ^"Getting started^getting_started +* ^"Request overview^overview +* ^"Erlang for beginners^erlang_beginners -:: HTTP +:: Configuration -* ^"The life of a request^http_req_life * ^"Routing^routing * ^"Constraints^constraints -* ^"Handling plain HTTP requests^http_handlers +* ^"Static files^static_files + +:: Request and response + +* ^"Handlers^handlers * ^"The Req object^req * ^"Reading the request body^req_body * ^"Sending a response^resp * ^"Using cookies^cookies - -:: Multipart - -* ^"Introduction to multipart^multipart_intro -* ^"Multipart requests^multipart_req - -:: Static files - -* ^"Static handler^static_handlers +* ^"Multipart^multipart :: REST @@ -43,14 +42,14 @@ best use of Cowboy for writing powerful web applications. * ^"The Websocket protocol^ws_protocol * ^"Handling Websocket connections^ws_handlers -:: Server push +:: Push technology * ^"Loop handlers^loop_handlers -:: Pluggable interface +:: Extensions * ^"Middlewares^middlewares -* ^"Protocol upgrades^upgrade_protocol +* ^"Sub protocols^sub_protocols * ^"Hooks^hooks :: Internals diff --git a/doc/src/guide/introduction.ezdoc b/doc/src/guide/introduction.ezdoc index 18345df..e1d2e60 100644 --- a/doc/src/guide/introduction.ezdoc +++ b/doc/src/guide/introduction.ezdoc @@ -16,9 +16,7 @@ features both a Function Reference and a User Guide. :: Prerequisites -No Erlang knowledge is required for reading this guide. The reader will -be introduced to Erlang concepts and redirected to reference material -whenever necessary. +Beginner Erlang knowledge is recommended for reading this guide. Knowledge of the HTTP protocol is recommended but not required, as it will be detailed throughout the guide. @@ -32,12 +30,15 @@ guarantee that the experience will be safe and smooth. You are advised to perform the necessary testing and security audits prior to deploying on other platforms. -Cowboy is developed for Erlang/OTP R16B01, R16B02, R16B03-1, 17.0 and -17.1.2. +Cowboy is developed for Erlang/OTP 17.0, 17.1.2 and 17.3. By the time +this branch gets released the target version will probably be 18.0 and +above. Cowboy may be compiled on other Erlang versions with small source code modifications but there is no guarantee that it will work as expected. +Cowboy uses the maps data type which was introduced in Erlang 17.0. + :: Versioning Cowboy uses ^"Semantic Versioning 2.0.0^http://semver.org/^. diff --git a/doc/src/guide/loop_handlers.ezdoc b/doc/src/guide/loop_handlers.ezdoc index 445854c..8da2805 100644 --- a/doc/src/guide/loop_handlers.ezdoc +++ b/doc/src/guide/loop_handlers.ezdoc @@ -8,7 +8,7 @@ a response. Loop handlers are used for requests where a response might not be immediately available, but where you would like to keep the connection open for a while in case the response arrives. The -most known example of such practice is known as long-polling. +most known example of such practice is known as long polling. Loop handlers can also be used for requests where a response is partially available and you need to stream the response body @@ -21,12 +21,12 @@ and allow using built-in features like hibernation and timeouts. Loop handlers essentially wait for one or more Erlang messages and feed these messages to the `info/3` callback. It also features -the `init/3` and `terminate/3` callbacks which work the same as +the `init/2` and `terminate/3` callbacks which work the same as for plain HTTP handlers. :: Initialization -The `init/3` function must return a `loop` tuple to enable +The `init/2` function must return a `cowboy_loop` tuple to enable loop handler behavior. This tuple may optionally contain a timeout value and/or the atom `hibernate` to make the process enter hibernation until a message is received. @@ -35,7 +35,7 @@ This snippet enables the loop handler. ``` erlang init(Req, Opts) -> - {long_polling, Req, Opts}. + {cowboy_loop, Req, Opts}. ``` However it is largely recommended that you set a timeout @@ -44,7 +44,7 @@ also makes the process hibernate. ``` erlang init(Req, Opts) -> - {long_polling, Req, Opts, 30000, hibernate}. + {cowboy_loop, Req, Opts, 30000, hibernate}. ``` :: Receive loop @@ -61,9 +61,9 @@ message otherwise. ``` erlang info({reply, Body}, Req, State) -> Req2 = cowboy_req:reply(200, [], Body, Req), - {ok, Req2, State}; + {shutdown, Req2, State}; info(_Msg, Req, State) -> - {loop, Req, State, hibernate}. + {ok, Req, State, hibernate}. ``` Do note that the `reply` tuple here may be any message @@ -76,17 +76,17 @@ return a tuple indicating if more messages are to be expected. The callback may also choose to do nothing at all and just skip the message received. -If a reply is sent, then the `ok` tuple should be returned. +If a reply is sent, then the `shutdown` tuple should be returned. This will instruct Cowboy to end the request. -Otherwise a `loop` tuple should be returned. +Otherwise an `ok` tuple should be returned. :: Streaming loop Another common case well suited for loop handlers is streaming data received in the form of Erlang messages. This can be done by initiating a chunked reply in the -`init/3` callback and then using `cowboy_req:chunk/2` +`init/2` callback and then using `cowboy_req:chunk/2` every time a message is received. The following snippet does exactly that. As you can see @@ -96,15 +96,15 @@ and the loop is stopped by sending an `eof` message. ``` erlang init(Req, Opts) -> Req2 = cowboy_req:chunked_reply(200, [], Req), - {long_polling, Req2, Opts}. + {cowboy_loop, Req2, Opts}. info(eof, Req, State) -> - {ok, Req, State}; + {shutdown, Req, State}; info({chunk, Chunk}, Req, State) -> cowboy_req:chunk(Chunk, Req), - {loop, Req, State}; + {ok, Req, State}; info(_Msg, Req, State) -> - {loop, Req, State}. + {ok, Req, State}. ``` :: Cleaning up diff --git a/doc/src/guide/multipart_req.ezdoc b/doc/src/guide/multipart.ezdoc index 21762f6..d0b2e40 100644 --- a/doc/src/guide/multipart_req.ezdoc +++ b/doc/src/guide/multipart.ezdoc @@ -1,11 +1,60 @@ ::: Multipart requests +Multipart originates from MIME, an Internet standard that +extends the format of emails. Multipart messages are a +container for parts of any content-type. + +For example, a multipart message may have a part +containing text and a second part containing an +image. This is what allows you to attach files +to emails. + +In the context of HTTP, multipart is most often used +with the `multipart/form-data` content-type. This is +the content-type you have to use when you want browsers +to be allowed to upload files through HTML forms. + +Multipart is of course not required for uploading +files, it is only required when you want to do so +through HTML forms. + You can read and parse multipart messages using the Req object directly. Cowboy defines two functions that allows you to get information about each part and read their contents. +:: Structure + +A multipart message is a list of parts. Parts may +contain either a multipart message or a non-multipart +content-type. This allows parts to be arranged in a +tree structure, although this is a rare case as far +as the Web is concerned. + +:: Form-data + +In the normal case, when a form is submitted, the +browser will use the `application/x-www-form-urlencoded` +content-type. This type is just a list of keys and +values and is therefore not fit for uploading files. + +That's where the `multipart/form-data` content-type +comes in. When the form is configured to use this +content-type, the browser will use one part of the +message for each form field. This means that a file +input field will be sent in its own part, but the +same applies to all other kinds of fields. + +A form with a text input, a file input and a select +choice box will result in a multipart message with +three parts, one for each field. + +The browser does its best to determine the content-type +of the files it sends this way, but you should not +rely on it for determining the contents of the file. +Proper investigation of the contents is recommended. + :: Checking the content-type While there is a variety of multipart messages, the diff --git a/doc/src/guide/multipart_intro.ezdoc b/doc/src/guide/multipart_intro.ezdoc deleted file mode 100644 index b9a7fa9..0000000 --- a/doc/src/guide/multipart_intro.ezdoc +++ /dev/null @@ -1,50 +0,0 @@ -::: Introduction to multipart - -Multipart originates from MIME, an Internet standard that -extends the format of emails. Multipart messages are a -container for parts of any content-type. - -For example, a multipart message may have a part -containing text and a second part containing an -image. This is what allows you to attach files -to emails. - -In the context of HTTP, multipart is most often used -with the `multipart/form-data` content-type. This is -the content-type you have to use when you want browsers -to be allowed to upload files through HTML forms. - -Multipart is of course not required for uploading -files, it is only required when you want to do so -through HTML forms. - -:: Structure - -A multipart message is a list of parts. Parts may -contain either a multipart message or a non-multipart -content-type. This allows parts to be arranged in a -tree structure, although this is a rare case as far -as the Web is concerned. - -:: Form-data - -In the normal case, when a form is submitted, the -browser will use the `application/x-www-form-urlencoded` -content-type. This type is just a list of keys and -values and is therefore not fit for uploading files. - -That's where the `multipart/form-data` content-type -comes in. When the form is configured to use this -content-type, the browser will use one part of the -message for each form field. This means that a file -input field will be sent in its own part, but the -same applies to all other kinds of fields. - -A form with a text input, a file input and a select -choice box will result in a multipart message with -three parts, one for each field. - -The browser does its best to determine the content-type -of the files it sends this way, but you should not -rely on it for determining the contents of the file. -Proper investigation of the contents is recommended. diff --git a/doc/src/guide/http_req_life.ezdoc b/doc/src/guide/overview.ezdoc index ffe5dfa..725ae4e 100644 --- a/doc/src/guide/http_req_life.ezdoc +++ b/doc/src/guide/overview.ezdoc @@ -1,4 +1,4 @@ -::: The life of a request +::: Request overview This chapter explains the different steps a request goes through until a response is sent, along with diff --git a/doc/src/guide/rest_handlers.ezdoc b/doc/src/guide/rest_handlers.ezdoc index 294392a..8cdd12e 100644 --- a/doc/src/guide/rest_handlers.ezdoc +++ b/doc/src/guide/rest_handlers.ezdoc @@ -1,20 +1,20 @@ ::: REST handlers -REST is implemented in Cowboy as a protocol upgrade. Once upgraded, -the request is handled as a state machine with many optional callbacks +REST is implemented in Cowboy as a sub protocol. The request +is handled as a state machine with many optional callbacks describing the resource and modifying the machine's behavior. -The REST handler is the recommended way to handle requests. +The REST handler is the recommended way to handle HTTP requests. :: Initialization First, the `init/2` callback is called. This callback is common to all handlers. To use REST for the current request, this function -must return a `rest` tuple. +must return a `cowboy_rest` tuple. ``` erlang init(Req, Opts) -> - {rest, Req, Opts}. + {cowboy_rest, Req, Opts}. ``` Cowboy will then switch to the REST protocol and start executing diff --git a/doc/src/guide/static_handlers.ezdoc b/doc/src/guide/static_files.ezdoc index f5eaac3..5a289d0 100644 --- a/doc/src/guide/static_handlers.ezdoc +++ b/doc/src/guide/static_files.ezdoc @@ -1,8 +1,9 @@ -::: Static handler +::: Static files -The static handler is a built-in REST handler for serving files. -It is available as a convenience and provides a quick solution -for serving files during development. +Cowboy comes with a special handler built as a REST handler +and designed specifically for serving static files. It is +provided as a convenience and provides a quick solution for +serving files during development. For systems in production, consider using one of the many Content Distribution Network (CDN) available on the market, diff --git a/doc/src/guide/sub_protocols.ezdoc b/doc/src/guide/sub_protocols.ezdoc new file mode 100644 index 0000000..d34f21e --- /dev/null +++ b/doc/src/guide/sub_protocols.ezdoc @@ -0,0 +1,64 @@ +::: Sub protocols + +Sub protocols are used for creating new types of handlers that +provide extra functionality in a reusable way. Cowboy uses this +mechanism to provide its loop, REST and Websocket handlers. + +This chapter will explain how to create your own sub protocols +and handler types. + +:: Usage + +To switch to a sub protocol, the `init/2` callback must return +the name of the sub protocol module. Everything past this point +is handled by the sub protocol. + +``` erlang +init(Req, Opts) -> + {cowboy_websocket, Req, Opts}. +``` + +The return value may also have a `Timeout` value and/or the +atom `hibernate`. These options are useful for long living +connections. When they are not provided, the timeout value +defaults to `infinity` and the hibernate value to `run`. + +The following snippet switches to the `my_protocol` sub +protocol, sets the timeout value to 5 seconds and enables +hibernation: + +``` erlang +init(Req, Opts) -> + {my_protocol, Req, Opts, 5000, hibernate}. +``` + +If a sub protocol does not make use of these options, it should +crash if it receives anything other than the default values. + +:: Upgrade + +After the `init/2` function returns, Cowboy will then call the +`upgrade/6` function. This is the only callback defined by the +`cowboy_sub_protocol` behavior. + +The function is named `upgrade` because it mimics the mechanism +of HTTP protocol upgrades. For some sub protocols, like Websocket, +an actual upgrade is performed. For others, like REST, this is +only an upgrade at Cowboy's level and the client has nothing to +do about it. + +The upgrade callback receives the Req object, the middleware +environment, the handler and its options, and the aforementioned +timeout and hibernate values. + +``` erlang +upgrade(Req, Env, Handler, HandlerOpts, Timeout, Hibernate) -> + %% Sub protocol code here. +``` + +This callback is expected to behave like a middleware and to +return an updated environment and Req object. + +Sub protocols are expected to call the `cowboy_handler:terminate/4` +function when they terminate. This function will make sure that +the optional `terminate/3` callback is called, if present. diff --git a/doc/src/guide/upgrade_protocol.ezdoc b/doc/src/guide/upgrade_protocol.ezdoc deleted file mode 100644 index ad2d25a..0000000 --- a/doc/src/guide/upgrade_protocol.ezdoc +++ /dev/null @@ -1,61 +0,0 @@ -::: Protocol upgrades - -Cowboy features many different handlers, each for different purposes. -All handlers have a common entry point: the `init/2` function. -This function returns the name of the protocol that will be -used for processing the request, along with various options. - -Cowboy defines four built-in handler types. Three of them are -implemented as sub protocols. More can be implemented by -writing a custom sub protocol. - -The following table lists the built-in handler types. - -|| Alias Module Description -| -| http - Plain HTTP handler -| long_polling cowboy_long_polling Long-polling handler -| rest cowboy_rest REST handler -| ws cowboy_websocket Websocket handler - -Both the alias or the module name can be used to specify the -kind of handler. In addition, a user-defined module name can -be used. - -``` erlang -init(Req, Opts) -> - {my_protocol, Req, Opts}. -``` - -The `init/2` function can also return some extra options for -handlers that are meant to be long running, for example the -`long_polling` and `ws` handler types. These options can also -be passed on to custom sub protocols. For example the following -`init/2` function defines both a timeout value and enables -process hibernation: - -``` erlang -init(Req, Opts) -> - {my_protocol, Req, Opts, 5000, hibernate}. -``` - -It is up to the sub protocol to implement these (or reject -them if they are not supported). - -The `cowboy_sub_protocol` behavior only requires one callback, -`upgrade/6`. It receives the Req object, the middleware environment, -the handler and options for this request, and the timeout and -hibernate values. The default timeout value is `infinity` and -the default hibernate value is `run`. - -``` erlang -upgrade(Req, Env, Handler, HandlerOpts, Timeout, Hibernate) -> - %% ... -``` - -This callback is expected to behave like a middleware. Please -see the corresponding chapter for more information. - -Sub protocols are expected to call the `cowboy_handler:terminate/5` -function when they terminate. This function will make sure that -the optional `terminate/3` callback will be called, if present. diff --git a/doc/src/guide/ws_handlers.ezdoc b/doc/src/guide/ws_handlers.ezdoc index 893624b..cb30511 100644 --- a/doc/src/guide/ws_handlers.ezdoc +++ b/doc/src/guide/ws_handlers.ezdoc @@ -17,7 +17,7 @@ must return a `ws` tuple. ``` erlang init(Req, Opts) -> - {ws, Req, Opts}. + {cowboy_websocket, Req, Opts}. ``` Upon receiving this tuple, Cowboy will switch to the code @@ -61,7 +61,7 @@ or enabling timers. init(Req, _Opts) -> self() ! post_init, %% Register process here... - {ws, Req, #state{}}. + {cowboy_websocket, Req, #state{}}. websocket_info(post_init, Req, State) -> %% Perform post_init initialization here... @@ -161,7 +161,7 @@ A good timeout value is 60 seconds. ``` erlang init(Req, _Opts) -> - {ws, Req, #state{}, 60000}. + {cowboy_websocket, Req, #state{}, 60000}. ``` This value cannot be changed once it is set. It defaults to diff --git a/doc/src/guide/ws_protocol.ezdoc b/doc/src/guide/ws_protocol.ezdoc index 15aea2c..d283ae3 100644 --- a/doc/src/guide/ws_protocol.ezdoc +++ b/doc/src/guide/ws_protocol.ezdoc @@ -25,7 +25,7 @@ excluding the initial flawed draft sometimes known as :: Implementation Cowboy implements Websocket as a protocol upgrade. Once the -upgrade is performed from the `init/3` callback, Cowboy +upgrade is performed from the `init/2` callback, Cowboy switches to Websocket. Please consult the next chapter for more information on initiating and handling Websocket connections. diff --git a/doc/src/manual/cowboy_handler.ezdoc b/doc/src/manual/cowboy_handler.ezdoc index 0495f28..b440b60 100644 --- a/doc/src/manual/cowboy_handler.ezdoc +++ b/doc/src/manual/cowboy_handler.ezdoc @@ -15,10 +15,96 @@ Environment output: * result = ok +This module also defines the `cowboy_handler` behaviour that +defines the basic interface for handlers. All Cowboy handlers +implement at least the `init/2` callback, and may implement +the `terminate/3` callback optionally. + :: Types None. +:: Terminate reasons + +The following values may be received as the terminate reason +in the optional `terminate/3` callback. Different handler types +may define additional terminate reasons. + +: normal + +The connection was closed normally. + +: {crash, Class, Reason} + +A crash occurred in the handler. `Class` and `Reason` can be +used to obtain more information about the crash. The function +`erlang:get_stacktrace/0` can also be called to obtain the +stacktrace of the process when the crash occurred. + +:: Callbacks + +: init(Req, Opts) + -> {ok, Req, State} + | {Module, Req, State} + | {Module, Req, State, hibernate} + | {Module, Req, State, Timeout} + | {Module, Req, State, Timeout, hibernate} + +Types: + +* Req = cowboy_req:req() +* Opts = any() +* State = any() +* Module = module() +* Timeout = timeout() + +Process the request. + +This function can be used to switch to an alternate handler +type by returning the name of the module to be used, along +with a few options. + +For basic handlers this is the function where the response +should be sent. If no response is sent, Cowboy will ensure +that a `204 No Content` response is sent. + +A crash in this callback will result in `terminate/3` being +called if it is defined, with the `State` argument set to +the value of `Opts` originally given to the `init/2` callback. + +: terminate(Reason, Req, State) -> ok + +Types: + +* Reason = any() +* Req = cowboy_req:req() +* State = any() + +Perform any necessary cleanup of the state. + +This callback should release any resource currently in use, +clear any active timer and reset the process to its original +state, as it might be reused for future requests sent on the +same connection. Typical plain HTTP handlers rarely need to +use it. + +A crash in this callback or an invalid return value will +result in the closing of the connection and the termination +of the process. + :: Exports -None. +: terminate(Reason, Req, State, Handler) -> ok + +Types: + +* Reason = any() +* Req = cowboy_req:req() +* State = any() +* Handler = module() + +Call the optional `terminate/3` callback if it exists. + +This function should always be called at the end of the execution +of a handler, to give it a chance to clean up or perform +miscellaneous operations. diff --git a/doc/src/manual/cowboy_http_handler.ezdoc b/doc/src/manual/cowboy_http_handler.ezdoc deleted file mode 100644 index 6776598..0000000 --- a/doc/src/manual/cowboy_http_handler.ezdoc +++ /dev/null @@ -1,57 +0,0 @@ -::: cowboy_http_handler - -The `cowboy_http_handler` behaviour defines the interface used -by plain HTTP handlers. - -Unless noted otherwise, the callbacks will be executed sequentially. - -:: Types - -None. - -:: Callbacks - -: init({TransportName, ProtocolName}, Req, Opts) - -> {ok, Req, State} | {shutdown, Req, State} - -Types: - -* TransportName = tcp | ssl | atom() -* ProtocolName = http | atom() -* Req = cowboy_req:req() -* Opts = any() -* State = any() - -Initialize the state for this request. - -The `shutdown` return value can be used to skip the `handle/2` -call entirely. - -: handle(Req, State) -> {ok, Req, State} - -Types: - -* Req = cowboy_req:req() -* State = any() - -Handle the request. - -This callback is where the request is handled and a response -should be sent. If a response is not sent, Cowboy will send -a `204 No Content` response automatically. - -: terminate(Reason, Req, State) -> ok - -Types: - -* Reason = {normal, shutdown} | {error, atom()} -* Req = cowboy_req:req() -* State = any() - -Perform any necessary cleanup of the state. - -This callback should release any resource currently in use, -clear any active timer and reset the process to its original -state, as it might be reused for future requests sent on the -same connection. Typical plain HTTP handlers rarely need to -use it. diff --git a/doc/src/manual/cowboy_loop.ezdoc b/doc/src/manual/cowboy_loop.ezdoc new file mode 100644 index 0000000..1f3ab9e --- /dev/null +++ b/doc/src/manual/cowboy_loop.ezdoc @@ -0,0 +1,100 @@ +::: cowboy_loop + +The `cowboy_loop` module implements a handler interface for +long running HTTP connections. It is the recommended interface +for long polling and server-sent events, amongst others. + +This module is a sub protocol that defines three callbacks to +be implemented by handlers. The `init/2` and `terminate/3` +callbacks are common to all handler types and are documented +in the manual for the ^cowboy_handler module. + +The `info/3` callback is specific to loop handlers and will be +called as many times as necessary until a reply is sent. + +It is highly recommended to return a timeout value from the +`init/2` callback to ensure that the process is terminated +when no data has been received during that timespan. The +default timeout is `infinity`, which should only be used if +you have alternate means of ending inactive connections. + +:: Types + +None. + +:: Terminate reasons + +The following values may be received as the terminate reason +in the optional `terminate/3` callback. + +: normal + +The connection was closed normally before switching to the +loop sub protocol. This typically happens if an `ok` tuple is +returned from the `init/2` callback. + +: shutdown + +The handler requested to close the connection by returning +a `shutdown` tuple. + +: timeout + +The connection has been closed due to inactivity. The timeout +value can be configured from `init/2`. The response sent when +this happens is a `204 No Content`. + +: {crash, Class, Reason} + +A crash occurred in the handler. `Class` and `Reason` can be +used to obtain more information about the crash. The function +`erlang:get_stacktrace/0` can also be called to obtain the +stacktrace of the process when the crash occurred. + +: {error, overflow} + +The connection is being closed and the process terminated +because the buffer Cowboy uses to keep data sent by the +client has reached its maximum. The buffer size can be +configured through the environment value `loop_max_buffer` +and defaults to 5000 bytes. + +If the long running request comes with a body it is recommended +to process this body before switching to the loop sub protocol. + +: {error, closed} + +The socket has been closed brutally without a close frame being +received first. + +: {error, Reason} + +A socket error ocurred. + +:: Callbacks + +: info(Info, Req, State) + -> {ok, Req, State} + | {ok, Req, State, hibernate} + | {shutdown, Req, State} + +Types: + +* Info = any() +* Req = cowboy_req:req() +* State = any() + +Handle the Erlang message received. + +This function will be called every time an Erlang message +has been received. The message can be any Erlang term. + +The `shutdown` return value can be used to stop the receive loop, +typically because a response has been sent. + +The `hibernate` option will hibernate the process until +it receives another message. + +:: Exports + +None. diff --git a/doc/src/manual/cowboy_loop_handler.ezdoc b/doc/src/manual/cowboy_loop_handler.ezdoc deleted file mode 100644 index 0811a9a..0000000 --- a/doc/src/manual/cowboy_loop_handler.ezdoc +++ /dev/null @@ -1,91 +0,0 @@ -::: cowboy_loop_handler - -The `cowboy_loop_handler` behaviour defines the interface used -by HTTP handlers that do not send a response directly, instead -requiring a receive loop to process Erlang messages. - -This interface is best fit for long-polling types of requests. - -The `init/3` callback will always be called, followed by zero -or more calls to `info/3`. The `terminate/3` callback will -always be called last. - -:: Types - -None. - -:: Callbacks - -: init({TransportName, ProtocolName}, Req, Opts) - -> {loop, Req, State} - | {loop, Req, State, hibernate} - | {loop, Req, State, Timeout} - | {loop, Req, State, Timeout, hibernate} - | {shutdown, Req, State} - -Types: - -* TransportName = tcp | ssl | atom() -* ProtocolName = http | atom() -* Req = cowboy_req:req() -* Opts = any() -* State = any() -* Timeout = timeout() - -Initialize the state for this request. - -This callback will typically be used to register this process -to an event manager or a message queue in order to receive -the messages the handler wants to process. - -The receive loop will run for a duration of up to `Timeout` -milliseconds after it last received data from the socket, -at which point it will stop and send a `204 No Content` reply. -By default this value is set to `infinity`. It is recommended -to either set this value or ensure by any other mechanism -that the handler will be closed after a certain period of -inactivity. - -The `hibernate` option will hibernate the process until it -starts receiving messages. - -The `shutdown` return value can be used to skip the receive -loop entirely. - -: info(Info, Req, State) -> {ok, Req, State} | {loop, Req, State} - | {loop, Req, State, hibernate} - -Types: - -* Info = any() -* Req = cowboy_req:req() -* State = any() - -Handle the Erlang message received. - -This function will be called every time an Erlang message -has been received. The message can be any Erlang term. - -The `ok` return value can be used to stop the receive loop, -typically because a response has been sent. - -The `hibernate` option will hibernate the process until -it receives another message. - -: terminate(Reason, Req, State) -> ok - -Types: - -* Reason = {normal, shutdown} | {normal, timeout} | {error, closed} | {error, overflow} | {error, atom()} -* Req = cowboy_req:req() -* State = any() - -Perform any necessary cleanup of the state. - -This callback will typically unregister from any event manager -or message queue it registered to in `init/3`. - -This callback should release any resource currently in use, -clear any active timer and reset the process to its original -state, as it might be reused for future requests sent on the -same connection. diff --git a/doc/src/manual/cowboy_rest.ezdoc b/doc/src/manual/cowboy_rest.ezdoc index 9fe5c77..f128a22 100644 --- a/doc/src/manual/cowboy_rest.ezdoc +++ b/doc/src/manual/cowboy_rest.ezdoc @@ -3,12 +3,13 @@ The `cowboy_rest` module implements REST semantics on top of the HTTP protocol. -This module cannot be described as a behaviour due to most of -the callbacks it defines being optional. It has the same -semantics as a behaviour otherwise. +This module is a sub protocol that defines many callbacks +be implemented by handlers. The `init/2` and `terminate/3` +callbacks are common to all handler types and are documented +in the manual for the ^cowboy_handler module. -The only mandatory callback is `init/3`, needed to perform -the protocol upgrade. +All other callbacks are optional, though some may become +required depending on the return value of previous callbacks. :: Types @@ -43,46 +44,23 @@ The media-type is the content-type, excluding the charset. This value is always defined after the call to `content_types_provided/2`. -:: Callbacks - -: init({TransportName, ProtocolName}, Req, Opts) - -> {upgrade, protocol, cowboy_rest} - | {upgrade, protocol, cowboy_rest, Req, Opts} - -Types: - -* TransportName = tcp | ssl | atom() -* ProtocolName = http | atom() -* Req = cowboy_req:req() -* Opts = any() - -Upgrade the protocol to `cowboy_rest`. - -This is the only mandatory callback. - -: rest_init(Req, Opts) -> {ok, Req, State} +:: Terminate reasons -Types: +The following values may be received as the terminate reason +in the optional `terminate/3` callback. -* Req = cowboy_req:req() -* Opts = any() -* State = any() +: normal -Initialize the state for this request. +The connection was closed normally. -: rest_terminate(Req, State) -> ok +: {crash, Class, Reason} -Types: +A crash occurred in the handler. `Class` and `Reason` can be +used to obtain more information about the crash. The function +`erlang:get_stacktrace/0` can also be called to obtain the +stacktrace of the process when the crash occurred. -* Req = cowboy_req:req() -* State = any() - -Perform any necessary cleanup of the state. - -This callback should release any resource currently in use, -clear any active timer and reset the process to its original -state, as it might be reused for future requests sent on the -same connection. +:: Callbacks : Callback(Req, State) -> {Value, Req, State} | {halt, Req, State} diff --git a/doc/src/manual/cowboy_websocket.ezdoc b/doc/src/manual/cowboy_websocket.ezdoc index 59a6248..889ddd7 100644 --- a/doc/src/manual/cowboy_websocket.ezdoc +++ b/doc/src/manual/cowboy_websocket.ezdoc @@ -2,8 +2,25 @@ The `cowboy_websocket` module implements the Websocket protocol. -The callbacks for websocket handlers are defined in the manual -for the `cowboy_websocket_handler` behaviour. +This module is a sub protocol that defines four callbacks to +be implemented by handlers. The `init/2` and `terminate/3` +callbacks are common to all handler types and are documented +in the manual for the ^cowboy_handler module. + +The `websocket_handle/3` and `websocket_info/3` callbacks are +specific to Websocket handlers and will be called as many times +as necessary until the Websocket connection is closed. + +The `init/2` callback can be used to negotiate Websocket protocol +extensions with the client. It is highly recommended to return a +timeout value from this callback to ensure that the process is +terminated when no data has been received during that timespan. +The default timeout is `infinity`, which should only be used if +you have alternate means of ending inactive connections. + +Cowboy will terminate the process right after closing the +Websocket connection. This means that there is no real need to +perform any cleanup in the optional `terminate/3` callback. :: Types @@ -31,6 +48,118 @@ Type: 7 | 8 | 13 The version of the Websocket protocol being used. +:: Terminate reasons + +The following values may be received as the terminate reason +in the optional `terminate/3` callback. + +: normal + +The connection was closed normally before establishing a Websocket +connection. This typically happens if an `ok` tuple is returned +from the `init/2` callback. + +: remote + +The remote endpoint closed the connection without giving any +further details. + +: {remote, Code, Payload} + +The remote endpoint closed the connection with the given +`Code` and `Payload` as the reason. + +: shutdown + +The handler requested to close the connection, either by returning +a `shutdown` tuple or by sending a `close` frame. + +: timeout + +The connection has been closed due to inactivity. The timeout +value can be configured from `init/2`. + +: {crash, Class, Reason} + +A crash occurred in the handler. `Class` and `Reason` can be +used to obtain more information about the crash. The function +`erlang:get_stacktrace/0` can also be called to obtain the +stacktrace of the process when the crash occurred. + +: {error, badencoding} + +A text frame was sent by the client with invalid encoding. All +text frames must be valid UTF-8. + +: {error, badframe} + +A protocol error has been detected. + +: {error, closed} + +The socket has been closed brutally without a close frame being +received first. + +: {error, Reason} + +A socket error ocurred. + +:: Callbacks + +: websocket_handle(InFrame, Req, State) + -> {ok, Req, State} + | {ok, Req, State, hibernate} + | {reply, OutFrame | [OutFrame], Req, State} + | {reply, OutFrame | [OutFrame], Req, State, hibernate} + | {shutdown, Req, State} + +Types: + +* InFrame = {text | binary | ping | pong, binary()} +* Req = cowboy_req:req() +* State = any() +* OutFrame = frame() + +Handle the data received from the Websocket connection. + +This function will be called every time data is received +from the Websocket connection. + +The `shutdown` return value can be used to close the +connection. A close reply will also result in the connection +being closed. + +The `hibernate` option will hibernate the process until +it receives new data from the Websocket connection or an +Erlang message. + +: websocket_info(Info, Req, State) + -> {ok, Req, State} + | {ok, Req, State, hibernate} + | {reply, OutFrame | [OutFrame], Req, State} + | {reply, OutFrame | [OutFrame], Req, State, hibernate} + | {shutdown, Req, State} + +Types: + +* Info = any() +* Req = cowboy_req:req() +* State = any() +* OutFrame = frame() + +Handle the Erlang message received. + +This function will be called every time an Erlang message +has been received. The message can be any Erlang term. + +The `shutdown` return value can be used to close the +connection. A close reply will also result in the connection +being closed. + +The `hibernate` option will hibernate the process until +it receives another message or new data from the Websocket +connection. + :: Exports None. diff --git a/doc/src/manual/cowboy_websocket_handler.ezdoc b/doc/src/manual/cowboy_websocket_handler.ezdoc deleted file mode 100644 index 0d31a54..0000000 --- a/doc/src/manual/cowboy_websocket_handler.ezdoc +++ /dev/null @@ -1,133 +0,0 @@ -::: cowboy_websocket_handler - -The `cowboy_websocket_handler` behaviour defines the interface used -by Websocket handlers. - -The `init/3` and `websocket_init/3` callbacks will always be called, -followed by zero or more calls to `websocket_handle/3` and -`websocket_info/3`. The `websocket_terminate/3` will always -be called last. - -:: Types - -None. - -:: Callbacks - -: init({TransportName, ProtocolName}, Req, Opts) - -> {upgrade, protocol, cowboy_websocket} - | {upgrade, protocol, cowboy_websocket, Req, Opts} - -Types: - -* TransportName = tcp | ssl | atom() -* ProtocolName = http | atom() -* Req = cowboy_req:req() -* Opts = any() - -Upgrade the protocol to `cowboy_websocket`. - -: websocket_init(TransportName, Req, Opts) - -> {ok, Req, State} - | {ok, Req, State, hibernate} - | {ok, Req, State, Timeout} - | {ok, Req, State, Timeout, hibernate} - | {shutdown, Req} - -Types: - -* TransportName = tcp | ssl | atom() -* Req = cowboy_req:req() -* Opts = any() -* State = any() -* Timeout = timeout() - -Initialize the state for this session. - -This function is called before the upgrade to Websocket occurs. -It can be used to negotiate Websocket protocol extensions -with the client. It will typically be used to register this process -to an event manager or a message queue in order to receive -the messages the handler wants to process. - -The connection will stay up for a duration of up to `Timeout` -milliseconds after it last received data from the socket, -at which point it will stop and close the connection. -By default this value is set to `infinity`. It is recommended -to either set this value or ensure by any other mechanism -that the handler will be closed after a certain period of -inactivity. - -The `hibernate` option will hibernate the process until it -starts receiving either data from the Websocket connection -or Erlang messages. - -The `shutdown` return value can be used to close the connection -before upgrading to Websocket. - -: websocket_handle(InFrame, Req, State) - -> {ok, Req, State} - | {ok, Req, State, hibernate} - | {reply, OutFrame | [OutFrame], Req, State} - | {reply, OutFrame | [OutFrame], Req, State, hibernate} - | {shutdown, Req, State} - -Types: - -* InFrame = {text | binary | ping | pong, binary()} -* Req = cowboy_req:req() -* State = any() -* OutFrame = cowboy_websocket:frame() - -Handle the data received from the Websocket connection. - -This function will be called every time data is received -from the Websocket connection. - -The `shutdown` return value can be used to close the -connection. A close reply will also result in the connection -being closed. - -The `hibernate` option will hibernate the process until -it receives new data from the Websocket connection or an -Erlang message. - -: websocket_info(Info, Req, State) - -> {ok, Req, State} - | {ok, Req, State, hibernate} - | {reply, OutFrame | [OutFrame], Req, State} - | {reply, OutFrame | [OutFrame], Req, State, hibernate} - | {shutdown, Req, State} - -Types: - -* Info = any() -* Req = cowboy_req:req() -* State = any() -* OutFrame = cowboy_websocket:frame() - -Handle the Erlang message received. - -This function will be called every time an Erlang message -has been received. The message can be any Erlang term. - -The `shutdown` return value can be used to close the -connection. A close reply will also result in the connection -being closed. - -The `hibernate` option will hibernate the process until -it receives another message or new data from the Websocket -connection. - -: websocket_terminate(Reason, Req, State) -> ok - -Types: - -* Reason = {normal, shutdown | timeout} | {remote, closed} | {remote, cowboy_websocket:close_code(), binary()} | {error, badencoding | badframe | closed | atom()} -* Req = cowboy_req:req() -* State = any() - -Perform any necessary cleanup of the state. - -The connection will be closed and the process stopped right -after this call. diff --git a/doc/src/manual/index.ezdoc b/doc/src/manual/index.ezdoc index e364e90..133a341 100644 --- a/doc/src/manual/index.ezdoc +++ b/doc/src/manual/index.ezdoc @@ -5,8 +5,7 @@ The function reference documents the public interface of Cowboy. * ^"The Cowboy Application^cowboy_app * ^cowboy * ^cowboy_handler -* ^cowboy_http_handler -* ^cowboy_loop_handler +* ^cowboy_loop * ^cowboy_middleware * ^cowboy_protocol * ^cowboy_req @@ -16,5 +15,4 @@ The function reference documents the public interface of Cowboy. * ^cowboy_static * ^cowboy_sub_protocol * ^cowboy_websocket -* ^cowboy_websocket_handler * ^"HTTP status codes^http_status_codes diff --git a/examples/chunked_hello_world/src/toppage_handler.erl b/examples/chunked_hello_world/src/toppage_handler.erl index 6b47156..cb1d130 100644 --- a/examples/chunked_hello_world/src/toppage_handler.erl +++ b/examples/chunked_hello_world/src/toppage_handler.erl @@ -4,16 +4,12 @@ -module(toppage_handler). -export([init/2]). --export([handle/2]). init(Req, Opts) -> - {http, Req, Opts}. - -handle(Req, State) -> Req2 = cowboy_req:chunked_reply(200, Req), cowboy_req:chunk("Hello\r\n", Req2), timer:sleep(1000), cowboy_req:chunk("World\r\n", Req2), timer:sleep(1000), cowboy_req:chunk("Chunked!\r\n", Req2), - {ok, Req2, State}. + {ok, Req2, Opts}. diff --git a/examples/compress_response/src/toppage_handler.erl b/examples/compress_response/src/toppage_handler.erl index 09c8689..4631f71 100644 --- a/examples/compress_response/src/toppage_handler.erl +++ b/examples/compress_response/src/toppage_handler.erl @@ -4,12 +4,8 @@ -module(toppage_handler). -export([init/2]). --export([handle/2]). init(Req, Opts) -> - {http, Req, Opts}. - -handle(Req, State) -> BigBody = <<"A cowboy is an animal herder who tends cattle on ranches in North America, traditionally on horseback, and often performs a multitude of other ranch- @@ -24,4 +20,4 @@ considerable respect for their achievements. There are also cattle handlers in many other parts of the world, particularly South America and Australia, who perform work similar to the cowboy in their respective nations.\n">>, Req2 = cowboy_req:reply(200, [], BigBody, Req), - {ok, Req2, State}. + {ok, Req2, Opts}. diff --git a/examples/cookie/src/toppage_handler.erl b/examples/cookie/src/toppage_handler.erl index d1a1126..52e155b 100644 --- a/examples/cookie/src/toppage_handler.erl +++ b/examples/cookie/src/toppage_handler.erl @@ -4,12 +4,8 @@ -module(toppage_handler). -export([init/2]). --export([handle/2]). init(Req, Opts) -> - {http, Req, Opts}. - -handle(Req, State) -> NewValue = integer_to_list(random:uniform(1000000)), Req2 = cowboy_req:set_resp_cookie( <<"server">>, NewValue, [{path, <<"/">>}], Req), @@ -22,4 +18,4 @@ handle(Req, State) -> Req3 = cowboy_req:reply(200, [{<<"content-type">>, <<"text/html">>}], Body, Req2), - {ok, Req3, State}. + {ok, Req3, Opts}. diff --git a/examples/echo_get/src/toppage_handler.erl b/examples/echo_get/src/toppage_handler.erl index be657c6..a7c8d7f 100644 --- a/examples/echo_get/src/toppage_handler.erl +++ b/examples/echo_get/src/toppage_handler.erl @@ -4,16 +4,12 @@ -module(toppage_handler). -export([init/2]). --export([handle/2]). init(Req, Opts) -> - {http, Req, Opts}. - -handle(Req, State) -> Method = cowboy_req:method(Req), #{echo := Echo} = cowboy_req:match_qs(Req, [echo]), Req2 = echo(Method, Echo, Req), - {ok, Req2, State}. + {ok, Req2, Opts}. echo(<<"GET">>, undefined, Req) -> cowboy_req:reply(400, [], <<"Missing echo parameter.">>, Req); diff --git a/examples/echo_post/src/toppage_handler.erl b/examples/echo_post/src/toppage_handler.erl index 2bafed0..de75e5e 100644 --- a/examples/echo_post/src/toppage_handler.erl +++ b/examples/echo_post/src/toppage_handler.erl @@ -4,16 +4,12 @@ -module(toppage_handler). -export([init/2]). --export([handle/2]). init(Req, Opts) -> - {http, Req, Opts}. - -handle(Req, State) -> Method = cowboy_req:method(Req), HasBody = cowboy_req:has_body(Req), Req2 = maybe_echo(Method, HasBody, Req), - {ok, Req2, State}. + {ok, Req2, Opts}. maybe_echo(<<"POST">>, true, Req) -> {ok, PostVals, Req2} = cowboy_req:body_qs(Req), diff --git a/examples/eventsource/src/eventsource_handler.erl b/examples/eventsource/src/eventsource_handler.erl index 3aa60e7..c896638 100644 --- a/examples/eventsource/src/eventsource_handler.erl +++ b/examples/eventsource/src/eventsource_handler.erl @@ -10,12 +10,12 @@ init(Req, Opts) -> Headers = [{<<"content-type">>, <<"text/event-stream">>}], Req2 = cowboy_req:chunked_reply(200, Headers, Req), erlang:send_after(1000, self(), {message, "Tick"}), - {long_polling, Req2, Opts, 5000}. + {cowboy_loop, Req2, Opts, 5000}. info({message, Msg}, Req, State) -> cowboy_req:chunk(["id: ", id(), "\ndata: ", Msg, "\n\n"], Req), erlang:send_after(1000, self(), {message, "Tick"}), - {loop, Req, State}. + {ok, Req, State}. id() -> {Mega, Sec, Micro} = erlang:now(), diff --git a/examples/hello_world/src/toppage_handler.erl b/examples/hello_world/src/toppage_handler.erl index 18a6343..e8e672e 100644 --- a/examples/hello_world/src/toppage_handler.erl +++ b/examples/hello_world/src/toppage_handler.erl @@ -4,13 +4,9 @@ -module(toppage_handler). -export([init/2]). --export([handle/2]). init(Req, Opts) -> - {http, Req, Opts}. - -handle(Req, State) -> Req2 = cowboy_req:reply(200, [ {<<"content-type">>, <<"text/plain">>} ], <<"Hello world!">>, Req), - {ok, Req2, State}. + {ok, Req2, Opts}. diff --git a/examples/rest_basic_auth/src/toppage_handler.erl b/examples/rest_basic_auth/src/toppage_handler.erl index 59c5888..18a8cae 100644 --- a/examples/rest_basic_auth/src/toppage_handler.erl +++ b/examples/rest_basic_auth/src/toppage_handler.erl @@ -9,7 +9,7 @@ -export([to_text/2]). init(Req, Opts) -> - {rest, Req, Opts}. + {cowboy_rest, Req, Opts}. is_authorized(Req, State) -> case cowboy_req:parse_header(<<"authorization">>, Req) of diff --git a/examples/rest_hello_world/src/toppage_handler.erl b/examples/rest_hello_world/src/toppage_handler.erl index 5b0dfc8..7657281 100644 --- a/examples/rest_hello_world/src/toppage_handler.erl +++ b/examples/rest_hello_world/src/toppage_handler.erl @@ -10,7 +10,7 @@ -export([hello_to_text/2]). init(Req, Opts) -> - {rest, Req, Opts}. + {cowboy_rest, Req, Opts}. content_types_provided(Req, State) -> {[ diff --git a/examples/rest_pastebin/src/toppage_handler.erl b/examples/rest_pastebin/src/toppage_handler.erl index 89fd786..80974fe 100644 --- a/examples/rest_pastebin/src/toppage_handler.erl +++ b/examples/rest_pastebin/src/toppage_handler.erl @@ -17,7 +17,7 @@ init(Req, Opts) -> random:seed(now()), - {rest, Req, Opts}. + {cowboy_rest, Req, Opts}. allowed_methods(Req, State) -> {[<<"GET">>, <<"POST">>], Req, State}. diff --git a/examples/rest_stream_response/src/toppage_handler.erl b/examples/rest_stream_response/src/toppage_handler.erl index 6c66d21..61f5ab9 100644 --- a/examples/rest_stream_response/src/toppage_handler.erl +++ b/examples/rest_stream_response/src/toppage_handler.erl @@ -8,7 +8,7 @@ -export([streaming_csv/2]). init(Req, Table) -> - {rest, Req, Table}. + {cowboy_rest, Req, Table}. content_types_provided(Req, State) -> {[ diff --git a/examples/ssl_hello_world/src/toppage_handler.erl b/examples/ssl_hello_world/src/toppage_handler.erl index 18a6343..e8e672e 100644 --- a/examples/ssl_hello_world/src/toppage_handler.erl +++ b/examples/ssl_hello_world/src/toppage_handler.erl @@ -4,13 +4,9 @@ -module(toppage_handler). -export([init/2]). --export([handle/2]). init(Req, Opts) -> - {http, Req, Opts}. - -handle(Req, State) -> Req2 = cowboy_req:reply(200, [ {<<"content-type">>, <<"text/plain">>} ], <<"Hello world!">>, Req), - {ok, Req2, State}. + {ok, Req2, Opts}. diff --git a/examples/upload/src/upload_handler.erl b/examples/upload/src/upload_handler.erl index 63cda96..dec634d 100644 --- a/examples/upload/src/upload_handler.erl +++ b/examples/upload/src/upload_handler.erl @@ -4,16 +4,12 @@ -module(upload_handler). -export([init/2]). --export([handle/2]). init(Req, Opts) -> - {http, Req, Opts}. - -handle(Req, State) -> {ok, Headers, Req2} = cowboy_req:part(Req), {ok, Data, Req3} = cowboy_req:part_body(Req2), {file, <<"inputfile">>, Filename, ContentType, _TE} = cow_multipart:form_data(Headers), io:format("Received file ~p of content-type ~p as follow:~n~p~n~n", [Filename, ContentType, Data]), - {ok, Req3, State}. + {ok, Req3, Opts}. diff --git a/examples/web_server/src/directory_handler.erl b/examples/web_server/src/directory_handler.erl index 5863bfa..7af67ba 100644 --- a/examples/web_server/src/directory_handler.erl +++ b/examples/web_server/src/directory_handler.erl @@ -14,7 +14,7 @@ -export([list_html/2]). init(Req, Paths) -> - {rest, Req, Paths}. + {cowboy_rest, Req, Paths}. allowed_methods(Req, State) -> {[<<"GET">>], Req, State}. diff --git a/examples/websocket/src/ws_handler.erl b/examples/websocket/src/ws_handler.erl index 18f9526..eaa9284 100644 --- a/examples/websocket/src/ws_handler.erl +++ b/examples/websocket/src/ws_handler.erl @@ -6,7 +6,7 @@ init(Req, Opts) -> erlang:start_timer(1000, self(), <<"Hello!">>), - {ws, Req, Opts}. + {cowboy_websocket, Req, Opts}. websocket_handle({text, Msg}, Req, State) -> {reply, {text, << "That's what she said! ", Msg/binary >>}, Req, State}; diff --git a/src/cowboy_handler.erl b/src/cowboy_handler.erl index a666714..e3faf66 100644 --- a/src/cowboy_handler.erl +++ b/src/cowboy_handler.erl @@ -21,7 +21,15 @@ -behaviour(cowboy_middleware). -export([execute/2]). --export([terminate/5]). +-export([terminate/4]). + +-callback init(Req, any()) + -> {ok | module(), Req, any()} + | {module(), Req, any(), hibernate} + | {module(), Req, any(), timeout()} + | {module(), Req, any(), timeout(), hibernate} + when Req::cowboy_req:req(). +%% @todo optional -callback terminate(terminate_reason(), cowboy_req:req(), state()) -> ok. -spec execute(Req, Env) -> {ok, Req, Env} when Req::cowboy_req:req(), Env::cowboy_middleware:env(). @@ -29,21 +37,21 @@ execute(Req, Env) -> {_, Handler} = lists:keyfind(handler, 1, Env), {_, HandlerOpts} = lists:keyfind(handler_opts, 1, Env), try Handler:init(Req, HandlerOpts) of - {http, Req2, State} -> - handle(Req2, Env, Handler, State); - {shutdown, Req2, State} -> - terminate(Req2, Env, Handler, State, {normal, shutdown}); + {ok, Req2, State} -> + Result = terminate(normal, Req2, State, Handler), + {ok, Req2, [{result, Result}|Env]}; {Mod, Req2, State} -> - upgrade(Req2, Env, Handler, State, infinity, run, Mod); + Mod:upgrade(Req2, Env, Handler, State, infinity, run); {Mod, Req2, State, hibernate} -> - upgrade(Req2, Env, Handler, State, infinity, hibernate, Mod); + Mod:upgrade(Req2, Env, Handler, State, infinity, hibernate); {Mod, Req2, State, Timeout} -> - upgrade(Req2, Env, Handler, State, Timeout, run, Mod); + Mod:upgrade(Req2, Env, Handler, State, Timeout, run); {Mod, Req2, State, Timeout, hibernate} -> - upgrade(Req2, Env, Handler, State, Timeout, hibernate, Mod) + Mod:upgrade(Req2, Env, Handler, State, Timeout, hibernate) catch Class:Reason -> Stacktrace = erlang:get_stacktrace(), cowboy_req:maybe_reply(Stacktrace, Req), + terminate({crash, Class, Reason}, Req, HandlerOpts, Handler), erlang:Class([ {reason, Reason}, {mfa, {Handler, init, 2}}, @@ -53,36 +61,9 @@ execute(Req, Env) -> ]) end. -handle(Req, Env, Handler, State) -> - try Handler:handle(Req, State) of - {ok, Req2, State2} -> - terminate(Req2, Env, Handler, State2, {normal, shutdown}) - catch Class:Reason -> - Stacktrace = erlang:get_stacktrace(), - cowboy_req:maybe_reply(Stacktrace, Req), - _ = terminate(Req, Env, Handler, State, Reason), - erlang:Class([ - {reason, Reason}, - {mfa, {Handler, handle, 2}}, - {stacktrace, Stacktrace}, - {req, cowboy_req:to_list(Req)}, - {state, State} - ]) - end. - -upgrade(Req, Env, Handler, State, Timeout, Hibernate, Mod) -> - Mod2 = case Mod of - long_polling -> cowboy_long_polling; - rest -> cowboy_rest; - ws -> cowboy_websocket; - _ when Mod =/= http -> Mod - end, - Mod2:upgrade(Req, Env, Handler, State, Timeout, Hibernate). - --spec terminate(Req, Env, module(), any(), {normal, shutdown} | {error, atom()} | any()) - -> {ok, Req, Env} when Req::cowboy_req:req(), Env::cowboy_middleware:env(). -terminate(Req, Env, Handler, State, Reason) -> - Result = case erlang:function_exported(Handler, terminate, 3) of +-spec terminate(any(), Req, any(), module()) -> ok when Req::cowboy_req:req(). +terminate(Reason, Req, State, Handler) -> + case erlang:function_exported(Handler, terminate, 3) of true -> try Handler:terminate(Reason, cowboy_req:lock(Req), State) @@ -98,5 +79,4 @@ terminate(Req, Env, Handler, State, Reason) -> end; false -> ok - end, - {ok, Req, [{result, Result}|Env]}. + end. diff --git a/src/cowboy_http_handler.erl b/src/cowboy_http_handler.erl deleted file mode 100644 index 75b41d2..0000000 --- a/src/cowboy_http_handler.erl +++ /dev/null @@ -1,36 +0,0 @@ -%% Copyright (c) 2011-2014, Loïc Hoguin <[email protected]> -%% -%% Permission to use, copy, modify, and/or distribute this software for any -%% purpose with or without fee is hereby granted, provided that the above -%% copyright notice and this permission notice appear in all copies. -%% -%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - --module(cowboy_http_handler). - --type opts() :: any(). --type state() :: any(). -%% @todo see terminate -%-type terminate_reason() :: {normal, shutdown} -% | {normal, timeout} %% Only occurs in loop handlers. -% | {error, closed} %% Only occurs in loop handlers. -% | {error, overflow} %% Only occurs in loop handlers. -% | {error, atom()}. - --callback init(Req, opts()) - -> {http, Req, state()} - | {long_polling | rest | ws | module(), Req, state()} - | {long_polling | rest | ws | module(), Req, state(), hibernate} - | {long_polling | rest | ws | module(), Req, state(), timeout()} - | {long_polling | rest | ws | module(), Req, state(), timeout(), hibernate} - | {shutdown, Req, state()} - when Req::cowboy_req:req(). --callback handle(Req, State) -> {ok, Req, State} - when Req::cowboy_req:req(), State::state(). -%% @todo optional -callback terminate(terminate_reason(), cowboy_req:req(), state()) -> ok. diff --git a/src/cowboy_long_polling.erl b/src/cowboy_loop.erl index 6616a78..b9eb8cd 100644 --- a/src/cowboy_long_polling.erl +++ b/src/cowboy_loop.erl @@ -21,12 +21,25 @@ %% by default. This can be configured through the <em>loop_max_buffer</em> %% environment value. The request will be terminated with an %% <em>{error, overflow}</em> reason if this threshold is reached. --module(cowboy_long_polling). +-module(cowboy_loop). -behaviour(cowboy_sub_protocol). -export([upgrade/6]). -export([loop/4]). +-callback init(Req, any()) + -> {ok | module(), Req, any()} + | {module(), Req, any(), hibernate} + | {module(), Req, any(), timeout()} + | {module(), Req, any(), timeout(), hibernate} + when Req::cowboy_req:req(). +-callback info(any(), Req, State) + -> {ok, Req, State} + | {ok, Req, State, hibernate} + | {shutdown, Req, State} + when Req::cowboy_req:req(), State::any(). +%% @todo optional -callback terminate(terminate_reason(), cowboy_req:req(), state()) -> ok. + -record(state, { env :: cowboy_middleware:env(), hibernate = false :: boolean(), @@ -91,7 +104,7 @@ timeout(State=#state{timeout=Timeout, -spec loop(Req, #state{}, module(), any()) -> {ok, Req, cowboy_middleware:env()} | {suspend, module(), atom(), [any()]} when Req::cowboy_req:req(). -loop(Req, State=#state{env=Env, buffer_size=NbBytes, +loop(Req, State=#state{buffer_size=NbBytes, max_buffer=Threshold, timeout_ref=TRef, resp_sent=RespSent}, Handler, HandlerState) -> [Socket, Transport] = cowboy_req:get([socket, transport], Req), @@ -103,7 +116,7 @@ loop(Req, State=#state{env=Env, buffer_size=NbBytes, _ = if RespSent -> ok; true -> cowboy_req:reply(500, Req) end, - _ = cowboy_handler:terminate(Req, Env, Handler, HandlerState, {error, overflow}), + cowboy_handler:terminate({error, overflow}, Req, HandlerState, Handler), exit(normal); true -> Req2 = cowboy_req:append_buffer(Data, Req), @@ -115,7 +128,7 @@ loop(Req, State=#state{env=Env, buffer_size=NbBytes, {Error, Socket, Reason} -> terminate(Req, State, Handler, HandlerState, {error, Reason}); {timeout, TRef, ?MODULE} -> - after_loop(Req, State, Handler, HandlerState, {normal, timeout}); + after_loop(Req, State, Handler, HandlerState, timeout); {timeout, OlderTRef, ?MODULE} when is_reference(OlderTRef) -> loop(Req, State, Handler, HandlerState); Message -> @@ -133,21 +146,21 @@ loop(Req, State=#state{env=Env, buffer_size=NbBytes, call(Req2, State, Handler, HandlerState, Message) end. -call(Req, State=#state{env=Env, resp_sent=RespSent}, +call(Req, State=#state{resp_sent=RespSent}, Handler, HandlerState, Message) -> try Handler:info(Message, Req, HandlerState) of {ok, Req2, HandlerState2} -> - after_loop(Req2, State, Handler, HandlerState2, {normal, shutdown}); - {loop, Req2, HandlerState2} -> after_call(Req2, State, Handler, HandlerState2); - {loop, Req2, HandlerState2, hibernate} -> - after_call(Req2, State#state{hibernate=true}, Handler, HandlerState2) + {ok, Req2, HandlerState2, hibernate} -> + after_call(Req2, State#state{hibernate=true}, Handler, HandlerState2); + {shutdown, Req2, HandlerState2} -> + after_loop(Req2, State, Handler, HandlerState2, shutdown) catch Class:Reason -> Stacktrace = erlang:get_stacktrace(), if RespSent -> ok; true -> cowboy_req:maybe_reply(Stacktrace, Req) end, - _ = cowboy_handler:terminate(Req, Env, Handler, HandlerState, {error, overflow}), + cowboy_handler:terminate({crash, Class, Reason}, Req, HandlerState, Handler), erlang:Class([ {reason, Reason}, {mfa, {Handler, info, 3}}, @@ -180,7 +193,8 @@ terminate(Req, #state{env=Env, timeout_ref=TRef}, TRef -> erlang:cancel_timer(TRef) end, flush_timeouts(), - cowboy_handler:terminate(Req, Env, Handler, HandlerState, Reason). + Result = cowboy_handler:terminate(Reason, Req, HandlerState, Handler), + {ok, Req, [{result, Result}|Env]}. flush_timeouts() -> receive diff --git a/src/cowboy_loop_handler.erl b/src/cowboy_loop_handler.erl deleted file mode 100644 index 5e50c34..0000000 --- a/src/cowboy_loop_handler.erl +++ /dev/null @@ -1,39 +0,0 @@ -%% Copyright (c) 2011-2014, Loïc Hoguin <[email protected]> -%% -%% Permission to use, copy, modify, and/or distribute this software for any -%% purpose with or without fee is hereby granted, provided that the above -%% copyright notice and this permission notice appear in all copies. -%% -%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - --module(cowboy_loop_handler). - --type opts() :: any(). --type state() :: any(). -%% @todo see terminate -%-type terminate_reason() :: {normal, shutdown} -% | {normal, timeout} -% | {error, closed} -% | {error, overflow} -% | {error, atom()}. - --callback init(Req, opts()) - -> {http, Req, state()} - | {long_polling | rest | ws | module(), Req, state()} - | {long_polling | rest | ws | module(), Req, state(), hibernate} - | {long_polling | rest | ws | module(), Req, state(), timeout()} - | {long_polling | rest | ws | module(), Req, state(), timeout(), hibernate} - | {shutdown, Req, state()} - when Req::cowboy_req:req(). --callback info(any(), Req, State) - -> {ok, Req, State} - | {loop, Req, State} - | {loop, Req, State, hibernate} - when Req::cowboy_req:req(), State::state(). -%% @todo optional -callback terminate(terminate_reason(), cowboy_req:req(), state()) -> ok. diff --git a/src/cowboy_rest.erl b/src/cowboy_rest.erl index 63f33e6..98c6f3b 100644 --- a/src/cowboy_rest.erl +++ b/src/cowboy_rest.erl @@ -19,6 +19,15 @@ -export([upgrade/6]). +-callback init(Req, any()) + -> {ok | module(), Req, any()} + | {module(), Req, any(), hibernate} + | {module(), Req, any(), timeout()} + | {module(), Req, any(), timeout(), hibernate} + when Req::cowboy_req:req(). +%% @todo optional REST callbacks +%% @todo optional -callback terminate(terminate_reason(), cowboy_req:req(), state()) -> ok. + -record(state, { env :: cowboy_middleware:env(), method = undefined :: binary(), @@ -967,11 +976,11 @@ next(Req, State, StatusCode) when is_integer(StatusCode) -> respond(Req, State, StatusCode) -> terminate(cowboy_req:reply(StatusCode, Req), State). -error_terminate(Req, State=#state{handler=Handler, handler_state=HandlerState}, +error_terminate(Req, #state{handler=Handler, handler_state=HandlerState}, Class, Reason, Callback) -> - _ = terminate(Req, State), Stacktrace = erlang:get_stacktrace(), cowboy_req:maybe_reply(Stacktrace, Req), + cowboy_handler:terminate({crash, Class, Reason}, Req, HandlerState, Handler), erlang:Class([ {reason, Reason}, {mfa, {Handler, Callback, 2}}, @@ -981,4 +990,5 @@ error_terminate(Req, State=#state{handler=Handler, handler_state=HandlerState}, ]). terminate(Req, #state{env=Env, handler=Handler, handler_state=HandlerState}) -> - cowboy_handler:terminate(Req, Env, Handler, HandlerState, {normal, shutdown}). + Result = cowboy_handler:terminate(normal, Req, HandlerState, Handler), + {ok, Req, [{result, Result}|Env]}. diff --git a/src/cowboy_static.erl b/src/cowboy_static.erl index ecca6fa..99ad82e 100644 --- a/src/cowboy_static.erl +++ b/src/cowboy_static.erl @@ -42,7 +42,7 @@ %% If the handler is configured to manage a directory, check that the %% requested file is inside the configured directory. --spec init(Req, opts()) -> {rest, Req, error | state()} when Req::cowboy_req:req(). +-spec init(Req, opts()) -> {cowboy_rest, Req, error | state()} when Req::cowboy_req:req(). init(Req, {Name, Path}) -> init_opts(Req, {Name, Path, []}); init(Req, {Name, App, Path}) @@ -87,7 +87,7 @@ init_dir(Req, Path, Extra) -> << Dir:Len/binary, $/, _/binary >> -> init_info(Req, Filepath, Extra); _ -> - {rest, Req, error} + {cowboy_rest, Req, error} end. fullpath(Path) -> @@ -105,7 +105,7 @@ fullpath([Segment|Tail], Acc) -> init_info(Req, Path, Extra) -> Info = file:read_file_info(Path, [{time, universal}]), - {rest, Req, {Path, Info, Extra}}. + {cowboy_rest, Req, {Path, Info, Extra}}. -ifdef(TEST). fullpath_test_() -> diff --git a/src/cowboy_websocket.erl b/src/cowboy_websocket.erl index e0b20ca..c700af9 100644 --- a/src/cowboy_websocket.erl +++ b/src/cowboy_websocket.erl @@ -33,8 +33,32 @@ -type frag_state() :: undefined | {nofin, opcode(), binary()} | {fin, opcode(), binary()}. -type rsv() :: << _:3 >>. --type terminate_reason() :: {normal | error | remote, atom()} - | {remote, close_code(), binary()}. +-type terminate_reason() :: normal | shutdown | timeout + | remote | {remote, close_code(), binary()} + | {error, badencoding | badframe | closed | atom()} + | {crash, error | exit | throw, any()}. + +-callback init(Req, any()) + -> {ok | module(), Req, any()} + | {module(), Req, any(), hibernate} + | {module(), Req, any(), timeout()} + | {module(), Req, any(), timeout(), hibernate} + when Req::cowboy_req:req(). +-callback websocket_handle({text | binary | ping | pong, binary()}, Req, State) + -> {ok, Req, State} + | {ok, Req, State, hibernate} + | {reply, frame() | [frame()], Req, State} + | {reply, frame() | [frame()], Req, State, hibernate} + | {shutdown, Req, State} + when Req::cowboy_req:req(), State::any(). +-callback websocket_info(any(), Req, State) + -> {ok, Req, State} + | {ok, Req, State, hibernate} + | {reply, frame() | [frame()], Req, State} + | {reply, frame() | [frame()], Req, State, hibernate} + | {shutdown, Req, State} + when Req::cowboy_req:req(), State::any(). +%% @todo optional -callback terminate(terminate_reason(), cowboy_req:req(), state()) -> ok. -record(state, { env :: cowboy_middleware:env(), @@ -181,7 +205,7 @@ handler_loop(State=#state{socket=Socket, messages={OK, Closed, Error}, {Error, Socket, Reason} -> handler_terminate(State, Req, HandlerState, {error, Reason}); {timeout, TRef, ?MODULE} -> - websocket_close(State, Req, HandlerState, {normal, timeout}); + websocket_close(State, Req, HandlerState, timeout); {timeout, OlderTRef, ?MODULE} when is_reference(OlderTRef) -> handler_loop(State, Req, HandlerState, SoFar); Message -> @@ -487,7 +511,7 @@ websocket_payload_loop(State=#state{socket=Socket, transport=Transport, {Error, Socket, Reason} -> handler_terminate(State, Req, HandlerState, {error, Reason}); {timeout, TRef, ?MODULE} -> - websocket_close(State, Req, HandlerState, {normal, timeout}); + websocket_close(State, Req, HandlerState, timeout); {timeout, OlderTRef, ?MODULE} when is_reference(OlderTRef) -> websocket_payload_loop(State, Req, HandlerState, Opcode, Len, MaskKey, Unmasked, UnmaskedLen, Rsv); @@ -524,7 +548,7 @@ websocket_dispatch(State, Req, HandlerState, RemainingData, 2, Payload) -> websocket_handle, {binary, Payload}, fun websocket_data/4); %% Close control frame. websocket_dispatch(State, Req, HandlerState, _RemainingData, 8, <<>>) -> - websocket_close(State, Req, HandlerState, {remote, closed}); + websocket_close(State, Req, HandlerState, remote); websocket_dispatch(State, Req, HandlerState, _RemainingData, 8, << Code:16, Payload/bits >>) -> websocket_close(State, Req, HandlerState, {remote, Code, Payload}); @@ -558,8 +582,7 @@ handler_call(State=#state{handler=Handler}, Req, HandlerState, {ok, State2} -> NextState(State2, Req2, HandlerState2, RemainingData); {shutdown, State2} -> - handler_terminate(State2, Req2, HandlerState2, - {normal, shutdown}); + handler_terminate(State2, Req2, HandlerState2, shutdown); {{error, _} = Error, State2} -> handler_terminate(State2, Req2, HandlerState2, Error) end; @@ -570,8 +593,7 @@ handler_call(State=#state{handler=Handler}, Req, HandlerState, NextState(State2#state{hibernate=true}, Req2, HandlerState2, RemainingData); {shutdown, State2} -> - handler_terminate(State2, Req2, HandlerState2, - {normal, shutdown}); + handler_terminate(State2, Req2, HandlerState2, shutdown); {{error, _} = Error, State2} -> handler_terminate(State2, Req2, HandlerState2, Error) end; @@ -580,8 +602,7 @@ handler_call(State=#state{handler=Handler}, Req, HandlerState, {ok, State2} -> NextState(State2, Req2, HandlerState2, RemainingData); {shutdown, State2} -> - handler_terminate(State2, Req2, HandlerState2, - {normal, shutdown}); + handler_terminate(State2, Req2, HandlerState2, shutdown); {{error, _} = Error, State2} -> handler_terminate(State2, Req2, HandlerState2, Error) end; @@ -591,15 +612,14 @@ handler_call(State=#state{handler=Handler}, Req, HandlerState, NextState(State2#state{hibernate=true}, Req2, HandlerState2, RemainingData); {shutdown, State2} -> - handler_terminate(State2, Req2, HandlerState2, - {normal, shutdown}); + handler_terminate(State2, Req2, HandlerState2, shutdown); {{error, _} = Error, State2} -> handler_terminate(State2, Req2, HandlerState2, Error) end; {shutdown, Req2, HandlerState2} -> - websocket_close(State, Req2, HandlerState2, {normal, shutdown}) + websocket_close(State, Req2, HandlerState2, shutdown) catch Class:Reason -> - _ = websocket_close(State, Req, HandlerState, {error, handler}), + _ = websocket_close(State, Req, HandlerState, {crash, Class, Reason}), erlang:Class([ {reason, Reason}, {mfa, {Handler, Callback, 3}}, @@ -696,15 +716,15 @@ websocket_send_many([Frame|Tail], State) -> websocket_close(State=#state{socket=Socket, transport=Transport}, Req, HandlerState, Reason) -> case Reason of - {normal, _} -> + Normal when Normal =:= shutdown; Normal =:= timeout -> Transport:send(Socket, << 1:1, 0:3, 8:4, 0:1, 2:7, 1000:16 >>); {error, badframe} -> Transport:send(Socket, << 1:1, 0:3, 8:4, 0:1, 2:7, 1002:16 >>); {error, badencoding} -> Transport:send(Socket, << 1:1, 0:3, 8:4, 0:1, 2:7, 1007:16 >>); - {error, handler} -> + {crash, _, _} -> Transport:send(Socket, << 1:1, 0:3, 8:4, 0:1, 2:7, 1011:16 >>); - {remote, closed} -> + remote -> Transport:send(Socket, << 1:1, 0:3, 8:4, 0:8 >>); {remote, Code, _} -> Transport:send(Socket, << 1:1, 0:3, 8:4, 0:1, 2:7, Code:16 >>) @@ -716,5 +736,5 @@ websocket_close(State=#state{socket=Socket, transport=Transport}, when Req::cowboy_req:req(). handler_terminate(#state{env=Env, handler=Handler}, Req, HandlerState, Reason) -> - _ = cowboy_handler:terminate(Req, Env, Handler, HandlerState, Reason), + cowboy_handler:terminate(Reason, Req, HandlerState, Handler), {ok, Req, [{result, closed}|Env]}. diff --git a/src/cowboy_websocket_handler.erl b/src/cowboy_websocket_handler.erl deleted file mode 100644 index a6a036b..0000000 --- a/src/cowboy_websocket_handler.erl +++ /dev/null @@ -1,51 +0,0 @@ -%% Copyright (c) 2011-2014, Loïc Hoguin <[email protected]> -%% -%% Permission to use, copy, modify, and/or distribute this software for any -%% purpose with or without fee is hereby granted, provided that the above -%% copyright notice and this permission notice appear in all copies. -%% -%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - --module(cowboy_websocket_handler). - --type opts() :: any(). --type state() :: any(). -%% @todo see terminate -%-type terminate_reason() :: {normal, shutdown} -% | {normal, timeout} -% | {error, closed} -% | {remote, closed} -% | {remote, cowboy_websocket:close_code(), binary()} -% | {error, badencoding} -% | {error, badframe} -% | {error, atom()}. - --callback init(Req, opts()) - -> {http, Req, state()} - | {long_polling | rest | ws | module(), Req, state()} - | {long_polling | rest | ws | module(), Req, state(), hibernate} - | {long_polling | rest | ws | module(), Req, state(), timeout()} - | {long_polling | rest | ws | module(), Req, state(), timeout(), hibernate} - | {shutdown, Req, state()} - when Req::cowboy_req:req(). --callback websocket_handle({text | binary | ping | pong, binary()}, Req, State) - -> {ok, Req, State} - | {ok, Req, State, hibernate} - | {reply, cowboy_websocket:frame() | [cowboy_websocket:frame()], Req, State} - | {reply, cowboy_websocket:frame() | [cowboy_websocket:frame()], Req, State, hibernate} - | {shutdown, Req, State} - when Req::cowboy_req:req(), State::state(). --callback websocket_info(any(), Req, State) - -> {ok, Req, State} - | {ok, Req, State, hibernate} - | {reply, cowboy_websocket:frame() | [cowboy_websocket:frame()], Req, State} - | {reply, cowboy_websocket:frame() | [cowboy_websocket:frame()], Req, State, hibernate} - | {shutdown, Req, State} - when Req::cowboy_req:req(), State::state(). -%% @todo optional -callback terminate(terminate_reason(), cowboy_req:req(), state()) -> ok. diff --git a/test/handlers/long_polling_h.erl b/test/handlers/long_polling_h.erl index 1b240fd..20fe7ee 100644 --- a/test/handlers/long_polling_h.erl +++ b/test/handlers/long_polling_h.erl @@ -11,15 +11,15 @@ init(Req, _) -> erlang:send_after(200, self(), timeout), - {long_polling, Req, 2, 5000, hibernate}. + {cowboy_loop, Req, 2, 5000, hibernate}. info(timeout, Req, 0) -> - {ok, cowboy_req:reply(102, Req), 0}; + {shutdown, cowboy_req:reply(102, Req), 0}; info(timeout, Req, Count) -> erlang:send_after(200, self(), timeout), - {loop, Req, Count - 1, hibernate}. + {ok, Req, Count - 1, hibernate}. -terminate({normal, shutdown}, _, 0) -> +terminate(shutdown, _, 0) -> ok; terminate({error, overflow}, _, _) -> ok. diff --git a/test/handlers/loop_handler_body_h.erl b/test/handlers/loop_handler_body_h.erl index ac75773..096fb3d 100644 --- a/test/handlers/loop_handler_body_h.erl +++ b/test/handlers/loop_handler_body_h.erl @@ -11,12 +11,12 @@ init(Req, _) -> self() ! timeout, - {long_polling, Req, undefined, 5000, hibernate}. + {cowboy_loop, Req, undefined, 5000, hibernate}. info(timeout, Req, State) -> {ok, Body, Req2} = cowboy_req:body(Req), 100000 = byte_size(Body), - {ok, cowboy_req:reply(200, Req2), State}. + {shutdown, cowboy_req:reply(200, Req2), State}. -terminate({normal, shutdown}, _, _) -> +terminate(shutdown, _, _) -> ok. diff --git a/test/handlers/loop_handler_timeout_h.erl b/test/handlers/loop_handler_timeout_h.erl index 8e24d33..a1bfa51 100644 --- a/test/handlers/loop_handler_timeout_h.erl +++ b/test/handlers/loop_handler_timeout_h.erl @@ -1,7 +1,7 @@ %% This module implements a loop handler that sends %% itself a timeout that will intentionally arrive %% too late, as it configures itself to only wait -%% 200ms before closing the connection in init/3. +%% 200ms before closing the connection in init/2. %% This results in a 204 reply being sent back by Cowboy. -module(loop_handler_timeout_h). @@ -12,10 +12,10 @@ init(Req, _) -> erlang:send_after(1000, self(), timeout), - {long_polling, Req, undefined, 200, hibernate}. + {cowboy_loop, Req, undefined, 200, hibernate}. info(timeout, Req, State) -> - {ok, cowboy_req:reply(500, Req), State}. + {shutdown, cowboy_req:reply(500, Req), State}. -terminate({normal, timeout}, _, _) -> +terminate(timeout, _, _) -> ok. diff --git a/test/http_SUITE.erl b/test/http_SUITE.erl index e98ce1b..3783b6e 100644 --- a/test/http_SUITE.erl +++ b/test/http_SUITE.erl @@ -142,7 +142,6 @@ init_dispatch(Config) -> {"localhost", [ {"/chunked_response", http_chunked, []}, {"/streamed_response", http_streamed, []}, - {"/init_shutdown", http_init_shutdown, []}, {"/headers/dupe", http_handler, [{headers, [{<<"connection">>, <<"close">>}]}]}, {"/set_resp/header", http_set_resp, @@ -292,9 +291,7 @@ check_status(Config) -> {403, "/static/unreadable"}, {404, "/not/found"}, {404, "/static/not_found"}, - {500, "/handler_errors?case=handle_before_reply"}, - {500, "/handler_errors?case=init_before_reply"}, - {666, "/init_shutdown"} + {500, "/handler_errors?case=init_before_reply"} ], _ = [{Status, URL} = begin Ret = do_get(URL, Config), @@ -342,40 +339,12 @@ echo_body_qs_max_length(Config) -> {response, nofin, 413, _} = gun:await(ConnPid, Ref), ok. -error_chain_handle_after_reply(Config) -> - {ConnPid, MRef} = gun_monitor_open(Config), - Ref1 = gun:get(ConnPid, "/"), - Ref2 = gun:get(ConnPid, "/handler_errors?case=handle_after_reply"), - {response, nofin, 200, _} = gun:await(ConnPid, Ref1, MRef), - {response, nofin, 200, _} = gun:await(ConnPid, Ref2, MRef), - gun_is_gone(ConnPid, MRef). - -error_chain_handle_before_reply(Config) -> - {ConnPid, MRef} = gun_monitor_open(Config), - Ref1 = gun:get(ConnPid, "/"), - Ref2 = gun:get(ConnPid, "/handler_errors?case=handle_before_reply"), - {response, nofin, 200, _} = gun:await(ConnPid, Ref1, MRef), - {response, fin, 500, _} = gun:await(ConnPid, Ref2, MRef), - gun_is_gone(ConnPid, MRef). - -error_handle_after_reply(Config) -> - {ConnPid, MRef} = gun_monitor_open(Config), - Ref = gun:get(ConnPid, "/handler_errors?case=handle_after_reply"), - {response, nofin, 200, _} = gun:await(ConnPid, Ref, MRef), - gun_is_gone(ConnPid, MRef). - error_init_after_reply(Config) -> {ConnPid, MRef} = gun_monitor_open(Config), Ref = gun:get(ConnPid, "/handler_errors?case=init_after_reply"), {response, nofin, 200, _} = gun:await(ConnPid, Ref, MRef), gun_is_gone(ConnPid, MRef). -error_init_reply_handle_error(Config) -> - {ConnPid, MRef} = gun_monitor_open(Config), - Ref = gun:get(ConnPid, "/handler_errors?case=init_reply_handle_error"), - {response, nofin, 200, _} = gun:await(ConnPid, Ref, MRef), - gun_is_gone(ConnPid, MRef). - headers_dupe(Config) -> {ConnPid, MRef} = gun_monitor_open(Config), Ref = gun:get(ConnPid, "/headers/dupe"), diff --git a/test/http_SUITE_data/http_body_qs.erl b/test/http_SUITE_data/http_body_qs.erl index b219566..945b7fd 100644 --- a/test/http_SUITE_data/http_body_qs.erl +++ b/test/http_SUITE_data/http_body_qs.erl @@ -3,15 +3,11 @@ -module(http_body_qs). -export([init/2]). --export([handle/2]). init(Req, Opts) -> - {http, Req, Opts}. - -handle(Req, State) -> Method = cowboy_req:method(Req), HasBody = cowboy_req:has_body(Req), - {ok, maybe_echo(Method, HasBody, Req), State}. + {ok, maybe_echo(Method, HasBody, Req), Opts}. maybe_echo(<<"POST">>, true, Req) -> case cowboy_req:body_qs(Req) of diff --git a/test/http_SUITE_data/http_chunked.erl b/test/http_SUITE_data/http_chunked.erl index 0c18363..87a6852 100644 --- a/test/http_SUITE_data/http_chunked.erl +++ b/test/http_SUITE_data/http_chunked.erl @@ -3,15 +3,11 @@ -module(http_chunked). -export([init/2]). --export([handle/2]). init(Req, Opts) -> - {http, Req, Opts}. - -handle(Req, State) -> Req2 = cowboy_req:chunked_reply(200, Req), timer:sleep(100), cowboy_req:chunk("chunked_handler\r\n", Req2), timer:sleep(100), cowboy_req:chunk("works fine!", Req2), - {ok, Req2, State}. + {ok, Req2, Opts}. diff --git a/test/http_SUITE_data/http_echo_body.erl b/test/http_SUITE_data/http_echo_body.erl index 8743844..c76b9b3 100644 --- a/test/http_SUITE_data/http_echo_body.erl +++ b/test/http_SUITE_data/http_echo_body.erl @@ -3,18 +3,14 @@ -module(http_echo_body). -export([init/2]). --export([handle/2]). init(Req, Opts) -> - {http, Req, Opts}. - -handle(Req, State) -> true = cowboy_req:has_body(Req), Req3 = case cowboy_req:body(Req, [{length, 1000000}]) of {ok, Body, Req2} -> handle_body(Req2, Body); {more, _, Req2} -> handle_badlength(Req2) end, - {ok, Req3, State}. + {ok, Req3, Opts}. handle_badlength(Req) -> cowboy_req:reply(413, [], <<"Request entity too large">>, Req). diff --git a/test/http_SUITE_data/http_errors.erl b/test/http_SUITE_data/http_errors.erl index 39eda60..f105c58 100644 --- a/test/http_SUITE_data/http_errors.erl +++ b/test/http_SUITE_data/http_errors.erl @@ -3,7 +3,6 @@ -module(http_errors). -export([init/2]). --export([handle/2]). init(Req, _Opts) -> #{'case' := Case} = cowboy_req:match_qs(Req, ['case']), @@ -15,22 +14,4 @@ case_init(<<"init_before_reply">> = Case, _Req) -> case_init(<<"init_after_reply">> = Case, Req) -> cowboy_error_h:ignore(?MODULE, case_init, 2), _ = cowboy_req:reply(200, [], "http_handler_crashes", Req), - error(Case); -case_init(<<"init_reply_handle_error">> = Case, Req) -> - Req1 = cowboy_req:reply(200, [], "http_handler_crashes", Req), - {http, Req1, Case}; -case_init(<<"handle_before_reply">> = Case, Req) -> - {http, Req, Case}; -case_init(<<"handle_after_reply">> = Case, Req) -> - {http, Req, Case}. - -handle(_Req, <<"init_reply_handle_error">> = Case) -> - cowboy_error_h:ignore(?MODULE, handle, 2), - error(Case); -handle(_Req, <<"handle_before_reply">> = Case) -> - cowboy_error_h:ignore(?MODULE, handle, 2), - error(Case); -handle(Req, <<"handle_after_reply">> = Case) -> - cowboy_error_h:ignore(?MODULE, handle, 2), - _ = cowboy_req:reply(200, [], "http_handler_crashes", Req), error(Case). diff --git a/test/http_SUITE_data/http_handler.erl b/test/http_SUITE_data/http_handler.erl index 61c14f8..62e9193 100644 --- a/test/http_SUITE_data/http_handler.erl +++ b/test/http_SUITE_data/http_handler.erl @@ -3,12 +3,8 @@ -module(http_handler). -export([init/2]). --export([handle/2]). init(Req, Opts) -> - {http, Req, Opts}. - -handle(Req, State) -> - Headers = proplists:get_value(headers, State, []), - Body = proplists:get_value(body, State, "http_handler"), - {ok, cowboy_req:reply(200, Headers, Body, Req), State}. + Headers = proplists:get_value(headers, Opts, []), + Body = proplists:get_value(body, Opts, "http_handler"), + {ok, cowboy_req:reply(200, Headers, Body, Req), Opts}. diff --git a/test/http_SUITE_data/http_init_shutdown.erl b/test/http_SUITE_data/http_init_shutdown.erl deleted file mode 100644 index 5aae898..0000000 --- a/test/http_SUITE_data/http_init_shutdown.erl +++ /dev/null @@ -1,10 +0,0 @@ -%% Feel free to use, reuse and abuse the code in this file. - --module(http_init_shutdown). - --export([init/2]). - -init(Req, _) -> - Req2 = cowboy_req:reply(<<"666 Init Shutdown Testing">>, - [{<<"connection">>, <<"close">>}], Req), - {shutdown, Req2, undefined}. diff --git a/test/http_SUITE_data/http_loop_stream_recv.erl b/test/http_SUITE_data/http_loop_stream_recv.erl index 8547cc9..4cd39a2 100644 --- a/test/http_SUITE_data/http_loop_stream_recv.erl +++ b/test/http_SUITE_data/http_loop_stream_recv.erl @@ -9,7 +9,7 @@ init(Req, _) -> receive after 100 -> ok end, self() ! stream, - {long_polling, Req, undefined, 100}. + {cowboy_loop, Req, undefined, 100}. info(stream, Req, undefined) -> stream(Req, 1, <<>>). @@ -17,7 +17,7 @@ info(stream, Req, undefined) -> stream(Req, ID, Acc) -> case cowboy_req:body(Req) of {ok, <<>>, Req2} -> - {ok, cowboy_req:reply(200, Req2), undefined}; + {shutdown, cowboy_req:reply(200, Req2), undefined}; {_, Data, Req2} -> parse_id(Req2, ID, << Acc/binary, Data/binary >>) end. @@ -30,5 +30,5 @@ parse_id(Req, ID, Data) -> stream(Req, ID, Data) end. -terminate({normal, shutdown}, _, _) -> +terminate(shutdown, _, _) -> ok. diff --git a/test/http_SUITE_data/http_multipart.erl b/test/http_SUITE_data/http_multipart.erl index 6bd6408..196cbce 100644 --- a/test/http_SUITE_data/http_multipart.erl +++ b/test/http_SUITE_data/http_multipart.erl @@ -3,14 +3,10 @@ -module(http_multipart). -export([init/2]). --export([handle/2]). init(Req, Opts) -> - {http, Req, Opts}. - -handle(Req, State) -> {Result, Req2} = acc_multipart(Req, []), - {ok, cowboy_req:reply(200, [], term_to_binary(Result), Req2), State}. + {ok, cowboy_req:reply(200, [], term_to_binary(Result), Req2), Opts}. acc_multipart(Req, Acc) -> case cowboy_req:part(Req) of diff --git a/test/http_SUITE_data/http_multipart_stream.erl b/test/http_SUITE_data/http_multipart_stream.erl index 43d459a..82662ad 100644 --- a/test/http_SUITE_data/http_multipart_stream.erl +++ b/test/http_SUITE_data/http_multipart_stream.erl @@ -3,14 +3,10 @@ -module(http_multipart_stream). -export([init/2]). --export([handle/2]). init(Req, Opts) -> - {http, Req, Opts}. - -handle(Req, State) -> Req2 = multipart(Req), - {ok, cowboy_req:reply(200, Req2), State}. + {ok, cowboy_req:reply(200, Req2), Opts}. multipart(Req) -> case cowboy_req:part(Req) of diff --git a/test/http_SUITE_data/http_req_attr.erl b/test/http_SUITE_data/http_req_attr.erl index 9c5acba..ce9c185 100644 --- a/test/http_SUITE_data/http_req_attr.erl +++ b/test/http_SUITE_data/http_req_attr.erl @@ -5,15 +5,11 @@ -module(http_req_attr). -export([init/2]). --export([handle/2]). init(Req, Opts) -> - {http, Req, Opts}. - -handle(Req, State) -> #{attr := Attr} = cowboy_req:match_qs(Req, [attr]), <<"host_and_port">> = Attr, Host = cowboy_req:host(Req), Port = cowboy_req:port(Req), Value = [Host, "\n", integer_to_list(Port)], - {ok, cowboy_req:reply(200, [], Value, Req), State}. + {ok, cowboy_req:reply(200, [], Value, Req), Opts}. diff --git a/test/http_SUITE_data/http_set_resp.erl b/test/http_SUITE_data/http_set_resp.erl index 77196a8..2e7f835 100644 --- a/test/http_SUITE_data/http_set_resp.erl +++ b/test/http_SUITE_data/http_set_resp.erl @@ -3,7 +3,6 @@ -module(http_set_resp). -export([init/2]). --export([handle/2]). init(Req, Opts) -> Headers = proplists:get_value(headers, Opts, []), @@ -14,16 +13,13 @@ init(Req, Opts) -> Req3 = cowboy_req:set_resp_body(Body, Req2), Req4 = cowboy_req:set_resp_header(<<"x-cowboy-test">>, <<"ok">>, Req3), Req5 = cowboy_req:set_resp_cookie(<<"cake">>, <<"lie">>, [], Req4), - {http, Req5, undefined}. - -handle(Req, State) -> - case cowboy_req:has_resp_header(<<"x-cowboy-test">>, Req) of - false -> {ok, Req, State}; + case cowboy_req:has_resp_header(<<"x-cowboy-test">>, Req5) of + false -> {ok, Req5, Opts}; true -> - case cowboy_req:has_resp_body(Req) of + case cowboy_req:has_resp_body(Req5) of false -> - {ok, Req, State}; + {ok, Req5, Opts}; true -> - {ok, cowboy_req:reply(200, Req), State} + {ok, cowboy_req:reply(200, Req5), Opts} end end. diff --git a/test/http_SUITE_data/http_stream_body.erl b/test/http_SUITE_data/http_stream_body.erl index 29569cd..aea5300 100644 --- a/test/http_SUITE_data/http_stream_body.erl +++ b/test/http_SUITE_data/http_stream_body.erl @@ -3,14 +3,10 @@ -module(http_stream_body). -export([init/2]). --export([handle/2]). init(Req, Opts) -> - {http, Req, Opts}. - -handle(Req, State) -> - Body = proplists:get_value(body, State, "http_handler_stream_body"), - Reply = proplists:get_value(reply, State), + Body = proplists:get_value(body, Opts, "http_handler_stream_body"), + Reply = proplists:get_value(reply, Opts), SFun = fun(Socket, Transport) -> Transport:send(Socket, Body) end, Req2 = case Reply of set_resp -> @@ -23,4 +19,4 @@ handle(Req, State) -> SFun2 = fun(SendFun) -> lists:foreach(SendFun, Body) end, cowboy_req:set_resp_body_fun(chunked, SFun2, Req) end, - {ok, cowboy_req:reply(200, Req2), State}. + {ok, cowboy_req:reply(200, Req2), Opts}. diff --git a/test/http_SUITE_data/http_streamed.erl b/test/http_SUITE_data/http_streamed.erl index ba710f1..6ca53fb 100644 --- a/test/http_SUITE_data/http_streamed.erl +++ b/test/http_SUITE_data/http_streamed.erl @@ -3,16 +3,12 @@ -module(http_streamed). -export([init/2]). --export([handle/2]). init(Req, Opts) -> - {http, Req, Opts}. - -handle(Req, State) -> Req2 = cowboy_req:set([{resp_state, waiting_stream}], Req), Req3 = cowboy_req:chunked_reply(200, Req2), timer:sleep(100), cowboy_req:chunk("streamed_handler\r\n", Req3), timer:sleep(100), cowboy_req:chunk("works fine!", Req3), - {ok, Req3, State}. + {ok, Req3, Opts}. diff --git a/test/http_SUITE_data/rest_empty_resource.erl b/test/http_SUITE_data/rest_empty_resource.erl index 97fc26f..8a944cd 100644 --- a/test/http_SUITE_data/rest_empty_resource.erl +++ b/test/http_SUITE_data/rest_empty_resource.erl @@ -3,4 +3,4 @@ -export([init/2]). init(Req, Opts) -> - {rest, Req, Opts}. + {cowboy_rest, Req, Opts}. diff --git a/test/http_SUITE_data/rest_expires.erl b/test/http_SUITE_data/rest_expires.erl index e71b107..8665b06 100644 --- a/test/http_SUITE_data/rest_expires.erl +++ b/test/http_SUITE_data/rest_expires.erl @@ -7,7 +7,7 @@ -export([last_modified/2]). init(Req, Opts) -> - {rest, Req, Opts}. + {cowboy_rest, Req, Opts}. content_types_provided(Req, State) -> {[{{<<"text">>, <<"plain">>, []}, get_text_plain}], Req, State}. diff --git a/test/http_SUITE_data/rest_expires_binary.erl b/test/http_SUITE_data/rest_expires_binary.erl index 84b0675..4d6bd3c 100644 --- a/test/http_SUITE_data/rest_expires_binary.erl +++ b/test/http_SUITE_data/rest_expires_binary.erl @@ -6,7 +6,7 @@ -export([expires/2]). init(Req, Opts) -> - {rest, Req, Opts}. + {cowboy_rest, Req, Opts}. content_types_provided(Req, State) -> {[{{<<"text">>, <<"plain">>, []}, get_text_plain}], Req, State}. diff --git a/test/http_SUITE_data/rest_forbidden_resource.erl b/test/http_SUITE_data/rest_forbidden_resource.erl index 1e6d99f..0a65228 100644 --- a/test/http_SUITE_data/rest_forbidden_resource.erl +++ b/test/http_SUITE_data/rest_forbidden_resource.erl @@ -9,7 +9,7 @@ -export([from_text/2]). init(Req, [Forbidden]) -> - {rest, Req, Forbidden}. + {cowboy_rest, Req, Forbidden}. allowed_methods(Req, State) -> {[<<"GET">>, <<"HEAD">>, <<"POST">>], Req, State}. diff --git a/test/http_SUITE_data/rest_missing_callbacks.erl b/test/http_SUITE_data/rest_missing_callbacks.erl index fec308a..e1fcac1 100644 --- a/test/http_SUITE_data/rest_missing_callbacks.erl +++ b/test/http_SUITE_data/rest_missing_callbacks.erl @@ -6,7 +6,7 @@ -export([content_types_provided/2]). init(Req, Opts) -> - {rest, Req, Opts}. + {cowboy_rest, Req, Opts}. allowed_methods(Req, State) -> {[<<"GET">>, <<"PUT">>], Req, State}. diff --git a/test/http_SUITE_data/rest_nodelete_resource.erl b/test/http_SUITE_data/rest_nodelete_resource.erl index e8123db..b9f40bd 100644 --- a/test/http_SUITE_data/rest_nodelete_resource.erl +++ b/test/http_SUITE_data/rest_nodelete_resource.erl @@ -6,7 +6,7 @@ -export([get_text_plain/2]). init(Req, Opts) -> - {rest, Req, Opts}. + {cowboy_rest, Req, Opts}. allowed_methods(Req, State) -> {[<<"GET">>, <<"HEAD">>, <<"DELETE">>], Req, State}. diff --git a/test/http_SUITE_data/rest_param_all.erl b/test/http_SUITE_data/rest_param_all.erl index 54950eb..2b2ea23 100644 --- a/test/http_SUITE_data/rest_param_all.erl +++ b/test/http_SUITE_data/rest_param_all.erl @@ -8,7 +8,7 @@ -export([put_text_plain/2]). init(Req, Opts) -> - {rest, Req, Opts}. + {cowboy_rest, Req, Opts}. allowed_methods(Req, State) -> {[<<"GET">>, <<"PUT">>], Req, State}. diff --git a/test/http_SUITE_data/rest_patch_resource.erl b/test/http_SUITE_data/rest_patch_resource.erl index 4b81648..03f780f 100644 --- a/test/http_SUITE_data/rest_patch_resource.erl +++ b/test/http_SUITE_data/rest_patch_resource.erl @@ -8,7 +8,7 @@ -export([patch_text_plain/2]). init(Req, Opts) -> - {rest, Req, Opts}. + {cowboy_rest, Req, Opts}. allowed_methods(Req, State) -> {[<<"HEAD">>, <<"GET">>, <<"PATCH">>], Req, State}. diff --git a/test/http_SUITE_data/rest_post_charset_resource.erl b/test/http_SUITE_data/rest_post_charset_resource.erl index 7b7c49c..0be6aa3 100644 --- a/test/http_SUITE_data/rest_post_charset_resource.erl +++ b/test/http_SUITE_data/rest_post_charset_resource.erl @@ -6,7 +6,7 @@ -export([from_text/2]). init(Req, Opts) -> - {rest, Req, Opts}. + {cowboy_rest, Req, Opts}. allowed_methods(Req, State) -> {[<<"POST">>], Req, State}. diff --git a/test/http_SUITE_data/rest_postonly_resource.erl b/test/http_SUITE_data/rest_postonly_resource.erl index 9b14b24..942e55d 100644 --- a/test/http_SUITE_data/rest_postonly_resource.erl +++ b/test/http_SUITE_data/rest_postonly_resource.erl @@ -6,7 +6,7 @@ -export([from_text/2]). init(Req, Opts) -> - {rest, Req, Opts}. + {cowboy_rest, Req, Opts}. allowed_methods(Req, State) -> {[<<"POST">>], Req, State}. diff --git a/test/http_SUITE_data/rest_resource_etags.erl b/test/http_SUITE_data/rest_resource_etags.erl index 9509d0a..23b9dfc 100644 --- a/test/http_SUITE_data/rest_resource_etags.erl +++ b/test/http_SUITE_data/rest_resource_etags.erl @@ -6,7 +6,7 @@ -export([get_text_plain/2]). init(Req, Opts) -> - {rest, Req, Opts}. + {cowboy_rest, Req, Opts}. generate_etag(Req, State) -> #{type := Type} = cowboy_req:match_qs(Req, [type]), diff --git a/test/http_SUITE_data/rest_simple_resource.erl b/test/http_SUITE_data/rest_simple_resource.erl index 3d2787f..68e1b95 100644 --- a/test/http_SUITE_data/rest_simple_resource.erl +++ b/test/http_SUITE_data/rest_simple_resource.erl @@ -5,7 +5,7 @@ -export([get_text_plain/2]). init(Req, Opts) -> - {rest, Req, Opts}. + {cowboy_rest, Req, Opts}. content_types_provided(Req, State) -> {[{{<<"text">>, <<"plain">>, []}, get_text_plain}], Req, State}. diff --git a/test/ws_SUITE_data/ws_echo.erl b/test/ws_SUITE_data/ws_echo.erl index c4ab406..89ebcb6 100644 --- a/test/ws_SUITE_data/ws_echo.erl +++ b/test/ws_SUITE_data/ws_echo.erl @@ -7,7 +7,7 @@ -export([websocket_info/3]). init(Req, _) -> - {ws, Req, undefined}. + {cowboy_websocket, Req, undefined}. websocket_handle({text, Data}, Req, State) -> {reply, {text, Data}, Req, State}; diff --git a/test/ws_SUITE_data/ws_echo_timer.erl b/test/ws_SUITE_data/ws_echo_timer.erl index 199f02c..a26c332 100644 --- a/test/ws_SUITE_data/ws_echo_timer.erl +++ b/test/ws_SUITE_data/ws_echo_timer.erl @@ -8,7 +8,7 @@ init(Req, _) -> erlang:start_timer(1000, self(), <<"websocket_init">>), - {ws, Req, undefined}. + {cowboy_websocket, Req, undefined}. websocket_handle({text, Data}, Req, State) -> {reply, {text, Data}, Req, State}; diff --git a/test/ws_SUITE_data/ws_init_shutdown.erl b/test/ws_SUITE_data/ws_init_shutdown.erl index 68f96f0..7bce03b 100644 --- a/test/ws_SUITE_data/ws_init_shutdown.erl +++ b/test/ws_SUITE_data/ws_init_shutdown.erl @@ -4,5 +4,5 @@ -export([init/2]). -init(Req, _) -> - {shutdown, cowboy_req:reply(403, Req), undefined}. +init(Req, Opts) -> + {ok, cowboy_req:reply(403, Req), Opts}. diff --git a/test/ws_SUITE_data/ws_send_many.erl b/test/ws_SUITE_data/ws_send_many.erl index 2da82c3..6585ffa 100644 --- a/test/ws_SUITE_data/ws_send_many.erl +++ b/test/ws_SUITE_data/ws_send_many.erl @@ -8,7 +8,7 @@ init(Req, Opts) -> erlang:send_after(10, self(), send_many), - {ws, Req, Opts}. + {cowboy_websocket, Req, Opts}. websocket_handle(_Frame, Req, State) -> {ok, Req, State}. diff --git a/test/ws_SUITE_data/ws_timeout_cancel.erl b/test/ws_SUITE_data/ws_timeout_cancel.erl index 6fcfc43..7376140 100644 --- a/test/ws_SUITE_data/ws_timeout_cancel.erl +++ b/test/ws_SUITE_data/ws_timeout_cancel.erl @@ -8,7 +8,7 @@ init(Req, _) -> erlang:start_timer(500, self(), should_not_cancel_timer), - {ws, Req, undefined, 1000}. + {cowboy_websocket, Req, undefined, 1000}. websocket_handle({text, Data}, Req, State) -> {reply, {text, Data}, Req, State}; diff --git a/test/ws_SUITE_data/ws_timeout_hibernate.erl b/test/ws_SUITE_data/ws_timeout_hibernate.erl index da901d7..15cde66 100644 --- a/test/ws_SUITE_data/ws_timeout_hibernate.erl +++ b/test/ws_SUITE_data/ws_timeout_hibernate.erl @@ -7,7 +7,7 @@ -export([websocket_info/3]). init(Req, _) -> - {ws, Req, undefined, 1000, hibernate}. + {cowboy_websocket, Req, undefined, 1000, hibernate}. websocket_handle(_Frame, Req, State) -> {ok, Req, State, hibernate}. |