cowboy_http.erl 39 KB

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