cow_http_hd.erl 48 KB

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