cowboy_rest.erl 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294
  1. %% Copyright (c) 2011-2017, 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. %% Originally based on the Webmachine Diagram from Alan Dean and
  15. %% Justin Sheehy.
  16. -module(cowboy_rest).
  17. -behaviour(cowboy_sub_protocol).
  18. -ifdef(OTP_RELEASE).
  19. -compile({nowarn_deprecated_function, [{erlang, get_stacktrace, 0}]}).
  20. -endif.
  21. -export([upgrade/4]).
  22. -export([upgrade/5]).
  23. -type switch_handler() :: {switch_handler, module()}
  24. | {switch_handler, module(), any()}.
  25. %% Common handler callbacks.
  26. -callback init(Req, any())
  27. -> {ok | module(), Req, any()}
  28. | {module(), Req, any(), any()}
  29. when Req::cowboy_req:req().
  30. -callback terminate(any(), cowboy_req:req(), any()) -> ok.
  31. -optional_callbacks([terminate/3]).
  32. %% REST handler callbacks.
  33. -callback allowed_methods(Req, State)
  34. -> {[binary()], Req, State}
  35. | {stop, Req, State}
  36. | {switch_handler(), Req, State}
  37. when Req::cowboy_req:req(), State::any().
  38. -optional_callbacks([allowed_methods/2]).
  39. -callback allow_missing_post(Req, State)
  40. -> {boolean(), Req, State}
  41. | {stop, Req, State}
  42. | {switch_handler(), Req, State}
  43. when Req::cowboy_req:req(), State::any().
  44. -optional_callbacks([allow_missing_post/2]).
  45. -callback charsets_provided(Req, State)
  46. -> {[binary()], Req, State}
  47. | {stop, Req, State}
  48. | {switch_handler(), Req, State}
  49. when Req::cowboy_req:req(), State::any().
  50. -optional_callbacks([charsets_provided/2]).
  51. -callback content_types_accepted(Req, State)
  52. -> {[{binary() | {binary(), binary(), '*' | [{binary(), binary()}]}, atom()}], Req, State}
  53. | {stop, Req, State}
  54. | {switch_handler(), Req, State}
  55. when Req::cowboy_req:req(), State::any().
  56. -optional_callbacks([content_types_accepted/2]).
  57. -callback content_types_provided(Req, State)
  58. -> {[{binary() | {binary(), binary(), '*' | [{binary(), binary()}]}, atom()}], Req, State}
  59. | {stop, Req, State}
  60. | {switch_handler(), Req, State}
  61. when Req::cowboy_req:req(), State::any().
  62. -optional_callbacks([content_types_provided/2]).
  63. -callback delete_completed(Req, State)
  64. -> {boolean(), Req, State}
  65. | {stop, Req, State}
  66. | {switch_handler(), Req, State}
  67. when Req::cowboy_req:req(), State::any().
  68. -optional_callbacks([delete_completed/2]).
  69. -callback delete_resource(Req, State)
  70. -> {boolean(), Req, State}
  71. | {stop, Req, State}
  72. | {switch_handler(), Req, State}
  73. when Req::cowboy_req:req(), State::any().
  74. -optional_callbacks([delete_resource/2]).
  75. -callback expires(Req, State)
  76. -> {calendar:datetime() | binary() | undefined, Req, State}
  77. when Req::cowboy_req:req(), State::any().
  78. -optional_callbacks([expires/2]).
  79. -callback forbidden(Req, State)
  80. -> {boolean(), Req, State}
  81. | {stop, Req, State}
  82. | {switch_handler(), Req, State}
  83. when Req::cowboy_req:req(), State::any().
  84. -optional_callbacks([forbidden/2]).
  85. -callback generate_etag(Req, State)
  86. -> {binary() | {weak | strong, binary()}, Req, State}
  87. when Req::cowboy_req:req(), State::any().
  88. -optional_callbacks([generate_etag/2]).
  89. -callback is_authorized(Req, State)
  90. -> {true | {false, iodata()}, Req, State}
  91. | {stop, Req, State}
  92. | {switch_handler(), Req, State}
  93. when Req::cowboy_req:req(), State::any().
  94. -optional_callbacks([is_authorized/2]).
  95. -callback is_conflict(Req, State)
  96. -> {boolean(), Req, State}
  97. | {stop, Req, State}
  98. | {switch_handler(), Req, State}
  99. when Req::cowboy_req:req(), State::any().
  100. -optional_callbacks([is_conflict/2]).
  101. -callback known_methods(Req, State)
  102. -> {[binary()], Req, State}
  103. | {stop, Req, State}
  104. | {switch_handler(), Req, State}
  105. when Req::cowboy_req:req(), State::any().
  106. -optional_callbacks([known_methods/2]).
  107. -callback languages_provided(Req, State)
  108. -> {[binary()], Req, State}
  109. | {stop, Req, State}
  110. | {switch_handler(), Req, State}
  111. when Req::cowboy_req:req(), State::any().
  112. -optional_callbacks([languages_provided/2]).
  113. -callback last_modified(Req, State)
  114. -> {calendar:datetime(), Req, State}
  115. when Req::cowboy_req:req(), State::any().
  116. -optional_callbacks([last_modified/2]).
  117. -callback malformed_request(Req, State)
  118. -> {boolean(), Req, State}
  119. | {stop, Req, State}
  120. | {switch_handler(), Req, State}
  121. when Req::cowboy_req:req(), State::any().
  122. -optional_callbacks([malformed_request/2]).
  123. -callback moved_permanently(Req, State)
  124. -> {{true, iodata()} | false, Req, State}
  125. | {stop, Req, State}
  126. | {switch_handler(), Req, State}
  127. when Req::cowboy_req:req(), State::any().
  128. -optional_callbacks([moved_permanently/2]).
  129. -callback moved_temporarily(Req, State)
  130. -> {{true, iodata()} | false, Req, State}
  131. | {stop, Req, State}
  132. | {switch_handler(), Req, State}
  133. when Req::cowboy_req:req(), State::any().
  134. -optional_callbacks([moved_temporarily/2]).
  135. -callback multiple_choices(Req, State)
  136. -> {boolean(), Req, State}
  137. | {stop, Req, State}
  138. | {switch_handler(), Req, State}
  139. when Req::cowboy_req:req(), State::any().
  140. -optional_callbacks([multiple_choices/2]).
  141. -callback options(Req, State)
  142. -> {ok, Req, State}
  143. | {stop, Req, State}
  144. | {switch_handler(), Req, State}
  145. when Req::cowboy_req:req(), State::any().
  146. -optional_callbacks([options/2]).
  147. -callback previously_existed(Req, State)
  148. -> {boolean(), Req, State}
  149. | {stop, Req, State}
  150. | {switch_handler(), Req, State}
  151. when Req::cowboy_req:req(), State::any().
  152. -optional_callbacks([previously_existed/2]).
  153. -callback rate_limited(Req, State)
  154. -> {{true, non_neg_integer() | calendar:datetime()} | false, Req, State}
  155. | {stop, Req, State}
  156. | {switch_handler(), Req, State}
  157. when Req::cowboy_req:req(), State::any().
  158. -optional_callbacks([rate_limited/2]).
  159. -callback resource_exists(Req, State)
  160. -> {boolean(), Req, State}
  161. | {stop, Req, State}
  162. | {switch_handler(), Req, State}
  163. when Req::cowboy_req:req(), State::any().
  164. -optional_callbacks([resource_exists/2]).
  165. -callback service_available(Req, State)
  166. -> {boolean(), Req, State}
  167. | {stop, Req, State}
  168. | {switch_handler(), Req, State}
  169. when Req::cowboy_req:req(), State::any().
  170. -optional_callbacks([service_available/2]).
  171. -callback uri_too_long(Req, State)
  172. -> {boolean(), Req, State}
  173. | {stop, Req, State}
  174. | {switch_handler(), Req, State}
  175. when Req::cowboy_req:req(), State::any().
  176. -optional_callbacks([uri_too_long/2]).
  177. -callback valid_content_headers(Req, State)
  178. -> {boolean(), Req, State}
  179. | {stop, Req, State}
  180. | {switch_handler(), Req, State}
  181. when Req::cowboy_req:req(), State::any().
  182. -optional_callbacks([valid_content_headers/2]).
  183. -callback valid_entity_length(Req, State)
  184. -> {boolean(), Req, State}
  185. | {stop, Req, State}
  186. | {switch_handler(), Req, State}
  187. when Req::cowboy_req:req(), State::any().
  188. -optional_callbacks([valid_entity_length/2]).
  189. -callback variances(Req, State)
  190. -> {[binary()], Req, State}
  191. when Req::cowboy_req:req(), State::any().
  192. -optional_callbacks([variances/2]).
  193. %% End of REST callbacks. Whew!
  194. -record(state, {
  195. method = undefined :: binary(),
  196. %% Handler.
  197. handler :: atom(),
  198. handler_state :: any(),
  199. %% Allowed methods. Only used for OPTIONS requests.
  200. allowed_methods :: [binary()] | undefined,
  201. %% Media type.
  202. content_types_p = [] ::
  203. [{binary() | {binary(), binary(), [{binary(), binary()}] | '*'},
  204. atom()}],
  205. content_type_a :: undefined
  206. | {binary() | {binary(), binary(), [{binary(), binary()}] | '*'},
  207. atom()},
  208. %% Language.
  209. languages_p = [] :: [binary()],
  210. language_a :: undefined | binary(),
  211. %% Charset.
  212. charsets_p = undefined :: undefined | [binary()],
  213. charset_a :: undefined | binary(),
  214. %% Whether the resource exists.
  215. exists = false :: boolean(),
  216. %% Cached resource calls.
  217. etag :: undefined | no_call | {strong | weak, binary()},
  218. last_modified :: undefined | no_call | calendar:datetime(),
  219. expires :: undefined | no_call | calendar:datetime() | binary()
  220. }).
  221. -spec upgrade(Req, Env, module(), any())
  222. -> {ok, Req, Env} when Req::cowboy_req:req(), Env::cowboy_middleware:env().
  223. upgrade(Req0, Env, Handler, HandlerState0) ->
  224. Method = cowboy_req:method(Req0),
  225. case service_available(Req0, #state{method=Method,
  226. handler=Handler, handler_state=HandlerState0}) of
  227. {ok, Req, Result} ->
  228. {ok, Req, Env#{result => Result}};
  229. {Mod, Req, HandlerState} ->
  230. Mod:upgrade(Req, Env, Handler, HandlerState);
  231. {Mod, Req, HandlerState, Opts} ->
  232. Mod:upgrade(Req, Env, Handler, HandlerState, Opts)
  233. end.
  234. -spec upgrade(Req, Env, module(), any(), any())
  235. -> {ok, Req, Env} when Req::cowboy_req:req(), Env::cowboy_middleware:env().
  236. %% cowboy_rest takes no options.
  237. upgrade(Req, Env, Handler, HandlerState, _Opts) ->
  238. upgrade(Req, Env, Handler, HandlerState).
  239. service_available(Req, State) ->
  240. expect(Req, State, service_available, true, fun known_methods/2, 503).
  241. %% known_methods/2 should return a list of binary methods.
  242. known_methods(Req, State=#state{method=Method}) ->
  243. case call(Req, State, known_methods) of
  244. no_call when Method =:= <<"HEAD">>; Method =:= <<"GET">>;
  245. Method =:= <<"POST">>; Method =:= <<"PUT">>;
  246. Method =:= <<"PATCH">>; Method =:= <<"DELETE">>;
  247. Method =:= <<"OPTIONS">> ->
  248. next(Req, State, fun uri_too_long/2);
  249. no_call ->
  250. next(Req, State, 501);
  251. {stop, Req2, HandlerState} ->
  252. terminate(Req2, State#state{handler_state=HandlerState});
  253. {Switch, Req2, HandlerState} when element(1, Switch) =:= switch_handler ->
  254. switch_handler(Switch, Req2, HandlerState);
  255. {List, Req2, HandlerState} ->
  256. State2 = State#state{handler_state=HandlerState},
  257. case lists:member(Method, List) of
  258. true -> next(Req2, State2, fun uri_too_long/2);
  259. false -> next(Req2, State2, 501)
  260. end
  261. end.
  262. uri_too_long(Req, State) ->
  263. expect(Req, State, uri_too_long, false, fun allowed_methods/2, 414).
  264. %% allowed_methods/2 should return a list of binary methods.
  265. allowed_methods(Req, State=#state{method=Method}) ->
  266. case call(Req, State, allowed_methods) of
  267. no_call when Method =:= <<"HEAD">>; Method =:= <<"GET">> ->
  268. next(Req, State, fun malformed_request/2);
  269. no_call when Method =:= <<"OPTIONS">> ->
  270. next(Req, State#state{allowed_methods=
  271. [<<"HEAD">>, <<"GET">>, <<"OPTIONS">>]},
  272. fun malformed_request/2);
  273. no_call ->
  274. method_not_allowed(Req, State,
  275. [<<"HEAD">>, <<"GET">>, <<"OPTIONS">>]);
  276. {stop, Req2, HandlerState} ->
  277. terminate(Req2, State#state{handler_state=HandlerState});
  278. {Switch, Req2, HandlerState} when element(1, Switch) =:= switch_handler ->
  279. switch_handler(Switch, Req2, HandlerState);
  280. {List, Req2, HandlerState} ->
  281. State2 = State#state{handler_state=HandlerState},
  282. case lists:member(Method, List) of
  283. true when Method =:= <<"OPTIONS">> ->
  284. next(Req2, State2#state{allowed_methods=List},
  285. fun malformed_request/2);
  286. true ->
  287. next(Req2, State2, fun malformed_request/2);
  288. false ->
  289. method_not_allowed(Req2, State2, List)
  290. end
  291. end.
  292. method_not_allowed(Req, State, []) ->
  293. Req2 = cowboy_req:set_resp_header(<<"allow">>, <<>>, Req),
  294. respond(Req2, State, 405);
  295. method_not_allowed(Req, State, Methods) ->
  296. << ", ", Allow/binary >> = << << ", ", M/binary >> || M <- Methods >>,
  297. Req2 = cowboy_req:set_resp_header(<<"allow">>, Allow, Req),
  298. respond(Req2, State, 405).
  299. malformed_request(Req, State) ->
  300. expect(Req, State, malformed_request, false, fun is_authorized/2, 400).
  301. %% is_authorized/2 should return true or {false, WwwAuthenticateHeader}.
  302. is_authorized(Req, State) ->
  303. case call(Req, State, is_authorized) of
  304. no_call ->
  305. forbidden(Req, State);
  306. {stop, Req2, HandlerState} ->
  307. terminate(Req2, State#state{handler_state=HandlerState});
  308. {Switch, Req2, HandlerState} when element(1, Switch) =:= switch_handler ->
  309. switch_handler(Switch, Req2, HandlerState);
  310. {true, Req2, HandlerState} ->
  311. forbidden(Req2, State#state{handler_state=HandlerState});
  312. {{false, AuthHead}, Req2, HandlerState} ->
  313. Req3 = cowboy_req:set_resp_header(
  314. <<"www-authenticate">>, AuthHead, Req2),
  315. respond(Req3, State#state{handler_state=HandlerState}, 401)
  316. end.
  317. forbidden(Req, State) ->
  318. expect(Req, State, forbidden, false, fun rate_limited/2, 403).
  319. rate_limited(Req, State) ->
  320. case call(Req, State, rate_limited) of
  321. no_call ->
  322. valid_content_headers(Req, State);
  323. {stop, Req2, HandlerState} ->
  324. terminate(Req2, State#state{handler_state=HandlerState});
  325. {Switch, Req2, HandlerState} when element(1, Switch) =:= switch_handler ->
  326. switch_handler(Switch, Req2, HandlerState);
  327. {false, Req2, HandlerState} ->
  328. valid_content_headers(Req2, State#state{handler_state=HandlerState});
  329. {{true, RetryAfter0}, Req2, HandlerState} ->
  330. RetryAfter = if
  331. is_integer(RetryAfter0), RetryAfter0 >= 0 ->
  332. integer_to_binary(RetryAfter0);
  333. is_tuple(RetryAfter0) ->
  334. cowboy_clock:rfc1123(RetryAfter0)
  335. end,
  336. Req3 = cowboy_req:set_resp_header(<<"retry-after">>, RetryAfter, Req2),
  337. respond(Req3, State#state{handler_state=HandlerState}, 429)
  338. end.
  339. valid_content_headers(Req, State) ->
  340. expect(Req, State, valid_content_headers, true,
  341. fun valid_entity_length/2, 501).
  342. valid_entity_length(Req, State) ->
  343. expect(Req, State, valid_entity_length, true, fun options/2, 413).
  344. %% If you need to add additional headers to the response at this point,
  345. %% you should do it directly in the options/2 call using set_resp_headers.
  346. options(Req, State=#state{allowed_methods=Methods, method= <<"OPTIONS">>}) ->
  347. case call(Req, State, options) of
  348. no_call when Methods =:= [] ->
  349. Req2 = cowboy_req:set_resp_header(<<"allow">>, <<>>, Req),
  350. respond(Req2, State, 200);
  351. no_call ->
  352. << ", ", Allow/binary >>
  353. = << << ", ", M/binary >> || M <- Methods >>,
  354. Req2 = cowboy_req:set_resp_header(<<"allow">>, Allow, Req),
  355. respond(Req2, State, 200);
  356. {Switch, Req2, HandlerState} when element(1, Switch) =:= switch_handler ->
  357. switch_handler(Switch, Req2, HandlerState);
  358. {stop, Req2, HandlerState} ->
  359. terminate(Req2, State#state{handler_state=HandlerState});
  360. {ok, Req2, HandlerState} ->
  361. respond(Req2, State#state{handler_state=HandlerState}, 200)
  362. end;
  363. options(Req, State) ->
  364. content_types_provided(Req, State).
  365. %% content_types_provided/2 should return a list of content types and their
  366. %% associated callback function as a tuple: {{Type, SubType, Params}, Fun}.
  367. %% Type and SubType are the media type as binary. Params is a list of
  368. %% Key/Value tuple, with Key and Value a binary. Fun is the name of the
  369. %% callback that will be used to return the content of the response. It is
  370. %% given as an atom.
  371. %%
  372. %% An example of such return value would be:
  373. %% {{<<"text">>, <<"html">>, []}, to_html}
  374. %%
  375. %% Note that it is also possible to return a binary content type that will
  376. %% then be parsed by Cowboy. However note that while this may make your
  377. %% resources a little more readable, this is a lot less efficient.
  378. %%
  379. %% An example of such return value would be:
  380. %% {<<"text/html">>, to_html}
  381. content_types_provided(Req, State) ->
  382. case call(Req, State, content_types_provided) of
  383. no_call ->
  384. State2 = State#state{
  385. content_types_p=[{{<<"text">>, <<"html">>, '*'}, to_html}]},
  386. try cowboy_req:parse_header(<<"accept">>, Req) of
  387. undefined ->
  388. languages_provided(
  389. Req#{media_type => {<<"text">>, <<"html">>, []}},
  390. State2#state{content_type_a={{<<"text">>, <<"html">>, []}, to_html}});
  391. Accept ->
  392. choose_media_type(Req, State2, prioritize_accept(Accept))
  393. catch _:_ ->
  394. respond(Req, State2, 400)
  395. end;
  396. {stop, Req2, HandlerState} ->
  397. terminate(Req2, State#state{handler_state=HandlerState});
  398. {Switch, Req2, HandlerState} when element(1, Switch) =:= switch_handler ->
  399. switch_handler(Switch, Req2, HandlerState);
  400. {[], Req2, HandlerState} ->
  401. not_acceptable(Req2, State#state{handler_state=HandlerState});
  402. {CTP, Req2, HandlerState} ->
  403. CTP2 = [normalize_content_types(P) || P <- CTP],
  404. State2 = State#state{
  405. handler_state=HandlerState, content_types_p=CTP2},
  406. try cowboy_req:parse_header(<<"accept">>, Req2) of
  407. undefined ->
  408. {PMT, _Fun} = HeadCTP = hd(CTP2),
  409. languages_provided(
  410. Req2#{media_type => PMT},
  411. State2#state{content_type_a=HeadCTP});
  412. Accept ->
  413. choose_media_type(Req2, State2, prioritize_accept(Accept))
  414. catch _:_ ->
  415. respond(Req2, State2, 400)
  416. end
  417. end.
  418. normalize_content_types({ContentType, Callback})
  419. when is_binary(ContentType) ->
  420. {cow_http_hd:parse_content_type(ContentType), Callback};
  421. normalize_content_types(Normalized) ->
  422. Normalized.
  423. prioritize_accept(Accept) ->
  424. lists:sort(
  425. fun ({MediaTypeA, Quality, _AcceptParamsA},
  426. {MediaTypeB, Quality, _AcceptParamsB}) ->
  427. %% Same quality, check precedence in more details.
  428. prioritize_mediatype(MediaTypeA, MediaTypeB);
  429. ({_MediaTypeA, QualityA, _AcceptParamsA},
  430. {_MediaTypeB, QualityB, _AcceptParamsB}) ->
  431. %% Just compare the quality.
  432. QualityA > QualityB
  433. end, Accept).
  434. %% Media ranges can be overridden by more specific media ranges or
  435. %% specific media types. If more than one media range applies to a given
  436. %% type, the most specific reference has precedence.
  437. %%
  438. %% We always choose B over A when we can't decide between the two.
  439. prioritize_mediatype({TypeA, SubTypeA, ParamsA}, {TypeB, SubTypeB, ParamsB}) ->
  440. case TypeB of
  441. TypeA ->
  442. case SubTypeB of
  443. SubTypeA -> length(ParamsA) > length(ParamsB);
  444. <<"*">> -> true;
  445. _Any -> false
  446. end;
  447. <<"*">> -> true;
  448. _Any -> false
  449. end.
  450. %% Ignoring the rare AcceptParams. Not sure what should be done about them.
  451. choose_media_type(Req, State, []) ->
  452. not_acceptable(Req, State);
  453. choose_media_type(Req, State=#state{content_types_p=CTP},
  454. [MediaType|Tail]) ->
  455. match_media_type(Req, State, Tail, CTP, MediaType).
  456. match_media_type(Req, State, Accept, [], _MediaType) ->
  457. choose_media_type(Req, State, Accept);
  458. match_media_type(Req, State, Accept, CTP,
  459. MediaType = {{<<"*">>, <<"*">>, _Params_A}, _QA, _APA}) ->
  460. match_media_type_params(Req, State, Accept, CTP, MediaType);
  461. match_media_type(Req, State, Accept,
  462. CTP = [{{Type, SubType_P, _PP}, _Fun}|_Tail],
  463. MediaType = {{Type, SubType_A, _PA}, _QA, _APA})
  464. when SubType_P =:= SubType_A; SubType_A =:= <<"*">> ->
  465. match_media_type_params(Req, State, Accept, CTP, MediaType);
  466. match_media_type(Req, State, Accept, [_Any|Tail], MediaType) ->
  467. match_media_type(Req, State, Accept, Tail, MediaType).
  468. match_media_type_params(Req, State, Accept,
  469. [Provided = {{TP, STP, '*'}, _Fun}|Tail],
  470. MediaType = {{TA, _STA, Params_A0}, _QA, _APA}) ->
  471. case lists:keytake(<<"charset">>, 1, Params_A0) of
  472. {value, {_, Charset}, Params_A} when TA =:= <<"text">> ->
  473. %% When we match against a wildcard, the media type is text
  474. %% and has a charset parameter, we call charsets_provided
  475. %% and check that the charset is provided. If the callback
  476. %% is not exported, we accept inconditionally but ignore
  477. %% the given charset so as to not send a wrong value back.
  478. case call(Req, State, charsets_provided) of
  479. no_call ->
  480. languages_provided(Req#{media_type => {TP, STP, Params_A0}},
  481. State#state{content_type_a=Provided});
  482. {stop, Req2, HandlerState} ->
  483. terminate(Req2, State#state{handler_state=HandlerState});
  484. {Switch, Req2, HandlerState} when element(1, Switch) =:= switch_handler ->
  485. switch_handler(Switch, Req2, HandlerState);
  486. {CP, Req2, HandlerState} ->
  487. State2 = State#state{handler_state=HandlerState, charsets_p=CP},
  488. case lists:member(Charset, CP) of
  489. false ->
  490. match_media_type(Req2, State2, Accept, Tail, MediaType);
  491. true ->
  492. languages_provided(Req2#{media_type => {TP, STP, Params_A}},
  493. State2#state{content_type_a=Provided,
  494. charset_a=Charset})
  495. end
  496. end;
  497. _ ->
  498. languages_provided(Req#{media_type => {TP, STP, Params_A0}},
  499. State#state{content_type_a=Provided})
  500. end;
  501. match_media_type_params(Req, State, Accept,
  502. [Provided = {PMT = {TP, STP, Params_P0}, Fun}|Tail],
  503. MediaType = {{_TA, _STA, Params_A}, _QA, _APA}) ->
  504. case lists:sort(Params_P0) =:= lists:sort(Params_A) of
  505. true when TP =:= <<"text">> ->
  506. %% When a charset was provided explicitly in both the charset header
  507. %% and the media types provided and the negotiation is successful,
  508. %% we keep the charset and don't call charsets_provided. This only
  509. %% applies to text media types, however.
  510. {Charset, Params_P} = case lists:keytake(<<"charset">>, 1, Params_P0) of
  511. false -> {undefined, Params_P0};
  512. {value, {_, Charset0}, Params_P1} -> {Charset0, Params_P1}
  513. end,
  514. languages_provided(Req#{media_type => {TP, STP, Params_P}},
  515. State#state{content_type_a={{TP, STP, Params_P}, Fun},
  516. charset_a=Charset});
  517. true ->
  518. languages_provided(Req#{media_type => PMT},
  519. State#state{content_type_a=Provided});
  520. false ->
  521. match_media_type(Req, State, Accept, Tail, MediaType)
  522. end.
  523. %% languages_provided should return a list of binary values indicating
  524. %% which languages are accepted by the resource.
  525. %%
  526. %% @todo I suppose we should also ask the resource if it wants to
  527. %% set a language itself or if it wants it to be automatically chosen.
  528. languages_provided(Req, State) ->
  529. case call(Req, State, languages_provided) of
  530. no_call ->
  531. charsets_provided(Req, State);
  532. {stop, Req2, HandlerState} ->
  533. terminate(Req2, State#state{handler_state=HandlerState});
  534. {Switch, Req2, HandlerState} when element(1, Switch) =:= switch_handler ->
  535. switch_handler(Switch, Req2, HandlerState);
  536. {[], Req2, HandlerState} ->
  537. not_acceptable(Req2, State#state{handler_state=HandlerState});
  538. {LP, Req2, HandlerState} ->
  539. State2 = State#state{handler_state=HandlerState, languages_p=LP},
  540. case cowboy_req:parse_header(<<"accept-language">>, Req2) of
  541. undefined ->
  542. set_language(Req2, State2#state{language_a=hd(LP)});
  543. AcceptLanguage ->
  544. AcceptLanguage2 = prioritize_languages(AcceptLanguage),
  545. choose_language(Req2, State2, AcceptLanguage2)
  546. end
  547. end.
  548. %% A language-range matches a language-tag if it exactly equals the tag,
  549. %% or if it exactly equals a prefix of the tag such that the first tag
  550. %% character following the prefix is "-". The special range "*", if
  551. %% present in the Accept-Language field, matches every tag not matched
  552. %% by any other range present in the Accept-Language field.
  553. %%
  554. %% @todo The last sentence probably means we should always put '*'
  555. %% at the end of the list.
  556. prioritize_languages(AcceptLanguages) ->
  557. lists:sort(
  558. fun ({_TagA, QualityA}, {_TagB, QualityB}) ->
  559. QualityA > QualityB
  560. end, AcceptLanguages).
  561. choose_language(Req, State, []) ->
  562. not_acceptable(Req, State);
  563. choose_language(Req, State=#state{languages_p=LP}, [Language|Tail]) ->
  564. match_language(Req, State, Tail, LP, Language).
  565. match_language(Req, State, Accept, [], _Language) ->
  566. choose_language(Req, State, Accept);
  567. match_language(Req, State, _Accept, [Provided|_Tail], {'*', _Quality}) ->
  568. set_language(Req, State#state{language_a=Provided});
  569. match_language(Req, State, _Accept, [Provided|_Tail], {Provided, _Quality}) ->
  570. set_language(Req, State#state{language_a=Provided});
  571. match_language(Req, State, Accept, [Provided|Tail],
  572. Language = {Tag, _Quality}) ->
  573. Length = byte_size(Tag),
  574. case Provided of
  575. << Tag:Length/binary, $-, _Any/bits >> ->
  576. set_language(Req, State#state{language_a=Provided});
  577. _Any ->
  578. match_language(Req, State, Accept, Tail, Language)
  579. end.
  580. set_language(Req, State=#state{language_a=Language}) ->
  581. Req2 = cowboy_req:set_resp_header(<<"content-language">>, Language, Req),
  582. charsets_provided(Req2#{language => Language}, State).
  583. %% charsets_provided should return a list of binary values indicating
  584. %% which charsets are accepted by the resource.
  585. %%
  586. %% A charset may have been selected while negotiating the accept header.
  587. %% There's no need to select one again.
  588. charsets_provided(Req, State=#state{charset_a=Charset})
  589. when Charset =/= undefined ->
  590. set_content_type(Req, State);
  591. %% If charsets_p is defined, use it instead of calling charsets_provided
  592. %% again. We also call this clause during normal execution to avoid
  593. %% duplicating code.
  594. charsets_provided(Req, State=#state{charsets_p=[]}) ->
  595. not_acceptable(Req, State);
  596. charsets_provided(Req, State=#state{charsets_p=CP})
  597. when CP =/= undefined ->
  598. case cowboy_req:parse_header(<<"accept-charset">>, Req) of
  599. undefined ->
  600. set_content_type(Req, State#state{charset_a=hd(CP)});
  601. AcceptCharset0 ->
  602. AcceptCharset = prioritize_charsets(AcceptCharset0),
  603. choose_charset(Req, State, AcceptCharset)
  604. end;
  605. charsets_provided(Req, State) ->
  606. case call(Req, State, charsets_provided) of
  607. no_call ->
  608. set_content_type(Req, State);
  609. {stop, Req2, HandlerState} ->
  610. terminate(Req2, State#state{handler_state=HandlerState});
  611. {Switch, Req2, HandlerState} when element(1, Switch) =:= switch_handler ->
  612. switch_handler(Switch, Req2, HandlerState);
  613. {CP, Req2, HandlerState} ->
  614. charsets_provided(Req2, State#state{handler_state=HandlerState, charsets_p=CP})
  615. end.
  616. prioritize_charsets(AcceptCharsets) ->
  617. lists:sort(
  618. fun ({_CharsetA, QualityA}, {_CharsetB, QualityB}) ->
  619. QualityA > QualityB
  620. end, AcceptCharsets).
  621. choose_charset(Req, State, []) ->
  622. not_acceptable(Req, State);
  623. %% A q-value of 0 means not acceptable.
  624. choose_charset(Req, State, [{_, 0}|Tail]) ->
  625. choose_charset(Req, State, Tail);
  626. choose_charset(Req, State=#state{charsets_p=CP}, [Charset|Tail]) ->
  627. match_charset(Req, State, Tail, CP, Charset).
  628. match_charset(Req, State, Accept, [], _Charset) ->
  629. choose_charset(Req, State, Accept);
  630. match_charset(Req, State, _Accept, [Provided|_], {<<"*">>, _}) ->
  631. set_content_type(Req, State#state{charset_a=Provided});
  632. match_charset(Req, State, _Accept, [Provided|_], {Provided, _}) ->
  633. set_content_type(Req, State#state{charset_a=Provided});
  634. match_charset(Req, State, Accept, [_|Tail], Charset) ->
  635. match_charset(Req, State, Accept, Tail, Charset).
  636. set_content_type(Req, State=#state{
  637. content_type_a={{Type, SubType, Params}, _Fun},
  638. charset_a=Charset}) ->
  639. ParamsBin = set_content_type_build_params(Params, []),
  640. ContentType = [Type, <<"/">>, SubType, ParamsBin],
  641. ContentType2 = case {Type, Charset} of
  642. {<<"text">>, Charset} when Charset =/= undefined ->
  643. [ContentType, <<"; charset=">>, Charset];
  644. _ ->
  645. ContentType
  646. end,
  647. Req2 = cowboy_req:set_resp_header(<<"content-type">>, ContentType2, Req),
  648. encodings_provided(Req2#{charset => Charset}, State).
  649. set_content_type_build_params('*', []) ->
  650. <<>>;
  651. set_content_type_build_params([], []) ->
  652. <<>>;
  653. set_content_type_build_params([], Acc) ->
  654. lists:reverse(Acc);
  655. set_content_type_build_params([{Attr, Value}|Tail], Acc) ->
  656. set_content_type_build_params(Tail, [[Attr, <<"=">>, Value], <<";">>|Acc]).
  657. %% @todo Match for identity as we provide nothing else for now.
  658. %% @todo Don't forget to set the Content-Encoding header when we reply a body
  659. %% and the found encoding is something other than identity.
  660. encodings_provided(Req, State) ->
  661. variances(Req, State).
  662. not_acceptable(Req, State) ->
  663. respond(Req, State, 406).
  664. %% variances/2 should return a list of headers that will be added
  665. %% to the Vary response header. The Accept, Accept-Language,
  666. %% Accept-Charset and Accept-Encoding headers do not need to be
  667. %% specified.
  668. %%
  669. %% @todo Do Accept-Encoding too when we handle it.
  670. %% @todo Does the order matter?
  671. variances(Req, State=#state{content_types_p=CTP,
  672. languages_p=LP, charsets_p=CP}) ->
  673. Variances = case CTP of
  674. [] -> [];
  675. [_] -> [];
  676. [_|_] -> [<<"accept">>]
  677. end,
  678. Variances2 = case LP of
  679. [] -> Variances;
  680. [_] -> Variances;
  681. [_|_] -> [<<"accept-language">>|Variances]
  682. end,
  683. Variances3 = case CP of
  684. undefined -> Variances2;
  685. [] -> Variances2;
  686. [_] -> Variances2;
  687. [_|_] -> [<<"accept-charset">>|Variances2]
  688. end,
  689. try variances(Req, State, Variances3) of
  690. {Variances4, Req2, State2} ->
  691. case [[<<", ">>, V] || V <- Variances4] of
  692. [] ->
  693. resource_exists(Req2, State2);
  694. [[<<", ">>, H]|Variances5] ->
  695. Req3 = cowboy_req:set_resp_header(
  696. <<"vary">>, [H|Variances5], Req2),
  697. resource_exists(Req3, State2)
  698. end
  699. catch Class:Reason ->
  700. error_terminate(Req, State, Class, Reason)
  701. end.
  702. variances(Req, State, Variances) ->
  703. case unsafe_call(Req, State, variances) of
  704. no_call ->
  705. {Variances, Req, State};
  706. {HandlerVariances, Req2, HandlerState} ->
  707. {Variances ++ HandlerVariances, Req2,
  708. State#state{handler_state=HandlerState}}
  709. end.
  710. resource_exists(Req, State) ->
  711. expect(Req, State, resource_exists, true,
  712. fun if_match_exists/2, fun if_match_must_not_exist/2).
  713. if_match_exists(Req, State) ->
  714. State2 = State#state{exists=true},
  715. case cowboy_req:parse_header(<<"if-match">>, Req) of
  716. undefined ->
  717. if_unmodified_since_exists(Req, State2);
  718. '*' ->
  719. if_unmodified_since_exists(Req, State2);
  720. ETagsList ->
  721. if_match(Req, State2, ETagsList)
  722. end.
  723. if_match(Req, State, EtagsList) ->
  724. try generate_etag(Req, State) of
  725. %% Strong Etag comparison: weak Etag never matches.
  726. {{weak, _}, Req2, State2} ->
  727. precondition_failed(Req2, State2);
  728. {Etag, Req2, State2} ->
  729. case lists:member(Etag, EtagsList) of
  730. true -> if_none_match_exists(Req2, State2);
  731. %% Etag may be `undefined' which cannot be a member.
  732. false -> precondition_failed(Req2, State2)
  733. end
  734. catch Class:Reason ->
  735. error_terminate(Req, State, Class, Reason)
  736. end.
  737. if_match_must_not_exist(Req, State) ->
  738. case cowboy_req:header(<<"if-match">>, Req) of
  739. undefined -> is_put_to_missing_resource(Req, State);
  740. _ -> precondition_failed(Req, State)
  741. end.
  742. if_unmodified_since_exists(Req, State) ->
  743. try cowboy_req:parse_header(<<"if-unmodified-since">>, Req) of
  744. undefined ->
  745. if_none_match_exists(Req, State);
  746. IfUnmodifiedSince ->
  747. if_unmodified_since(Req, State, IfUnmodifiedSince)
  748. catch _:_ ->
  749. if_none_match_exists(Req, State)
  750. end.
  751. %% If LastModified is the atom 'no_call', we continue.
  752. if_unmodified_since(Req, State, IfUnmodifiedSince) ->
  753. try last_modified(Req, State) of
  754. {LastModified, Req2, State2} ->
  755. case LastModified > IfUnmodifiedSince of
  756. true -> precondition_failed(Req2, State2);
  757. false -> if_none_match_exists(Req2, State2)
  758. end
  759. catch Class:Reason ->
  760. error_terminate(Req, State, Class, Reason)
  761. end.
  762. if_none_match_exists(Req, State) ->
  763. case cowboy_req:parse_header(<<"if-none-match">>, Req) of
  764. undefined ->
  765. if_modified_since_exists(Req, State);
  766. '*' ->
  767. precondition_is_head_get(Req, State);
  768. EtagsList ->
  769. if_none_match(Req, State, EtagsList)
  770. end.
  771. if_none_match(Req, State, EtagsList) ->
  772. try generate_etag(Req, State) of
  773. {Etag, Req2, State2} ->
  774. case Etag of
  775. undefined ->
  776. precondition_failed(Req2, State2);
  777. Etag ->
  778. case is_weak_match(Etag, EtagsList) of
  779. true -> precondition_is_head_get(Req2, State2);
  780. false -> method(Req2, State2)
  781. end
  782. end
  783. catch Class:Reason ->
  784. error_terminate(Req, State, Class, Reason)
  785. end.
  786. %% Weak Etag comparison: only check the opaque tag.
  787. is_weak_match(_, []) ->
  788. false;
  789. is_weak_match({_, Tag}, [{_, Tag}|_]) ->
  790. true;
  791. is_weak_match(Etag, [_|Tail]) ->
  792. is_weak_match(Etag, Tail).
  793. precondition_is_head_get(Req, State=#state{method=Method})
  794. when Method =:= <<"HEAD">>; Method =:= <<"GET">> ->
  795. not_modified(Req, State);
  796. precondition_is_head_get(Req, State) ->
  797. precondition_failed(Req, State).
  798. if_modified_since_exists(Req, State) ->
  799. try cowboy_req:parse_header(<<"if-modified-since">>, Req) of
  800. undefined ->
  801. method(Req, State);
  802. IfModifiedSince ->
  803. if_modified_since_now(Req, State, IfModifiedSince)
  804. catch _:_ ->
  805. method(Req, State)
  806. end.
  807. if_modified_since_now(Req, State, IfModifiedSince) ->
  808. case IfModifiedSince > erlang:universaltime() of
  809. true -> method(Req, State);
  810. false -> if_modified_since(Req, State, IfModifiedSince)
  811. end.
  812. if_modified_since(Req, State, IfModifiedSince) ->
  813. try last_modified(Req, State) of
  814. {undefined, Req2, State2} ->
  815. method(Req2, State2);
  816. {LastModified, Req2, State2} ->
  817. case LastModified > IfModifiedSince of
  818. true -> method(Req2, State2);
  819. false -> not_modified(Req2, State2)
  820. end
  821. catch Class:Reason ->
  822. error_terminate(Req, State, Class, Reason)
  823. end.
  824. not_modified(Req, State) ->
  825. Req2 = cowboy_req:delete_resp_header(<<"content-type">>, Req),
  826. try set_resp_etag(Req2, State) of
  827. {Req3, State2} ->
  828. try set_resp_expires(Req3, State2) of
  829. {Req4, State3} ->
  830. respond(Req4, State3, 304)
  831. catch Class:Reason ->
  832. error_terminate(Req, State2, Class, Reason)
  833. end
  834. catch Class:Reason ->
  835. error_terminate(Req, State, Class, Reason)
  836. end.
  837. precondition_failed(Req, State) ->
  838. respond(Req, State, 412).
  839. is_put_to_missing_resource(Req, State=#state{method= <<"PUT">>}) ->
  840. moved_permanently(Req, State, fun is_conflict/2);
  841. is_put_to_missing_resource(Req, State) ->
  842. previously_existed(Req, State).
  843. %% moved_permanently/2 should return either false or {true, Location}
  844. %% with Location the full new URI of the resource.
  845. moved_permanently(Req, State, OnFalse) ->
  846. case call(Req, State, moved_permanently) of
  847. {{true, Location}, Req2, HandlerState} ->
  848. Req3 = cowboy_req:set_resp_header(
  849. <<"location">>, Location, Req2),
  850. respond(Req3, State#state{handler_state=HandlerState}, 301);
  851. {false, Req2, HandlerState} ->
  852. OnFalse(Req2, State#state{handler_state=HandlerState});
  853. {stop, Req2, HandlerState} ->
  854. terminate(Req2, State#state{handler_state=HandlerState});
  855. {Switch, Req2, HandlerState} when element(1, Switch) =:= switch_handler ->
  856. switch_handler(Switch, Req2, HandlerState);
  857. no_call ->
  858. OnFalse(Req, State)
  859. end.
  860. previously_existed(Req, State) ->
  861. expect(Req, State, previously_existed, false,
  862. fun (R, S) -> is_post_to_missing_resource(R, S, 404) end,
  863. fun (R, S) -> moved_permanently(R, S, fun moved_temporarily/2) end).
  864. %% moved_temporarily/2 should return either false or {true, Location}
  865. %% with Location the full new URI of the resource.
  866. moved_temporarily(Req, State) ->
  867. case call(Req, State, moved_temporarily) of
  868. {{true, Location}, Req2, HandlerState} ->
  869. Req3 = cowboy_req:set_resp_header(
  870. <<"location">>, Location, Req2),
  871. respond(Req3, State#state{handler_state=HandlerState}, 307);
  872. {false, Req2, HandlerState} ->
  873. is_post_to_missing_resource(Req2, State#state{handler_state=HandlerState}, 410);
  874. {stop, Req2, HandlerState} ->
  875. terminate(Req2, State#state{handler_state=HandlerState});
  876. {Switch, Req2, HandlerState} when element(1, Switch) =:= switch_handler ->
  877. switch_handler(Switch, Req2, HandlerState);
  878. no_call ->
  879. is_post_to_missing_resource(Req, State, 410)
  880. end.
  881. is_post_to_missing_resource(Req, State=#state{method= <<"POST">>}, OnFalse) ->
  882. allow_missing_post(Req, State, OnFalse);
  883. is_post_to_missing_resource(Req, State, OnFalse) ->
  884. respond(Req, State, OnFalse).
  885. allow_missing_post(Req, State, OnFalse) ->
  886. expect(Req, State, allow_missing_post, true, fun accept_resource/2, OnFalse).
  887. method(Req, State=#state{method= <<"DELETE">>}) ->
  888. delete_resource(Req, State);
  889. method(Req, State=#state{method= <<"PUT">>}) ->
  890. is_conflict(Req, State);
  891. method(Req, State=#state{method=Method})
  892. when Method =:= <<"POST">>; Method =:= <<"PATCH">> ->
  893. accept_resource(Req, State);
  894. method(Req, State=#state{method=Method})
  895. when Method =:= <<"GET">>; Method =:= <<"HEAD">> ->
  896. set_resp_body_etag(Req, State);
  897. method(Req, State) ->
  898. multiple_choices(Req, State).
  899. %% delete_resource/2 should start deleting the resource and return.
  900. delete_resource(Req, State) ->
  901. expect(Req, State, delete_resource, false, 500, fun delete_completed/2).
  902. %% delete_completed/2 indicates whether the resource has been deleted yet.
  903. delete_completed(Req, State) ->
  904. expect(Req, State, delete_completed, true, fun has_resp_body/2, 202).
  905. is_conflict(Req, State) ->
  906. expect(Req, State, is_conflict, false, fun accept_resource/2, 409).
  907. %% content_types_accepted should return a list of media types and their
  908. %% associated callback functions in the same format as content_types_provided.
  909. %%
  910. %% The callback will then be called and is expected to process the content
  911. %% pushed to the resource in the request body.
  912. %%
  913. %% content_types_accepted SHOULD return a different list
  914. %% for each HTTP method.
  915. accept_resource(Req, State) ->
  916. case call(Req, State, content_types_accepted) of
  917. no_call ->
  918. respond(Req, State, 415);
  919. {stop, Req2, HandlerState} ->
  920. terminate(Req2, State#state{handler_state=HandlerState});
  921. {Switch, Req2, HandlerState} when element(1, Switch) =:= switch_handler ->
  922. switch_handler(Switch, Req2, HandlerState);
  923. {CTA, Req2, HandlerState} ->
  924. CTA2 = [normalize_content_types(P) || P <- CTA],
  925. State2 = State#state{handler_state=HandlerState},
  926. try cowboy_req:parse_header(<<"content-type">>, Req2) of
  927. ContentType ->
  928. choose_content_type(Req2, State2, ContentType, CTA2)
  929. catch _:_ ->
  930. respond(Req2, State2, 415)
  931. end
  932. end.
  933. %% The special content type '*' will always match. It can be used as a
  934. %% catch-all content type for accepting any kind of request content.
  935. %% Note that because it will always match, it should be the last of the
  936. %% list of content types, otherwise it'll shadow the ones following.
  937. choose_content_type(Req, State, _ContentType, []) ->
  938. respond(Req, State, 415);
  939. choose_content_type(Req, State, ContentType, [{Accepted, Fun}|_Tail])
  940. when Accepted =:= '*'; Accepted =:= ContentType ->
  941. process_content_type(Req, State, Fun);
  942. %% The special parameter '*' will always match any kind of content type
  943. %% parameters.
  944. %% Note that because it will always match, it should be the last of the
  945. %% list for specific content type, otherwise it'll shadow the ones following.
  946. choose_content_type(Req, State, {Type, SubType, Param},
  947. [{{Type, SubType, AcceptedParam}, Fun}|_Tail])
  948. when AcceptedParam =:= '*'; AcceptedParam =:= Param ->
  949. process_content_type(Req, State, Fun);
  950. choose_content_type(Req, State, ContentType, [_Any|Tail]) ->
  951. choose_content_type(Req, State, ContentType, Tail).
  952. process_content_type(Req, State=#state{method=Method, exists=Exists}, Fun) ->
  953. try case call(Req, State, Fun) of
  954. {stop, Req2, HandlerState2} ->
  955. terminate(Req2, State#state{handler_state=HandlerState2});
  956. {Switch, Req2, HandlerState} when element(1, Switch) =:= switch_handler ->
  957. switch_handler(Switch, Req2, HandlerState);
  958. {true, Req2, HandlerState2} when Exists ->
  959. State2 = State#state{handler_state=HandlerState2},
  960. next(Req2, State2, fun has_resp_body/2);
  961. {true, Req2, HandlerState2} ->
  962. State2 = State#state{handler_state=HandlerState2},
  963. next(Req2, State2, fun maybe_created/2);
  964. {false, Req2, HandlerState2} ->
  965. State2 = State#state{handler_state=HandlerState2},
  966. respond(Req2, State2, 400);
  967. {{true, ResURL}, Req2, HandlerState2} when Method =:= <<"POST">> ->
  968. State2 = State#state{handler_state=HandlerState2},
  969. Req3 = cowboy_req:set_resp_header(
  970. <<"location">>, ResURL, Req2),
  971. if
  972. Exists -> respond(Req3, State2, 303);
  973. true -> respond(Req3, State2, 201)
  974. end
  975. end catch Class:Reason = {case_clause, no_call} ->
  976. error_terminate(Req, State, Class, Reason)
  977. end.
  978. %% If PUT was used then the resource has been created at the current URL.
  979. %% Otherwise, if a location header has been set then the resource has been
  980. %% created at a new URL. If not, send a 200 or 204 as expected from a
  981. %% POST or PATCH request.
  982. maybe_created(Req, State=#state{method= <<"PUT">>}) ->
  983. respond(Req, State, 201);
  984. maybe_created(Req, State) ->
  985. case cowboy_req:has_resp_header(<<"location">>, Req) of
  986. true -> respond(Req, State, 201);
  987. false -> has_resp_body(Req, State)
  988. end.
  989. has_resp_body(Req, State) ->
  990. case cowboy_req:has_resp_body(Req) of
  991. true -> multiple_choices(Req, State);
  992. false -> respond(Req, State, 204)
  993. end.
  994. %% Set the Etag header if any for the response provided.
  995. set_resp_body_etag(Req, State) ->
  996. try set_resp_etag(Req, State) of
  997. {Req2, State2} ->
  998. set_resp_body_last_modified(Req2, State2)
  999. catch Class:Reason ->
  1000. error_terminate(Req, State, Class, Reason)
  1001. end.
  1002. %% Set the Last-Modified header if any for the response provided.
  1003. set_resp_body_last_modified(Req, State) ->
  1004. try last_modified(Req, State) of
  1005. {LastModified, Req2, State2} ->
  1006. case LastModified of
  1007. LastModified when is_atom(LastModified) ->
  1008. set_resp_body_expires(Req2, State2);
  1009. LastModified ->
  1010. LastModifiedBin = cowboy_clock:rfc1123(LastModified),
  1011. Req3 = cowboy_req:set_resp_header(
  1012. <<"last-modified">>, LastModifiedBin, Req2),
  1013. set_resp_body_expires(Req3, State2)
  1014. end
  1015. catch Class:Reason ->
  1016. error_terminate(Req, State, Class, Reason)
  1017. end.
  1018. %% Set the Expires header if any for the response provided.
  1019. set_resp_body_expires(Req, State) ->
  1020. try set_resp_expires(Req, State) of
  1021. {Req2, State2} ->
  1022. set_resp_body(Req2, State2)
  1023. catch Class:Reason ->
  1024. error_terminate(Req, State, Class, Reason)
  1025. end.
  1026. %% Set the response headers and call the callback found using
  1027. %% content_types_provided/2 to obtain the request body and add
  1028. %% it to the response.
  1029. set_resp_body(Req, State=#state{handler=Handler, content_type_a={_, Callback}}) ->
  1030. try case call(Req, State, Callback) of
  1031. {stop, Req2, HandlerState2} ->
  1032. terminate(Req2, State#state{handler_state=HandlerState2});
  1033. {Switch, Req2, HandlerState} when element(1, Switch) =:= switch_handler ->
  1034. switch_handler(Switch, Req2, HandlerState);
  1035. {Body, Req2, HandlerState2} ->
  1036. State2 = State#state{handler_state=HandlerState2},
  1037. Req3 = cowboy_req:set_resp_body(Body, Req2),
  1038. multiple_choices(Req3, State2)
  1039. end catch Class:{case_clause, no_call} ->
  1040. error_terminate(Req, State, Class, {error, {missing_callback, {Handler, Callback, 2}},
  1041. 'A callback specified in content_types_provided/2 is not exported.'})
  1042. end.
  1043. multiple_choices(Req, State) ->
  1044. expect(Req, State, multiple_choices, false, 200, 300).
  1045. %% Response utility functions.
  1046. set_resp_etag(Req, State) ->
  1047. {Etag, Req2, State2} = generate_etag(Req, State),
  1048. case Etag of
  1049. undefined ->
  1050. {Req2, State2};
  1051. Etag ->
  1052. Req3 = cowboy_req:set_resp_header(
  1053. <<"etag">>, encode_etag(Etag), Req2),
  1054. {Req3, State2}
  1055. end.
  1056. -spec encode_etag({strong | weak, binary()}) -> iolist().
  1057. encode_etag({strong, Etag}) -> [$",Etag,$"];
  1058. encode_etag({weak, Etag}) -> ["W/\"",Etag,$"].
  1059. set_resp_expires(Req, State) ->
  1060. {Expires, Req2, State2} = expires(Req, State),
  1061. case Expires of
  1062. Expires when is_atom(Expires) ->
  1063. {Req2, State2};
  1064. Expires when is_binary(Expires) ->
  1065. Req3 = cowboy_req:set_resp_header(
  1066. <<"expires">>, Expires, Req2),
  1067. {Req3, State2};
  1068. Expires ->
  1069. ExpiresBin = cowboy_clock:rfc1123(Expires),
  1070. Req3 = cowboy_req:set_resp_header(
  1071. <<"expires">>, ExpiresBin, Req2),
  1072. {Req3, State2}
  1073. end.
  1074. %% Info retrieval. No logic.
  1075. generate_etag(Req, State=#state{etag=no_call}) ->
  1076. {undefined, Req, State};
  1077. generate_etag(Req, State=#state{etag=undefined}) ->
  1078. case unsafe_call(Req, State, generate_etag) of
  1079. no_call ->
  1080. {undefined, Req, State#state{etag=no_call}};
  1081. {Etag, Req2, HandlerState} when is_binary(Etag) ->
  1082. Etag2 = cow_http_hd:parse_etag(Etag),
  1083. {Etag2, Req2, State#state{handler_state=HandlerState, etag=Etag2}};
  1084. {Etag, Req2, HandlerState} ->
  1085. {Etag, Req2, State#state{handler_state=HandlerState, etag=Etag}}
  1086. end;
  1087. generate_etag(Req, State=#state{etag=Etag}) ->
  1088. {Etag, Req, State}.
  1089. last_modified(Req, State=#state{last_modified=no_call}) ->
  1090. {undefined, Req, State};
  1091. last_modified(Req, State=#state{last_modified=undefined}) ->
  1092. case unsafe_call(Req, State, last_modified) of
  1093. no_call ->
  1094. {undefined, Req, State#state{last_modified=no_call}};
  1095. {LastModified, Req2, HandlerState} ->
  1096. {LastModified, Req2, State#state{handler_state=HandlerState,
  1097. last_modified=LastModified}}
  1098. end;
  1099. last_modified(Req, State=#state{last_modified=LastModified}) ->
  1100. {LastModified, Req, State}.
  1101. expires(Req, State=#state{expires=no_call}) ->
  1102. {undefined, Req, State};
  1103. expires(Req, State=#state{expires=undefined}) ->
  1104. case unsafe_call(Req, State, expires) of
  1105. no_call ->
  1106. {undefined, Req, State#state{expires=no_call}};
  1107. {Expires, Req2, HandlerState} ->
  1108. {Expires, Req2, State#state{handler_state=HandlerState,
  1109. expires=Expires}}
  1110. end;
  1111. expires(Req, State=#state{expires=Expires}) ->
  1112. {Expires, Req, State}.
  1113. %% REST primitives.
  1114. expect(Req, State, Callback, Expected, OnTrue, OnFalse) ->
  1115. case call(Req, State, Callback) of
  1116. no_call ->
  1117. next(Req, State, OnTrue);
  1118. {stop, Req2, HandlerState} ->
  1119. terminate(Req2, State#state{handler_state=HandlerState});
  1120. {Switch, Req2, HandlerState} when element(1, Switch) =:= switch_handler ->
  1121. switch_handler(Switch, Req2, HandlerState);
  1122. {Expected, Req2, HandlerState} ->
  1123. next(Req2, State#state{handler_state=HandlerState}, OnTrue);
  1124. {_Unexpected, Req2, HandlerState} ->
  1125. next(Req2, State#state{handler_state=HandlerState}, OnFalse)
  1126. end.
  1127. call(Req, State=#state{handler=Handler, handler_state=HandlerState},
  1128. Callback) ->
  1129. case erlang:function_exported(Handler, Callback, 2) of
  1130. true ->
  1131. try
  1132. Handler:Callback(Req, HandlerState)
  1133. catch Class:Reason ->
  1134. error_terminate(Req, State, Class, Reason)
  1135. end;
  1136. false ->
  1137. no_call
  1138. end.
  1139. unsafe_call(Req, #state{handler=Handler, handler_state=HandlerState},
  1140. Callback) ->
  1141. case erlang:function_exported(Handler, Callback, 2) of
  1142. true -> Handler:Callback(Req, HandlerState);
  1143. false -> no_call
  1144. end.
  1145. next(Req, State, Next) when is_function(Next) ->
  1146. Next(Req, State);
  1147. next(Req, State, StatusCode) when is_integer(StatusCode) ->
  1148. respond(Req, State, StatusCode).
  1149. respond(Req, State, StatusCode) ->
  1150. terminate(cowboy_req:reply(StatusCode, Req), State).
  1151. switch_handler({switch_handler, Mod}, Req, HandlerState) ->
  1152. {Mod, Req, HandlerState};
  1153. switch_handler({switch_handler, Mod, Opts}, Req, HandlerState) ->
  1154. {Mod, Req, HandlerState, Opts}.
  1155. -spec error_terminate(cowboy_req:req(), #state{}, atom(), any()) -> no_return().
  1156. error_terminate(Req, #state{handler=Handler, handler_state=HandlerState}, Class, Reason) ->
  1157. cowboy_handler:terminate({crash, Class, Reason}, Req, HandlerState, Handler),
  1158. erlang:raise(Class, Reason, erlang:get_stacktrace()).
  1159. terminate(Req, #state{handler=Handler, handler_state=HandlerState}) ->
  1160. Result = cowboy_handler:terminate(normal, Req, HandlerState, Handler),
  1161. {ok, Req, Result}.