mirror of
https://github.com/ninenines/cowboy.git
synced 2025-07-14 20:30:23 +00:00
Unify the init and terminate callbacks
This set of changes is the first step to simplify the writing of handlers, by removing some extraneous callbacks and making others optional. init/3 is now init/2, its first argument being removed. rest_init/2 and rest_terminate/2 have been removed. websocket_init/3 and websocket_terminate/3 have been removed. terminate/3 is now optional. It is called regardless of the type of handler, including rest and websocket. The return value of init/2 changed. It now returns {Mod, Req, Opts} with Mod being either one of the four handler type or a custom module. It can also return extra timeout and hibernate options. The signature for sub protocols has changed, they now receive these extra timeout and hibernate options. Loop handlers are now implemented in cowboy_long_polling, and will be renamed throughout the project in a future commit.
This commit is contained in:
parent
fd37fad592
commit
5ce4c2bfb4
70 changed files with 668 additions and 1015 deletions
18
ROADMAP.md
18
ROADMAP.md
|
@ -20,24 +20,6 @@ A number of backward incompatible changes are planned. These
|
|||
changes are individually small, but together should result
|
||||
in a large improvement in usability.
|
||||
|
||||
### init/terminate unification
|
||||
|
||||
The first argument of the `init/3` function is too rarely used.
|
||||
It will be removed.
|
||||
|
||||
The return value of the `init/2` function will become
|
||||
`{http, Req, State} | {loop, Req, State} | {Module, Req, State}`
|
||||
with `Module` being `cowboy_rest`, `cowboy_websocket` or a
|
||||
user provided module.
|
||||
|
||||
The `rest_init` and `websocket_init` callbacks will be removed
|
||||
as they become unnecessary with the new `init/2` interface.
|
||||
|
||||
Similarly, the `rest_terminate` and `websocket_terminate`
|
||||
callbacks will be removed in favor of a unified `terminate/3`.
|
||||
|
||||
The `terminate/3` callback will become optional.
|
||||
|
||||
### Hooks
|
||||
|
||||
The interface of the `onresponse` hook will change. There has
|
||||
|
|
|
@ -7,36 +7,17 @@ will simply call the three callbacks sequentially.
|
|||
|
||||
:: Initialization
|
||||
|
||||
The first callback, `init/3`, is common to all handlers,
|
||||
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 `ok`.
|
||||
HTTP handlers just return `http`.
|
||||
|
||||
``` erlang
|
||||
init(_Type, Req, _Opts) ->
|
||||
{ok, Req, no_state}.
|
||||
init(Req, Opts) ->
|
||||
{http, Req, Opts}.
|
||||
```
|
||||
|
||||
This function receives the name of the transport and
|
||||
protocol modules used for processing the request.
|
||||
They can be used to quickly dismiss requests. For
|
||||
example the following handler will crash when accessed
|
||||
using TCP instead of SSL.
|
||||
|
||||
``` erlang
|
||||
init({ssl, _}, Req, _Opts) ->
|
||||
{ok, Req, no_state}.
|
||||
```
|
||||
|
||||
This function also receives the options associated with
|
||||
this route that you configured previously. If your
|
||||
handler does not use options, then it is recommended
|
||||
you match the value `[]` directly to quickly detect
|
||||
configuration errors.
|
||||
|
||||
``` erlang
|
||||
init(_Type, Req, []) ->
|
||||
{ok, Req, no_state}.
|
||||
```
|
||||
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
|
||||
|
@ -45,9 +26,9 @@ example, this will crash if the required `lang`
|
|||
option is not found.
|
||||
|
||||
``` erlang
|
||||
init(_Type, Req, Opts) ->
|
||||
{_, _Lang} = lists:keyfind(lang, 1, Opts),
|
||||
{ok, Req, no_state}.
|
||||
init(Req, Opts) ->
|
||||
{_, Lang} = lists:keyfind(lang, 1, Opts),
|
||||
{http, Req, Lang}.
|
||||
```
|
||||
|
||||
If your users are unlikely to figure out the issue
|
||||
|
@ -58,15 +39,15 @@ continue with the handler code, so we use the
|
|||
`shutdown` return value to stop early.
|
||||
|
||||
``` erlang
|
||||
init(_Type, Req, Opts) ->
|
||||
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, no_state};
|
||||
_ ->
|
||||
{ok, Req, no_state}
|
||||
{shutdown, Req2, undefined};
|
||||
{_, Lang} ->
|
||||
{http, Req, Lang}
|
||||
end.
|
||||
```
|
||||
|
||||
|
@ -75,9 +56,9 @@ safely. So we need to pass them onward to the rest of
|
|||
the handler. That's what the third element of the return
|
||||
tuple, the state, is for.
|
||||
|
||||
We recommend that you create a state record for this.
|
||||
The record will make your handler code clearer and
|
||||
will allow you to better use Dialyzer for type checking.
|
||||
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, {
|
||||
|
@ -85,15 +66,25 @@ will allow you to better use Dialyzer for type checking.
|
|||
%% More fields here.
|
||||
}).
|
||||
|
||||
init(_Type, Req, Opts) ->
|
||||
init(Req, Opts) ->
|
||||
{_, Lang} = lists:keyfind(lang, 1, Opts),
|
||||
{ok, Req, #state{lang=Lang}}.
|
||||
{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, wait for it, handle the request.
|
||||
handlers. It's where you handle the request.
|
||||
|
||||
A handle function that does nothing would look like this:
|
||||
|
||||
|
@ -118,8 +109,7 @@ handle(Req, State) ->
|
|||
|
||||
:: Cleaning up
|
||||
|
||||
The third and last callback, `terminate/3`, will most likely
|
||||
be empty in your handler.
|
||||
The third and last callback, `terminate/3`, is optional.
|
||||
|
||||
``` erlang
|
||||
terminate(_Reason, Req, State) ->
|
||||
|
@ -140,6 +130,3 @@ thin however. The use of the process dictionary is discouraged
|
|||
in Erlang code in general. And if you need to use timers, monitors
|
||||
or to receive messages, you are better off with a loop handler,
|
||||
a different kind of handler meant specifically for this use.
|
||||
|
||||
This function is still available should you need it. It will
|
||||
always be called.
|
||||
|
|
|
@ -34,8 +34,8 @@ process enter hibernation until a message is received.
|
|||
This snippet enables the loop handler.
|
||||
|
||||
``` erlang
|
||||
init(_Type, Req, _Opts) ->
|
||||
{loop, Req, undefined_state}.
|
||||
init(Req, Opts) ->
|
||||
{long_polling, Req, Opts}.
|
||||
```
|
||||
|
||||
However it is largely recommended that you set a timeout
|
||||
|
@ -43,8 +43,8 @@ value. The next example sets a timeout value of 30s and
|
|||
also makes the process hibernate.
|
||||
|
||||
``` erlang
|
||||
init(_Type, Req, _Opts) ->
|
||||
{loop, Req, undefined_state, 30000, hibernate}.
|
||||
init(Req, Opts) ->
|
||||
{long_polling, Req, Opts, 30000, hibernate}.
|
||||
```
|
||||
|
||||
:: Receive loop
|
||||
|
@ -94,9 +94,9 @@ a chunk is sent every time a `chunk` message is received,
|
|||
and the loop is stopped by sending an `eof` message.
|
||||
|
||||
``` erlang
|
||||
init(_Type, Req, _Opts) ->
|
||||
init(Req, Opts) ->
|
||||
Req2 = cowboy_req:chunked_reply(200, [], Req),
|
||||
{loop, Req2, undefined_state}.
|
||||
{long_polling, Req2, Opts}.
|
||||
|
||||
info(eof, Req, State) ->
|
||||
{ok, Req, State};
|
||||
|
|
|
@ -267,17 +267,3 @@ rather the one of the machine that connected to the server.
|
|||
``` erlang
|
||||
{IP, Port} = cowboy_req:peer(Req).
|
||||
```
|
||||
|
||||
:: Reducing the memory footprint
|
||||
|
||||
When you are done reading information from the request object
|
||||
and know you are not going to access it anymore, for example
|
||||
when using long-polling or Websocket, you can use the `compact/1`
|
||||
function to remove most of the data from the request object and
|
||||
free memory.
|
||||
|
||||
``` erlang
|
||||
Req2 = cowboy_req:compact(Req).
|
||||
```
|
||||
|
||||
You will still be able to send a reply if needed.
|
||||
|
|
|
@ -8,18 +8,20 @@ The REST handler is the recommended way to handle requests.
|
|||
|
||||
:: Initialization
|
||||
|
||||
First, the `init/3` callback is called. This callback is common
|
||||
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 an `upgrade` tuple.
|
||||
must return a `rest` tuple.
|
||||
|
||||
``` erlang
|
||||
init({tcp, http}, Req, Opts) ->
|
||||
{upgrade, protocol, cowboy_rest}.
|
||||
init(Req, Opts) ->
|
||||
{rest, Req, Opts}.
|
||||
```
|
||||
|
||||
Cowboy will then switch to the REST protocol and start executing
|
||||
the state machine, starting from `rest_init/2` if it's defined,
|
||||
and ending with `rest_terminate/2` also if defined.
|
||||
the state machine.
|
||||
|
||||
After reaching the end of the flowchart, the `terminate/3` callback
|
||||
will be called if it is defined.
|
||||
|
||||
:: Methods
|
||||
|
||||
|
@ -36,28 +38,17 @@ on what other defined callbacks return. The various flowcharts
|
|||
in the next chapter should be a useful to determine which callbacks
|
||||
you need.
|
||||
|
||||
When the request starts being processed, Cowboy will call the
|
||||
`rest_init/2` function if it is defined, with the Req object
|
||||
and the handler options as arguments. This function must return
|
||||
`{ok, Req, State}` where `State` is the handler's state that all
|
||||
subsequent callbacks will receive.
|
||||
All callbacks take two arguments, the Req object and the State,
|
||||
and return a three-element tuple of the form `{Value, Req, State}`.
|
||||
|
||||
At the end of every request, the special callback `rest_terminate/2`
|
||||
will be called if it is defined. It cannot be used to send a reply,
|
||||
and must always return `ok`.
|
||||
|
||||
All other callbacks are resource callbacks. They all take two
|
||||
arguments, the Req object and the State, and return a three-element
|
||||
tuple of the form `{Value, Req, State}`.
|
||||
All callbacks can also return `{halt, Req, State}` to stop execution
|
||||
of the request.
|
||||
|
||||
The following table summarizes the callbacks and their default values.
|
||||
If the callback isn't defined, then the default value will be used.
|
||||
Please look at the flowcharts to find out the result of each return
|
||||
value.
|
||||
|
||||
All callbacks can also return `{halt, Req, State}` to stop execution
|
||||
of the request, at which point `rest_terminate/2` will be called.
|
||||
|
||||
In the following table, "skip" means the callback is entirely skipped
|
||||
if it is undefined, moving directly to the next step. Similarly,
|
||||
"none" means there is no default value for this callback.
|
||||
|
|
|
@ -1,36 +1,61 @@
|
|||
::: Protocol upgrades
|
||||
|
||||
Cowboy features many different handlers, each for different purposes.
|
||||
All handlers have a common entry point: the `init/3` function.
|
||||
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.
|
||||
|
||||
The default handler type is the simple HTTP handler.
|
||||
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.
|
||||
|
||||
To switch to a different protocol, you must perform a protocol
|
||||
upgrade. This is what is done for Websocket and REST and is
|
||||
explained in details in the respective chapters.
|
||||
The following table lists the built-in handler types.
|
||||
|
||||
You can also create your own protocol on top of Cowboy and use
|
||||
the protocol upgrade mechanism to switch to it.
|
||||
|| 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
|
||||
|
||||
For example, if you create the `my_protocol` module implementing
|
||||
the `cowboy_sub_protocol` behavior, then you can upgrade to it
|
||||
by simply returning the module name from `init/3`.
|
||||
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(_, _, _Opts) ->
|
||||
{upgrade, protocol, my_protocol}.
|
||||
init(Req, Opts) ->
|
||||
{my_protocol, Req, Opts}.
|
||||
```
|
||||
|
||||
The `cowboy_sub_protocol` behavior only requires one callback,
|
||||
`upgrade/4`. It receives the Req object, the middleware environment,
|
||||
and the handler and options for this request. This is the same
|
||||
module as the `init/3` function and the same options that were
|
||||
passed to it.
|
||||
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
|
||||
upgrade(Req, Env, Handler, HandlerOpts) ->
|
||||
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.
|
||||
|
|
|
@ -11,53 +11,18 @@ socket communication and decoding/encoding of frames.
|
|||
|
||||
:: Initialization
|
||||
|
||||
First, the `init/3` callback is called. This callback is common
|
||||
First, the `init/2` callback is called. This callback is common
|
||||
to all handlers. To establish a Websocket connection, this function
|
||||
must return an `upgrade` tuple.
|
||||
must return a `ws` tuple.
|
||||
|
||||
``` erlang
|
||||
init(_, Req, Opts) ->
|
||||
{upgrade, protocol, cowboy_websocket}.
|
||||
```
|
||||
|
||||
It is also possible to return an update Req object and options
|
||||
using the longer form of this tuple.
|
||||
|
||||
``` erlang
|
||||
init(_Type, Req, Opts) ->
|
||||
{upgrade, protocol, cowboy_websocket, Req, Opts}.
|
||||
init(Req, Opts) ->
|
||||
{ws, Req, Opts}.
|
||||
```
|
||||
|
||||
Upon receiving this tuple, Cowboy will switch to the code
|
||||
that handles Websocket connections. It does not immediately
|
||||
perform the handshake however. First, it calls the `websocket_init/3`
|
||||
callback.
|
||||
|
||||
This function must be used to initialize the state, and can
|
||||
also be used to register the process, start a timer, etc.
|
||||
As long as the function returns an `ok` tuple, then Cowboy
|
||||
performs the Websocket handshake.
|
||||
|
||||
``` erlang
|
||||
websocket_init(_Type, Req, _Opts) ->
|
||||
{ok, Req, #state{}}.
|
||||
```
|
||||
|
||||
A `shutdown` tuple can be returned to refuse to perform the
|
||||
handshake. When doing so, Cowboy will send a `400 Bad Request`
|
||||
response to the client and close the connection.
|
||||
|
||||
``` erlang
|
||||
websocket_init(_Type, Req, _Opts) ->
|
||||
{shutdown, Req}.
|
||||
```
|
||||
|
||||
It is also possible to perform a `cowboy_req:reply/{2,3,4}`
|
||||
before returning a `shutdown` tuple, allowing you to override
|
||||
the response sent back to the client.
|
||||
|
||||
Note that browser support for handling Websocket connection
|
||||
failures may vary.
|
||||
that handles Websocket connections and perform the handshake
|
||||
immediately.
|
||||
|
||||
If the sec-websocket-protocol header was sent with the request
|
||||
for establishing a Websocket connection, then the Websocket
|
||||
|
@ -66,7 +31,7 @@ back to the client, otherwise the client might decide to close
|
|||
the connection, assuming no correct subprotocol was found.
|
||||
|
||||
``` erlang
|
||||
websocket_init(_Type, Req, _Opts) ->
|
||||
init(Req, _Opts) ->
|
||||
case cowboy_req:parse_header(<<"sec-websocket-protocol">>, Req) of
|
||||
undefined ->
|
||||
{ok, Req, #state{}};
|
||||
|
@ -77,15 +42,15 @@ websocket_init(_Type, Req, _Opts) ->
|
|||
<<"mychat2">>, Req),
|
||||
{ok, Req2, #state{}};
|
||||
false ->
|
||||
{shutdown, Req}
|
||||
{shutdown, Req, undefined}
|
||||
end
|
||||
end.
|
||||
```
|
||||
|
||||
It is not recommended to wait too long inside the `websocket_init/3`
|
||||
It is not recommended to wait too long inside the `init/2`
|
||||
function. Any extra initialization may be done after returning by
|
||||
sending yourself a message before doing anything. Any message sent
|
||||
to `self()` from `websocket_init/3` is guaranteed to arrive before
|
||||
to `self()` from `init/2` is guaranteed to arrive before
|
||||
any frames from the client.
|
||||
|
||||
It is also very easy to ensure that this message arrives before
|
||||
|
@ -93,10 +58,10 @@ any message from other processes by sending it before registering
|
|||
or enabling timers.
|
||||
|
||||
``` erlang
|
||||
websocket_init(_Type, Req, _Opts) ->
|
||||
init(Req, _Opts) ->
|
||||
self() ! post_init,
|
||||
%% Register process here...
|
||||
{ok, Req, #state{}}.
|
||||
{ws, Req, #state{}}.
|
||||
|
||||
websocket_info(post_init, Req, State) ->
|
||||
%% Perform post_init initialization here...
|
||||
|
@ -195,8 +160,8 @@ leave the process alive forever.
|
|||
A good timeout value is 60 seconds.
|
||||
|
||||
``` erlang
|
||||
websocket_init(_Type, Req, _Opts) ->
|
||||
{ok, Req, #state{}, 60000}.
|
||||
init(Req, _Opts) ->
|
||||
{ws, Req, #state{}, 60000}.
|
||||
```
|
||||
|
||||
This value cannot be changed once it is set. It defaults to
|
||||
|
|
|
@ -713,13 +713,3 @@ Set a response header.
|
|||
|
||||
You should use `set_resp_cookie/4` instead of this function
|
||||
to set cookies.
|
||||
|
||||
:: Misc. exports
|
||||
|
||||
: compact(Req) -> Req2
|
||||
|
||||
Remove any non-essential data from the Req object.
|
||||
|
||||
Long-lived connections usually only need to manipulate the
|
||||
Req object at initialization. Compacting allows saving up
|
||||
memory by discarding extraneous information.
|
||||
|
|
|
@ -3,12 +3,11 @@
|
|||
%% @doc Chunked hello world handler.
|
||||
-module(toppage_handler).
|
||||
|
||||
-export([init/3]).
|
||||
-export([init/2]).
|
||||
-export([handle/2]).
|
||||
-export([terminate/3]).
|
||||
|
||||
init(_Transport, Req, []) ->
|
||||
{ok, Req, undefined}.
|
||||
init(Req, Opts) ->
|
||||
{http, Req, Opts}.
|
||||
|
||||
handle(Req, State) ->
|
||||
Req2 = cowboy_req:chunked_reply(200, Req),
|
||||
|
@ -18,6 +17,3 @@ handle(Req, State) ->
|
|||
timer:sleep(1000),
|
||||
cowboy_req:chunk("Chunked!\r\n", Req2),
|
||||
{ok, Req2, State}.
|
||||
|
||||
terminate(_Reason, _Req, _State) ->
|
||||
ok.
|
||||
|
|
|
@ -3,12 +3,11 @@
|
|||
%% @doc Compress response handler.
|
||||
-module(toppage_handler).
|
||||
|
||||
-export([init/3]).
|
||||
-export([init/2]).
|
||||
-export([handle/2]).
|
||||
-export([terminate/3]).
|
||||
|
||||
init(_Transport, Req, []) ->
|
||||
{ok, Req, undefined}.
|
||||
init(Req, Opts) ->
|
||||
{http, Req, Opts}.
|
||||
|
||||
handle(Req, State) ->
|
||||
BigBody =
|
||||
|
@ -26,6 +25,3 @@ 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}.
|
||||
|
||||
terminate(_Reason, _Req, _State) ->
|
||||
ok.
|
||||
|
|
|
@ -3,12 +3,11 @@
|
|||
%% @doc Cookie handler.
|
||||
-module(toppage_handler).
|
||||
|
||||
-export([init/3]).
|
||||
-export([init/2]).
|
||||
-export([handle/2]).
|
||||
-export([terminate/3]).
|
||||
|
||||
init(_Transport, Req, []) ->
|
||||
{ok, Req, undefined}.
|
||||
init(Req, Opts) ->
|
||||
{http, Req, Opts}.
|
||||
|
||||
handle(Req, State) ->
|
||||
NewValue = integer_to_list(random:uniform(1000000)),
|
||||
|
@ -24,6 +23,3 @@ handle(Req, State) ->
|
|||
[{<<"content-type">>, <<"text/html">>}],
|
||||
Body, Req2),
|
||||
{ok, Req3, State}.
|
||||
|
||||
terminate(_Reason, _Req, _State) ->
|
||||
ok.
|
||||
|
|
|
@ -3,12 +3,11 @@
|
|||
%% @doc GET echo handler.
|
||||
-module(toppage_handler).
|
||||
|
||||
-export([init/3]).
|
||||
-export([init/2]).
|
||||
-export([handle/2]).
|
||||
-export([terminate/3]).
|
||||
|
||||
init(_Transport, Req, []) ->
|
||||
{ok, Req, undefined}.
|
||||
init(Req, Opts) ->
|
||||
{http, Req, Opts}.
|
||||
|
||||
handle(Req, State) ->
|
||||
Method = cowboy_req:method(Req),
|
||||
|
@ -25,6 +24,3 @@ echo(<<"GET">>, Echo, Req) ->
|
|||
echo(_, _, Req) ->
|
||||
%% Method not allowed.
|
||||
cowboy_req:reply(405, Req).
|
||||
|
||||
terminate(_Reason, _Req, _State) ->
|
||||
ok.
|
||||
|
|
|
@ -3,12 +3,11 @@
|
|||
%% @doc POST echo handler.
|
||||
-module(toppage_handler).
|
||||
|
||||
-export([init/3]).
|
||||
-export([init/2]).
|
||||
-export([handle/2]).
|
||||
-export([terminate/3]).
|
||||
|
||||
init(_Transport, Req, []) ->
|
||||
{ok, Req, undefined}.
|
||||
init(Req, Opts) ->
|
||||
{http, Req, Opts}.
|
||||
|
||||
handle(Req, State) ->
|
||||
Method = cowboy_req:method(Req),
|
||||
|
@ -32,6 +31,3 @@ echo(Echo, Req) ->
|
|||
cowboy_req:reply(200, [
|
||||
{<<"content-type">>, <<"text/plain; charset=utf-8">>}
|
||||
], Echo, Req).
|
||||
|
||||
terminate(_Reason, _Req, _State) ->
|
||||
ok.
|
||||
|
|
|
@ -3,24 +3,20 @@
|
|||
%% @doc EventSource emitter.
|
||||
-module(eventsource_handler).
|
||||
|
||||
-export([init/3]).
|
||||
-export([init/2]).
|
||||
-export([info/3]).
|
||||
-export([terminate/3]).
|
||||
|
||||
init(_Transport, Req, []) ->
|
||||
init(Req, Opts) ->
|
||||
Headers = [{<<"content-type">>, <<"text/event-stream">>}],
|
||||
Req2 = cowboy_req:chunked_reply(200, Headers, Req),
|
||||
erlang:send_after(1000, self(), {message, "Tick"}),
|
||||
{loop, Req2, undefined, 5000}.
|
||||
{long_polling, 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}.
|
||||
|
||||
terminate(_Reason, _Req, _State) ->
|
||||
ok.
|
||||
|
||||
id() ->
|
||||
{Mega, Sec, Micro} = erlang:now(),
|
||||
Id = (Mega * 1000000 + Sec) * 1000000 + Micro,
|
||||
|
|
|
@ -3,18 +3,14 @@
|
|||
%% @doc Hello world handler.
|
||||
-module(toppage_handler).
|
||||
|
||||
-export([init/3]).
|
||||
-export([init/2]).
|
||||
-export([handle/2]).
|
||||
-export([terminate/3]).
|
||||
|
||||
init(_Type, Req, []) ->
|
||||
{ok, Req, undefined}.
|
||||
init(Req, Opts) ->
|
||||
{http, Req, Opts}.
|
||||
|
||||
handle(Req, State) ->
|
||||
Req2 = cowboy_req:reply(200, [
|
||||
{<<"content-type">>, <<"text/plain">>}
|
||||
], <<"Hello world!">>, Req),
|
||||
{ok, Req2, State}.
|
||||
|
||||
terminate(_Reason, _Req, _State) ->
|
||||
ok.
|
||||
|
|
|
@ -3,13 +3,13 @@
|
|||
%% @doc Handler with basic HTTP authorization.
|
||||
-module(toppage_handler).
|
||||
|
||||
-export([init/3]).
|
||||
-export([init/2]).
|
||||
-export([content_types_provided/2]).
|
||||
-export([is_authorized/2]).
|
||||
-export([to_text/2]).
|
||||
|
||||
init(_Transport, _Req, []) ->
|
||||
{upgrade, protocol, cowboy_rest}.
|
||||
init(Req, Opts) ->
|
||||
{rest, Req, Opts}.
|
||||
|
||||
is_authorized(Req, State) ->
|
||||
case cowboy_req:parse_header(<<"authorization">>, Req) of
|
||||
|
|
|
@ -3,14 +3,14 @@
|
|||
%% @doc Hello world handler.
|
||||
-module(toppage_handler).
|
||||
|
||||
-export([init/3]).
|
||||
-export([init/2]).
|
||||
-export([content_types_provided/2]).
|
||||
-export([hello_to_html/2]).
|
||||
-export([hello_to_json/2]).
|
||||
-export([hello_to_text/2]).
|
||||
|
||||
init(_Transport, _Req, []) ->
|
||||
{upgrade, protocol, cowboy_rest}.
|
||||
init(Req, Opts) ->
|
||||
{rest, Req, Opts}.
|
||||
|
||||
content_types_provided(Req, State) ->
|
||||
{[
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
-module(toppage_handler).
|
||||
|
||||
%% Standard callbacks.
|
||||
-export([init/3]).
|
||||
-export([init/2]).
|
||||
-export([allowed_methods/2]).
|
||||
-export([content_types_provided/2]).
|
||||
-export([content_types_accepted/2]).
|
||||
|
@ -15,11 +15,9 @@
|
|||
-export([paste_html/2]).
|
||||
-export([paste_text/2]).
|
||||
|
||||
init(_Transport, _Req, []) ->
|
||||
% For the random number generator:
|
||||
{X, Y, Z} = now(),
|
||||
random:seed(X, Y, Z),
|
||||
{upgrade, protocol, cowboy_rest}.
|
||||
init(Req, Opts) ->
|
||||
random:seed(now()),
|
||||
{rest, Req, Opts}.
|
||||
|
||||
allowed_methods(Req, State) ->
|
||||
{[<<"GET">>, <<"POST">>], Req, State}.
|
||||
|
|
|
@ -3,16 +3,12 @@
|
|||
%% @doc Streaming handler.
|
||||
-module(toppage_handler).
|
||||
|
||||
-export([init/3]).
|
||||
-export([rest_init/2]).
|
||||
-export([init/2]).
|
||||
-export([content_types_provided/2]).
|
||||
-export([streaming_csv/2]).
|
||||
|
||||
init(_Transport, _Req, _Table) ->
|
||||
{upgrade, protocol, cowboy_rest}.
|
||||
|
||||
rest_init(Req, Table) ->
|
||||
{ok, Req, Table}.
|
||||
init(Req, Table) ->
|
||||
{rest, Req, Table}.
|
||||
|
||||
content_types_provided(Req, State) ->
|
||||
{[
|
||||
|
|
|
@ -3,18 +3,14 @@
|
|||
%% @doc Hello world handler.
|
||||
-module(toppage_handler).
|
||||
|
||||
-export([init/3]).
|
||||
-export([init/2]).
|
||||
-export([handle/2]).
|
||||
-export([terminate/3]).
|
||||
|
||||
init(_Transport, Req, []) ->
|
||||
{ok, Req, undefined}.
|
||||
init(Req, Opts) ->
|
||||
{http, Req, Opts}.
|
||||
|
||||
handle(Req, State) ->
|
||||
Req2 = cowboy_req:reply(200, [
|
||||
{<<"content-type">>, <<"text/plain">>}
|
||||
], <<"Hello world!">>, Req),
|
||||
{ok, Req2, State}.
|
||||
|
||||
terminate(_Reason, _Req, _State) ->
|
||||
ok.
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
%% Feel free to use, reuse and abuse the code in this file.
|
||||
|
||||
%% @doc Upload handler.
|
||||
-module(upload_handler).
|
||||
-behaviour(cowboy_http_handler).
|
||||
|
||||
-export([init/3]).
|
||||
-export([init/2]).
|
||||
-export([handle/2]).
|
||||
-export([terminate/3]).
|
||||
|
||||
init(_, Req, _Opts) ->
|
||||
{ok, Req, undefined}.
|
||||
init(Req, Opts) ->
|
||||
{http, Req, Opts}.
|
||||
|
||||
handle(Req, State) ->
|
||||
{ok, Headers, Req2} = cowboy_req:part(Req),
|
||||
|
@ -16,6 +17,3 @@ handle(Req, State) ->
|
|||
io:format("Received file ~p of content-type ~p as follow:~n~p~n~n",
|
||||
[Filename, ContentType, Data]),
|
||||
{ok, Req3, State}.
|
||||
|
||||
terminate(_Reason, _Req, _State) ->
|
||||
ok.
|
||||
|
|
|
@ -4,8 +4,7 @@
|
|||
-module(directory_handler).
|
||||
|
||||
%% REST Callbacks
|
||||
-export([init/3]).
|
||||
-export([rest_init/2]).
|
||||
-export([init/2]).
|
||||
-export([allowed_methods/2]).
|
||||
-export([resource_exists/2]).
|
||||
-export([content_types_provided/2]).
|
||||
|
@ -14,11 +13,8 @@
|
|||
-export([list_json/2]).
|
||||
-export([list_html/2]).
|
||||
|
||||
init(_Transport, _Req, _Paths) ->
|
||||
{upgrade, protocol, cowboy_rest}.
|
||||
|
||||
rest_init(Req, Paths) ->
|
||||
{ok, Req, Paths}.
|
||||
init(Req, Paths) ->
|
||||
{rest, Req, Paths}.
|
||||
|
||||
allowed_methods(Req, State) ->
|
||||
{[<<"GET">>], Req, State}.
|
||||
|
|
|
@ -1,18 +1,12 @@
|
|||
-module(ws_handler).
|
||||
-behaviour(cowboy_websocket_handler).
|
||||
|
||||
-export([init/3]).
|
||||
-export([websocket_init/3]).
|
||||
-export([init/2]).
|
||||
-export([websocket_handle/3]).
|
||||
-export([websocket_info/3]).
|
||||
-export([websocket_terminate/3]).
|
||||
|
||||
init({tcp, http}, _Req, _Opts) ->
|
||||
{upgrade, protocol, cowboy_websocket}.
|
||||
|
||||
websocket_init(_TransportName, Req, _Opts) ->
|
||||
init(Req, Opts) ->
|
||||
erlang:start_timer(1000, self(), <<"Hello!">>),
|
||||
{ok, Req, undefined_state}.
|
||||
{ws, Req, Opts}.
|
||||
|
||||
websocket_handle({text, Msg}, Req, State) ->
|
||||
{reply, {text, << "That's what she said! ", Msg/binary >>}, Req, State};
|
||||
|
@ -24,6 +18,3 @@ websocket_info({timeout, _Ref, Msg}, Req, State) ->
|
|||
{reply, {text, Msg}, Req, State};
|
||||
websocket_info(_Info, Req, State) ->
|
||||
{ok, Req, State}.
|
||||
|
||||
websocket_terminate(_Reason, _Req, _State) ->
|
||||
ok.
|
||||
|
|
|
@ -17,287 +17,86 @@
|
|||
%% Execute the handler given by the <em>handler</em> and <em>handler_opts</em>
|
||||
%% environment values. The result of this execution is added to the
|
||||
%% environment under the <em>result</em> value.
|
||||
%%
|
||||
%% When using loop handlers, we are receiving data from the socket because we
|
||||
%% want to know when the socket gets closed. This is generally not an issue
|
||||
%% because these kinds of requests are generally not pipelined, and don't have
|
||||
%% a body. If they do have a body, this body is often read in the
|
||||
%% <em>init/3</em> callback and this is no problem. Otherwise, this data
|
||||
%% accumulates in a buffer until we reach a certain threshold of 5000 bytes
|
||||
%% 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_handler).
|
||||
-behaviour(cowboy_middleware).
|
||||
|
||||
-export([execute/2]).
|
||||
-export([handler_loop/4]).
|
||||
-export([terminate/5]).
|
||||
|
||||
-record(state, {
|
||||
env :: cowboy_middleware:env(),
|
||||
hibernate = false :: boolean(),
|
||||
loop_buffer_size = 0 :: non_neg_integer(),
|
||||
loop_max_buffer = 5000 :: non_neg_integer() | infinity,
|
||||
loop_timeout = infinity :: timeout(),
|
||||
loop_timeout_ref = undefined :: undefined | reference(),
|
||||
resp_sent = false :: boolean()
|
||||
}).
|
||||
|
||||
-spec execute(Req, Env)
|
||||
-> {ok, Req, Env} | {suspend, ?MODULE, handler_loop, [any()]}
|
||||
-spec execute(Req, Env) -> {ok, Req, Env}
|
||||
when Req::cowboy_req:req(), Env::cowboy_middleware:env().
|
||||
execute(Req, Env) ->
|
||||
{_, Handler} = lists:keyfind(handler, 1, Env),
|
||||
{_, HandlerOpts} = lists:keyfind(handler_opts, 1, Env),
|
||||
MaxBuffer = case lists:keyfind(loop_max_buffer, 1, Env) of
|
||||
false -> 5000;
|
||||
{_, MaxBuffer0} -> MaxBuffer0
|
||||
end,
|
||||
handler_init(Req, #state{env=Env, loop_max_buffer=MaxBuffer},
|
||||
Handler, HandlerOpts).
|
||||
|
||||
-spec handler_init(Req, #state{}, module(), any())
|
||||
-> {ok, Req, cowboy_middleware:env()} | {suspend, module(), atom(), [any()]}
|
||||
when Req::cowboy_req:req().
|
||||
handler_init(Req, State, Handler, HandlerOpts) ->
|
||||
Transport = cowboy_req:get(transport, Req),
|
||||
try Handler:init({Transport:name(), http}, Req, HandlerOpts) of
|
||||
{ok, Req2, HandlerState} ->
|
||||
handler_handle(Req2, State, Handler, HandlerState);
|
||||
{loop, Req2, HandlerState} ->
|
||||
handler_after_callback(Req2, State, Handler, HandlerState);
|
||||
{loop, Req2, HandlerState, hibernate} ->
|
||||
handler_after_callback(Req2, State#state{hibernate=true},
|
||||
Handler, HandlerState);
|
||||
{loop, Req2, HandlerState, Timeout} ->
|
||||
State2 = handler_loop_timeout(State#state{loop_timeout=Timeout}),
|
||||
handler_after_callback(Req2, State2, Handler, HandlerState);
|
||||
{loop, Req2, HandlerState, Timeout, hibernate} ->
|
||||
State2 = handler_loop_timeout(State#state{
|
||||
hibernate=true, loop_timeout=Timeout}),
|
||||
handler_after_callback(Req2, State2, Handler, HandlerState);
|
||||
{shutdown, Req2, HandlerState} ->
|
||||
terminate_request(Req2, State, Handler, HandlerState,
|
||||
{normal, shutdown});
|
||||
{upgrade, protocol, Module} ->
|
||||
upgrade_protocol(Req, State, Handler, HandlerOpts, Module);
|
||||
{upgrade, protocol, Module, Req2, HandlerOpts2} ->
|
||||
upgrade_protocol(Req2, State, Handler, HandlerOpts2, Module)
|
||||
try Handler:init(Req, HandlerOpts) of
|
||||
{http, Req2, State} ->
|
||||
handle(Req2, Env, Handler, State);
|
||||
{shutdown, Req2, State} ->
|
||||
terminate(Req2, Env, Handler, State, {normal, shutdown});
|
||||
{Mod, Req2, State} ->
|
||||
upgrade(Req2, Env, Handler, State, infinity, run, Mod);
|
||||
{Mod, Req2, State, hibernate} ->
|
||||
upgrade(Req2, Env, Handler, State, infinity, hibernate, Mod);
|
||||
{Mod, Req2, State, Timeout} ->
|
||||
upgrade(Req2, Env, Handler, State, Timeout, run, Mod);
|
||||
{Mod, Req2, State, Timeout, hibernate} ->
|
||||
upgrade(Req2, Env, Handler, State, Timeout, hibernate, Mod)
|
||||
catch Class:Reason ->
|
||||
Stacktrace = erlang:get_stacktrace(),
|
||||
cowboy_req:maybe_reply(Stacktrace, Req),
|
||||
erlang:Class([
|
||||
{reason, Reason},
|
||||
{mfa, {Handler, init, 3}},
|
||||
{mfa, {Handler, init, 2}},
|
||||
{stacktrace, Stacktrace},
|
||||
{req, cowboy_req:to_list(Req)},
|
||||
{opts, HandlerOpts}
|
||||
])
|
||||
end.
|
||||
|
||||
-spec upgrade_protocol(Req, #state{}, module(), any(), module())
|
||||
-> {ok, Req, Env}
|
||||
| {suspend, module(), atom(), any()}
|
||||
| {halt, Req}
|
||||
when Req::cowboy_req:req(), Env::cowboy_middleware:env().
|
||||
upgrade_protocol(Req, #state{env=Env},
|
||||
Handler, HandlerOpts, Module) ->
|
||||
Module:upgrade(Req, Env, Handler, HandlerOpts).
|
||||
|
||||
-spec handler_handle(Req, #state{}, module(), any())
|
||||
-> {ok, Req, cowboy_middleware:env()} when Req::cowboy_req:req().
|
||||
handler_handle(Req, State, Handler, HandlerState) ->
|
||||
try Handler:handle(Req, HandlerState) of
|
||||
{ok, Req2, HandlerState2} ->
|
||||
terminate_request(Req2, State, Handler, HandlerState2,
|
||||
{normal, shutdown})
|
||||
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),
|
||||
handler_terminate(Req, Handler, HandlerState, Reason),
|
||||
_ = terminate(Req, Env, Handler, State, Reason),
|
||||
erlang:Class([
|
||||
{reason, Reason},
|
||||
{mfa, {Handler, handle, 2}},
|
||||
{stacktrace, Stacktrace},
|
||||
{req, cowboy_req:to_list(Req)},
|
||||
{state, HandlerState}
|
||||
{state, State}
|
||||
])
|
||||
end.
|
||||
|
||||
%% Update the state if the response was sent in the callback.
|
||||
-spec handler_after_callback(Req, #state{}, module(), any())
|
||||
-> {ok, Req, cowboy_middleware:env()} | {suspend, module(), atom(), [any()]}
|
||||
when Req::cowboy_req:req().
|
||||
handler_after_callback(Req, State=#state{resp_sent=false}, Handler,
|
||||
HandlerState) ->
|
||||
receive
|
||||
{cowboy_req, resp_sent} ->
|
||||
handler_before_loop(Req, State#state{resp_sent=true}, Handler,
|
||||
HandlerState)
|
||||
after 0 ->
|
||||
handler_before_loop(Req, State, Handler, HandlerState)
|
||||
end;
|
||||
handler_after_callback(Req, State, Handler, HandlerState) ->
|
||||
handler_before_loop(Req, State, Handler, HandlerState).
|
||||
|
||||
-spec handler_before_loop(Req, #state{}, module(), any())
|
||||
-> {ok, Req, cowboy_middleware:env()} | {suspend, module(), atom(), [any()]}
|
||||
when Req::cowboy_req:req().
|
||||
handler_before_loop(Req, State=#state{hibernate=true}, Handler, HandlerState) ->
|
||||
[Socket, Transport] = cowboy_req:get([socket, transport], Req),
|
||||
Transport:setopts(Socket, [{active, once}]),
|
||||
{suspend, ?MODULE, handler_loop,
|
||||
[Req, State#state{hibernate=false}, Handler, HandlerState]};
|
||||
handler_before_loop(Req, State, Handler, HandlerState) ->
|
||||
[Socket, Transport] = cowboy_req:get([socket, transport], Req),
|
||||
Transport:setopts(Socket, [{active, once}]),
|
||||
handler_loop(Req, State, Handler, HandlerState).
|
||||
|
||||
%% Almost the same code can be found in cowboy_websocket.
|
||||
-spec handler_loop_timeout(#state{}) -> #state{}.
|
||||
handler_loop_timeout(State=#state{loop_timeout=infinity}) ->
|
||||
State#state{loop_timeout_ref=undefined};
|
||||
handler_loop_timeout(State=#state{loop_timeout=Timeout,
|
||||
loop_timeout_ref=PrevRef}) ->
|
||||
_ = case PrevRef of
|
||||
undefined -> ignore;
|
||||
PrevRef -> erlang:cancel_timer(PrevRef)
|
||||
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,
|
||||
TRef = erlang:start_timer(Timeout, self(), ?MODULE),
|
||||
State#state{loop_timeout_ref=TRef}.
|
||||
Mod2:upgrade(Req, Env, Handler, State, Timeout, Hibernate).
|
||||
|
||||
-spec handler_loop(Req, #state{}, module(), any())
|
||||
-> {ok, Req, cowboy_middleware:env()} | {suspend, module(), atom(), [any()]}
|
||||
when Req::cowboy_req:req().
|
||||
handler_loop(Req, State=#state{loop_buffer_size=NbBytes,
|
||||
loop_max_buffer=Threshold, loop_timeout_ref=TRef,
|
||||
resp_sent=RespSent}, Handler, HandlerState) ->
|
||||
[Socket, Transport] = cowboy_req:get([socket, transport], Req),
|
||||
{OK, Closed, Error} = Transport:messages(),
|
||||
receive
|
||||
{OK, Socket, Data} ->
|
||||
NbBytes2 = NbBytes + byte_size(Data),
|
||||
if NbBytes2 > Threshold ->
|
||||
_ = handler_terminate(Req, Handler, HandlerState,
|
||||
{error, overflow}),
|
||||
_ = if RespSent -> ok; true ->
|
||||
cowboy_req:reply(500, Req)
|
||||
end,
|
||||
exit(normal);
|
||||
-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
|
||||
true ->
|
||||
Req2 = cowboy_req:append_buffer(Data, Req),
|
||||
State2 = handler_loop_timeout(State#state{
|
||||
loop_buffer_size=NbBytes2}),
|
||||
handler_before_loop(Req2, State2, Handler, HandlerState)
|
||||
end;
|
||||
{Closed, Socket} ->
|
||||
terminate_request(Req, State, Handler, HandlerState,
|
||||
{error, closed});
|
||||
{Error, Socket, Reason} ->
|
||||
terminate_request(Req, State, Handler, HandlerState,
|
||||
{error, Reason});
|
||||
{timeout, TRef, ?MODULE} ->
|
||||
handler_after_loop(Req, State, Handler, HandlerState,
|
||||
{normal, timeout});
|
||||
{timeout, OlderTRef, ?MODULE} when is_reference(OlderTRef) ->
|
||||
handler_loop(Req, State, Handler, HandlerState);
|
||||
Message ->
|
||||
%% We set the socket back to {active, false} mode in case
|
||||
%% the handler is going to call recv. We also flush any
|
||||
%% data received after that and put it into the buffer.
|
||||
%% We do not check the size here, if data keeps coming
|
||||
%% we'll error out on the next packet received.
|
||||
Transport:setopts(Socket, [{active, false}]),
|
||||
Req2 = receive {OK, Socket, Data} ->
|
||||
cowboy_req:append_buffer(Data, Req)
|
||||
after 0 ->
|
||||
Req
|
||||
end,
|
||||
handler_call(Req2, State, Handler, HandlerState, Message)
|
||||
end.
|
||||
|
||||
-spec handler_call(Req, #state{}, module(), any(), any())
|
||||
-> {ok, Req, cowboy_middleware:env()} | {suspend, module(), atom(), [any()]}
|
||||
when Req::cowboy_req:req().
|
||||
handler_call(Req, State=#state{resp_sent=RespSent},
|
||||
Handler, HandlerState, Message) ->
|
||||
try Handler:info(Message, Req, HandlerState) of
|
||||
{ok, Req2, HandlerState2} ->
|
||||
handler_after_loop(Req2, State, Handler, HandlerState2,
|
||||
{normal, shutdown});
|
||||
{loop, Req2, HandlerState2} ->
|
||||
handler_after_callback(Req2, State, Handler, HandlerState2);
|
||||
{loop, Req2, HandlerState2, hibernate} ->
|
||||
handler_after_callback(Req2, State#state{hibernate=true},
|
||||
Handler, HandlerState2)
|
||||
catch Class:Reason ->
|
||||
Stacktrace = erlang:get_stacktrace(),
|
||||
if RespSent -> ok; true ->
|
||||
cowboy_req:maybe_reply(Stacktrace, Req)
|
||||
end,
|
||||
handler_terminate(Req, Handler, HandlerState, Reason),
|
||||
erlang:Class([
|
||||
{reason, Reason},
|
||||
{mfa, {Handler, info, 3}},
|
||||
{stacktrace, Stacktrace},
|
||||
{req, cowboy_req:to_list(Req)},
|
||||
{state, HandlerState}
|
||||
])
|
||||
end.
|
||||
|
||||
%% It is sometimes important to make a socket passive as it was initially
|
||||
%% and as it is expected to be by cowboy_protocol, right after we're done
|
||||
%% with loop handling. The browser may freely pipeline a bunch of requests
|
||||
%% if previous one was, say, a JSONP long-polling request.
|
||||
-spec handler_after_loop(Req, #state{}, module(), any(),
|
||||
{normal, timeout | shutdown} | {error, atom()}) ->
|
||||
{ok, Req, cowboy_middleware:env()} when Req::cowboy_req:req().
|
||||
handler_after_loop(Req, State, Handler, HandlerState, Reason) ->
|
||||
[Socket, Transport] = cowboy_req:get([socket, transport], Req),
|
||||
Transport:setopts(Socket, [{active, false}]),
|
||||
{OK, _Closed, _Error} = Transport:messages(),
|
||||
Req2 = receive
|
||||
{OK, Socket, Data} ->
|
||||
cowboy_req:append_buffer(Data, Req)
|
||||
after 0 ->
|
||||
Req
|
||||
end,
|
||||
terminate_request(Req2, State, Handler, HandlerState, Reason).
|
||||
|
||||
-spec terminate_request(Req, #state{}, module(), any(),
|
||||
{normal, timeout | shutdown} | {error, atom()}) ->
|
||||
{ok, Req, cowboy_middleware:env()} when Req::cowboy_req:req().
|
||||
terminate_request(Req, #state{env=Env, loop_timeout_ref=TRef},
|
||||
Handler, HandlerState, Reason) ->
|
||||
HandlerRes = handler_terminate(Req, Handler, HandlerState, Reason),
|
||||
_ = case TRef of
|
||||
undefined -> ignore;
|
||||
TRef -> erlang:cancel_timer(TRef)
|
||||
end,
|
||||
flush_timeouts(),
|
||||
{ok, Req, [{result, HandlerRes}|Env]}.
|
||||
|
||||
-spec handler_terminate(cowboy_req:req(), module(), any(),
|
||||
{normal, timeout | shutdown} | {error, atom()}) -> ok.
|
||||
handler_terminate(Req, Handler, HandlerState, Reason) ->
|
||||
try
|
||||
Handler:terminate(Reason, cowboy_req:lock(Req), HandlerState)
|
||||
Handler:terminate(Reason, cowboy_req:lock(Req), State)
|
||||
catch Class:Reason2 ->
|
||||
erlang:Class([
|
||||
{reason, Reason2},
|
||||
{mfa, {Handler, terminate, 3}},
|
||||
{stacktrace, erlang:get_stacktrace()},
|
||||
{req, cowboy_req:to_list(Req)},
|
||||
{state, HandlerState},
|
||||
{state, State},
|
||||
{terminate_reason, Reason}
|
||||
])
|
||||
end.
|
||||
|
||||
-spec flush_timeouts() -> ok.
|
||||
flush_timeouts() ->
|
||||
receive
|
||||
{timeout, TRef, ?MODULE} when is_reference(TRef) ->
|
||||
flush_timeouts()
|
||||
after 0 ->
|
||||
end;
|
||||
false ->
|
||||
ok
|
||||
end.
|
||||
end,
|
||||
{ok, Req, [{result, Result}|Env]}.
|
||||
|
|
|
@ -16,22 +16,21 @@
|
|||
|
||||
-type opts() :: any().
|
||||
-type state() :: any().
|
||||
-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()}.
|
||||
%% @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({atom(), http}, Req, opts())
|
||||
-> {ok, Req, state()}
|
||||
| {loop, Req, state()}
|
||||
| {loop, Req, state(), hibernate}
|
||||
| {loop, Req, state(), timeout()}
|
||||
| {loop, Req, state(), timeout(), hibernate}
|
||||
-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()}
|
||||
| {upgrade, protocol, module()}
|
||||
| {upgrade, protocol, module(), Req, opts()}
|
||||
when Req::cowboy_req:req().
|
||||
-callback handle(Req, State) -> {ok, Req, State}
|
||||
when Req::cowboy_req:req(), State::state().
|
||||
-callback terminate(terminate_reason(), cowboy_req:req(), state()) -> ok.
|
||||
%% @todo optional -callback terminate(terminate_reason(), cowboy_req:req(), state()) -> ok.
|
||||
|
|
191
src/cowboy_long_polling.erl
Normal file
191
src/cowboy_long_polling.erl
Normal file
|
@ -0,0 +1,191 @@
|
|||
%% Copyright (c) 2011-2014, Loïc Hoguin <essen@ninenines.eu>
|
||||
%%
|
||||
%% 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.
|
||||
|
||||
%% When using loop handlers, we are receiving data from the socket because we
|
||||
%% want to know when the socket gets closed. This is generally not an issue
|
||||
%% because these kinds of requests are generally not pipelined, and don't have
|
||||
%% a body. If they do have a body, this body is often read in the
|
||||
%% <em>init/2</em> callback and this is no problem. Otherwise, this data
|
||||
%% accumulates in a buffer until we reach a certain threshold of 5000 bytes
|
||||
%% 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).
|
||||
-behaviour(cowboy_sub_protocol).
|
||||
|
||||
-export([upgrade/6]).
|
||||
-export([loop/4]).
|
||||
|
||||
-record(state, {
|
||||
env :: cowboy_middleware:env(),
|
||||
hibernate = false :: boolean(),
|
||||
buffer_size = 0 :: non_neg_integer(),
|
||||
max_buffer = 5000 :: non_neg_integer() | infinity,
|
||||
timeout = infinity :: timeout(),
|
||||
timeout_ref = undefined :: undefined | reference(),
|
||||
resp_sent = false :: boolean()
|
||||
}).
|
||||
|
||||
-spec upgrade(Req, Env, module(), any(), timeout(), run | hibernate)
|
||||
-> {ok, Req, Env} | {suspend, module(), atom(), [any()]}
|
||||
when Req::cowboy_req:req(), Env::cowboy_middleware:env().
|
||||
upgrade(Req, Env, Handler, HandlerState, Timeout, run) ->
|
||||
State = #state{env=Env, max_buffer=get_max_buffer(Env), timeout=Timeout},
|
||||
State2 = timeout(State),
|
||||
after_call(Req, State2, Handler, HandlerState);
|
||||
upgrade(Req, Env, Handler, HandlerState, Timeout, hibernate) ->
|
||||
State = #state{env=Env, max_buffer=get_max_buffer(Env), hibernate=true, timeout=Timeout},
|
||||
State2 = timeout(State),
|
||||
after_call(Req, State2, Handler, HandlerState).
|
||||
|
||||
get_max_buffer(Env) ->
|
||||
case lists:keyfind(loop_max_buffer, 1, Env) of
|
||||
false -> 5000;
|
||||
{_, MaxBuffer} -> MaxBuffer
|
||||
end.
|
||||
|
||||
%% Update the state if the response was sent in the callback.
|
||||
after_call(Req, State=#state{resp_sent=false}, Handler,
|
||||
HandlerState) ->
|
||||
receive
|
||||
{cowboy_req, resp_sent} ->
|
||||
before_loop(Req, State#state{resp_sent=true}, Handler, HandlerState)
|
||||
after 0 ->
|
||||
before_loop(Req, State, Handler, HandlerState)
|
||||
end;
|
||||
after_call(Req, State, Handler, HandlerState) ->
|
||||
before_loop(Req, State, Handler, HandlerState).
|
||||
|
||||
before_loop(Req, State=#state{hibernate=true}, Handler, HandlerState) ->
|
||||
[Socket, Transport] = cowboy_req:get([socket, transport], Req),
|
||||
Transport:setopts(Socket, [{active, once}]),
|
||||
{suspend, ?MODULE, loop, [Req, State#state{hibernate=false}, Handler, HandlerState]};
|
||||
before_loop(Req, State, Handler, HandlerState) ->
|
||||
[Socket, Transport] = cowboy_req:get([socket, transport], Req),
|
||||
Transport:setopts(Socket, [{active, once}]),
|
||||
loop(Req, State, Handler, HandlerState).
|
||||
|
||||
%% Almost the same code can be found in cowboy_websocket.
|
||||
timeout(State=#state{timeout=infinity}) ->
|
||||
State#state{timeout_ref=undefined};
|
||||
timeout(State=#state{timeout=Timeout,
|
||||
timeout_ref=PrevRef}) ->
|
||||
_ = case PrevRef of
|
||||
undefined -> ignore;
|
||||
PrevRef -> erlang:cancel_timer(PrevRef)
|
||||
end,
|
||||
TRef = erlang:start_timer(Timeout, self(), ?MODULE),
|
||||
State#state{timeout_ref=TRef}.
|
||||
|
||||
-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,
|
||||
max_buffer=Threshold, timeout_ref=TRef,
|
||||
resp_sent=RespSent}, Handler, HandlerState) ->
|
||||
[Socket, Transport] = cowboy_req:get([socket, transport], Req),
|
||||
{OK, Closed, Error} = Transport:messages(),
|
||||
receive
|
||||
{OK, Socket, Data} ->
|
||||
NbBytes2 = NbBytes + byte_size(Data),
|
||||
if NbBytes2 > Threshold ->
|
||||
_ = if RespSent -> ok; true ->
|
||||
cowboy_req:reply(500, Req)
|
||||
end,
|
||||
_ = cowboy_handler:terminate(Req, Env, Handler, HandlerState, {error, overflow}),
|
||||
exit(normal);
|
||||
true ->
|
||||
Req2 = cowboy_req:append_buffer(Data, Req),
|
||||
State2 = timeout(State#state{buffer_size=NbBytes2}),
|
||||
before_loop(Req2, State2, Handler, HandlerState)
|
||||
end;
|
||||
{Closed, Socket} ->
|
||||
terminate(Req, State, Handler, HandlerState, {error, closed});
|
||||
{Error, Socket, Reason} ->
|
||||
terminate(Req, State, Handler, HandlerState, {error, Reason});
|
||||
{timeout, TRef, ?MODULE} ->
|
||||
after_loop(Req, State, Handler, HandlerState, {normal, timeout});
|
||||
{timeout, OlderTRef, ?MODULE} when is_reference(OlderTRef) ->
|
||||
loop(Req, State, Handler, HandlerState);
|
||||
Message ->
|
||||
%% We set the socket back to {active, false} mode in case
|
||||
%% the handler is going to call recv. We also flush any
|
||||
%% data received after that and put it into the buffer.
|
||||
%% We do not check the size here, if data keeps coming
|
||||
%% we'll error out on the next packet received.
|
||||
Transport:setopts(Socket, [{active, false}]),
|
||||
Req2 = receive {OK, Socket, Data} ->
|
||||
cowboy_req:append_buffer(Data, Req)
|
||||
after 0 ->
|
||||
Req
|
||||
end,
|
||||
call(Req2, State, Handler, HandlerState, Message)
|
||||
end.
|
||||
|
||||
call(Req, State=#state{env=Env, 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)
|
||||
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}),
|
||||
erlang:Class([
|
||||
{reason, Reason},
|
||||
{mfa, {Handler, info, 3}},
|
||||
{stacktrace, Stacktrace},
|
||||
{req, cowboy_req:to_list(Req)},
|
||||
{state, HandlerState}
|
||||
])
|
||||
end.
|
||||
|
||||
%% It is sometimes important to make a socket passive as it was initially
|
||||
%% and as it is expected to be by cowboy_protocol, right after we're done
|
||||
%% with loop handling. The browser may freely pipeline a bunch of requests
|
||||
%% if previous one was, say, a JSONP long-polling request.
|
||||
after_loop(Req, State, Handler, HandlerState, Reason) ->
|
||||
[Socket, Transport] = cowboy_req:get([socket, transport], Req),
|
||||
Transport:setopts(Socket, [{active, false}]),
|
||||
{OK, _Closed, _Error} = Transport:messages(),
|
||||
Req2 = receive
|
||||
{OK, Socket, Data} ->
|
||||
cowboy_req:append_buffer(Data, Req)
|
||||
after 0 ->
|
||||
Req
|
||||
end,
|
||||
terminate(Req2, State, Handler, HandlerState, Reason).
|
||||
|
||||
terminate(Req, #state{env=Env, timeout_ref=TRef},
|
||||
Handler, HandlerState, Reason) ->
|
||||
_ = case TRef of
|
||||
undefined -> ignore;
|
||||
TRef -> erlang:cancel_timer(TRef)
|
||||
end,
|
||||
flush_timeouts(),
|
||||
cowboy_handler:terminate(Req, Env, Handler, HandlerState, Reason).
|
||||
|
||||
flush_timeouts() ->
|
||||
receive
|
||||
{timeout, TRef, ?MODULE} when is_reference(TRef) ->
|
||||
flush_timeouts()
|
||||
after 0 ->
|
||||
ok
|
||||
end.
|
|
@ -16,25 +16,24 @@
|
|||
|
||||
-type opts() :: any().
|
||||
-type state() :: any().
|
||||
-type terminate_reason() :: {normal, shutdown}
|
||||
| {normal, timeout}
|
||||
| {error, closed}
|
||||
| {error, overflow}
|
||||
| {error, atom()}.
|
||||
%% @todo see terminate
|
||||
%-type terminate_reason() :: {normal, shutdown}
|
||||
% | {normal, timeout}
|
||||
% | {error, closed}
|
||||
% | {error, overflow}
|
||||
% | {error, atom()}.
|
||||
|
||||
-callback init({atom(), http}, Req, opts())
|
||||
-> {ok, Req, state()}
|
||||
| {loop, Req, state()}
|
||||
| {loop, Req, state(), hibernate}
|
||||
| {loop, Req, state(), timeout()}
|
||||
| {loop, Req, state(), timeout(), hibernate}
|
||||
-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()}
|
||||
| {upgrade, protocol, module()}
|
||||
| {upgrade, protocol, module(), Req, opts()}
|
||||
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().
|
||||
-callback terminate(terminate_reason(), cowboy_req:req(), state()) -> ok.
|
||||
%% @todo optional -callback terminate(terminate_reason(), cowboy_req:req(), state()) -> ok.
|
||||
|
|
|
@ -83,9 +83,6 @@
|
|||
-export([get/2]).
|
||||
-export([set/2]).
|
||||
-export([set_bindings/4]).
|
||||
|
||||
%% Misc API.
|
||||
-export([compact/1]).
|
||||
-export([lock/1]).
|
||||
-export([to_list/1]).
|
||||
|
||||
|
@ -1002,13 +999,6 @@ set_bindings(HostInfo, PathInfo, Bindings, Req) ->
|
|||
Req#http_req{host_info=HostInfo, path_info=PathInfo,
|
||||
bindings=Bindings}.
|
||||
|
||||
%% Misc API.
|
||||
|
||||
-spec compact(Req) -> Req when Req::req().
|
||||
compact(Req) ->
|
||||
Req#http_req{host_info=undefined, path_info=undefined,
|
||||
bindings=undefined, headers=[]}.
|
||||
|
||||
-spec lock(Req) -> Req when Req::req().
|
||||
lock(Req) ->
|
||||
Req#http_req{resp_state=locked}.
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
-module(cowboy_rest).
|
||||
-behaviour(cowboy_sub_protocol).
|
||||
|
||||
-export([upgrade/4]).
|
||||
-export([upgrade/6]).
|
||||
|
||||
-record(state, {
|
||||
env :: cowboy_middleware:env(),
|
||||
|
@ -55,31 +55,12 @@
|
|||
expires :: undefined | no_call | calendar:datetime() | binary()
|
||||
}).
|
||||
|
||||
-spec upgrade(Req, Env, module(), any())
|
||||
-spec upgrade(Req, Env, module(), any(), infinity, run)
|
||||
-> {ok, Req, Env} when Req::cowboy_req:req(), Env::cowboy_middleware:env().
|
||||
upgrade(Req, Env, Handler, HandlerOpts) ->
|
||||
upgrade(Req, Env, Handler, HandlerState, infinity, run) ->
|
||||
Method = cowboy_req:method(Req),
|
||||
case erlang:function_exported(Handler, rest_init, 2) of
|
||||
true ->
|
||||
try Handler:rest_init(Req, HandlerOpts) of
|
||||
{ok, Req2, HandlerState} ->
|
||||
service_available(Req2, #state{env=Env, method=Method,
|
||||
handler=Handler, handler_state=HandlerState})
|
||||
catch Class:Reason ->
|
||||
Stacktrace = erlang:get_stacktrace(),
|
||||
cowboy_req:maybe_reply(Stacktrace, Req),
|
||||
erlang:Class([
|
||||
{reason, Reason},
|
||||
{mfa, {Handler, rest_init, 2}},
|
||||
{stacktrace, Stacktrace},
|
||||
{req, cowboy_req:to_list(Req)},
|
||||
{opts, HandlerOpts}
|
||||
])
|
||||
end;
|
||||
false ->
|
||||
service_available(Req, #state{env=Env, method=Method,
|
||||
handler=Handler})
|
||||
end.
|
||||
handler=Handler, handler_state=HandlerState}).
|
||||
|
||||
service_available(Req, State) ->
|
||||
expect(Req, State, service_available, true, fun known_methods/2, 503).
|
||||
|
@ -986,13 +967,9 @@ next(Req, State, StatusCode) when is_integer(StatusCode) ->
|
|||
respond(Req, State, StatusCode) ->
|
||||
terminate(cowboy_req:reply(StatusCode, Req), State).
|
||||
|
||||
terminate(Req, State=#state{env=Env}) ->
|
||||
rest_terminate(Req, State),
|
||||
{ok, Req, [{result, ok}|Env]}.
|
||||
|
||||
error_terminate(Req, State=#state{handler=Handler, handler_state=HandlerState},
|
||||
Class, Reason, Callback) ->
|
||||
rest_terminate(Req, State),
|
||||
_ = terminate(Req, State),
|
||||
Stacktrace = erlang:get_stacktrace(),
|
||||
cowboy_req:maybe_reply(Stacktrace, Req),
|
||||
erlang:Class([
|
||||
|
@ -1003,9 +980,5 @@ error_terminate(Req, State=#state{handler=Handler, handler_state=HandlerState},
|
|||
{state, HandlerState}
|
||||
]).
|
||||
|
||||
rest_terminate(Req, #state{handler=Handler, handler_state=HandlerState}) ->
|
||||
case erlang:function_exported(Handler, rest_terminate, 2) of
|
||||
true -> ok = Handler:rest_terminate(
|
||||
cowboy_req:lock(Req), HandlerState);
|
||||
false -> ok
|
||||
end.
|
||||
terminate(Req, #state{env=Env, handler=Handler, handler_state=HandlerState}) ->
|
||||
cowboy_handler:terminate(Req, Env, Handler, HandlerState, {normal, shutdown}).
|
||||
|
|
|
@ -15,8 +15,7 @@
|
|||
|
||||
-module(cowboy_static).
|
||||
|
||||
-export([init/3]).
|
||||
-export([rest_init/2]).
|
||||
-export([init/2]).
|
||||
-export([malformed_request/2]).
|
||||
-export([forbidden/2]).
|
||||
-export([content_types_provided/2]).
|
||||
|
@ -39,33 +38,27 @@
|
|||
|
||||
-type state() :: {binary(), {ok, #file_info{}} | {error, atom()}, extra()}.
|
||||
|
||||
-spec init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
|
||||
init(_, _, _) ->
|
||||
{upgrade, protocol, cowboy_rest}.
|
||||
|
||||
%% Resolve the file that will be sent and get its file information.
|
||||
%% If the handler is configured to manage a directory, check that the
|
||||
%% requested file is inside the configured directory.
|
||||
|
||||
-spec rest_init(Req, opts())
|
||||
-> {ok, Req, error | state()}
|
||||
when Req::cowboy_req:req().
|
||||
rest_init(Req, {Name, Path}) ->
|
||||
rest_init_opts(Req, {Name, Path, []});
|
||||
rest_init(Req, {Name, App, Path})
|
||||
-spec init(Req, opts()) -> {rest, Req, error | state()} when Req::cowboy_req:req().
|
||||
init(Req, {Name, Path}) ->
|
||||
init_opts(Req, {Name, Path, []});
|
||||
init(Req, {Name, App, Path})
|
||||
when Name =:= priv_file; Name =:= priv_dir ->
|
||||
rest_init_opts(Req, {Name, App, Path, []});
|
||||
rest_init(Req, Opts) ->
|
||||
rest_init_opts(Req, Opts).
|
||||
init_opts(Req, {Name, App, Path, []});
|
||||
init(Req, Opts) ->
|
||||
init_opts(Req, Opts).
|
||||
|
||||
rest_init_opts(Req, {priv_file, App, Path, Extra}) ->
|
||||
rest_init_info(Req, absname(priv_path(App, Path)), Extra);
|
||||
rest_init_opts(Req, {file, Path, Extra}) ->
|
||||
rest_init_info(Req, absname(Path), Extra);
|
||||
rest_init_opts(Req, {priv_dir, App, Path, Extra}) ->
|
||||
rest_init_dir(Req, priv_path(App, Path), Extra);
|
||||
rest_init_opts(Req, {dir, Path, Extra}) ->
|
||||
rest_init_dir(Req, Path, Extra).
|
||||
init_opts(Req, {priv_file, App, Path, Extra}) ->
|
||||
init_info(Req, absname(priv_path(App, Path)), Extra);
|
||||
init_opts(Req, {file, Path, Extra}) ->
|
||||
init_info(Req, absname(Path), Extra);
|
||||
init_opts(Req, {priv_dir, App, Path, Extra}) ->
|
||||
init_dir(Req, priv_path(App, Path), Extra);
|
||||
init_opts(Req, {dir, Path, Extra}) ->
|
||||
init_dir(Req, Path, Extra).
|
||||
|
||||
priv_path(App, Path) ->
|
||||
case code:priv_dir(App) of
|
||||
|
@ -83,18 +76,18 @@ absname(Path) when is_list(Path) ->
|
|||
absname(Path) when is_binary(Path) ->
|
||||
filename:absname(Path).
|
||||
|
||||
rest_init_dir(Req, Path, Extra) when is_list(Path) ->
|
||||
rest_init_dir(Req, list_to_binary(Path), Extra);
|
||||
rest_init_dir(Req, Path, Extra) ->
|
||||
init_dir(Req, Path, Extra) when is_list(Path) ->
|
||||
init_dir(Req, list_to_binary(Path), Extra);
|
||||
init_dir(Req, Path, Extra) ->
|
||||
Dir = fullpath(filename:absname(Path)),
|
||||
PathInfo = cowboy_req:path_info(Req),
|
||||
Filepath = filename:join([Dir|PathInfo]),
|
||||
Len = byte_size(Dir),
|
||||
case fullpath(Filepath) of
|
||||
<< Dir:Len/binary, $/, _/binary >> ->
|
||||
rest_init_info(Req, Filepath, Extra);
|
||||
init_info(Req, Filepath, Extra);
|
||||
_ ->
|
||||
{ok, Req, error}
|
||||
{rest, Req, error}
|
||||
end.
|
||||
|
||||
fullpath(Path) ->
|
||||
|
@ -110,9 +103,9 @@ fullpath([<<"..">>|Tail], [_|Acc]) ->
|
|||
fullpath([Segment|Tail], Acc) ->
|
||||
fullpath(Tail, [Segment|Acc]).
|
||||
|
||||
rest_init_info(Req, Path, Extra) ->
|
||||
init_info(Req, Path, Extra) ->
|
||||
Info = file:read_file_info(Path, [{time, universal}]),
|
||||
{ok, Req, {Path, Info, Extra}}.
|
||||
{rest, Req, {Path, Info, Extra}}.
|
||||
|
||||
-ifdef(TEST).
|
||||
fullpath_test_() ->
|
||||
|
|
|
@ -15,9 +15,6 @@
|
|||
|
||||
-module(cowboy_sub_protocol).
|
||||
|
||||
-callback upgrade(Req, Env, module(), any())
|
||||
-> {ok, Req, Env}
|
||||
| {suspend, module(), atom(), [any()]}
|
||||
| {halt, Req}
|
||||
| {error, cowboy:http_status(), Req}
|
||||
-callback upgrade(Req, Env, module(), any(), timeout(), run | hibernate)
|
||||
-> {ok, Req, Env} | {suspend, module(), atom(), [any()]} | {halt, Req}
|
||||
when Req::cowboy_req:req(), Env::cowboy_middleware:env().
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
-module(cowboy_websocket).
|
||||
-behaviour(cowboy_sub_protocol).
|
||||
|
||||
-export([upgrade/4]).
|
||||
-export([upgrade/6]).
|
||||
-export([handler_loop/4]).
|
||||
|
||||
-type close_code() :: 1000..4999.
|
||||
|
@ -53,19 +53,18 @@
|
|||
deflate_state :: undefined | port()
|
||||
}).
|
||||
|
||||
-spec upgrade(Req, Env, module(), any())
|
||||
-> {ok, Req, Env}
|
||||
| {suspend, module(), atom(), [any()]}
|
||||
-spec upgrade(Req, Env, module(), any(), timeout(), run | hibernate)
|
||||
-> {ok, Req, Env} | {suspend, module(), atom(), [any()]}
|
||||
when Req::cowboy_req:req(), Env::cowboy_middleware:env().
|
||||
upgrade(Req, Env, Handler, HandlerOpts) ->
|
||||
upgrade(Req, Env, Handler, HandlerState, Timeout, Hibernate) ->
|
||||
{_, Ref} = lists:keyfind(listener, 1, Env),
|
||||
ranch:remove_connection(Ref),
|
||||
[Socket, Transport] = cowboy_req:get([socket, transport], Req),
|
||||
State = #state{env=Env, socket=Socket, transport=Transport,
|
||||
handler=Handler},
|
||||
State = #state{env=Env, socket=Socket, transport=Transport, handler=Handler,
|
||||
timeout=Timeout, hibernate=Hibernate =:= hibernate},
|
||||
try websocket_upgrade(State, Req) of
|
||||
{ok, State2, Req2} ->
|
||||
handler_init(State2, Req2, HandlerOpts)
|
||||
websocket_handshake(State2, Req2, HandlerState)
|
||||
catch _:_ ->
|
||||
receive
|
||||
{cowboy_req, resp_sent} -> ok
|
||||
|
@ -121,38 +120,6 @@ websocket_extensions(State, Req) ->
|
|||
end
|
||||
end.
|
||||
|
||||
-spec handler_init(#state{}, Req, any())
|
||||
-> {ok, Req, cowboy_middleware:env()} | {suspend, module(), atom(), [any()]}
|
||||
when Req::cowboy_req:req().
|
||||
handler_init(State=#state{env=Env, transport=Transport,
|
||||
handler=Handler}, Req, HandlerOpts) ->
|
||||
try Handler:websocket_init(Transport:name(), Req, HandlerOpts) of
|
||||
{ok, Req2, HandlerState} ->
|
||||
websocket_handshake(State, Req2, HandlerState);
|
||||
{ok, Req2, HandlerState, hibernate} ->
|
||||
websocket_handshake(State#state{hibernate=true},
|
||||
Req2, HandlerState);
|
||||
{ok, Req2, HandlerState, Timeout} ->
|
||||
websocket_handshake(State#state{timeout=Timeout},
|
||||
Req2, HandlerState);
|
||||
{ok, Req2, HandlerState, Timeout, hibernate} ->
|
||||
websocket_handshake(State#state{timeout=Timeout,
|
||||
hibernate=true}, Req2, HandlerState);
|
||||
{shutdown, Req2} ->
|
||||
cowboy_req:ensure_response(Req2, 400),
|
||||
{ok, Req2, [{result, closed}|Env]}
|
||||
catch Class:Reason ->
|
||||
Stacktrace = erlang:get_stacktrace(),
|
||||
cowboy_req:maybe_reply(Stacktrace, Req),
|
||||
erlang:Class([
|
||||
{reason, Reason},
|
||||
{mfa, {Handler, websocket_init, 3}},
|
||||
{stacktrace, Stacktrace},
|
||||
{req, cowboy_req:to_list(Req)},
|
||||
{opts, HandlerOpts}
|
||||
])
|
||||
end.
|
||||
|
||||
-spec websocket_handshake(#state{}, Req, any())
|
||||
-> {ok, Req, cowboy_middleware:env()}
|
||||
| {suspend, module(), atom(), [any()]}
|
||||
|
@ -703,6 +670,15 @@ websocket_send({Type, Payload0}, State=#state{socket=Socket, transport=Transport
|
|||
{Transport:send(Socket,
|
||||
[<< 1:1, Rsv/bits, Opcode:4, 0:1, BinLen/bits >>, Payload]), State2}.
|
||||
|
||||
-spec payload_length_to_binary(0..16#7fffffffffffffff)
|
||||
-> << _:7 >> | << _:23 >> | << _:71 >>.
|
||||
payload_length_to_binary(N) ->
|
||||
case N of
|
||||
N when N =< 125 -> << N:7 >>;
|
||||
N when N =< 16#ffff -> << 126:7, N:16 >>;
|
||||
N when N =< 16#7fffffffffffffff -> << 127:7, N:64 >>
|
||||
end.
|
||||
|
||||
-spec websocket_send_many([frame()], #state{})
|
||||
-> {ok, #state{}} | {shutdown, #state{}} | {{error, atom()}, #state{}}.
|
||||
websocket_send_many([], State) ->
|
||||
|
@ -739,26 +715,6 @@ websocket_close(State=#state{socket=Socket, transport=Transport},
|
|||
-> {ok, Req, cowboy_middleware:env()}
|
||||
when Req::cowboy_req:req().
|
||||
handler_terminate(#state{env=Env, handler=Handler},
|
||||
Req, HandlerState, TerminateReason) ->
|
||||
try
|
||||
Handler:websocket_terminate(TerminateReason, Req, HandlerState)
|
||||
catch Class:Reason ->
|
||||
erlang:Class([
|
||||
{reason, Reason},
|
||||
{mfa, {Handler, websocket_terminate, 3}},
|
||||
{stacktrace, erlang:get_stacktrace()},
|
||||
{req, cowboy_req:to_list(Req)},
|
||||
{state, HandlerState},
|
||||
{terminate_reason, TerminateReason}
|
||||
])
|
||||
end,
|
||||
Req, HandlerState, Reason) ->
|
||||
_ = cowboy_handler:terminate(Req, Env, Handler, HandlerState, Reason),
|
||||
{ok, Req, [{result, closed}|Env]}.
|
||||
|
||||
-spec payload_length_to_binary(0..16#7fffffffffffffff)
|
||||
-> << _:7 >> | << _:23 >> | << _:71 >>.
|
||||
payload_length_to_binary(N) ->
|
||||
case N of
|
||||
N when N =< 125 -> << N:7 >>;
|
||||
N when N =< 16#ffff -> << 126:7, N:16 >>;
|
||||
N when N =< 16#7fffffffffffffff -> << 127:7, N:64 >>
|
||||
end.
|
||||
|
|
|
@ -16,21 +16,23 @@
|
|||
|
||||
-type opts() :: any().
|
||||
-type state() :: any().
|
||||
-type terminate_reason() :: {normal, shutdown}
|
||||
| {normal, timeout}
|
||||
| {error, closed}
|
||||
| {remote, closed}
|
||||
| {remote, cowboy_websocket:close_code(), binary()}
|
||||
| {error, badencoding}
|
||||
| {error, badframe}
|
||||
| {error, atom()}.
|
||||
%% @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 websocket_init(atom(), Req, opts())
|
||||
-> {ok, Req, state()}
|
||||
| {ok, Req, state(), hibernate}
|
||||
| {ok, Req, state(), timeout()}
|
||||
| {ok, Req, state(), timeout(), hibernate}
|
||||
| {shutdown, Req}
|
||||
-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}
|
||||
|
@ -46,5 +48,4 @@
|
|||
| {reply, cowboy_websocket:frame() | [cowboy_websocket:frame()], Req, State, hibernate}
|
||||
| {shutdown, Req, State}
|
||||
when Req::cowboy_req:req(), State::state().
|
||||
-callback websocket_terminate(terminate_reason(), cowboy_req:req(), state())
|
||||
-> ok.
|
||||
%% @todo optional -callback terminate(terminate_reason(), cowboy_req:req(), state()) -> ok.
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
|
||||
-module(input_crash_h).
|
||||
|
||||
-export([init/3]).
|
||||
-export([init/2]).
|
||||
|
||||
init(_, Req, content_length) ->
|
||||
init(Req, content_length) ->
|
||||
cowboy_error_h:ignore(cow_http_hd, number, 2),
|
||||
cowboy_req:parse_header(<<"content-length">>, Req).
|
||||
|
|
|
@ -4,15 +4,14 @@
|
|||
%% When it receives the last message, it sends a 102 reply back.
|
||||
|
||||
-module(long_polling_h).
|
||||
-behaviour(cowboy_loop_handler).
|
||||
|
||||
-export([init/3]).
|
||||
-export([init/2]).
|
||||
-export([info/3]).
|
||||
-export([terminate/3]).
|
||||
|
||||
init(_, Req, _) ->
|
||||
init(Req, _) ->
|
||||
erlang:send_after(200, self(), timeout),
|
||||
{loop, Req, 2, 5000, hibernate}.
|
||||
{long_polling, Req, 2, 5000, hibernate}.
|
||||
|
||||
info(timeout, Req, 0) ->
|
||||
{ok, cowboy_req:reply(102, Req), 0};
|
||||
|
|
|
@ -4,15 +4,14 @@
|
|||
%% then sends a 200 reply back.
|
||||
|
||||
-module(loop_handler_body_h).
|
||||
-behaviour(cowboy_loop_handler).
|
||||
|
||||
-export([init/3]).
|
||||
-export([init/2]).
|
||||
-export([info/3]).
|
||||
-export([terminate/3]).
|
||||
|
||||
init(_, Req, _) ->
|
||||
init(Req, _) ->
|
||||
self() ! timeout,
|
||||
{loop, Req, undefined, 5000, hibernate}.
|
||||
{long_polling, Req, undefined, 5000, hibernate}.
|
||||
|
||||
info(timeout, Req, State) ->
|
||||
{ok, Body, Req2} = cowboy_req:body(Req),
|
||||
|
|
|
@ -5,15 +5,14 @@
|
|||
%% This results in a 204 reply being sent back by Cowboy.
|
||||
|
||||
-module(loop_handler_timeout_h).
|
||||
-behaviour(cowboy_loop_handler).
|
||||
|
||||
-export([init/3]).
|
||||
-export([init/2]).
|
||||
-export([info/3]).
|
||||
-export([terminate/3]).
|
||||
|
||||
init(_, Req, _) ->
|
||||
init(Req, _) ->
|
||||
erlang:send_after(1000, self(), timeout),
|
||||
{loop, Req, undefined, 200, hibernate}.
|
||||
{long_polling, Req, undefined, 200, hibernate}.
|
||||
|
||||
info(timeout, Req, State) ->
|
||||
{ok, cowboy_req:reply(500, Req), State}.
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
%% Feel free to use, reuse and abuse the code in this file.
|
||||
|
||||
-module(http_body_qs).
|
||||
-behaviour(cowboy_http_handler).
|
||||
-export([init/3, handle/2, terminate/3]).
|
||||
|
||||
init({_, http}, Req, _) ->
|
||||
{ok, Req, undefined}.
|
||||
-export([init/2]).
|
||||
-export([handle/2]).
|
||||
|
||||
init(Req, Opts) ->
|
||||
{http, Req, Opts}.
|
||||
|
||||
handle(Req, State) ->
|
||||
Method = cowboy_req:method(Req),
|
||||
|
@ -33,6 +34,3 @@ echo(Echo, Req) ->
|
|||
cowboy_req:reply(200, [
|
||||
{<<"content-type">>, <<"text/plain; charset=utf-8">>}
|
||||
], Echo, Req).
|
||||
|
||||
terminate(_, _, _) ->
|
||||
ok.
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
%% Feel free to use, reuse and abuse the code in this file.
|
||||
|
||||
-module(http_chunked).
|
||||
-behaviour(cowboy_http_handler).
|
||||
-export([init/3, handle/2, terminate/3]).
|
||||
|
||||
init({_Transport, http}, Req, _Opts) ->
|
||||
{ok, Req, undefined}.
|
||||
-export([init/2]).
|
||||
-export([handle/2]).
|
||||
|
||||
init(Req, Opts) ->
|
||||
{http, Req, Opts}.
|
||||
|
||||
handle(Req, State) ->
|
||||
Req2 = cowboy_req:chunked_reply(200, Req),
|
||||
|
@ -14,6 +15,3 @@ handle(Req, State) ->
|
|||
timer:sleep(100),
|
||||
cowboy_req:chunk("works fine!", Req2),
|
||||
{ok, Req2, State}.
|
||||
|
||||
terminate(_, _, _) ->
|
||||
ok.
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
%% Feel free to use, reuse and abuse the code in this file.
|
||||
|
||||
-module(http_echo_body).
|
||||
-behaviour(cowboy_http_handler).
|
||||
-export([init/3, handle/2, terminate/3]).
|
||||
|
||||
init({_, http}, Req, _) ->
|
||||
{ok, Req, undefined}.
|
||||
-export([init/2]).
|
||||
-export([handle/2]).
|
||||
|
||||
init(Req, Opts) ->
|
||||
{http, Req, Opts}.
|
||||
|
||||
handle(Req, State) ->
|
||||
true = cowboy_req:has_body(Req),
|
||||
|
@ -22,6 +23,3 @@ handle_body(Req, Body) ->
|
|||
Size = cowboy_req:body_length(Req),
|
||||
Size = byte_size(Body),
|
||||
cowboy_req:reply(200, [], Body, Req).
|
||||
|
||||
terminate(_, _, _) ->
|
||||
ok.
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
%% Feel free to use, reuse and abuse the code in this file.
|
||||
|
||||
-module(http_errors).
|
||||
-behaviour(cowboy_http_handler).
|
||||
-export([init/3, handle/2, terminate/3]).
|
||||
|
||||
init({_Transport, http}, Req, _Opts) ->
|
||||
-export([init/2]).
|
||||
-export([handle/2]).
|
||||
|
||||
init(Req, _Opts) ->
|
||||
#{'case' := Case} = cowboy_req:match_qs(Req, ['case']),
|
||||
case_init(Case, Req).
|
||||
|
||||
|
@ -17,11 +18,11 @@ case_init(<<"init_after_reply">> = Case, Req) ->
|
|||
error(Case);
|
||||
case_init(<<"init_reply_handle_error">> = Case, Req) ->
|
||||
Req1 = cowboy_req:reply(200, [], "http_handler_crashes", Req),
|
||||
{ok, Req1, Case};
|
||||
{http, Req1, Case};
|
||||
case_init(<<"handle_before_reply">> = Case, Req) ->
|
||||
{ok, Req, Case};
|
||||
{http, Req, Case};
|
||||
case_init(<<"handle_after_reply">> = Case, Req) ->
|
||||
{ok, Req, Case}.
|
||||
{http, Req, Case}.
|
||||
|
||||
handle(_Req, <<"init_reply_handle_error">> = Case) ->
|
||||
cowboy_error_h:ignore(?MODULE, handle, 2),
|
||||
|
@ -33,6 +34,3 @@ handle(Req, <<"handle_after_reply">> = Case) ->
|
|||
cowboy_error_h:ignore(?MODULE, handle, 2),
|
||||
_ = cowboy_req:reply(200, [], "http_handler_crashes", Req),
|
||||
error(Case).
|
||||
|
||||
terminate(_, _, _) ->
|
||||
ok.
|
||||
|
|
|
@ -1,18 +1,14 @@
|
|||
%% Feel free to use, reuse and abuse the code in this file.
|
||||
|
||||
-module(http_handler).
|
||||
-behaviour(cowboy_http_handler).
|
||||
-export([init/3, handle/2, terminate/3]).
|
||||
|
||||
-record(state, {headers, body}).
|
||||
-export([init/2]).
|
||||
-export([handle/2]).
|
||||
|
||||
init({_Transport, http}, Req, Opts) ->
|
||||
Headers = proplists:get_value(headers, Opts, []),
|
||||
Body = proplists:get_value(body, Opts, "http_handler"),
|
||||
{ok, Req, #state{headers=Headers, body=Body}}.
|
||||
init(Req, Opts) ->
|
||||
{http, Req, Opts}.
|
||||
|
||||
handle(Req, State=#state{headers=Headers, body=Body}) ->
|
||||
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}.
|
||||
|
||||
terminate(_, _, _) ->
|
||||
ok.
|
||||
|
|
|
@ -1,17 +1,10 @@
|
|||
%% Feel free to use, reuse and abuse the code in this file.
|
||||
|
||||
-module(http_init_shutdown).
|
||||
-behaviour(cowboy_http_handler).
|
||||
-export([init/3, handle/2, terminate/3]).
|
||||
|
||||
init({_Transport, http}, Req, _Opts) ->
|
||||
-export([init/2]).
|
||||
|
||||
init(Req, _) ->
|
||||
Req2 = cowboy_req:reply(<<"666 Init Shutdown Testing">>,
|
||||
[{<<"connection">>, <<"close">>}], Req),
|
||||
{shutdown, Req2, undefined}.
|
||||
|
||||
handle(Req, State) ->
|
||||
Req2 = cowboy_req:reply(200, [], "Hello world!", Req),
|
||||
{ok, Req2, State}.
|
||||
|
||||
terminate(_, _, _) ->
|
||||
ok.
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
%% Feel free to use, reuse and abuse the code in this file.
|
||||
|
||||
-module(http_loop_stream_recv).
|
||||
-export([init/3]).
|
||||
|
||||
-export([init/2]).
|
||||
-export([info/3]).
|
||||
-export([terminate/3]).
|
||||
|
||||
init({_, http}, Req, _) ->
|
||||
init(Req, _) ->
|
||||
receive after 100 -> ok end,
|
||||
self() ! stream,
|
||||
{loop, Req, undefined, 100}.
|
||||
{long_polling, Req, undefined, 100}.
|
||||
|
||||
info(stream, Req, undefined) ->
|
||||
stream(Req, 1, <<>>).
|
||||
|
|
|
@ -1,19 +1,17 @@
|
|||
%% Feel free to use, reuse and abuse the code in this file.
|
||||
|
||||
-module(http_multipart).
|
||||
-behaviour(cowboy_http_handler).
|
||||
-export([init/3, handle/2, terminate/3]).
|
||||
|
||||
init({_Transport, http}, Req, []) ->
|
||||
{ok, Req, {}}.
|
||||
-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}.
|
||||
|
||||
terminate(_, _, _) ->
|
||||
ok.
|
||||
|
||||
acc_multipart(Req, Acc) ->
|
||||
case cowboy_req:part(Req) of
|
||||
{ok, Headers, Req2} ->
|
||||
|
|
|
@ -1,19 +1,17 @@
|
|||
%% Feel free to use, reuse and abuse the code in this file.
|
||||
|
||||
-module(http_multipart_stream).
|
||||
-behaviour(cowboy_http_handler).
|
||||
-export([init/3, handle/2, terminate/3]).
|
||||
|
||||
init(_, Req, []) ->
|
||||
{ok, Req, undefined}.
|
||||
-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}.
|
||||
|
||||
terminate(_, _, _) ->
|
||||
ok.
|
||||
|
||||
multipart(Req) ->
|
||||
case cowboy_req:part(Req) of
|
||||
{ok, [{<<"content-length">>, BinLength}], Req2} ->
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
%% Feel free to use, reuse and abuse the code in this file.
|
||||
|
||||
%% @todo That module was clearly meant to do more than one
|
||||
%% thing and yet doesn't.
|
||||
-module(http_req_attr).
|
||||
-behaviour(cowboy_http_handler).
|
||||
-export([init/3, handle/2, terminate/3]).
|
||||
|
||||
init({_, http}, Req, _) ->
|
||||
-export([init/2]).
|
||||
-export([handle/2]).
|
||||
|
||||
init(Req, Opts) ->
|
||||
{http, Req, Opts}.
|
||||
|
||||
handle(Req, State) ->
|
||||
#{attr := Attr} = cowboy_req:match_qs(Req, [attr]),
|
||||
{ok, Req, Attr}.
|
||||
|
||||
handle(Req, <<"host_and_port">> = 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), Attr}.
|
||||
|
||||
terminate(_, _, _) ->
|
||||
ok.
|
||||
{ok, cowboy_req:reply(200, [], Value, Req), State}.
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
%% Feel free to use, reuse and abuse the code in this file.
|
||||
|
||||
-module(http_set_resp).
|
||||
-behaviour(cowboy_http_handler).
|
||||
-export([init/3, handle/2, terminate/3]).
|
||||
|
||||
init({_Transport, http}, Req, Opts) ->
|
||||
-export([init/2]).
|
||||
-export([handle/2]).
|
||||
|
||||
init(Req, Opts) ->
|
||||
Headers = proplists:get_value(headers, Opts, []),
|
||||
Body = proplists:get_value(body, Opts, <<"http_handler_set_resp">>),
|
||||
Req2 = lists:foldl(fun({Name, Value}, R) ->
|
||||
|
@ -13,7 +14,7 @@ init({_Transport, http}, 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),
|
||||
{ok, Req5, undefined}.
|
||||
{http, Req5, undefined}.
|
||||
|
||||
handle(Req, State) ->
|
||||
case cowboy_req:has_resp_header(<<"x-cowboy-test">>, Req) of
|
||||
|
@ -26,6 +27,3 @@ handle(Req, State) ->
|
|||
{ok, cowboy_req:reply(200, Req), State}
|
||||
end
|
||||
end.
|
||||
|
||||
terminate(_, _, _) ->
|
||||
ok.
|
||||
|
|
|
@ -1,18 +1,16 @@
|
|||
%% Feel free to use, reuse and abuse the code in this file.
|
||||
|
||||
-module(http_stream_body).
|
||||
-behaviour(cowboy_http_handler).
|
||||
-export([init/3, handle/2, terminate/3]).
|
||||
|
||||
-record(state, {headers, body, reply}).
|
||||
-export([init/2]).
|
||||
-export([handle/2]).
|
||||
|
||||
init({_Transport, http}, Req, Opts) ->
|
||||
Headers = proplists:get_value(headers, Opts, []),
|
||||
Body = proplists:get_value(body, Opts, "http_handler_stream_body"),
|
||||
Reply = proplists:get_value(reply, Opts),
|
||||
{ok, Req, #state{headers=Headers, body=Body, reply=Reply}}.
|
||||
init(Req, Opts) ->
|
||||
{http, Req, Opts}.
|
||||
|
||||
handle(Req, State=#state{headers=_Headers, body=Body, reply=Reply}) ->
|
||||
handle(Req, State) ->
|
||||
Body = proplists:get_value(body, State, "http_handler_stream_body"),
|
||||
Reply = proplists:get_value(reply, State),
|
||||
SFun = fun(Socket, Transport) -> Transport:send(Socket, Body) end,
|
||||
Req2 = case Reply of
|
||||
set_resp ->
|
||||
|
@ -26,6 +24,3 @@ handle(Req, State=#state{headers=_Headers, body=Body, reply=Reply}) ->
|
|||
cowboy_req:set_resp_body_fun(chunked, SFun2, Req)
|
||||
end,
|
||||
{ok, cowboy_req:reply(200, Req2), State}.
|
||||
|
||||
terminate(_, _, _) ->
|
||||
ok.
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
%% Feel free to use, reuse and abuse the code in this file.
|
||||
|
||||
-module(http_streamed).
|
||||
-behaviour(cowboy_http_handler).
|
||||
-export([init/3, handle/2, terminate/3]).
|
||||
|
||||
init({_Transport, http}, Req, _Opts) ->
|
||||
{ok, Req, undefined}.
|
||||
-export([init/2]).
|
||||
-export([handle/2]).
|
||||
|
||||
init(Req, Opts) ->
|
||||
{http, Req, Opts}.
|
||||
|
||||
handle(Req, State) ->
|
||||
Req2 = cowboy_req:set([{resp_state, waiting_stream}], Req),
|
||||
|
@ -15,6 +16,3 @@ handle(Req, State) ->
|
|||
timer:sleep(100),
|
||||
cowboy_req:chunk("works fine!", Req3),
|
||||
{ok, Req3, State}.
|
||||
|
||||
terminate(_, _, _) ->
|
||||
ok.
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
-module(rest_empty_resource).
|
||||
-export([init/3]).
|
||||
|
||||
init(_Transport, _Req, _Opts) ->
|
||||
{upgrade, protocol, cowboy_rest}.
|
||||
-export([init/2]).
|
||||
|
||||
init(Req, Opts) ->
|
||||
{rest, Req, Opts}.
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
-module(rest_expires).
|
||||
|
||||
-export([init/3]).
|
||||
-export([init/2]).
|
||||
-export([content_types_provided/2]).
|
||||
-export([get_text_plain/2]).
|
||||
-export([expires/2]).
|
||||
-export([last_modified/2]).
|
||||
|
||||
init(_Transport, _Req, _Opts) ->
|
||||
{upgrade, protocol, cowboy_rest}.
|
||||
init(Req, Opts) ->
|
||||
{rest, Req, Opts}.
|
||||
|
||||
content_types_provided(Req, State) ->
|
||||
{[{{<<"text">>, <<"plain">>, []}, get_text_plain}], Req, State}.
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
-module(rest_expires_binary).
|
||||
|
||||
-export([init/3]).
|
||||
-export([init/2]).
|
||||
-export([content_types_provided/2]).
|
||||
-export([get_text_plain/2]).
|
||||
-export([expires/2]).
|
||||
|
||||
init(_Transport, _Req, _Opts) ->
|
||||
{upgrade, protocol, cowboy_rest}.
|
||||
init(Req, Opts) ->
|
||||
{rest, Req, Opts}.
|
||||
|
||||
content_types_provided(Req, State) ->
|
||||
{[{{<<"text">>, <<"plain">>, []}, get_text_plain}], Req, State}.
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
-module(rest_forbidden_resource).
|
||||
-export([init/3, rest_init/2, allowed_methods/2, forbidden/2,
|
||||
content_types_provided/2, content_types_accepted/2,
|
||||
to_text/2, from_text/2]).
|
||||
|
||||
init(_Transport, _Req, _Opts) ->
|
||||
{upgrade, protocol, cowboy_rest}.
|
||||
-export([init/2]).
|
||||
-export([allowed_methods/2]).
|
||||
-export([forbidden/2]).
|
||||
-export([content_types_provided/2]).
|
||||
-export([content_types_accepted/2]).
|
||||
-export([to_text/2]).
|
||||
-export([from_text/2]).
|
||||
|
||||
rest_init(Req, [Forbidden]) ->
|
||||
{ok, Req, Forbidden}.
|
||||
init(Req, [Forbidden]) ->
|
||||
{rest, Req, Forbidden}.
|
||||
|
||||
allowed_methods(Req, State) ->
|
||||
{[<<"GET">>, <<"HEAD">>, <<"POST">>], Req, State}.
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
-module(rest_missing_callbacks).
|
||||
-export([init/3]).
|
||||
|
||||
-export([init/2]).
|
||||
-export([allowed_methods/2]).
|
||||
-export([content_types_accepted/2]).
|
||||
-export([content_types_provided/2]).
|
||||
|
||||
init(_Transport, _Req, _Opts) ->
|
||||
{upgrade, protocol, cowboy_rest}.
|
||||
init(Req, Opts) ->
|
||||
{rest, Req, Opts}.
|
||||
|
||||
allowed_methods(Req, State) ->
|
||||
{[<<"GET">>, <<"PUT">>], Req, State}.
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
-module(rest_nodelete_resource).
|
||||
-export([init/3, allowed_methods/2, content_types_provided/2,
|
||||
get_text_plain/2]).
|
||||
|
||||
init(_Transport, _Req, _Opts) ->
|
||||
{upgrade, protocol, cowboy_rest}.
|
||||
-export([init/2]).
|
||||
-export([allowed_methods/2]).
|
||||
-export([content_types_provided/2]).
|
||||
-export([get_text_plain/2]).
|
||||
|
||||
init(Req, Opts) ->
|
||||
{rest, Req, Opts}.
|
||||
|
||||
allowed_methods(Req, State) ->
|
||||
{[<<"GET">>, <<"HEAD">>, <<"DELETE">>], Req, State}.
|
||||
|
||||
|
||||
content_types_provided(Req, State) ->
|
||||
{[{{<<"text">>, <<"plain">>, []}, get_text_plain}], Req, State}.
|
||||
|
||||
get_text_plain(Req, State) ->
|
||||
{<<"This is REST!">>, Req, State}.
|
||||
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
-module(rest_param_all).
|
||||
|
||||
-export([init/3]).
|
||||
-export([init/2]).
|
||||
-export([allowed_methods/2]).
|
||||
-export([content_types_provided/2]).
|
||||
-export([get_text_plain/2]).
|
||||
-export([content_types_accepted/2]).
|
||||
-export([put_text_plain/2]).
|
||||
|
||||
init(_Transport, _Req, _Opts) ->
|
||||
{upgrade, protocol, cowboy_rest}.
|
||||
init(Req, Opts) ->
|
||||
{rest, Req, Opts}.
|
||||
|
||||
allowed_methods(Req, State) ->
|
||||
{[<<"GET">>, <<"PUT">>], Req, State}.
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
-module(rest_patch_resource).
|
||||
-export([init/3, allowed_methods/2, content_types_provided/2, get_text_plain/2,
|
||||
content_types_accepted/2, patch_text_plain/2]).
|
||||
|
||||
init(_Transport, _Req, _Opts) ->
|
||||
{upgrade, protocol, cowboy_rest}.
|
||||
-export([init/2]).
|
||||
-export([allowed_methods/2]).
|
||||
-export([content_types_provided/2]).
|
||||
-export([get_text_plain/2]).
|
||||
-export([content_types_accepted/2]).
|
||||
-export([patch_text_plain/2]).
|
||||
|
||||
init(Req, Opts) ->
|
||||
{rest, Req, Opts}.
|
||||
|
||||
allowed_methods(Req, State) ->
|
||||
{[<<"HEAD">>, <<"GET">>, <<"PATCH">>], Req, State}.
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
-module(rest_post_charset_resource).
|
||||
-export([init/3, allowed_methods/2, content_types_accepted/2, from_text/2]).
|
||||
|
||||
init(_Transport, _Req, _Opts) ->
|
||||
{upgrade, protocol, cowboy_rest}.
|
||||
-export([init/2]).
|
||||
-export([allowed_methods/2]).
|
||||
-export([content_types_accepted/2]).
|
||||
-export([from_text/2]).
|
||||
|
||||
init(Req, Opts) ->
|
||||
{rest, Req, Opts}.
|
||||
|
||||
allowed_methods(Req, State) ->
|
||||
{[<<"POST">>], Req, State}.
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
-module(rest_postonly_resource).
|
||||
-export([init/3, allowed_methods/2, content_types_accepted/2, from_text/2]).
|
||||
|
||||
init(_Transport, _Req, _Opts) ->
|
||||
{upgrade, protocol, cowboy_rest}.
|
||||
-export([init/2]).
|
||||
-export([allowed_methods/2]).
|
||||
-export([content_types_accepted/2]).
|
||||
-export([from_text/2]).
|
||||
|
||||
init(Req, Opts) ->
|
||||
{rest, Req, Opts}.
|
||||
|
||||
allowed_methods(Req, State) ->
|
||||
{[<<"POST">>], Req, State}.
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
-module(rest_resource_etags).
|
||||
-export([init/3, generate_etag/2, content_types_provided/2, get_text_plain/2]).
|
||||
|
||||
init(_Transport, _Req, _Opts) ->
|
||||
{upgrade, protocol, cowboy_rest}.
|
||||
-export([init/2]).
|
||||
-export([generate_etag/2]).
|
||||
-export([content_types_provided/2]).
|
||||
-export([get_text_plain/2]).
|
||||
|
||||
init(Req, Opts) ->
|
||||
{rest, Req, Opts}.
|
||||
|
||||
generate_etag(Req, State) ->
|
||||
#{type := Type} = cowboy_req:match_qs(Req, [type]),
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
-module(rest_simple_resource).
|
||||
-export([init/3, content_types_provided/2, get_text_plain/2]).
|
||||
|
||||
init(_Transport, _Req, _Opts) ->
|
||||
{upgrade, protocol, cowboy_rest}.
|
||||
-export([init/2]).
|
||||
-export([content_types_provided/2]).
|
||||
-export([get_text_plain/2]).
|
||||
|
||||
init(Req, Opts) ->
|
||||
{rest, Req, Opts}.
|
||||
|
||||
content_types_provided(Req, State) ->
|
||||
{[{{<<"text">>, <<"plain">>, []}, get_text_plain}], Req, State}.
|
||||
|
||||
get_text_plain(Req, State) ->
|
||||
{<<"This is REST!">>, Req, State}.
|
||||
|
||||
|
|
|
@ -80,9 +80,7 @@ init_dispatch() ->
|
|||
{text, <<"won't be received">>}]}
|
||||
]},
|
||||
{"/ws_timeout_hibernate", ws_timeout_hibernate, []},
|
||||
{"/ws_timeout_cancel", ws_timeout_cancel, []},
|
||||
{"/ws_upgrade_with_opts", ws_upgrade_with_opts,
|
||||
<<"failure">>}
|
||||
{"/ws_timeout_cancel", ws_timeout_cancel, []}
|
||||
]}
|
||||
]).
|
||||
|
||||
|
@ -653,35 +651,6 @@ ws_timeout_reset(Config) ->
|
|||
{error, closed} = gen_tcp:recv(Socket, 0, 6000),
|
||||
ok.
|
||||
|
||||
ws_upgrade_with_opts(Config) ->
|
||||
{port, Port} = lists:keyfind(port, 1, Config),
|
||||
{ok, Socket} = gen_tcp:connect("localhost", Port,
|
||||
[binary, {active, false}, {packet, raw}]),
|
||||
ok = gen_tcp:send(Socket, [
|
||||
"GET /ws_upgrade_with_opts HTTP/1.1\r\n"
|
||||
"Host: localhost\r\n"
|
||||
"Connection: Upgrade\r\n"
|
||||
"Upgrade: websocket\r\n"
|
||||
"Sec-WebSocket-Origin: http://localhost\r\n"
|
||||
"Sec-WebSocket-Version: 8\r\n"
|
||||
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
|
||||
"\r\n"]),
|
||||
{ok, Handshake} = gen_tcp:recv(Socket, 0, 6000),
|
||||
{ok, {http_response, {1, 1}, 101, "Switching Protocols"}, Rest}
|
||||
= erlang:decode_packet(http, Handshake, []),
|
||||
[Headers, <<>>] = do_decode_headers(
|
||||
erlang:decode_packet(httph, Rest, []), []),
|
||||
{'Connection', "Upgrade"} = lists:keyfind('Connection', 1, Headers),
|
||||
{'Upgrade', "websocket"} = lists:keyfind('Upgrade', 1, Headers),
|
||||
{"sec-websocket-accept", "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="}
|
||||
= lists:keyfind("sec-websocket-accept", 1, Headers),
|
||||
{ok, Response} = gen_tcp:recv(Socket, 9, 6000),
|
||||
<< 1:1, 0:3, 1:4, 0:1, 7:7, "success" >> = Response,
|
||||
ok = gen_tcp:send(Socket, << 1:1, 0:3, 8:4, 1:1, 0:7, 0:32 >>), %% close
|
||||
{ok, << 1:1, 0:3, 8:4, 0:8 >>} = gen_tcp:recv(Socket, 0, 6000),
|
||||
{error, closed} = gen_tcp:recv(Socket, 0, 6000),
|
||||
ok.
|
||||
|
||||
%% Internal.
|
||||
|
||||
do_decode_headers({ok, http_eoh, Rest}, Acc) ->
|
||||
|
|
|
@ -1,17 +1,13 @@
|
|||
%% Feel free to use, reuse and abuse the code in this file.
|
||||
|
||||
-module(ws_echo).
|
||||
-behaviour(cowboy_websocket_handler).
|
||||
-export([init/3]).
|
||||
-export([websocket_init/3, websocket_handle/3,
|
||||
websocket_info/3, websocket_terminate/3]).
|
||||
|
||||
init(_Any, _Req, _Opts) ->
|
||||
{upgrade, protocol, cowboy_websocket}.
|
||||
-export([init/2]).
|
||||
-export([websocket_handle/3]).
|
||||
-export([websocket_info/3]).
|
||||
|
||||
websocket_init(_TransportName, Req, _Opts) ->
|
||||
Req2 = cowboy_req:compact(Req),
|
||||
{ok, Req2, undefined}.
|
||||
init(Req, _) ->
|
||||
{ws, Req, undefined}.
|
||||
|
||||
websocket_handle({text, Data}, Req, State) ->
|
||||
{reply, {text, Data}, Req, State};
|
||||
|
@ -22,6 +18,3 @@ websocket_handle(_Frame, Req, State) ->
|
|||
|
||||
websocket_info(_Info, Req, State) ->
|
||||
{ok, Req, State}.
|
||||
|
||||
websocket_terminate(_Reason, _Req, _State) ->
|
||||
ok.
|
||||
|
|
|
@ -1,18 +1,14 @@
|
|||
%% Feel free to use, reuse and abuse the code in this file.
|
||||
|
||||
-module(ws_echo_timer).
|
||||
-behaviour(cowboy_websocket_handler).
|
||||
-export([init/3]).
|
||||
-export([websocket_init/3, websocket_handle/3,
|
||||
websocket_info/3, websocket_terminate/3]).
|
||||
|
||||
init(_Any, _Req, _Opts) ->
|
||||
{upgrade, protocol, cowboy_websocket}.
|
||||
-export([init/2]).
|
||||
-export([websocket_handle/3]).
|
||||
-export([websocket_info/3]).
|
||||
|
||||
websocket_init(_TransportName, Req, _Opts) ->
|
||||
init(Req, _) ->
|
||||
erlang:start_timer(1000, self(), <<"websocket_init">>),
|
||||
Req2 = cowboy_req:compact(Req),
|
||||
{ok, Req2, undefined}.
|
||||
{ws, Req, undefined}.
|
||||
|
||||
websocket_handle({text, Data}, Req, State) ->
|
||||
{reply, {text, Data}, Req, State};
|
||||
|
@ -26,6 +22,3 @@ websocket_info({timeout, _Ref, Msg}, Req, State) ->
|
|||
{reply, {text, Msg}, Req, State};
|
||||
websocket_info(_Info, Req, State) ->
|
||||
{ok, Req, State}.
|
||||
|
||||
websocket_terminate(_Reason, _Req, _State) ->
|
||||
ok.
|
||||
|
|
|
@ -1,22 +1,8 @@
|
|||
%% Feel free to use, reuse and abuse the code in this file.
|
||||
|
||||
-module(ws_init_shutdown).
|
||||
-behaviour(cowboy_websocket_handler).
|
||||
-export([init/3]).
|
||||
-export([websocket_init/3, websocket_handle/3,
|
||||
websocket_info/3, websocket_terminate/3]).
|
||||
|
||||
init(_Any, _Req, _Opts) ->
|
||||
{upgrade, protocol, cowboy_websocket}.
|
||||
-export([init/2]).
|
||||
|
||||
websocket_init(_TransportName, Req, _Opts) ->
|
||||
{shutdown, cowboy_req:reply(403, Req)}.
|
||||
|
||||
websocket_handle(_Frame, _Req, _State) ->
|
||||
exit(badarg).
|
||||
|
||||
websocket_info(_Info, _Req, _State) ->
|
||||
exit(badarg).
|
||||
|
||||
websocket_terminate(_Reason, _Req, _State) ->
|
||||
exit(badarg).
|
||||
init(Req, _) ->
|
||||
{shutdown, cowboy_req:reply(403, Req), undefined}.
|
||||
|
|
|
@ -1,27 +1,17 @@
|
|||
%% Feel free to use, reuse and abuse the code in this file.
|
||||
|
||||
-module(ws_send_many).
|
||||
-behaviour(cowboy_websocket_handler).
|
||||
|
||||
-export([init/3]).
|
||||
-export([websocket_init/3]).
|
||||
-export([init/2]).
|
||||
-export([websocket_handle/3]).
|
||||
-export([websocket_info/3]).
|
||||
-export([websocket_terminate/3]).
|
||||
|
||||
init(_Any, _Req, _Opts) ->
|
||||
{upgrade, protocol, cowboy_websocket}.
|
||||
|
||||
websocket_init(_TransportName, Req, Sequence) ->
|
||||
Req2 = cowboy_req:compact(Req),
|
||||
init(Req, Opts) ->
|
||||
erlang:send_after(10, self(), send_many),
|
||||
{ok, Req2, Sequence}.
|
||||
{ws, Req, Opts}.
|
||||
|
||||
websocket_handle(_Frame, Req, State) ->
|
||||
{ok, Req, State}.
|
||||
|
||||
websocket_info(send_many, Req, State = [{sequence, Sequence}]) ->
|
||||
{reply, Sequence, Req, State}.
|
||||
|
||||
websocket_terminate(_Reason, _Req, _State) ->
|
||||
ok.
|
||||
|
|
|
@ -1,17 +1,14 @@
|
|||
%% Feel free to use, reuse and abuse the code in this file.
|
||||
|
||||
-module(ws_timeout_cancel).
|
||||
-behaviour(cowboy_websocket_handler).
|
||||
-export([init/3]).
|
||||
-export([websocket_init/3, websocket_handle/3,
|
||||
websocket_info/3, websocket_terminate/3]).
|
||||
|
||||
init(_Any, _Req, _Opts) ->
|
||||
{upgrade, protocol, cowboy_websocket}.
|
||||
-export([init/2]).
|
||||
-export([websocket_handle/3]).
|
||||
-export([websocket_info/3]).
|
||||
|
||||
websocket_init(_TransportName, Req, _Opts) ->
|
||||
init(Req, _) ->
|
||||
erlang:start_timer(500, self(), should_not_cancel_timer),
|
||||
{ok, Req, undefined, 1000}.
|
||||
{ws, Req, undefined, 1000}.
|
||||
|
||||
websocket_handle({text, Data}, Req, State) ->
|
||||
{reply, {text, Data}, Req, State};
|
||||
|
@ -21,6 +18,3 @@ websocket_handle({binary, Data}, Req, State) ->
|
|||
websocket_info(_Info, Req, State) ->
|
||||
erlang:start_timer(500, self(), should_not_cancel_timer),
|
||||
{ok, Req, State}.
|
||||
|
||||
websocket_terminate(_Reason, _Req, _State) ->
|
||||
ok.
|
||||
|
|
|
@ -1,22 +1,16 @@
|
|||
%% Feel free to use, reuse and abuse the code in this file.
|
||||
|
||||
-module(ws_timeout_hibernate).
|
||||
-behaviour(cowboy_websocket_handler).
|
||||
-export([init/3]).
|
||||
-export([websocket_init/3, websocket_handle/3,
|
||||
websocket_info/3, websocket_terminate/3]).
|
||||
|
||||
init(_Any, _Req, _Opts) ->
|
||||
{upgrade, protocol, cowboy_websocket}.
|
||||
-export([init/2]).
|
||||
-export([websocket_handle/3]).
|
||||
-export([websocket_info/3]).
|
||||
|
||||
websocket_init(_TransportName, Req, _Opts) ->
|
||||
{ok, Req, undefined, 1000, hibernate}.
|
||||
init(Req, _) ->
|
||||
{ws, Req, undefined, 1000, hibernate}.
|
||||
|
||||
websocket_handle(_Frame, Req, State) ->
|
||||
{ok, Req, State, hibernate}.
|
||||
|
||||
websocket_info(_Info, Req, State) ->
|
||||
{ok, Req, State, hibernate}.
|
||||
|
||||
websocket_terminate(_Reason, _Req, _State) ->
|
||||
ok.
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
%% Feel free to use, reuse and abuse the code in this file.
|
||||
|
||||
-module(ws_upgrade_with_opts).
|
||||
-behaviour(cowboy_websocket_handler).
|
||||
|
||||
-export([init/3]).
|
||||
-export([websocket_init/3]).
|
||||
-export([websocket_handle/3]).
|
||||
-export([websocket_info/3]).
|
||||
-export([websocket_terminate/3]).
|
||||
|
||||
init(_Any, Req, _Opts) ->
|
||||
{upgrade, protocol, cowboy_websocket, Req, <<"success">>}.
|
||||
|
||||
websocket_init(_TransportName, Req, Response) ->
|
||||
Req2 = cowboy_req:compact(Req),
|
||||
erlang:send_after(10, self(), send_response),
|
||||
{ok, Req2, Response}.
|
||||
|
||||
websocket_handle(_Frame, Req, State) ->
|
||||
{ok, Req, State}.
|
||||
|
||||
websocket_info(send_response, Req, State = Response)
|
||||
when is_binary(Response) ->
|
||||
{reply, {text, Response}, Req, State}.
|
||||
|
||||
websocket_terminate(_Reason, _Req, _State) ->
|
||||
ok.
|
Loading…
Add table
Add a link
Reference in a new issue