cowboy_req.erl 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042
  1. %% Copyright (c) 2011-2012, 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. %% @doc HTTP request manipulation API.
  16. %%
  17. %% Almost all functions in this module return a new <em>Req</em> variable.
  18. %% It should always be used instead of the one used in your function call
  19. %% because it keeps the state of the request. It also allows Cowboy to do
  20. %% some lazy evaluation and cache results where possible.
  21. -module(cowboy_req).
  22. %% Request API.
  23. -export([method/1]).
  24. -export([version/1]).
  25. -export([peer/1]).
  26. -export([peer_addr/1]).
  27. -export([host/1]).
  28. -export([host_info/1]).
  29. -export([raw_host/1]).
  30. -export([port/1]).
  31. -export([path/1]).
  32. -export([path_info/1]).
  33. -export([raw_path/1]).
  34. -export([qs_val/2]).
  35. -export([qs_val/3]).
  36. -export([qs_vals/1]).
  37. -export([raw_qs/1]).
  38. -export([binding/2]).
  39. -export([binding/3]).
  40. -export([bindings/1]).
  41. -export([header/2]).
  42. -export([header/3]).
  43. -export([headers/1]).
  44. -export([parse_header/2]).
  45. -export([parse_header/3]).
  46. -export([cookie/2]).
  47. -export([cookie/3]).
  48. -export([cookies/1]).
  49. -export([meta/2]).
  50. -export([meta/3]).
  51. %% Request body API.
  52. -export([has_body/1]).
  53. -export([body_length/1]).
  54. -export([init_stream/4]).
  55. -export([stream_body/1]).
  56. -export([skip_body/1]).
  57. -export([body/1]).
  58. -export([body/2]).
  59. -export([body_qs/1]).
  60. -export([multipart_data/1]).
  61. -export([multipart_skip/1]).
  62. %% Response API.
  63. -export([set_resp_cookie/4]).
  64. -export([set_resp_header/3]).
  65. -export([set_resp_body/2]).
  66. -export([set_resp_body_fun/3]).
  67. -export([has_resp_header/2]).
  68. -export([has_resp_body/1]).
  69. -export([reply/2]).
  70. -export([reply/3]).
  71. -export([reply/4]).
  72. -export([chunked_reply/2]).
  73. -export([chunked_reply/3]).
  74. -export([chunk/2]).
  75. -export([upgrade_reply/3]).
  76. %% Misc API.
  77. -export([compact/1]).
  78. -export([transport/1]).
  79. -include("http.hrl").
  80. -type req() :: #http_req{}.
  81. -export_type([req/0]).
  82. %% Request API.
  83. %% @doc Return the HTTP method of the request.
  84. -spec method(Req) -> {cowboy_http:method(), Req} when Req::req().
  85. method(Req) ->
  86. {Req#http_req.method, Req}.
  87. %% @doc Return the HTTP version used for the request.
  88. -spec version(Req) -> {cowboy_http:version(), Req} when Req::req().
  89. version(Req) ->
  90. {Req#http_req.version, Req}.
  91. %% @doc Return the peer address and port number of the remote host.
  92. -spec peer(Req)
  93. -> {{inet:ip_address(), inet:port_number()}, Req} when Req::req().
  94. peer(Req=#http_req{socket=Socket, transport=Transport, peer=undefined}) ->
  95. {ok, Peer} = Transport:peername(Socket),
  96. {Peer, Req#http_req{peer=Peer}};
  97. peer(Req) ->
  98. {Req#http_req.peer, Req}.
  99. %% @doc Returns the peer address calculated from headers.
  100. -spec peer_addr(Req) -> {inet:ip_address(), Req} when Req::req().
  101. peer_addr(Req = #http_req{}) ->
  102. {RealIp, Req1} = header(<<"X-Real-Ip">>, Req),
  103. {ForwardedForRaw, Req2} = header(<<"X-Forwarded-For">>, Req1),
  104. {{PeerIp, _PeerPort}, Req3} = peer(Req2),
  105. ForwardedFor = case ForwardedForRaw of
  106. undefined ->
  107. undefined;
  108. ForwardedForRaw ->
  109. case re:run(ForwardedForRaw, "^(?<first_ip>[^\\,]+)",
  110. [{capture, [first_ip], binary}]) of
  111. {match, [FirstIp]} -> FirstIp;
  112. _Any -> undefined
  113. end
  114. end,
  115. {ok, PeerAddr} = if
  116. is_binary(RealIp) -> inet_parse:address(binary_to_list(RealIp));
  117. is_binary(ForwardedFor) -> inet_parse:address(binary_to_list(ForwardedFor));
  118. true -> {ok, PeerIp}
  119. end,
  120. {PeerAddr, Req3}.
  121. %% @doc Return the tokens for the hostname requested.
  122. -spec host(Req) -> {cowboy_dispatcher:tokens(), Req} when Req::req().
  123. host(Req) ->
  124. {Req#http_req.host, Req}.
  125. %% @doc Return the extra host information obtained from partially matching
  126. %% the hostname using <em>'...'</em>.
  127. -spec host_info(Req)
  128. -> {cowboy_dispatcher:tokens() | undefined, Req} when Req::req().
  129. host_info(Req) ->
  130. {Req#http_req.host_info, Req}.
  131. %% @doc Return the raw host directly taken from the request.
  132. -spec raw_host(Req) -> {binary(), Req} when Req::req().
  133. raw_host(Req) ->
  134. {Req#http_req.raw_host, Req}.
  135. %% @doc Return the port used for this request.
  136. -spec port(Req) -> {inet:port_number(), Req} when Req::req().
  137. port(Req) ->
  138. {Req#http_req.port, Req}.
  139. %% @doc Return the path segments for the path requested.
  140. %%
  141. %% Following RFC2396, this function may return path segments containing any
  142. %% character, including <em>/</em> if, and only if, a <em>/</em> was escaped
  143. %% and part of a path segment in the path requested.
  144. -spec path(Req) -> {cowboy_dispatcher:tokens(), Req} when Req::req().
  145. path(Req) ->
  146. {Req#http_req.path, Req}.
  147. %% @doc Return the extra path information obtained from partially matching
  148. %% the patch using <em>'...'</em>.
  149. -spec path_info(Req)
  150. -> {cowboy_dispatcher:tokens() | undefined, Req} when Req::req().
  151. path_info(Req) ->
  152. {Req#http_req.path_info, Req}.
  153. %% @doc Return the raw path directly taken from the request.
  154. -spec raw_path(Req) -> {binary(), Req} when Req::req().
  155. raw_path(Req) ->
  156. {Req#http_req.raw_path, Req}.
  157. %% @equiv qs_val(Name, Req, undefined)
  158. -spec qs_val(binary(), Req)
  159. -> {binary() | true | undefined, Req} when Req::req().
  160. qs_val(Name, Req) when is_binary(Name) ->
  161. qs_val(Name, Req, undefined).
  162. %% @doc Return the query string value for the given key, or a default if
  163. %% missing.
  164. -spec qs_val(binary(), Req, Default)
  165. -> {binary() | true | Default, Req} when Req::req(), Default::any().
  166. qs_val(Name, Req=#http_req{raw_qs=RawQs, qs_vals=undefined,
  167. urldecode={URLDecFun, URLDecArg}}, Default) when is_binary(Name) ->
  168. QsVals = cowboy_http:x_www_form_urlencoded(
  169. RawQs, fun(Bin) -> URLDecFun(Bin, URLDecArg) end),
  170. qs_val(Name, Req#http_req{qs_vals=QsVals}, Default);
  171. qs_val(Name, Req, Default) ->
  172. case lists:keyfind(Name, 1, Req#http_req.qs_vals) of
  173. {Name, Value} -> {Value, Req};
  174. false -> {Default, Req}
  175. end.
  176. %% @doc Return the full list of query string values.
  177. -spec qs_vals(Req) -> {list({binary(), binary() | true}), Req} when Req::req().
  178. qs_vals(Req=#http_req{raw_qs=RawQs, qs_vals=undefined,
  179. urldecode={URLDecFun, URLDecArg}}) ->
  180. QsVals = cowboy_http:x_www_form_urlencoded(
  181. RawQs, fun(Bin) -> URLDecFun(Bin, URLDecArg) end),
  182. qs_vals(Req#http_req{qs_vals=QsVals});
  183. qs_vals(Req=#http_req{qs_vals=QsVals}) ->
  184. {QsVals, Req}.
  185. %% @doc Return the raw query string directly taken from the request.
  186. -spec raw_qs(Req) -> {binary(), Req} when Req::req().
  187. raw_qs(Req) ->
  188. {Req#http_req.raw_qs, Req}.
  189. %% @equiv binding(Name, Req, undefined)
  190. -spec binding(atom(), Req) -> {binary() | undefined, Req} when Req::req().
  191. binding(Name, Req) when is_atom(Name) ->
  192. binding(Name, Req, undefined).
  193. %% @doc Return the binding value for the given key obtained when matching
  194. %% the host and path against the dispatch list, or a default if missing.
  195. -spec binding(atom(), Req, Default)
  196. -> {binary() | Default, Req} when Req::req(), Default::any().
  197. binding(Name, Req, Default) when is_atom(Name) ->
  198. case lists:keyfind(Name, 1, Req#http_req.bindings) of
  199. {Name, Value} -> {Value, Req};
  200. false -> {Default, Req}
  201. end.
  202. %% @doc Return the full list of binding values.
  203. -spec bindings(Req) -> {list({atom(), binary()}), Req} when Req::req().
  204. bindings(Req) ->
  205. {Req#http_req.bindings, Req}.
  206. %% @equiv header(Name, Req, undefined)
  207. -spec header(atom() | binary(), Req)
  208. -> {binary() | undefined, Req} when Req::req().
  209. header(Name, Req) when is_atom(Name) orelse is_binary(Name) ->
  210. header(Name, Req, undefined).
  211. %% @doc Return the header value for the given key, or a default if missing.
  212. -spec header(atom() | binary(), Req, Default)
  213. -> {binary() | Default, Req} when Req::req(), Default::any().
  214. header(Name, Req, Default) when is_atom(Name) orelse is_binary(Name) ->
  215. case lists:keyfind(Name, 1, Req#http_req.headers) of
  216. {Name, Value} -> {Value, Req};
  217. false -> {Default, Req}
  218. end.
  219. %% @doc Return the full list of headers.
  220. -spec headers(Req) -> {cowboy_http:headers(), Req} when Req::req().
  221. headers(Req) ->
  222. {Req#http_req.headers, Req}.
  223. %% @doc Semantically parse headers.
  224. %%
  225. %% When the value isn't found, a proper default value for the type
  226. %% returned is used as a return value.
  227. %% @see parse_header/3
  228. -spec parse_header(cowboy_http:header(), Req)
  229. -> {any(), Req} | {undefined, binary(), Req}
  230. | {error, badarg} when Req::req().
  231. parse_header(Name, Req=#http_req{p_headers=PHeaders}) ->
  232. case lists:keyfind(Name, 1, PHeaders) of
  233. false -> parse_header(Name, Req, parse_header_default(Name));
  234. {Name, Value} -> {Value, Req}
  235. end.
  236. %% @doc Default values for semantic header parsing.
  237. -spec parse_header_default(cowboy_http:header()) -> any().
  238. parse_header_default('Connection') -> [];
  239. parse_header_default('Transfer-Encoding') -> [<<"identity">>];
  240. parse_header_default(_Name) -> undefined.
  241. %% @doc Semantically parse headers.
  242. %%
  243. %% When the header is unknown, the value is returned directly without parsing.
  244. -spec parse_header(cowboy_http:header(), Req, any())
  245. -> {any(), Req} | {undefined, binary(), Req}
  246. | {error, badarg} when Req::req().
  247. parse_header(Name, Req, Default) when Name =:= 'Accept' ->
  248. parse_header(Name, Req, Default,
  249. fun (Value) ->
  250. cowboy_http:list(Value, fun cowboy_http:media_range/2)
  251. end);
  252. parse_header(Name, Req, Default) when Name =:= 'Accept-Charset' ->
  253. parse_header(Name, Req, Default,
  254. fun (Value) ->
  255. cowboy_http:nonempty_list(Value, fun cowboy_http:conneg/2)
  256. end);
  257. parse_header(Name, Req, Default) when Name =:= 'Accept-Encoding' ->
  258. parse_header(Name, Req, Default,
  259. fun (Value) ->
  260. cowboy_http:list(Value, fun cowboy_http:conneg/2)
  261. end);
  262. parse_header(Name, Req, Default) when Name =:= 'Accept-Language' ->
  263. parse_header(Name, Req, Default,
  264. fun (Value) ->
  265. cowboy_http:nonempty_list(Value, fun cowboy_http:language_range/2)
  266. end);
  267. parse_header(Name, Req, Default) when Name =:= 'Connection' ->
  268. parse_header(Name, Req, Default,
  269. fun (Value) ->
  270. cowboy_http:nonempty_list(Value, fun cowboy_http:token_ci/2)
  271. end);
  272. parse_header(Name, Req, Default) when Name =:= 'Content-Length' ->
  273. parse_header(Name, Req, Default,
  274. fun (Value) ->
  275. cowboy_http:digits(Value)
  276. end);
  277. parse_header(Name, Req, Default) when Name =:= 'Content-Type' ->
  278. parse_header(Name, Req, Default,
  279. fun (Value) ->
  280. cowboy_http:content_type(Value)
  281. end);
  282. parse_header(Name, Req, Default) when Name =:= <<"Expect">> ->
  283. parse_header(Name, Req, Default,
  284. fun (Value) ->
  285. cowboy_http:nonempty_list(Value, fun cowboy_http:expectation/2)
  286. end);
  287. parse_header(Name, Req, Default)
  288. when Name =:= 'If-Match'; Name =:= 'If-None-Match' ->
  289. parse_header(Name, Req, Default,
  290. fun (Value) ->
  291. cowboy_http:entity_tag_match(Value)
  292. end);
  293. parse_header(Name, Req, Default)
  294. when Name =:= 'If-Modified-Since'; Name =:= 'If-Unmodified-Since' ->
  295. parse_header(Name, Req, Default,
  296. fun (Value) ->
  297. cowboy_http:http_date(Value)
  298. end);
  299. %% @todo Extension parameters.
  300. parse_header(Name, Req, Default) when Name =:= 'Transfer-Encoding' ->
  301. parse_header(Name, Req, Default,
  302. fun (Value) ->
  303. cowboy_http:nonempty_list(Value, fun cowboy_http:token_ci/2)
  304. end);
  305. parse_header(Name, Req, Default) when Name =:= 'Upgrade' ->
  306. parse_header(Name, Req, Default,
  307. fun (Value) ->
  308. cowboy_http:nonempty_list(Value, fun cowboy_http:token_ci/2)
  309. end);
  310. parse_header(Name, Req, Default) ->
  311. {Value, Req2} = header(Name, Req, Default),
  312. {undefined, Value, Req2}.
  313. parse_header(Name, Req=#http_req{p_headers=PHeaders}, Default, Fun) ->
  314. case lists:keyfind(Name, 1, PHeaders) of
  315. {Name, P} ->
  316. {P, Req};
  317. false ->
  318. parse_header_no_cache(Name, Req, Default, Fun)
  319. end.
  320. parse_header_no_cache(Name, Req=#http_req{p_headers=PHeaders}, Default, Fun) ->
  321. case header(Name, Req) of
  322. {undefined, Req2} ->
  323. {Default, Req2#http_req{p_headers=[{Name, Default}|PHeaders]}};
  324. {Value, Req2} ->
  325. case Fun(Value) of
  326. {error, badarg} ->
  327. {error, badarg};
  328. P ->
  329. {P, Req2#http_req{p_headers=[{Name, P}|PHeaders]}}
  330. end
  331. end.
  332. %% @equiv cookie(Name, Req, undefined)
  333. -spec cookie(binary(), Req)
  334. -> {binary() | true | undefined, Req} when Req::req().
  335. cookie(Name, Req) when is_binary(Name) ->
  336. cookie(Name, Req, undefined).
  337. %% @doc Return the cookie value for the given key, or a default if
  338. %% missing.
  339. -spec cookie(binary(), Req, Default)
  340. -> {binary() | true | Default, Req} when Req::req(), Default::any().
  341. cookie(Name, Req=#http_req{cookies=undefined}, Default) when is_binary(Name) ->
  342. case header('Cookie', Req) of
  343. {undefined, Req2} ->
  344. {Default, Req2#http_req{cookies=[]}};
  345. {RawCookie, Req2} ->
  346. Cookies = cowboy_cookies:parse_cookie(RawCookie),
  347. cookie(Name, Req2#http_req{cookies=Cookies}, Default)
  348. end;
  349. cookie(Name, Req, Default) ->
  350. case lists:keyfind(Name, 1, Req#http_req.cookies) of
  351. {Name, Value} -> {Value, Req};
  352. false -> {Default, Req}
  353. end.
  354. %% @doc Return the full list of cookie values.
  355. -spec cookies(Req) -> {list({binary(), binary() | true}), Req} when Req::req().
  356. cookies(Req=#http_req{cookies=undefined}) ->
  357. case header('Cookie', Req) of
  358. {undefined, Req2} ->
  359. {[], Req2#http_req{cookies=[]}};
  360. {RawCookie, Req2} ->
  361. Cookies = cowboy_cookies:parse_cookie(RawCookie),
  362. cookies(Req2#http_req{cookies=Cookies})
  363. end;
  364. cookies(Req=#http_req{cookies=Cookies}) ->
  365. {Cookies, Req}.
  366. %% @equiv meta(Name, Req, undefined)
  367. -spec meta(atom(), Req) -> {any() | undefined, Req} when Req::req().
  368. meta(Name, Req) ->
  369. meta(Name, Req, undefined).
  370. %% @doc Return metadata information about the request.
  371. %%
  372. %% Metadata information varies from one protocol to another. Websockets
  373. %% would define the protocol version here, while REST would use it to
  374. %% indicate which media type, language and charset were retained.
  375. -spec meta(atom(), Req, any()) -> {any(), Req} when Req::req().
  376. meta(Name, Req, Default) ->
  377. case lists:keyfind(Name, 1, Req#http_req.meta) of
  378. {Name, Value} -> {Value, Req};
  379. false -> {Default, Req}
  380. end.
  381. %% Request Body API.
  382. %% @doc Return whether the request message has a body.
  383. -spec has_body(Req) -> {boolean(), Req} when Req::req().
  384. has_body(Req) ->
  385. Has = lists:keymember('Content-Length', 1, Req#http_req.headers) orelse
  386. lists:keymember('Transfer-Encoding', 1, Req#http_req.headers),
  387. {Has, Req}.
  388. %% @doc Return the request message body length, if known.
  389. %%
  390. %% The length may not be known if Transfer-Encoding is not identity,
  391. %% and the body hasn't been read at the time of the call.
  392. -spec body_length(Req) -> {undefined | non_neg_integer(), Req} when Req::req().
  393. body_length(Req) ->
  394. case lists:keymember('Transfer-Encoding', 1, Req#http_req.headers) of
  395. true -> {undefined, Req};
  396. false -> parse_header('Content-Length', Req, 0)
  397. end.
  398. %% @doc Initialize body streaming and set custom decoding functions.
  399. %%
  400. %% Calling this function is optional. It should only be used if you
  401. %% need to override the default behavior of Cowboy. Otherwise you
  402. %% should call stream_body/1 directly.
  403. %%
  404. %% Two decodings happen. First a decoding function is applied to the
  405. %% transferred data, and then another is applied to the actual content.
  406. %%
  407. %% Transfer encoding is generally used for chunked bodies. The decoding
  408. %% function uses a state to keep track of how much it has read, which is
  409. %% also initialized through this function.
  410. %%
  411. %% Content encoding is generally used for compression.
  412. %%
  413. %% Standard encodings can be found in cowboy_http.
  414. -spec init_stream(fun(), any(), fun(), Req) -> {ok, Req} when Req::req().
  415. init_stream(TransferDecode, TransferState, ContentDecode, Req) ->
  416. {ok, Req#http_req{body_state=
  417. {stream, TransferDecode, TransferState, ContentDecode}}}.
  418. %% @doc Stream the request's body.
  419. %%
  420. %% This is the most low level function to read the request body.
  421. %%
  422. %% In most cases, if they weren't defined before using stream_body/4,
  423. %% this function will guess which transfer and content encodings were
  424. %% used for building the request body, and configure the decoding
  425. %% functions that will be used when streaming.
  426. %%
  427. %% It then starts streaming the body, returning {ok, Data, Req}
  428. %% for each streamed part, and {done, Req} when it's finished streaming.
  429. -spec stream_body(Req) -> {ok, binary(), Req}
  430. | {done, Req} | {error, atom()} when Req::req().
  431. stream_body(Req=#http_req{body_state=waiting,
  432. version=Version, transport=Transport, socket=Socket}) ->
  433. case parse_header(<<"Expect">>, Req) of
  434. {[<<"100-continue">>], Req1} ->
  435. HTTPVer = cowboy_http:version_to_binary(Version),
  436. Transport:send(Socket,
  437. << HTTPVer/binary, " ", (status(100))/binary, "\r\n\r\n" >>);
  438. {undefined, Req1} ->
  439. ok;
  440. {undefined, _, Req1} ->
  441. ok
  442. end,
  443. case parse_header('Transfer-Encoding', Req1) of
  444. {[<<"chunked">>], Req2} ->
  445. stream_body(Req2#http_req{body_state=
  446. {stream, fun cowboy_http:te_chunked/2, {0, 0},
  447. fun cowboy_http:ce_identity/1}});
  448. {[<<"identity">>], Req2} ->
  449. {Length, Req3} = body_length(Req2),
  450. case Length of
  451. 0 ->
  452. {done, Req3#http_req{body_state=done}};
  453. Length ->
  454. stream_body(Req3#http_req{body_state=
  455. {stream, fun cowboy_http:te_identity/2, {0, Length},
  456. fun cowboy_http:ce_identity/1}})
  457. end
  458. end;
  459. stream_body(Req=#http_req{buffer=Buffer, body_state={stream, _, _, _}})
  460. when Buffer =/= <<>> ->
  461. transfer_decode(Buffer, Req#http_req{buffer= <<>>});
  462. stream_body(Req=#http_req{body_state={stream, _, _, _}}) ->
  463. stream_body_recv(Req);
  464. stream_body(Req=#http_req{body_state=done}) ->
  465. {done, Req};
  466. stream_body(Req=#http_req{body_state={multipart, _N, _Fun},
  467. transport=Transport, socket=Socket}) ->
  468. case Transport:recv(Socket, 0, 5000) of
  469. {ok, Data} -> {ok, Data, Req};
  470. {error, Reason} -> {error, Reason}
  471. end.
  472. -spec stream_body_recv(Req)
  473. -> {ok, binary(), Req} | {error, atom()} when Req::req().
  474. stream_body_recv(Req=#http_req{
  475. transport=Transport, socket=Socket, buffer=Buffer}) ->
  476. %% @todo Allow configuring the timeout.
  477. case Transport:recv(Socket, 0, 5000) of
  478. {ok, Data} -> transfer_decode(<< Buffer/binary, Data/binary >>, Req);
  479. {error, Reason} -> {error, Reason}
  480. end.
  481. -spec transfer_decode(binary(), Req)
  482. -> {ok, binary(), Req} | {error, atom()} when Req::req().
  483. transfer_decode(Data, Req=#http_req{
  484. body_state={stream, TransferDecode, TransferState, ContentDecode}}) ->
  485. case TransferDecode(Data, TransferState) of
  486. {ok, Data2, TransferState2} ->
  487. content_decode(ContentDecode, Data2, Req#http_req{body_state=
  488. {stream, TransferDecode, TransferState2, ContentDecode}});
  489. {ok, Data2, Rest, TransferState2} ->
  490. content_decode(ContentDecode, Data2, Req#http_req{
  491. buffer=Rest, body_state=
  492. {stream, TransferDecode, TransferState2, ContentDecode}});
  493. %% @todo {header(s) for chunked
  494. more ->
  495. stream_body_recv(Req#http_req{buffer=Data});
  496. {done, Length, Rest} ->
  497. Req2 = transfer_decode_done(Length, Rest, Req),
  498. {done, Req2};
  499. {done, Data2, Length, Rest} ->
  500. Req2 = transfer_decode_done(Length, Rest, Req),
  501. content_decode(ContentDecode, Data2, Req2);
  502. {error, Reason} ->
  503. {error, Reason}
  504. end.
  505. -spec transfer_decode_done(non_neg_integer(), binary(), Req)
  506. -> Req when Req::req().
  507. transfer_decode_done(Length, Rest, Req=#http_req{
  508. headers=Headers, p_headers=PHeaders}) ->
  509. Headers2 = lists:keystore('Content-Length', 1, Headers,
  510. {'Content-Length', list_to_binary(integer_to_list(Length))}),
  511. %% At this point we just assume TEs were all decoded.
  512. Headers3 = lists:keydelete('Transfer-Encoding', 1, Headers2),
  513. PHeaders2 = lists:keystore('Content-Length', 1, PHeaders,
  514. {'Content-Length', Length}),
  515. PHeaders3 = lists:keydelete('Transfer-Encoding', 1, PHeaders2),
  516. Req#http_req{buffer=Rest, body_state=done,
  517. headers=Headers3, p_headers=PHeaders3}.
  518. %% @todo Probably needs a Rest.
  519. -spec content_decode(fun(), binary(), Req)
  520. -> {ok, binary(), Req} | {error, atom()} when Req::req().
  521. content_decode(ContentDecode, Data, Req) ->
  522. case ContentDecode(Data) of
  523. {ok, Data2} -> {ok, Data2, Req};
  524. {error, Reason} -> {error, Reason}
  525. end.
  526. %% @doc Return the full body sent with the request.
  527. -spec body(Req) -> {ok, binary(), Req} | {error, atom()} when Req::req().
  528. body(Req) ->
  529. read_body(infinity, Req, <<>>).
  530. %% @doc Return the full body sent with the request as long as the body
  531. %% length doesn't go over MaxLength.
  532. %%
  533. %% This is most useful to quickly be able to get the full body while
  534. %% avoiding filling your memory with huge request bodies when you're
  535. %% not expecting it.
  536. -spec body(non_neg_integer() | infinity, Req)
  537. -> {ok, binary(), Req} | {error, atom()} when Req::req().
  538. body(MaxLength, Req) ->
  539. read_body(MaxLength, Req, <<>>).
  540. -spec read_body(non_neg_integer() | infinity, Req, binary())
  541. -> {ok, binary(), Req} | {error, atom()} when Req::req().
  542. read_body(MaxLength, Req, Acc) when MaxLength > byte_size(Acc) ->
  543. case stream_body(Req) of
  544. {ok, Data, Req2} ->
  545. read_body(MaxLength, Req2, << Acc/binary, Data/binary >>);
  546. {done, Req2} ->
  547. {ok, Acc, Req2};
  548. {error, Reason} ->
  549. {error, Reason}
  550. end.
  551. -spec skip_body(Req) -> {ok, Req} | {error, atom()} when Req::req().
  552. skip_body(Req) ->
  553. case stream_body(Req) of
  554. {ok, _, Req2} -> skip_body(Req2);
  555. {done, Req2} -> {ok, Req2};
  556. {error, Reason} -> {error, Reason}
  557. end.
  558. %% @doc Return the full body sent with the reqest, parsed as an
  559. %% application/x-www-form-urlencoded string. Essentially a POST query string.
  560. %% @todo We need an option to limit the size of the body for QS too.
  561. -spec body_qs(Req) -> {list({binary(), binary() | true}), Req} when Req::req().
  562. body_qs(Req=#http_req{urldecode={URLDecFun, URLDecArg}}) ->
  563. {ok, Body, Req2} = body(Req),
  564. {cowboy_http:x_www_form_urlencoded(
  565. Body, fun(Bin) -> URLDecFun(Bin, URLDecArg) end), Req2}.
  566. %% Multipart Request API.
  567. %% @doc Return data from the multipart parser.
  568. %%
  569. %% Use this function for multipart streaming. For each part in the request,
  570. %% this function returns <em>{headers, Headers}</em> followed by a sequence of
  571. %% <em>{body, Data}</em> tuples and finally <em>end_of_part</em>. When there
  572. %% is no part to parse anymore, <em>eof</em> is returned.
  573. %%
  574. %% If the request Content-Type is not a multipart one, <em>{error, badarg}</em>
  575. %% is returned.
  576. -spec multipart_data(Req)
  577. -> {{headers, cowboy_http:headers()} | {body, binary()}
  578. | end_of_part | eof, Req} when Req::req().
  579. multipart_data(Req=#http_req{body_state=waiting}) ->
  580. {{<<"multipart">>, _SubType, Params}, Req2} =
  581. parse_header('Content-Type', Req),
  582. {_, Boundary} = lists:keyfind(<<"boundary">>, 1, Params),
  583. {Length, Req3} = parse_header('Content-Length', Req2),
  584. multipart_data(Req3, Length, {more, cowboy_multipart:parser(Boundary)});
  585. multipart_data(Req=#http_req{body_state={multipart, Length, Cont}}) ->
  586. multipart_data(Req, Length, Cont());
  587. multipart_data(Req=#http_req{body_state=done}) ->
  588. {eof, Req}.
  589. %% @todo Typespecs.
  590. multipart_data(Req, Length, {headers, Headers, Cont}) ->
  591. {{headers, Headers}, Req#http_req{body_state={multipart, Length, Cont}}};
  592. multipart_data(Req, Length, {body, Data, Cont}) ->
  593. {{body, Data}, Req#http_req{body_state={multipart, Length, Cont}}};
  594. multipart_data(Req, Length, {end_of_part, Cont}) ->
  595. {end_of_part, Req#http_req{body_state={multipart, Length, Cont}}};
  596. multipart_data(Req, 0, eof) ->
  597. {eof, Req#http_req{body_state=done}};
  598. multipart_data(Req=#http_req{socket=Socket, transport=Transport},
  599. Length, eof) ->
  600. %% We just want to skip so no need to stream data here.
  601. {ok, _Data} = Transport:recv(Socket, Length, 5000),
  602. {eof, Req#http_req{body_state=done}};
  603. multipart_data(Req, Length, {more, Parser}) when Length > 0 ->
  604. case stream_body(Req) of
  605. {ok, << Data:Length/binary, Buffer/binary >>, Req2} ->
  606. multipart_data(Req2#http_req{buffer=Buffer}, 0, Parser(Data));
  607. {ok, Data, Req2} ->
  608. multipart_data(Req2, Length - byte_size(Data), Parser(Data))
  609. end.
  610. %% @doc Skip a part returned by the multipart parser.
  611. %%
  612. %% This function repeatedly calls <em>multipart_data/1</em> until
  613. %% <em>end_of_part</em> or <em>eof</em> is parsed.
  614. -spec multipart_skip(Req) -> {ok, Req} when Req::req().
  615. multipart_skip(Req) ->
  616. case multipart_data(Req) of
  617. {end_of_part, Req2} -> {ok, Req2};
  618. {eof, Req2} -> {ok, Req2};
  619. {_Other, Req2} -> multipart_skip(Req2)
  620. end.
  621. %% Response API.
  622. %% @doc Add a cookie header to the response.
  623. -spec set_resp_cookie(binary(), binary(),
  624. [cowboy_cookies:cookie_option()], Req) -> {ok, Req} when Req::req().
  625. set_resp_cookie(Name, Value, Options, Req) ->
  626. {HeaderName, HeaderValue} = cowboy_cookies:cookie(Name, Value, Options),
  627. set_resp_header(HeaderName, HeaderValue, Req).
  628. %% @doc Add a header to the response.
  629. -spec set_resp_header(cowboy_http:header(), iodata(), Req)
  630. -> {ok, Req} when Req::req().
  631. set_resp_header(Name, Value, Req=#http_req{resp_headers=RespHeaders}) ->
  632. NameBin = header_to_binary(Name),
  633. {ok, Req#http_req{resp_headers=[{NameBin, Value}|RespHeaders]}}.
  634. %% @doc Add a body to the response.
  635. %%
  636. %% The body set here is ignored if the response is later sent using
  637. %% anything other than reply/2 or reply/3. The response body is expected
  638. %% to be a binary or an iolist.
  639. -spec set_resp_body(iodata(), Req) -> {ok, Req} when Req::req().
  640. set_resp_body(Body, Req) ->
  641. {ok, Req#http_req{resp_body=Body}}.
  642. %% @doc Add a body function to the response.
  643. %%
  644. %% The response body may also be set to a content-length - stream-function pair.
  645. %% If the response body is of this type normal response headers will be sent.
  646. %% After the response headers has been sent the body function is applied.
  647. %% The body function is expected to write the response body directly to the
  648. %% socket using the transport module.
  649. %%
  650. %% If the body function crashes while writing the response body or writes fewer
  651. %% bytes than declared the behaviour is undefined. The body set here is ignored
  652. %% if the response is later sent using anything other than `reply/2' or
  653. %% `reply/3'.
  654. %%
  655. %% @see cowboy_req:transport/1.
  656. -spec set_resp_body_fun(non_neg_integer(),
  657. fun(() -> {sent, non_neg_integer()}), Req) -> {ok, Req} when Req::req().
  658. set_resp_body_fun(StreamLen, StreamFun, Req) ->
  659. {ok, Req#http_req{resp_body={StreamLen, StreamFun}}}.
  660. %% @doc Return whether the given header has been set for the response.
  661. -spec has_resp_header(cowboy_http:header(), req()) -> boolean().
  662. has_resp_header(Name, #http_req{resp_headers=RespHeaders}) ->
  663. NameBin = header_to_binary(Name),
  664. lists:keymember(NameBin, 1, RespHeaders).
  665. %% @doc Return whether a body has been set for the response.
  666. -spec has_resp_body(req()) -> boolean().
  667. has_resp_body(#http_req{resp_body={Length, _}}) ->
  668. Length > 0;
  669. has_resp_body(#http_req{resp_body=RespBody}) ->
  670. iolist_size(RespBody) > 0.
  671. %% @equiv reply(Status, [], [], Req)
  672. -spec reply(cowboy_http:status(), Req) -> {ok, Req} when Req::req().
  673. reply(Status, Req=#http_req{resp_body=Body}) ->
  674. reply(Status, [], Body, Req).
  675. %% @equiv reply(Status, Headers, [], Req)
  676. -spec reply(cowboy_http:status(), cowboy_http:headers(), Req)
  677. -> {ok, Req} when Req::req().
  678. reply(Status, Headers, Req=#http_req{resp_body=Body}) ->
  679. reply(Status, Headers, Body, Req).
  680. %% @doc Send a reply to the client.
  681. -spec reply(cowboy_http:status(), cowboy_http:headers(), iodata(), Req)
  682. -> {ok, Req} when Req::req().
  683. reply(Status, Headers, Body, Req=#http_req{socket=Socket, transport=Transport,
  684. version=Version, connection=Connection,
  685. method=Method, resp_state=waiting, resp_headers=RespHeaders}) ->
  686. RespConn = response_connection(Headers, Connection),
  687. ContentLen = case Body of {CL, _} -> CL; _ -> iolist_size(Body) end,
  688. HTTP11Headers = case Version of
  689. {1, 1} -> [{<<"Connection">>, atom_to_connection(Connection)}];
  690. _ -> []
  691. end,
  692. {ReplyType, Req2} = response(Status, Headers, RespHeaders, [
  693. {<<"Content-Length">>, integer_to_list(ContentLen)},
  694. {<<"Date">>, cowboy_clock:rfc1123()},
  695. {<<"Server">>, <<"Cowboy">>}
  696. |HTTP11Headers], Req),
  697. if Method =:= 'HEAD' -> ok;
  698. ReplyType =:= hook -> ok; %% Hook replied for us, stop there.
  699. true ->
  700. case Body of
  701. {_, StreamFun} -> StreamFun();
  702. _ -> Transport:send(Socket, Body)
  703. end
  704. end,
  705. {ok, Req2#http_req{connection=RespConn, resp_state=done,
  706. resp_headers=[], resp_body= <<>>}}.
  707. %% @equiv chunked_reply(Status, [], Req)
  708. -spec chunked_reply(cowboy_http:status(), Req) -> {ok, Req} when Req::req().
  709. chunked_reply(Status, Req) ->
  710. chunked_reply(Status, [], Req).
  711. %% @doc Initiate the sending of a chunked reply to the client.
  712. %% @see cowboy_req:chunk/2
  713. -spec chunked_reply(cowboy_http:status(), cowboy_http:headers(), Req)
  714. -> {ok, Req} when Req::req().
  715. chunked_reply(Status, Headers, Req=#http_req{
  716. version=Version, connection=Connection,
  717. resp_state=waiting, resp_headers=RespHeaders}) ->
  718. RespConn = response_connection(Headers, Connection),
  719. HTTP11Headers = case Version of
  720. {1, 1} -> [
  721. {<<"Connection">>, atom_to_connection(Connection)},
  722. {<<"Transfer-Encoding">>, <<"chunked">>}];
  723. _ -> []
  724. end,
  725. {_, Req2} = response(Status, Headers, RespHeaders, [
  726. {<<"Date">>, cowboy_clock:rfc1123()},
  727. {<<"Server">>, <<"Cowboy">>}
  728. |HTTP11Headers], Req),
  729. {ok, Req2#http_req{connection=RespConn, resp_state=chunks,
  730. resp_headers=[], resp_body= <<>>}}.
  731. %% @doc Send a chunk of data.
  732. %%
  733. %% A chunked reply must have been initiated before calling this function.
  734. -spec chunk(iodata(), req()) -> ok | {error, atom()}.
  735. chunk(_Data, #http_req{socket=_Socket, transport=_Transport, method='HEAD'}) ->
  736. ok;
  737. chunk(Data, #http_req{socket=Socket, transport=Transport, version={1, 0}}) ->
  738. Transport:send(Socket, Data);
  739. chunk(Data, #http_req{socket=Socket, transport=Transport, resp_state=chunks}) ->
  740. Transport:send(Socket, [integer_to_list(iolist_size(Data), 16),
  741. <<"\r\n">>, Data, <<"\r\n">>]).
  742. %% @doc Send an upgrade reply.
  743. %% @private
  744. -spec upgrade_reply(cowboy_http:status(), cowboy_http:headers(), Req)
  745. -> {ok, Req} when Req::req().
  746. upgrade_reply(Status, Headers, Req=#http_req{
  747. resp_state=waiting, resp_headers=RespHeaders}) ->
  748. {_, Req2} = response(Status, Headers, RespHeaders, [
  749. {<<"Connection">>, <<"Upgrade">>}
  750. ], Req),
  751. {ok, Req2#http_req{resp_state=done, resp_headers=[], resp_body= <<>>}}.
  752. %% Misc API.
  753. %% @doc Compact the request data by removing all non-system information.
  754. %%
  755. %% This essentially removes the host, path, query string, bindings and headers.
  756. %% Use it when you really need to save up memory, for example when having
  757. %% many concurrent long-running connections.
  758. -spec compact(Req) -> Req when Req::req().
  759. compact(Req) ->
  760. Req#http_req{host=undefined, host_info=undefined, path=undefined,
  761. path_info=undefined, qs_vals=undefined,
  762. bindings=undefined, headers=[],
  763. p_headers=[], cookies=[]}.
  764. %% @doc Return the transport module and socket associated with a request.
  765. %%
  766. %% This exposes the same socket interface used internally by the HTTP protocol
  767. %% implementation to developers that needs low level access to the socket.
  768. %%
  769. %% It is preferred to use this in conjuction with the stream function support
  770. %% in `set_resp_body_fun/3' if this is used to write a response body directly
  771. %% to the socket. This ensures that the response headers are set correctly.
  772. -spec transport(req()) -> {ok, module(), inet:socket()}.
  773. transport(#http_req{transport=Transport, socket=Socket}) ->
  774. {ok, Transport, Socket}.
  775. %% Internal.
  776. -spec response(cowboy_http:status(), cowboy_http:headers(),
  777. cowboy_http:headers(), cowboy_http:headers(), Req)
  778. -> {normal | hook, Req} when Req::req().
  779. response(Status, Headers, RespHeaders, DefaultHeaders, Req=#http_req{
  780. socket=Socket, transport=Transport, version=Version,
  781. pid=ReqPid, onresponse=OnResponse}) ->
  782. FullHeaders = response_merge_headers(Headers, RespHeaders, DefaultHeaders),
  783. Req2 = case OnResponse of
  784. undefined -> Req;
  785. OnResponse -> OnResponse(Status, FullHeaders,
  786. %% Don't call 'onresponse' from the hook itself.
  787. Req#http_req{resp_headers=[], resp_body= <<>>,
  788. onresponse=undefined})
  789. end,
  790. ReplyType = case Req2#http_req.resp_state of
  791. waiting ->
  792. HTTPVer = cowboy_http:version_to_binary(Version),
  793. StatusLine = << HTTPVer/binary, " ",
  794. (status(Status))/binary, "\r\n" >>,
  795. HeaderLines = [[Key, <<": ">>, Value, <<"\r\n">>]
  796. || {Key, Value} <- FullHeaders],
  797. Transport:send(Socket, [StatusLine, HeaderLines, <<"\r\n">>]),
  798. ReqPid ! {?MODULE, resp_sent},
  799. normal;
  800. _ ->
  801. hook
  802. end,
  803. {ReplyType, Req2}.
  804. -spec response_connection(cowboy_http:headers(), keepalive | close)
  805. -> keepalive | close.
  806. response_connection([], Connection) ->
  807. Connection;
  808. response_connection([{Name, Value}|Tail], Connection) ->
  809. case Name of
  810. 'Connection' -> response_connection_parse(Value);
  811. Name when is_atom(Name) -> response_connection(Tail, Connection);
  812. Name ->
  813. Name2 = cowboy_bstr:to_lower(Name),
  814. case Name2 of
  815. <<"connection">> -> response_connection_parse(Value);
  816. _Any -> response_connection(Tail, Connection)
  817. end
  818. end.
  819. -spec response_connection_parse(binary()) -> keepalive | close.
  820. response_connection_parse(ReplyConn) ->
  821. Tokens = cowboy_http:nonempty_list(ReplyConn, fun cowboy_http:token/2),
  822. cowboy_http:connection_to_atom(Tokens).
  823. -spec response_merge_headers(cowboy_http:headers(), cowboy_http:headers(),
  824. cowboy_http:headers()) -> cowboy_http:headers().
  825. response_merge_headers(Headers, RespHeaders, DefaultHeaders) ->
  826. Headers2 = [{header_to_binary(Key), Value} || {Key, Value} <- Headers],
  827. merge_headers(
  828. merge_headers(Headers2, RespHeaders),
  829. DefaultHeaders).
  830. -spec merge_headers(cowboy_http:headers(), cowboy_http:headers())
  831. -> cowboy_http:headers().
  832. merge_headers(Headers, []) ->
  833. Headers;
  834. merge_headers(Headers, [{Name, Value}|Tail]) ->
  835. Headers2 = case lists:keymember(Name, 1, Headers) of
  836. true -> Headers;
  837. false -> Headers ++ [{Name, Value}]
  838. end,
  839. merge_headers(Headers2, Tail).
  840. -spec atom_to_connection(keepalive) -> <<_:80>>;
  841. (close) -> <<_:40>>.
  842. atom_to_connection(keepalive) ->
  843. <<"keep-alive">>;
  844. atom_to_connection(close) ->
  845. <<"close">>.
  846. -spec status(cowboy_http:status()) -> binary().
  847. status(100) -> <<"100 Continue">>;
  848. status(101) -> <<"101 Switching Protocols">>;
  849. status(102) -> <<"102 Processing">>;
  850. status(200) -> <<"200 OK">>;
  851. status(201) -> <<"201 Created">>;
  852. status(202) -> <<"202 Accepted">>;
  853. status(203) -> <<"203 Non-Authoritative Information">>;
  854. status(204) -> <<"204 No Content">>;
  855. status(205) -> <<"205 Reset Content">>;
  856. status(206) -> <<"206 Partial Content">>;
  857. status(207) -> <<"207 Multi-Status">>;
  858. status(226) -> <<"226 IM Used">>;
  859. status(300) -> <<"300 Multiple Choices">>;
  860. status(301) -> <<"301 Moved Permanently">>;
  861. status(302) -> <<"302 Found">>;
  862. status(303) -> <<"303 See Other">>;
  863. status(304) -> <<"304 Not Modified">>;
  864. status(305) -> <<"305 Use Proxy">>;
  865. status(306) -> <<"306 Switch Proxy">>;
  866. status(307) -> <<"307 Temporary Redirect">>;
  867. status(400) -> <<"400 Bad Request">>;
  868. status(401) -> <<"401 Unauthorized">>;
  869. status(402) -> <<"402 Payment Required">>;
  870. status(403) -> <<"403 Forbidden">>;
  871. status(404) -> <<"404 Not Found">>;
  872. status(405) -> <<"405 Method Not Allowed">>;
  873. status(406) -> <<"406 Not Acceptable">>;
  874. status(407) -> <<"407 Proxy Authentication Required">>;
  875. status(408) -> <<"408 Request Timeout">>;
  876. status(409) -> <<"409 Conflict">>;
  877. status(410) -> <<"410 Gone">>;
  878. status(411) -> <<"411 Length Required">>;
  879. status(412) -> <<"412 Precondition Failed">>;
  880. status(413) -> <<"413 Request Entity Too Large">>;
  881. status(414) -> <<"414 Request-URI Too Long">>;
  882. status(415) -> <<"415 Unsupported Media Type">>;
  883. status(416) -> <<"416 Requested Range Not Satisfiable">>;
  884. status(417) -> <<"417 Expectation Failed">>;
  885. status(418) -> <<"418 I'm a teapot">>;
  886. status(422) -> <<"422 Unprocessable Entity">>;
  887. status(423) -> <<"423 Locked">>;
  888. status(424) -> <<"424 Failed Dependency">>;
  889. status(425) -> <<"425 Unordered Collection">>;
  890. status(426) -> <<"426 Upgrade Required">>;
  891. status(428) -> <<"428 Precondition Required">>;
  892. status(429) -> <<"429 Too Many Requests">>;
  893. status(431) -> <<"431 Request Header Fields Too Large">>;
  894. status(500) -> <<"500 Internal Server Error">>;
  895. status(501) -> <<"501 Not Implemented">>;
  896. status(502) -> <<"502 Bad Gateway">>;
  897. status(503) -> <<"503 Service Unavailable">>;
  898. status(504) -> <<"504 Gateway Timeout">>;
  899. status(505) -> <<"505 HTTP Version Not Supported">>;
  900. status(506) -> <<"506 Variant Also Negotiates">>;
  901. status(507) -> <<"507 Insufficient Storage">>;
  902. status(510) -> <<"510 Not Extended">>;
  903. status(511) -> <<"511 Network Authentication Required">>;
  904. status(B) when is_binary(B) -> B.
  905. -spec header_to_binary(cowboy_http:header()) -> binary().
  906. header_to_binary('Cache-Control') -> <<"Cache-Control">>;
  907. header_to_binary('Connection') -> <<"Connection">>;
  908. header_to_binary('Date') -> <<"Date">>;
  909. header_to_binary('Pragma') -> <<"Pragma">>;
  910. header_to_binary('Transfer-Encoding') -> <<"Transfer-Encoding">>;
  911. header_to_binary('Upgrade') -> <<"Upgrade">>;
  912. header_to_binary('Via') -> <<"Via">>;
  913. header_to_binary('Accept') -> <<"Accept">>;
  914. header_to_binary('Accept-Charset') -> <<"Accept-Charset">>;
  915. header_to_binary('Accept-Encoding') -> <<"Accept-Encoding">>;
  916. header_to_binary('Accept-Language') -> <<"Accept-Language">>;
  917. header_to_binary('Authorization') -> <<"Authorization">>;
  918. header_to_binary('From') -> <<"From">>;
  919. header_to_binary('Host') -> <<"Host">>;
  920. header_to_binary('If-Modified-Since') -> <<"If-Modified-Since">>;
  921. header_to_binary('If-Match') -> <<"If-Match">>;
  922. header_to_binary('If-None-Match') -> <<"If-None-Match">>;
  923. header_to_binary('If-Range') -> <<"If-Range">>;
  924. header_to_binary('If-Unmodified-Since') -> <<"If-Unmodified-Since">>;
  925. header_to_binary('Max-Forwards') -> <<"Max-Forwards">>;
  926. header_to_binary('Proxy-Authorization') -> <<"Proxy-Authorization">>;
  927. header_to_binary('Range') -> <<"Range">>;
  928. header_to_binary('Referer') -> <<"Referer">>;
  929. header_to_binary('User-Agent') -> <<"User-Agent">>;
  930. header_to_binary('Age') -> <<"Age">>;
  931. header_to_binary('Location') -> <<"Location">>;
  932. header_to_binary('Proxy-Authenticate') -> <<"Proxy-Authenticate">>;
  933. header_to_binary('Public') -> <<"Public">>;
  934. header_to_binary('Retry-After') -> <<"Retry-After">>;
  935. header_to_binary('Server') -> <<"Server">>;
  936. header_to_binary('Vary') -> <<"Vary">>;
  937. header_to_binary('Warning') -> <<"Warning">>;
  938. header_to_binary('Www-Authenticate') -> <<"Www-Authenticate">>;
  939. header_to_binary('Allow') -> <<"Allow">>;
  940. header_to_binary('Content-Base') -> <<"Content-Base">>;
  941. header_to_binary('Content-Encoding') -> <<"Content-Encoding">>;
  942. header_to_binary('Content-Language') -> <<"Content-Language">>;
  943. header_to_binary('Content-Length') -> <<"Content-Length">>;
  944. header_to_binary('Content-Location') -> <<"Content-Location">>;
  945. header_to_binary('Content-Md5') -> <<"Content-Md5">>;
  946. header_to_binary('Content-Range') -> <<"Content-Range">>;
  947. header_to_binary('Content-Type') -> <<"Content-Type">>;
  948. header_to_binary('Etag') -> <<"Etag">>;
  949. header_to_binary('Expires') -> <<"Expires">>;
  950. header_to_binary('Last-Modified') -> <<"Last-Modified">>;
  951. header_to_binary('Accept-Ranges') -> <<"Accept-Ranges">>;
  952. header_to_binary('Set-Cookie') -> <<"Set-Cookie">>;
  953. header_to_binary('Set-Cookie2') -> <<"Set-Cookie2">>;
  954. header_to_binary('X-Forwarded-For') -> <<"X-Forwarded-For">>;
  955. header_to_binary('Cookie') -> <<"Cookie">>;
  956. header_to_binary('Keep-Alive') -> <<"Keep-Alive">>;
  957. header_to_binary('Proxy-Connection') -> <<"Proxy-Connection">>;
  958. header_to_binary(B) when is_binary(B) -> B.