cowboy_http.erl 41 KB

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