cowboy_rest.erl 58 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625
  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 range_satisfiable(Req, State)
  154. -> {boolean() | {false, non_neg_integer() | iodata()}, Req, State}
  155. | {stop, Req, State}
  156. | {switch_handler(), Req, State}
  157. when Req::cowboy_req:req(), State::any().
  158. -optional_callbacks([range_satisfiable/2]).
  159. -callback ranges_provided(Req, State)
  160. -> {[{binary(), atom()}], Req, State}
  161. | {stop, Req, State}
  162. | {switch_handler(), Req, State}
  163. when Req::cowboy_req:req(), State::any().
  164. -optional_callbacks([ranges_provided/2]).
  165. -callback rate_limited(Req, State)
  166. -> {{true, non_neg_integer() | calendar:datetime()} | false, Req, State}
  167. | {stop, Req, State}
  168. | {switch_handler(), Req, State}
  169. when Req::cowboy_req:req(), State::any().
  170. -optional_callbacks([rate_limited/2]).
  171. -callback resource_exists(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([resource_exists/2]).
  177. -callback service_available(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([service_available/2]).
  183. -callback uri_too_long(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([uri_too_long/2]).
  189. -callback valid_content_headers(Req, State)
  190. -> {boolean(), Req, State}
  191. | {stop, Req, State}
  192. | {switch_handler(), Req, State}
  193. when Req::cowboy_req:req(), State::any().
  194. -optional_callbacks([valid_content_headers/2]).
  195. -callback valid_entity_length(Req, State)
  196. -> {boolean(), Req, State}
  197. | {stop, Req, State}
  198. | {switch_handler(), Req, State}
  199. when Req::cowboy_req:req(), State::any().
  200. -optional_callbacks([valid_entity_length/2]).
  201. -callback variances(Req, State)
  202. -> {[binary()], Req, State}
  203. when Req::cowboy_req:req(), State::any().
  204. -optional_callbacks([variances/2]).
  205. %% End of REST callbacks. Whew!
  206. -record(state, {
  207. method = undefined :: binary(),
  208. %% Handler.
  209. handler :: atom(),
  210. handler_state :: any(),
  211. %% Allowed methods. Only used for OPTIONS requests.
  212. allowed_methods :: [binary()] | undefined,
  213. %% Media type.
  214. content_types_p = [] ::
  215. [{binary() | {binary(), binary(), [{binary(), binary()}] | '*'},
  216. atom()}],
  217. content_type_a :: undefined
  218. | {binary() | {binary(), binary(), [{binary(), binary()}] | '*'},
  219. atom()},
  220. %% Language.
  221. languages_p = [] :: [binary()],
  222. language_a :: undefined | binary(),
  223. %% Charset.
  224. charsets_p = undefined :: undefined | [binary()],
  225. charset_a :: undefined | binary(),
  226. %% Range units.
  227. ranges_a = [] :: [{binary(), atom()}],
  228. %% Whether the resource exists.
  229. exists = false :: boolean(),
  230. %% Cached resource calls.
  231. etag :: undefined | no_call | {strong | weak, binary()},
  232. last_modified :: undefined | no_call | calendar:datetime(),
  233. expires :: undefined | no_call | calendar:datetime() | binary()
  234. }).
  235. -spec upgrade(Req, Env, module(), any())
  236. -> {ok, Req, Env} when Req::cowboy_req:req(), Env::cowboy_middleware:env().
  237. upgrade(Req0, Env, Handler, HandlerState0) ->
  238. Method = cowboy_req:method(Req0),
  239. case service_available(Req0, #state{method=Method,
  240. handler=Handler, handler_state=HandlerState0}) of
  241. {ok, Req, Result} ->
  242. {ok, Req, Env#{result => Result}};
  243. {Mod, Req, HandlerState} ->
  244. Mod:upgrade(Req, Env, Handler, HandlerState);
  245. {Mod, Req, HandlerState, Opts} ->
  246. Mod:upgrade(Req, Env, Handler, HandlerState, Opts)
  247. end.
  248. -spec upgrade(Req, Env, module(), any(), any())
  249. -> {ok, Req, Env} when Req::cowboy_req:req(), Env::cowboy_middleware:env().
  250. %% cowboy_rest takes no options.
  251. upgrade(Req, Env, Handler, HandlerState, _Opts) ->
  252. upgrade(Req, Env, Handler, HandlerState).
  253. service_available(Req, State) ->
  254. expect(Req, State, service_available, true, fun known_methods/2, 503).
  255. %% known_methods/2 should return a list of binary methods.
  256. known_methods(Req, State=#state{method=Method}) ->
  257. case call(Req, State, known_methods) of
  258. no_call when Method =:= <<"HEAD">>; Method =:= <<"GET">>;
  259. Method =:= <<"POST">>; Method =:= <<"PUT">>;
  260. Method =:= <<"PATCH">>; Method =:= <<"DELETE">>;
  261. Method =:= <<"OPTIONS">> ->
  262. next(Req, State, fun uri_too_long/2);
  263. no_call ->
  264. next(Req, State, 501);
  265. {stop, Req2, State2} ->
  266. terminate(Req2, State2);
  267. {Switch, Req2, State2} when element(1, Switch) =:= switch_handler ->
  268. switch_handler(Switch, Req2, State2);
  269. {List, Req2, State2} ->
  270. case lists:member(Method, List) of
  271. true -> next(Req2, State2, fun uri_too_long/2);
  272. false -> next(Req2, State2, 501)
  273. end
  274. end.
  275. uri_too_long(Req, State) ->
  276. expect(Req, State, uri_too_long, false, fun allowed_methods/2, 414).
  277. %% allowed_methods/2 should return a list of binary methods.
  278. allowed_methods(Req, State=#state{method=Method}) ->
  279. case call(Req, State, allowed_methods) of
  280. no_call when Method =:= <<"HEAD">>; Method =:= <<"GET">> ->
  281. next(Req, State, fun malformed_request/2);
  282. no_call when Method =:= <<"OPTIONS">> ->
  283. next(Req, State#state{allowed_methods=
  284. [<<"HEAD">>, <<"GET">>, <<"OPTIONS">>]},
  285. fun malformed_request/2);
  286. no_call ->
  287. method_not_allowed(Req, State,
  288. [<<"HEAD">>, <<"GET">>, <<"OPTIONS">>]);
  289. {stop, Req2, State2} ->
  290. terminate(Req2, State2);
  291. {Switch, Req2, State2} when element(1, Switch) =:= switch_handler ->
  292. switch_handler(Switch, Req2, State2);
  293. {List, Req2, State2} ->
  294. case lists:member(Method, List) of
  295. true when Method =:= <<"OPTIONS">> ->
  296. next(Req2, State2#state{allowed_methods=List},
  297. fun malformed_request/2);
  298. true ->
  299. next(Req2, State2, fun malformed_request/2);
  300. false ->
  301. method_not_allowed(Req2, State2, List)
  302. end
  303. end.
  304. method_not_allowed(Req, State, []) ->
  305. Req2 = cowboy_req:set_resp_header(<<"allow">>, <<>>, Req),
  306. respond(Req2, State, 405);
  307. method_not_allowed(Req, State, Methods) ->
  308. << ", ", Allow/binary >> = << << ", ", M/binary >> || M <- Methods >>,
  309. Req2 = cowboy_req:set_resp_header(<<"allow">>, Allow, Req),
  310. respond(Req2, State, 405).
  311. malformed_request(Req, State) ->
  312. expect(Req, State, malformed_request, false, fun is_authorized/2, 400).
  313. %% is_authorized/2 should return true or {false, WwwAuthenticateHeader}.
  314. is_authorized(Req, State) ->
  315. case call(Req, State, is_authorized) of
  316. no_call ->
  317. forbidden(Req, State);
  318. {stop, Req2, State2} ->
  319. terminate(Req2, State2);
  320. {Switch, Req2, State2} when element(1, Switch) =:= switch_handler ->
  321. switch_handler(Switch, Req2, State2);
  322. {true, Req2, State2} ->
  323. forbidden(Req2, State2);
  324. {{false, AuthHead}, Req2, State2} ->
  325. Req3 = cowboy_req:set_resp_header(
  326. <<"www-authenticate">>, AuthHead, Req2),
  327. respond(Req3, State2, 401)
  328. end.
  329. forbidden(Req, State) ->
  330. expect(Req, State, forbidden, false, fun rate_limited/2, 403).
  331. rate_limited(Req, State) ->
  332. case call(Req, State, rate_limited) of
  333. no_call ->
  334. valid_content_headers(Req, State);
  335. {stop, Req2, State2} ->
  336. terminate(Req2, State2);
  337. {Switch, Req2, State2} when element(1, Switch) =:= switch_handler ->
  338. switch_handler(Switch, Req2, State2);
  339. {false, Req2, State2} ->
  340. valid_content_headers(Req2, State2);
  341. {{true, RetryAfter0}, Req2, State2} ->
  342. RetryAfter = if
  343. is_integer(RetryAfter0), RetryAfter0 >= 0 ->
  344. integer_to_binary(RetryAfter0);
  345. is_tuple(RetryAfter0) ->
  346. cowboy_clock:rfc1123(RetryAfter0)
  347. end,
  348. Req3 = cowboy_req:set_resp_header(<<"retry-after">>, RetryAfter, Req2),
  349. respond(Req3, State2, 429)
  350. end.
  351. valid_content_headers(Req, State) ->
  352. expect(Req, State, valid_content_headers, true,
  353. fun valid_entity_length/2, 501).
  354. valid_entity_length(Req, State) ->
  355. expect(Req, State, valid_entity_length, true, fun options/2, 413).
  356. %% If you need to add additional headers to the response at this point,
  357. %% you should do it directly in the options/2 call using set_resp_headers.
  358. options(Req, State=#state{allowed_methods=Methods, method= <<"OPTIONS">>}) ->
  359. case call(Req, State, options) of
  360. no_call when Methods =:= [] ->
  361. Req2 = cowboy_req:set_resp_header(<<"allow">>, <<>>, Req),
  362. respond(Req2, State, 200);
  363. no_call ->
  364. << ", ", Allow/binary >>
  365. = << << ", ", M/binary >> || M <- Methods >>,
  366. Req2 = cowboy_req:set_resp_header(<<"allow">>, Allow, Req),
  367. respond(Req2, State, 200);
  368. {stop, Req2, State2} ->
  369. terminate(Req2, State2);
  370. {Switch, Req2, State2} when element(1, Switch) =:= switch_handler ->
  371. switch_handler(Switch, Req2, State2);
  372. {ok, Req2, State2} ->
  373. respond(Req2, State2, 200)
  374. end;
  375. options(Req, State) ->
  376. content_types_provided(Req, State).
  377. %% content_types_provided/2 should return a list of content types and their
  378. %% associated callback function as a tuple: {{Type, SubType, Params}, Fun}.
  379. %% Type and SubType are the media type as binary. Params is a list of
  380. %% Key/Value tuple, with Key and Value a binary. Fun is the name of the
  381. %% callback that will be used to return the content of the response. It is
  382. %% given as an atom.
  383. %%
  384. %% An example of such return value would be:
  385. %% {{<<"text">>, <<"html">>, []}, to_html}
  386. %%
  387. %% Note that it is also possible to return a binary content type that will
  388. %% then be parsed by Cowboy. However note that while this may make your
  389. %% resources a little more readable, this is a lot less efficient.
  390. %%
  391. %% An example of such return value would be:
  392. %% {<<"text/html">>, to_html}
  393. content_types_provided(Req, State) ->
  394. case call(Req, State, content_types_provided) of
  395. no_call ->
  396. State2 = State#state{
  397. content_types_p=[{{<<"text">>, <<"html">>, '*'}, to_html}]},
  398. try cowboy_req:parse_header(<<"accept">>, Req) of
  399. undefined ->
  400. languages_provided(
  401. Req#{media_type => {<<"text">>, <<"html">>, []}},
  402. State2#state{content_type_a={{<<"text">>, <<"html">>, []}, to_html}});
  403. Accept ->
  404. choose_media_type(Req, State2, prioritize_accept(Accept))
  405. catch _:_ ->
  406. respond(Req, State2, 400)
  407. end;
  408. {stop, Req2, State2} ->
  409. terminate(Req2, State2);
  410. {Switch, Req2, State2} when element(1, Switch) =:= switch_handler ->
  411. switch_handler(Switch, Req2, State2);
  412. {[], Req2, State2} ->
  413. not_acceptable(Req2, State2);
  414. {CTP, Req2, State2} ->
  415. CTP2 = [normalize_content_types(P) || P <- CTP],
  416. State3 = State2#state{content_types_p=CTP2},
  417. try cowboy_req:parse_header(<<"accept">>, Req2) of
  418. undefined ->
  419. {PMT, _Fun} = HeadCTP = hd(CTP2),
  420. languages_provided(
  421. Req2#{media_type => PMT},
  422. State3#state{content_type_a=HeadCTP});
  423. Accept ->
  424. choose_media_type(Req2, State3, prioritize_accept(Accept))
  425. catch _:_ ->
  426. respond(Req2, State3, 400)
  427. end
  428. end.
  429. normalize_content_types({ContentType, Callback})
  430. when is_binary(ContentType) ->
  431. {cow_http_hd:parse_content_type(ContentType), Callback};
  432. normalize_content_types(Normalized) ->
  433. Normalized.
  434. prioritize_accept(Accept) ->
  435. lists:sort(
  436. fun ({MediaTypeA, Quality, _AcceptParamsA},
  437. {MediaTypeB, Quality, _AcceptParamsB}) ->
  438. %% Same quality, check precedence in more details.
  439. prioritize_mediatype(MediaTypeA, MediaTypeB);
  440. ({_MediaTypeA, QualityA, _AcceptParamsA},
  441. {_MediaTypeB, QualityB, _AcceptParamsB}) ->
  442. %% Just compare the quality.
  443. QualityA > QualityB
  444. end, Accept).
  445. %% Media ranges can be overridden by more specific media ranges or
  446. %% specific media types. If more than one media range applies to a given
  447. %% type, the most specific reference has precedence.
  448. %%
  449. %% We always choose B over A when we can't decide between the two.
  450. prioritize_mediatype({TypeA, SubTypeA, ParamsA}, {TypeB, SubTypeB, ParamsB}) ->
  451. case TypeB of
  452. TypeA ->
  453. case SubTypeB of
  454. SubTypeA -> length(ParamsA) > length(ParamsB);
  455. <<"*">> -> true;
  456. _Any -> false
  457. end;
  458. <<"*">> -> true;
  459. _Any -> false
  460. end.
  461. %% Ignoring the rare AcceptParams. Not sure what should be done about them.
  462. choose_media_type(Req, State, []) ->
  463. not_acceptable(Req, State);
  464. choose_media_type(Req, State=#state{content_types_p=CTP},
  465. [MediaType|Tail]) ->
  466. match_media_type(Req, State, Tail, CTP, MediaType).
  467. match_media_type(Req, State, Accept, [], _MediaType) ->
  468. choose_media_type(Req, State, Accept);
  469. match_media_type(Req, State, Accept, CTP,
  470. MediaType = {{<<"*">>, <<"*">>, _Params_A}, _QA, _APA}) ->
  471. match_media_type_params(Req, State, Accept, CTP, MediaType);
  472. match_media_type(Req, State, Accept,
  473. CTP = [{{Type, SubType_P, _PP}, _Fun}|_Tail],
  474. MediaType = {{Type, SubType_A, _PA}, _QA, _APA})
  475. when SubType_P =:= SubType_A; SubType_A =:= <<"*">> ->
  476. match_media_type_params(Req, State, Accept, CTP, MediaType);
  477. match_media_type(Req, State, Accept, [_Any|Tail], MediaType) ->
  478. match_media_type(Req, State, Accept, Tail, MediaType).
  479. match_media_type_params(Req, State, Accept,
  480. [Provided = {{TP, STP, '*'}, _Fun}|Tail],
  481. MediaType = {{TA, _STA, Params_A0}, _QA, _APA}) ->
  482. case lists:keytake(<<"charset">>, 1, Params_A0) of
  483. {value, {_, Charset}, Params_A} when TA =:= <<"text">> ->
  484. %% When we match against a wildcard, the media type is text
  485. %% and has a charset parameter, we call charsets_provided
  486. %% and check that the charset is provided. If the callback
  487. %% is not exported, we accept inconditionally but ignore
  488. %% the given charset so as to not send a wrong value back.
  489. case call(Req, State, charsets_provided) of
  490. no_call ->
  491. languages_provided(Req#{media_type => {TP, STP, Params_A0}},
  492. State#state{content_type_a=Provided});
  493. {stop, Req2, State2} ->
  494. terminate(Req2, State2);
  495. {Switch, Req2, State2} when element(1, Switch) =:= switch_handler ->
  496. switch_handler(Switch, Req2, State2);
  497. {CP, Req2, State2} ->
  498. State3 = State2#state{charsets_p=CP},
  499. case lists:member(Charset, CP) of
  500. false ->
  501. match_media_type(Req2, State3, Accept, Tail, MediaType);
  502. true ->
  503. languages_provided(Req2#{media_type => {TP, STP, Params_A}},
  504. State3#state{content_type_a=Provided,
  505. charset_a=Charset})
  506. end
  507. end;
  508. _ ->
  509. languages_provided(Req#{media_type => {TP, STP, Params_A0}},
  510. State#state{content_type_a=Provided})
  511. end;
  512. match_media_type_params(Req, State, Accept,
  513. [Provided = {PMT = {TP, STP, Params_P0}, Fun}|Tail],
  514. MediaType = {{_TA, _STA, Params_A}, _QA, _APA}) ->
  515. case lists:sort(Params_P0) =:= lists:sort(Params_A) of
  516. true when TP =:= <<"text">> ->
  517. %% When a charset was provided explicitly in both the charset header
  518. %% and the media types provided and the negotiation is successful,
  519. %% we keep the charset and don't call charsets_provided. This only
  520. %% applies to text media types, however.
  521. {Charset, Params_P} = case lists:keytake(<<"charset">>, 1, Params_P0) of
  522. false -> {undefined, Params_P0};
  523. {value, {_, Charset0}, Params_P1} -> {Charset0, Params_P1}
  524. end,
  525. languages_provided(Req#{media_type => {TP, STP, Params_P}},
  526. State#state{content_type_a={{TP, STP, Params_P}, Fun},
  527. charset_a=Charset});
  528. true ->
  529. languages_provided(Req#{media_type => PMT},
  530. State#state{content_type_a=Provided});
  531. false ->
  532. match_media_type(Req, State, Accept, Tail, MediaType)
  533. end.
  534. %% languages_provided should return a list of binary values indicating
  535. %% which languages are accepted by the resource.
  536. %%
  537. %% @todo I suppose we should also ask the resource if it wants to
  538. %% set a language itself or if it wants it to be automatically chosen.
  539. languages_provided(Req, State) ->
  540. case call(Req, State, languages_provided) of
  541. no_call ->
  542. charsets_provided(Req, State);
  543. {stop, Req2, State2} ->
  544. terminate(Req2, State2);
  545. {Switch, Req2, State2} when element(1, Switch) =:= switch_handler ->
  546. switch_handler(Switch, Req2, State2);
  547. {[], Req2, State2} ->
  548. not_acceptable(Req2, State2);
  549. {LP, Req2, State2} ->
  550. State3 = State2#state{languages_p=LP},
  551. case cowboy_req:parse_header(<<"accept-language">>, Req2) of
  552. undefined ->
  553. set_language(Req2, State3#state{language_a=hd(LP)});
  554. AcceptLanguage ->
  555. AcceptLanguage2 = prioritize_languages(AcceptLanguage),
  556. choose_language(Req2, State3, AcceptLanguage2)
  557. end
  558. end.
  559. %% A language-range matches a language-tag if it exactly equals the tag,
  560. %% or if it exactly equals a prefix of the tag such that the first tag
  561. %% character following the prefix is "-". The special range "*", if
  562. %% present in the Accept-Language field, matches every tag not matched
  563. %% by any other range present in the Accept-Language field.
  564. %%
  565. %% @todo The last sentence probably means we should always put '*'
  566. %% at the end of the list.
  567. prioritize_languages(AcceptLanguages) ->
  568. lists:sort(
  569. fun ({_TagA, QualityA}, {_TagB, QualityB}) ->
  570. QualityA > QualityB
  571. end, AcceptLanguages).
  572. choose_language(Req, State, []) ->
  573. not_acceptable(Req, State);
  574. choose_language(Req, State=#state{languages_p=LP}, [Language|Tail]) ->
  575. match_language(Req, State, Tail, LP, Language).
  576. match_language(Req, State, Accept, [], _Language) ->
  577. choose_language(Req, State, Accept);
  578. match_language(Req, State, _Accept, [Provided|_Tail], {'*', _Quality}) ->
  579. set_language(Req, State#state{language_a=Provided});
  580. match_language(Req, State, _Accept, [Provided|_Tail], {Provided, _Quality}) ->
  581. set_language(Req, State#state{language_a=Provided});
  582. match_language(Req, State, Accept, [Provided|Tail],
  583. Language = {Tag, _Quality}) ->
  584. Length = byte_size(Tag),
  585. case Provided of
  586. << Tag:Length/binary, $-, _Any/bits >> ->
  587. set_language(Req, State#state{language_a=Provided});
  588. _Any ->
  589. match_language(Req, State, Accept, Tail, Language)
  590. end.
  591. set_language(Req, State=#state{language_a=Language}) ->
  592. Req2 = cowboy_req:set_resp_header(<<"content-language">>, Language, Req),
  593. charsets_provided(Req2#{language => Language}, State).
  594. %% charsets_provided should return a list of binary values indicating
  595. %% which charsets are accepted by the resource.
  596. %%
  597. %% A charset may have been selected while negotiating the accept header.
  598. %% There's no need to select one again.
  599. charsets_provided(Req, State=#state{charset_a=Charset})
  600. when Charset =/= undefined ->
  601. set_content_type(Req, State);
  602. %% If charsets_p is defined, use it instead of calling charsets_provided
  603. %% again. We also call this clause during normal execution to avoid
  604. %% duplicating code.
  605. charsets_provided(Req, State=#state{charsets_p=[]}) ->
  606. not_acceptable(Req, State);
  607. charsets_provided(Req, State=#state{charsets_p=CP})
  608. when CP =/= undefined ->
  609. case cowboy_req:parse_header(<<"accept-charset">>, Req) of
  610. undefined ->
  611. set_content_type(Req, State#state{charset_a=hd(CP)});
  612. AcceptCharset0 ->
  613. AcceptCharset = prioritize_charsets(AcceptCharset0),
  614. choose_charset(Req, State, AcceptCharset)
  615. end;
  616. charsets_provided(Req, State) ->
  617. case call(Req, State, charsets_provided) of
  618. no_call ->
  619. set_content_type(Req, State);
  620. {stop, Req2, State2} ->
  621. terminate(Req2, State2);
  622. {Switch, Req2, State2} when element(1, Switch) =:= switch_handler ->
  623. switch_handler(Switch, Req2, State2);
  624. {CP, Req2, State2} ->
  625. charsets_provided(Req2, State2#state{charsets_p=CP})
  626. end.
  627. prioritize_charsets(AcceptCharsets) ->
  628. lists:sort(
  629. fun ({_CharsetA, QualityA}, {_CharsetB, QualityB}) ->
  630. QualityA > QualityB
  631. end, AcceptCharsets).
  632. choose_charset(Req, State, []) ->
  633. not_acceptable(Req, State);
  634. %% A q-value of 0 means not acceptable.
  635. choose_charset(Req, State, [{_, 0}|Tail]) ->
  636. choose_charset(Req, State, Tail);
  637. choose_charset(Req, State=#state{charsets_p=CP}, [Charset|Tail]) ->
  638. match_charset(Req, State, Tail, CP, Charset).
  639. match_charset(Req, State, Accept, [], _Charset) ->
  640. choose_charset(Req, State, Accept);
  641. match_charset(Req, State, _Accept, [Provided|_], {<<"*">>, _}) ->
  642. set_content_type(Req, State#state{charset_a=Provided});
  643. match_charset(Req, State, _Accept, [Provided|_], {Provided, _}) ->
  644. set_content_type(Req, State#state{charset_a=Provided});
  645. match_charset(Req, State, Accept, [_|Tail], Charset) ->
  646. match_charset(Req, State, Accept, Tail, Charset).
  647. set_content_type(Req, State=#state{
  648. content_type_a={{Type, SubType, Params}, _Fun},
  649. charset_a=Charset}) ->
  650. ParamsBin = set_content_type_build_params(Params, []),
  651. ContentType = [Type, <<"/">>, SubType, ParamsBin],
  652. ContentType2 = case {Type, Charset} of
  653. {<<"text">>, Charset} when Charset =/= undefined ->
  654. [ContentType, <<"; charset=">>, Charset];
  655. _ ->
  656. ContentType
  657. end,
  658. Req2 = cowboy_req:set_resp_header(<<"content-type">>, ContentType2, Req),
  659. encodings_provided(Req2#{charset => Charset}, State).
  660. set_content_type_build_params('*', []) ->
  661. <<>>;
  662. set_content_type_build_params([], []) ->
  663. <<>>;
  664. set_content_type_build_params([], Acc) ->
  665. lists:reverse(Acc);
  666. set_content_type_build_params([{Attr, Value}|Tail], Acc) ->
  667. set_content_type_build_params(Tail, [[Attr, <<"=">>, Value], <<";">>|Acc]).
  668. %% @todo Match for identity as we provide nothing else for now.
  669. %% @todo Don't forget to set the Content-Encoding header when we reply a body
  670. %% and the found encoding is something other than identity.
  671. encodings_provided(Req, State) ->
  672. ranges_provided(Req, State).
  673. not_acceptable(Req, State) ->
  674. respond(Req, State, 406).
  675. ranges_provided(Req, State) ->
  676. case call(Req, State, ranges_provided) of
  677. no_call ->
  678. variances(Req, State);
  679. {stop, Req2, State2} ->
  680. terminate(Req2, State2);
  681. {Switch, Req2, State2} when element(1, Switch) =:= switch_handler ->
  682. switch_handler(Switch, Req2, State2);
  683. {[], Req2, State2} ->
  684. Req3 = cowboy_req:set_resp_header(<<"accept-ranges">>, <<"none">>, Req2),
  685. variances(Req3, State2#state{ranges_a=[]});
  686. {RP, Req2, State2} ->
  687. <<", ", AcceptRanges/binary>> = <<<<", ", R/binary>> || {R, _} <- RP>>,
  688. Req3 = cowboy_req:set_resp_header(<<"accept-ranges">>, AcceptRanges, Req2),
  689. variances(Req3, State2#state{ranges_a=RP})
  690. end.
  691. %% variances/2 should return a list of headers that will be added
  692. %% to the Vary response header. The Accept, Accept-Language,
  693. %% Accept-Charset and Accept-Encoding headers do not need to be
  694. %% specified.
  695. %%
  696. %% @todo Do Accept-Encoding too when we handle it.
  697. %% @todo Does the order matter?
  698. variances(Req, State=#state{content_types_p=CTP,
  699. languages_p=LP, charsets_p=CP}) ->
  700. Variances = case CTP of
  701. [] -> [];
  702. [_] -> [];
  703. [_|_] -> [<<"accept">>]
  704. end,
  705. Variances2 = case LP of
  706. [] -> Variances;
  707. [_] -> Variances;
  708. [_|_] -> [<<"accept-language">>|Variances]
  709. end,
  710. Variances3 = case CP of
  711. undefined -> Variances2;
  712. [] -> Variances2;
  713. [_] -> Variances2;
  714. [_|_] -> [<<"accept-charset">>|Variances2]
  715. end,
  716. try variances(Req, State, Variances3) of
  717. {Variances4, Req2, State2} ->
  718. case [[<<", ">>, V] || V <- Variances4] of
  719. [] ->
  720. resource_exists(Req2, State2);
  721. [[<<", ">>, H]|Variances5] ->
  722. Req3 = cowboy_req:set_resp_header(
  723. <<"vary">>, [H|Variances5], Req2),
  724. resource_exists(Req3, State2)
  725. end
  726. catch Class:Reason ->
  727. error_terminate(Req, State, Class, Reason)
  728. end.
  729. variances(Req, State, Variances) ->
  730. case unsafe_call(Req, State, variances) of
  731. no_call ->
  732. {Variances, Req, State};
  733. {HandlerVariances, Req2, State2} ->
  734. {Variances ++ HandlerVariances, Req2, State2}
  735. end.
  736. resource_exists(Req, State) ->
  737. expect(Req, State, resource_exists, true,
  738. fun if_match_exists/2, fun if_match_must_not_exist/2).
  739. if_match_exists(Req, State) ->
  740. State2 = State#state{exists=true},
  741. case cowboy_req:parse_header(<<"if-match">>, Req) of
  742. undefined ->
  743. if_unmodified_since_exists(Req, State2);
  744. '*' ->
  745. if_unmodified_since_exists(Req, State2);
  746. ETagsList ->
  747. if_match(Req, State2, ETagsList)
  748. end.
  749. if_match(Req, State, EtagsList) ->
  750. try generate_etag(Req, State) of
  751. %% Strong Etag comparison: weak Etag never matches.
  752. {{weak, _}, Req2, State2} ->
  753. precondition_failed(Req2, State2);
  754. {Etag, Req2, State2} ->
  755. case lists:member(Etag, EtagsList) of
  756. true -> if_none_match_exists(Req2, State2);
  757. %% Etag may be `undefined' which cannot be a member.
  758. false -> precondition_failed(Req2, State2)
  759. end
  760. catch Class:Reason ->
  761. error_terminate(Req, State, Class, Reason)
  762. end.
  763. if_match_must_not_exist(Req, State) ->
  764. case cowboy_req:header(<<"if-match">>, Req) of
  765. undefined -> is_put_to_missing_resource(Req, State);
  766. _ -> precondition_failed(Req, State)
  767. end.
  768. if_unmodified_since_exists(Req, State) ->
  769. try cowboy_req:parse_header(<<"if-unmodified-since">>, Req) of
  770. undefined ->
  771. if_none_match_exists(Req, State);
  772. IfUnmodifiedSince ->
  773. if_unmodified_since(Req, State, IfUnmodifiedSince)
  774. catch _:_ ->
  775. if_none_match_exists(Req, State)
  776. end.
  777. %% If LastModified is the atom 'no_call', we continue.
  778. if_unmodified_since(Req, State, IfUnmodifiedSince) ->
  779. try last_modified(Req, State) of
  780. {LastModified, Req2, State2} ->
  781. case LastModified > IfUnmodifiedSince of
  782. true -> precondition_failed(Req2, State2);
  783. false -> if_none_match_exists(Req2, State2)
  784. end
  785. catch Class:Reason ->
  786. error_terminate(Req, State, Class, Reason)
  787. end.
  788. if_none_match_exists(Req, State) ->
  789. case cowboy_req:parse_header(<<"if-none-match">>, Req) of
  790. undefined ->
  791. if_modified_since_exists(Req, State);
  792. '*' ->
  793. precondition_is_head_get(Req, State);
  794. EtagsList ->
  795. if_none_match(Req, State, EtagsList)
  796. end.
  797. if_none_match(Req, State, EtagsList) ->
  798. try generate_etag(Req, State) of
  799. {Etag, Req2, State2} ->
  800. case Etag of
  801. undefined ->
  802. precondition_failed(Req2, State2);
  803. Etag ->
  804. case is_weak_match(Etag, EtagsList) of
  805. true -> precondition_is_head_get(Req2, State2);
  806. false -> method(Req2, State2)
  807. end
  808. end
  809. catch Class:Reason ->
  810. error_terminate(Req, State, Class, Reason)
  811. end.
  812. %% Weak Etag comparison: only check the opaque tag.
  813. is_weak_match(_, []) ->
  814. false;
  815. is_weak_match({_, Tag}, [{_, Tag}|_]) ->
  816. true;
  817. is_weak_match(Etag, [_|Tail]) ->
  818. is_weak_match(Etag, Tail).
  819. precondition_is_head_get(Req, State=#state{method=Method})
  820. when Method =:= <<"HEAD">>; Method =:= <<"GET">> ->
  821. not_modified(Req, State);
  822. precondition_is_head_get(Req, State) ->
  823. precondition_failed(Req, State).
  824. if_modified_since_exists(Req, State) ->
  825. try cowboy_req:parse_header(<<"if-modified-since">>, Req) of
  826. undefined ->
  827. method(Req, State);
  828. IfModifiedSince ->
  829. if_modified_since_now(Req, State, IfModifiedSince)
  830. catch _:_ ->
  831. method(Req, State)
  832. end.
  833. if_modified_since_now(Req, State, IfModifiedSince) ->
  834. case IfModifiedSince > erlang:universaltime() of
  835. true -> method(Req, State);
  836. false -> if_modified_since(Req, State, IfModifiedSince)
  837. end.
  838. if_modified_since(Req, State, IfModifiedSince) ->
  839. try last_modified(Req, State) of
  840. {undefined, Req2, State2} ->
  841. method(Req2, State2);
  842. {LastModified, Req2, State2} ->
  843. case LastModified > IfModifiedSince of
  844. true -> method(Req2, State2);
  845. false -> not_modified(Req2, State2)
  846. end
  847. catch Class:Reason ->
  848. error_terminate(Req, State, Class, Reason)
  849. end.
  850. not_modified(Req, State) ->
  851. Req2 = cowboy_req:delete_resp_header(<<"content-type">>, Req),
  852. try set_resp_etag(Req2, State) of
  853. {Req3, State2} ->
  854. try set_resp_expires(Req3, State2) of
  855. {Req4, State3} ->
  856. respond(Req4, State3, 304)
  857. catch Class:Reason ->
  858. error_terminate(Req, State2, Class, Reason)
  859. end
  860. catch Class:Reason ->
  861. error_terminate(Req, State, Class, Reason)
  862. end.
  863. precondition_failed(Req, State) ->
  864. respond(Req, State, 412).
  865. is_put_to_missing_resource(Req, State=#state{method= <<"PUT">>}) ->
  866. moved_permanently(Req, State, fun is_conflict/2);
  867. is_put_to_missing_resource(Req, State) ->
  868. previously_existed(Req, State).
  869. %% moved_permanently/2 should return either false or {true, Location}
  870. %% with Location the full new URI of the resource.
  871. moved_permanently(Req, State, OnFalse) ->
  872. case call(Req, State, moved_permanently) of
  873. {{true, Location}, Req2, State2} ->
  874. Req3 = cowboy_req:set_resp_header(
  875. <<"location">>, Location, Req2),
  876. respond(Req3, State2, 301);
  877. {false, Req2, State2} ->
  878. OnFalse(Req2, State2);
  879. {stop, Req2, State2} ->
  880. terminate(Req2, State2);
  881. {Switch, Req2, State2} when element(1, Switch) =:= switch_handler ->
  882. switch_handler(Switch, Req2, State2);
  883. no_call ->
  884. OnFalse(Req, State)
  885. end.
  886. previously_existed(Req, State) ->
  887. expect(Req, State, previously_existed, false,
  888. fun (R, S) -> is_post_to_missing_resource(R, S, 404) end,
  889. fun (R, S) -> moved_permanently(R, S, fun moved_temporarily/2) end).
  890. %% moved_temporarily/2 should return either false or {true, Location}
  891. %% with Location the full new URI of the resource.
  892. moved_temporarily(Req, State) ->
  893. case call(Req, State, moved_temporarily) of
  894. {{true, Location}, Req2, State2} ->
  895. Req3 = cowboy_req:set_resp_header(
  896. <<"location">>, Location, Req2),
  897. respond(Req3, State2, 307);
  898. {false, Req2, State2} ->
  899. is_post_to_missing_resource(Req2, State2, 410);
  900. {stop, Req2, State2} ->
  901. terminate(Req2, State2);
  902. {Switch, Req2, State2} when element(1, Switch) =:= switch_handler ->
  903. switch_handler(Switch, Req2, State2);
  904. no_call ->
  905. is_post_to_missing_resource(Req, State, 410)
  906. end.
  907. is_post_to_missing_resource(Req, State=#state{method= <<"POST">>}, OnFalse) ->
  908. allow_missing_post(Req, State, OnFalse);
  909. is_post_to_missing_resource(Req, State, OnFalse) ->
  910. respond(Req, State, OnFalse).
  911. allow_missing_post(Req, State, OnFalse) ->
  912. expect(Req, State, allow_missing_post, true, fun accept_resource/2, OnFalse).
  913. method(Req, State=#state{method= <<"DELETE">>}) ->
  914. delete_resource(Req, State);
  915. method(Req, State=#state{method= <<"PUT">>}) ->
  916. is_conflict(Req, State);
  917. method(Req, State=#state{method=Method})
  918. when Method =:= <<"POST">>; Method =:= <<"PATCH">> ->
  919. accept_resource(Req, State);
  920. method(Req, State=#state{method=Method})
  921. when Method =:= <<"GET">>; Method =:= <<"HEAD">> ->
  922. set_resp_body_etag(Req, State);
  923. method(Req, State) ->
  924. multiple_choices(Req, State).
  925. %% delete_resource/2 should start deleting the resource and return.
  926. delete_resource(Req, State) ->
  927. expect(Req, State, delete_resource, false, 500, fun delete_completed/2).
  928. %% delete_completed/2 indicates whether the resource has been deleted yet.
  929. delete_completed(Req, State) ->
  930. expect(Req, State, delete_completed, true, fun has_resp_body/2, 202).
  931. is_conflict(Req, State) ->
  932. expect(Req, State, is_conflict, false, fun accept_resource/2, 409).
  933. %% content_types_accepted should return a list of media types and their
  934. %% associated callback functions in the same format as content_types_provided.
  935. %%
  936. %% The callback will then be called and is expected to process the content
  937. %% pushed to the resource in the request body.
  938. %%
  939. %% content_types_accepted SHOULD return a different list
  940. %% for each HTTP method.
  941. accept_resource(Req, State) ->
  942. case call(Req, State, content_types_accepted) of
  943. no_call ->
  944. respond(Req, State, 415);
  945. {stop, Req2, State2} ->
  946. terminate(Req2, State2);
  947. {Switch, Req2, State2} when element(1, Switch) =:= switch_handler ->
  948. switch_handler(Switch, Req2, State2);
  949. {CTA, Req2, State2} ->
  950. CTA2 = [normalize_content_types(P) || P <- CTA],
  951. try cowboy_req:parse_header(<<"content-type">>, Req2) of
  952. %% We do not match against the boundary parameter for multipart.
  953. {Type = <<"multipart">>, SubType, Params} ->
  954. ContentType = {Type, SubType, lists:keydelete(<<"boundary">>, 1, Params)},
  955. choose_content_type(Req2, State2, ContentType, CTA2);
  956. ContentType ->
  957. choose_content_type(Req2, State2, ContentType, CTA2)
  958. catch _:_ ->
  959. respond(Req2, State2, 415)
  960. end
  961. end.
  962. %% The special content type '*' will always match. It can be used as a
  963. %% catch-all content type for accepting any kind of request content.
  964. %% Note that because it will always match, it should be the last of the
  965. %% list of content types, otherwise it'll shadow the ones following.
  966. choose_content_type(Req, State, _ContentType, []) ->
  967. respond(Req, State, 415);
  968. choose_content_type(Req, State, ContentType, [{Accepted, Fun}|_Tail])
  969. when Accepted =:= '*'; Accepted =:= ContentType ->
  970. process_content_type(Req, State, Fun);
  971. %% The special parameter '*' will always match any kind of content type
  972. %% parameters.
  973. %% Note that because it will always match, it should be the last of the
  974. %% list for specific content type, otherwise it'll shadow the ones following.
  975. choose_content_type(Req, State, {Type, SubType, Param},
  976. [{{Type, SubType, AcceptedParam}, Fun}|_Tail])
  977. when AcceptedParam =:= '*'; AcceptedParam =:= Param ->
  978. process_content_type(Req, State, Fun);
  979. choose_content_type(Req, State, ContentType, [_Any|Tail]) ->
  980. choose_content_type(Req, State, ContentType, Tail).
  981. process_content_type(Req, State=#state{method=Method, exists=Exists}, Fun) ->
  982. try case call(Req, State, Fun) of
  983. {stop, Req2, State2} ->
  984. terminate(Req2, State2);
  985. {Switch, Req2, State2} when element(1, Switch) =:= switch_handler ->
  986. switch_handler(Switch, Req2, State2);
  987. {true, Req2, State2} when Exists ->
  988. next(Req2, State2, fun has_resp_body/2);
  989. {true, Req2, State2} ->
  990. next(Req2, State2, fun maybe_created/2);
  991. {false, Req2, State2} ->
  992. respond(Req2, State2, 400);
  993. {{true, ResURL}, Req2, State2} when Method =:= <<"POST">> ->
  994. Req3 = cowboy_req:set_resp_header(
  995. <<"location">>, ResURL, Req2),
  996. if
  997. Exists -> respond(Req3, State2, 303);
  998. true -> respond(Req3, State2, 201)
  999. end
  1000. end catch Class:Reason = {case_clause, no_call} ->
  1001. error_terminate(Req, State, Class, Reason)
  1002. end.
  1003. %% If PUT was used then the resource has been created at the current URL.
  1004. %% Otherwise, if a location header has been set then the resource has been
  1005. %% created at a new URL. If not, send a 200 or 204 as expected from a
  1006. %% POST or PATCH request.
  1007. maybe_created(Req, State=#state{method= <<"PUT">>}) ->
  1008. respond(Req, State, 201);
  1009. maybe_created(Req, State) ->
  1010. case cowboy_req:has_resp_header(<<"location">>, Req) of
  1011. true -> respond(Req, State, 201);
  1012. false -> has_resp_body(Req, State)
  1013. end.
  1014. has_resp_body(Req, State) ->
  1015. case cowboy_req:has_resp_body(Req) of
  1016. true -> multiple_choices(Req, State);
  1017. false -> respond(Req, State, 204)
  1018. end.
  1019. %% Set the Etag header if any for the response provided.
  1020. set_resp_body_etag(Req, State) ->
  1021. try set_resp_etag(Req, State) of
  1022. {Req2, State2} ->
  1023. set_resp_body_last_modified(Req2, State2)
  1024. catch Class:Reason ->
  1025. error_terminate(Req, State, Class, Reason)
  1026. end.
  1027. %% Set the Last-Modified header if any for the response provided.
  1028. set_resp_body_last_modified(Req, State) ->
  1029. try last_modified(Req, State) of
  1030. {LastModified, Req2, State2} ->
  1031. case LastModified of
  1032. LastModified when is_atom(LastModified) ->
  1033. set_resp_body_expires(Req2, State2);
  1034. LastModified ->
  1035. LastModifiedBin = cowboy_clock:rfc1123(LastModified),
  1036. Req3 = cowboy_req:set_resp_header(
  1037. <<"last-modified">>, LastModifiedBin, Req2),
  1038. set_resp_body_expires(Req3, State2)
  1039. end
  1040. catch Class:Reason ->
  1041. error_terminate(Req, State, Class, Reason)
  1042. end.
  1043. %% Set the Expires header if any for the response provided.
  1044. set_resp_body_expires(Req, State) ->
  1045. try set_resp_expires(Req, State) of
  1046. {Req2, State2} ->
  1047. if_range(Req2, State2)
  1048. catch Class:Reason ->
  1049. error_terminate(Req, State, Class, Reason)
  1050. end.
  1051. %% When both the if-range and range headers are set, we perform
  1052. %% a strong comparison. If it fails, we send a full response.
  1053. if_range(Req=#{headers := #{<<"if-range">> := _, <<"range">> := _}},
  1054. State=#state{etag=Etag}) ->
  1055. try cowboy_req:parse_header(<<"if-range">>, Req) of
  1056. %% Strong etag comparison is an exact match with the generate_etag result.
  1057. Etag={strong, _} ->
  1058. range(Req, State);
  1059. %% We cannot do a strong date comparison because we have
  1060. %% no way of knowing whether the representation changed
  1061. %% twice during the second covered by the presented
  1062. %% validator. (RFC7232 2.2.2)
  1063. _ ->
  1064. set_resp_body(Req, State)
  1065. catch _:_ ->
  1066. set_resp_body(Req, State)
  1067. end;
  1068. if_range(Req, State) ->
  1069. range(Req, State).
  1070. range(Req, State=#state{ranges_a=[]}) ->
  1071. set_resp_body(Req, State);
  1072. range(Req, State) ->
  1073. try cowboy_req:parse_header(<<"range">>, Req) of
  1074. undefined ->
  1075. set_resp_body(Req, State);
  1076. %% @todo Maybe change parse_header to return <<"bytes">> in 3.0.
  1077. {bytes, BytesRange} ->
  1078. choose_range(Req, State, {<<"bytes">>, BytesRange});
  1079. Range ->
  1080. choose_range(Req, State, Range)
  1081. catch _:_ ->
  1082. %% We send a 416 response back when we can't parse the
  1083. %% range header at all. I'm not sure this is the right
  1084. %% way to go but at least this can help clients identify
  1085. %% what went wrong when their range requests never work.
  1086. range_not_satisfiable(Req, State, undefined)
  1087. end.
  1088. choose_range(Req, State=#state{ranges_a=RangesAccepted}, Range={RangeUnit, _}) ->
  1089. case lists:keyfind(RangeUnit, 1, RangesAccepted) of
  1090. {_, Callback} ->
  1091. %% We pass the selected range onward in the Req.
  1092. range_satisfiable(Req#{range => Range}, State, Callback);
  1093. false ->
  1094. set_resp_body(Req, State)
  1095. end.
  1096. range_satisfiable(Req, State, Callback) ->
  1097. case call(Req, State, range_satisfiable) of
  1098. no_call ->
  1099. set_ranged_body(Req, State, Callback);
  1100. {stop, Req2, State2} ->
  1101. terminate(Req2, State2);
  1102. {Switch, Req2, State2} when element(1, Switch) =:= switch_handler ->
  1103. switch_handler(Switch, Req2, State2);
  1104. {true, Req2, State2} ->
  1105. set_ranged_body(Req2, State2, Callback);
  1106. {false, Req2, State2} ->
  1107. range_not_satisfiable(Req2, State2, undefined);
  1108. {{false, Int}, Req2, State2} when is_integer(Int) ->
  1109. range_not_satisfiable(Req2, State2, [<<"*/">>, integer_to_binary(Int)]);
  1110. {{false, Iodata}, Req2, State2} when is_binary(Iodata); is_list(Iodata) ->
  1111. range_not_satisfiable(Req2, State2, Iodata)
  1112. end.
  1113. %% When the callback selected is 'auto' and the range unit
  1114. %% is bytes, we call the normal provide callback and split
  1115. %% the content automatically.
  1116. set_ranged_body(Req=#{range := {<<"bytes">>, _}}, State, auto) ->
  1117. set_ranged_body_auto(Req, State);
  1118. set_ranged_body(Req, State, Callback) ->
  1119. set_ranged_body_callback(Req, State, Callback).
  1120. set_ranged_body_auto(Req, State=#state{handler=Handler, content_type_a={_, Callback}}) ->
  1121. try case call(Req, State, Callback) of
  1122. {stop, Req2, State2} ->
  1123. terminate(Req2, State2);
  1124. {Switch, Req2, State2} when element(1, Switch) =:= switch_handler ->
  1125. switch_handler(Switch, Req2, State2);
  1126. {Body, Req2, State2} ->
  1127. maybe_set_ranged_body_auto(Req2, State2, Body)
  1128. end catch Class:{case_clause, no_call} ->
  1129. error_terminate(Req, State, Class, {error, {missing_callback, {Handler, Callback, 2}},
  1130. 'A callback specified in content_types_provided/2 is not exported.'})
  1131. end.
  1132. maybe_set_ranged_body_auto(Req=#{range := {_, Ranges}}, State, Body) ->
  1133. Size = case Body of
  1134. {sendfile, _, Bytes, _} -> Bytes;
  1135. _ -> iolist_size(Body)
  1136. end,
  1137. Checks = [case Range of
  1138. {From, infinity} -> From < Size;
  1139. {From, To} -> (From < Size) andalso (From =< To) andalso (To =< Size);
  1140. Neg -> (Neg =/= 0) andalso (-Neg < Size)
  1141. end || Range <- Ranges],
  1142. case lists:usort(Checks) of
  1143. [true] -> set_ranged_body_auto(Req, State, Body);
  1144. _ -> range_not_satisfiable(Req, State, [<<"*/">>, integer_to_binary(Size)])
  1145. end.
  1146. %% We might also want to have some checks about range order,
  1147. %% number of ranges, and perhaps also join ranges that are
  1148. %% too close into one contiguous range. Some of these can
  1149. %% be done before calling the ProvideCallback.
  1150. set_ranged_body_auto(Req=#{range := {_, Ranges}}, State, Body) ->
  1151. Parts = [ranged_partition(Range, Body) || Range <- Ranges],
  1152. case Parts of
  1153. [OnePart] -> set_one_ranged_body(Req, State, OnePart);
  1154. _ when is_tuple(Body) -> send_multipart_ranged_body(Req, State, Parts);
  1155. _ -> set_multipart_ranged_body(Req, State, Parts)
  1156. end.
  1157. ranged_partition(Range, {sendfile, Offset0, Bytes0, Path}) ->
  1158. {From, To, Offset, Bytes} = case Range of
  1159. {From0, infinity} -> {From0, Bytes0 - 1, Offset0 + From0, Bytes0 - From0};
  1160. {From0, To0} -> {From0, To0, Offset0 + From0, 1 + To0 - From0};
  1161. Neg -> {Bytes0 + Neg, Bytes0 - 1, Offset0 + Bytes0 + Neg, -Neg}
  1162. end,
  1163. {{From, To, Bytes0}, {sendfile, Offset, Bytes, Path}};
  1164. ranged_partition(Range, Data0) ->
  1165. Total = iolist_size(Data0),
  1166. {From, To, Data} = case Range of
  1167. {From0, infinity} ->
  1168. {_, Data1} = cow_iolists:split(From0, Data0),
  1169. {From0, Total - 1, Data1};
  1170. {From0, To0} ->
  1171. {_, Data1} = cow_iolists:split(From0, Data0),
  1172. {Data2, _} = cow_iolists:split(To0 - From0 + 1, Data1),
  1173. {From0, To0, Data2};
  1174. Neg ->
  1175. {_, Data1} = cow_iolists:split(Total + Neg, Data0),
  1176. {Total + Neg, Total - 1, Data1}
  1177. end,
  1178. {{From, To, Total}, Data}.
  1179. -ifdef(TEST).
  1180. ranged_partition_test_() ->
  1181. Tests = [
  1182. %% Sendfile with open-ended range.
  1183. {{0, infinity}, {sendfile, 0, 12, "t"}, {{0, 11, 12}, {sendfile, 0, 12, "t"}}},
  1184. {{6, infinity}, {sendfile, 0, 12, "t"}, {{6, 11, 12}, {sendfile, 6, 6, "t"}}},
  1185. {{11, infinity}, {sendfile, 0, 12, "t"}, {{11, 11, 12}, {sendfile, 11, 1, "t"}}},
  1186. %% Sendfile with open-ended range. Sendfile tuple has an offset originally.
  1187. {{0, infinity}, {sendfile, 3, 12, "t"}, {{0, 11, 12}, {sendfile, 3, 12, "t"}}},
  1188. {{6, infinity}, {sendfile, 3, 12, "t"}, {{6, 11, 12}, {sendfile, 9, 6, "t"}}},
  1189. {{11, infinity}, {sendfile, 3, 12, "t"}, {{11, 11, 12}, {sendfile, 14, 1, "t"}}},
  1190. %% Sendfile with a specific range.
  1191. {{0, 11}, {sendfile, 0, 12, "t"}, {{0, 11, 12}, {sendfile, 0, 12, "t"}}},
  1192. {{6, 11}, {sendfile, 0, 12, "t"}, {{6, 11, 12}, {sendfile, 6, 6, "t"}}},
  1193. {{11, 11}, {sendfile, 0, 12, "t"}, {{11, 11, 12}, {sendfile, 11, 1, "t"}}},
  1194. {{1, 10}, {sendfile, 0, 12, "t"}, {{1, 10, 12}, {sendfile, 1, 10, "t"}}},
  1195. %% Sendfile with a specific range. Sendfile tuple has an offset originally.
  1196. {{0, 11}, {sendfile, 3, 12, "t"}, {{0, 11, 12}, {sendfile, 3, 12, "t"}}},
  1197. {{6, 11}, {sendfile, 3, 12, "t"}, {{6, 11, 12}, {sendfile, 9, 6, "t"}}},
  1198. {{11, 11}, {sendfile, 3, 12, "t"}, {{11, 11, 12}, {sendfile, 14, 1, "t"}}},
  1199. {{1, 10}, {sendfile, 3, 12, "t"}, {{1, 10, 12}, {sendfile, 4, 10, "t"}}},
  1200. %% Sendfile with negative range.
  1201. {-12, {sendfile, 0, 12, "t"}, {{0, 11, 12}, {sendfile, 0, 12, "t"}}},
  1202. {-6, {sendfile, 0, 12, "t"}, {{6, 11, 12}, {sendfile, 6, 6, "t"}}},
  1203. {-1, {sendfile, 0, 12, "t"}, {{11, 11, 12}, {sendfile, 11, 1, "t"}}},
  1204. %% Sendfile with negative range. Sendfile tuple has an offset originally.
  1205. {-12, {sendfile, 3, 12, "t"}, {{0, 11, 12}, {sendfile, 3, 12, "t"}}},
  1206. {-6, {sendfile, 3, 12, "t"}, {{6, 11, 12}, {sendfile, 9, 6, "t"}}},
  1207. {-1, {sendfile, 3, 12, "t"}, {{11, 11, 12}, {sendfile, 14, 1, "t"}}},
  1208. %% Iodata with open-ended range.
  1209. {{0, infinity}, <<"Hello world!">>, {{0, 11, 12}, <<"Hello world!">>}},
  1210. {{6, infinity}, <<"Hello world!">>, {{6, 11, 12}, <<"world!">>}},
  1211. {{11, infinity}, <<"Hello world!">>, {{11, 11, 12}, <<"!">>}},
  1212. %% Iodata with a specific range. The resulting data is
  1213. %% wrapped in a list because of how cow_iolists:split/2 works.
  1214. {{0, 11}, <<"Hello world!">>, {{0, 11, 12}, [<<"Hello world!">>]}},
  1215. {{6, 11}, <<"Hello world!">>, {{6, 11, 12}, [<<"world!">>]}},
  1216. {{11, 11}, <<"Hello world!">>, {{11, 11, 12}, [<<"!">>]}},
  1217. {{1, 10}, <<"Hello world!">>, {{1, 10, 12}, [<<"ello world">>]}},
  1218. %% Iodata with negative range.
  1219. {-12, <<"Hello world!">>, {{0, 11, 12}, <<"Hello world!">>}},
  1220. {-6, <<"Hello world!">>, {{6, 11, 12}, <<"world!">>}},
  1221. {-1, <<"Hello world!">>, {{11, 11, 12}, <<"!">>}}
  1222. ],
  1223. [{iolist_to_binary(io_lib:format("range ~p data ~p", [VR, VD])),
  1224. fun() -> R = ranged_partition(VR, VD) end} || {VR, VD, R} <- Tests].
  1225. -endif.
  1226. set_ranged_body_callback(Req, State=#state{handler=Handler}, Callback) ->
  1227. try case call(Req, State, Callback) of
  1228. {stop, Req2, State2} ->
  1229. terminate(Req2, State2);
  1230. {Switch, Req2, State2} when element(1, Switch) =:= switch_handler ->
  1231. switch_handler(Switch, Req2, State2);
  1232. %% When we receive a single range, we send it directly.
  1233. {[OneRange], Req2, State2} ->
  1234. set_one_ranged_body(Req2, State2, OneRange);
  1235. %% When we receive multiple ranges we have to send them as multipart/byteranges.
  1236. %% This also applies to non-bytes units. (RFC7233 A) If users don't want to use
  1237. %% this for non-bytes units they can always return a single range with a binary
  1238. %% content-range information.
  1239. {Ranges, Req2, State2} when length(Ranges) > 1 ->
  1240. %% We have to check whether there are sendfile tuples in the
  1241. %% ranges to be sent. If there are we must use stream_reply.
  1242. HasSendfile = [] =/= [true || {_, {sendfile, _, _, _}} <- Ranges],
  1243. case HasSendfile of
  1244. true -> send_multipart_ranged_body(Req2, State2, Ranges);
  1245. false -> set_multipart_ranged_body(Req2, State2, Ranges)
  1246. end
  1247. end catch Class:{case_clause, no_call} ->
  1248. error_terminate(Req, State, Class, {error, {missing_callback, {Handler, Callback, 2}},
  1249. 'A callback specified in ranges_provided/2 is not exported.'})
  1250. end.
  1251. set_one_ranged_body(Req0, State, OneRange) ->
  1252. {ContentRange, Body} = prepare_range(Req0, OneRange),
  1253. Req1 = cowboy_req:set_resp_header(<<"content-range">>, ContentRange, Req0),
  1254. Req = cowboy_req:set_resp_body(Body, Req1),
  1255. respond(Req, State, 206).
  1256. set_multipart_ranged_body(Req, State, [FirstRange|MoreRanges]) ->
  1257. Boundary = cow_multipart:boundary(),
  1258. ContentType = cowboy_req:resp_header(<<"content-type">>, Req),
  1259. {FirstContentRange, FirstPartBody} = prepare_range(Req, FirstRange),
  1260. FirstPartHead = cow_multipart:first_part(Boundary, [
  1261. {<<"content-type">>, ContentType},
  1262. {<<"content-range">>, FirstContentRange}
  1263. ]),
  1264. MoreParts = [begin
  1265. {NextContentRange, NextPartBody} = prepare_range(Req, NextRange),
  1266. NextPartHead = cow_multipart:part(Boundary, [
  1267. {<<"content-type">>, ContentType},
  1268. {<<"content-range">>, NextContentRange}
  1269. ]),
  1270. [NextPartHead, NextPartBody]
  1271. end || NextRange <- MoreRanges],
  1272. Body = [FirstPartHead, FirstPartBody, MoreParts, cow_multipart:close(Boundary)],
  1273. Req2 = cowboy_req:set_resp_header(<<"content-type">>,
  1274. [<<"multipart/byteranges; boundary=">>, Boundary], Req),
  1275. Req3 = cowboy_req:set_resp_body(Body, Req2),
  1276. respond(Req3, State, 206).
  1277. %% Similar to set_multipart_ranged_body except we have to stream
  1278. %% the data because the parts contain sendfile tuples.
  1279. send_multipart_ranged_body(Req, State, [FirstRange|MoreRanges]) ->
  1280. Boundary = cow_multipart:boundary(),
  1281. ContentType = cowboy_req:resp_header(<<"content-type">>, Req),
  1282. Req2 = cowboy_req:set_resp_header(<<"content-type">>,
  1283. [<<"multipart/byteranges; boundary=">>, Boundary], Req),
  1284. Req3 = cowboy_req:stream_reply(206, Req2),
  1285. {FirstContentRange, FirstPartBody} = prepare_range(Req, FirstRange),
  1286. FirstPartHead = cow_multipart:first_part(Boundary, [
  1287. {<<"content-type">>, ContentType},
  1288. {<<"content-range">>, FirstContentRange}
  1289. ]),
  1290. cowboy_req:stream_body(FirstPartHead, nofin, Req3),
  1291. cowboy_req:stream_body(FirstPartBody, nofin, Req3),
  1292. _ = [begin
  1293. {NextContentRange, NextPartBody} = prepare_range(Req, NextRange),
  1294. NextPartHead = cow_multipart:part(Boundary, [
  1295. {<<"content-type">>, ContentType},
  1296. {<<"content-range">>, NextContentRange}
  1297. ]),
  1298. cowboy_req:stream_body(NextPartHead, nofin, Req3),
  1299. cowboy_req:stream_body(NextPartBody, nofin, Req3),
  1300. [NextPartHead, NextPartBody]
  1301. end || NextRange <- MoreRanges],
  1302. cowboy_req:stream_body(cow_multipart:close(Boundary), fin, Req3),
  1303. terminate(Req3, State).
  1304. prepare_range(#{range := {RangeUnit, _}}, {{From, To, Total0}, Body}) ->
  1305. Total = case Total0 of
  1306. '*' -> <<"*">>;
  1307. _ -> integer_to_binary(Total0)
  1308. end,
  1309. ContentRange = [RangeUnit, $\s, integer_to_binary(From),
  1310. $-, integer_to_binary(To), $/, Total],
  1311. {ContentRange, Body};
  1312. prepare_range(#{range := {RangeUnit, _}}, {RangeData, Body}) ->
  1313. {[RangeUnit, $\s, RangeData], Body}.
  1314. %% We send the content-range header when we can on error.
  1315. range_not_satisfiable(Req, State, undefined) ->
  1316. respond(Req, State, 416);
  1317. range_not_satisfiable(Req0=#{range := {RangeUnit, _}}, State, RangeData) ->
  1318. Req = cowboy_req:set_resp_header(<<"content-range">>,
  1319. [RangeUnit, $\s, RangeData], Req0),
  1320. respond(Req, State, 416).
  1321. %% Set the response headers and call the callback found using
  1322. %% content_types_provided/2 to obtain the request body and add
  1323. %% it to the response.
  1324. set_resp_body(Req, State=#state{handler=Handler, content_type_a={_, Callback}}) ->
  1325. try case call(Req, State, Callback) of
  1326. {stop, Req2, State2} ->
  1327. terminate(Req2, State2);
  1328. {Switch, Req2, State2} when element(1, Switch) =:= switch_handler ->
  1329. switch_handler(Switch, Req2, State2);
  1330. {Body, Req2, State2} ->
  1331. Req3 = cowboy_req:set_resp_body(Body, Req2),
  1332. multiple_choices(Req3, State2)
  1333. end catch Class:{case_clause, no_call} ->
  1334. error_terminate(Req, State, Class, {error, {missing_callback, {Handler, Callback, 2}},
  1335. 'A callback specified in content_types_provided/2 is not exported.'})
  1336. end.
  1337. multiple_choices(Req, State) ->
  1338. expect(Req, State, multiple_choices, false, 200, 300).
  1339. %% Response utility functions.
  1340. set_resp_etag(Req, State) ->
  1341. {Etag, Req2, State2} = generate_etag(Req, State),
  1342. case Etag of
  1343. undefined ->
  1344. {Req2, State2};
  1345. Etag ->
  1346. Req3 = cowboy_req:set_resp_header(
  1347. <<"etag">>, encode_etag(Etag), Req2),
  1348. {Req3, State2}
  1349. end.
  1350. -spec encode_etag({strong | weak, binary()}) -> iolist().
  1351. encode_etag({strong, Etag}) -> [$",Etag,$"];
  1352. encode_etag({weak, Etag}) -> ["W/\"",Etag,$"].
  1353. set_resp_expires(Req, State) ->
  1354. {Expires, Req2, State2} = expires(Req, State),
  1355. case Expires of
  1356. Expires when is_atom(Expires) ->
  1357. {Req2, State2};
  1358. Expires when is_binary(Expires) ->
  1359. Req3 = cowboy_req:set_resp_header(
  1360. <<"expires">>, Expires, Req2),
  1361. {Req3, State2};
  1362. Expires ->
  1363. ExpiresBin = cowboy_clock:rfc1123(Expires),
  1364. Req3 = cowboy_req:set_resp_header(
  1365. <<"expires">>, ExpiresBin, Req2),
  1366. {Req3, State2}
  1367. end.
  1368. %% Info retrieval. No logic.
  1369. generate_etag(Req, State=#state{etag=no_call}) ->
  1370. {undefined, Req, State};
  1371. generate_etag(Req, State=#state{etag=undefined}) ->
  1372. case unsafe_call(Req, State, generate_etag) of
  1373. no_call ->
  1374. {undefined, Req, State#state{etag=no_call}};
  1375. {Etag, Req2, State2} when is_binary(Etag) ->
  1376. Etag2 = cow_http_hd:parse_etag(Etag),
  1377. {Etag2, Req2, State2#state{etag=Etag2}};
  1378. {Etag, Req2, State2} ->
  1379. {Etag, Req2, State2#state{etag=Etag}}
  1380. end;
  1381. generate_etag(Req, State=#state{etag=Etag}) ->
  1382. {Etag, Req, State}.
  1383. last_modified(Req, State=#state{last_modified=no_call}) ->
  1384. {undefined, Req, State};
  1385. last_modified(Req, State=#state{last_modified=undefined}) ->
  1386. case unsafe_call(Req, State, last_modified) of
  1387. no_call ->
  1388. {undefined, Req, State#state{last_modified=no_call}};
  1389. {LastModified, Req2, State2} ->
  1390. {LastModified, Req2, State2#state{last_modified=LastModified}}
  1391. end;
  1392. last_modified(Req, State=#state{last_modified=LastModified}) ->
  1393. {LastModified, Req, State}.
  1394. expires(Req, State=#state{expires=no_call}) ->
  1395. {undefined, Req, State};
  1396. expires(Req, State=#state{expires=undefined}) ->
  1397. case unsafe_call(Req, State, expires) of
  1398. no_call ->
  1399. {undefined, Req, State#state{expires=no_call}};
  1400. {Expires, Req2, State2} ->
  1401. {Expires, Req2, State2#state{expires=Expires}}
  1402. end;
  1403. expires(Req, State=#state{expires=Expires}) ->
  1404. {Expires, Req, State}.
  1405. %% REST primitives.
  1406. expect(Req, State, Callback, Expected, OnTrue, OnFalse) ->
  1407. case call(Req, State, Callback) of
  1408. no_call ->
  1409. next(Req, State, OnTrue);
  1410. {stop, Req2, State2} ->
  1411. terminate(Req2, State2);
  1412. {Switch, Req2, State2} when element(1, Switch) =:= switch_handler ->
  1413. switch_handler(Switch, Req2, State2);
  1414. {Expected, Req2, State2} ->
  1415. next(Req2, State2, OnTrue);
  1416. {_Unexpected, Req2, State2} ->
  1417. next(Req2, State2, OnFalse)
  1418. end.
  1419. call(Req0, State=#state{handler=Handler,
  1420. handler_state=HandlerState0}, Callback) ->
  1421. case erlang:function_exported(Handler, Callback, 2) of
  1422. true ->
  1423. try Handler:Callback(Req0, HandlerState0) of
  1424. no_call ->
  1425. no_call;
  1426. {Result, Req, HandlerState} ->
  1427. {Result, Req, State#state{handler_state=HandlerState}}
  1428. catch Class:Reason ->
  1429. error_terminate(Req0, State, Class, Reason)
  1430. end;
  1431. false ->
  1432. no_call
  1433. end.
  1434. unsafe_call(Req0, State=#state{handler=Handler,
  1435. handler_state=HandlerState0}, Callback) ->
  1436. case erlang:function_exported(Handler, Callback, 2) of
  1437. false ->
  1438. no_call;
  1439. true ->
  1440. case Handler:Callback(Req0, HandlerState0) of
  1441. no_call ->
  1442. no_call;
  1443. {Result, Req, HandlerState} ->
  1444. {Result, Req, State#state{handler_state=HandlerState}}
  1445. end
  1446. end.
  1447. next(Req, State, Next) when is_function(Next) ->
  1448. Next(Req, State);
  1449. next(Req, State, StatusCode) when is_integer(StatusCode) ->
  1450. respond(Req, State, StatusCode).
  1451. respond(Req0, State, StatusCode) ->
  1452. %% We remove the content-type header when there is no body,
  1453. %% except when the status code is 200 because it might have
  1454. %% been intended (for example sending an empty file).
  1455. Req = case cowboy_req:has_resp_body(Req0) of
  1456. true when StatusCode =:= 200 -> Req0;
  1457. true -> Req0;
  1458. false -> cowboy_req:delete_resp_header(<<"content-type">>, Req0)
  1459. end,
  1460. terminate(cowboy_req:reply(StatusCode, Req), State).
  1461. switch_handler({switch_handler, Mod}, Req, #state{handler_state=HandlerState}) ->
  1462. {Mod, Req, HandlerState};
  1463. switch_handler({switch_handler, Mod, Opts}, Req, #state{handler_state=HandlerState}) ->
  1464. {Mod, Req, HandlerState, Opts}.
  1465. -spec error_terminate(cowboy_req:req(), #state{}, atom(), any()) -> no_return().
  1466. error_terminate(Req, #state{handler=Handler, handler_state=HandlerState}, Class, Reason) ->
  1467. cowboy_handler:terminate({crash, Class, Reason}, Req, HandlerState, Handler),
  1468. erlang:raise(Class, Reason, erlang:get_stacktrace()).
  1469. terminate(Req, #state{handler=Handler, handler_state=HandlerState}) ->
  1470. Result = cowboy_handler:terminate(normal, Req, HandlerState, Handler),
  1471. {ok, Req, Result}.