cowboy_http.erl 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888
  1. %% Copyright (c) 2011, Loïc Hoguin <essen@dev-extend.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, nonempty_list/2, content_type/1,
  19. media_range/2, conneg/2, language_range/2, entity_tag_match/1,
  20. http_date/1, rfc1123_date/1, rfc850_date/1, asctime_date/1,
  21. digits/1, token/2, token_ci/2, quoted_string/2]).
  22. %% Interpretation.
  23. -export([connection_to_atom/1, urldecode/1, urldecode/2]).
  24. -include("include/http.hrl").
  25. -include_lib("eunit/include/eunit.hrl").
  26. %% Parsing.
  27. %% @doc Parse a non-empty list of the given type.
  28. -spec nonempty_list(binary(), fun()) -> [any(), ...] | {error, badarg}.
  29. nonempty_list(Data, Fun) ->
  30. case list(Data, Fun, []) of
  31. {error, badarg} -> {error, badarg};
  32. [] -> {error, badarg};
  33. L -> lists:reverse(L)
  34. end.
  35. %% @doc Parse a list of the given type.
  36. -spec list(binary(), fun()) -> list() | {error, badarg}.
  37. list(Data, Fun) ->
  38. case list(Data, Fun, []) of
  39. {error, badarg} -> {error, badarg};
  40. L -> lists:reverse(L)
  41. end.
  42. -spec list(binary(), fun(), [binary()]) -> [any()] | {error, badarg}.
  43. %% From the RFC:
  44. %% <blockquote>Wherever this construct is used, null elements are allowed,
  45. %% but do not contribute to the count of elements present.
  46. %% That is, "(element), , (element) " is permitted, but counts
  47. %% as only two elements. Therefore, where at least one element is required,
  48. %% at least one non-null element MUST be present.</blockquote>
  49. list(Data, Fun, Acc) ->
  50. whitespace(Data,
  51. fun (<<>>) -> Acc;
  52. (<< $,, Rest/bits >>) -> list(Rest, Fun, Acc);
  53. (Rest) -> Fun(Rest,
  54. fun (D, I) -> whitespace(D,
  55. fun (<<>>) -> [I|Acc];
  56. (<< $,, R/bits >>) -> list(R, Fun, [I|Acc]);
  57. (_Any) -> {error, badarg}
  58. end)
  59. end)
  60. end).
  61. %% @doc Parse a content type.
  62. -spec content_type(binary()) -> any().
  63. content_type(Data) ->
  64. media_type(Data,
  65. fun (Rest, Type, SubType) ->
  66. content_type_params(Rest,
  67. fun (Params) -> {Type, SubType, Params} end, [])
  68. end).
  69. -spec content_type_params(binary(), fun(), list({binary(), binary()}))
  70. -> any().
  71. content_type_params(Data, Fun, Acc) ->
  72. whitespace(Data,
  73. fun (<< $;, Rest/bits >>) -> content_type_param(Rest, Fun, Acc);
  74. (<<>>) -> Fun(lists:reverse(Acc));
  75. (_Rest) -> {error, badarg}
  76. end).
  77. -spec content_type_param(binary(), fun(), list({binary(), binary()}))
  78. -> any().
  79. content_type_param(Data, Fun, Acc) ->
  80. whitespace(Data,
  81. fun (Rest) ->
  82. token_ci(Rest,
  83. fun (_Rest2, <<>>) -> {error, badarg};
  84. (<< $=, Rest2/bits >>, Attr) ->
  85. word(Rest2,
  86. fun (Rest3, Value) ->
  87. content_type_params(Rest3, Fun,
  88. [{Attr, Value}|Acc])
  89. end);
  90. (_Rest2, _Attr) -> {error, badarg}
  91. end)
  92. end).
  93. %% @doc Parse a media range.
  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/bits >>) ->
  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/bits >>, 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. %% @doc Parse a media type.
  133. -spec media_type(binary(), fun()) -> any().
  134. media_type(Data, Fun) ->
  135. token_ci(Data,
  136. fun (_Rest, <<>>) -> {error, badarg};
  137. (<< $/, Rest/bits >>, Type) ->
  138. token_ci(Rest,
  139. fun (_Rest2, <<>>) -> {error, badarg};
  140. (Rest2, SubType) -> Fun(Rest2, Type, SubType)
  141. end);
  142. (_Rest, _Type) -> {error, badarg}
  143. end).
  144. -spec accept_ext(binary(), fun(), binary(), binary(),
  145. [{binary(), binary()}], 0..1000,
  146. [{binary(), binary()} | binary()]) -> any().
  147. accept_ext(Data, Fun, Type, SubType, Params, Quality, Acc) ->
  148. whitespace(Data,
  149. fun (<< $;, Rest/bits >>) ->
  150. whitespace(Rest,
  151. fun (Rest2) ->
  152. accept_ext_attr(Rest2, Fun,
  153. Type, SubType, Params, Quality, Acc)
  154. end);
  155. (Rest) ->
  156. Fun(Rest, {{Type, SubType, lists:reverse(Params)},
  157. Quality, lists:reverse(Acc)})
  158. end).
  159. -spec accept_ext_attr(binary(), fun(), binary(), binary(),
  160. [{binary(), binary()}], 0..1000,
  161. [{binary(), binary()} | binary()]) -> any().
  162. accept_ext_attr(Data, Fun, Type, SubType, Params, Quality, Acc) ->
  163. token_ci(Data,
  164. fun (_Rest, <<>>) -> {error, badarg};
  165. (<< $=, Rest/bits >>, Attr) ->
  166. accept_ext_value(Rest, Fun, Type, SubType, Params,
  167. Quality, Acc, Attr);
  168. (Rest, Attr) ->
  169. accept_ext(Rest, Fun, Type, SubType, Params,
  170. Quality, [Attr|Acc])
  171. end).
  172. -spec accept_ext_value(binary(), fun(), binary(), binary(),
  173. [{binary(), binary()}], 0..1000,
  174. [{binary(), binary()} | binary()], binary()) -> any().
  175. accept_ext_value(Data, Fun, Type, SubType, Params, Quality, Acc, Attr) ->
  176. word(Data,
  177. fun (Rest, Value) ->
  178. accept_ext(Rest, Fun,
  179. Type, SubType, Params, Quality, [{Attr, Value}|Acc])
  180. end).
  181. %% @doc Parse a conneg header (Accept-Charset, Accept-Encoding),
  182. %% followed by an optional quality value.
  183. -spec conneg(binary(), fun()) -> any().
  184. conneg(Data, Fun) ->
  185. token_ci(Data,
  186. fun (_Rest, <<>>) -> {error, badarg};
  187. (Rest, Conneg) ->
  188. maybe_qparam(Rest,
  189. fun (Rest2, Quality) ->
  190. Fun(Rest2, {Conneg, Quality})
  191. end)
  192. end).
  193. %% @doc Parse a language range, followed by an optional quality value.
  194. -spec language_range(binary(), fun()) -> any().
  195. language_range(<< $*, Rest/bits >>, Fun) ->
  196. language_range_ret(Rest, Fun, '*');
  197. language_range(Data, Fun) ->
  198. language_tag(Data,
  199. fun (Rest, LanguageTag) ->
  200. language_range_ret(Rest, Fun, LanguageTag)
  201. end).
  202. -spec language_range_ret(binary(), fun(), '*' | {binary(), [binary()]}) -> any().
  203. language_range_ret(Data, Fun, LanguageTag) ->
  204. maybe_qparam(Data,
  205. fun (Rest, Quality) ->
  206. Fun(Rest, {LanguageTag, Quality})
  207. end).
  208. -spec language_tag(binary(), fun()) -> any().
  209. language_tag(Data, Fun) ->
  210. alpha(Data,
  211. fun (_Rest, Tag) when byte_size(Tag) =:= 0; byte_size(Tag) > 8 ->
  212. {error, badarg};
  213. (<< $-, Rest/bits >>, Tag) ->
  214. language_subtag(Rest, Fun, Tag, []);
  215. (Rest, Tag) ->
  216. Fun(Rest, Tag)
  217. end).
  218. -spec language_subtag(binary(), fun(), binary(), [binary()]) -> any().
  219. language_subtag(Data, Fun, Tag, Acc) ->
  220. alpha(Data,
  221. fun (_Rest, SubTag) when byte_size(SubTag) =:= 0;
  222. byte_size(SubTag) > 8 -> {error, badarg};
  223. (<< $-, Rest/bits >>, SubTag) ->
  224. language_subtag(Rest, Fun, Tag, [SubTag|Acc]);
  225. (Rest, SubTag) ->
  226. %% Rebuild the full tag now that we know it's correct
  227. Sub = << << $-, S/binary >> || S <- lists:reverse([SubTag|Acc]) >>,
  228. Fun(Rest, << Tag/binary, Sub/binary >>)
  229. end).
  230. -spec maybe_qparam(binary(), fun()) -> any().
  231. maybe_qparam(Data, Fun) ->
  232. whitespace(Data,
  233. fun (<< $;, Rest/bits >>) ->
  234. whitespace(Rest,
  235. fun (Rest2) ->
  236. qparam(Rest2, Fun)
  237. end);
  238. (Rest) ->
  239. Fun(Rest, 1000)
  240. end).
  241. %% @doc Parse a quality parameter string (for example q=0.500).
  242. -spec qparam(binary(), fun()) -> any().
  243. qparam(<< Q, $=, Data/bits >>, Fun) when Q =:= $q; Q =:= $Q ->
  244. qvalue(Data, Fun).
  245. %% @doc Parse either a list of entity tags or a "*".
  246. -spec entity_tag_match(binary()) -> any().
  247. entity_tag_match(<< $*, Rest/bits >>) ->
  248. whitespace(Rest,
  249. fun (<<>>) -> '*';
  250. (_Any) -> {error, badarg}
  251. end);
  252. entity_tag_match(Data) ->
  253. nonempty_list(Data, fun entity_tag/2).
  254. %% @doc Parse an entity-tag.
  255. -spec entity_tag(binary(), fun()) -> any().
  256. entity_tag(<< "W/", Rest/bits >>, Fun) ->
  257. opaque_tag(Rest, Fun, weak);
  258. entity_tag(Data, Fun) ->
  259. opaque_tag(Data, Fun, strong).
  260. -spec opaque_tag(binary(), fun(), weak | strong) -> any().
  261. opaque_tag(Data, Fun, Strength) ->
  262. quoted_string(Data,
  263. fun (_Rest, <<>>) -> {error, badarg};
  264. (Rest, OpaqueTag) -> Fun(Rest, {Strength, OpaqueTag})
  265. end).
  266. %% @doc Parse an HTTP date (RFC1123, RFC850 or asctime date).
  267. %% @end
  268. %%
  269. %% While this may not be the most efficient date parsing we can do,
  270. %% it should work fine for our purposes because all HTTP dates should
  271. %% be sent as RFC1123 dates in HTTP/1.1.
  272. -spec http_date(binary()) -> any().
  273. http_date(Data) ->
  274. case rfc1123_date(Data) of
  275. {error, badarg} ->
  276. case rfc850_date(Data) of
  277. {error, badarg} ->
  278. case asctime_date(Data) of
  279. {error, badarg} ->
  280. {error, badarg};
  281. HTTPDate ->
  282. HTTPDate
  283. end;
  284. HTTPDate ->
  285. HTTPDate
  286. end;
  287. HTTPDate ->
  288. HTTPDate
  289. end.
  290. %% @doc Parse an RFC1123 date.
  291. -spec rfc1123_date(binary()) -> any().
  292. rfc1123_date(Data) ->
  293. wkday(Data,
  294. fun (<< ", ", Rest/bits >>, _WkDay) ->
  295. date1(Rest,
  296. fun (<< " ", Rest2/bits >>, Date) ->
  297. time(Rest2,
  298. fun (<< " GMT", Rest3/bits >>, Time) ->
  299. http_date_ret(Rest3, {Date, Time});
  300. (_Any, _Time) ->
  301. {error, badarg}
  302. end);
  303. (_Any, _Date) ->
  304. {error, badarg}
  305. end);
  306. (_Any, _WkDay) ->
  307. {error, badarg}
  308. end).
  309. %% @doc Parse an RFC850 date.
  310. -spec rfc850_date(binary()) -> any().
  311. %% From the RFC:
  312. %% HTTP/1.1 clients and caches SHOULD assume that an RFC-850 date
  313. %% which appears to be more than 50 years in the future is in fact
  314. %% in the past (this helps solve the "year 2000" problem).
  315. rfc850_date(Data) ->
  316. weekday(Data,
  317. fun (<< ", ", Rest/bits >>, _WeekDay) ->
  318. date2(Rest,
  319. fun (<< " ", Rest2/bits >>, Date) ->
  320. time(Rest2,
  321. fun (<< " GMT", Rest3/bits >>, Time) ->
  322. http_date_ret(Rest3, {Date, Time});
  323. (_Any, _Time) ->
  324. {error, badarg}
  325. end);
  326. (_Any, _Date) ->
  327. {error, badarg}
  328. end);
  329. (_Any, _WeekDay) ->
  330. {error, badarg}
  331. end).
  332. %% @doc Parse an asctime date.
  333. -spec asctime_date(binary()) -> any().
  334. asctime_date(Data) ->
  335. wkday(Data,
  336. fun (<< " ", Rest/bits >>, _WkDay) ->
  337. date3(Rest,
  338. fun (<< " ", Rest2/bits >>, PartialDate) ->
  339. time(Rest2,
  340. fun (<< " ", Rest3/bits >>, Time) ->
  341. asctime_year(Rest3,
  342. PartialDate, Time);
  343. (_Any, _Time) ->
  344. {error, badarg}
  345. end);
  346. (_Any, _PartialDate) ->
  347. {error, badarg}
  348. end);
  349. (_Any, _WkDay) ->
  350. {error, badarg1}
  351. end).
  352. -spec asctime_year(binary(), tuple(), tuple()) -> any().
  353. asctime_year(<< Y1, Y2, Y3, Y4, Rest/bits >>, {Month, Day}, Time)
  354. when Y1 >= $0, Y1 =< $9, Y2 >= $0, Y2 =< $9,
  355. Y3 >= $0, Y3 =< $9, Y4 >= $0, Y4 =< $9 ->
  356. Year = (Y1 - $0) * 1000 + (Y2 - $0) * 100 + (Y3 - $0) * 10 + (Y4 - $0),
  357. http_date_ret(Rest, {{Year, Month, Day}, Time}).
  358. -spec http_date_ret(binary(), tuple()) -> any().
  359. http_date_ret(Data, DateTime = {Date, _Time}) ->
  360. whitespace(Data,
  361. fun (<<>>) ->
  362. case calendar:valid_date(Date) of
  363. true -> DateTime;
  364. false -> {error, badarg}
  365. end;
  366. (_Any) ->
  367. {error, badarg}
  368. end).
  369. %% We never use it, pretty much just checks the wkday is right.
  370. -spec wkday(binary(), fun()) -> any().
  371. wkday(<< WkDay:3/binary, Rest/bits >>, Fun)
  372. when WkDay =:= <<"Mon">>; WkDay =:= <<"Tue">>; WkDay =:= <<"Wed">>;
  373. WkDay =:= <<"Thu">>; WkDay =:= <<"Fri">>; WkDay =:= <<"Sat">>;
  374. WkDay =:= <<"Sun">> ->
  375. Fun(Rest, WkDay);
  376. wkday(_Any, _Fun) ->
  377. {error, badarg}.
  378. %% We never use it, pretty much just checks the weekday is right.
  379. -spec weekday(binary(), fun()) -> any().
  380. weekday(<< "Monday", Rest/binary >>, Fun) ->
  381. Fun(Rest, <<"Monday">>);
  382. weekday(<< "Tuesday", Rest/binary >>, Fun) ->
  383. Fun(Rest, <<"Tuesday">>);
  384. weekday(<< "Wednesday", Rest/binary >>, Fun) ->
  385. Fun(Rest, <<"Wednesday">>);
  386. weekday(<< "Thursday", Rest/binary >>, Fun) ->
  387. Fun(Rest, <<"Thursday">>);
  388. weekday(<< "Friday", Rest/binary >>, Fun) ->
  389. Fun(Rest, <<"Friday">>);
  390. weekday(<< "Saturday", Rest/binary >>, Fun) ->
  391. Fun(Rest, <<"Saturday">>);
  392. weekday(<< "Sunday", Rest/binary >>, Fun) ->
  393. Fun(Rest, <<"Sunday">>);
  394. weekday(_Any, _Fun) ->
  395. {error, badarg}.
  396. -spec date1(binary(), fun()) -> any().
  397. date1(<< D1, D2, " ", M:3/binary, " ", Y1, Y2, Y3, Y4, Rest/bits >>, Fun)
  398. when D1 >= $0, D1 =< $9, D2 >= $0, D2 =< $9,
  399. Y1 >= $0, Y1 =< $9, Y2 >= $0, Y2 =< $9,
  400. Y3 >= $0, Y3 =< $9, Y4 >= $0, Y4 =< $9 ->
  401. case month(M) of
  402. {error, badarg} ->
  403. {error, badarg};
  404. Month ->
  405. Fun(Rest, {
  406. (Y1 - $0) * 1000 + (Y2 - $0) * 100 + (Y3 - $0) * 10 + (Y4 - $0),
  407. Month,
  408. (D1 - $0) * 10 + (D2 - $0)
  409. })
  410. end;
  411. date1(_Data, _Fun) ->
  412. {error, badarg}.
  413. -spec date2(binary(), fun()) -> any().
  414. date2(<< D1, D2, "-", M:3/binary, "-", Y1, Y2, Rest/bits >>, Fun)
  415. when D1 >= $0, D1 =< $9, D2 >= $0, D2 =< $9,
  416. Y1 >= $0, Y1 =< $9, Y2 >= $0, Y2 =< $9 ->
  417. case month(M) of
  418. {error, badarg} ->
  419. {error, badarg};
  420. Month ->
  421. Year = (Y1 - $0) * 10 + (Y2 - $0),
  422. Year2 = case Year > 50 of
  423. true -> Year + 1900;
  424. false -> Year + 2000
  425. end,
  426. Fun(Rest, {
  427. Year2,
  428. Month,
  429. (D1 - $0) * 10 + (D2 - $0)
  430. })
  431. end;
  432. date2(_Data, _Fun) ->
  433. {error, badarg}.
  434. -spec date3(binary(), fun()) -> any().
  435. date3(<< M:3/binary, " ", D1, D2, Rest/bits >>, Fun)
  436. when (D1 >= $0 andalso D1 =< $3) orelse D1 =:= $\s,
  437. D2 >= $0, D2 =< $9 ->
  438. case month(M) of
  439. {error, badarg} ->
  440. {error, badarg};
  441. Month ->
  442. Day = case D1 of
  443. $\s -> D2 - $0;
  444. D1 -> (D1 - $0) * 10 + (D2 - $0)
  445. end,
  446. Fun(Rest, {Month, Day})
  447. end;
  448. date3(_Data, _Fun) ->
  449. {error, badarg}.
  450. -spec month(<< _:24 >>) -> 1..12 | {error, badarg}.
  451. month(<<"Jan">>) -> 1;
  452. month(<<"Feb">>) -> 2;
  453. month(<<"Mar">>) -> 3;
  454. month(<<"Apr">>) -> 4;
  455. month(<<"May">>) -> 5;
  456. month(<<"Jun">>) -> 6;
  457. month(<<"Jul">>) -> 7;
  458. month(<<"Aug">>) -> 8;
  459. month(<<"Sep">>) -> 9;
  460. month(<<"Oct">>) -> 10;
  461. month(<<"Nov">>) -> 11;
  462. month(<<"Dec">>) -> 12;
  463. month(_Any) -> {error, badarg}.
  464. -spec time(binary(), fun()) -> any().
  465. time(<< H1, H2, ":", M1, M2, ":", S1, S2, Rest/bits >>, Fun)
  466. when H1 >= $0, H1 =< $2, H2 >= $0, H2 =< $9,
  467. M1 >= $0, M1 =< $5, M2 >= $0, M2 =< $9,
  468. S1 >= $0, S1 =< $5, S2 >= $0, S2 =< $9 ->
  469. Hour = (H1 - $0) * 10 + (H2 - $0),
  470. case Hour < 24 of
  471. true ->
  472. Time = {
  473. Hour,
  474. (M1 - $0) * 10 + (M2 - $0),
  475. (S1 - $0) * 10 + (S2 - $0)
  476. },
  477. Fun(Rest, Time);
  478. false ->
  479. {error, badarg}
  480. end.
  481. %% @doc Skip whitespace.
  482. -spec whitespace(binary(), fun()) -> any().
  483. whitespace(<< C, Rest/bits >>, Fun)
  484. when C =:= $\s; C =:= $\t ->
  485. whitespace(Rest, Fun);
  486. whitespace(Data, Fun) ->
  487. Fun(Data).
  488. %% @doc Parse a list of digits as a non negative integer.
  489. -spec digits(binary()) -> non_neg_integer() | {error, badarg}.
  490. digits(Data) ->
  491. digits(Data,
  492. fun (Rest, I) ->
  493. whitespace(Rest,
  494. fun (<<>>) ->
  495. I;
  496. (_Rest2) ->
  497. {error, badarg}
  498. end)
  499. end).
  500. -spec digits(binary(), fun()) -> any().
  501. digits(<< C, Rest/bits >>, Fun)
  502. when C >= $0, C =< $9 ->
  503. digits(Rest, Fun, C - $0);
  504. digits(_Data, _Fun) ->
  505. {error, badarg}.
  506. -spec digits(binary(), fun(), non_neg_integer()) -> any().
  507. digits(<< C, Rest/bits >>, Fun, Acc)
  508. when C >= $0, C =< $9 ->
  509. digits(Rest, Fun, Acc * 10 + (C - $0));
  510. digits(Data, Fun, Acc) ->
  511. Fun(Data, Acc).
  512. %% @doc Parse a list of case-insensitive alpha characters.
  513. %%
  514. %% Changes all characters to lowercase.
  515. -spec alpha(binary(), fun()) -> any().
  516. alpha(Data, Fun) ->
  517. alpha(Data, Fun, <<>>).
  518. -spec alpha(binary(), fun(), binary()) -> any().
  519. alpha(<<>>, Fun, Acc) ->
  520. Fun(<<>>, Acc);
  521. alpha(<< C, Rest/bits >>, Fun, Acc)
  522. when C >= $a andalso C =< $z;
  523. C >= $A andalso C =< $Z ->
  524. C2 = cowboy_bstr:char_to_lower(C),
  525. alpha(Rest, Fun, << Acc/binary, C2 >>);
  526. alpha(Data, Fun, Acc) ->
  527. Fun(Data, Acc).
  528. %% @doc Parse either a token or a quoted string.
  529. -spec word(binary(), fun()) -> any().
  530. word(Data = << $", _/bits >>, Fun) ->
  531. quoted_string(Data, Fun);
  532. word(Data, Fun) ->
  533. token(Data,
  534. fun (_Rest, <<>>) -> {error, badarg};
  535. (Rest, Token) -> Fun(Rest, Token)
  536. end).
  537. %% @doc Parse a case-insensitive token.
  538. %%
  539. %% Changes all characters to lowercase.
  540. -spec token_ci(binary(), fun()) -> any().
  541. token_ci(Data, Fun) ->
  542. token(Data, Fun, ci, <<>>).
  543. %% @doc Parse a token.
  544. -spec token(binary(), fun()) -> any().
  545. token(Data, Fun) ->
  546. token(Data, Fun, cs, <<>>).
  547. -spec token(binary(), fun(), ci | cs, binary()) -> any().
  548. token(<<>>, Fun, _Case, Acc) ->
  549. Fun(<<>>, Acc);
  550. token(Data = << C, _Rest/bits >>, Fun, _Case, Acc)
  551. when C =:= $(; C =:= $); C =:= $<; C =:= $>; C =:= $@;
  552. C =:= $,; C =:= $;; C =:= $:; C =:= $\\; C =:= $";
  553. C =:= $/; C =:= $[; C =:= $]; C =:= $?; C =:= $=;
  554. C =:= ${; C =:= $}; C =:= $\s; C =:= $\t;
  555. C < 32; C =:= 127 ->
  556. Fun(Data, Acc);
  557. token(<< C, Rest/bits >>, Fun, Case = ci, Acc) ->
  558. C2 = cowboy_bstr:char_to_lower(C),
  559. token(Rest, Fun, Case, << Acc/binary, C2 >>);
  560. token(<< C, Rest/bits >>, Fun, Case, Acc) ->
  561. token(Rest, Fun, Case, << Acc/binary, C >>).
  562. %% @doc Parse a quoted string.
  563. -spec quoted_string(binary(), fun()) -> any().
  564. quoted_string(<< $", Rest/bits >>, Fun) ->
  565. quoted_string(Rest, Fun, <<>>).
  566. -spec quoted_string(binary(), fun(), binary()) -> any().
  567. quoted_string(<<>>, _Fun, _Acc) ->
  568. {error, badarg};
  569. quoted_string(<< $", Rest/bits >>, Fun, Acc) ->
  570. Fun(Rest, Acc);
  571. quoted_string(<< $\\, C, Rest/bits >>, Fun, Acc) ->
  572. quoted_string(Rest, Fun, << Acc/binary, C >>);
  573. quoted_string(<< C, Rest/bits >>, Fun, Acc) ->
  574. quoted_string(Rest, Fun, << Acc/binary, C >>).
  575. %% @doc Parse a quality value.
  576. -spec qvalue(binary(), fun()) -> any().
  577. qvalue(<< $0, $., Rest/bits >>, Fun) ->
  578. qvalue(Rest, Fun, 0, 100);
  579. qvalue(<< $0, Rest/bits >>, Fun) ->
  580. Fun(Rest, 0);
  581. qvalue(<< $1, $., $0, $0, $0, Rest/bits >>, Fun) ->
  582. Fun(Rest, 1000);
  583. qvalue(<< $1, $., $0, $0, Rest/bits >>, Fun) ->
  584. Fun(Rest, 1000);
  585. qvalue(<< $1, $., $0, Rest/bits >>, Fun) ->
  586. Fun(Rest, 1000);
  587. qvalue(<< $1, Rest/bits >>, Fun) ->
  588. Fun(Rest, 1000);
  589. qvalue(_Data, _Fun) ->
  590. {error, badarg}.
  591. -spec qvalue(binary(), fun(), integer(), 1 | 10 | 100) -> any().
  592. qvalue(Data, Fun, Q, 0) ->
  593. Fun(Data, Q);
  594. qvalue(<< C, Rest/bits >>, Fun, Q, M)
  595. when C >= $0, C =< $9 ->
  596. qvalue(Rest, Fun, Q + (C - $0) * M, M div 10);
  597. qvalue(Data, Fun, Q, _M) ->
  598. Fun(Data, Q).
  599. %% Interpretation.
  600. %% @doc Walk through a tokens list and return whether
  601. %% the connection is keepalive or closed.
  602. %%
  603. %% The connection token is expected to be lower-case.
  604. -spec connection_to_atom([binary()]) -> keepalive | close.
  605. connection_to_atom([]) ->
  606. keepalive;
  607. connection_to_atom([<<"keep-alive">>|_Tail]) ->
  608. keepalive;
  609. connection_to_atom([<<"close">>|_Tail]) ->
  610. close;
  611. connection_to_atom([_Any|Tail]) ->
  612. connection_to_atom(Tail).
  613. %% @doc Decode a URL encoded binary.
  614. %% @equiv urldecode(Bin, crash)
  615. -spec urldecode(binary()) -> binary().
  616. urldecode(Bin) when is_binary(Bin) ->
  617. urldecode(Bin, <<>>, crash).
  618. %% @doc Decode a URL encoded binary.
  619. %% The second argument specifies how to handle percent characters that are not
  620. %% followed by two valid hex characters. Use `skip' to ignore such errors,
  621. %% if `crash' is used the function will fail with the reason `badarg'.
  622. -spec urldecode(binary(), crash | skip) -> binary().
  623. urldecode(Bin, OnError) when is_binary(Bin) ->
  624. urldecode(Bin, <<>>, OnError).
  625. -spec urldecode(binary(), binary(), crash | skip) -> binary().
  626. urldecode(<<$%, H, L, Rest/binary>>, Acc, OnError) ->
  627. G = unhex(H),
  628. M = unhex(L),
  629. if G =:= error; M =:= error ->
  630. case OnError of skip -> ok; crash -> erlang:error(badarg) end,
  631. urldecode(<<H, L, Rest/binary>>, <<Acc/binary, $%>>, OnError);
  632. true ->
  633. urldecode(Rest, <<Acc/binary, (G bsl 4 bor M)>>, OnError)
  634. end;
  635. urldecode(<<$%, Rest/binary>>, Acc, OnError) ->
  636. case OnError of skip -> ok; crash -> erlang:error(badarg) end,
  637. urldecode(Rest, <<Acc/binary, $%>>, OnError);
  638. urldecode(<<$+, Rest/binary>>, Acc, OnError) ->
  639. urldecode(Rest, <<Acc/binary, $ >>, OnError);
  640. urldecode(<<C, Rest/binary>>, Acc, OnError) ->
  641. urldecode(Rest, <<Acc/binary, C>>, OnError);
  642. urldecode(<<>>, Acc, _OnError) ->
  643. Acc.
  644. -spec unhex(byte()) -> byte() | error.
  645. unhex(C) when C >= $0, C =< $9 -> C - $0;
  646. unhex(C) when C >= $A, C =< $F -> C - $A + 10;
  647. unhex(C) when C >= $a, C =< $f -> C - $a + 10;
  648. unhex(_) -> error.
  649. %% Tests.
  650. -ifdef(TEST).
  651. nonempty_charset_list_test_() ->
  652. %% {Value, Result}
  653. Tests = [
  654. {<<>>, {error, badarg}},
  655. {<<"iso-8859-5, unicode-1-1;q=0.8">>, [
  656. {<<"iso-8859-5">>, 1000},
  657. {<<"unicode-1-1">>, 800}
  658. ]}
  659. ],
  660. [{V, fun() -> R = nonempty_list(V, fun conneg/2) end} || {V, R} <- Tests].
  661. nonempty_language_range_list_test_() ->
  662. %% {Value, Result}
  663. Tests = [
  664. {<<"da, en-gb;q=0.8, en;q=0.7">>, [
  665. {<<"da">>, 1000},
  666. {<<"en-gb">>, 800},
  667. {<<"en">>, 700}
  668. ]},
  669. {<<"en, en-US, en-cockney, i-cherokee, x-pig-latin">>, [
  670. {<<"en">>, 1000},
  671. {<<"en-us">>, 1000},
  672. {<<"en-cockney">>, 1000},
  673. {<<"i-cherokee">>, 1000},
  674. {<<"x-pig-latin">>, 1000}
  675. ]}
  676. ],
  677. [{V, fun() -> R = nonempty_list(V, fun language_range/2) end}
  678. || {V, R} <- Tests].
  679. nonempty_token_list_test_() ->
  680. %% {Value, Result}
  681. Tests = [
  682. {<<>>, {error, badarg}},
  683. {<<" ">>, {error, badarg}},
  684. {<<" , ">>, {error, badarg}},
  685. {<<",,,">>, {error, badarg}},
  686. {<<"a b">>, {error, badarg}},
  687. {<<"a , , , ">>, [<<"a">>]},
  688. {<<" , , , a">>, [<<"a">>]},
  689. {<<"a, , b">>, [<<"a">>, <<"b">>]},
  690. {<<"close">>, [<<"close">>]},
  691. {<<"keep-alive, upgrade">>, [<<"keep-alive">>, <<"upgrade">>]}
  692. ],
  693. [{V, fun() -> R = nonempty_list(V, fun token/2) end} || {V, R} <- Tests].
  694. media_range_list_test_() ->
  695. %% {Tokens, Result}
  696. Tests = [
  697. {<<"audio/*; q=0.2, audio/basic">>, [
  698. {{<<"audio">>, <<"*">>, []}, 200, []},
  699. {{<<"audio">>, <<"basic">>, []}, 1000, []}
  700. ]},
  701. {<<"text/plain; q=0.5, text/html, "
  702. "text/x-dvi; q=0.8, text/x-c">>, [
  703. {{<<"text">>, <<"plain">>, []}, 500, []},
  704. {{<<"text">>, <<"html">>, []}, 1000, []},
  705. {{<<"text">>, <<"x-dvi">>, []}, 800, []},
  706. {{<<"text">>, <<"x-c">>, []}, 1000, []}
  707. ]},
  708. {<<"text/*, text/html, text/html;level=1, */*">>, [
  709. {{<<"text">>, <<"*">>, []}, 1000, []},
  710. {{<<"text">>, <<"html">>, []}, 1000, []},
  711. {{<<"text">>, <<"html">>, [{<<"level">>, <<"1">>}]}, 1000, []},
  712. {{<<"*">>, <<"*">>, []}, 1000, []}
  713. ]},
  714. {<<"text/*;q=0.3, text/html;q=0.7, text/html;level=1, "
  715. "text/html;level=2;q=0.4, */*;q=0.5">>, [
  716. {{<<"text">>, <<"*">>, []}, 300, []},
  717. {{<<"text">>, <<"html">>, []}, 700, []},
  718. {{<<"text">>, <<"html">>, [{<<"level">>, <<"1">>}]}, 1000, []},
  719. {{<<"text">>, <<"html">>, [{<<"level">>, <<"2">>}]}, 400, []},
  720. {{<<"*">>, <<"*">>, []}, 500, []}
  721. ]},
  722. {<<"text/html;level=1;quoted=\"hi hi hi\";"
  723. "q=0.123;standalone;complex=gits, text/plain">>, [
  724. {{<<"text">>, <<"html">>,
  725. [{<<"level">>, <<"1">>}, {<<"quoted">>, <<"hi hi hi">>}]}, 123,
  726. [<<"standalone">>, {<<"complex">>, <<"gits">>}]},
  727. {{<<"text">>, <<"plain">>, []}, 1000, []}
  728. ]}
  729. ],
  730. [{V, fun() -> R = list(V, fun media_range/2) end} || {V, R} <- Tests].
  731. entity_tag_match_test_() ->
  732. %% {Tokens, Result}
  733. Tests = [
  734. {<<"\"xyzzy\"">>, [{strong, <<"xyzzy">>}]},
  735. {<<"\"xyzzy\", W/\"r2d2xxxx\", \"c3piozzzz\"">>,
  736. [{strong, <<"xyzzy">>},
  737. {weak, <<"r2d2xxxx">>},
  738. {strong, <<"c3piozzzz">>}]},
  739. {<<"*">>, '*'}
  740. ],
  741. [{V, fun() -> R = entity_tag_match(V) end} || {V, R} <- Tests].
  742. http_date_test_() ->
  743. %% {Tokens, Result}
  744. Tests = [
  745. {<<"Sun, 06 Nov 1994 08:49:37 GMT">>, {{1994, 11, 6}, {8, 49, 37}}},
  746. {<<"Sunday, 06-Nov-94 08:49:37 GMT">>, {{1994, 11, 6}, {8, 49, 37}}},
  747. {<<"Sun Nov 6 08:49:37 1994">>, {{1994, 11, 6}, {8, 49, 37}}}
  748. ],
  749. [{V, fun() -> R = http_date(V) end} || {V, R} <- Tests].
  750. rfc1123_date_test_() ->
  751. %% {Tokens, Result}
  752. Tests = [
  753. {<<"Sun, 06 Nov 1994 08:49:37 GMT">>, {{1994, 11, 6}, {8, 49, 37}}}
  754. ],
  755. [{V, fun() -> R = rfc1123_date(V) end} || {V, R} <- Tests].
  756. rfc850_date_test_() ->
  757. %% {Tokens, Result}
  758. Tests = [
  759. {<<"Sunday, 06-Nov-94 08:49:37 GMT">>, {{1994, 11, 6}, {8, 49, 37}}}
  760. ],
  761. [{V, fun() -> R = rfc850_date(V) end} || {V, R} <- Tests].
  762. asctime_date_test_() ->
  763. %% {Tokens, Result}
  764. Tests = [
  765. {<<"Sun Nov 6 08:49:37 1994">>, {{1994, 11, 6}, {8, 49, 37}}}
  766. ],
  767. [{V, fun() -> R = asctime_date(V) end} || {V, R} <- Tests].
  768. connection_to_atom_test_() ->
  769. %% {Tokens, Result}
  770. Tests = [
  771. {[<<"close">>], close},
  772. {[<<"keep-alive">>], keepalive},
  773. {[<<"keep-alive">>, <<"upgrade">>], keepalive}
  774. ],
  775. [{lists:flatten(io_lib:format("~p", [T])),
  776. fun() -> R = connection_to_atom(T) end} || {T, R} <- Tests].
  777. content_type_test_() ->
  778. %% {ContentType, Result}
  779. Tests = [
  780. {<<"text/plain; charset=iso-8859-4">>,
  781. {<<"text">>, <<"plain">>, [{<<"charset">>, <<"iso-8859-4">>}]}},
  782. {<<"multipart/form-data \t;Boundary=\"MultipartIsUgly\"">>,
  783. {<<"multipart">>, <<"form-data">>, [
  784. {<<"boundary">>, <<"MultipartIsUgly">>}
  785. ]}},
  786. {<<"foo/bar; one=FirstParam; two=SecondParam">>,
  787. {<<"foo">>, <<"bar">>, [
  788. {<<"one">>, <<"FirstParam">>},
  789. {<<"two">>, <<"SecondParam">>}
  790. ]}}
  791. ],
  792. [{V, fun () -> R = content_type(V) end} || {V, R} <- Tests].
  793. digits_test_() ->
  794. %% {Digits, Result}
  795. Tests = [
  796. {<<"42 ">>, 42},
  797. {<<"69\t">>, 69},
  798. {<<"1337">>, 1337}
  799. ],
  800. [{V, fun() -> R = digits(V) end} || {V, R} <- Tests].
  801. urldecode_test_() ->
  802. Tests = [
  803. {<<" ">>, <<"%20">>},
  804. {<<" ">>, <<"+">>},
  805. {<<0>>, <<"%00">>},
  806. {<<255>>, <<"%fF">>}
  807. ],
  808. [{I, ?_assertEqual(E, urldecode(I))} || {E, I} <- Tests].
  809. -endif.