cowboy_http.erl 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297
  1. %% Copyright (c) 2011-2013, 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 Core HTTP parsing API.
  16. -module(cowboy_http).
  17. %% Parsing.
  18. -export([list/2]).
  19. -export([nonempty_list/2]).
  20. -export([cookie_list/1]).
  21. -export([content_type/1]).
  22. -export([media_range/2]).
  23. -export([conneg/2]).
  24. -export([language_range/2]).
  25. -export([entity_tag_match/1]).
  26. -export([expectation/2]).
  27. -export([params/2]).
  28. -export([http_date/1]).
  29. -export([rfc1123_date/1]).
  30. -export([rfc850_date/1]).
  31. -export([asctime_date/1]).
  32. -export([whitespace/2]).
  33. -export([digits/1]).
  34. -export([token/2]).
  35. -export([token_ci/2]).
  36. -export([quoted_string/2]).
  37. %% Decoding.
  38. -export([te_chunked/2]).
  39. -export([te_identity/2]).
  40. -export([ce_identity/1]).
  41. %% Interpretation.
  42. -export([cookie_to_iodata/3]).
  43. -export([version_to_binary/1]).
  44. -export([urldecode/1]).
  45. -export([urldecode/2]).
  46. -export([urlencode/1]).
  47. -export([urlencode/2]).
  48. -export([x_www_form_urlencoded/1]).
  49. -type version() :: {Major::non_neg_integer(), Minor::non_neg_integer()}.
  50. -type headers() :: [{binary(), iodata()}].
  51. -type status() :: non_neg_integer() | binary().
  52. -export_type([version/0]).
  53. -export_type([headers/0]).
  54. -export_type([status/0]).
  55. -ifdef(TEST).
  56. -include_lib("eunit/include/eunit.hrl").
  57. -endif.
  58. %% Parsing.
  59. %% @doc Parse a non-empty list of the given type.
  60. -spec nonempty_list(binary(), fun()) -> [any(), ...] | {error, badarg}.
  61. nonempty_list(Data, Fun) ->
  62. case list(Data, Fun, []) of
  63. {error, badarg} -> {error, badarg};
  64. [] -> {error, badarg};
  65. L -> lists:reverse(L)
  66. end.
  67. %% @doc Parse a list of the given type.
  68. -spec list(binary(), fun()) -> list() | {error, badarg}.
  69. list(Data, Fun) ->
  70. case list(Data, Fun, []) of
  71. {error, badarg} -> {error, badarg};
  72. L -> lists:reverse(L)
  73. end.
  74. -spec list(binary(), fun(), [binary()]) -> [any()] | {error, badarg}.
  75. %% From the RFC:
  76. %% <blockquote>Wherever this construct is used, null elements are allowed,
  77. %% but do not contribute to the count of elements present.
  78. %% That is, "(element), , (element) " is permitted, but counts
  79. %% as only two elements. Therefore, where at least one element is required,
  80. %% at least one non-null element MUST be present.</blockquote>
  81. list(Data, Fun, Acc) ->
  82. whitespace(Data,
  83. fun (<<>>) -> Acc;
  84. (<< $,, Rest/binary >>) -> list(Rest, Fun, Acc);
  85. (Rest) -> Fun(Rest,
  86. fun (D, I) -> whitespace(D,
  87. fun (<<>>) -> [I|Acc];
  88. (<< $,, R/binary >>) -> list(R, Fun, [I|Acc]);
  89. (_Any) -> {error, badarg}
  90. end)
  91. end)
  92. end).
  93. %% @doc Parse a list of cookies.
  94. %%
  95. %% We need a special function for this because we need to support both
  96. %% $; and $, as separators as per RFC2109.
  97. -spec cookie_list(binary()) -> [{binary(), binary()}] | {error, badarg}.
  98. cookie_list(Data) ->
  99. case cookie_list(Data, []) of
  100. {error, badarg} -> {error, badarg};
  101. [] -> {error, badarg};
  102. L -> lists:reverse(L)
  103. end.
  104. -spec cookie_list(binary(), Acc) -> Acc | {error, badarg}
  105. when Acc::[{binary(), binary()}].
  106. cookie_list(Data, Acc) ->
  107. whitespace(Data,
  108. fun (<<>>) -> Acc;
  109. (<< $,, Rest/binary >>) -> cookie_list(Rest, Acc);
  110. (<< $;, Rest/binary >>) -> cookie_list(Rest, Acc);
  111. (Rest) -> cookie(Rest,
  112. fun (Rest2, << $$, _/bits >>, _) ->
  113. cookie_list(Rest2, Acc);
  114. (Rest2, Name, Value) ->
  115. cookie_list(Rest2, [{Name, Value}|Acc])
  116. end)
  117. end).
  118. -spec cookie(binary(), fun()) -> any().
  119. cookie(Data, Fun) ->
  120. whitespace(Data,
  121. fun (Rest) ->
  122. cookie_name(Rest,
  123. fun (_Rest2, <<>>) -> {error, badarg};
  124. (<< $=, Rest2/binary >>, Name) ->
  125. cookie_value(Rest2,
  126. fun (Rest3, Value) ->
  127. Fun(Rest3, Name, Value)
  128. end);
  129. (_Rest2, _Attr) -> {error, badarg}
  130. end)
  131. end).
  132. -spec cookie_name(binary(), fun()) -> any().
  133. cookie_name(Data, Fun) ->
  134. cookie_name(Data, Fun, <<>>).
  135. -spec cookie_name(binary(), fun(), binary()) -> any().
  136. cookie_name(<<>>, Fun, Acc) ->
  137. Fun(<<>>, Acc);
  138. cookie_name(Data = << C, _Rest/binary >>, Fun, Acc)
  139. when C =:= $=; C =:= $,; C =:= $;; C =:= $\s; C =:= $\t;
  140. C =:= $\r; C =:= $\n; C =:= $\013; C =:= $\014 ->
  141. Fun(Data, Acc);
  142. cookie_name(<< C, Rest/binary >>, Fun, Acc) ->
  143. C2 = cowboy_bstr:char_to_lower(C),
  144. cookie_name(Rest, Fun, << Acc/binary, C2 >>).
  145. -spec cookie_value(binary(), fun()) -> any().
  146. cookie_value(Data, Fun) ->
  147. cookie_value(Data, Fun, <<>>).
  148. -spec cookie_value(binary(), fun(), binary()) -> any().
  149. cookie_value(<<>>, Fun, Acc) ->
  150. Fun(<<>>, Acc);
  151. cookie_value(Data = << C, _Rest/binary >>, Fun, Acc)
  152. when C =:= $,; C =:= $;; C =:= $\s; C =:= $\t;
  153. C =:= $\r; C =:= $\n; C =:= $\013; C =:= $\014 ->
  154. Fun(Data, Acc);
  155. cookie_value(<< C, Rest/binary >>, Fun, Acc) ->
  156. cookie_value(Rest, Fun, << Acc/binary, C >>).
  157. %% @doc Parse a content type.
  158. -spec content_type(binary()) -> any().
  159. content_type(Data) ->
  160. media_type(Data,
  161. fun (Rest, Type, SubType) ->
  162. params(Rest,
  163. fun (<<>>, Params) -> {Type, SubType, Params};
  164. (_Rest2, _) -> {error, badarg}
  165. end)
  166. end).
  167. %% @doc Parse a media range.
  168. -spec media_range(binary(), fun()) -> any().
  169. media_range(Data, Fun) ->
  170. media_type(Data,
  171. fun (Rest, Type, SubType) ->
  172. media_range_params(Rest, Fun, Type, SubType, [])
  173. end).
  174. -spec media_range_params(binary(), fun(), binary(), binary(),
  175. [{binary(), binary()}]) -> any().
  176. media_range_params(Data, Fun, Type, SubType, Acc) ->
  177. whitespace(Data,
  178. fun (<< $;, Rest/binary >>) ->
  179. whitespace(Rest,
  180. fun (Rest2) ->
  181. media_range_param_attr(Rest2, Fun, Type, SubType, Acc)
  182. end);
  183. (Rest) -> Fun(Rest, {{Type, SubType, lists:reverse(Acc)}, 1000, []})
  184. end).
  185. -spec media_range_param_attr(binary(), fun(), binary(), binary(),
  186. [{binary(), binary()}]) -> any().
  187. media_range_param_attr(Data, Fun, Type, SubType, Acc) ->
  188. token_ci(Data,
  189. fun (_Rest, <<>>) -> {error, badarg};
  190. (<< $=, Rest/binary >>, Attr) ->
  191. media_range_param_value(Rest, Fun, Type, SubType, Acc, Attr)
  192. end).
  193. -spec media_range_param_value(binary(), fun(), binary(), binary(),
  194. [{binary(), binary()}], binary()) -> any().
  195. media_range_param_value(Data, Fun, Type, SubType, Acc, <<"q">>) ->
  196. qvalue(Data,
  197. fun (Rest, Quality) ->
  198. accept_ext(Rest, Fun, Type, SubType, Acc, Quality, [])
  199. end);
  200. media_range_param_value(Data, Fun, Type, SubType, Acc, Attr) ->
  201. word(Data,
  202. fun (Rest, Value) ->
  203. media_range_params(Rest, Fun,
  204. Type, SubType, [{Attr, Value}|Acc])
  205. end).
  206. %% @doc Parse a media type.
  207. -spec media_type(binary(), fun()) -> any().
  208. media_type(Data, Fun) ->
  209. token_ci(Data,
  210. fun (_Rest, <<>>) -> {error, badarg};
  211. (<< $/, Rest/binary >>, Type) ->
  212. token_ci(Rest,
  213. fun (_Rest2, <<>>) -> {error, badarg};
  214. (Rest2, SubType) -> Fun(Rest2, Type, SubType)
  215. end);
  216. %% This is a non-strict parsing clause required by some user agents
  217. %% that use * instead of */* in the list of media types.
  218. (Rest, <<"*">> = Type) ->
  219. token_ci(<<"*", Rest/binary>>,
  220. fun (_Rest2, <<>>) -> {error, badarg};
  221. (Rest2, SubType) -> Fun(Rest2, Type, SubType)
  222. end);
  223. (_Rest, _Type) -> {error, badarg}
  224. end).
  225. -spec accept_ext(binary(), fun(), binary(), binary(),
  226. [{binary(), binary()}], 0..1000,
  227. [{binary(), binary()} | binary()]) -> any().
  228. accept_ext(Data, Fun, Type, SubType, Params, Quality, Acc) ->
  229. whitespace(Data,
  230. fun (<< $;, Rest/binary >>) ->
  231. whitespace(Rest,
  232. fun (Rest2) ->
  233. accept_ext_attr(Rest2, Fun,
  234. Type, SubType, Params, Quality, Acc)
  235. end);
  236. (Rest) ->
  237. Fun(Rest, {{Type, SubType, lists:reverse(Params)},
  238. Quality, lists:reverse(Acc)})
  239. end).
  240. -spec accept_ext_attr(binary(), fun(), binary(), binary(),
  241. [{binary(), binary()}], 0..1000,
  242. [{binary(), binary()} | binary()]) -> any().
  243. accept_ext_attr(Data, Fun, Type, SubType, Params, Quality, Acc) ->
  244. token_ci(Data,
  245. fun (_Rest, <<>>) -> {error, badarg};
  246. (<< $=, Rest/binary >>, Attr) ->
  247. accept_ext_value(Rest, Fun, Type, SubType, Params,
  248. Quality, Acc, Attr);
  249. (Rest, Attr) ->
  250. accept_ext(Rest, Fun, Type, SubType, Params,
  251. Quality, [Attr|Acc])
  252. end).
  253. -spec accept_ext_value(binary(), fun(), binary(), binary(),
  254. [{binary(), binary()}], 0..1000,
  255. [{binary(), binary()} | binary()], binary()) -> any().
  256. accept_ext_value(Data, Fun, Type, SubType, Params, Quality, Acc, Attr) ->
  257. word(Data,
  258. fun (Rest, Value) ->
  259. accept_ext(Rest, Fun,
  260. Type, SubType, Params, Quality, [{Attr, Value}|Acc])
  261. end).
  262. %% @doc Parse a conneg header (Accept-Charset, Accept-Encoding),
  263. %% followed by an optional quality value.
  264. -spec conneg(binary(), fun()) -> any().
  265. conneg(Data, Fun) ->
  266. token_ci(Data,
  267. fun (_Rest, <<>>) -> {error, badarg};
  268. (Rest, Conneg) ->
  269. maybe_qparam(Rest,
  270. fun (Rest2, Quality) ->
  271. Fun(Rest2, {Conneg, Quality})
  272. end)
  273. end).
  274. %% @doc Parse a language range, followed by an optional quality value.
  275. -spec language_range(binary(), fun()) -> any().
  276. language_range(<< $*, Rest/binary >>, Fun) ->
  277. language_range_ret(Rest, Fun, '*');
  278. language_range(Data, Fun) ->
  279. language_tag(Data,
  280. fun (Rest, LanguageTag) ->
  281. language_range_ret(Rest, Fun, LanguageTag)
  282. end).
  283. -spec language_range_ret(binary(), fun(), '*' | {binary(), [binary()]}) -> any().
  284. language_range_ret(Data, Fun, LanguageTag) ->
  285. maybe_qparam(Data,
  286. fun (Rest, Quality) ->
  287. Fun(Rest, {LanguageTag, Quality})
  288. end).
  289. -spec language_tag(binary(), fun()) -> any().
  290. language_tag(Data, Fun) ->
  291. alpha(Data,
  292. fun (_Rest, Tag) when byte_size(Tag) =:= 0; byte_size(Tag) > 8 ->
  293. {error, badarg};
  294. (<< $-, Rest/binary >>, Tag) ->
  295. language_subtag(Rest, Fun, Tag, []);
  296. (Rest, Tag) ->
  297. Fun(Rest, Tag)
  298. end).
  299. -spec language_subtag(binary(), fun(), binary(), [binary()]) -> any().
  300. language_subtag(Data, Fun, Tag, Acc) ->
  301. alpha(Data,
  302. fun (_Rest, SubTag) when byte_size(SubTag) =:= 0;
  303. byte_size(SubTag) > 8 -> {error, badarg};
  304. (<< $-, Rest/binary >>, SubTag) ->
  305. language_subtag(Rest, Fun, Tag, [SubTag|Acc]);
  306. (Rest, SubTag) ->
  307. %% Rebuild the full tag now that we know it's correct
  308. Sub = << << $-, S/binary >> || S <- lists:reverse([SubTag|Acc]) >>,
  309. Fun(Rest, << Tag/binary, Sub/binary >>)
  310. end).
  311. -spec maybe_qparam(binary(), fun()) -> any().
  312. maybe_qparam(Data, Fun) ->
  313. whitespace(Data,
  314. fun (<< $;, Rest/binary >>) ->
  315. whitespace(Rest,
  316. fun (Rest2) ->
  317. %% This is a non-strict parsing clause required by some user agents
  318. %% that use the wrong delimiter putting a charset where a qparam is
  319. %% expected.
  320. try qparam(Rest2, Fun) of
  321. Result -> Result
  322. catch
  323. error:function_clause ->
  324. Fun(<<",", Rest2/binary>>, 1000)
  325. end
  326. end);
  327. (Rest) ->
  328. Fun(Rest, 1000)
  329. end).
  330. %% @doc Parse a quality parameter string (for example q=0.500).
  331. -spec qparam(binary(), fun()) -> any().
  332. qparam(<< Q, $=, Data/binary >>, Fun) when Q =:= $q; Q =:= $Q ->
  333. qvalue(Data, Fun).
  334. %% @doc Parse either a list of entity tags or a "*".
  335. -spec entity_tag_match(binary()) -> any().
  336. entity_tag_match(<< $*, Rest/binary >>) ->
  337. whitespace(Rest,
  338. fun (<<>>) -> '*';
  339. (_Any) -> {error, badarg}
  340. end);
  341. entity_tag_match(Data) ->
  342. nonempty_list(Data, fun entity_tag/2).
  343. %% @doc Parse an entity-tag.
  344. -spec entity_tag(binary(), fun()) -> any().
  345. entity_tag(<< "W/", Rest/binary >>, Fun) ->
  346. opaque_tag(Rest, Fun, weak);
  347. entity_tag(Data, Fun) ->
  348. opaque_tag(Data, Fun, strong).
  349. -spec opaque_tag(binary(), fun(), weak | strong) -> any().
  350. opaque_tag(Data, Fun, Strength) ->
  351. quoted_string(Data,
  352. fun (_Rest, <<>>) -> {error, badarg};
  353. (Rest, OpaqueTag) -> Fun(Rest, {Strength, OpaqueTag})
  354. end).
  355. %% @doc Parse an expectation.
  356. -spec expectation(binary(), fun()) -> any().
  357. expectation(Data, Fun) ->
  358. token_ci(Data,
  359. fun (_Rest, <<>>) -> {error, badarg};
  360. (<< $=, Rest/binary >>, Expectation) ->
  361. word(Rest,
  362. fun (Rest2, ExtValue) ->
  363. params(Rest2, fun (Rest3, ExtParams) ->
  364. Fun(Rest3, {Expectation, ExtValue, ExtParams})
  365. end)
  366. end);
  367. (Rest, Expectation) ->
  368. Fun(Rest, Expectation)
  369. end).
  370. %% @doc Parse a list of parameters (a=b;c=d).
  371. -spec params(binary(), fun()) -> any().
  372. params(Data, Fun) ->
  373. params(Data, Fun, []).
  374. -spec params(binary(), fun(), [{binary(), binary()}]) -> any().
  375. params(Data, Fun, Acc) ->
  376. whitespace(Data,
  377. fun (<< $;, Rest/binary >>) ->
  378. param(Rest,
  379. fun (Rest2, Attr, Value) ->
  380. params(Rest2, Fun, [{Attr, Value}|Acc])
  381. end);
  382. (Rest) ->
  383. Fun(Rest, lists:reverse(Acc))
  384. end).
  385. -spec param(binary(), fun()) -> any().
  386. param(Data, Fun) ->
  387. whitespace(Data,
  388. fun (Rest) ->
  389. token_ci(Rest,
  390. fun (_Rest2, <<>>) -> {error, badarg};
  391. (<< $=, Rest2/binary >>, Attr) ->
  392. word(Rest2,
  393. fun (Rest3, Value) ->
  394. Fun(Rest3, Attr, Value)
  395. end);
  396. (_Rest2, _Attr) -> {error, badarg}
  397. end)
  398. end).
  399. %% @doc Parse an HTTP date (RFC1123, RFC850 or asctime date).
  400. %% @end
  401. %%
  402. %% While this may not be the most efficient date parsing we can do,
  403. %% it should work fine for our purposes because all HTTP dates should
  404. %% be sent as RFC1123 dates in HTTP/1.1.
  405. -spec http_date(binary()) -> any().
  406. http_date(Data) ->
  407. case rfc1123_date(Data) of
  408. {error, badarg} ->
  409. case rfc850_date(Data) of
  410. {error, badarg} ->
  411. case asctime_date(Data) of
  412. {error, badarg} ->
  413. {error, badarg};
  414. HTTPDate ->
  415. HTTPDate
  416. end;
  417. HTTPDate ->
  418. HTTPDate
  419. end;
  420. HTTPDate ->
  421. HTTPDate
  422. end.
  423. %% @doc Parse an RFC1123 date.
  424. -spec rfc1123_date(binary()) -> any().
  425. rfc1123_date(Data) ->
  426. wkday(Data,
  427. fun (<< ", ", Rest/binary >>, _WkDay) ->
  428. date1(Rest,
  429. fun (<< " ", Rest2/binary >>, Date) ->
  430. time(Rest2,
  431. fun (<< " GMT", Rest3/binary >>, Time) ->
  432. http_date_ret(Rest3, {Date, Time});
  433. (_Any, _Time) ->
  434. {error, badarg}
  435. end);
  436. (_Any, _Date) ->
  437. {error, badarg}
  438. end);
  439. (_Any, _WkDay) ->
  440. {error, badarg}
  441. end).
  442. %% @doc Parse an RFC850 date.
  443. -spec rfc850_date(binary()) -> any().
  444. %% From the RFC:
  445. %% HTTP/1.1 clients and caches SHOULD assume that an RFC-850 date
  446. %% which appears to be more than 50 years in the future is in fact
  447. %% in the past (this helps solve the "year 2000" problem).
  448. rfc850_date(Data) ->
  449. weekday(Data,
  450. fun (<< ", ", Rest/binary >>, _WeekDay) ->
  451. date2(Rest,
  452. fun (<< " ", Rest2/binary >>, Date) ->
  453. time(Rest2,
  454. fun (<< " GMT", Rest3/binary >>, Time) ->
  455. http_date_ret(Rest3, {Date, Time});
  456. (_Any, _Time) ->
  457. {error, badarg}
  458. end);
  459. (_Any, _Date) ->
  460. {error, badarg}
  461. end);
  462. (_Any, _WeekDay) ->
  463. {error, badarg}
  464. end).
  465. %% @doc Parse an asctime date.
  466. -spec asctime_date(binary()) -> any().
  467. asctime_date(Data) ->
  468. wkday(Data,
  469. fun (<< " ", Rest/binary >>, _WkDay) ->
  470. date3(Rest,
  471. fun (<< " ", Rest2/binary >>, PartialDate) ->
  472. time(Rest2,
  473. fun (<< " ", Rest3/binary >>, Time) ->
  474. asctime_year(Rest3,
  475. PartialDate, Time);
  476. (_Any, _Time) ->
  477. {error, badarg}
  478. end);
  479. (_Any, _PartialDate) ->
  480. {error, badarg}
  481. end);
  482. (_Any, _WkDay) ->
  483. {error, badarg1}
  484. end).
  485. -spec asctime_year(binary(), tuple(), tuple()) -> any().
  486. asctime_year(<< Y1, Y2, Y3, Y4, Rest/binary >>, {Month, Day}, Time)
  487. when Y1 >= $0, Y1 =< $9, Y2 >= $0, Y2 =< $9,
  488. Y3 >= $0, Y3 =< $9, Y4 >= $0, Y4 =< $9 ->
  489. Year = (Y1 - $0) * 1000 + (Y2 - $0) * 100 + (Y3 - $0) * 10 + (Y4 - $0),
  490. http_date_ret(Rest, {{Year, Month, Day}, Time}).
  491. -spec http_date_ret(binary(), tuple()) -> any().
  492. http_date_ret(Data, DateTime = {Date, _Time}) ->
  493. whitespace(Data,
  494. fun (<<>>) ->
  495. case calendar:valid_date(Date) of
  496. true -> DateTime;
  497. false -> {error, badarg}
  498. end;
  499. (_Any) ->
  500. {error, badarg}
  501. end).
  502. %% We never use it, pretty much just checks the wkday is right.
  503. -spec wkday(binary(), fun()) -> any().
  504. wkday(<< WkDay:3/binary, Rest/binary >>, Fun)
  505. when WkDay =:= <<"Mon">>; WkDay =:= <<"Tue">>; WkDay =:= <<"Wed">>;
  506. WkDay =:= <<"Thu">>; WkDay =:= <<"Fri">>; WkDay =:= <<"Sat">>;
  507. WkDay =:= <<"Sun">> ->
  508. Fun(Rest, WkDay);
  509. wkday(_Any, _Fun) ->
  510. {error, badarg}.
  511. %% We never use it, pretty much just checks the weekday is right.
  512. -spec weekday(binary(), fun()) -> any().
  513. weekday(<< "Monday", Rest/binary >>, Fun) ->
  514. Fun(Rest, <<"Monday">>);
  515. weekday(<< "Tuesday", Rest/binary >>, Fun) ->
  516. Fun(Rest, <<"Tuesday">>);
  517. weekday(<< "Wednesday", Rest/binary >>, Fun) ->
  518. Fun(Rest, <<"Wednesday">>);
  519. weekday(<< "Thursday", Rest/binary >>, Fun) ->
  520. Fun(Rest, <<"Thursday">>);
  521. weekday(<< "Friday", Rest/binary >>, Fun) ->
  522. Fun(Rest, <<"Friday">>);
  523. weekday(<< "Saturday", Rest/binary >>, Fun) ->
  524. Fun(Rest, <<"Saturday">>);
  525. weekday(<< "Sunday", Rest/binary >>, Fun) ->
  526. Fun(Rest, <<"Sunday">>);
  527. weekday(_Any, _Fun) ->
  528. {error, badarg}.
  529. -spec date1(binary(), fun()) -> any().
  530. date1(<< D1, D2, " ", M:3/binary, " ", Y1, Y2, Y3, Y4, Rest/binary >>, Fun)
  531. when D1 >= $0, D1 =< $9, D2 >= $0, D2 =< $9,
  532. Y1 >= $0, Y1 =< $9, Y2 >= $0, Y2 =< $9,
  533. Y3 >= $0, Y3 =< $9, Y4 >= $0, Y4 =< $9 ->
  534. case month(M) of
  535. {error, badarg} ->
  536. {error, badarg};
  537. Month ->
  538. Fun(Rest, {
  539. (Y1 - $0) * 1000 + (Y2 - $0) * 100 + (Y3 - $0) * 10 + (Y4 - $0),
  540. Month,
  541. (D1 - $0) * 10 + (D2 - $0)
  542. })
  543. end;
  544. date1(_Data, _Fun) ->
  545. {error, badarg}.
  546. -spec date2(binary(), fun()) -> any().
  547. date2(<< D1, D2, "-", M:3/binary, "-", Y1, Y2, Rest/binary >>, Fun)
  548. when D1 >= $0, D1 =< $9, D2 >= $0, D2 =< $9,
  549. Y1 >= $0, Y1 =< $9, Y2 >= $0, Y2 =< $9 ->
  550. case month(M) of
  551. {error, badarg} ->
  552. {error, badarg};
  553. Month ->
  554. Year = (Y1 - $0) * 10 + (Y2 - $0),
  555. Year2 = case Year > 50 of
  556. true -> Year + 1900;
  557. false -> Year + 2000
  558. end,
  559. Fun(Rest, {
  560. Year2,
  561. Month,
  562. (D1 - $0) * 10 + (D2 - $0)
  563. })
  564. end;
  565. date2(_Data, _Fun) ->
  566. {error, badarg}.
  567. -spec date3(binary(), fun()) -> any().
  568. date3(<< M:3/binary, " ", D1, D2, Rest/binary >>, Fun)
  569. when (D1 >= $0 andalso D1 =< $3) orelse D1 =:= $\s,
  570. D2 >= $0, D2 =< $9 ->
  571. case month(M) of
  572. {error, badarg} ->
  573. {error, badarg};
  574. Month ->
  575. Day = case D1 of
  576. $\s -> D2 - $0;
  577. D1 -> (D1 - $0) * 10 + (D2 - $0)
  578. end,
  579. Fun(Rest, {Month, Day})
  580. end;
  581. date3(_Data, _Fun) ->
  582. {error, badarg}.
  583. -spec month(<< _:24 >>) -> 1..12 | {error, badarg}.
  584. month(<<"Jan">>) -> 1;
  585. month(<<"Feb">>) -> 2;
  586. month(<<"Mar">>) -> 3;
  587. month(<<"Apr">>) -> 4;
  588. month(<<"May">>) -> 5;
  589. month(<<"Jun">>) -> 6;
  590. month(<<"Jul">>) -> 7;
  591. month(<<"Aug">>) -> 8;
  592. month(<<"Sep">>) -> 9;
  593. month(<<"Oct">>) -> 10;
  594. month(<<"Nov">>) -> 11;
  595. month(<<"Dec">>) -> 12;
  596. month(_Any) -> {error, badarg}.
  597. -spec time(binary(), fun()) -> any().
  598. time(<< H1, H2, ":", M1, M2, ":", S1, S2, Rest/binary >>, Fun)
  599. when H1 >= $0, H1 =< $2, H2 >= $0, H2 =< $9,
  600. M1 >= $0, M1 =< $5, M2 >= $0, M2 =< $9,
  601. S1 >= $0, S1 =< $5, S2 >= $0, S2 =< $9 ->
  602. Hour = (H1 - $0) * 10 + (H2 - $0),
  603. case Hour < 24 of
  604. true ->
  605. Time = {
  606. Hour,
  607. (M1 - $0) * 10 + (M2 - $0),
  608. (S1 - $0) * 10 + (S2 - $0)
  609. },
  610. Fun(Rest, Time);
  611. false ->
  612. {error, badarg}
  613. end.
  614. %% @doc Skip whitespace.
  615. -spec whitespace(binary(), fun()) -> any().
  616. whitespace(<< C, Rest/binary >>, Fun)
  617. when C =:= $\s; C =:= $\t ->
  618. whitespace(Rest, Fun);
  619. whitespace(Data, Fun) ->
  620. Fun(Data).
  621. %% @doc Parse a list of digits as a non negative integer.
  622. -spec digits(binary()) -> non_neg_integer() | {error, badarg}.
  623. digits(Data) ->
  624. digits(Data,
  625. fun (Rest, I) ->
  626. whitespace(Rest,
  627. fun (<<>>) ->
  628. I;
  629. (_Rest2) ->
  630. {error, badarg}
  631. end)
  632. end).
  633. -spec digits(binary(), fun()) -> any().
  634. digits(<< C, Rest/binary >>, Fun)
  635. when C >= $0, C =< $9 ->
  636. digits(Rest, Fun, C - $0);
  637. digits(_Data, _Fun) ->
  638. {error, badarg}.
  639. -spec digits(binary(), fun(), non_neg_integer()) -> any().
  640. digits(<< C, Rest/binary >>, Fun, Acc)
  641. when C >= $0, C =< $9 ->
  642. digits(Rest, Fun, Acc * 10 + (C - $0));
  643. digits(Data, Fun, Acc) ->
  644. Fun(Data, Acc).
  645. %% @doc Parse a list of case-insensitive alpha characters.
  646. %%
  647. %% Changes all characters to lowercase.
  648. -spec alpha(binary(), fun()) -> any().
  649. alpha(Data, Fun) ->
  650. alpha(Data, Fun, <<>>).
  651. -spec alpha(binary(), fun(), binary()) -> any().
  652. alpha(<<>>, Fun, Acc) ->
  653. Fun(<<>>, Acc);
  654. alpha(<< C, Rest/binary >>, Fun, Acc)
  655. when C >= $a andalso C =< $z;
  656. C >= $A andalso C =< $Z ->
  657. C2 = cowboy_bstr:char_to_lower(C),
  658. alpha(Rest, Fun, << Acc/binary, C2 >>);
  659. alpha(Data, Fun, Acc) ->
  660. Fun(Data, Acc).
  661. %% @doc Parse either a token or a quoted string.
  662. -spec word(binary(), fun()) -> any().
  663. word(Data = << $", _/binary >>, Fun) ->
  664. quoted_string(Data, Fun);
  665. word(Data, Fun) ->
  666. token(Data,
  667. fun (_Rest, <<>>) -> {error, badarg};
  668. (Rest, Token) -> Fun(Rest, Token)
  669. end).
  670. %% @doc Parse a case-insensitive token.
  671. %%
  672. %% Changes all characters to lowercase.
  673. -spec token_ci(binary(), fun()) -> any().
  674. token_ci(Data, Fun) ->
  675. token(Data, Fun, ci, <<>>).
  676. %% @doc Parse a token.
  677. -spec token(binary(), fun()) -> any().
  678. token(Data, Fun) ->
  679. token(Data, Fun, cs, <<>>).
  680. -spec token(binary(), fun(), ci | cs, binary()) -> any().
  681. token(<<>>, Fun, _Case, Acc) ->
  682. Fun(<<>>, Acc);
  683. token(Data = << C, _Rest/binary >>, Fun, _Case, Acc)
  684. when C =:= $(; C =:= $); C =:= $<; C =:= $>; C =:= $@;
  685. C =:= $,; C =:= $;; C =:= $:; C =:= $\\; C =:= $";
  686. C =:= $/; C =:= $[; C =:= $]; C =:= $?; C =:= $=;
  687. C =:= ${; C =:= $}; C =:= $\s; C =:= $\t;
  688. C < 32; C =:= 127 ->
  689. Fun(Data, Acc);
  690. token(<< C, Rest/binary >>, Fun, Case = ci, Acc) ->
  691. C2 = cowboy_bstr:char_to_lower(C),
  692. token(Rest, Fun, Case, << Acc/binary, C2 >>);
  693. token(<< C, Rest/binary >>, Fun, Case, Acc) ->
  694. token(Rest, Fun, Case, << Acc/binary, C >>).
  695. %% @doc Parse a quoted string.
  696. -spec quoted_string(binary(), fun()) -> any().
  697. quoted_string(<< $", Rest/binary >>, Fun) ->
  698. quoted_string(Rest, Fun, <<>>).
  699. -spec quoted_string(binary(), fun(), binary()) -> any().
  700. quoted_string(<<>>, _Fun, _Acc) ->
  701. {error, badarg};
  702. quoted_string(<< $", Rest/binary >>, Fun, Acc) ->
  703. Fun(Rest, Acc);
  704. quoted_string(<< $\\, C, Rest/binary >>, Fun, Acc) ->
  705. quoted_string(Rest, Fun, << Acc/binary, C >>);
  706. quoted_string(<< C, Rest/binary >>, Fun, Acc) ->
  707. quoted_string(Rest, Fun, << Acc/binary, C >>).
  708. %% @doc Parse a quality value.
  709. -spec qvalue(binary(), fun()) -> any().
  710. qvalue(<< $0, $., Rest/binary >>, Fun) ->
  711. qvalue(Rest, Fun, 0, 100);
  712. %% Some user agents use q=.x instead of q=0.x
  713. qvalue(<< $., Rest/binary >>, Fun) ->
  714. qvalue(Rest, Fun, 0, 100);
  715. qvalue(<< $0, Rest/binary >>, Fun) ->
  716. Fun(Rest, 0);
  717. qvalue(<< $1, $., $0, $0, $0, Rest/binary >>, Fun) ->
  718. Fun(Rest, 1000);
  719. qvalue(<< $1, $., $0, $0, Rest/binary >>, Fun) ->
  720. Fun(Rest, 1000);
  721. qvalue(<< $1, $., $0, Rest/binary >>, Fun) ->
  722. Fun(Rest, 1000);
  723. qvalue(<< $1, Rest/binary >>, Fun) ->
  724. Fun(Rest, 1000);
  725. qvalue(_Data, _Fun) ->
  726. {error, badarg}.
  727. -spec qvalue(binary(), fun(), integer(), 1 | 10 | 100) -> any().
  728. qvalue(Data, Fun, Q, 0) ->
  729. Fun(Data, Q);
  730. qvalue(<< C, Rest/binary >>, Fun, Q, M)
  731. when C >= $0, C =< $9 ->
  732. qvalue(Rest, Fun, Q + (C - $0) * M, M div 10);
  733. qvalue(Data, Fun, Q, _M) ->
  734. Fun(Data, Q).
  735. %% Decoding.
  736. %% @doc Decode a stream of chunks.
  737. -spec te_chunked(Bin, TransferState)
  738. -> more | {more, non_neg_integer(), Bin, TransferState}
  739. | {ok, Bin, TransferState} | {ok, Bin, Bin, TransferState}
  740. | {done, non_neg_integer(), Bin} | {error, badarg}
  741. when Bin::binary(), TransferState::{non_neg_integer(), non_neg_integer()}.
  742. te_chunked(<< "0\r\n\r\n", Rest/binary >>, {0, Streamed}) ->
  743. {done, Streamed, Rest};
  744. te_chunked(Data, {0, Streamed}) ->
  745. %% @todo We are expecting an hex size, not a general token.
  746. token(Data,
  747. fun (<< "\r\n", Rest/binary >>, BinLen) ->
  748. Len = list_to_integer(binary_to_list(BinLen), 16),
  749. te_chunked(Rest, {Len, Streamed});
  750. %% Chunk size shouldn't take too many bytes,
  751. %% don't try to stream forever.
  752. (Rest, _) when byte_size(Rest) < 16 ->
  753. more;
  754. (_, _) ->
  755. {error, badarg}
  756. end);
  757. te_chunked(Data, {ChunkRem, Streamed}) when byte_size(Data) >= ChunkRem + 2 ->
  758. << Chunk:ChunkRem/binary, "\r\n", Rest/binary >> = Data,
  759. {ok, Chunk, Rest, {0, Streamed + byte_size(Chunk)}};
  760. te_chunked(Data, {ChunkRem, Streamed}) ->
  761. {more, ChunkRem + 2, Data, {ChunkRem, Streamed}}.
  762. %% @doc Decode an identity stream.
  763. -spec te_identity(Bin, TransferState)
  764. -> {ok, Bin, TransferState} | {done, Bin, non_neg_integer(), Bin}
  765. when Bin::binary(), TransferState::{non_neg_integer(), non_neg_integer()}.
  766. te_identity(Data, {Streamed, Total})
  767. when Streamed + byte_size(Data) < Total ->
  768. {ok, Data, {Streamed + byte_size(Data), Total}};
  769. te_identity(Data, {Streamed, Total}) ->
  770. Size = Total - Streamed,
  771. << Data2:Size/binary, Rest/binary >> = Data,
  772. {done, Data2, Total, Rest}.
  773. %% @doc Decode an identity content.
  774. -spec ce_identity(binary()) -> {ok, binary()}.
  775. ce_identity(Data) ->
  776. {ok, Data}.
  777. %% Interpretation.
  778. %% @doc Convert a cookie name, value and options to its iodata form.
  779. %% @end
  780. %%
  781. %% Initially from Mochiweb:
  782. %% * Copyright 2007 Mochi Media, Inc.
  783. %% Initial binary implementation:
  784. %% * Copyright 2011 Thomas Burdick <thomas.burdick@gmail.com>
  785. -spec cookie_to_iodata(iodata(), iodata(), cowboy_req:cookie_opts())
  786. -> iodata().
  787. cookie_to_iodata(Name, Value, Opts) ->
  788. case binary:match(iolist_to_binary(Name), [<<$=>>, <<$,>>, <<$;>>,
  789. <<$\s>>, <<$\t>>, <<$\r>>, <<$\n>>, <<$\013>>, <<$\014>>]) of
  790. nomatch -> ok
  791. end,
  792. case binary:match(iolist_to_binary(Value), [<<$,>>, <<$;>>,
  793. <<$\s>>, <<$\t>>, <<$\r>>, <<$\n>>, <<$\013>>, <<$\014>>]) of
  794. nomatch -> ok
  795. end,
  796. MaxAgeBin = case lists:keyfind(max_age, 1, Opts) of
  797. false -> <<>>;
  798. {_, 0} ->
  799. %% MSIE requires an Expires date in the past to delete a cookie.
  800. <<"; Expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0">>;
  801. {_, MaxAge} when is_integer(MaxAge), MaxAge > 0 ->
  802. UTC = calendar:universal_time(),
  803. Secs = calendar:datetime_to_gregorian_seconds(UTC),
  804. Expires = calendar:gregorian_seconds_to_datetime(Secs + MaxAge),
  805. [<<"; Expires=">>, cowboy_clock:rfc2109(Expires),
  806. <<"; Max-Age=">>, integer_to_list(MaxAge)]
  807. end,
  808. DomainBin = case lists:keyfind(domain, 1, Opts) of
  809. false -> <<>>;
  810. {_, Domain} -> [<<"; Domain=">>, Domain]
  811. end,
  812. PathBin = case lists:keyfind(path, 1, Opts) of
  813. false -> <<>>;
  814. {_, Path} -> [<<"; Path=">>, Path]
  815. end,
  816. SecureBin = case lists:keyfind(secure, 1, Opts) of
  817. false -> <<>>;
  818. {_, true} -> <<"; Secure">>
  819. end,
  820. HttpOnlyBin = case lists:keyfind(http_only, 1, Opts) of
  821. false -> <<>>;
  822. {_, true} -> <<"; HttpOnly">>
  823. end,
  824. [Name, <<"=">>, Value, <<"; Version=1">>,
  825. MaxAgeBin, DomainBin, PathBin, SecureBin, HttpOnlyBin].
  826. %% @doc Convert an HTTP version tuple to its binary form.
  827. -spec version_to_binary(version()) -> binary().
  828. version_to_binary({1, 1}) -> <<"HTTP/1.1">>;
  829. version_to_binary({1, 0}) -> <<"HTTP/1.0">>.
  830. %% @doc Decode a URL encoded binary.
  831. %% @equiv urldecode(Bin, crash)
  832. -spec urldecode(binary()) -> binary().
  833. urldecode(Bin) when is_binary(Bin) ->
  834. urldecode(Bin, <<>>, crash).
  835. %% @doc Decode a URL encoded binary.
  836. %% The second argument specifies how to handle percent characters that are not
  837. %% followed by two valid hex characters. Use `skip' to ignore such errors,
  838. %% if `crash' is used the function will fail with the reason `badarg'.
  839. -spec urldecode(binary(), crash | skip) -> binary().
  840. urldecode(Bin, OnError) when is_binary(Bin) ->
  841. urldecode(Bin, <<>>, OnError).
  842. -spec urldecode(binary(), binary(), crash | skip) -> binary().
  843. urldecode(<<$%, H, L, Rest/binary>>, Acc, OnError) ->
  844. G = unhex(H),
  845. M = unhex(L),
  846. if G =:= error; M =:= error ->
  847. case OnError of skip -> ok; crash -> erlang:error(badarg) end,
  848. urldecode(<<H, L, Rest/binary>>, <<Acc/binary, $%>>, OnError);
  849. true ->
  850. urldecode(Rest, <<Acc/binary, (G bsl 4 bor M)>>, OnError)
  851. end;
  852. urldecode(<<$%, Rest/binary>>, Acc, OnError) ->
  853. case OnError of skip -> ok; crash -> erlang:error(badarg) end,
  854. urldecode(Rest, <<Acc/binary, $%>>, OnError);
  855. urldecode(<<$+, Rest/binary>>, Acc, OnError) ->
  856. urldecode(Rest, <<Acc/binary, $ >>, OnError);
  857. urldecode(<<C, Rest/binary>>, Acc, OnError) ->
  858. urldecode(Rest, <<Acc/binary, C>>, OnError);
  859. urldecode(<<>>, Acc, _OnError) ->
  860. Acc.
  861. -spec unhex(byte()) -> byte() | error.
  862. unhex(C) when C >= $0, C =< $9 -> C - $0;
  863. unhex(C) when C >= $A, C =< $F -> C - $A + 10;
  864. unhex(C) when C >= $a, C =< $f -> C - $a + 10;
  865. unhex(_) -> error.
  866. %% @doc URL encode a string binary.
  867. %% @equiv urlencode(Bin, [])
  868. -spec urlencode(binary()) -> binary().
  869. urlencode(Bin) ->
  870. urlencode(Bin, []).
  871. %% @doc URL encode a string binary.
  872. %% The `noplus' option disables the default behaviour of quoting space
  873. %% characters, `\s', as `+'. The `upper' option overrides the default behaviour
  874. %% of writing hex numbers using lowecase letters to using uppercase letters
  875. %% instead.
  876. -spec urlencode(binary(), [noplus|upper]) -> binary().
  877. urlencode(Bin, Opts) ->
  878. Plus = not lists:member(noplus, Opts),
  879. Upper = lists:member(upper, Opts),
  880. urlencode(Bin, <<>>, Plus, Upper).
  881. -spec urlencode(binary(), binary(), boolean(), boolean()) -> binary().
  882. urlencode(<<C, Rest/binary>>, Acc, P=Plus, U=Upper) ->
  883. if C >= $0, C =< $9 -> urlencode(Rest, <<Acc/binary, C>>, P, U);
  884. C >= $A, C =< $Z -> urlencode(Rest, <<Acc/binary, C>>, P, U);
  885. C >= $a, C =< $z -> urlencode(Rest, <<Acc/binary, C>>, P, U);
  886. C =:= $.; C =:= $-; C =:= $~; C =:= $_ ->
  887. urlencode(Rest, <<Acc/binary, C>>, P, U);
  888. C =:= $ , Plus ->
  889. urlencode(Rest, <<Acc/binary, $+>>, P, U);
  890. true ->
  891. H = C band 16#F0 bsr 4, L = C band 16#0F,
  892. H1 = if Upper -> tohexu(H); true -> tohexl(H) end,
  893. L1 = if Upper -> tohexu(L); true -> tohexl(L) end,
  894. urlencode(Rest, <<Acc/binary, $%, H1, L1>>, P, U)
  895. end;
  896. urlencode(<<>>, Acc, _Plus, _Upper) ->
  897. Acc.
  898. -spec tohexu(byte()) -> byte().
  899. tohexu(C) when C < 10 -> $0 + C;
  900. tohexu(C) when C < 17 -> $A + C - 10.
  901. -spec tohexl(byte()) -> byte().
  902. tohexl(C) when C < 10 -> $0 + C;
  903. tohexl(C) when C < 17 -> $a + C - 10.
  904. -spec x_www_form_urlencoded(binary()) -> list({binary(), binary() | true}).
  905. x_www_form_urlencoded(<<>>) ->
  906. [];
  907. x_www_form_urlencoded(Qs) ->
  908. Tokens = binary:split(Qs, <<"&">>, [global, trim]),
  909. [case binary:split(Token, <<"=">>) of
  910. [Token] -> {urldecode(Token), true};
  911. [Name, Value] -> {urldecode(Name), urldecode(Value)}
  912. end || Token <- Tokens].
  913. %% Tests.
  914. -ifdef(TEST).
  915. nonempty_charset_list_test_() ->
  916. %% {Value, Result}
  917. Tests = [
  918. {<<>>, {error, badarg}},
  919. {<<"iso-8859-5, unicode-1-1;q=0.8">>, [
  920. {<<"iso-8859-5">>, 1000},
  921. {<<"unicode-1-1">>, 800}
  922. ]},
  923. %% Some user agents send this invalid value for the Accept-Charset header
  924. {<<"ISO-8859-1;utf-8;q=0.7,*;q=0.7">>, [
  925. {<<"iso-8859-1">>, 1000},
  926. {<<"utf-8">>, 700},
  927. {<<"*">>, 700}
  928. ]}
  929. ],
  930. [{V, fun() -> R = nonempty_list(V, fun conneg/2) end} || {V, R} <- Tests].
  931. nonempty_language_range_list_test_() ->
  932. %% {Value, Result}
  933. Tests = [
  934. {<<"da, en-gb;q=0.8, en;q=0.7">>, [
  935. {<<"da">>, 1000},
  936. {<<"en-gb">>, 800},
  937. {<<"en">>, 700}
  938. ]},
  939. {<<"en, en-US, en-cockney, i-cherokee, x-pig-latin">>, [
  940. {<<"en">>, 1000},
  941. {<<"en-us">>, 1000},
  942. {<<"en-cockney">>, 1000},
  943. {<<"i-cherokee">>, 1000},
  944. {<<"x-pig-latin">>, 1000}
  945. ]}
  946. ],
  947. [{V, fun() -> R = nonempty_list(V, fun language_range/2) end}
  948. || {V, R} <- Tests].
  949. nonempty_token_list_test_() ->
  950. %% {Value, Result}
  951. Tests = [
  952. {<<>>, {error, badarg}},
  953. {<<" ">>, {error, badarg}},
  954. {<<" , ">>, {error, badarg}},
  955. {<<",,,">>, {error, badarg}},
  956. {<<"a b">>, {error, badarg}},
  957. {<<"a , , , ">>, [<<"a">>]},
  958. {<<" , , , a">>, [<<"a">>]},
  959. {<<"a, , b">>, [<<"a">>, <<"b">>]},
  960. {<<"close">>, [<<"close">>]},
  961. {<<"keep-alive, upgrade">>, [<<"keep-alive">>, <<"upgrade">>]}
  962. ],
  963. [{V, fun() -> R = nonempty_list(V, fun token/2) end} || {V, R} <- Tests].
  964. cookie_list_test_() ->
  965. %% {Value, Result}.
  966. Tests = [
  967. {<<"name=value; name2=value2">>, [
  968. {<<"name">>, <<"value">>},
  969. {<<"name2">>, <<"value2">>}
  970. ]},
  971. {<<"$Version=1; Customer=WILE_E_COYOTE; $Path=/acme">>, [
  972. {<<"customer">>, <<"WILE_E_COYOTE">>}
  973. ]},
  974. {<<"$Version=1; Customer=WILE_E_COYOTE; $Path=/acme; "
  975. "Part_Number=Rocket_Launcher_0001; $Path=/acme; "
  976. "Shipping=FedEx; $Path=/acme">>, [
  977. {<<"customer">>, <<"WILE_E_COYOTE">>},
  978. {<<"part_number">>, <<"Rocket_Launcher_0001">>},
  979. {<<"shipping">>, <<"FedEx">>}
  980. ]},
  981. %% Potential edge cases (initially from Mochiweb).
  982. {<<"foo=\\x">>, [{<<"foo">>, <<"\\x">>}]},
  983. {<<"=">>, {error, badarg}},
  984. {<<" foo ; bar ">>, {error, badarg}},
  985. {<<"foo=;bar=">>, [{<<"foo">>, <<>>}, {<<"bar">>, <<>>}]},
  986. {<<"foo=\\\";;bar ">>, {error, badarg}},
  987. {<<"foo=\\\";;bar=good ">>,
  988. [{<<"foo">>, <<"\\\"">>}, {<<"bar">>, <<"good">>}]},
  989. {<<"foo=\"\\\";bar">>, {error, badarg}},
  990. {<<"">>, {error, badarg}},
  991. {<<"foo=bar , baz=wibble ">>,
  992. [{<<"foo">>, <<"bar">>}, {<<"baz">>, <<"wibble">>}]}
  993. ],
  994. [{V, fun() -> R = cookie_list(V) end} || {V, R} <- Tests].
  995. media_range_list_test_() ->
  996. %% {Tokens, Result}
  997. Tests = [
  998. {<<"audio/*; q=0.2, audio/basic">>, [
  999. {{<<"audio">>, <<"*">>, []}, 200, []},
  1000. {{<<"audio">>, <<"basic">>, []}, 1000, []}
  1001. ]},
  1002. {<<"text/plain; q=0.5, text/html, "
  1003. "text/x-dvi; q=0.8, text/x-c">>, [
  1004. {{<<"text">>, <<"plain">>, []}, 500, []},
  1005. {{<<"text">>, <<"html">>, []}, 1000, []},
  1006. {{<<"text">>, <<"x-dvi">>, []}, 800, []},
  1007. {{<<"text">>, <<"x-c">>, []}, 1000, []}
  1008. ]},
  1009. {<<"text/*, text/html, text/html;level=1, */*">>, [
  1010. {{<<"text">>, <<"*">>, []}, 1000, []},
  1011. {{<<"text">>, <<"html">>, []}, 1000, []},
  1012. {{<<"text">>, <<"html">>, [{<<"level">>, <<"1">>}]}, 1000, []},
  1013. {{<<"*">>, <<"*">>, []}, 1000, []}
  1014. ]},
  1015. {<<"text/*;q=0.3, text/html;q=0.7, text/html;level=1, "
  1016. "text/html;level=2;q=0.4, */*;q=0.5">>, [
  1017. {{<<"text">>, <<"*">>, []}, 300, []},
  1018. {{<<"text">>, <<"html">>, []}, 700, []},
  1019. {{<<"text">>, <<"html">>, [{<<"level">>, <<"1">>}]}, 1000, []},
  1020. {{<<"text">>, <<"html">>, [{<<"level">>, <<"2">>}]}, 400, []},
  1021. {{<<"*">>, <<"*">>, []}, 500, []}
  1022. ]},
  1023. {<<"text/html;level=1;quoted=\"hi hi hi\";"
  1024. "q=0.123;standalone;complex=gits, text/plain">>, [
  1025. {{<<"text">>, <<"html">>,
  1026. [{<<"level">>, <<"1">>}, {<<"quoted">>, <<"hi hi hi">>}]}, 123,
  1027. [<<"standalone">>, {<<"complex">>, <<"gits">>}]},
  1028. {{<<"text">>, <<"plain">>, []}, 1000, []}
  1029. ]},
  1030. {<<"text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2">>, [
  1031. {{<<"text">>, <<"html">>, []}, 1000, []},
  1032. {{<<"image">>, <<"gif">>, []}, 1000, []},
  1033. {{<<"image">>, <<"jpeg">>, []}, 1000, []},
  1034. {{<<"*">>, <<"*">>, []}, 200, []},
  1035. {{<<"*">>, <<"*">>, []}, 200, []}
  1036. ]}
  1037. ],
  1038. [{V, fun() -> R = list(V, fun media_range/2) end} || {V, R} <- Tests].
  1039. entity_tag_match_test_() ->
  1040. %% {Tokens, Result}
  1041. Tests = [
  1042. {<<"\"xyzzy\"">>, [{strong, <<"xyzzy">>}]},
  1043. {<<"\"xyzzy\", W/\"r2d2xxxx\", \"c3piozzzz\"">>,
  1044. [{strong, <<"xyzzy">>},
  1045. {weak, <<"r2d2xxxx">>},
  1046. {strong, <<"c3piozzzz">>}]},
  1047. {<<"*">>, '*'}
  1048. ],
  1049. [{V, fun() -> R = entity_tag_match(V) end} || {V, R} <- Tests].
  1050. http_date_test_() ->
  1051. %% {Tokens, Result}
  1052. Tests = [
  1053. {<<"Sun, 06 Nov 1994 08:49:37 GMT">>, {{1994, 11, 6}, {8, 49, 37}}},
  1054. {<<"Sunday, 06-Nov-94 08:49:37 GMT">>, {{1994, 11, 6}, {8, 49, 37}}},
  1055. {<<"Sun Nov 6 08:49:37 1994">>, {{1994, 11, 6}, {8, 49, 37}}}
  1056. ],
  1057. [{V, fun() -> R = http_date(V) end} || {V, R} <- Tests].
  1058. rfc1123_date_test_() ->
  1059. %% {Tokens, Result}
  1060. Tests = [
  1061. {<<"Sun, 06 Nov 1994 08:49:37 GMT">>, {{1994, 11, 6}, {8, 49, 37}}}
  1062. ],
  1063. [{V, fun() -> R = rfc1123_date(V) end} || {V, R} <- Tests].
  1064. rfc850_date_test_() ->
  1065. %% {Tokens, Result}
  1066. Tests = [
  1067. {<<"Sunday, 06-Nov-94 08:49:37 GMT">>, {{1994, 11, 6}, {8, 49, 37}}}
  1068. ],
  1069. [{V, fun() -> R = rfc850_date(V) end} || {V, R} <- Tests].
  1070. asctime_date_test_() ->
  1071. %% {Tokens, Result}
  1072. Tests = [
  1073. {<<"Sun Nov 6 08:49:37 1994">>, {{1994, 11, 6}, {8, 49, 37}}}
  1074. ],
  1075. [{V, fun() -> R = asctime_date(V) end} || {V, R} <- Tests].
  1076. content_type_test_() ->
  1077. %% {ContentType, Result}
  1078. Tests = [
  1079. {<<"text/plain; charset=iso-8859-4">>,
  1080. {<<"text">>, <<"plain">>, [{<<"charset">>, <<"iso-8859-4">>}]}},
  1081. {<<"multipart/form-data \t;Boundary=\"MultipartIsUgly\"">>,
  1082. {<<"multipart">>, <<"form-data">>, [
  1083. {<<"boundary">>, <<"MultipartIsUgly">>}
  1084. ]}},
  1085. {<<"foo/bar; one=FirstParam; two=SecondParam">>,
  1086. {<<"foo">>, <<"bar">>, [
  1087. {<<"one">>, <<"FirstParam">>},
  1088. {<<"two">>, <<"SecondParam">>}
  1089. ]}}
  1090. ],
  1091. [{V, fun () -> R = content_type(V) end} || {V, R} <- Tests].
  1092. digits_test_() ->
  1093. %% {Digits, Result}
  1094. Tests = [
  1095. {<<"42 ">>, 42},
  1096. {<<"69\t">>, 69},
  1097. {<<"1337">>, 1337}
  1098. ],
  1099. [{V, fun() -> R = digits(V) end} || {V, R} <- Tests].
  1100. cookie_to_iodata_test_() ->
  1101. %% {Name, Value, Opts, Result}
  1102. Tests = [
  1103. {<<"Customer">>, <<"WILE_E_COYOTE">>,
  1104. [{http_only, true}, {domain, <<"acme.com">>}],
  1105. <<"Customer=WILE_E_COYOTE; Version=1; "
  1106. "Domain=acme.com; HttpOnly">>},
  1107. {<<"Customer">>, <<"WILE_E_COYOTE">>,
  1108. [{path, <<"/acme">>}],
  1109. <<"Customer=WILE_E_COYOTE; Version=1; Path=/acme">>},
  1110. {<<"Customer">>, <<"WILE_E_COYOTE">>,
  1111. [{path, <<"/acme">>}, {badoption, <<"negatory">>}],
  1112. <<"Customer=WILE_E_COYOTE; Version=1; Path=/acme">>}
  1113. ],
  1114. [{R, fun() -> R = iolist_to_binary(cookie_to_iodata(N, V, O)) end}
  1115. || {N, V, O, R} <- Tests].
  1116. cookie_to_iodata_max_age_test() ->
  1117. F = fun(N, V, O) ->
  1118. binary:split(iolist_to_binary(
  1119. cookie_to_iodata(N, V, O)), <<";">>, [global])
  1120. end,
  1121. [<<"Customer=WILE_E_COYOTE">>,
  1122. <<" Version=1">>,
  1123. <<" Expires=", _/binary>>,
  1124. <<" Max-Age=111">>,
  1125. <<" Secure">>] = F(<<"Customer">>, <<"WILE_E_COYOTE">>,
  1126. [{max_age, 111}, {secure, true}]),
  1127. case catch F(<<"Customer">>, <<"WILE_E_COYOTE">>, [{max_age, -111}]) of
  1128. {'EXIT', {{case_clause, {max_age, -111}}, _}} -> ok
  1129. end,
  1130. [<<"Customer=WILE_E_COYOTE">>,
  1131. <<" Version=1">>,
  1132. <<" Expires=", _/binary>>,
  1133. <<" Max-Age=86417">>] = F(<<"Customer">>, <<"WILE_E_COYOTE">>,
  1134. [{max_age, 86417}]),
  1135. ok.
  1136. cookie_to_iodata_failures_test_() ->
  1137. F = fun(N, V) ->
  1138. try cookie_to_iodata(N, V, []) of
  1139. _ ->
  1140. false
  1141. catch _:_ ->
  1142. true
  1143. end
  1144. end,
  1145. Tests = [
  1146. {<<"Na=me">>, <<"Value">>},
  1147. {<<"Name;">>, <<"Value">>},
  1148. {<<"\r\name">>, <<"Value">>},
  1149. {<<"Name">>, <<"Value;">>},
  1150. {<<"Name">>, <<"\value">>}
  1151. ],
  1152. [{iolist_to_binary(io_lib:format("{~p, ~p} failure", [N, V])),
  1153. fun() -> true = F(N, V) end}
  1154. || {N, V} <- Tests].
  1155. x_www_form_urlencoded_test_() ->
  1156. %% {Qs, Result}
  1157. Tests = [
  1158. {<<"">>, []},
  1159. {<<"a=b">>, [{<<"a">>, <<"b">>}]},
  1160. {<<"aaa=bbb">>, [{<<"aaa">>, <<"bbb">>}]},
  1161. {<<"a&b">>, [{<<"a">>, true}, {<<"b">>, true}]},
  1162. {<<"a=b&c&d=e">>, [{<<"a">>, <<"b">>},
  1163. {<<"c">>, true}, {<<"d">>, <<"e">>}]},
  1164. {<<"a=b=c=d=e&f=g">>, [{<<"a">>, <<"b=c=d=e">>}, {<<"f">>, <<"g">>}]},
  1165. {<<"a+b=c+d">>, [{<<"a b">>, <<"c d">>}]}
  1166. ],
  1167. [{Qs, fun() -> R = x_www_form_urlencoded(Qs) end} || {Qs, R} <- Tests].
  1168. urldecode_test_() ->
  1169. U = fun urldecode/2,
  1170. [?_assertEqual(<<" ">>, U(<<"%20">>, crash)),
  1171. ?_assertEqual(<<" ">>, U(<<"+">>, crash)),
  1172. ?_assertEqual(<<0>>, U(<<"%00">>, crash)),
  1173. ?_assertEqual(<<255>>, U(<<"%fF">>, crash)),
  1174. ?_assertEqual(<<"123">>, U(<<"123">>, crash)),
  1175. ?_assertEqual(<<"%i5">>, U(<<"%i5">>, skip)),
  1176. ?_assertEqual(<<"%5">>, U(<<"%5">>, skip)),
  1177. ?_assertError(badarg, U(<<"%i5">>, crash)),
  1178. ?_assertError(badarg, U(<<"%5">>, crash))
  1179. ].
  1180. urlencode_test_() ->
  1181. U = fun urlencode/2,
  1182. [?_assertEqual(<<"%ff%00">>, U(<<255,0>>, [])),
  1183. ?_assertEqual(<<"%FF%00">>, U(<<255,0>>, [upper])),
  1184. ?_assertEqual(<<"+">>, U(<<" ">>, [])),
  1185. ?_assertEqual(<<"%20">>, U(<<" ">>, [noplus])),
  1186. ?_assertEqual(<<"aBc">>, U(<<"aBc">>, [])),
  1187. ?_assertEqual(<<".-~_">>, U(<<".-~_">>, [])),
  1188. ?_assertEqual(<<"%ff+">>, urlencode(<<255, " ">>))
  1189. ].
  1190. -endif.