cowboy_handler.erl 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. %% Copyright (c) 2011-2013, Loïc Hoguin <essen@ninenines.eu>
  2. %%
  3. %% Permission to use, copy, modify, and/or distribute this software for any
  4. %% purpose with or without fee is hereby granted, provided that the above
  5. %% copyright notice and this permission notice appear in all copies.
  6. %%
  7. %% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  8. %% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  9. %% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  10. %% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  11. %% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  12. %% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  13. %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  14. %% @doc Handler middleware.
  15. %%
  16. %% Execute the handler given by the <em>handler</em> and <em>handler_opts</em>
  17. %% environment values. The result of this execution is added to the
  18. %% environment under the <em>result</em> value.
  19. %%
  20. %% When using loop handlers, we are receiving data from the socket because we
  21. %% want to know when the socket gets closed. This is generally not an issue
  22. %% because these kinds of requests are generally not pipelined, and don't have
  23. %% a body. If they do have a body, this body is often read in the
  24. %% <em>init/3</em> callback and this is no problem. Otherwise, this data
  25. %% accumulates in a buffer until we reach a certain threshold of 5000 bytes
  26. %% by default. This can be configured through the <em>loop_max_buffer</em>
  27. %% environment value. The request will be terminated with an
  28. %% <em>{error, overflow}</em> reason if this threshold is reached.
  29. %%
  30. %% @see cowboy_http_handler
  31. -module(cowboy_handler).
  32. -behaviour(cowboy_middleware).
  33. -export([execute/2]).
  34. -export([handler_loop/4]).
  35. -record(state, {
  36. env :: cowboy_middleware:env(),
  37. hibernate = false :: boolean(),
  38. loop_buffer_size = 0 :: non_neg_integer(),
  39. loop_max_buffer = 5000 :: non_neg_integer() | infinity,
  40. loop_timeout = infinity :: timeout(),
  41. loop_timeout_ref = undefined :: undefined | reference(),
  42. resp_sent = false :: boolean()
  43. }).
  44. %% @private
  45. -spec execute(Req, Env)
  46. -> {ok, Req, Env} | {error, 500, Req}
  47. | {suspend, ?MODULE, handler_loop, [any()]}
  48. when Req::cowboy_req:req(), Env::cowboy_middleware:env().
  49. execute(Req, Env) ->
  50. {_, Handler} = lists:keyfind(handler, 1, Env),
  51. {_, HandlerOpts} = lists:keyfind(handler_opts, 1, Env),
  52. MaxBuffer = case lists:keyfind(loop_max_buffer, 1, Env) of
  53. false -> 5000;
  54. {_, MaxBuffer0} -> MaxBuffer0
  55. end,
  56. handler_init(Req, #state{env=Env, loop_max_buffer=MaxBuffer},
  57. Handler, HandlerOpts).
  58. -spec handler_init(Req, #state{}, module(), any())
  59. -> {ok, Req, cowboy_middleware:env()}
  60. | {error, 500, Req} | {suspend, module(), atom(), [any()]}
  61. when Req::cowboy_req:req().
  62. handler_init(Req, State, Handler, HandlerOpts) ->
  63. Transport = cowboy_req:get(transport, Req),
  64. try Handler:init({Transport:name(), http}, Req, HandlerOpts) of
  65. {ok, Req2, HandlerState} ->
  66. handler_handle(Req2, State, Handler, HandlerState);
  67. {loop, Req2, HandlerState} ->
  68. handler_after_callback(Req2, State, Handler, HandlerState);
  69. {loop, Req2, HandlerState, hibernate} ->
  70. handler_after_callback(Req2, State#state{hibernate=true},
  71. Handler, HandlerState);
  72. {loop, Req2, HandlerState, Timeout} ->
  73. State2 = handler_loop_timeout(State#state{loop_timeout=Timeout}),
  74. handler_after_callback(Req2, State2, Handler, HandlerState);
  75. {loop, Req2, HandlerState, Timeout, hibernate} ->
  76. State2 = handler_loop_timeout(State#state{
  77. hibernate=true, loop_timeout=Timeout}),
  78. handler_after_callback(Req2, State2, Handler, HandlerState);
  79. {shutdown, Req2, HandlerState} ->
  80. terminate_request(Req2, State, Handler, HandlerState,
  81. {normal, shutdown});
  82. %% @todo {upgrade, transport, Module}
  83. {upgrade, protocol, Module} ->
  84. upgrade_protocol(Req, State, Handler, HandlerOpts, Module);
  85. {upgrade, protocol, Module, Req2, HandlerOpts2} ->
  86. upgrade_protocol(Req2, State, Handler, HandlerOpts2, Module)
  87. catch Class:Reason ->
  88. error_logger:error_msg(
  89. "** Cowboy handler ~p terminating in ~p/~p~n"
  90. " for the reason ~p:~p~n"
  91. "** Options were ~p~n"
  92. "** Request was ~p~n"
  93. "** Stacktrace: ~p~n~n",
  94. [Handler, init, 3, Class, Reason, HandlerOpts,
  95. cowboy_req:to_list(Req), erlang:get_stacktrace()]),
  96. error_terminate(Req, State)
  97. end.
  98. -spec upgrade_protocol(Req, #state{}, module(), any(), module())
  99. -> {ok, Req, Env}
  100. | {suspend, module(), atom(), any()}
  101. | {halt, Req}
  102. | {error, cowboy:http_status(), Req}
  103. when Req::cowboy_req:req(), Env::cowboy_middleware:env().
  104. upgrade_protocol(Req, #state{env=Env},
  105. Handler, HandlerOpts, Module) ->
  106. Module:upgrade(Req, Env, Handler, HandlerOpts).
  107. -spec handler_handle(Req, #state{}, module(), any())
  108. -> {ok, Req, cowboy_middleware:env()}
  109. | {error, 500, Req}
  110. when Req::cowboy_req:req().
  111. handler_handle(Req, State, Handler, HandlerState) ->
  112. try Handler:handle(Req, HandlerState) of
  113. {ok, Req2, HandlerState2} ->
  114. terminate_request(Req2, State, Handler, HandlerState2,
  115. {normal, shutdown})
  116. catch Class:Reason ->
  117. error_logger:error_msg(
  118. "** Cowboy handler ~p terminating in ~p/~p~n"
  119. " for the reason ~p:~p~n"
  120. "** Handler state was ~p~n"
  121. "** Request was ~p~n"
  122. "** Stacktrace: ~p~n~n",
  123. [Handler, handle, 2, Class, Reason, HandlerState,
  124. cowboy_req:to_list(Req), erlang:get_stacktrace()]),
  125. handler_terminate(Req, Handler, HandlerState, Reason),
  126. error_terminate(Req, State)
  127. end.
  128. %% Update the state if the response was sent in the callback.
  129. -spec handler_after_callback(Req, #state{}, module(), any())
  130. -> {ok, Req, cowboy_middleware:env()}
  131. | {error, 500, Req} | {suspend, module(), atom(), [any()]}
  132. when Req::cowboy_req:req().
  133. handler_after_callback(Req, State=#state{resp_sent=false}, Handler,
  134. HandlerState) ->
  135. receive
  136. {cowboy_req, resp_sent} ->
  137. handler_before_loop(Req, State#state{resp_sent=true}, Handler,
  138. HandlerState)
  139. after 0 ->
  140. handler_before_loop(Req, State, Handler, HandlerState)
  141. end;
  142. handler_after_callback(Req, State, Handler, HandlerState) ->
  143. handler_before_loop(Req, State, Handler, HandlerState).
  144. %% We don't listen for Transport closes because that would force us
  145. %% to receive data and buffer it indefinitely.
  146. -spec handler_before_loop(Req, #state{}, module(), any())
  147. -> {ok, Req, cowboy_middleware:env()}
  148. | {error, 500, Req} | {suspend, module(), atom(), [any()]}
  149. when Req::cowboy_req:req().
  150. handler_before_loop(Req, State=#state{hibernate=true}, Handler, HandlerState) ->
  151. [Socket, Transport] = cowboy_req:get([socket, transport], Req),
  152. Transport:setopts(Socket, [{active, once}]),
  153. {suspend, ?MODULE, handler_loop,
  154. [Req, State#state{hibernate=false}, Handler, HandlerState]};
  155. handler_before_loop(Req, State, Handler, HandlerState) ->
  156. [Socket, Transport] = cowboy_req:get([socket, transport], Req),
  157. Transport:setopts(Socket, [{active, once}]),
  158. handler_loop(Req, State, Handler, HandlerState).
  159. %% Almost the same code can be found in cowboy_websocket.
  160. -spec handler_loop_timeout(#state{}) -> #state{}.
  161. handler_loop_timeout(State=#state{loop_timeout=infinity}) ->
  162. State#state{loop_timeout_ref=undefined};
  163. handler_loop_timeout(State=#state{loop_timeout=Timeout,
  164. loop_timeout_ref=PrevRef}) ->
  165. _ = case PrevRef of
  166. undefined -> ignore;
  167. PrevRef -> erlang:cancel_timer(PrevRef)
  168. end,
  169. TRef = erlang:start_timer(Timeout, self(), ?MODULE),
  170. State#state{loop_timeout_ref=TRef}.
  171. %% @private
  172. -spec handler_loop(Req, #state{}, module(), any())
  173. -> {ok, Req, cowboy_middleware:env()}
  174. | {error, 500, Req} | {suspend, module(), atom(), [any()]}
  175. when Req::cowboy_req:req().
  176. handler_loop(Req, State=#state{loop_buffer_size=NbBytes,
  177. loop_max_buffer=Threshold, loop_timeout_ref=TRef},
  178. Handler, HandlerState) ->
  179. [Socket, Transport] = cowboy_req:get([socket, transport], Req),
  180. {OK, Closed, Error} = Transport:messages(),
  181. receive
  182. {OK, Socket, Data} ->
  183. NbBytes2 = NbBytes + byte_size(Data),
  184. if NbBytes2 > Threshold ->
  185. _ = handler_terminate(Req, Handler, HandlerState,
  186. {error, overflow}),
  187. error_terminate(Req, State);
  188. true ->
  189. Req2 = cowboy_req:append_buffer(Data, Req),
  190. State2 = handler_loop_timeout(State#state{
  191. loop_buffer_size=NbBytes2}),
  192. handler_before_loop(Req2, State2, Handler, HandlerState)
  193. end;
  194. {Closed, Socket} ->
  195. terminate_request(Req, State, Handler, HandlerState,
  196. {error, closed});
  197. {Error, Socket, Reason} ->
  198. terminate_request(Req, State, Handler, HandlerState,
  199. {error, Reason});
  200. {timeout, TRef, ?MODULE} ->
  201. handler_after_loop(Req, State, Handler, HandlerState,
  202. {normal, timeout});
  203. {timeout, OlderTRef, ?MODULE} when is_reference(OlderTRef) ->
  204. handler_before_loop(Req, State, Handler, HandlerState);
  205. Message ->
  206. %% We set the socket back to {active, false} mode in case
  207. %% the handler is going to call recv. We also flush any
  208. %% data received after that and put it into the buffer.
  209. %% We do not check the size here, if data keeps coming
  210. %% we'll error out on the next packet received.
  211. Transport:setopts(Socket, [{active, false}]),
  212. Req2 = receive {OK, Socket, Data} ->
  213. cowboy_req:append_buffer(Data, Req)
  214. after 0 ->
  215. Req
  216. end,
  217. handler_call(Req2, State, Handler, HandlerState, Message)
  218. end.
  219. -spec handler_call(Req, #state{}, module(), any(), any())
  220. -> {ok, Req, cowboy_middleware:env()}
  221. | {error, 500, Req} | {suspend, module(), atom(), [any()]}
  222. when Req::cowboy_req:req().
  223. handler_call(Req, State, Handler, HandlerState, Message) ->
  224. try Handler:info(Message, Req, HandlerState) of
  225. {ok, Req2, HandlerState2} ->
  226. handler_after_loop(Req2, State, Handler, HandlerState2,
  227. {normal, shutdown});
  228. {loop, Req2, HandlerState2} ->
  229. handler_after_callback(Req2, State, Handler, HandlerState2);
  230. {loop, Req2, HandlerState2, hibernate} ->
  231. handler_after_callback(Req2, State#state{hibernate=true},
  232. Handler, HandlerState2)
  233. catch Class:Reason ->
  234. error_logger:error_msg(
  235. "** Cowboy handler ~p terminating in ~p/~p~n"
  236. " for the reason ~p:~p~n"
  237. "** Handler state was ~p~n"
  238. "** Request was ~p~n"
  239. "** Stacktrace: ~p~n~n",
  240. [Handler, info, 3, Class, Reason, HandlerState,
  241. cowboy_req:to_list(Req), erlang:get_stacktrace()]),
  242. handler_terminate(Req, Handler, HandlerState, Reason),
  243. error_terminate(Req, State)
  244. end.
  245. %% It is sometimes important to make a socket passive as it was initially
  246. %% and as it is expected to be by cowboy_protocol, right after we're done
  247. %% with loop handling. The browser may freely pipeline a bunch of requests
  248. %% if previous one was, say, a JSONP long-polling request.
  249. -spec handler_after_loop(Req, #state{}, module(), any(),
  250. {normal, timeout | shutdown} | {error, atom()}) ->
  251. {ok, Req, cowboy_middleware:env()} when Req::cowboy_req:req().
  252. handler_after_loop(Req, State, Handler, HandlerState, Reason) ->
  253. [Socket, Transport] = cowboy_req:get([socket, transport], Req),
  254. Transport:setopts(Socket, [{active, false}]),
  255. {OK, _Closed, _Error} = Transport:messages(),
  256. Req2 = receive
  257. {OK, Socket, Data} ->
  258. cowboy_req:append_buffer(Data, Req)
  259. after 0 ->
  260. Req
  261. end,
  262. terminate_request(Req2, State, Handler, HandlerState, Reason).
  263. -spec terminate_request(Req, #state{}, module(), any(),
  264. {normal, timeout | shutdown} | {error, atom()}) ->
  265. {ok, Req, cowboy_middleware:env()} when Req::cowboy_req:req().
  266. terminate_request(Req, #state{env=Env}, Handler, HandlerState, Reason) ->
  267. HandlerRes = handler_terminate(Req, Handler, HandlerState, Reason),
  268. {ok, Req, [{result, HandlerRes}|Env]}.
  269. -spec handler_terminate(cowboy_req:req(), module(), any(),
  270. {normal, timeout | shutdown} | {error, atom()}) -> ok.
  271. handler_terminate(Req, Handler, HandlerState, Reason) ->
  272. try
  273. Handler:terminate(Reason, cowboy_req:lock(Req), HandlerState)
  274. catch Class:Reason2 ->
  275. error_logger:error_msg(
  276. "** Cowboy handler ~p terminating in ~p/~p~n"
  277. " for the reason ~p:~p~n"
  278. "** Handler state was ~p~n"
  279. "** Request was ~p~n"
  280. "** Stacktrace: ~p~n~n",
  281. [Handler, terminate, 3, Class, Reason2, HandlerState,
  282. cowboy_req:to_list(Req), erlang:get_stacktrace()])
  283. end.
  284. %% Only send an error reply if there is no resp_sent message.
  285. -spec error_terminate(Req, #state{})
  286. -> {error, 500, Req} | {halt, Req} when Req::cowboy_req:req().
  287. error_terminate(Req, #state{resp_sent=true}) ->
  288. %% Close the connection, but do not attempt sending a reply.
  289. {halt, cowboy_req:set([{connection, close}, {resp_state, done}], Req)};
  290. error_terminate(Req, _) ->
  291. {error, 500, Req}.