cowboy_http.erl 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066
  1. %% Copyright (c) 2011-2014, Loïc Hoguin <essen@ninenines.eu>
  2. %% Copyright (c) 2011, Anthony Ramine <nox@dev-extend.eu>
  3. %%
  4. %% Permission to use, copy, modify, and/or distribute this software for any
  5. %% purpose with or without fee is hereby granted, provided that the above
  6. %% copyright notice and this permission notice appear in all copies.
  7. %%
  8. %% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  9. %% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  10. %% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  11. %% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  12. %% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  13. %% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  14. %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. %% Deprecated 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([ce_identity/1]).
  41. %% Parsing.
  42. -spec nonempty_list(binary(), fun()) -> [any(), ...] | {error, badarg}.
  43. nonempty_list(Data, Fun) ->
  44. case list(Data, Fun, []) of
  45. {error, badarg} -> {error, badarg};
  46. [] -> {error, badarg};
  47. L -> lists:reverse(L)
  48. end.
  49. -spec list(binary(), fun()) -> list() | {error, badarg}.
  50. list(Data, Fun) ->
  51. case list(Data, Fun, []) of
  52. {error, badarg} -> {error, badarg};
  53. L -> lists:reverse(L)
  54. end.
  55. -spec list(binary(), fun(), [binary()]) -> [any()] | {error, badarg}.
  56. %% From the RFC:
  57. %% <blockquote>Wherever this construct is used, null elements are allowed,
  58. %% but do not contribute to the count of elements present.
  59. %% That is, "(element), , (element) " is permitted, but counts
  60. %% as only two elements. Therefore, where at least one element is required,
  61. %% at least one non-null element MUST be present.</blockquote>
  62. list(Data, Fun, Acc) ->
  63. whitespace(Data,
  64. fun (<<>>) -> Acc;
  65. (<< $,, Rest/binary >>) -> list(Rest, Fun, Acc);
  66. (Rest) -> Fun(Rest,
  67. fun (D, I) -> whitespace(D,
  68. fun (<<>>) -> [I|Acc];
  69. (<< $,, R/binary >>) -> list(R, Fun, [I|Acc]);
  70. (_Any) -> {error, badarg}
  71. end)
  72. end)
  73. end).
  74. %% We lowercase the charset header as we know it's case insensitive.
  75. -spec content_type(binary()) -> any().
  76. content_type(Data) ->
  77. media_type(Data,
  78. fun (Rest, Type, SubType) ->
  79. params(Rest,
  80. fun (<<>>, Params) ->
  81. case lists:keyfind(<<"charset">>, 1, Params) of
  82. false ->
  83. {Type, SubType, Params};
  84. {_, Charset} ->
  85. Charset2 = cowboy_bstr:to_lower(Charset),
  86. Params2 = lists:keyreplace(<<"charset">>,
  87. 1, Params, {<<"charset">>, Charset2}),
  88. {Type, SubType, Params2}
  89. end;
  90. (_Rest2, _) ->
  91. {error, badarg}
  92. end)
  93. end).
  94. -spec media_range(binary(), fun()) -> any().
  95. media_range(Data, Fun) ->
  96. media_type(Data,
  97. fun (Rest, Type, SubType) ->
  98. media_range_params(Rest, Fun, Type, SubType, [])
  99. end).
  100. -spec media_range_params(binary(), fun(), binary(), binary(),
  101. [{binary(), binary()}]) -> any().
  102. media_range_params(Data, Fun, Type, SubType, Acc) ->
  103. whitespace(Data,
  104. fun (<< $;, Rest/binary >>) ->
  105. whitespace(Rest,
  106. fun (Rest2) ->
  107. media_range_param_attr(Rest2, Fun, Type, SubType, Acc)
  108. end);
  109. (Rest) -> Fun(Rest, {{Type, SubType, lists:reverse(Acc)}, 1000, []})
  110. end).
  111. -spec media_range_param_attr(binary(), fun(), binary(), binary(),
  112. [{binary(), binary()}]) -> any().
  113. media_range_param_attr(Data, Fun, Type, SubType, Acc) ->
  114. token_ci(Data,
  115. fun (_Rest, <<>>) -> {error, badarg};
  116. (<< $=, Rest/binary >>, Attr) ->
  117. media_range_param_value(Rest, Fun, Type, SubType, Acc, Attr)
  118. end).
  119. -spec media_range_param_value(binary(), fun(), binary(), binary(),
  120. [{binary(), binary()}], binary()) -> any().
  121. media_range_param_value(Data, Fun, Type, SubType, Acc, <<"q">>) ->
  122. qvalue(Data,
  123. fun (Rest, Quality) ->
  124. accept_ext(Rest, Fun, Type, SubType, Acc, Quality, [])
  125. end);
  126. media_range_param_value(Data, Fun, Type, SubType, Acc, Attr) ->
  127. word(Data,
  128. fun (Rest, Value) ->
  129. media_range_params(Rest, Fun,
  130. Type, SubType, [{Attr, Value}|Acc])
  131. end).
  132. -spec media_type(binary(), fun()) -> any().
  133. media_type(Data, Fun) ->
  134. token_ci(Data,
  135. fun (_Rest, <<>>) -> {error, badarg};
  136. (<< $/, Rest/binary >>, Type) ->
  137. token_ci(Rest,
  138. fun (_Rest2, <<>>) -> {error, badarg};
  139. (Rest2, SubType) -> Fun(Rest2, Type, SubType)
  140. end);
  141. %% This is a non-strict parsing clause required by some user agents
  142. %% that use * instead of */* in the list of media types.
  143. (Rest, <<"*">> = Type) ->
  144. token_ci(<<"*", Rest/binary>>,
  145. fun (_Rest2, <<>>) -> {error, badarg};
  146. (Rest2, SubType) -> Fun(Rest2, Type, SubType)
  147. end);
  148. (_Rest, _Type) -> {error, badarg}
  149. end).
  150. -spec accept_ext(binary(), fun(), binary(), binary(),
  151. [{binary(), binary()}], 0..1000,
  152. [{binary(), binary()} | binary()]) -> any().
  153. accept_ext(Data, Fun, Type, SubType, Params, Quality, Acc) ->
  154. whitespace(Data,
  155. fun (<< $;, Rest/binary >>) ->
  156. whitespace(Rest,
  157. fun (Rest2) ->
  158. accept_ext_attr(Rest2, Fun,
  159. Type, SubType, Params, Quality, Acc)
  160. end);
  161. (Rest) ->
  162. Fun(Rest, {{Type, SubType, lists:reverse(Params)},
  163. Quality, lists:reverse(Acc)})
  164. end).
  165. -spec accept_ext_attr(binary(), fun(), binary(), binary(),
  166. [{binary(), binary()}], 0..1000,
  167. [{binary(), binary()} | binary()]) -> any().
  168. accept_ext_attr(Data, Fun, Type, SubType, Params, Quality, Acc) ->
  169. token_ci(Data,
  170. fun (_Rest, <<>>) -> {error, badarg};
  171. (<< $=, Rest/binary >>, Attr) ->
  172. accept_ext_value(Rest, Fun, Type, SubType, Params,
  173. Quality, Acc, Attr);
  174. (Rest, Attr) ->
  175. accept_ext(Rest, Fun, Type, SubType, Params,
  176. Quality, [Attr|Acc])
  177. end).
  178. -spec accept_ext_value(binary(), fun(), binary(), binary(),
  179. [{binary(), binary()}], 0..1000,
  180. [{binary(), binary()} | binary()], binary()) -> any().
  181. accept_ext_value(Data, Fun, Type, SubType, Params, Quality, Acc, Attr) ->
  182. word(Data,
  183. fun (Rest, Value) ->
  184. accept_ext(Rest, Fun,
  185. Type, SubType, Params, Quality, [{Attr, Value}|Acc])
  186. end).
  187. -spec conneg(binary(), fun()) -> any().
  188. conneg(Data, Fun) ->
  189. token_ci(Data,
  190. fun (_Rest, <<>>) -> {error, badarg};
  191. (Rest, Conneg) ->
  192. maybe_qparam(Rest,
  193. fun (Rest2, Quality) ->
  194. Fun(Rest2, {Conneg, Quality})
  195. end)
  196. end).
  197. -spec language_range(binary(), fun()) -> any().
  198. language_range(<< $*, Rest/binary >>, Fun) ->
  199. language_range_ret(Rest, Fun, '*');
  200. language_range(Data, Fun) ->
  201. language_tag(Data,
  202. fun (Rest, LanguageTag) ->
  203. language_range_ret(Rest, Fun, LanguageTag)
  204. end).
  205. -spec language_range_ret(binary(), fun(), '*' | {binary(), [binary()]}) -> any().
  206. language_range_ret(Data, Fun, LanguageTag) ->
  207. maybe_qparam(Data,
  208. fun (Rest, Quality) ->
  209. Fun(Rest, {LanguageTag, Quality})
  210. end).
  211. -spec language_tag(binary(), fun()) -> any().
  212. language_tag(Data, Fun) ->
  213. alpha(Data,
  214. fun (_Rest, Tag) when byte_size(Tag) =:= 0; byte_size(Tag) > 8 ->
  215. {error, badarg};
  216. (<< $-, Rest/binary >>, Tag) ->
  217. language_subtag(Rest, Fun, Tag, []);
  218. (Rest, Tag) ->
  219. Fun(Rest, Tag)
  220. end).
  221. -spec language_subtag(binary(), fun(), binary(), [binary()]) -> any().
  222. language_subtag(Data, Fun, Tag, Acc) ->
  223. alphanumeric(Data,
  224. fun (_Rest, SubTag) when byte_size(SubTag) =:= 0;
  225. byte_size(SubTag) > 8 -> {error, badarg};
  226. (<< $-, Rest/binary >>, SubTag) ->
  227. language_subtag(Rest, Fun, Tag, [SubTag|Acc]);
  228. (Rest, SubTag) ->
  229. %% Rebuild the full tag now that we know it's correct
  230. Sub = << << $-, S/binary >> || S <- lists:reverse([SubTag|Acc]) >>,
  231. Fun(Rest, << Tag/binary, Sub/binary >>)
  232. end).
  233. -spec maybe_qparam(binary(), fun()) -> any().
  234. maybe_qparam(Data, Fun) ->
  235. whitespace(Data,
  236. fun (<< $;, Rest/binary >>) ->
  237. whitespace(Rest,
  238. fun (Rest2) ->
  239. %% This is a non-strict parsing clause required by some user agents
  240. %% that use the wrong delimiter putting a charset where a qparam is
  241. %% expected.
  242. try qparam(Rest2, Fun) of
  243. Result -> Result
  244. catch
  245. error:function_clause ->
  246. Fun(<<",", Rest2/binary>>, 1000)
  247. end
  248. end);
  249. (Rest) ->
  250. Fun(Rest, 1000)
  251. end).
  252. -spec qparam(binary(), fun()) -> any().
  253. qparam(<< Q, $=, Data/binary >>, Fun) when Q =:= $q; Q =:= $Q ->
  254. qvalue(Data, Fun).
  255. -spec entity_tag_match(binary()) -> any().
  256. entity_tag_match(<< $*, Rest/binary >>) ->
  257. whitespace(Rest,
  258. fun (<<>>) -> '*';
  259. (_Any) -> {error, badarg}
  260. end);
  261. entity_tag_match(Data) ->
  262. nonempty_list(Data, fun entity_tag/2).
  263. -spec entity_tag(binary(), fun()) -> any().
  264. entity_tag(<< "W/", Rest/binary >>, Fun) ->
  265. opaque_tag(Rest, Fun, weak);
  266. entity_tag(Data, Fun) ->
  267. opaque_tag(Data, Fun, strong).
  268. -spec opaque_tag(binary(), fun(), weak | strong) -> any().
  269. opaque_tag(Data, Fun, Strength) ->
  270. quoted_string(Data,
  271. fun (_Rest, <<>>) -> {error, badarg};
  272. (Rest, OpaqueTag) -> Fun(Rest, {Strength, OpaqueTag})
  273. end).
  274. -spec expectation(binary(), fun()) -> any().
  275. expectation(Data, Fun) ->
  276. token_ci(Data,
  277. fun (_Rest, <<>>) -> {error, badarg};
  278. (<< $=, Rest/binary >>, Expectation) ->
  279. word(Rest,
  280. fun (Rest2, ExtValue) ->
  281. params(Rest2, fun (Rest3, ExtParams) ->
  282. Fun(Rest3, {Expectation, ExtValue, ExtParams})
  283. end)
  284. end);
  285. (Rest, Expectation) ->
  286. Fun(Rest, Expectation)
  287. end).
  288. -spec params(binary(), fun()) -> any().
  289. params(Data, Fun) ->
  290. params(Data, Fun, []).
  291. -spec params(binary(), fun(), [{binary(), binary()}]) -> any().
  292. params(Data, Fun, Acc) ->
  293. whitespace(Data,
  294. fun (<< $;, Rest/binary >>) ->
  295. param(Rest,
  296. fun (Rest2, Attr, Value) ->
  297. params(Rest2, Fun, [{Attr, Value}|Acc])
  298. end);
  299. (Rest) ->
  300. Fun(Rest, lists:reverse(Acc))
  301. end).
  302. -spec param(binary(), fun()) -> any().
  303. param(Data, Fun) ->
  304. whitespace(Data,
  305. fun (Rest) ->
  306. token_ci(Rest,
  307. fun (_Rest2, <<>>) -> {error, badarg};
  308. (<< $=, Rest2/binary >>, Attr) ->
  309. word(Rest2,
  310. fun (Rest3, Value) ->
  311. Fun(Rest3, Attr, Value)
  312. end);
  313. (_Rest2, _Attr) -> {error, badarg}
  314. end)
  315. end).
  316. %% While this may not be the most efficient date parsing we can do,
  317. %% it should work fine for our purposes because all HTTP dates should
  318. %% be sent as RFC1123 dates in HTTP/1.1.
  319. -spec http_date(binary()) -> any().
  320. http_date(Data) ->
  321. case rfc1123_date(Data) of
  322. {error, badarg} ->
  323. case rfc850_date(Data) of
  324. {error, badarg} ->
  325. case asctime_date(Data) of
  326. {error, badarg} ->
  327. {error, badarg};
  328. HTTPDate ->
  329. HTTPDate
  330. end;
  331. HTTPDate ->
  332. HTTPDate
  333. end;
  334. HTTPDate ->
  335. HTTPDate
  336. end.
  337. -spec rfc1123_date(binary()) -> any().
  338. rfc1123_date(Data) ->
  339. wkday(Data,
  340. fun (<< ", ", Rest/binary >>, _WkDay) ->
  341. date1(Rest,
  342. fun (<< " ", Rest2/binary >>, Date) ->
  343. time(Rest2,
  344. fun (<< " GMT", Rest3/binary >>, Time) ->
  345. http_date_ret(Rest3, {Date, Time});
  346. (_Any, _Time) ->
  347. {error, badarg}
  348. end);
  349. (_Any, _Date) ->
  350. {error, badarg}
  351. end);
  352. (_Any, _WkDay) ->
  353. {error, badarg}
  354. end).
  355. -spec rfc850_date(binary()) -> any().
  356. %% From the RFC:
  357. %% HTTP/1.1 clients and caches SHOULD assume that an RFC-850 date
  358. %% which appears to be more than 50 years in the future is in fact
  359. %% in the past (this helps solve the "year 2000" problem).
  360. rfc850_date(Data) ->
  361. weekday(Data,
  362. fun (<< ", ", Rest/binary >>, _WeekDay) ->
  363. date2(Rest,
  364. fun (<< " ", Rest2/binary >>, Date) ->
  365. time(Rest2,
  366. fun (<< " GMT", Rest3/binary >>, Time) ->
  367. http_date_ret(Rest3, {Date, Time});
  368. (_Any, _Time) ->
  369. {error, badarg}
  370. end);
  371. (_Any, _Date) ->
  372. {error, badarg}
  373. end);
  374. (_Any, _WeekDay) ->
  375. {error, badarg}
  376. end).
  377. -spec asctime_date(binary()) -> any().
  378. asctime_date(Data) ->
  379. wkday(Data,
  380. fun (<< " ", Rest/binary >>, _WkDay) ->
  381. date3(Rest,
  382. fun (<< " ", Rest2/binary >>, PartialDate) ->
  383. time(Rest2,
  384. fun (<< " ", Rest3/binary >>, Time) ->
  385. asctime_year(Rest3,
  386. PartialDate, Time);
  387. (_Any, _Time) ->
  388. {error, badarg}
  389. end);
  390. (_Any, _PartialDate) ->
  391. {error, badarg}
  392. end);
  393. (_Any, _WkDay) ->
  394. {error, badarg1}
  395. end).
  396. -spec asctime_year(binary(), tuple(), tuple()) -> any().
  397. asctime_year(<< Y1, Y2, Y3, Y4, Rest/binary >>, {Month, Day}, Time)
  398. when Y1 >= $0, Y1 =< $9, Y2 >= $0, Y2 =< $9,
  399. Y3 >= $0, Y3 =< $9, Y4 >= $0, Y4 =< $9 ->
  400. Year = (Y1 - $0) * 1000 + (Y2 - $0) * 100 + (Y3 - $0) * 10 + (Y4 - $0),
  401. http_date_ret(Rest, {{Year, Month, Day}, Time}).
  402. -spec http_date_ret(binary(), tuple()) -> any().
  403. http_date_ret(Data, DateTime = {Date, _Time}) ->
  404. whitespace(Data,
  405. fun (<<>>) ->
  406. case calendar:valid_date(Date) of
  407. true -> DateTime;
  408. false -> {error, badarg}
  409. end;
  410. (_Any) ->
  411. {error, badarg}
  412. end).
  413. %% We never use it, pretty much just checks the wkday is right.
  414. -spec wkday(binary(), fun()) -> any().
  415. wkday(<< WkDay:3/binary, Rest/binary >>, Fun)
  416. when WkDay =:= <<"Mon">>; WkDay =:= <<"Tue">>; WkDay =:= <<"Wed">>;
  417. WkDay =:= <<"Thu">>; WkDay =:= <<"Fri">>; WkDay =:= <<"Sat">>;
  418. WkDay =:= <<"Sun">> ->
  419. Fun(Rest, WkDay);
  420. wkday(_Any, _Fun) ->
  421. {error, badarg}.
  422. %% We never use it, pretty much just checks the weekday is right.
  423. -spec weekday(binary(), fun()) -> any().
  424. weekday(<< "Monday", Rest/binary >>, Fun) ->
  425. Fun(Rest, <<"Monday">>);
  426. weekday(<< "Tuesday", Rest/binary >>, Fun) ->
  427. Fun(Rest, <<"Tuesday">>);
  428. weekday(<< "Wednesday", Rest/binary >>, Fun) ->
  429. Fun(Rest, <<"Wednesday">>);
  430. weekday(<< "Thursday", Rest/binary >>, Fun) ->
  431. Fun(Rest, <<"Thursday">>);
  432. weekday(<< "Friday", Rest/binary >>, Fun) ->
  433. Fun(Rest, <<"Friday">>);
  434. weekday(<< "Saturday", Rest/binary >>, Fun) ->
  435. Fun(Rest, <<"Saturday">>);
  436. weekday(<< "Sunday", Rest/binary >>, Fun) ->
  437. Fun(Rest, <<"Sunday">>);
  438. weekday(_Any, _Fun) ->
  439. {error, badarg}.
  440. -spec date1(binary(), fun()) -> any().
  441. date1(<< D1, D2, " ", M:3/binary, " ", Y1, Y2, Y3, Y4, Rest/binary >>, Fun)
  442. when D1 >= $0, D1 =< $9, D2 >= $0, D2 =< $9,
  443. Y1 >= $0, Y1 =< $9, Y2 >= $0, Y2 =< $9,
  444. Y3 >= $0, Y3 =< $9, Y4 >= $0, Y4 =< $9 ->
  445. case month(M) of
  446. {error, badarg} ->
  447. {error, badarg};
  448. Month ->
  449. Fun(Rest, {
  450. (Y1 - $0) * 1000 + (Y2 - $0) * 100 + (Y3 - $0) * 10 + (Y4 - $0),
  451. Month,
  452. (D1 - $0) * 10 + (D2 - $0)
  453. })
  454. end;
  455. date1(_Data, _Fun) ->
  456. {error, badarg}.
  457. -spec date2(binary(), fun()) -> any().
  458. date2(<< D1, D2, "-", M:3/binary, "-", Y1, Y2, Rest/binary >>, Fun)
  459. when D1 >= $0, D1 =< $9, D2 >= $0, D2 =< $9,
  460. Y1 >= $0, Y1 =< $9, Y2 >= $0, Y2 =< $9 ->
  461. case month(M) of
  462. {error, badarg} ->
  463. {error, badarg};
  464. Month ->
  465. Year = (Y1 - $0) * 10 + (Y2 - $0),
  466. Year2 = case Year > 50 of
  467. true -> Year + 1900;
  468. false -> Year + 2000
  469. end,
  470. Fun(Rest, {
  471. Year2,
  472. Month,
  473. (D1 - $0) * 10 + (D2 - $0)
  474. })
  475. end;
  476. date2(_Data, _Fun) ->
  477. {error, badarg}.
  478. -spec date3(binary(), fun()) -> any().
  479. date3(<< M:3/binary, " ", D1, D2, Rest/binary >>, Fun)
  480. when (D1 >= $0 andalso D1 =< $3) orelse D1 =:= $\s,
  481. D2 >= $0, D2 =< $9 ->
  482. case month(M) of
  483. {error, badarg} ->
  484. {error, badarg};
  485. Month ->
  486. Day = case D1 of
  487. $\s -> D2 - $0;
  488. D1 -> (D1 - $0) * 10 + (D2 - $0)
  489. end,
  490. Fun(Rest, {Month, Day})
  491. end;
  492. date3(_Data, _Fun) ->
  493. {error, badarg}.
  494. -spec month(<< _:24 >>) -> 1..12 | {error, badarg}.
  495. month(<<"Jan">>) -> 1;
  496. month(<<"Feb">>) -> 2;
  497. month(<<"Mar">>) -> 3;
  498. month(<<"Apr">>) -> 4;
  499. month(<<"May">>) -> 5;
  500. month(<<"Jun">>) -> 6;
  501. month(<<"Jul">>) -> 7;
  502. month(<<"Aug">>) -> 8;
  503. month(<<"Sep">>) -> 9;
  504. month(<<"Oct">>) -> 10;
  505. month(<<"Nov">>) -> 11;
  506. month(<<"Dec">>) -> 12;
  507. month(_Any) -> {error, badarg}.
  508. -spec time(binary(), fun()) -> any().
  509. time(<< H1, H2, ":", M1, M2, ":", S1, S2, Rest/binary >>, Fun)
  510. when H1 >= $0, H1 =< $2, H2 >= $0, H2 =< $9,
  511. M1 >= $0, M1 =< $5, M2 >= $0, M2 =< $9,
  512. S1 >= $0, S1 =< $5, S2 >= $0, S2 =< $9 ->
  513. Hour = (H1 - $0) * 10 + (H2 - $0),
  514. case Hour < 24 of
  515. true ->
  516. Time = {
  517. Hour,
  518. (M1 - $0) * 10 + (M2 - $0),
  519. (S1 - $0) * 10 + (S2 - $0)
  520. },
  521. Fun(Rest, Time);
  522. false ->
  523. {error, badarg}
  524. end.
  525. -spec whitespace(binary(), fun()) -> any().
  526. whitespace(<< C, Rest/binary >>, Fun)
  527. when C =:= $\s; C =:= $\t ->
  528. whitespace(Rest, Fun);
  529. whitespace(Data, Fun) ->
  530. Fun(Data).
  531. -spec digits(binary()) -> non_neg_integer() | {error, badarg}.
  532. digits(Data) ->
  533. digits(Data,
  534. fun (Rest, I) ->
  535. whitespace(Rest,
  536. fun (<<>>) ->
  537. I;
  538. (_Rest2) ->
  539. {error, badarg}
  540. end)
  541. end).
  542. -spec digits(binary(), fun()) -> any().
  543. digits(<< C, Rest/binary >>, Fun)
  544. when C >= $0, C =< $9 ->
  545. digits(Rest, Fun, C - $0);
  546. digits(_Data, _Fun) ->
  547. {error, badarg}.
  548. -spec digits(binary(), fun(), non_neg_integer()) -> any().
  549. digits(<< C, Rest/binary >>, Fun, Acc)
  550. when C >= $0, C =< $9 ->
  551. digits(Rest, Fun, Acc * 10 + (C - $0));
  552. digits(Data, Fun, Acc) ->
  553. Fun(Data, Acc).
  554. %% Changes all characters to lowercase.
  555. -spec alpha(binary(), fun()) -> any().
  556. alpha(Data, Fun) ->
  557. alpha(Data, Fun, <<>>).
  558. -spec alpha(binary(), fun(), binary()) -> any().
  559. alpha(<<>>, Fun, Acc) ->
  560. Fun(<<>>, Acc);
  561. alpha(<< C, Rest/binary >>, Fun, Acc)
  562. when C >= $a andalso C =< $z;
  563. C >= $A andalso C =< $Z ->
  564. C2 = cowboy_bstr:char_to_lower(C),
  565. alpha(Rest, Fun, << Acc/binary, C2 >>);
  566. alpha(Data, Fun, Acc) ->
  567. Fun(Data, Acc).
  568. -spec alphanumeric(binary(), fun()) -> any().
  569. alphanumeric(Data, Fun) ->
  570. alphanumeric(Data, Fun, <<>>).
  571. -spec alphanumeric(binary(), fun(), binary()) -> any().
  572. alphanumeric(<<>>, Fun, Acc) ->
  573. Fun(<<>>, Acc);
  574. alphanumeric(<< C, Rest/binary >>, Fun, Acc)
  575. when C >= $a andalso C =< $z;
  576. C >= $A andalso C =< $Z;
  577. C >= $0 andalso C =< $9 ->
  578. C2 = cowboy_bstr:char_to_lower(C),
  579. alphanumeric(Rest, Fun, << Acc/binary, C2 >>);
  580. alphanumeric(Data, Fun, Acc) ->
  581. Fun(Data, Acc).
  582. %% @doc Parse either a token or a quoted string.
  583. -spec word(binary(), fun()) -> any().
  584. word(Data = << $", _/binary >>, Fun) ->
  585. quoted_string(Data, Fun);
  586. word(Data, Fun) ->
  587. token(Data,
  588. fun (_Rest, <<>>) -> {error, badarg};
  589. (Rest, Token) -> Fun(Rest, Token)
  590. end).
  591. %% Changes all characters to lowercase.
  592. -spec token_ci(binary(), fun()) -> any().
  593. token_ci(Data, Fun) ->
  594. token(Data, Fun, ci, <<>>).
  595. -spec token(binary(), fun()) -> any().
  596. token(Data, Fun) ->
  597. token(Data, Fun, cs, <<>>).
  598. -spec token(binary(), fun(), ci | cs, binary()) -> any().
  599. token(<<>>, Fun, _Case, Acc) ->
  600. Fun(<<>>, Acc);
  601. token(Data = << C, _Rest/binary >>, Fun, _Case, Acc)
  602. when C =:= $(; C =:= $); C =:= $<; C =:= $>; C =:= $@;
  603. C =:= $,; C =:= $;; C =:= $:; C =:= $\\; C =:= $";
  604. C =:= $/; C =:= $[; C =:= $]; C =:= $?; C =:= $=;
  605. C =:= ${; C =:= $}; C =:= $\s; C =:= $\t;
  606. C < 32; C =:= 127 ->
  607. Fun(Data, Acc);
  608. token(<< C, Rest/binary >>, Fun, Case = ci, Acc) ->
  609. C2 = cowboy_bstr:char_to_lower(C),
  610. token(Rest, Fun, Case, << Acc/binary, C2 >>);
  611. token(<< C, Rest/binary >>, Fun, Case, Acc) ->
  612. token(Rest, Fun, Case, << Acc/binary, C >>).
  613. -spec quoted_string(binary(), fun()) -> any().
  614. quoted_string(<< $", Rest/binary >>, Fun) ->
  615. quoted_string(Rest, Fun, <<>>).
  616. -spec quoted_string(binary(), fun(), binary()) -> any().
  617. quoted_string(<<>>, _Fun, _Acc) ->
  618. {error, badarg};
  619. quoted_string(<< $", Rest/binary >>, Fun, Acc) ->
  620. Fun(Rest, Acc);
  621. quoted_string(<< $\\, C, Rest/binary >>, Fun, Acc) ->
  622. quoted_string(Rest, Fun, << Acc/binary, C >>);
  623. quoted_string(<< C, Rest/binary >>, Fun, Acc) ->
  624. quoted_string(Rest, Fun, << Acc/binary, C >>).
  625. -spec qvalue(binary(), fun()) -> any().
  626. qvalue(<< $0, $., Rest/binary >>, Fun) ->
  627. qvalue(Rest, Fun, 0, 100);
  628. %% Some user agents use q=.x instead of q=0.x
  629. qvalue(<< $., Rest/binary >>, Fun) ->
  630. qvalue(Rest, Fun, 0, 100);
  631. qvalue(<< $0, Rest/binary >>, Fun) ->
  632. Fun(Rest, 0);
  633. qvalue(<< $1, $., $0, $0, $0, Rest/binary >>, Fun) ->
  634. Fun(Rest, 1000);
  635. qvalue(<< $1, $., $0, $0, Rest/binary >>, Fun) ->
  636. Fun(Rest, 1000);
  637. qvalue(<< $1, $., $0, Rest/binary >>, Fun) ->
  638. Fun(Rest, 1000);
  639. qvalue(<< $1, Rest/binary >>, Fun) ->
  640. Fun(Rest, 1000);
  641. qvalue(_Data, _Fun) ->
  642. {error, badarg}.
  643. -spec qvalue(binary(), fun(), integer(), 1 | 10 | 100) -> any().
  644. qvalue(Data, Fun, Q, 0) ->
  645. Fun(Data, Q);
  646. qvalue(<< C, Rest/binary >>, Fun, Q, M)
  647. when C >= $0, C =< $9 ->
  648. qvalue(Rest, Fun, Q + (C - $0) * M, M div 10);
  649. qvalue(Data, Fun, Q, _M) ->
  650. Fun(Data, Q).
  651. %% Only RFC2617 Basic authorization is supported so far.
  652. -spec authorization(binary(), binary()) -> {binary(), any()} | {error, badarg}.
  653. authorization(UserPass, Type = <<"basic">>) ->
  654. whitespace(UserPass,
  655. fun(D) ->
  656. authorization_basic_userid(base64:mime_decode(D),
  657. fun(Rest, Userid) ->
  658. authorization_basic_password(Rest,
  659. fun(Password) ->
  660. {Type, {Userid, Password}}
  661. end)
  662. end)
  663. end);
  664. authorization(String, Type) ->
  665. whitespace(String, fun(Rest) -> {Type, Rest} end).
  666. -spec authorization_basic_userid(binary(), fun()) -> any().
  667. authorization_basic_userid(Data, Fun) ->
  668. authorization_basic_userid(Data, Fun, <<>>).
  669. authorization_basic_userid(<<>>, _Fun, _Acc) ->
  670. {error, badarg};
  671. authorization_basic_userid(<<C, _Rest/binary>>, _Fun, Acc)
  672. when C < 32; C =:= 127; (C =:=$: andalso Acc =:= <<>>) ->
  673. {error, badarg};
  674. authorization_basic_userid(<<$:, Rest/binary>>, Fun, Acc) ->
  675. Fun(Rest, Acc);
  676. authorization_basic_userid(<<C, Rest/binary>>, Fun, Acc) ->
  677. authorization_basic_userid(Rest, Fun, <<Acc/binary, C>>).
  678. -spec authorization_basic_password(binary(), fun()) -> any().
  679. authorization_basic_password(Data, Fun) ->
  680. authorization_basic_password(Data, Fun, <<>>).
  681. authorization_basic_password(<<C, _Rest/binary>>, _Fun, _Acc)
  682. when C < 32; C=:= 127 ->
  683. {error, badarg};
  684. authorization_basic_password(<<>>, Fun, Acc) ->
  685. Fun(Acc);
  686. authorization_basic_password(<<C, Rest/binary>>, Fun, Acc) ->
  687. authorization_basic_password(Rest, Fun, <<Acc/binary, C>>).
  688. -spec range(binary()) -> {Unit, [Range]} | {error, badarg} when
  689. Unit :: binary(),
  690. Range :: {non_neg_integer(), non_neg_integer() | infinity} | neg_integer().
  691. range(Data) ->
  692. token_ci(Data, fun range/2).
  693. range(Data, Token) ->
  694. whitespace(Data,
  695. fun(<<"=", Rest/binary>>) ->
  696. case list(Rest, fun range_beginning/2) of
  697. {error, badarg} ->
  698. {error, badarg};
  699. Ranges ->
  700. {Token, Ranges}
  701. end;
  702. (_) ->
  703. {error, badarg}
  704. end).
  705. range_beginning(Data, Fun) ->
  706. range_digits(Data, suffix,
  707. fun(D, RangeBeginning) ->
  708. range_ending(D, Fun, RangeBeginning)
  709. end).
  710. range_ending(Data, Fun, RangeBeginning) ->
  711. whitespace(Data,
  712. fun(<<"-", R/binary>>) ->
  713. case RangeBeginning of
  714. suffix ->
  715. range_digits(R, fun(D, RangeEnding) -> Fun(D, -RangeEnding) end);
  716. _ ->
  717. range_digits(R, infinity,
  718. fun(D, RangeEnding) ->
  719. Fun(D, {RangeBeginning, RangeEnding})
  720. end)
  721. end;
  722. (_) ->
  723. {error, badarg}
  724. end).
  725. -spec range_digits(binary(), fun()) -> any().
  726. range_digits(Data, Fun) ->
  727. whitespace(Data,
  728. fun(D) ->
  729. digits(D, Fun)
  730. end).
  731. -spec range_digits(binary(), any(), fun()) -> any().
  732. range_digits(Data, Default, Fun) ->
  733. whitespace(Data,
  734. fun(<< C, Rest/binary >>) when C >= $0, C =< $9 ->
  735. digits(Rest, Fun, C - $0);
  736. (_) ->
  737. Fun(Data, Default)
  738. end).
  739. -spec parameterized_tokens(binary()) -> any().
  740. parameterized_tokens(Data) ->
  741. nonempty_list(Data,
  742. fun (D, Fun) ->
  743. token(D,
  744. fun (_Rest, <<>>) -> {error, badarg};
  745. (Rest, Token) ->
  746. parameterized_tokens_params(Rest,
  747. fun (Rest2, Params) ->
  748. Fun(Rest2, {Token, Params})
  749. end, [])
  750. end)
  751. end).
  752. -spec parameterized_tokens_params(binary(), fun(), [binary() | {binary(), binary()}]) -> any().
  753. parameterized_tokens_params(Data, Fun, Acc) ->
  754. whitespace(Data,
  755. fun (<< $;, Rest/binary >>) ->
  756. parameterized_tokens_param(Rest,
  757. fun (Rest2, Param) ->
  758. parameterized_tokens_params(Rest2, Fun, [Param|Acc])
  759. end);
  760. (Rest) ->
  761. Fun(Rest, lists:reverse(Acc))
  762. end).
  763. -spec parameterized_tokens_param(binary(), fun()) -> any().
  764. parameterized_tokens_param(Data, Fun) ->
  765. whitespace(Data,
  766. fun (Rest) ->
  767. token(Rest,
  768. fun (_Rest2, <<>>) -> {error, badarg};
  769. (<< $=, Rest2/binary >>, Attr) ->
  770. word(Rest2,
  771. fun (Rest3, Value) ->
  772. Fun(Rest3, {Attr, Value})
  773. end);
  774. (Rest2, Attr) ->
  775. Fun(Rest2, Attr)
  776. end)
  777. end).
  778. %% Decoding.
  779. %% @todo Move this to cowlib too I suppose. :-)
  780. -spec ce_identity(binary()) -> {ok, binary()}.
  781. ce_identity(Data) ->
  782. {ok, Data}.
  783. %% Tests.
  784. -ifdef(TEST).
  785. nonempty_charset_list_test_() ->
  786. Tests = [
  787. {<<>>, {error, badarg}},
  788. {<<"iso-8859-5, unicode-1-1;q=0.8">>, [
  789. {<<"iso-8859-5">>, 1000},
  790. {<<"unicode-1-1">>, 800}
  791. ]},
  792. %% Some user agents send this invalid value for the Accept-Charset header
  793. {<<"ISO-8859-1;utf-8;q=0.7,*;q=0.7">>, [
  794. {<<"iso-8859-1">>, 1000},
  795. {<<"utf-8">>, 700},
  796. {<<"*">>, 700}
  797. ]}
  798. ],
  799. [{V, fun() -> R = nonempty_list(V, fun conneg/2) end} || {V, R} <- Tests].
  800. nonempty_language_range_list_test_() ->
  801. Tests = [
  802. {<<"da, en-gb;q=0.8, en;q=0.7">>, [
  803. {<<"da">>, 1000},
  804. {<<"en-gb">>, 800},
  805. {<<"en">>, 700}
  806. ]},
  807. {<<"en, en-US, en-cockney, i-cherokee, x-pig-latin, es-419">>, [
  808. {<<"en">>, 1000},
  809. {<<"en-us">>, 1000},
  810. {<<"en-cockney">>, 1000},
  811. {<<"i-cherokee">>, 1000},
  812. {<<"x-pig-latin">>, 1000},
  813. {<<"es-419">>, 1000}
  814. ]}
  815. ],
  816. [{V, fun() -> R = nonempty_list(V, fun language_range/2) end}
  817. || {V, R} <- Tests].
  818. nonempty_token_list_test_() ->
  819. Tests = [
  820. {<<>>, {error, badarg}},
  821. {<<" ">>, {error, badarg}},
  822. {<<" , ">>, {error, badarg}},
  823. {<<",,,">>, {error, badarg}},
  824. {<<"a b">>, {error, badarg}},
  825. {<<"a , , , ">>, [<<"a">>]},
  826. {<<" , , , a">>, [<<"a">>]},
  827. {<<"a, , b">>, [<<"a">>, <<"b">>]},
  828. {<<"close">>, [<<"close">>]},
  829. {<<"keep-alive, upgrade">>, [<<"keep-alive">>, <<"upgrade">>]}
  830. ],
  831. [{V, fun() -> R = nonempty_list(V, fun token/2) end} || {V, R} <- Tests].
  832. media_range_list_test_() ->
  833. Tests = [
  834. {<<"audio/*; q=0.2, audio/basic">>, [
  835. {{<<"audio">>, <<"*">>, []}, 200, []},
  836. {{<<"audio">>, <<"basic">>, []}, 1000, []}
  837. ]},
  838. {<<"text/plain; q=0.5, text/html, "
  839. "text/x-dvi; q=0.8, text/x-c">>, [
  840. {{<<"text">>, <<"plain">>, []}, 500, []},
  841. {{<<"text">>, <<"html">>, []}, 1000, []},
  842. {{<<"text">>, <<"x-dvi">>, []}, 800, []},
  843. {{<<"text">>, <<"x-c">>, []}, 1000, []}
  844. ]},
  845. {<<"text/*, text/html, text/html;level=1, */*">>, [
  846. {{<<"text">>, <<"*">>, []}, 1000, []},
  847. {{<<"text">>, <<"html">>, []}, 1000, []},
  848. {{<<"text">>, <<"html">>, [{<<"level">>, <<"1">>}]}, 1000, []},
  849. {{<<"*">>, <<"*">>, []}, 1000, []}
  850. ]},
  851. {<<"text/*;q=0.3, text/html;q=0.7, text/html;level=1, "
  852. "text/html;level=2;q=0.4, */*;q=0.5">>, [
  853. {{<<"text">>, <<"*">>, []}, 300, []},
  854. {{<<"text">>, <<"html">>, []}, 700, []},
  855. {{<<"text">>, <<"html">>, [{<<"level">>, <<"1">>}]}, 1000, []},
  856. {{<<"text">>, <<"html">>, [{<<"level">>, <<"2">>}]}, 400, []},
  857. {{<<"*">>, <<"*">>, []}, 500, []}
  858. ]},
  859. {<<"text/html;level=1;quoted=\"hi hi hi\";"
  860. "q=0.123;standalone;complex=gits, text/plain">>, [
  861. {{<<"text">>, <<"html">>,
  862. [{<<"level">>, <<"1">>}, {<<"quoted">>, <<"hi hi hi">>}]}, 123,
  863. [<<"standalone">>, {<<"complex">>, <<"gits">>}]},
  864. {{<<"text">>, <<"plain">>, []}, 1000, []}
  865. ]},
  866. {<<"text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2">>, [
  867. {{<<"text">>, <<"html">>, []}, 1000, []},
  868. {{<<"image">>, <<"gif">>, []}, 1000, []},
  869. {{<<"image">>, <<"jpeg">>, []}, 1000, []},
  870. {{<<"*">>, <<"*">>, []}, 200, []},
  871. {{<<"*">>, <<"*">>, []}, 200, []}
  872. ]}
  873. ],
  874. [{V, fun() -> R = list(V, fun media_range/2) end} || {V, R} <- Tests].
  875. entity_tag_match_test_() ->
  876. Tests = [
  877. {<<"\"xyzzy\"">>, [{strong, <<"xyzzy">>}]},
  878. {<<"\"xyzzy\", W/\"r2d2xxxx\", \"c3piozzzz\"">>,
  879. [{strong, <<"xyzzy">>},
  880. {weak, <<"r2d2xxxx">>},
  881. {strong, <<"c3piozzzz">>}]},
  882. {<<"*">>, '*'}
  883. ],
  884. [{V, fun() -> R = entity_tag_match(V) end} || {V, R} <- Tests].
  885. http_date_test_() ->
  886. Tests = [
  887. {<<"Sun, 06 Nov 1994 08:49:37 GMT">>, {{1994, 11, 6}, {8, 49, 37}}},
  888. {<<"Sunday, 06-Nov-94 08:49:37 GMT">>, {{1994, 11, 6}, {8, 49, 37}}},
  889. {<<"Sun Nov 6 08:49:37 1994">>, {{1994, 11, 6}, {8, 49, 37}}}
  890. ],
  891. [{V, fun() -> R = http_date(V) end} || {V, R} <- Tests].
  892. rfc1123_date_test_() ->
  893. Tests = [
  894. {<<"Sun, 06 Nov 1994 08:49:37 GMT">>, {{1994, 11, 6}, {8, 49, 37}}}
  895. ],
  896. [{V, fun() -> R = rfc1123_date(V) end} || {V, R} <- Tests].
  897. rfc850_date_test_() ->
  898. Tests = [
  899. {<<"Sunday, 06-Nov-94 08:49:37 GMT">>, {{1994, 11, 6}, {8, 49, 37}}}
  900. ],
  901. [{V, fun() -> R = rfc850_date(V) end} || {V, R} <- Tests].
  902. asctime_date_test_() ->
  903. Tests = [
  904. {<<"Sun Nov 6 08:49:37 1994">>, {{1994, 11, 6}, {8, 49, 37}}}
  905. ],
  906. [{V, fun() -> R = asctime_date(V) end} || {V, R} <- Tests].
  907. content_type_test_() ->
  908. Tests = [
  909. {<<"text/plain; charset=iso-8859-4">>,
  910. {<<"text">>, <<"plain">>, [{<<"charset">>, <<"iso-8859-4">>}]}},
  911. {<<"multipart/form-data \t;Boundary=\"MultipartIsUgly\"">>,
  912. {<<"multipart">>, <<"form-data">>, [
  913. {<<"boundary">>, <<"MultipartIsUgly">>}
  914. ]}},
  915. {<<"foo/bar; one=FirstParam; two=SecondParam">>,
  916. {<<"foo">>, <<"bar">>, [
  917. {<<"one">>, <<"FirstParam">>},
  918. {<<"two">>, <<"SecondParam">>}
  919. ]}}
  920. ],
  921. [{V, fun () -> R = content_type(V) end} || {V, R} <- Tests].
  922. parameterized_tokens_test_() ->
  923. Tests = [
  924. {<<"foo">>, [{<<"foo">>, []}]},
  925. {<<"bar; baz=2">>, [{<<"bar">>, [{<<"baz">>, <<"2">>}]}]},
  926. {<<"bar; baz=2;bat">>, [{<<"bar">>, [{<<"baz">>, <<"2">>}, <<"bat">>]}]},
  927. {<<"bar; baz=2;bat=\"z=1,2;3\"">>, [{<<"bar">>, [{<<"baz">>, <<"2">>}, {<<"bat">>, <<"z=1,2;3">>}]}]},
  928. {<<"foo, bar; baz=2">>, [{<<"foo">>, []}, {<<"bar">>, [{<<"baz">>, <<"2">>}]}]}
  929. ],
  930. [{V, fun () -> R = parameterized_tokens(V) end} || {V, R} <- Tests].
  931. digits_test_() ->
  932. Tests = [
  933. {<<"42 ">>, 42},
  934. {<<"69\t">>, 69},
  935. {<<"1337">>, 1337}
  936. ],
  937. [{V, fun() -> R = digits(V) end} || {V, R} <- Tests].
  938. http_authorization_test_() ->
  939. Tests = [
  940. {<<"basic">>, <<"QWxsYWRpbjpvcGVuIHNlc2FtZQ==">>,
  941. {<<"basic">>, {<<"Alladin">>, <<"open sesame">>}}},
  942. {<<"basic">>, <<"dXNlcm5hbWU6">>,
  943. {<<"basic">>, {<<"username">>, <<>>}}},
  944. {<<"basic">>, <<"dXNlcm5hbWUK">>,
  945. {error, badarg}},
  946. {<<"basic">>, <<"_[]@#$%^&*()-AA==">>,
  947. {error, badarg}},
  948. {<<"basic">>, <<"dXNlcjpwYXNzCA==">>,
  949. {error, badarg}},
  950. {<<"bearer">>, <<" some_secret_key">>,
  951. {<<"bearer">>,<<"some_secret_key">>}}
  952. ],
  953. [{V, fun() -> R = authorization(V,T) end} || {T, V, R} <- Tests].
  954. http_range_test_() ->
  955. Tests = [
  956. {<<"bytes=1-20">>,
  957. {<<"bytes">>, [{1, 20}]}},
  958. {<<"bytes=-100">>,
  959. {<<"bytes">>, [-100]}},
  960. {<<"bytes=1-">>,
  961. {<<"bytes">>, [{1, infinity}]}},
  962. {<<"bytes=1-20,30-40,50-">>,
  963. {<<"bytes">>, [{1, 20}, {30, 40}, {50, infinity}]}},
  964. {<<"bytes = 1 - 20 , 50 - , - 300 ">>,
  965. {<<"bytes">>, [{1, 20}, {50, infinity}, -300]}},
  966. {<<"bytes=1-20,-500,30-40">>,
  967. {<<"bytes">>, [{1, 20}, -500, {30, 40}]}},
  968. {<<"test=1-20,-500,30-40">>,
  969. {<<"test">>, [{1, 20}, -500, {30, 40}]}},
  970. {<<"bytes=-">>,
  971. {error, badarg}},
  972. {<<"bytes=-30,-">>,
  973. {error, badarg}}
  974. ],
  975. [fun() -> R = range(V) end ||{V, R} <- Tests].
  976. -endif.