cowboy_req.erl 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330
  1. %% Copyright (c) 2011-2014, Loïc Hoguin <essen@ninenines.eu>
  2. %% Copyright (c) 2011, Anthony Ramine <nox@dev-extend.eu>
  3. %%
  4. %% Permission to use, copy, modify, and/or distribute this software for any
  5. %% purpose with or without fee is hereby granted, provided that the above
  6. %% copyright notice and this permission notice appear in all copies.
  7. %%
  8. %% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  9. %% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  10. %% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  11. %% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  12. %% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  13. %% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  14. %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. -module(cowboy_req).
  16. %% Request API.
  17. -export([new/14]).
  18. -export([method/1]).
  19. -export([version/1]).
  20. -export([peer/1]).
  21. -export([host/1]).
  22. -export([host_info/1]).
  23. -export([port/1]).
  24. -export([path/1]).
  25. -export([path_info/1]).
  26. -export([qs/1]).
  27. -export([parse_qs/1]).
  28. -export([match_qs/2]).
  29. -export([host_url/1]).
  30. -export([url/1]).
  31. -export([binding/2]).
  32. -export([binding/3]).
  33. -export([bindings/1]).
  34. -export([header/2]).
  35. -export([header/3]).
  36. -export([headers/1]).
  37. -export([parse_header/2]).
  38. -export([parse_header/3]).
  39. -export([parse_cookies/1]).
  40. -export([match_cookies/2]).
  41. -export([meta/2]).
  42. -export([meta/3]).
  43. -export([set_meta/3]).
  44. %% Request body API.
  45. -export([has_body/1]).
  46. -export([body_length/1]).
  47. -export([body/1]).
  48. -export([body/2]).
  49. -export([body_qs/1]).
  50. -export([body_qs/2]).
  51. %% Multipart API.
  52. -export([part/1]).
  53. -export([part/2]).
  54. -export([part_body/1]).
  55. -export([part_body/2]).
  56. %% Response API.
  57. -export([set_resp_cookie/4]).
  58. -export([set_resp_header/3]).
  59. -export([set_resp_body/2]).
  60. -export([set_resp_body_fun/2]).
  61. -export([set_resp_body_fun/3]).
  62. -export([has_resp_header/2]).
  63. -export([has_resp_body/1]).
  64. -export([delete_resp_header/2]).
  65. -export([reply/2]).
  66. -export([reply/3]).
  67. -export([reply/4]).
  68. -export([chunked_reply/2]).
  69. -export([chunked_reply/3]).
  70. -export([chunk/2]).
  71. -export([upgrade_reply/3]).
  72. -export([continue/1]).
  73. -export([maybe_reply/2]).
  74. -export([ensure_response/2]).
  75. %% Private setter/getter API.
  76. -export([append_buffer/2]).
  77. -export([get/2]).
  78. -export([set/2]).
  79. -export([set_bindings/4]).
  80. -export([lock/1]).
  81. -export([to_list/1]).
  82. -type cookie_opts() :: cow_cookie:cookie_opts().
  83. -export_type([cookie_opts/0]).
  84. -type content_decode_fun() :: fun((binary()) -> binary()).
  85. -type transfer_decode_fun() :: fun((binary(), any())
  86. -> cow_http_te:decode_ret()).
  87. -type body_opts() :: [{continue, boolean()}
  88. | {length, non_neg_integer()}
  89. | {read_length, non_neg_integer()}
  90. | {read_timeout, timeout()}
  91. | {transfer_decode, transfer_decode_fun(), any()}
  92. | {content_decode, content_decode_fun()}].
  93. -export_type([body_opts/0]).
  94. -type resp_body_fun() :: fun((any(), module()) -> ok).
  95. -type send_chunk_fun() :: fun((iodata()) -> ok).
  96. -type resp_chunked_fun() :: fun((send_chunk_fun()) -> ok).
  97. -record(http_req, {
  98. %% Transport.
  99. socket = undefined :: any(),
  100. transport = undefined :: undefined | module(),
  101. connection = keepalive :: keepalive | close,
  102. %% Request.
  103. pid = undefined :: pid(),
  104. method = <<"GET">> :: binary(),
  105. version = 'HTTP/1.1' :: cowboy:http_version(),
  106. peer = undefined :: undefined | {inet:ip_address(), inet:port_number()},
  107. host = undefined :: undefined | binary(),
  108. host_info = undefined :: undefined | cowboy_router:tokens(),
  109. port = undefined :: undefined | inet:port_number(),
  110. path = undefined :: binary(),
  111. path_info = undefined :: undefined | cowboy_router:tokens(),
  112. qs = undefined :: binary(),
  113. bindings = undefined :: undefined | cowboy_router:bindings(),
  114. headers = [] :: cowboy:http_headers(),
  115. meta = [] :: [{atom(), any()}],
  116. %% Request body.
  117. body_state = waiting :: waiting | done | {stream, non_neg_integer(),
  118. transfer_decode_fun(), any(), content_decode_fun()},
  119. buffer = <<>> :: binary(),
  120. multipart = undefined :: undefined | {binary(), binary()},
  121. %% Response.
  122. resp_compress = false :: boolean(),
  123. resp_state = waiting :: locked | waiting | waiting_stream
  124. | chunks | stream | done,
  125. resp_headers = [] :: cowboy:http_headers(),
  126. resp_body = <<>> :: iodata() | resp_body_fun()
  127. | {non_neg_integer(), resp_body_fun()}
  128. | {chunked, resp_chunked_fun()},
  129. %% Functions.
  130. onresponse = undefined :: undefined | already_called
  131. | cowboy:onresponse_fun()
  132. }).
  133. -opaque req() :: #http_req{}.
  134. -export_type([req/0]).
  135. %% Request API.
  136. -spec new(any(), module(),
  137. undefined | {inet:ip_address(), inet:port_number()},
  138. binary(), binary(), binary(),
  139. cowboy:http_version(), cowboy:http_headers(), binary(),
  140. inet:port_number() | undefined, binary(), boolean(), boolean(),
  141. undefined | cowboy:onresponse_fun())
  142. -> req().
  143. new(Socket, Transport, Peer, Method, Path, Query,
  144. Version, Headers, Host, Port, Buffer, CanKeepalive,
  145. Compress, OnResponse) ->
  146. Req = #http_req{socket=Socket, transport=Transport, pid=self(), peer=Peer,
  147. method=Method, path=Path, qs=Query, version=Version,
  148. headers=Headers, host=Host, port=Port, buffer=Buffer,
  149. resp_compress=Compress, onresponse=OnResponse},
  150. case CanKeepalive of
  151. false ->
  152. Req#http_req{connection=close};
  153. true ->
  154. case parse_header(<<"connection">>, Req) of
  155. undefined ->
  156. case Version of
  157. 'HTTP/1.1' -> Req; %% keepalive
  158. 'HTTP/1.0' -> Req#http_req{connection=close}
  159. end;
  160. Tokens ->
  161. Connection = connection_to_atom(Tokens),
  162. Req#http_req{connection=Connection}
  163. end
  164. end.
  165. -spec method(req()) -> binary().
  166. method(Req) ->
  167. Req#http_req.method.
  168. -spec version(req()) -> cowboy:http_version().
  169. version(Req) ->
  170. Req#http_req.version.
  171. -spec peer(req()) -> {inet:ip_address(), inet:port_number()} | undefined.
  172. peer(Req) ->
  173. Req#http_req.peer.
  174. -spec host(req()) -> binary().
  175. host(Req) ->
  176. Req#http_req.host.
  177. -spec host_info(req()) -> cowboy_router:tokens() | undefined.
  178. host_info(Req) ->
  179. Req#http_req.host_info.
  180. -spec port(req()) -> inet:port_number().
  181. port(Req) ->
  182. Req#http_req.port.
  183. -spec path(req()) -> binary().
  184. path(Req) ->
  185. Req#http_req.path.
  186. -spec path_info(req()) -> cowboy_router:tokens() | undefined.
  187. path_info(Req) ->
  188. Req#http_req.path_info.
  189. -spec qs(req()) -> binary().
  190. qs(Req) ->
  191. Req#http_req.qs.
  192. -spec parse_qs(req()) -> [{binary(), binary() | true}].
  193. parse_qs(#http_req{qs=Qs}) ->
  194. cow_qs:parse_qs(Qs).
  195. -spec match_qs(cowboy:fields(), req()) -> map().
  196. match_qs(Fields, Req) ->
  197. filter(Fields, kvlist_to_map(Fields, parse_qs(Req))).
  198. %% The URL includes the scheme, host and port only.
  199. -spec host_url(req()) -> undefined | binary().
  200. host_url(#http_req{port=undefined}) ->
  201. undefined;
  202. host_url(#http_req{transport=Transport, host=Host, port=Port}) ->
  203. TransportName = Transport:name(),
  204. Secure = case TransportName of
  205. ssl -> <<"s">>;
  206. _ -> <<>>
  207. end,
  208. PortBin = case {TransportName, Port} of
  209. {ssl, 443} -> <<>>;
  210. {tcp, 80} -> <<>>;
  211. _ -> << ":", (integer_to_binary(Port))/binary >>
  212. end,
  213. << "http", Secure/binary, "://", Host/binary, PortBin/binary >>.
  214. %% The URL includes the scheme, host, port, path and query string.
  215. -spec url(req()) -> undefined | binary().
  216. url(Req=#http_req{}) ->
  217. HostURL = host_url(Req),
  218. url(Req, HostURL).
  219. url(_, undefined) ->
  220. undefined;
  221. url(#http_req{path=Path, qs=QS}, HostURL) ->
  222. QS2 = case QS of
  223. <<>> -> <<>>;
  224. _ -> << "?", QS/binary >>
  225. end,
  226. << HostURL/binary, Path/binary, QS2/binary >>.
  227. -spec binding(atom(), req()) -> any() | undefined.
  228. binding(Name, Req) ->
  229. binding(Name, Req, undefined).
  230. -spec binding(atom(), req(), Default) -> any() | Default when Default::any().
  231. binding(Name, Req, Default) when is_atom(Name) ->
  232. case lists:keyfind(Name, 1, Req#http_req.bindings) of
  233. {_, Value} -> Value;
  234. false -> Default
  235. end.
  236. -spec bindings(req()) -> [{atom(), any()}].
  237. bindings(Req) ->
  238. Req#http_req.bindings.
  239. -spec header(binary(), req()) -> binary() | undefined.
  240. header(Name, Req) ->
  241. header(Name, Req, undefined).
  242. -spec header(binary(), req(), Default) -> binary() | Default when Default::any().
  243. header(Name, Req, Default) ->
  244. case lists:keyfind(Name, 1, Req#http_req.headers) of
  245. {Name, Value} -> Value;
  246. false -> Default
  247. end.
  248. -spec headers(req()) -> cowboy:http_headers().
  249. headers(Req) ->
  250. Req#http_req.headers.
  251. -spec parse_header(binary(), Req) -> any() when Req::req().
  252. parse_header(Name = <<"content-length">>, Req) ->
  253. parse_header(Name, Req, 0);
  254. parse_header(Name = <<"cookie">>, Req) ->
  255. parse_header(Name, Req, []);
  256. parse_header(Name = <<"transfer-encoding">>, Req) ->
  257. parse_header(Name, Req, [<<"identity">>]);
  258. parse_header(Name, Req) ->
  259. parse_header(Name, Req, undefined).
  260. -spec parse_header(binary(), Req, any()) -> any() when Req::req().
  261. parse_header(Name = <<"accept">>, Req, Default) ->
  262. parse_header(Name, Req, Default, fun (Value) ->
  263. cowboy_http:list(Value, fun cowboy_http:media_range/2) end);
  264. parse_header(Name = <<"accept-charset">>, Req, Default) ->
  265. parse_header(Name, Req, Default, fun (Value) ->
  266. cowboy_http:nonempty_list(Value, fun cowboy_http:conneg/2) end);
  267. parse_header(Name = <<"accept-encoding">>, Req, Default) ->
  268. parse_header(Name, Req, Default, fun (Value) ->
  269. cowboy_http:list(Value, fun cowboy_http:conneg/2) end);
  270. parse_header(Name = <<"accept-language">>, Req, Default) ->
  271. parse_header(Name, Req, Default, fun (Value) ->
  272. cowboy_http:nonempty_list(Value, fun cowboy_http:language_range/2) end);
  273. parse_header(Name = <<"authorization">>, Req, Default) ->
  274. parse_header(Name, Req, Default, fun (Value) ->
  275. cowboy_http:token_ci(Value, fun cowboy_http:authorization/2) end);
  276. parse_header(Name = <<"connection">>, Req, Default) ->
  277. case header(Name, Req) of
  278. undefined -> Default;
  279. Value -> cow_http_hd:parse_connection(Value)
  280. end;
  281. parse_header(Name = <<"content-length">>, Req, Default) ->
  282. case header(Name, Req) of
  283. undefined -> Default;
  284. Value -> cow_http_hd:parse_content_length(Value)
  285. end;
  286. parse_header(Name = <<"content-type">>, Req, Default) ->
  287. parse_header(Name, Req, Default, fun cowboy_http:content_type/1);
  288. parse_header(Name = <<"cookie">>, Req, Default) ->
  289. case header(Name, Req) of
  290. undefined -> Default;
  291. %% Flash player incorrectly sends an empty Cookie header.
  292. <<>> -> Default;
  293. Value -> cow_cookie:parse_cookie(Value)
  294. end;
  295. parse_header(Name = <<"expect">>, Req, Default) ->
  296. parse_header(Name, Req, Default, fun (Value) ->
  297. cowboy_http:nonempty_list(Value, fun cowboy_http:expectation/2) end);
  298. parse_header(Name, Req, Default)
  299. when Name =:= <<"if-match">>;
  300. Name =:= <<"if-none-match">> ->
  301. parse_header(Name, Req, Default, fun cowboy_http:entity_tag_match/1);
  302. parse_header(Name, Req, Default)
  303. when Name =:= <<"if-modified-since">>;
  304. Name =:= <<"if-unmodified-since">> ->
  305. parse_header(Name, Req, Default, fun cowboy_http:http_date/1);
  306. parse_header(Name = <<"range">>, Req, Default) ->
  307. parse_header(Name, Req, Default, fun cowboy_http:range/1);
  308. parse_header(Name, Req, Default)
  309. when Name =:= <<"sec-websocket-protocol">>;
  310. Name =:= <<"x-forwarded-for">> ->
  311. parse_header(Name, Req, Default, fun (Value) ->
  312. cowboy_http:nonempty_list(Value, fun cowboy_http:token/2) end);
  313. parse_header(Name = <<"transfer-encoding">>, Req, Default) ->
  314. case header(Name, Req) of
  315. undefined -> Default;
  316. Value -> cow_http_hd:parse_transfer_encoding(Value)
  317. end;
  318. %% @todo Product version.
  319. parse_header(Name = <<"upgrade">>, Req, Default) ->
  320. parse_header(Name, Req, Default, fun (Value) ->
  321. cowboy_http:nonempty_list(Value, fun cowboy_http:token_ci/2) end);
  322. parse_header(Name = <<"sec-websocket-extensions">>, Req, Default) ->
  323. parse_header(Name, Req, Default, fun cowboy_http:parameterized_tokens/1).
  324. %% @todo Remove this function when everything moved to cowlib.
  325. parse_header(Name, Req, Default, ParseFun) ->
  326. case header(Name, Req) of
  327. undefined ->
  328. Default;
  329. Value ->
  330. case ParseFun(Value) of
  331. {error, badarg} ->
  332. error(badarg);
  333. ParsedValue ->
  334. ParsedValue
  335. end
  336. end.
  337. -spec parse_cookies(req()) -> [{binary(), binary()}].
  338. parse_cookies(Req) ->
  339. parse_header(<<"cookie">>, Req).
  340. -spec match_cookies(cowboy:fields(), req()) -> map().
  341. match_cookies(Fields, Req) ->
  342. filter(Fields, kvlist_to_map(Fields, parse_cookies(Req))).
  343. -spec meta(atom(), req()) -> any() | undefined.
  344. meta(Name, Req) ->
  345. meta(Name, Req, undefined).
  346. -spec meta(atom(), req(), any()) -> any().
  347. meta(Name, Req, Default) ->
  348. case lists:keyfind(Name, 1, Req#http_req.meta) of
  349. {Name, Value} -> Value;
  350. false -> Default
  351. end.
  352. -spec set_meta(atom(), any(), Req) -> Req when Req::req().
  353. set_meta(Name, Value, Req=#http_req{meta=Meta}) ->
  354. Req#http_req{meta=lists:keystore(Name, 1, Meta, {Name, Value})}.
  355. %% Request Body API.
  356. -spec has_body(req()) -> boolean().
  357. has_body(Req) ->
  358. case lists:keyfind(<<"content-length">>, 1, Req#http_req.headers) of
  359. {_, <<"0">>} ->
  360. false;
  361. {_, _} ->
  362. true;
  363. _ ->
  364. lists:keymember(<<"transfer-encoding">>, 1, Req#http_req.headers)
  365. end.
  366. %% The length may not be known if Transfer-Encoding is not identity,
  367. %% and the body hasn't been read at the time of the call.
  368. -spec body_length(req()) -> undefined | non_neg_integer().
  369. body_length(Req) ->
  370. case parse_header(<<"transfer-encoding">>, Req) of
  371. [<<"identity">>] ->
  372. parse_header(<<"content-length">>, Req);
  373. _ ->
  374. undefined
  375. end.
  376. -spec body(Req) -> {ok, binary(), Req} | {more, binary(), Req} when Req::req().
  377. body(Req) ->
  378. body(Req, []).
  379. -spec body(Req, body_opts()) -> {ok, binary(), Req} | {more, binary(), Req} when Req::req().
  380. body(Req=#http_req{body_state=waiting}, Opts) ->
  381. %% Send a 100 continue if needed (enabled by default).
  382. case lists:keyfind(continue, 1, Opts) of
  383. {_, false} ->
  384. ok;
  385. _ ->
  386. ExpectHeader = parse_header(<<"expect">>, Req),
  387. ok = case ExpectHeader of
  388. [<<"100-continue">>] -> continue(Req);
  389. _ -> ok
  390. end
  391. end,
  392. %% Initialize body streaming state.
  393. CFun = case lists:keyfind(content_decode, 1, Opts) of
  394. false ->
  395. fun cowboy_http:ce_identity/1;
  396. {_, CFun0} ->
  397. CFun0
  398. end,
  399. case lists:keyfind(transfer_decode, 1, Opts) of
  400. false ->
  401. case parse_header(<<"transfer-encoding">>, Req) of
  402. [<<"chunked">>] ->
  403. body(Req#http_req{body_state={stream, 0,
  404. fun cow_http_te:stream_chunked/2, {0, 0}, CFun}}, Opts);
  405. [<<"identity">>] ->
  406. case body_length(Req) of
  407. 0 ->
  408. {ok, <<>>, Req#http_req{body_state=done}};
  409. Len ->
  410. body(Req#http_req{body_state={stream, Len,
  411. fun cow_http_te:stream_identity/2, {0, Len},
  412. CFun}}, Opts)
  413. end
  414. end;
  415. {_, TFun, TState} ->
  416. body(Req#http_req{body_state={stream, 0,
  417. TFun, TState, CFun}}, Opts)
  418. end;
  419. body(Req=#http_req{body_state=done}, _) ->
  420. {ok, <<>>, Req};
  421. body(Req, Opts) ->
  422. ChunkLen = case lists:keyfind(length, 1, Opts) of
  423. false -> 8000000;
  424. {_, ChunkLen0} -> ChunkLen0
  425. end,
  426. ReadLen = case lists:keyfind(read_length, 1, Opts) of
  427. false -> 1000000;
  428. {_, ReadLen0} -> ReadLen0
  429. end,
  430. ReadTimeout = case lists:keyfind(read_timeout, 1, Opts) of
  431. false -> 15000;
  432. {_, ReadTimeout0} -> ReadTimeout0
  433. end,
  434. body_loop(Req, ReadTimeout, ReadLen, ChunkLen, <<>>).
  435. body_loop(Req=#http_req{buffer=Buffer, body_state={stream, Length, _, _, _}},
  436. ReadTimeout, ReadLength, ChunkLength, Acc) ->
  437. {Tag, Res, Req2} = case Buffer of
  438. <<>> ->
  439. body_recv(Req, ReadTimeout, min(Length, ReadLength));
  440. _ ->
  441. body_decode(Req, ReadTimeout)
  442. end,
  443. case {Tag, Res} of
  444. {ok, Data} ->
  445. {ok, << Acc/binary, Data/binary >>, Req2};
  446. {more, Data} ->
  447. Acc2 = << Acc/binary, Data/binary >>,
  448. case byte_size(Acc2) >= ChunkLength of
  449. true -> {more, Acc2, Req2};
  450. false -> body_loop(Req2, ReadTimeout, ReadLength, ChunkLength, Acc2)
  451. end
  452. end.
  453. body_recv(Req=#http_req{transport=Transport, socket=Socket, buffer=Buffer},
  454. ReadTimeout, ReadLength) ->
  455. {ok, Data} = Transport:recv(Socket, ReadLength, ReadTimeout),
  456. body_decode(Req#http_req{buffer= << Buffer/binary, Data/binary >>}, ReadTimeout).
  457. %% Two decodings happen. First a decoding function is applied to the
  458. %% transferred data, and then another is applied to the actual content.
  459. %%
  460. %% Transfer encoding is generally used for chunked bodies. The decoding
  461. %% function uses a state to keep track of how much it has read, which is
  462. %% also initialized through this function.
  463. %%
  464. %% Content encoding is generally used for compression.
  465. %%
  466. %% @todo Handle chunked after-the-facts headers.
  467. %% @todo Depending on the length returned we might want to 0 or +5 it.
  468. body_decode(Req=#http_req{buffer=Data, body_state={stream, _,
  469. TDecode, TState, CDecode}}, ReadTimeout) ->
  470. case TDecode(Data, TState) of
  471. more ->
  472. body_recv(Req#http_req{body_state={stream, 0,
  473. TDecode, TState, CDecode}}, ReadTimeout, 0);
  474. {more, Data2, TState2} ->
  475. {more, CDecode(Data2), Req#http_req{body_state={stream, 0,
  476. TDecode, TState2, CDecode}, buffer= <<>>}};
  477. {more, Data2, Length, TState2} when is_integer(Length) ->
  478. {more, CDecode(Data2), Req#http_req{body_state={stream, Length,
  479. TDecode, TState2, CDecode}, buffer= <<>>}};
  480. {more, Data2, Rest, TState2} ->
  481. {more, CDecode(Data2), Req#http_req{body_state={stream, 0,
  482. TDecode, TState2, CDecode}, buffer=Rest}};
  483. {done, TotalLength, Rest} ->
  484. {ok, <<>>, body_decode_end(Req, TotalLength, Rest)};
  485. {done, Data2, TotalLength, Rest} ->
  486. {ok, CDecode(Data2), body_decode_end(Req, TotalLength, Rest)}
  487. end.
  488. body_decode_end(Req=#http_req{headers=Headers}, TotalLength, Rest) ->
  489. Headers2 = lists:keystore(<<"content-length">>, 1, Headers,
  490. {<<"content-length">>, integer_to_binary(TotalLength)}),
  491. %% At this point we just assume TEs were all decoded.
  492. Headers3 = lists:keydelete(<<"transfer-encoding">>, 1, Headers2),
  493. Req#http_req{buffer=Rest, body_state=done, headers=Headers3}.
  494. -spec body_qs(Req) -> {ok, [{binary(), binary() | true}], Req}
  495. | {badlength, Req} when Req::req().
  496. body_qs(Req) ->
  497. body_qs(Req, [
  498. {length, 64000},
  499. {read_length, 64000},
  500. {read_timeout, 5000}]).
  501. -spec body_qs(Req, body_opts()) -> {ok, [{binary(), binary() | true}], Req}
  502. | {badlength, Req} when Req::req().
  503. body_qs(Req, Opts) ->
  504. case body(Req, Opts) of
  505. {ok, Body, Req2} ->
  506. {ok, cow_qs:parse_qs(Body), Req2};
  507. {more, _, Req2} ->
  508. {badlength, Req2}
  509. end.
  510. %% Multipart API.
  511. -spec part(Req)
  512. -> {ok, cow_multipart:headers(), Req} | {done, Req}
  513. when Req::req().
  514. part(Req) ->
  515. part(Req, [
  516. {length, 64000},
  517. {read_length, 64000},
  518. {read_timeout, 5000}]).
  519. -spec part(Req, body_opts())
  520. -> {ok, cow_multipart:headers(), Req} | {done, Req}
  521. when Req::req().
  522. part(Req=#http_req{multipart=undefined}, Opts) ->
  523. part(init_multipart(Req), Opts);
  524. part(Req, Opts) ->
  525. {Data, Req2} = stream_multipart(Req, Opts),
  526. part(Data, Opts, Req2).
  527. part(Buffer, Opts, Req=#http_req{multipart={Boundary, _}}) ->
  528. case cow_multipart:parse_headers(Buffer, Boundary) of
  529. more ->
  530. {Data, Req2} = stream_multipart(Req, Opts),
  531. part(<< Buffer/binary, Data/binary >>, Opts, Req2);
  532. {more, Buffer2} ->
  533. {Data, Req2} = stream_multipart(Req, Opts),
  534. part(<< Buffer2/binary, Data/binary >>, Opts, Req2);
  535. {ok, Headers, Rest} ->
  536. {ok, Headers, Req#http_req{multipart={Boundary, Rest}}};
  537. %% Ignore epilogue.
  538. {done, _} ->
  539. {done, Req#http_req{multipart=undefined}}
  540. end.
  541. -spec part_body(Req)
  542. -> {ok, binary(), Req} | {more, binary(), Req}
  543. when Req::req().
  544. part_body(Req) ->
  545. part_body(Req, []).
  546. -spec part_body(Req, body_opts())
  547. -> {ok, binary(), Req} | {more, binary(), Req}
  548. when Req::req().
  549. part_body(Req=#http_req{multipart=undefined}, Opts) ->
  550. part_body(init_multipart(Req), Opts);
  551. part_body(Req, Opts) ->
  552. part_body(<<>>, Opts, Req, <<>>).
  553. part_body(Buffer, Opts, Req=#http_req{multipart={Boundary, _}}, Acc) ->
  554. ChunkLen = case lists:keyfind(length, 1, Opts) of
  555. false -> 8000000;
  556. {_, ChunkLen0} -> ChunkLen0
  557. end,
  558. case byte_size(Acc) > ChunkLen of
  559. true ->
  560. {more, Acc, Req#http_req{multipart={Boundary, Buffer}}};
  561. false ->
  562. {Data, Req2} = stream_multipart(Req, Opts),
  563. case cow_multipart:parse_body(<< Buffer/binary, Data/binary >>, Boundary) of
  564. {ok, Body} ->
  565. part_body(<<>>, Opts, Req2, << Acc/binary, Body/binary >>);
  566. {ok, Body, Rest} ->
  567. part_body(Rest, Opts, Req2, << Acc/binary, Body/binary >>);
  568. done ->
  569. {ok, Acc, Req2};
  570. {done, Body} ->
  571. {ok, << Acc/binary, Body/binary >>, Req2};
  572. {done, Body, Rest} ->
  573. {ok, << Acc/binary, Body/binary >>,
  574. Req2#http_req{multipart={Boundary, Rest}}}
  575. end
  576. end.
  577. init_multipart(Req) ->
  578. {<<"multipart">>, _, Params} = parse_header(<<"content-type">>, Req),
  579. {_, Boundary} = lists:keyfind(<<"boundary">>, 1, Params),
  580. Req#http_req{multipart={Boundary, <<>>}}.
  581. stream_multipart(Req=#http_req{body_state=BodyState, multipart={_, <<>>}}, Opts) ->
  582. true = BodyState =/= done,
  583. {_, Data, Req2} = body(Req, Opts),
  584. {Data, Req2};
  585. stream_multipart(Req=#http_req{multipart={Boundary, Buffer}}, _) ->
  586. {Buffer, Req#http_req{multipart={Boundary, <<>>}}}.
  587. %% Response API.
  588. %% The cookie name cannot contain any of the following characters:
  589. %% =,;\s\t\r\n\013\014
  590. %%
  591. %% The cookie value cannot contain any of the following characters:
  592. %% ,; \t\r\n\013\014
  593. -spec set_resp_cookie(iodata(), iodata(), cookie_opts(), Req)
  594. -> Req when Req::req().
  595. set_resp_cookie(Name, Value, Opts, Req) ->
  596. Cookie = cow_cookie:setcookie(Name, Value, Opts),
  597. set_resp_header(<<"set-cookie">>, Cookie, Req).
  598. -spec set_resp_header(binary(), iodata(), Req)
  599. -> Req when Req::req().
  600. set_resp_header(Name, Value, Req=#http_req{resp_headers=RespHeaders}) ->
  601. Req#http_req{resp_headers=[{Name, Value}|RespHeaders]}.
  602. -spec set_resp_body(iodata(), Req) -> Req when Req::req().
  603. set_resp_body(Body, Req) ->
  604. Req#http_req{resp_body=Body}.
  605. -spec set_resp_body_fun(resp_body_fun(), Req) -> Req when Req::req().
  606. set_resp_body_fun(StreamFun, Req) when is_function(StreamFun) ->
  607. Req#http_req{resp_body=StreamFun}.
  608. %% If the body function crashes while writing the response body or writes
  609. %% fewer bytes than declared the behaviour is undefined.
  610. -spec set_resp_body_fun(non_neg_integer(), resp_body_fun(), Req)
  611. -> Req when Req::req();
  612. (chunked, resp_chunked_fun(), Req)
  613. -> Req when Req::req().
  614. set_resp_body_fun(StreamLen, StreamFun, Req)
  615. when is_integer(StreamLen), is_function(StreamFun) ->
  616. Req#http_req{resp_body={StreamLen, StreamFun}};
  617. set_resp_body_fun(chunked, StreamFun, Req)
  618. when is_function(StreamFun) ->
  619. Req#http_req{resp_body={chunked, StreamFun}}.
  620. -spec has_resp_header(binary(), req()) -> boolean().
  621. has_resp_header(Name, #http_req{resp_headers=RespHeaders}) ->
  622. lists:keymember(Name, 1, RespHeaders).
  623. -spec has_resp_body(req()) -> boolean().
  624. has_resp_body(#http_req{resp_body=RespBody}) when is_function(RespBody) ->
  625. true;
  626. has_resp_body(#http_req{resp_body={chunked, _}}) ->
  627. true;
  628. has_resp_body(#http_req{resp_body={Length, _}}) ->
  629. Length > 0;
  630. has_resp_body(#http_req{resp_body=RespBody}) ->
  631. iolist_size(RespBody) > 0.
  632. -spec delete_resp_header(binary(), Req)
  633. -> Req when Req::req().
  634. delete_resp_header(Name, Req=#http_req{resp_headers=RespHeaders}) ->
  635. RespHeaders2 = lists:keydelete(Name, 1, RespHeaders),
  636. Req#http_req{resp_headers=RespHeaders2}.
  637. -spec reply(cowboy:http_status(), Req) -> Req when Req::req().
  638. reply(Status, Req=#http_req{resp_body=Body}) ->
  639. reply(Status, [], Body, Req).
  640. -spec reply(cowboy:http_status(), cowboy:http_headers(), Req)
  641. -> Req when Req::req().
  642. reply(Status, Headers, Req=#http_req{resp_body=Body}) ->
  643. reply(Status, Headers, Body, Req).
  644. -spec reply(cowboy:http_status(), cowboy:http_headers(),
  645. iodata() | {non_neg_integer() | resp_body_fun()}, Req)
  646. -> Req when Req::req().
  647. reply(Status, Headers, Body, Req=#http_req{
  648. socket=Socket, transport=Transport,
  649. version=Version, connection=Connection,
  650. method=Method, resp_compress=Compress,
  651. resp_state=RespState, resp_headers=RespHeaders})
  652. when RespState =:= waiting; RespState =:= waiting_stream ->
  653. HTTP11Headers = if
  654. Transport =/= cowboy_spdy, Version =:= 'HTTP/1.1' ->
  655. [{<<"connection">>, atom_to_connection(Connection)}];
  656. true ->
  657. []
  658. end,
  659. Req3 = case Body of
  660. BodyFun when is_function(BodyFun) ->
  661. %% We stream the response body until we close the connection.
  662. RespConn = close,
  663. {RespType, Req2} = if
  664. Transport =:= cowboy_spdy ->
  665. response(Status, Headers, RespHeaders, [
  666. {<<"date">>, cowboy_clock:rfc1123()},
  667. {<<"server">>, <<"Cowboy">>}
  668. ], stream, Req);
  669. true ->
  670. response(Status, Headers, RespHeaders, [
  671. {<<"connection">>, <<"close">>},
  672. {<<"date">>, cowboy_clock:rfc1123()},
  673. {<<"server">>, <<"Cowboy">>},
  674. {<<"transfer-encoding">>, <<"identity">>}
  675. ], <<>>, Req)
  676. end,
  677. if RespType =/= hook, Method =/= <<"HEAD">> ->
  678. BodyFun(Socket, Transport);
  679. true -> ok
  680. end,
  681. Req2#http_req{connection=RespConn};
  682. {chunked, BodyFun} ->
  683. %% We stream the response body in chunks.
  684. {RespType, Req2} = chunked_response(Status, Headers, Req),
  685. if RespType =/= hook, Method =/= <<"HEAD">> ->
  686. ChunkFun = fun(IoData) -> chunk(IoData, Req2) end,
  687. BodyFun(ChunkFun),
  688. %% Send the last chunk if chunked encoding was used.
  689. if
  690. Version =:= 'HTTP/1.0'; RespState =:= waiting_stream ->
  691. Req2;
  692. true ->
  693. last_chunk(Req2)
  694. end;
  695. true -> Req2
  696. end;
  697. {ContentLength, BodyFun} ->
  698. %% We stream the response body for ContentLength bytes.
  699. RespConn = response_connection(Headers, Connection),
  700. {RespType, Req2} = response(Status, Headers, RespHeaders, [
  701. {<<"content-length">>, integer_to_list(ContentLength)},
  702. {<<"date">>, cowboy_clock:rfc1123()},
  703. {<<"server">>, <<"Cowboy">>}
  704. |HTTP11Headers], stream, Req),
  705. if RespType =/= hook, Method =/= <<"HEAD">> ->
  706. BodyFun(Socket, Transport);
  707. true -> ok
  708. end,
  709. Req2#http_req{connection=RespConn};
  710. _ when Compress ->
  711. RespConn = response_connection(Headers, Connection),
  712. Req2 = reply_may_compress(Status, Headers, Body, Req,
  713. RespHeaders, HTTP11Headers, Method),
  714. Req2#http_req{connection=RespConn};
  715. _ ->
  716. RespConn = response_connection(Headers, Connection),
  717. Req2 = reply_no_compress(Status, Headers, Body, Req,
  718. RespHeaders, HTTP11Headers, Method, iolist_size(Body)),
  719. Req2#http_req{connection=RespConn}
  720. end,
  721. Req3#http_req{resp_state=done, resp_headers=[], resp_body= <<>>}.
  722. reply_may_compress(Status, Headers, Body, Req,
  723. RespHeaders, HTTP11Headers, Method) ->
  724. BodySize = iolist_size(Body),
  725. try parse_header(<<"accept-encoding">>, Req) of
  726. Encodings ->
  727. CanGzip = (BodySize > 300)
  728. andalso (false =:= lists:keyfind(<<"content-encoding">>,
  729. 1, Headers))
  730. andalso (false =:= lists:keyfind(<<"content-encoding">>,
  731. 1, RespHeaders))
  732. andalso (false =:= lists:keyfind(<<"transfer-encoding">>,
  733. 1, Headers))
  734. andalso (false =:= lists:keyfind(<<"transfer-encoding">>,
  735. 1, RespHeaders))
  736. andalso (Encodings =/= undefined)
  737. andalso (false =/= lists:keyfind(<<"gzip">>, 1, Encodings)),
  738. case CanGzip of
  739. true ->
  740. GzBody = zlib:gzip(Body),
  741. {_, Req2} = response(Status, Headers, RespHeaders, [
  742. {<<"content-length">>, integer_to_list(byte_size(GzBody))},
  743. {<<"content-encoding">>, <<"gzip">>},
  744. {<<"date">>, cowboy_clock:rfc1123()},
  745. {<<"server">>, <<"Cowboy">>}
  746. |HTTP11Headers],
  747. case Method of <<"HEAD">> -> <<>>; _ -> GzBody end,
  748. Req),
  749. Req2;
  750. false ->
  751. reply_no_compress(Status, Headers, Body, Req,
  752. RespHeaders, HTTP11Headers, Method, BodySize)
  753. end
  754. catch _:_ ->
  755. reply_no_compress(Status, Headers, Body, Req,
  756. RespHeaders, HTTP11Headers, Method, BodySize)
  757. end.
  758. reply_no_compress(Status, Headers, Body, Req,
  759. RespHeaders, HTTP11Headers, Method, BodySize) ->
  760. {_, Req2} = response(Status, Headers, RespHeaders, [
  761. {<<"content-length">>, integer_to_list(BodySize)},
  762. {<<"date">>, cowboy_clock:rfc1123()},
  763. {<<"server">>, <<"Cowboy">>}
  764. |HTTP11Headers],
  765. case Method of <<"HEAD">> -> <<>>; _ -> Body end,
  766. Req),
  767. Req2.
  768. -spec chunked_reply(cowboy:http_status(), Req) -> Req when Req::req().
  769. chunked_reply(Status, Req) ->
  770. chunked_reply(Status, [], Req).
  771. -spec chunked_reply(cowboy:http_status(), cowboy:http_headers(), Req)
  772. -> Req when Req::req().
  773. chunked_reply(Status, Headers, Req) ->
  774. {_, Req2} = chunked_response(Status, Headers, Req),
  775. Req2.
  776. -spec chunk(iodata(), req()) -> ok.
  777. chunk(_Data, #http_req{method= <<"HEAD">>}) ->
  778. ok;
  779. chunk(Data, #http_req{socket=Socket, transport=cowboy_spdy,
  780. resp_state=chunks}) ->
  781. cowboy_spdy:stream_data(Socket, Data);
  782. chunk(Data, #http_req{socket=Socket, transport=Transport,
  783. resp_state=stream}) ->
  784. ok = Transport:send(Socket, Data);
  785. chunk(Data, #http_req{socket=Socket, transport=Transport,
  786. resp_state=chunks}) ->
  787. ok = Transport:send(Socket, [integer_to_list(iolist_size(Data), 16),
  788. <<"\r\n">>, Data, <<"\r\n">>]).
  789. %% If ever made public, need to send nothing if HEAD.
  790. -spec last_chunk(Req) -> Req when Req::req().
  791. last_chunk(Req=#http_req{socket=Socket, transport=cowboy_spdy}) ->
  792. _ = cowboy_spdy:stream_close(Socket),
  793. Req#http_req{resp_state=done};
  794. last_chunk(Req=#http_req{socket=Socket, transport=Transport}) ->
  795. _ = Transport:send(Socket, <<"0\r\n\r\n">>),
  796. Req#http_req{resp_state=done}.
  797. -spec upgrade_reply(cowboy:http_status(), cowboy:http_headers(), Req)
  798. -> Req when Req::req().
  799. upgrade_reply(Status, Headers, Req=#http_req{transport=Transport,
  800. resp_state=waiting, resp_headers=RespHeaders})
  801. when Transport =/= cowboy_spdy ->
  802. {_, Req2} = response(Status, Headers, RespHeaders, [
  803. {<<"connection">>, <<"Upgrade">>}
  804. ], <<>>, Req),
  805. Req2#http_req{resp_state=done, resp_headers=[], resp_body= <<>>}.
  806. -spec continue(req()) -> ok.
  807. continue(#http_req{socket=Socket, transport=Transport,
  808. version=Version}) ->
  809. HTTPVer = atom_to_binary(Version, latin1),
  810. ok = Transport:send(Socket,
  811. << HTTPVer/binary, " ", (status(100))/binary, "\r\n\r\n" >>).
  812. %% Meant to be used internally for sending errors after crashes.
  813. -spec maybe_reply([{module(), atom(), arity() | [term()], _}], req()) -> ok.
  814. maybe_reply(Stacktrace, Req) ->
  815. receive
  816. {cowboy_req, resp_sent} -> ok
  817. after 0 ->
  818. _ = do_maybe_reply(Stacktrace, Req),
  819. ok
  820. end.
  821. do_maybe_reply([{cow_http_hd, _, _, _}|_], Req) ->
  822. cowboy_req:reply(400, Req);
  823. do_maybe_reply(_, Req) ->
  824. cowboy_req:reply(500, Req).
  825. -spec ensure_response(req(), cowboy:http_status()) -> ok.
  826. %% The response has already been fully sent to the client.
  827. ensure_response(#http_req{resp_state=done}, _) ->
  828. ok;
  829. %% No response has been sent but everything apparently went fine.
  830. %% Reply with the status code found in the second argument.
  831. ensure_response(Req=#http_req{resp_state=RespState}, Status)
  832. when RespState =:= waiting; RespState =:= waiting_stream ->
  833. _ = reply(Status, [], [], Req),
  834. ok;
  835. %% Terminate the chunked body for HTTP/1.1 only.
  836. ensure_response(#http_req{method= <<"HEAD">>}, _) ->
  837. ok;
  838. ensure_response(Req=#http_req{resp_state=chunks}, _) ->
  839. _ = last_chunk(Req),
  840. ok;
  841. ensure_response(#http_req{}, _) ->
  842. ok.
  843. %% Private setter/getter API.
  844. -spec append_buffer(binary(), Req) -> Req when Req::req().
  845. append_buffer(Suffix, Req=#http_req{buffer=Buffer}) ->
  846. Req#http_req{buffer= << Buffer/binary, Suffix/binary >>}.
  847. -spec get(atom(), req()) -> any(); ([atom()], req()) -> any().
  848. get(List, Req) when is_list(List) ->
  849. [g(Atom, Req) || Atom <- List];
  850. get(Atom, Req) when is_atom(Atom) ->
  851. g(Atom, Req).
  852. g(bindings, #http_req{bindings=Ret}) -> Ret;
  853. g(body_state, #http_req{body_state=Ret}) -> Ret;
  854. g(buffer, #http_req{buffer=Ret}) -> Ret;
  855. g(connection, #http_req{connection=Ret}) -> Ret;
  856. g(headers, #http_req{headers=Ret}) -> Ret;
  857. g(host, #http_req{host=Ret}) -> Ret;
  858. g(host_info, #http_req{host_info=Ret}) -> Ret;
  859. g(meta, #http_req{meta=Ret}) -> Ret;
  860. g(method, #http_req{method=Ret}) -> Ret;
  861. g(multipart, #http_req{multipart=Ret}) -> Ret;
  862. g(onresponse, #http_req{onresponse=Ret}) -> Ret;
  863. g(path, #http_req{path=Ret}) -> Ret;
  864. g(path_info, #http_req{path_info=Ret}) -> Ret;
  865. g(peer, #http_req{peer=Ret}) -> Ret;
  866. g(pid, #http_req{pid=Ret}) -> Ret;
  867. g(port, #http_req{port=Ret}) -> Ret;
  868. g(qs, #http_req{qs=Ret}) -> Ret;
  869. g(resp_body, #http_req{resp_body=Ret}) -> Ret;
  870. g(resp_compress, #http_req{resp_compress=Ret}) -> Ret;
  871. g(resp_headers, #http_req{resp_headers=Ret}) -> Ret;
  872. g(resp_state, #http_req{resp_state=Ret}) -> Ret;
  873. g(socket, #http_req{socket=Ret}) -> Ret;
  874. g(transport, #http_req{transport=Ret}) -> Ret;
  875. g(version, #http_req{version=Ret}) -> Ret.
  876. -spec set([{atom(), any()}], Req) -> Req when Req::req().
  877. set([], Req) -> Req;
  878. set([{bindings, Val}|Tail], Req) -> set(Tail, Req#http_req{bindings=Val});
  879. set([{body_state, Val}|Tail], Req) -> set(Tail, Req#http_req{body_state=Val});
  880. set([{buffer, Val}|Tail], Req) -> set(Tail, Req#http_req{buffer=Val});
  881. set([{connection, Val}|Tail], Req) -> set(Tail, Req#http_req{connection=Val});
  882. set([{headers, Val}|Tail], Req) -> set(Tail, Req#http_req{headers=Val});
  883. set([{host, Val}|Tail], Req) -> set(Tail, Req#http_req{host=Val});
  884. set([{host_info, Val}|Tail], Req) -> set(Tail, Req#http_req{host_info=Val});
  885. set([{meta, Val}|Tail], Req) -> set(Tail, Req#http_req{meta=Val});
  886. set([{method, Val}|Tail], Req) -> set(Tail, Req#http_req{method=Val});
  887. set([{multipart, Val}|Tail], Req) -> set(Tail, Req#http_req{multipart=Val});
  888. set([{onresponse, Val}|Tail], Req) -> set(Tail, Req#http_req{onresponse=Val});
  889. set([{path, Val}|Tail], Req) -> set(Tail, Req#http_req{path=Val});
  890. set([{path_info, Val}|Tail], Req) -> set(Tail, Req#http_req{path_info=Val});
  891. set([{peer, Val}|Tail], Req) -> set(Tail, Req#http_req{peer=Val});
  892. set([{pid, Val}|Tail], Req) -> set(Tail, Req#http_req{pid=Val});
  893. set([{port, Val}|Tail], Req) -> set(Tail, Req#http_req{port=Val});
  894. set([{qs, Val}|Tail], Req) -> set(Tail, Req#http_req{qs=Val});
  895. set([{resp_body, Val}|Tail], Req) -> set(Tail, Req#http_req{resp_body=Val});
  896. set([{resp_headers, Val}|Tail], Req) -> set(Tail, Req#http_req{resp_headers=Val});
  897. set([{resp_state, Val}|Tail], Req) -> set(Tail, Req#http_req{resp_state=Val});
  898. set([{socket, Val}|Tail], Req) -> set(Tail, Req#http_req{socket=Val});
  899. set([{transport, Val}|Tail], Req) -> set(Tail, Req#http_req{transport=Val});
  900. set([{version, Val}|Tail], Req) -> set(Tail, Req#http_req{version=Val}).
  901. -spec set_bindings(cowboy_router:tokens(), cowboy_router:tokens(),
  902. cowboy_router:bindings(), Req) -> Req when Req::req().
  903. set_bindings(HostInfo, PathInfo, Bindings, Req) ->
  904. Req#http_req{host_info=HostInfo, path_info=PathInfo,
  905. bindings=Bindings}.
  906. -spec lock(Req) -> Req when Req::req().
  907. lock(Req) ->
  908. Req#http_req{resp_state=locked}.
  909. -spec to_list(req()) -> [{atom(), any()}].
  910. to_list(Req) ->
  911. lists:zip(record_info(fields, http_req), tl(tuple_to_list(Req))).
  912. %% Internal.
  913. -spec chunked_response(cowboy:http_status(), cowboy:http_headers(), Req) ->
  914. {normal | hook, Req} when Req::req().
  915. chunked_response(Status, Headers, Req=#http_req{
  916. transport=cowboy_spdy, resp_state=waiting,
  917. resp_headers=RespHeaders}) ->
  918. {RespType, Req2} = response(Status, Headers, RespHeaders, [
  919. {<<"date">>, cowboy_clock:rfc1123()},
  920. {<<"server">>, <<"Cowboy">>}
  921. ], stream, Req),
  922. {RespType, Req2#http_req{resp_state=chunks,
  923. resp_headers=[], resp_body= <<>>}};
  924. chunked_response(Status, Headers, Req=#http_req{
  925. version=Version, connection=Connection,
  926. resp_state=RespState, resp_headers=RespHeaders})
  927. when RespState =:= waiting; RespState =:= waiting_stream ->
  928. RespConn = response_connection(Headers, Connection),
  929. HTTP11Headers = if
  930. Version =:= 'HTTP/1.0' -> [];
  931. true ->
  932. MaybeTE = if
  933. RespState =:= waiting_stream -> [];
  934. true -> [{<<"transfer-encoding">>, <<"chunked">>}]
  935. end,
  936. [{<<"connection">>, atom_to_connection(Connection)}|MaybeTE]
  937. end,
  938. RespState2 = if
  939. Version =:= 'HTTP/1.1', RespState =:= 'waiting' -> chunks;
  940. true -> stream
  941. end,
  942. {RespType, Req2} = response(Status, Headers, RespHeaders, [
  943. {<<"date">>, cowboy_clock:rfc1123()},
  944. {<<"server">>, <<"Cowboy">>}
  945. |HTTP11Headers], <<>>, Req),
  946. {RespType, Req2#http_req{connection=RespConn, resp_state=RespState2,
  947. resp_headers=[], resp_body= <<>>}}.
  948. -spec response(cowboy:http_status(), cowboy:http_headers(),
  949. cowboy:http_headers(), cowboy:http_headers(), stream | iodata(), Req)
  950. -> {normal | hook, Req} when Req::req().
  951. response(Status, Headers, RespHeaders, DefaultHeaders, Body, Req=#http_req{
  952. socket=Socket, transport=Transport, version=Version,
  953. pid=ReqPid, onresponse=OnResponse}) ->
  954. FullHeaders = case OnResponse of
  955. already_called -> Headers;
  956. _ -> response_merge_headers(Headers, RespHeaders, DefaultHeaders)
  957. end,
  958. Body2 = case Body of stream -> <<>>; _ -> Body end,
  959. {Status2, FullHeaders2, Req2} = case OnResponse of
  960. already_called -> {Status, FullHeaders, Req};
  961. undefined -> {Status, FullHeaders, Req};
  962. OnResponse ->
  963. case OnResponse(Status, FullHeaders, Body2,
  964. %% Don't call 'onresponse' from the hook itself.
  965. Req#http_req{resp_headers=[], resp_body= <<>>,
  966. onresponse=already_called}) of
  967. StHdReq = {_, _, _} ->
  968. StHdReq;
  969. Req1 ->
  970. {Status, FullHeaders, Req1}
  971. end
  972. end,
  973. ReplyType = case Req2#http_req.resp_state of
  974. waiting when Transport =:= cowboy_spdy, Body =:= stream ->
  975. cowboy_spdy:stream_reply(Socket, status(Status2), FullHeaders2),
  976. ReqPid ! {?MODULE, resp_sent},
  977. normal;
  978. waiting when Transport =:= cowboy_spdy ->
  979. cowboy_spdy:reply(Socket, status(Status2), FullHeaders2, Body),
  980. ReqPid ! {?MODULE, resp_sent},
  981. normal;
  982. RespState when RespState =:= waiting; RespState =:= waiting_stream ->
  983. HTTPVer = atom_to_binary(Version, latin1),
  984. StatusLine = << HTTPVer/binary, " ",
  985. (status(Status2))/binary, "\r\n" >>,
  986. HeaderLines = [[Key, <<": ">>, Value, <<"\r\n">>]
  987. || {Key, Value} <- FullHeaders2],
  988. ok = Transport:send(Socket, [StatusLine, HeaderLines, <<"\r\n">>, Body2]),
  989. ReqPid ! {?MODULE, resp_sent},
  990. normal;
  991. _ ->
  992. hook
  993. end,
  994. {ReplyType, Req2}.
  995. -spec response_connection(cowboy:http_headers(), keepalive | close)
  996. -> keepalive | close.
  997. response_connection([], Connection) ->
  998. Connection;
  999. response_connection([{Name, Value}|Tail], Connection) ->
  1000. case Name of
  1001. <<"connection">> ->
  1002. Tokens = cow_http_hd:parse_connection(Value),
  1003. connection_to_atom(Tokens);
  1004. _ ->
  1005. response_connection(Tail, Connection)
  1006. end.
  1007. -spec response_merge_headers(cowboy:http_headers(), cowboy:http_headers(),
  1008. cowboy:http_headers()) -> cowboy:http_headers().
  1009. response_merge_headers(Headers, RespHeaders, DefaultHeaders) ->
  1010. Headers2 = [{Key, Value} || {Key, Value} <- Headers],
  1011. merge_headers(
  1012. merge_headers(Headers2, RespHeaders),
  1013. DefaultHeaders).
  1014. -spec merge_headers(cowboy:http_headers(), cowboy:http_headers())
  1015. -> cowboy:http_headers().
  1016. %% Merge headers by prepending the tuples in the second list to the
  1017. %% first list. It also handles Set-Cookie properly, which supports
  1018. %% duplicated entries. Notice that, while the RFC2109 does allow more
  1019. %% than one cookie to be set per Set-Cookie header, we are following
  1020. %% the implementation of common web servers and applications which
  1021. %% return many distinct headers per each Set-Cookie entry to avoid
  1022. %% issues with clients/browser which may not support it.
  1023. merge_headers(Headers, []) ->
  1024. Headers;
  1025. merge_headers(Headers, [{<<"set-cookie">>, Value}|Tail]) ->
  1026. merge_headers([{<<"set-cookie">>, Value}|Headers], Tail);
  1027. merge_headers(Headers, [{Name, Value}|Tail]) ->
  1028. Headers2 = case lists:keymember(Name, 1, Headers) of
  1029. true -> Headers;
  1030. false -> [{Name, Value}|Headers]
  1031. end,
  1032. merge_headers(Headers2, Tail).
  1033. -spec atom_to_connection(keepalive) -> <<_:80>>;
  1034. (close) -> <<_:40>>.
  1035. atom_to_connection(keepalive) ->
  1036. <<"keep-alive">>;
  1037. atom_to_connection(close) ->
  1038. <<"close">>.
  1039. %% We don't match on "keep-alive" since it is the default value.
  1040. -spec connection_to_atom([binary()]) -> keepalive | close.
  1041. connection_to_atom([]) ->
  1042. keepalive;
  1043. connection_to_atom([<<"close">>|_]) ->
  1044. close;
  1045. connection_to_atom([_|Tail]) ->
  1046. connection_to_atom(Tail).
  1047. -spec status(cowboy:http_status()) -> binary().
  1048. status(100) -> <<"100 Continue">>;
  1049. status(101) -> <<"101 Switching Protocols">>;
  1050. status(102) -> <<"102 Processing">>;
  1051. status(200) -> <<"200 OK">>;
  1052. status(201) -> <<"201 Created">>;
  1053. status(202) -> <<"202 Accepted">>;
  1054. status(203) -> <<"203 Non-Authoritative Information">>;
  1055. status(204) -> <<"204 No Content">>;
  1056. status(205) -> <<"205 Reset Content">>;
  1057. status(206) -> <<"206 Partial Content">>;
  1058. status(207) -> <<"207 Multi-Status">>;
  1059. status(226) -> <<"226 IM Used">>;
  1060. status(300) -> <<"300 Multiple Choices">>;
  1061. status(301) -> <<"301 Moved Permanently">>;
  1062. status(302) -> <<"302 Found">>;
  1063. status(303) -> <<"303 See Other">>;
  1064. status(304) -> <<"304 Not Modified">>;
  1065. status(305) -> <<"305 Use Proxy">>;
  1066. status(306) -> <<"306 Switch Proxy">>;
  1067. status(307) -> <<"307 Temporary Redirect">>;
  1068. status(400) -> <<"400 Bad Request">>;
  1069. status(401) -> <<"401 Unauthorized">>;
  1070. status(402) -> <<"402 Payment Required">>;
  1071. status(403) -> <<"403 Forbidden">>;
  1072. status(404) -> <<"404 Not Found">>;
  1073. status(405) -> <<"405 Method Not Allowed">>;
  1074. status(406) -> <<"406 Not Acceptable">>;
  1075. status(407) -> <<"407 Proxy Authentication Required">>;
  1076. status(408) -> <<"408 Request Timeout">>;
  1077. status(409) -> <<"409 Conflict">>;
  1078. status(410) -> <<"410 Gone">>;
  1079. status(411) -> <<"411 Length Required">>;
  1080. status(412) -> <<"412 Precondition Failed">>;
  1081. status(413) -> <<"413 Request Entity Too Large">>;
  1082. status(414) -> <<"414 Request-URI Too Long">>;
  1083. status(415) -> <<"415 Unsupported Media Type">>;
  1084. status(416) -> <<"416 Requested Range Not Satisfiable">>;
  1085. status(417) -> <<"417 Expectation Failed">>;
  1086. status(418) -> <<"418 I'm a teapot">>;
  1087. status(422) -> <<"422 Unprocessable Entity">>;
  1088. status(423) -> <<"423 Locked">>;
  1089. status(424) -> <<"424 Failed Dependency">>;
  1090. status(425) -> <<"425 Unordered Collection">>;
  1091. status(426) -> <<"426 Upgrade Required">>;
  1092. status(428) -> <<"428 Precondition Required">>;
  1093. status(429) -> <<"429 Too Many Requests">>;
  1094. status(431) -> <<"431 Request Header Fields Too Large">>;
  1095. status(500) -> <<"500 Internal Server Error">>;
  1096. status(501) -> <<"501 Not Implemented">>;
  1097. status(502) -> <<"502 Bad Gateway">>;
  1098. status(503) -> <<"503 Service Unavailable">>;
  1099. status(504) -> <<"504 Gateway Timeout">>;
  1100. status(505) -> <<"505 HTTP Version Not Supported">>;
  1101. status(506) -> <<"506 Variant Also Negotiates">>;
  1102. status(507) -> <<"507 Insufficient Storage">>;
  1103. status(510) -> <<"510 Not Extended">>;
  1104. status(511) -> <<"511 Network Authentication Required">>;
  1105. status(B) when is_binary(B) -> B.
  1106. %% Create map, convert keys to atoms and group duplicate keys into lists.
  1107. %% Keys that are not found in the user provided list are entirely skipped.
  1108. %% @todo Can probably be done directly while parsing.
  1109. kvlist_to_map(Fields, KvList) ->
  1110. Keys = [case K of
  1111. {Key, _} -> Key;
  1112. {Key, _, _} -> Key;
  1113. Key -> Key
  1114. end || K <- Fields],
  1115. kvlist_to_map(Keys, KvList, #{}).
  1116. kvlist_to_map(_, [], Map) ->
  1117. Map;
  1118. kvlist_to_map(Keys, [{Key, Value}|Tail], Map) ->
  1119. try binary_to_existing_atom(Key, utf8) of
  1120. Atom ->
  1121. case lists:member(Atom, Keys) of
  1122. true ->
  1123. case maps:find(Atom, Map) of
  1124. {ok, MapValue} when is_list(MapValue) ->
  1125. kvlist_to_map(Keys, Tail,
  1126. maps:put(Atom, [Value|MapValue], Map));
  1127. {ok, MapValue} ->
  1128. kvlist_to_map(Keys, Tail,
  1129. maps:put(Atom, [Value, MapValue], Map));
  1130. error ->
  1131. kvlist_to_map(Keys, Tail,
  1132. maps:put(Atom, Value, Map))
  1133. end;
  1134. false ->
  1135. kvlist_to_map(Keys, Tail, Map)
  1136. end
  1137. catch error:badarg ->
  1138. kvlist_to_map(Keys, Tail, Map)
  1139. end.
  1140. %% Loop through fields, if value is missing and no default, crash;
  1141. %% else if value is missing and has a default, set default;
  1142. %% otherwise apply constraints. If constraint fails, crash.
  1143. filter([], Map) ->
  1144. Map;
  1145. filter([{Key, Constraints}|Tail], Map) ->
  1146. filter_constraints(Tail, Map, Key, maps:get(Key, Map), Constraints);
  1147. filter([{Key, Constraints, Default}|Tail], Map) ->
  1148. case maps:find(Key, Map) of
  1149. {ok, Value} ->
  1150. filter_constraints(Tail, Map, Key, Value, Constraints);
  1151. error ->
  1152. filter(Tail, maps:put(Key, Default, Map))
  1153. end;
  1154. filter([Key|Tail], Map) ->
  1155. true = maps:is_key(Key, Map),
  1156. filter(Tail, Map).
  1157. filter_constraints(Tail, Map, Key, Value, Constraints) ->
  1158. case cowboy_constraints:validate(Value, Constraints) of
  1159. true ->
  1160. filter(Tail, Map);
  1161. {true, Value2} ->
  1162. filter(Tail, maps:put(Key, Value2, Map))
  1163. end.
  1164. %% Tests.
  1165. -ifdef(TEST).
  1166. url_test() ->
  1167. undefined =
  1168. url(#http_req{transport=ranch_tcp, host= <<>>, port= undefined,
  1169. path= <<>>, qs= <<>>, pid=self()}),
  1170. <<"http://localhost/path">> =
  1171. url(#http_req{transport=ranch_tcp, host= <<"localhost">>, port=80,
  1172. path= <<"/path">>, qs= <<>>, pid=self()}),
  1173. <<"http://localhost:443/path">> =
  1174. url(#http_req{transport=ranch_tcp, host= <<"localhost">>, port=443,
  1175. path= <<"/path">>, qs= <<>>, pid=self()}),
  1176. <<"http://localhost:8080/path">> =
  1177. url(#http_req{transport=ranch_tcp, host= <<"localhost">>, port=8080,
  1178. path= <<"/path">>, qs= <<>>, pid=self()}),
  1179. <<"http://localhost:8080/path?dummy=2785">> =
  1180. url(#http_req{transport=ranch_tcp, host= <<"localhost">>, port=8080,
  1181. path= <<"/path">>, qs= <<"dummy=2785">>, pid=self()}),
  1182. <<"https://localhost/path">> =
  1183. url(#http_req{transport=ranch_ssl, host= <<"localhost">>, port=443,
  1184. path= <<"/path">>, qs= <<>>, pid=self()}),
  1185. <<"https://localhost:8443/path">> =
  1186. url(#http_req{transport=ranch_ssl, host= <<"localhost">>, port=8443,
  1187. path= <<"/path">>, qs= <<>>, pid=self()}),
  1188. <<"https://localhost:8443/path?dummy=2785">> =
  1189. url(#http_req{transport=ranch_ssl, host= <<"localhost">>, port=8443,
  1190. path= <<"/path">>, qs= <<"dummy=2785">>, pid=self()}),
  1191. ok.
  1192. connection_to_atom_test_() ->
  1193. Tests = [
  1194. {[<<"close">>], close},
  1195. {[<<"keep-alive">>], keepalive},
  1196. {[<<"keep-alive">>, <<"upgrade">>], keepalive}
  1197. ],
  1198. [{lists:flatten(io_lib:format("~p", [T])),
  1199. fun() -> R = connection_to_atom(T) end} || {T, R} <- Tests].
  1200. merge_headers_test_() ->
  1201. Tests = [
  1202. {[{<<"content-length">>,<<"13">>},{<<"server">>,<<"Cowboy">>}],
  1203. [{<<"set-cookie">>,<<"foo=bar">>},{<<"content-length">>,<<"11">>}],
  1204. [{<<"set-cookie">>,<<"foo=bar">>},
  1205. {<<"content-length">>,<<"13">>},
  1206. {<<"server">>,<<"Cowboy">>}]},
  1207. {[{<<"content-length">>,<<"13">>},{<<"server">>,<<"Cowboy">>}],
  1208. [{<<"set-cookie">>,<<"foo=bar">>},{<<"set-cookie">>,<<"bar=baz">>}],
  1209. [{<<"set-cookie">>,<<"bar=baz">>},
  1210. {<<"set-cookie">>,<<"foo=bar">>},
  1211. {<<"content-length">>,<<"13">>},
  1212. {<<"server">>,<<"Cowboy">>}]}
  1213. ],
  1214. [fun() -> Res = merge_headers(L,R) end || {L, R, Res} <- Tests].
  1215. -endif.