cow_http_hd.erl 52 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440
  1. %% Copyright (c) 2014, Loïc Hoguin <essen@ninenines.eu>
  2. %%
  3. %% Permission to use, copy, modify, and/or distribute this software for any
  4. %% purpose with or without fee is hereby granted, provided that the above
  5. %% copyright notice and this permission notice appear in all copies.
  6. %%
  7. %% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  8. %% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  9. %% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  10. %% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  11. %% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  12. %% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  13. %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  14. -module(cow_http_hd).
  15. -export([parse_accept/1]).
  16. -export([parse_accept_charset/1]).
  17. -export([parse_accept_encoding/1]).
  18. -export([parse_accept_language/1]).
  19. -export([parse_connection/1]).
  20. -export([parse_content_length/1]).
  21. -export([parse_content_type/1]).
  22. -export([parse_date/1]).
  23. -export([parse_etag/1]).
  24. -export([parse_expect/1]).
  25. -export([parse_if_match/1]).
  26. -export([parse_if_modified_since/1]).
  27. -export([parse_if_none_match/1]).
  28. -export([parse_if_unmodified_since/1]).
  29. -export([parse_last_modified/1]).
  30. -export([parse_max_forwards/1]).
  31. -export([parse_transfer_encoding/1]).
  32. -export([parse_upgrade/1]).
  33. -type etag() :: {weak | strong, binary()}.
  34. -export_type([etag/0]).
  35. -type media_type() :: {binary(), binary(), [{binary(), binary()}]}.
  36. -export_type([media_type/0]).
  37. -type qvalue() :: 0..1000.
  38. -export_type([qvalue/0]).
  39. -include("cow_inline.hrl").
  40. -ifdef(TEST).
  41. -include_lib("triq/include/triq.hrl").
  42. ows() ->
  43. list(oneof([$\s, $\t])).
  44. alpha_chars() -> "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".
  45. alphanum_chars() -> "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".
  46. alpha() ->
  47. oneof(alpha_chars()).
  48. alphanum() ->
  49. oneof(alphanum_chars()).
  50. tchar() ->
  51. frequency([
  52. {1, oneof([$!, $#, $$, $%, $&, $', $*, $+, $-, $., $^, $_, $`, $|, $~])},
  53. {99, oneof(alphanum_chars())}
  54. ]).
  55. token() ->
  56. ?LET(T,
  57. non_empty(list(tchar())),
  58. list_to_binary(T)).
  59. obs_text() ->
  60. [128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,
  61. 146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,
  62. 164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,
  63. 182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,
  64. 200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,
  65. 218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,
  66. 236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,
  67. 254,255].
  68. qdtext() ->
  69. frequency([
  70. {99, oneof("\t\s!#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~")},
  71. {1, oneof(obs_text())}
  72. ]).
  73. quoted_pair() ->
  74. [$\\, frequency([
  75. {99, oneof("\t\s!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~")},
  76. {1, oneof(obs_text())}
  77. ])].
  78. quoted_string() ->
  79. [$", list(frequency([{100, qdtext()}, {1, quoted_pair()}])), $"].
  80. %% Helper function for ( token / quoted-string ) values.
  81. unquote([$", V, $"]) -> unquote(V, <<>>);
  82. unquote(V) -> V.
  83. unquote([], Acc) -> Acc;
  84. unquote([[$\\, C]|Tail], Acc) -> unquote(Tail, << Acc/binary, C >>);
  85. unquote([C|Tail], Acc) -> unquote(Tail, << Acc/binary, C >>).
  86. parameter() ->
  87. ?SUCHTHAT({K, _, _, _},
  88. {token(), oneof([token(), quoted_string()]), ows(), ows()},
  89. K =/= <<"q">>).
  90. weight() ->
  91. frequency([
  92. {90, int(0, 1000)},
  93. {10, undefined}
  94. ]).
  95. %% Helper function for weight's qvalue formatting.
  96. qvalue_to_iodata(0) -> <<"0">>;
  97. qvalue_to_iodata(Q) when Q < 10 -> [<<"0.00">>, integer_to_binary(Q)];
  98. qvalue_to_iodata(Q) when Q < 100 -> [<<"0.0">>, integer_to_binary(Q)];
  99. qvalue_to_iodata(Q) when Q < 1000 -> [<<"0.">>, integer_to_binary(Q)];
  100. qvalue_to_iodata(1000) -> <<"1">>.
  101. -endif.
  102. %% @doc Parse the Accept header.
  103. -spec parse_accept(binary()) -> [{media_type(), qvalue(), [binary() | {binary(), binary()}]}].
  104. parse_accept(<<"*/*">>) ->
  105. [{{<<"*">>, <<"*">>, []}, 1000, []}];
  106. parse_accept(Accept) ->
  107. media_range_list(Accept, []).
  108. media_range_list(<<>>, Acc) -> lists:reverse(Acc);
  109. media_range_list(<< $\s, R/bits >>, Acc) -> media_range_list(R, Acc);
  110. media_range_list(<< $\t, R/bits >>, Acc) -> media_range_list(R, Acc);
  111. media_range_list(<< $,, R/bits >>, Acc) -> media_range_list(R, Acc);
  112. media_range_list(<< C, R/bits >>, Acc) when ?IS_TOKEN(C) ->
  113. case C of
  114. ?INLINE_LOWERCASE(media_range_type, R, Acc, <<>>)
  115. end.
  116. media_range_type(<< $/, R/bits >>, Acc, T) -> media_range_subtype(R, Acc, T, <<>>);
  117. %% Special clause for badly behaving user agents that send * instead of */*.
  118. media_range_type(<< $;, R/bits >>, Acc, <<"*">>) -> media_range_before_param(R, Acc, <<"*">>, <<"*">>, []);
  119. media_range_type(<< C, R/bits >>, Acc, T) when ?IS_TOKEN(C) ->
  120. case C of
  121. ?INLINE_LOWERCASE(media_range_type, R, Acc, T)
  122. end.
  123. media_range_subtype(<<>>, Acc, T, S) when S =/= <<>> -> lists:reverse([{{T, S, []}, 1000, []}|Acc]);
  124. media_range_subtype(<< $,, R/bits >>, Acc, T, S) when S =/= <<>> -> media_range_list(R, [{{T, S, []}, 1000, []}|Acc]);
  125. media_range_subtype(<< $;, R/bits >>, Acc, T, S) when S =/= <<>> -> media_range_before_param(R, Acc, T, S, []);
  126. media_range_subtype(<< $\s, R/bits >>, Acc, T, S) when S =/= <<>> -> media_range_before_semicolon(R, Acc, T, S, []);
  127. media_range_subtype(<< $\t, R/bits >>, Acc, T, S) when S =/= <<>> -> media_range_before_semicolon(R, Acc, T, S, []);
  128. media_range_subtype(<< C, R/bits >>, Acc, T, S) when ?IS_TOKEN(C) ->
  129. case C of
  130. ?INLINE_LOWERCASE(media_range_subtype, R, Acc, T, S)
  131. end.
  132. media_range_before_semicolon(<<>>, Acc, T, S, P) -> lists:reverse([{{T, S, lists:reverse(P)}, 1000, []}|Acc]);
  133. media_range_before_semicolon(<< $,, R/bits >>, Acc, T, S, P) -> media_range_list(R, [{{T, S, lists:reverse(P)}, 1000, []}|Acc]);
  134. media_range_before_semicolon(<< $;, R/bits >>, Acc, T, S, P) -> media_range_before_param(R, Acc, T, S, P);
  135. media_range_before_semicolon(<< $\s, R/bits >>, Acc, T, S, P) -> media_range_before_semicolon(R, Acc, T, S, P);
  136. media_range_before_semicolon(<< $\t, R/bits >>, Acc, T, S, P) -> media_range_before_semicolon(R, Acc, T, S, P).
  137. media_range_before_param(<< $\s, R/bits >>, Acc, T, S, P) -> media_range_before_param(R, Acc, T, S, P);
  138. media_range_before_param(<< $\t, R/bits >>, Acc, T, S, P) -> media_range_before_param(R, Acc, T, S, P);
  139. %% Special clause for badly behaving user agents that send .123 instead of 0.123.
  140. media_range_before_param(<< $q, $=, $., R/bits >>, Acc, T, S, P) -> media_range_broken_weight(R, Acc, T, S, P);
  141. media_range_before_param(<< $q, $=, R/bits >>, Acc, T, S, P) -> media_range_weight(R, Acc, T, S, P);
  142. media_range_before_param(<< C, R/bits >>, Acc, T, S, P) when ?IS_TOKEN(C) ->
  143. case C of
  144. ?INLINE_LOWERCASE(media_range_param, R, Acc, T, S, P, <<>>)
  145. end.
  146. media_range_param(<< $=, $", R/bits >>, Acc, T, S, P, K) -> media_range_quoted(R, Acc, T, S, P, K, <<>>);
  147. media_range_param(<< $=, R/bits >>, Acc, T, S, P, K) -> media_range_value(R, Acc, T, S, P, K, <<>>);
  148. media_range_param(<< C, R/bits >>, Acc, T, S, P, K) when ?IS_TOKEN(C) ->
  149. case C of
  150. ?INLINE_LOWERCASE(media_range_param, R, Acc, T, S, P, K)
  151. end.
  152. media_range_quoted(<< $", R/bits >>, Acc, T, S, P, K, V) -> media_range_before_semicolon(R, Acc, T, S, [{K, V}|P]);
  153. media_range_quoted(<< $\\, C, R/bits >>, Acc, T, S, P, K, V) when ?IS_VCHAR(C) -> media_range_quoted(R, Acc, T, S, P, K, << V/binary, C >>);
  154. media_range_quoted(<< C, R/bits >>, Acc, T, S, P, K, V) when ?IS_VCHAR(C) -> media_range_quoted(R, Acc, T, S, P, K, << V/binary, C >>).
  155. media_range_value(<<>>, Acc, T, S, P, K, V) -> lists:reverse([{{T, S, lists:reverse([{K, V}|P])}, 1000, []}|Acc]);
  156. media_range_value(<< $,, R/bits >>, Acc, T, S, P, K, V) -> media_range_list(R, [{{T, S, lists:reverse([{K, V}|P])}, 1000, []}|Acc]);
  157. media_range_value(<< $;, R/bits >>, Acc, T, S, P, K, V) -> media_range_before_param(R, Acc, T, S, [{K, V}|P]);
  158. media_range_value(<< $\s, R/bits >>, Acc, T, S, P, K, V) -> media_range_before_semicolon(R, Acc, T, S, [{K, V}|P]);
  159. media_range_value(<< $\t, R/bits >>, Acc, T, S, P, K, V) -> media_range_before_semicolon(R, Acc, T, S, [{K, V}|P]);
  160. media_range_value(<< C, R/bits >>, Acc, T, S, P, K, V) when ?IS_TOKEN(C) -> media_range_value(R, Acc, T, S, P, K, << V/binary, C >>).
  161. %% Special function for badly behaving user agents that send .123 instead of 0.123.
  162. media_range_broken_weight(<< A, B, C, R/bits >>, Acc, T, S, P)
  163. when A >= $0, A =< $9, B >= $0, B =< $9, C >= $0, C =< $9 ->
  164. accept_before_semicolon(R, Acc, T, S, P, (A - $0) * 100 + (B - $0) * 10 + (C - $0), []);
  165. media_range_broken_weight(<< A, B, R/bits >>, Acc, T, S, P)
  166. when A >= $0, A =< $9, B >= $0, B =< $9 ->
  167. accept_before_semicolon(R, Acc, T, S, P, (A - $0) * 100 + (B - $0) * 10, []);
  168. media_range_broken_weight(<< A, R/bits >>, Acc, T, S, P)
  169. when A >= $0, A =< $9 ->
  170. accept_before_semicolon(R, Acc, T, S, P, (A - $0) * 100, []).
  171. media_range_weight(<< "1.000", R/bits >>, Acc, T, S, P) -> accept_before_semicolon(R, Acc, T, S, P, 1000, []);
  172. media_range_weight(<< "1.00", R/bits >>, Acc, T, S, P) -> accept_before_semicolon(R, Acc, T, S, P, 1000, []);
  173. media_range_weight(<< "1.0", R/bits >>, Acc, T, S, P) -> accept_before_semicolon(R, Acc, T, S, P, 1000, []);
  174. media_range_weight(<< "1.", R/bits >>, Acc, T, S, P) -> accept_before_semicolon(R, Acc, T, S, P, 1000, []);
  175. media_range_weight(<< "1", R/bits >>, Acc, T, S, P) -> accept_before_semicolon(R, Acc, T, S, P, 1000, []);
  176. media_range_weight(<< "0.", A, B, C, R/bits >>, Acc, T, S, P)
  177. when A >= $0, A =< $9, B >= $0, B =< $9, C >= $0, C =< $9 ->
  178. accept_before_semicolon(R, Acc, T, S, P, (A - $0) * 100 + (B - $0) * 10 + (C - $0), []);
  179. media_range_weight(<< "0.", A, B, R/bits >>, Acc, T, S, P)
  180. when A >= $0, A =< $9, B >= $0, B =< $9 ->
  181. accept_before_semicolon(R, Acc, T, S, P, (A - $0) * 100 + (B - $0) * 10, []);
  182. media_range_weight(<< "0.", A, R/bits >>, Acc, T, S, P)
  183. when A >= $0, A =< $9 ->
  184. accept_before_semicolon(R, Acc, T, S, P, (A - $0) * 100, []);
  185. media_range_weight(<< "0.", R/bits >>, Acc, T, S, P) -> accept_before_semicolon(R, Acc, T, S, P, 0, []);
  186. media_range_weight(<< "0", R/bits >>, Acc, T, S, P) -> accept_before_semicolon(R, Acc, T, S, P, 0, []).
  187. accept_before_semicolon(<<>>, Acc, T, S, P, Q, E) -> lists:reverse([{{T, S, lists:reverse(P)}, Q, lists:reverse(E)}|Acc]);
  188. accept_before_semicolon(<< $,, R/bits >>, Acc, T, S, P, Q, E) -> media_range_list(R, [{{T, S, lists:reverse(P)}, Q, lists:reverse(E)}|Acc]);
  189. accept_before_semicolon(<< $;, R/bits >>, Acc, T, S, P, Q, E) -> accept_before_ext(R, Acc, T, S, P, Q, E);
  190. accept_before_semicolon(<< $\s, R/bits >>, Acc, T, S, P, Q, E) -> accept_before_semicolon(R, Acc, T, S, P, Q, E);
  191. accept_before_semicolon(<< $\t, R/bits >>, Acc, T, S, P, Q, E) -> accept_before_semicolon(R, Acc, T, S, P, Q, E).
  192. accept_before_ext(<< $\s, R/bits >>, Acc, T, S, P, Q, E) -> accept_before_ext(R, Acc, T, S, P, Q, E);
  193. accept_before_ext(<< $\t, R/bits >>, Acc, T, S, P, Q, E) -> accept_before_ext(R, Acc, T, S, P, Q, E);
  194. accept_before_ext(<< C, R/bits >>, Acc, T, S, P, Q, E) when ?IS_TOKEN(C) ->
  195. case C of
  196. ?INLINE_LOWERCASE(accept_ext, R, Acc, T, S, P, Q, E, <<>>)
  197. end.
  198. accept_ext(<<>>, Acc, T, S, P, Q, E, K) -> lists:reverse([{{T, S, lists:reverse(P)}, Q, lists:reverse([K|E])}|Acc]);
  199. accept_ext(<< $,, R/bits >>, Acc, T, S, P, Q, E, K) -> media_range_list(R, [{{T, S, lists:reverse(P)}, Q, lists:reverse([K|E])}|Acc]);
  200. accept_ext(<< $;, R/bits >>, Acc, T, S, P, Q, E, K) -> accept_before_ext(R, Acc, T, S, P, Q, [K|E]);
  201. accept_ext(<< $\s, R/bits >>, Acc, T, S, P, Q, E, K) -> accept_before_semicolon(R, Acc, T, S, P, Q, [K|E]);
  202. accept_ext(<< $\t, R/bits >>, Acc, T, S, P, Q, E, K) -> accept_before_semicolon(R, Acc, T, S, P, Q, [K|E]);
  203. accept_ext(<< $=, $", R/bits >>, Acc, T, S, P, Q, E, K) -> accept_quoted(R, Acc, T, S, P, Q, E, K, <<>>);
  204. accept_ext(<< $=, R/bits >>, Acc, T, S, P, Q, E, K) -> accept_value(R, Acc, T, S, P, Q, E, K, <<>>);
  205. accept_ext(<< C, R/bits >>, Acc, T, S, P, Q, E, K) when ?IS_TOKEN(C) ->
  206. case C of
  207. ?INLINE_LOWERCASE(accept_ext, R, Acc, T, S, P, Q, E, K)
  208. end.
  209. accept_quoted(<< $", R/bits >>, Acc, T, S, P, Q, E, K, V) -> accept_before_semicolon(R, Acc, T, S, P, Q, [{K, V}|E]);
  210. accept_quoted(<< $\\, C, R/bits >>, Acc, T, S, P, Q, E, K, V) when ?IS_VCHAR(C) -> accept_quoted(R, Acc, T, S, P, Q, E, K, << V/binary, C >>);
  211. accept_quoted(<< C, R/bits >>, Acc, T, S, P, Q, E, K, V) when ?IS_VCHAR(C) -> accept_quoted(R, Acc, T, S, P, Q, E, K, << V/binary, C >>).
  212. accept_value(<<>>, Acc, T, S, P, Q, E, K, V) -> lists:reverse([{{T, S, lists:reverse(P)}, Q, lists:reverse([{K, V}|E])}|Acc]);
  213. accept_value(<< $,, R/bits >>, Acc, T, S, P, Q, E, K, V) -> media_range_list(R, [{{T, S, lists:reverse(P)}, Q, lists:reverse([{K, V}|E])}|Acc]);
  214. accept_value(<< $;, R/bits >>, Acc, T, S, P, Q, E, K, V) -> accept_before_ext(R, Acc, T, S, P, Q, [{K, V}|E]);
  215. accept_value(<< $\s, R/bits >>, Acc, T, S, P, Q, E, K, V) -> accept_before_semicolon(R, Acc, T, S, P, Q, [{K, V}|E]);
  216. accept_value(<< $\t, R/bits >>, Acc, T, S, P, Q, E, K, V) -> accept_before_semicolon(R, Acc, T, S, P, Q, [{K, V}|E]);
  217. accept_value(<< C, R/bits >>, Acc, T, S, P, Q, E, K, V) when ?IS_TOKEN(C) -> accept_value(R, Acc, T, S, P, Q, E, K, << V/binary, C >>).
  218. -ifdef(TEST).
  219. accept_ext() ->
  220. oneof([token(), parameter()]).
  221. accept_params() ->
  222. frequency([
  223. {90, []},
  224. {10, list(accept_ext())}
  225. ]).
  226. accept() ->
  227. ?LET({T, S, P, W, E},
  228. {token(), token(), list(parameter()), weight(), accept_params()},
  229. {T, S, P, W, E, iolist_to_binary([T, $/, S,
  230. [[OWS1, $;, OWS2, K, $=, V] || {K, V, OWS1, OWS2} <- P],
  231. case W of
  232. undefined -> [];
  233. _ -> [
  234. [<<";q=">>, qvalue_to_iodata(W)],
  235. [case Ext of
  236. {K, V, OWS1, OWS2} -> [OWS1, $;, OWS2, K, $=, V];
  237. K -> [$;, K]
  238. end || Ext <- E]]
  239. end])}
  240. ).
  241. prop_parse_accept() ->
  242. ?FORALL(L,
  243. non_empty(list(accept())),
  244. begin
  245. << _, Accept/binary >> = iolist_to_binary([[$,, A] || {_, _, _, _, _, A} <- L]),
  246. ResL = parse_accept(Accept),
  247. CheckedL = [begin
  248. ExpectedP = [{?INLINE_LOWERCASE_BC(K), unquote(V)} || {K, V, _, _} <- P],
  249. ExpectedE = [case Ext of
  250. {K, V, _, _} -> {?INLINE_LOWERCASE_BC(K), unquote(V)};
  251. K -> ?INLINE_LOWERCASE_BC(K)
  252. end || Ext <- E],
  253. ResT =:= ?INLINE_LOWERCASE_BC(T)
  254. andalso ResS =:= ?INLINE_LOWERCASE_BC(S)
  255. andalso ResP =:= ExpectedP
  256. andalso (ResW =:= W orelse (W =:= undefined andalso ResW =:= 1000))
  257. andalso ((W =:= undefined andalso ResE =:= []) orelse (W =/= undefined andalso ResE =:= ExpectedE))
  258. end || {{T, S, P, W, E, _}, {{ResT, ResS, ResP}, ResW, ResE}} <- lists:zip(L, ResL)],
  259. [true] =:= lists:usort(CheckedL)
  260. end
  261. ).
  262. parse_accept_test_() ->
  263. Tests = [
  264. {<<>>, []},
  265. {<<" ">>, []},
  266. {<<"audio/*; q=0.2, audio/basic">>, [
  267. {{<<"audio">>, <<"*">>, []}, 200, []},
  268. {{<<"audio">>, <<"basic">>, []}, 1000, []}
  269. ]},
  270. {<<"text/plain; q=0.5, text/html, "
  271. "text/x-dvi; q=0.8, text/x-c">>, [
  272. {{<<"text">>, <<"plain">>, []}, 500, []},
  273. {{<<"text">>, <<"html">>, []}, 1000, []},
  274. {{<<"text">>, <<"x-dvi">>, []}, 800, []},
  275. {{<<"text">>, <<"x-c">>, []}, 1000, []}
  276. ]},
  277. {<<"text/*, text/html, text/html;level=1, */*">>, [
  278. {{<<"text">>, <<"*">>, []}, 1000, []},
  279. {{<<"text">>, <<"html">>, []}, 1000, []},
  280. {{<<"text">>, <<"html">>, [{<<"level">>, <<"1">>}]}, 1000, []},
  281. {{<<"*">>, <<"*">>, []}, 1000, []}
  282. ]},
  283. {<<"text/*;q=0.3, text/html;q=0.7, text/html;level=1, "
  284. "text/html;level=2;q=0.4, */*;q=0.5">>, [
  285. {{<<"text">>, <<"*">>, []}, 300, []},
  286. {{<<"text">>, <<"html">>, []}, 700, []},
  287. {{<<"text">>, <<"html">>, [{<<"level">>, <<"1">>}]}, 1000, []},
  288. {{<<"text">>, <<"html">>, [{<<"level">>, <<"2">>}]}, 400, []},
  289. {{<<"*">>, <<"*">>, []}, 500, []}
  290. ]},
  291. {<<"text/html;level=1;quoted=\"hi hi hi\";"
  292. "q=0.123;standalone;complex=gits, text/plain">>, [
  293. {{<<"text">>, <<"html">>,
  294. [{<<"level">>, <<"1">>}, {<<"quoted">>, <<"hi hi hi">>}]}, 123,
  295. [<<"standalone">>, {<<"complex">>, <<"gits">>}]},
  296. {{<<"text">>, <<"plain">>, []}, 1000, []}
  297. ]},
  298. {<<"text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2">>, [
  299. {{<<"text">>, <<"html">>, []}, 1000, []},
  300. {{<<"image">>, <<"gif">>, []}, 1000, []},
  301. {{<<"image">>, <<"jpeg">>, []}, 1000, []},
  302. {{<<"*">>, <<"*">>, []}, 200, []},
  303. {{<<"*">>, <<"*">>, []}, 200, []}
  304. ]}
  305. ],
  306. [{V, fun() -> R = parse_accept(V) end} || {V, R} <- Tests].
  307. parse_accept_error_test_() ->
  308. Tests = [
  309. <<"audio/basic, */;q=0.5">>,
  310. <<"audio/, audio/basic">>,
  311. <<"aud\tio/basic">>,
  312. <<"audio/basic;t=\"zero \\", 0, " woo\"">>
  313. ],
  314. [{V, fun() -> {'EXIT', _} = (catch parse_accept(V)) end} || V <- Tests].
  315. -endif.
  316. -ifdef(PERF).
  317. horse_parse_accept() ->
  318. horse:repeat(20000,
  319. parse_accept(<<"text/*;q=0.3, text/html;q=0.7, text/html;level=1, "
  320. "text/html;level=2;q=0.4, */*;q=0.5">>)
  321. ).
  322. -endif.
  323. %% @doc Parse the Accept-Charset header.
  324. -spec parse_accept_charset(binary()) -> [{binary(), qvalue()}].
  325. parse_accept_charset(Charset) ->
  326. nonempty(conneg_list(Charset, [])).
  327. conneg_list(<<>>, Acc) -> lists:reverse(Acc);
  328. conneg_list(<< $\s, R/bits >>, Acc) -> conneg_list(R, Acc);
  329. conneg_list(<< $\t, R/bits >>, Acc) -> conneg_list(R, Acc);
  330. conneg_list(<< $\,, R/bits >>, Acc) -> conneg_list(R, Acc);
  331. conneg_list(<< C, R/bits >>, Acc) when ?IS_TOKEN(C) ->
  332. case C of
  333. ?INLINE_LOWERCASE(conneg, R, Acc, <<>>)
  334. end.
  335. conneg(<<>>, Acc, T) -> lists:reverse([{T, 1000}|Acc]);
  336. conneg(<< $,, R/bits >>, Acc, T) -> conneg_list(R, [{T, 1000}|Acc]);
  337. conneg(<< $;, R/bits >>, Acc, T) -> conneg_before_weight(R, Acc, T);
  338. conneg(<< $\s, R/bits >>, Acc, T) -> conneg_before_semicolon(R, Acc, T);
  339. conneg(<< $\t, R/bits >>, Acc, T) -> conneg_before_semicolon(R, Acc, T);
  340. conneg(<< C, R/bits >>, Acc, T) when ?IS_TOKEN(C) ->
  341. case C of
  342. ?INLINE_LOWERCASE(conneg, R, Acc, T)
  343. end.
  344. conneg_before_semicolon(<<>>, Acc, T) -> lists:reverse([{T, 1000}|Acc]);
  345. conneg_before_semicolon(<< $,, R/bits >>, Acc, T) -> conneg_list(R, [{T, 1000}|Acc]);
  346. conneg_before_semicolon(<< $;, R/bits >>, Acc, T) -> conneg_before_weight(R, Acc, T);
  347. conneg_before_semicolon(<< $\s, R/bits >>, Acc, T) -> conneg_before_semicolon(R, Acc, T);
  348. conneg_before_semicolon(<< $\t, R/bits >>, Acc, T) -> conneg_before_semicolon(R, Acc, T).
  349. conneg_before_weight(<< $\s, R/bits >>, Acc, T) -> conneg_before_weight(R, Acc, T);
  350. conneg_before_weight(<< $\t, R/bits >>, Acc, T) -> conneg_before_weight(R, Acc, T);
  351. conneg_before_weight(<< $q, $=, R/bits >>, Acc, T) -> conneg_weight(R, Acc, T);
  352. %% Special clause for broken user agents that confuse ; and , separators.
  353. conneg_before_weight(<< C, R/bits >>, Acc, T) when ?IS_TOKEN(C) ->
  354. case C of
  355. ?INLINE_LOWERCASE(conneg, R, [{T, 1000}|Acc], <<>>)
  356. end.
  357. conneg_weight(<< "1.000", R/bits >>, Acc, T) -> conneg_list_sep(R, [{T, 1000}|Acc]);
  358. conneg_weight(<< "1.00", R/bits >>, Acc, T) -> conneg_list_sep(R, [{T, 1000}|Acc]);
  359. conneg_weight(<< "1.0", R/bits >>, Acc, T) -> conneg_list_sep(R, [{T, 1000}|Acc]);
  360. conneg_weight(<< "1.", R/bits >>, Acc, T) -> conneg_list_sep(R, [{T, 1000}|Acc]);
  361. conneg_weight(<< "1", R/bits >>, Acc, T) -> conneg_list_sep(R, [{T, 1000}|Acc]);
  362. conneg_weight(<< "0.", A, B, C, R/bits >>, Acc, T)
  363. when A >= $0, A =< $9, B >= $0, B =< $9, C >= $0, C =< $9 ->
  364. conneg_list_sep(R, [{T, (A - $0) * 100 + (B - $0) * 10 + (C - $0)}|Acc]);
  365. conneg_weight(<< "0.", A, B, R/bits >>, Acc, T)
  366. when A >= $0, A =< $9, B >= $0, B =< $9 ->
  367. conneg_list_sep(R, [{T, (A - $0) * 100 + (B - $0) * 10}|Acc]);
  368. conneg_weight(<< "0.", A, R/bits >>, Acc, T)
  369. when A >= $0, A =< $9 ->
  370. conneg_list_sep(R, [{T, (A - $0) * 100}|Acc]);
  371. conneg_weight(<< "0.", R/bits >>, Acc, T) -> conneg_list_sep(R, [{T, 0}|Acc]);
  372. conneg_weight(<< "0", R/bits >>, Acc, T) -> conneg_list_sep(R, [{T, 0}|Acc]).
  373. conneg_list_sep(<<>>, Acc) -> lists:reverse(Acc);
  374. conneg_list_sep(<< $\s, R/bits >>, Acc) -> conneg_list_sep(R, Acc);
  375. conneg_list_sep(<< $\t, R/bits >>, Acc) -> conneg_list_sep(R, Acc);
  376. conneg_list_sep(<< $,, R/bits >>, Acc) -> conneg_list(R, Acc).
  377. -ifdef(TEST).
  378. accept_charset() ->
  379. ?LET({C, W},
  380. {token(), weight()},
  381. {C, W, iolist_to_binary([C, case W of
  382. undefined -> [];
  383. _ -> [<<";q=">>, qvalue_to_iodata(W)]
  384. end])}
  385. ).
  386. prop_parse_accept_charset() ->
  387. ?FORALL(L,
  388. non_empty(list(accept_charset())),
  389. begin
  390. << _, AcceptCharset/binary >> = iolist_to_binary([[$,, A] || {_, _, A} <- L]),
  391. ResL = parse_accept_charset(AcceptCharset),
  392. CheckedL = [begin
  393. ResC =:= ?INLINE_LOWERCASE_BC(Ch)
  394. andalso (ResW =:= W orelse (W =:= undefined andalso ResW =:= 1000))
  395. end || {{Ch, W, _}, {ResC, ResW}} <- lists:zip(L, ResL)],
  396. [true] =:= lists:usort(CheckedL)
  397. end).
  398. parse_accept_charset_test_() ->
  399. Tests = [
  400. {<<"iso-8859-5, unicode-1-1;q=0.8">>, [
  401. {<<"iso-8859-5">>, 1000},
  402. {<<"unicode-1-1">>, 800}
  403. ]},
  404. %% Some user agents send this invalid value for the Accept-Charset header
  405. {<<"ISO-8859-1;utf-8;q=0.7,*;q=0.7">>, [
  406. {<<"iso-8859-1">>, 1000},
  407. {<<"utf-8">>, 700},
  408. {<<"*">>, 700}
  409. ]}
  410. ],
  411. [{V, fun() -> R = parse_accept_charset(V) end} || {V, R} <- Tests].
  412. parse_accept_charset_error_test_() ->
  413. Tests = [
  414. <<>>
  415. ],
  416. [{V, fun() -> {'EXIT', _} = (catch parse_accept_charset(V)) end} || V <- Tests].
  417. -endif.
  418. -ifdef(PERF).
  419. horse_parse_accept_charset() ->
  420. horse:repeat(20000,
  421. parse_accept_charset(<<"iso-8859-5, unicode-1-1;q=0.8">>)
  422. ).
  423. -endif.
  424. %% @doc Parse the Accept-Encoding header.
  425. -spec parse_accept_encoding(binary()) -> [{binary(), qvalue()}].
  426. parse_accept_encoding(Encoding) ->
  427. conneg_list(Encoding, []).
  428. -ifdef(TEST).
  429. accept_encoding() ->
  430. ?LET({E, W},
  431. {token(), weight()},
  432. {E, W, iolist_to_binary([E, case W of
  433. undefined -> [];
  434. _ -> [<<";q=">>, qvalue_to_iodata(W)]
  435. end])}
  436. ).
  437. prop_parse_accept_encoding() ->
  438. ?FORALL(L,
  439. non_empty(list(accept_encoding())),
  440. begin
  441. << _, AcceptEncoding/binary >> = iolist_to_binary([[$,, A] || {_, _, A} <- L]),
  442. ResL = parse_accept_encoding(AcceptEncoding),
  443. CheckedL = [begin
  444. ResE =:= ?INLINE_LOWERCASE_BC(E)
  445. andalso (ResW =:= W orelse (W =:= undefined andalso ResW =:= 1000))
  446. end || {{E, W, _}, {ResE, ResW}} <- lists:zip(L, ResL)],
  447. [true] =:= lists:usort(CheckedL)
  448. end).
  449. parse_accept_encoding_test_() ->
  450. Tests = [
  451. {<<>>, []},
  452. {<<"*">>, [{<<"*">>, 1000}]},
  453. {<<"compress, gzip">>, [
  454. {<<"compress">>, 1000},
  455. {<<"gzip">>, 1000}
  456. ]},
  457. {<<"compress;q=0.5, gzip;q=1.0">>, [
  458. {<<"compress">>, 500},
  459. {<<"gzip">>, 1000}
  460. ]},
  461. {<<"gzip;q=1.0, identity; q=0.5, *;q=0">>, [
  462. {<<"gzip">>, 1000},
  463. {<<"identity">>, 500},
  464. {<<"*">>, 0}
  465. ]}
  466. ],
  467. [{V, fun() -> R = parse_accept_encoding(V) end} || {V, R} <- Tests].
  468. -endif.
  469. -ifdef(PERF).
  470. horse_parse_accept_encoding() ->
  471. horse:repeat(20000,
  472. parse_accept_encoding(<<"gzip;q=1.0, identity; q=0.5, *;q=0">>)
  473. ).
  474. -endif.
  475. %% @doc Parse the Accept-Language header.
  476. -spec parse_accept_language(binary()) -> [{binary(), qvalue()}].
  477. parse_accept_language(LanguageRange) ->
  478. nonempty(language_range_list(LanguageRange, [])).
  479. language_range_list(<<>>, Acc) -> lists:reverse(Acc);
  480. language_range_list(<< $\s, R/bits >>, Acc) -> language_range_list(R, Acc);
  481. language_range_list(<< $\t, R/bits >>, Acc) -> language_range_list(R, Acc);
  482. language_range_list(<< $\,, R/bits >>, Acc) -> language_range_list(R, Acc);
  483. language_range_list(<< $*, R/bits >>, Acc) -> language_range_before_semicolon(R, Acc, <<"*">>);
  484. language_range_list(<< C, R/bits >>, Acc) when ?IS_ALPHA(C) ->
  485. case C of
  486. ?INLINE_LOWERCASE(language_range, R, Acc, 1, <<>>)
  487. end.
  488. language_range(<<>>, Acc, _, T) -> lists:reverse([{T, 1000}|Acc]);
  489. language_range(<< $,, R/bits >>, Acc, _, T) -> language_range_list(R, [{T, 1000}|Acc]);
  490. language_range(<< $;, R/bits >>, Acc, _, T) -> language_range_before_weight(R, Acc, T);
  491. language_range(<< $\s, R/bits >>, Acc, _, T) -> language_range_before_semicolon(R, Acc, T);
  492. language_range(<< $\t, R/bits >>, Acc, _, T) -> language_range_before_semicolon(R, Acc, T);
  493. language_range(<< $-, R/bits >>, Acc, _, T) -> language_range_sub(R, Acc, 0, << T/binary, $- >>);
  494. language_range(<< _, _/bits >>, _, 8, _) -> error(badarg);
  495. language_range(<< C, R/bits >>, Acc, N, T) when ?IS_ALPHA(C) ->
  496. case C of
  497. ?INLINE_LOWERCASE(language_range, R, Acc, N + 1, T)
  498. end.
  499. language_range_sub(<<>>, Acc, N, T) when N > 0 -> lists:reverse([{T, 1000}|Acc]);
  500. language_range_sub(<< $,, R/bits >>, Acc, N, T) when N > 0 -> language_range_list(R, [{T, 1000}|Acc]);
  501. language_range_sub(<< $;, R/bits >>, Acc, N, T) when N > 0 -> language_range_before_weight(R, Acc, T);
  502. language_range_sub(<< $\s, R/bits >>, Acc, N, T) when N > 0 -> language_range_before_semicolon(R, Acc, T);
  503. language_range_sub(<< $\t, R/bits >>, Acc, N, T) when N > 0 -> language_range_before_semicolon(R, Acc, T);
  504. language_range_sub(<< $-, R/bits >>, Acc, N, T) when N > 0 -> language_range_sub(R, Acc, 0, << T/binary, $- >>);
  505. language_range_sub(<< _, _/bits >>, _, 8, _) -> error(badarg);
  506. language_range_sub(<< C, R/bits >>, Acc, N, T) when ?IS_ALPHA(C); ?IS_DIGIT(C) ->
  507. case C of
  508. ?INLINE_LOWERCASE(language_range_sub, R, Acc, N + 1, T)
  509. end.
  510. language_range_before_semicolon(<<>>, Acc, T) -> lists:reverse([{T, 1000}|Acc]);
  511. language_range_before_semicolon(<< $,, R/bits >>, Acc, T) -> language_range_list(R, [{T, 1000}|Acc]);
  512. language_range_before_semicolon(<< $;, R/bits >>, Acc, T) -> language_range_before_weight(R, Acc, T);
  513. language_range_before_semicolon(<< $\s, R/bits >>, Acc, T) -> language_range_before_semicolon(R, Acc, T);
  514. language_range_before_semicolon(<< $\t, R/bits >>, Acc, T) -> language_range_before_semicolon(R, Acc, T).
  515. language_range_before_weight(<< $\s, R/bits >>, Acc, T) -> language_range_before_weight(R, Acc, T);
  516. language_range_before_weight(<< $\t, R/bits >>, Acc, T) -> language_range_before_weight(R, Acc, T);
  517. language_range_before_weight(<< $q, $=, R/bits >>, Acc, T) -> language_range_weight(R, Acc, T);
  518. %% Special clause for broken user agents that confuse ; and , separators.
  519. language_range_before_weight(<< C, R/bits >>, Acc, T) when ?IS_ALPHA(C) ->
  520. case C of
  521. ?INLINE_LOWERCASE(language_range, R, [{T, 1000}|Acc], 1, <<>>)
  522. end.
  523. language_range_weight(<< "1.000", R/bits >>, Acc, T) -> language_range_list_sep(R, [{T, 1000}|Acc]);
  524. language_range_weight(<< "1.00", R/bits >>, Acc, T) -> language_range_list_sep(R, [{T, 1000}|Acc]);
  525. language_range_weight(<< "1.0", R/bits >>, Acc, T) -> language_range_list_sep(R, [{T, 1000}|Acc]);
  526. language_range_weight(<< "1.", R/bits >>, Acc, T) -> language_range_list_sep(R, [{T, 1000}|Acc]);
  527. language_range_weight(<< "1", R/bits >>, Acc, T) -> language_range_list_sep(R, [{T, 1000}|Acc]);
  528. language_range_weight(<< "0.", A, B, C, R/bits >>, Acc, T)
  529. when A >= $0, A =< $9, B >= $0, B =< $9, C >= $0, C =< $9 ->
  530. language_range_list_sep(R, [{T, (A - $0) * 100 + (B - $0) * 10 + (C - $0)}|Acc]);
  531. language_range_weight(<< "0.", A, B, R/bits >>, Acc, T)
  532. when A >= $0, A =< $9, B >= $0, B =< $9 ->
  533. language_range_list_sep(R, [{T, (A - $0) * 100 + (B - $0) * 10}|Acc]);
  534. language_range_weight(<< "0.", A, R/bits >>, Acc, T)
  535. when A >= $0, A =< $9 ->
  536. language_range_list_sep(R, [{T, (A - $0) * 100}|Acc]);
  537. language_range_weight(<< "0.", R/bits >>, Acc, T) -> language_range_list_sep(R, [{T, 0}|Acc]);
  538. language_range_weight(<< "0", R/bits >>, Acc, T) -> language_range_list_sep(R, [{T, 0}|Acc]).
  539. language_range_list_sep(<<>>, Acc) -> lists:reverse(Acc);
  540. language_range_list_sep(<< $\s, R/bits >>, Acc) -> language_range_list_sep(R, Acc);
  541. language_range_list_sep(<< $\t, R/bits >>, Acc) -> language_range_list_sep(R, Acc);
  542. language_range_list_sep(<< $,, R/bits >>, Acc) -> language_range_list(R, Acc).
  543. -ifdef(TEST).
  544. language_tag() ->
  545. oneof([
  546. [alpha()],
  547. [alpha(), alpha()],
  548. [alpha(), alpha(), alpha()],
  549. [alpha(), alpha(), alpha(), alpha()],
  550. [alpha(), alpha(), alpha(), alpha(), alpha()],
  551. [alpha(), alpha(), alpha(), alpha(), alpha(), alpha()],
  552. [alpha(), alpha(), alpha(), alpha(), alpha(), alpha(), alpha()],
  553. [alpha(), alpha(), alpha(), alpha(), alpha(), alpha(), alpha(), alpha()]
  554. ]).
  555. language_subtag() ->
  556. [$-, oneof([
  557. [alphanum()],
  558. [alphanum(), alphanum()],
  559. [alphanum(), alphanum(), alphanum()],
  560. [alphanum(), alphanum(), alphanum(), alphanum()],
  561. [alphanum(), alphanum(), alphanum(), alphanum(), alphanum()],
  562. [alphanum(), alphanum(), alphanum(), alphanum(), alphanum(), alphanum()],
  563. [alphanum(), alphanum(), alphanum(), alphanum(), alphanum(), alphanum(), alphanum()],
  564. [alphanum(), alphanum(), alphanum(), alphanum(), alphanum(), alphanum(), alphanum(), alphanum()]
  565. ])].
  566. language_range() ->
  567. [language_tag(), list(language_subtag())].
  568. accept_language() ->
  569. ?LET({R, W},
  570. {language_range(), weight()},
  571. {iolist_to_binary(R), W, iolist_to_binary([R, case W of
  572. undefined -> [];
  573. _ -> [<<";q=">>, qvalue_to_iodata(W)]
  574. end])}
  575. ).
  576. prop_parse_accept_language() ->
  577. ?FORALL(L,
  578. non_empty(list(accept_language())),
  579. begin
  580. << _, AcceptLanguage/binary >> = iolist_to_binary([[$,, A] || {_, _, A} <- L]),
  581. ResL = parse_accept_language(AcceptLanguage),
  582. CheckedL = [begin
  583. ResR =:= ?INLINE_LOWERCASE_BC(R)
  584. andalso (ResW =:= W orelse (W =:= undefined andalso ResW =:= 1000))
  585. end || {{R, W, _}, {ResR, ResW}} <- lists:zip(L, ResL)],
  586. [true] =:= lists:usort(CheckedL)
  587. end).
  588. parse_accept_language_test_() ->
  589. Tests = [
  590. {<<"da, en-gb;q=0.8, en;q=0.7">>, [
  591. {<<"da">>, 1000},
  592. {<<"en-gb">>, 800},
  593. {<<"en">>, 700}
  594. ]},
  595. {<<"en, en-US, en-cockney, i-cherokee, x-pig-latin, es-419">>, [
  596. {<<"en">>, 1000},
  597. {<<"en-us">>, 1000},
  598. {<<"en-cockney">>, 1000},
  599. {<<"i-cherokee">>, 1000},
  600. {<<"x-pig-latin">>, 1000},
  601. {<<"es-419">>, 1000}
  602. ]}
  603. ],
  604. [{V, fun() -> R = parse_accept_language(V) end} || {V, R} <- Tests].
  605. parse_accept_language_error_test_() ->
  606. Tests = [
  607. <<>>,
  608. <<"loooooong">>,
  609. <<"en-us-loooooong">>,
  610. <<"419-en-us">>
  611. ],
  612. [{V, fun() -> {'EXIT', _} = (catch parse_accept_language(V)) end} || V <- Tests].
  613. -endif.
  614. -ifdef(PERF).
  615. horse_parse_accept_language() ->
  616. horse:repeat(20000,
  617. parse_accept_language(<<"da, en-gb;q=0.8, en;q=0.7">>)
  618. ).
  619. -endif.
  620. %% @doc Parse the Connection header.
  621. -spec parse_connection(binary()) -> [binary()].
  622. parse_connection(<<"close">>) ->
  623. [<<"close">>];
  624. parse_connection(<<"keep-alive">>) ->
  625. [<<"keep-alive">>];
  626. parse_connection(Connection) ->
  627. nonempty(token_ci_list(Connection, [])).
  628. -ifdef(TEST).
  629. prop_parse_connection() ->
  630. ?FORALL(L,
  631. non_empty(list(token())),
  632. begin
  633. << _, Connection/binary >> = iolist_to_binary([[$,, C] || C <- L]),
  634. ResL = parse_connection(Connection),
  635. CheckedL = [?INLINE_LOWERCASE_BC(Co) =:= ResC || {Co, ResC} <- lists:zip(L, ResL)],
  636. [true] =:= lists:usort(CheckedL)
  637. end).
  638. parse_connection_test_() ->
  639. Tests = [
  640. {<<"close">>, [<<"close">>]},
  641. {<<"ClOsE">>, [<<"close">>]},
  642. {<<"Keep-Alive">>, [<<"keep-alive">>]},
  643. {<<"keep-alive, Upgrade">>, [<<"keep-alive">>, <<"upgrade">>]}
  644. ],
  645. [{V, fun() -> R = parse_connection(V) end} || {V, R} <- Tests].
  646. parse_connection_error_test_() ->
  647. Tests = [
  648. <<>>
  649. ],
  650. [{V, fun() -> {'EXIT', _} = (catch parse_connection(V)) end} || V <- Tests].
  651. -endif.
  652. -ifdef(PERF).
  653. horse_parse_connection_close() ->
  654. horse:repeat(200000,
  655. parse_connection(<<"close">>)
  656. ).
  657. horse_parse_connection_keepalive() ->
  658. horse:repeat(200000,
  659. parse_connection(<<"keep-alive">>)
  660. ).
  661. horse_parse_connection_keepalive_upgrade() ->
  662. horse:repeat(200000,
  663. parse_connection(<<"keep-alive, upgrade">>)
  664. ).
  665. -endif.
  666. %% @doc Parse the Content-Length header.
  667. %%
  668. %% The value has at least one digit, and may be followed by whitespace.
  669. -spec parse_content_length(binary()) -> non_neg_integer().
  670. parse_content_length(<< $0 >>) -> 0;
  671. parse_content_length(<< $0, R/bits >>) -> number(R, 0);
  672. parse_content_length(<< $1, R/bits >>) -> number(R, 1);
  673. parse_content_length(<< $2, R/bits >>) -> number(R, 2);
  674. parse_content_length(<< $3, R/bits >>) -> number(R, 3);
  675. parse_content_length(<< $4, R/bits >>) -> number(R, 4);
  676. parse_content_length(<< $5, R/bits >>) -> number(R, 5);
  677. parse_content_length(<< $6, R/bits >>) -> number(R, 6);
  678. parse_content_length(<< $7, R/bits >>) -> number(R, 7);
  679. parse_content_length(<< $8, R/bits >>) -> number(R, 8);
  680. parse_content_length(<< $9, R/bits >>) -> number(R, 9).
  681. -ifdef(TEST).
  682. prop_parse_content_length() ->
  683. ?FORALL(
  684. X,
  685. non_neg_integer(),
  686. X =:= parse_content_length(integer_to_binary(X))
  687. ).
  688. parse_content_length_test_() ->
  689. Tests = [
  690. {<<"0">>, 0},
  691. {<<"42 ">>, 42},
  692. {<<"69\t">>, 69},
  693. {<<"1337">>, 1337},
  694. {<<"3495">>, 3495},
  695. {<<"1234567890">>, 1234567890},
  696. {<<"1234567890 ">>, 1234567890}
  697. ],
  698. [{V, fun() -> R = parse_content_length(V) end} || {V, R} <- Tests].
  699. parse_content_length_error_test_() ->
  700. Tests = [
  701. <<>>,
  702. <<"123, 123">>,
  703. <<"4.17">>
  704. ],
  705. [{V, fun() -> {'EXIT', _} = (catch parse_content_length(V)) end} || V <- Tests].
  706. -endif.
  707. -ifdef(PERF).
  708. horse_parse_content_length_zero() ->
  709. horse:repeat(100000,
  710. parse_content_length(<<"0">>)
  711. ).
  712. horse_parse_content_length_giga() ->
  713. horse:repeat(100000,
  714. parse_content_length(<<"1234567890">>)
  715. ).
  716. -endif.
  717. %% @doc Parse the Content-Type header.
  718. -spec parse_content_type(binary()) -> media_type().
  719. parse_content_type(<< C, R/bits >>) when ?IS_TOKEN(C) ->
  720. case C of
  721. ?INLINE_LOWERCASE(media_type, R, <<>>)
  722. end.
  723. media_type(<< $/, C, R/bits >>, T) when ?IS_TOKEN(C) ->
  724. case C of
  725. ?INLINE_LOWERCASE(media_subtype, R, T, <<>>)
  726. end;
  727. media_type(<< C, R/bits >>, T) when ?IS_TOKEN(C) ->
  728. case C of
  729. ?INLINE_LOWERCASE(media_type, R, T)
  730. end.
  731. media_subtype(<<>>, T, S) -> {T, S, []};
  732. media_subtype(<< $;, R/bits >>, T, S) -> media_before_param(R, T, S, []);
  733. media_subtype(<< $\s, R/bits >>, T, S) -> media_before_semicolon(R, T, S, []);
  734. media_subtype(<< $\t, R/bits >>, T, S) -> media_before_semicolon(R, T, S, []);
  735. media_subtype(<< C, R/bits >>, T, S) when ?IS_TOKEN(C) ->
  736. case C of
  737. ?INLINE_LOWERCASE(media_subtype, R, T, S)
  738. end.
  739. media_before_semicolon(<<>>, T, S, P) -> {T, S, lists:reverse(P)};
  740. media_before_semicolon(<< $;, R/bits >>, T, S, P) -> media_before_param(R, T, S, P);
  741. media_before_semicolon(<< $\s, R/bits >>, T, S, P) -> media_before_semicolon(R, T, S, P);
  742. media_before_semicolon(<< $\t, R/bits >>, T, S, P) -> media_before_semicolon(R, T, S, P).
  743. media_before_param(<< $\s, R/bits >>, T, S, P) -> media_before_param(R, T, S, P);
  744. media_before_param(<< $\t, R/bits >>, T, S, P) -> media_before_param(R, T, S, P);
  745. media_before_param(<< "charset=", $", R/bits >>, T, S, P) -> media_charset_quoted(R, T, S, P, <<>>);
  746. media_before_param(<< "charset=", R/bits >>, T, S, P) -> media_charset(R, T, S, P, <<>>);
  747. media_before_param(<< C, R/bits >>, T, S, P) when ?IS_TOKEN(C) ->
  748. case C of
  749. ?INLINE_LOWERCASE(media_param, R, T, S, P, <<>>)
  750. end.
  751. media_charset_quoted(<< $", R/bits >>, T, S, P, V) ->
  752. media_before_semicolon(R, T, S, [{<<"charset">>, V}|P]);
  753. media_charset_quoted(<< $\\, C, R/bits >>, T, S, P, V) when ?IS_VCHAR(C) ->
  754. case C of
  755. ?INLINE_LOWERCASE(media_charset_quoted, R, T, S, P, V)
  756. end;
  757. media_charset_quoted(<< C, R/bits >>, T, S, P, V) when ?IS_VCHAR(C) ->
  758. case C of
  759. ?INLINE_LOWERCASE(media_charset_quoted, R, T, S, P, V)
  760. end.
  761. media_charset(<<>>, T, S, P, V) -> {T, S, lists:reverse([{<<"charset">>, V}|P])};
  762. media_charset(<< $;, R/bits >>, T, S, P, V) -> media_before_param(R, T, S, [{<<"charset">>, V}|P]);
  763. media_charset(<< $\s, R/bits >>, T, S, P, V) -> media_before_semicolon(R, T, S, [{<<"charset">>, V}|P]);
  764. media_charset(<< $\t, R/bits >>, T, S, P, V) -> media_before_semicolon(R, T, S, [{<<"charset">>, V}|P]);
  765. media_charset(<< C, R/bits >>, T, S, P, V) when ?IS_TOKEN(C) ->
  766. case C of
  767. ?INLINE_LOWERCASE(media_charset, R, T, S, P, V)
  768. end.
  769. media_param(<< $=, $", R/bits >>, T, S, P, K) -> media_quoted(R, T, S, P, K, <<>>);
  770. media_param(<< $=, R/bits >>, T, S, P, K) -> media_value(R, T, S, P, K, <<>>);
  771. media_param(<< C, R/bits >>, T, S, P, K) when ?IS_TOKEN(C) ->
  772. case C of
  773. ?INLINE_LOWERCASE(media_param, R, T, S, P, K)
  774. end.
  775. media_quoted(<< $", R/bits >>, T, S, P, K, V) -> media_before_semicolon(R, T, S, [{K, V}|P]);
  776. media_quoted(<< $\\, C, R/bits >>, T, S, P, K, V) when ?IS_VCHAR(C) -> media_quoted(R, T, S, P, K, << V/binary, C >>);
  777. media_quoted(<< C, R/bits >>, T, S, P, K, V) when ?IS_VCHAR(C) -> media_quoted(R, T, S, P, K, << V/binary, C >>).
  778. media_value(<<>>, T, S, P, K, V) -> {T, S, lists:reverse([{K, V}|P])};
  779. media_value(<< $;, R/bits >>, T, S, P, K, V) -> media_before_param(R, T, S, [{K, V}|P]);
  780. media_value(<< $\s, R/bits >>, T, S, P, K, V) -> media_before_semicolon(R, T, S, [{K, V}|P]);
  781. media_value(<< $\t, R/bits >>, T, S, P, K, V) -> media_before_semicolon(R, T, S, [{K, V}|P]);
  782. media_value(<< C, R/bits >>, T, S, P, K, V) when ?IS_TOKEN(C) -> media_value(R, T, S, P, K, << V/binary, C >>).
  783. -ifdef(TEST).
  784. media_type_parameter() ->
  785. frequency([
  786. {90, parameter()},
  787. {10, {<<"charset">>, oneof([token(), quoted_string()]), <<>>, <<>>}}
  788. ]).
  789. media_type() ->
  790. ?LET({T, S, P},
  791. {token(), token(), list(media_type_parameter())},
  792. {T, S, P, iolist_to_binary([T, $/, S, [[OWS1, $;, OWS2, K, $=, V] || {K, V, OWS1, OWS2} <- P]])}
  793. ).
  794. prop_parse_content_type() ->
  795. ?FORALL({T, S, P, MediaType},
  796. media_type(),
  797. begin
  798. {ResT, ResS, ResP} = parse_content_type(MediaType),
  799. ExpectedP = [case ?INLINE_LOWERCASE_BC(K) of
  800. <<"charset">> -> {<<"charset">>, ?INLINE_LOWERCASE_BC(unquote(V))};
  801. LowK -> {LowK, unquote(V)}
  802. end || {K, V, _, _} <- P],
  803. ResT =:= ?INLINE_LOWERCASE_BC(T)
  804. andalso ResS =:= ?INLINE_LOWERCASE_BC(S)
  805. andalso ResP =:= ExpectedP
  806. end
  807. ).
  808. parse_content_type_test_() ->
  809. Tests = [
  810. {<<"text/html;charset=utf-8">>,
  811. {<<"text">>, <<"html">>, [{<<"charset">>, <<"utf-8">>}]}},
  812. {<<"text/html;charset=UTF-8">>,
  813. {<<"text">>, <<"html">>, [{<<"charset">>, <<"utf-8">>}]}},
  814. {<<"Text/HTML;Charset=\"utf-8\"">>,
  815. {<<"text">>, <<"html">>, [{<<"charset">>, <<"utf-8">>}]}},
  816. {<<"text/html; charset=\"utf-8\"">>,
  817. {<<"text">>, <<"html">>, [{<<"charset">>, <<"utf-8">>}]}},
  818. {<<"text/html; charset=ISO-8859-4">>,
  819. {<<"text">>, <<"html">>, [{<<"charset">>, <<"iso-8859-4">>}]}},
  820. {<<"text/plain; charset=iso-8859-4">>,
  821. {<<"text">>, <<"plain">>, [{<<"charset">>, <<"iso-8859-4">>}]}},
  822. {<<"multipart/form-data \t;Boundary=\"MultipartIsUgly\"">>,
  823. {<<"multipart">>, <<"form-data">>, [
  824. {<<"boundary">>, <<"MultipartIsUgly">>}
  825. ]}},
  826. {<<"foo/bar; one=FirstParam; two=SecondParam">>,
  827. {<<"foo">>, <<"bar">>, [
  828. {<<"one">>, <<"FirstParam">>},
  829. {<<"two">>, <<"SecondParam">>}
  830. ]}}
  831. ],
  832. [{V, fun() -> R = parse_content_type(V) end} || {V, R} <- Tests].
  833. -endif.
  834. -ifdef(PERF).
  835. horse_parse_content_type() ->
  836. horse:repeat(200000,
  837. parse_content_type(<<"text/html;charset=utf-8">>)
  838. ).
  839. -endif.
  840. %% @doc Parse the Date header.
  841. -spec parse_date(binary()) -> calendar:datetime().
  842. parse_date(Date) ->
  843. cow_date:parse_date(Date).
  844. -ifdef(TEST).
  845. parse_date_test_() ->
  846. Tests = [
  847. {<<"Tue, 15 Nov 1994 08:12:31 GMT">>, {{1994, 11, 15}, {8, 12, 31}}}
  848. ],
  849. [{V, fun() -> R = parse_date(V) end} || {V, R} <- Tests].
  850. -endif.
  851. %% @doc Parse the ETag header.
  852. -spec parse_etag(binary()) -> etag().
  853. parse_etag(<< $W, $/, $", R/bits >>) ->
  854. etag(R, weak, <<>>);
  855. parse_etag(<< $", R/bits >>) ->
  856. etag(R, strong, <<>>).
  857. etag(<< $", R/bits >>, Strength, Tag) ->
  858. ws_end(R),
  859. {Strength, Tag};
  860. etag(<< C, R/bits >>, Strength, Tag) when ?IS_ETAGC(C) ->
  861. etag(R, Strength, << Tag/binary, C >>).
  862. -ifdef(TEST).
  863. etagc() ->
  864. ?SUCHTHAT(C, int(16#21, 16#ff), C =/= 16#22 andalso C =/= 16#7f).
  865. etag() ->
  866. ?LET({Strength, Tag},
  867. {oneof([weak, strong]), list(etagc())},
  868. begin
  869. TagBin = list_to_binary(Tag),
  870. {{Strength, TagBin},
  871. case Strength of
  872. weak -> << $W, $/, $", TagBin/binary, $" >>;
  873. strong -> << $", TagBin/binary, $" >>
  874. end}
  875. end).
  876. prop_parse_etag() ->
  877. ?FORALL({Tag, TagBin},
  878. etag(),
  879. Tag =:= parse_etag(TagBin)).
  880. parse_etag_test_() ->
  881. Tests = [
  882. {<<"\"xyzzy\"">>, {strong, <<"xyzzy">>}},
  883. {<<"W/\"xyzzy\"">>, {weak, <<"xyzzy">>}},
  884. {<<"\"\"">>, {strong, <<>>}}
  885. ],
  886. [{V, fun() -> R = parse_etag(V) end} || {V, R} <- Tests].
  887. parse_etag_error_test_() ->
  888. Tests = [
  889. <<>>,
  890. <<"\"">>,
  891. <<"W">>,
  892. <<"W/">>
  893. ],
  894. [{V, fun() -> {'EXIT', _} = (catch parse_etag(V)) end} || V <- Tests].
  895. -endif.
  896. -ifdef(PERF).
  897. horse_parse_etag() ->
  898. horse:repeat(200000,
  899. parse_etag(<<"W/\"xyzzy\"">>)
  900. ).
  901. -endif.
  902. %% @doc Parse the Expect header.
  903. -spec parse_expect(binary()) -> continue.
  904. parse_expect(<<"100-continue", Rest/bits >>) ->
  905. ws_end(Rest),
  906. continue;
  907. parse_expect(<<"100-", C, O, N, T, I, M, U, E, Rest/bits >>)
  908. when C =:= $C orelse C =:= $c, O =:= $O orelse O =:= $o,
  909. N =:= $N orelse N =:= $n, T =:= $T orelse T =:= $t,
  910. I =:= $I orelse I =:= $i, M =:= $N orelse M =:= $n,
  911. U =:= $U orelse U =:= $u, E =:= $E orelse E =:= $e ->
  912. ws_end(Rest),
  913. continue.
  914. -ifdef(TEST).
  915. expect() ->
  916. ?LET(E,
  917. [$1, $0, $0, $-,
  918. oneof([$c, $C]), oneof([$o, $O]), oneof([$n, $N]),
  919. oneof([$t, $T]), oneof([$i, $I]), oneof([$n, $N]),
  920. oneof([$u, $U]), oneof([$e, $E])],
  921. list_to_binary(E)).
  922. prop_parse_expect() ->
  923. ?FORALL(E, expect(), continue =:= parse_expect(E)).
  924. parse_expect_test_() ->
  925. Tests = [
  926. <<"100-continue">>,
  927. <<"100-CONTINUE">>,
  928. <<"100-Continue">>,
  929. <<"100-CoNtInUe">>,
  930. <<"100-continue ">>
  931. ],
  932. [{V, fun() -> continue = parse_expect(V) end} || V <- Tests].
  933. parse_expect_error_test_() ->
  934. Tests = [
  935. <<>>,
  936. <<" ">>,
  937. <<"200-OK">>,
  938. <<"Cookies">>
  939. ],
  940. [{V, fun() -> {'EXIT', _} = (catch parse_expect(V)) end} || V <- Tests].
  941. -endif.
  942. -ifdef(PERF).
  943. horse_parse_expect() ->
  944. horse:repeat(200000,
  945. parse_expect(<<"100-continue">>)
  946. ).
  947. -endif.
  948. %% @doc Parse the If-Match header.
  949. -spec parse_if_match(binary()) -> '*' | [etag()].
  950. parse_if_match(<<"*">>) ->
  951. '*';
  952. parse_if_match(IfMatch) ->
  953. nonempty(etag_list(IfMatch, [])).
  954. etag_list(<<>>, Acc) -> lists:reverse(Acc);
  955. etag_list(<< $\s, R/bits >>, Acc) -> etag_list(R, Acc);
  956. etag_list(<< $\t, R/bits >>, Acc) -> etag_list(R, Acc);
  957. etag_list(<< $,, R/bits >>, Acc) -> etag_list(R, Acc);
  958. etag_list(<< $W, $/, $", R/bits >>, Acc) -> etag(R, Acc, weak, <<>>);
  959. etag_list(<< $", R/bits >>, Acc) -> etag(R, Acc, strong, <<>>).
  960. etag(<< $", R/bits >>, Acc, Strength, Tag) -> etag_list_sep(R, [{Strength, Tag}|Acc]);
  961. etag(<< C, R/bits >>, Acc, Strength, Tag) when ?IS_ETAGC(C) -> etag(R, Acc, Strength, << Tag/binary, C >>).
  962. etag_list_sep(<<>>, Acc) -> lists:reverse(Acc);
  963. etag_list_sep(<< $\s, R/bits >>, Acc) -> etag_list_sep(R, Acc);
  964. etag_list_sep(<< $\t, R/bits >>, Acc) -> etag_list_sep(R, Acc);
  965. etag_list_sep(<< $,, R/bits >>, Acc) -> etag_list(R, Acc).
  966. -ifdef(TEST).
  967. prop_parse_if_match() ->
  968. ?FORALL(L,
  969. non_empty(list(etag())),
  970. begin
  971. << _, IfMatch/binary >> = iolist_to_binary([[$,, T] || {_, T} <- L]),
  972. ResL = parse_if_match(IfMatch),
  973. CheckedL = [T =:= ResT || {{T, _}, ResT} <- lists:zip(L, ResL)],
  974. [true] =:= lists:usort(CheckedL)
  975. end).
  976. parse_if_match_test_() ->
  977. Tests = [
  978. {<<"\"xyzzy\"">>, [{strong, <<"xyzzy">>}]},
  979. {<<"\"xyzzy\", \"r2d2xxxx\", \"c3piozzzz\"">>,
  980. [{strong, <<"xyzzy">>}, {strong, <<"r2d2xxxx">>}, {strong, <<"c3piozzzz">>}]},
  981. {<<"*">>, '*'}
  982. ],
  983. [{V, fun() -> R = parse_if_match(V) end} || {V, R} <- Tests].
  984. parse_if_match_error_test_() ->
  985. Tests = [
  986. <<>>
  987. ],
  988. [{V, fun() -> {'EXIT', _} = (catch parse_if_match(V)) end} || V <- Tests].
  989. -endif.
  990. -ifdef(PERF).
  991. horse_parse_if_match() ->
  992. horse:repeat(200000,
  993. parse_if_match(<<"\"xyzzy\", \"r2d2xxxx\", \"c3piozzzz\"">>)
  994. ).
  995. -endif.
  996. %% @doc Parse the If-Modified-Since header.
  997. -spec parse_if_modified_since(binary()) -> calendar:datetime().
  998. parse_if_modified_since(IfModifiedSince) ->
  999. cow_date:parse_date(IfModifiedSince).
  1000. -ifdef(TEST).
  1001. parse_if_modified_since_test_() ->
  1002. Tests = [
  1003. {<<"Sat, 29 Oct 1994 19:43:31 GMT">>, {{1994, 10, 29}, {19, 43, 31}}}
  1004. ],
  1005. [{V, fun() -> R = parse_if_modified_since(V) end} || {V, R} <- Tests].
  1006. -endif.
  1007. %% @doc Parse the If-None-Match header.
  1008. -spec parse_if_none_match(binary()) -> '*' | [etag()].
  1009. parse_if_none_match(<<"*">>) ->
  1010. '*';
  1011. parse_if_none_match(IfNoneMatch) ->
  1012. nonempty(etag_list(IfNoneMatch, [])).
  1013. -ifdef(TEST).
  1014. parse_if_none_match_test_() ->
  1015. Tests = [
  1016. {<<"\"xyzzy\"">>, [{strong, <<"xyzzy">>}]},
  1017. {<<"W/\"xyzzy\"">>, [{weak, <<"xyzzy">>}]},
  1018. {<<"\"xyzzy\", \"r2d2xxxx\", \"c3piozzzz\"">>,
  1019. [{strong, <<"xyzzy">>}, {strong, <<"r2d2xxxx">>}, {strong, <<"c3piozzzz">>}]},
  1020. {<<"W/\"xyzzy\", W/\"r2d2xxxx\", W/\"c3piozzzz\"">>,
  1021. [{weak, <<"xyzzy">>}, {weak, <<"r2d2xxxx">>}, {weak, <<"c3piozzzz">>}]},
  1022. {<<"*">>, '*'}
  1023. ],
  1024. [{V, fun() -> R = parse_if_none_match(V) end} || {V, R} <- Tests].
  1025. parse_if_none_match_error_test_() ->
  1026. Tests = [
  1027. <<>>
  1028. ],
  1029. [{V, fun() -> {'EXIT', _} = (catch parse_if_none_match(V)) end} || V <- Tests].
  1030. -endif.
  1031. -ifdef(PERF).
  1032. horse_parse_if_none_match() ->
  1033. horse:repeat(200000,
  1034. parse_if_none_match(<<"W/\"xyzzy\", W/\"r2d2xxxx\", W/\"c3piozzzz\"">>)
  1035. ).
  1036. -endif.
  1037. %% @doc Parse the If-Unmodified-Since header.
  1038. -spec parse_if_unmodified_since(binary()) -> calendar:datetime().
  1039. parse_if_unmodified_since(IfModifiedSince) ->
  1040. cow_date:parse_date(IfModifiedSince).
  1041. -ifdef(TEST).
  1042. parse_if_unmodified_since_test_() ->
  1043. Tests = [
  1044. {<<"Sat, 29 Oct 1994 19:43:31 GMT">>, {{1994, 10, 29}, {19, 43, 31}}}
  1045. ],
  1046. [{V, fun() -> R = parse_if_unmodified_since(V) end} || {V, R} <- Tests].
  1047. -endif.
  1048. %% @doc Parse the Last-Modified header.
  1049. -spec parse_last_modified(binary()) -> calendar:datetime().
  1050. parse_last_modified(LastModified) ->
  1051. cow_date:parse_date(LastModified).
  1052. -ifdef(TEST).
  1053. parse_last_modified_test_() ->
  1054. Tests = [
  1055. {<<"Tue, 15 Nov 1994 12:45:26 GMT">>, {{1994, 11, 15}, {12, 45, 26}}}
  1056. ],
  1057. [{V, fun() -> R = parse_last_modified(V) end} || {V, R} <- Tests].
  1058. -endif.
  1059. %% @doc Parse the Max-Forwards header.
  1060. -spec parse_max_forwards(binary()) -> integer().
  1061. parse_max_forwards(<< $0, R/bits >>) -> number(R, 0);
  1062. parse_max_forwards(<< $1, R/bits >>) -> number(R, 1);
  1063. parse_max_forwards(<< $2, R/bits >>) -> number(R, 2);
  1064. parse_max_forwards(<< $3, R/bits >>) -> number(R, 3);
  1065. parse_max_forwards(<< $4, R/bits >>) -> number(R, 4);
  1066. parse_max_forwards(<< $5, R/bits >>) -> number(R, 5);
  1067. parse_max_forwards(<< $6, R/bits >>) -> number(R, 6);
  1068. parse_max_forwards(<< $7, R/bits >>) -> number(R, 7);
  1069. parse_max_forwards(<< $8, R/bits >>) -> number(R, 8);
  1070. parse_max_forwards(<< $9, R/bits >>) -> number(R, 9).
  1071. -ifdef(TEST).
  1072. prop_parse_max_forwards() ->
  1073. ?FORALL(
  1074. X,
  1075. non_neg_integer(),
  1076. X =:= parse_max_forwards(integer_to_binary(X))
  1077. ).
  1078. parse_max_forwards_test_() ->
  1079. Tests = [
  1080. {<<"0">>, 0},
  1081. {<<"42 ">>, 42},
  1082. {<<"69\t">>, 69},
  1083. {<<"1337">>, 1337},
  1084. {<<"1234567890">>, 1234567890},
  1085. {<<"1234567890 ">>, 1234567890}
  1086. ],
  1087. [{V, fun() -> R = parse_max_forwards(V) end} || {V, R} <- Tests].
  1088. parse_max_forwards_error_test_() ->
  1089. Tests = [
  1090. <<>>,
  1091. <<"123, 123">>,
  1092. <<"4.17">>
  1093. ],
  1094. [{V, fun() -> {'EXIT', _} = (catch parse_content_length(V)) end} || V <- Tests].
  1095. -endif.
  1096. %% @doc Parse the Transfer-Encoding header.
  1097. %%
  1098. %% @todo This function does not support parsing of transfer-parameter.
  1099. -spec parse_transfer_encoding(binary()) -> [binary()].
  1100. parse_transfer_encoding(<<"chunked">>) ->
  1101. [<<"chunked">>];
  1102. parse_transfer_encoding(TransferEncoding) ->
  1103. nonempty(token_ci_list(TransferEncoding, [])).
  1104. -ifdef(TEST).
  1105. prop_parse_transfer_encoding() ->
  1106. ?FORALL(L,
  1107. non_empty(list(token())),
  1108. begin
  1109. << _, TransferEncoding/binary >> = iolist_to_binary([[$,, C] || C <- L]),
  1110. ResL = parse_transfer_encoding(TransferEncoding),
  1111. CheckedL = [?INLINE_LOWERCASE_BC(Co) =:= ResC || {Co, ResC} <- lists:zip(L, ResL)],
  1112. [true] =:= lists:usort(CheckedL)
  1113. end).
  1114. parse_transfer_encoding_test_() ->
  1115. Tests = [
  1116. {<<"a , , , ">>, [<<"a">>]},
  1117. {<<" , , , a">>, [<<"a">>]},
  1118. {<<"a , , b">>, [<<"a">>, <<"b">>]},
  1119. {<<"chunked">>, [<<"chunked">>]},
  1120. {<<"chunked, something">>, [<<"chunked">>, <<"something">>]},
  1121. {<<"gzip, chunked">>, [<<"gzip">>, <<"chunked">>]}
  1122. ],
  1123. [{V, fun() -> R = parse_transfer_encoding(V) end} || {V, R} <- Tests].
  1124. parse_transfer_encoding_error_test_() ->
  1125. Tests = [
  1126. <<>>,
  1127. <<" ">>,
  1128. <<" , ">>,
  1129. <<",,,">>,
  1130. <<"a b">>
  1131. ],
  1132. [{V, fun() -> {'EXIT', _} = (catch parse_transfer_encoding(V)) end}
  1133. || V <- Tests].
  1134. -endif.
  1135. -ifdef(PERF).
  1136. horse_parse_transfer_encoding_chunked() ->
  1137. horse:repeat(200000,
  1138. parse_transfer_encoding(<<"chunked">>)
  1139. ).
  1140. horse_parse_transfer_encoding_custom() ->
  1141. horse:repeat(200000,
  1142. parse_transfer_encoding(<<"chunked, something">>)
  1143. ).
  1144. -endif.
  1145. %% @doc Parse the Upgrade header.
  1146. %%
  1147. %% It is unclear from the RFC whether the values here are
  1148. %% case sensitive.
  1149. %%
  1150. %% We handle them in a case insensitive manner because they
  1151. %% are described as case insensitive in the Websocket RFC.
  1152. -spec parse_upgrade(binary()) -> [binary()].
  1153. parse_upgrade(Upgrade) ->
  1154. nonempty(protocol_list(Upgrade, [])).
  1155. protocol_list(<<>>, Acc) -> lists:reverse(Acc);
  1156. protocol_list(<< $\s, R/bits >>, Acc) -> protocol_list(R, Acc);
  1157. protocol_list(<< $\t, R/bits >>, Acc) -> protocol_list(R, Acc);
  1158. protocol_list(<< $,, R/bits >>, Acc) -> protocol_list(R, Acc);
  1159. protocol_list(<< C, R/bits >>, Acc) when ?IS_TOKEN(C) ->
  1160. case C of
  1161. ?INLINE_LOWERCASE(protocol_name, R, Acc, <<>>)
  1162. end.
  1163. protocol_name(<<>>, Acc, P) -> lists:reverse([P|Acc]);
  1164. protocol_name(<< $\s, R/bits >>, Acc, P) -> protocol_list_sep(R, [P|Acc]);
  1165. protocol_name(<< $\t, R/bits >>, Acc, P) -> protocol_list_sep(R, [P|Acc]);
  1166. protocol_name(<< $,, R/bits >>, Acc, P) -> protocol_list(R, [P|Acc]);
  1167. protocol_name(<< $/, C, R/bits >>, Acc, P) ->
  1168. case C of
  1169. ?INLINE_LOWERCASE(protocol_version, R, Acc, << P/binary, $/ >>)
  1170. end;
  1171. protocol_name(<< C, R/bits >>, Acc, P) when ?IS_TOKEN(C) ->
  1172. case C of
  1173. ?INLINE_LOWERCASE(protocol_name, R, Acc, P)
  1174. end.
  1175. protocol_version(<<>>, Acc, P) -> lists:reverse([P|Acc]);
  1176. protocol_version(<< $\s, R/bits >>, Acc, P) -> protocol_list_sep(R, [P|Acc]);
  1177. protocol_version(<< $\t, R/bits >>, Acc, P) -> protocol_list_sep(R, [P|Acc]);
  1178. protocol_version(<< $,, R/bits >>, Acc, P) -> protocol_list(R, [P|Acc]);
  1179. protocol_version(<< C, R/bits >>, Acc, P) when ?IS_TOKEN(C) ->
  1180. case C of
  1181. ?INLINE_LOWERCASE(protocol_version, R, Acc, P)
  1182. end.
  1183. protocol_list_sep(<<>>, Acc) -> lists:reverse(Acc);
  1184. protocol_list_sep(<< $\s, R/bits >>, Acc) -> protocol_list_sep(R, Acc);
  1185. protocol_list_sep(<< $\t, R/bits >>, Acc) -> protocol_list_sep(R, Acc);
  1186. protocol_list_sep(<< $,, R/bits >>, Acc) -> protocol_list(R, Acc).
  1187. -ifdef(TEST).
  1188. protocols() ->
  1189. ?LET(P,
  1190. oneof([token(), [token(), $/, token()]]),
  1191. iolist_to_binary(P)).
  1192. prop_parse_upgrade() ->
  1193. ?FORALL(L,
  1194. non_empty(list(protocols())),
  1195. begin
  1196. << _, Upgrade/binary >> = iolist_to_binary([[$,, P] || P <- L]),
  1197. ResL = parse_upgrade(Upgrade),
  1198. CheckedL = [?INLINE_LOWERCASE_BC(P) =:= ResP || {P, ResP} <- lists:zip(L, ResL)],
  1199. [true] =:= lists:usort(CheckedL)
  1200. end).
  1201. parse_upgrade_test_() ->
  1202. Tests = [
  1203. {<<"HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11">>,
  1204. [<<"http/2.0">>, <<"shttp/1.3">>, <<"irc/6.9">>, <<"rta/x11">>]},
  1205. {<<"HTTP/2.0">>, [<<"http/2.0">>]}
  1206. ],
  1207. [{V, fun() -> R = parse_transfer_encoding(V) end} || {V, R} <- Tests].
  1208. parse_upgrade_error_test_() ->
  1209. Tests = [
  1210. <<>>
  1211. ],
  1212. [{V, fun() -> {'EXIT', _} = (catch parse_upgrade(V)) end}
  1213. || V <- Tests].
  1214. -endif.
  1215. %% Internal.
  1216. %% Only return if the list is not empty.
  1217. nonempty(L) when L =/= [] -> L.
  1218. %% Parse a number optionally followed by whitespace.
  1219. number(<< $0, R/bits >>, Acc) -> number(R, Acc * 10);
  1220. number(<< $1, R/bits >>, Acc) -> number(R, Acc * 10 + 1);
  1221. number(<< $2, R/bits >>, Acc) -> number(R, Acc * 10 + 2);
  1222. number(<< $3, R/bits >>, Acc) -> number(R, Acc * 10 + 3);
  1223. number(<< $4, R/bits >>, Acc) -> number(R, Acc * 10 + 4);
  1224. number(<< $5, R/bits >>, Acc) -> number(R, Acc * 10 + 5);
  1225. number(<< $6, R/bits >>, Acc) -> number(R, Acc * 10 + 6);
  1226. number(<< $7, R/bits >>, Acc) -> number(R, Acc * 10 + 7);
  1227. number(<< $8, R/bits >>, Acc) -> number(R, Acc * 10 + 8);
  1228. number(<< $9, R/bits >>, Acc) -> number(R, Acc * 10 + 9);
  1229. number(<< $\s, R/bits >>, Acc) -> ws_end(R), Acc;
  1230. number(<< $\t, R/bits >>, Acc) -> ws_end(R), Acc;
  1231. number(<<>>, Acc) -> Acc.
  1232. ws_end(<< $\s, R/bits >>) -> ws_end(R);
  1233. ws_end(<< $\t, R/bits >>) -> ws_end(R);
  1234. ws_end(<<>>) -> ok.
  1235. %% Parse a list of case insensitive tokens.
  1236. token_ci_list(<<>>, Acc) -> lists:reverse(Acc);
  1237. token_ci_list(<< $\s, R/bits >>, Acc) -> token_ci_list(R, Acc);
  1238. token_ci_list(<< $\t, R/bits >>, Acc) -> token_ci_list(R, Acc);
  1239. token_ci_list(<< $,, R/bits >>, Acc) -> token_ci_list(R, Acc);
  1240. token_ci_list(<< C, R/bits >>, Acc) ->
  1241. case C of
  1242. ?INLINE_LOWERCASE(token_ci_list, R, Acc, <<>>)
  1243. end.
  1244. token_ci_list(<<>>, Acc, T) -> lists:reverse([T|Acc]);
  1245. token_ci_list(<< $\s, R/bits >>, Acc, T) -> token_ci_list_sep(R, Acc, T);
  1246. token_ci_list(<< $\t, R/bits >>, Acc, T) -> token_ci_list_sep(R, Acc, T);
  1247. token_ci_list(<< $,, R/bits >>, Acc, T) -> token_ci_list(R, [T|Acc]);
  1248. token_ci_list(<< C, R/bits >>, Acc, T) ->
  1249. case C of
  1250. ?INLINE_LOWERCASE(token_ci_list, R, Acc, T)
  1251. end.
  1252. token_ci_list_sep(<<>>, Acc, T) -> lists:reverse([T|Acc]);
  1253. token_ci_list_sep(<< $\s, R/bits >>, Acc, T) -> token_ci_list_sep(R, Acc, T);
  1254. token_ci_list_sep(<< $\t, R/bits >>, Acc, T) -> token_ci_list_sep(R, Acc, T);
  1255. token_ci_list_sep(<< $,, R/bits >>, Acc, T) -> token_ci_list(R, [T|Acc]).