cow_http_hd.erl (134749B)
1 %% Copyright (c) 2014-2018, 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 15 -module(cow_http_hd). 16 17 %% Functions are ordered by header name, with the parse 18 %% function before the build function. 19 20 -export([parse_accept/1]). 21 -export([parse_accept_charset/1]). 22 % @todo -export([parse_accept_datetime/1]). RFC7089 23 -export([parse_accept_encoding/1]). 24 % @todo -export([parse_accept_features/1]). RFC2295 25 -export([parse_accept_language/1]). 26 -export([parse_accept_ranges/1]). 27 % @todo -export([parse_access_control_allow_credentials/1]). CORS 28 -export([access_control_allow_credentials/0]). 29 % @todo -export([parse_access_control_allow_headers/1]). CORS 30 -export([access_control_allow_headers/1]). 31 % @todo -export([parse_access_control_allow_methods/1]). CORS 32 -export([access_control_allow_methods/1]). 33 % @todo -export([parse_access_control_allow_origin/1]). CORS 34 -export([access_control_allow_origin/1]). 35 % @todo -export([parse_access_control_expose_headers/1]). CORS 36 -export([access_control_expose_headers/1]). 37 % @todo -export([parse_access_control_max_age/1]). CORS 38 -export([access_control_max_age/1]). 39 -export([parse_access_control_request_headers/1]). 40 -export([parse_access_control_request_method/1]). 41 -export([parse_age/1]). 42 -export([parse_allow/1]). 43 % @todo -export([parse_alternates/1]). RFC2295 44 % @todo -export([parse_authentication_info/1]). RFC2617 45 -export([parse_authorization/1]). 46 -export([parse_cache_control/1]). 47 -export([parse_connection/1]). 48 % @todo -export([parse_content_disposition/1]). RFC6266 49 -export([parse_content_encoding/1]). 50 -export([parse_content_language/1]). 51 -export([parse_content_length/1]). 52 % @todo -export([parse_content_location/1]). RFC7231 53 % @todo -export([parse_content_md5/1]). RFC2616 (deprecated) 54 -export([parse_content_range/1]). 55 % @todo -export([parse_content_security_policy/1]). CSP 56 % @todo -export([parse_content_security_policy_report_only/1]). CSP 57 -export([parse_content_type/1]). 58 -export([parse_cookie/1]). 59 -export([parse_date/1]). 60 % @todo -export([parse_digest/1]). RFC3230 61 % @todo -export([parse_dnt/1]). http://donottrack.us/ 62 -export([parse_etag/1]). 63 -export([parse_expect/1]). 64 -export([parse_expires/1]). 65 % @todo -export([parse_forwarded/1]). RFC7239 66 % @todo -export([parse_from/1]). RFC7231 67 -export([parse_host/1]). 68 -export([parse_http2_settings/1]). 69 -export([parse_if_match/1]). 70 -export([parse_if_modified_since/1]). 71 -export([parse_if_none_match/1]). 72 -export([parse_if_range/1]). 73 -export([parse_if_unmodified_since/1]). 74 % @todo -export([parse_last_event_id/1]). eventsource 75 -export([parse_last_modified/1]). 76 -export([parse_link/1]). 77 % @todo -export([parse_location/1]). RFC7231 78 -export([parse_max_forwards/1]). 79 % @todo -export([parse_memento_datetime/1]). RFC7089 80 % @todo -export([parse_negotiate/1]). RFC2295 81 -export([parse_origin/1]). 82 -export([parse_pragma/1]). 83 % @todo -export([parse_prefer/1]). RFC7240 84 -export([parse_proxy_authenticate/1]). 85 % @todo -export([parse_proxy_authentication_info/1]). RFC2617 86 -export([parse_proxy_authorization/1]). 87 % @todo -export([parse_proxy_support/1]). RFC4559 88 % @todo -export([parse_public_key_pins/1]). Key Pinning (upcoming) 89 % @todo -export([parse_public_key_pins_report_only/1]). Key Pinning (upcoming) 90 -export([parse_range/1]). 91 % @todo -export([parse_referer/1]). RFC7231 92 % @todo -export([parse_refresh/1]). Non-standard (examples: "5", "5; url=http://example.com/") 93 -export([parse_retry_after/1]). 94 -export([parse_sec_websocket_accept/1]). 95 -export([parse_sec_websocket_extensions/1]). 96 -export([parse_sec_websocket_key/1]). 97 % @todo -export([parse_sec_websocket_origin/1]). Websocket drafts 7 and 8 98 -export([parse_sec_websocket_protocol_req/1]). 99 -export([parse_sec_websocket_protocol_resp/1]). 100 -export([parse_sec_websocket_version_req/1]). 101 -export([parse_sec_websocket_version_resp/1]). 102 % @todo -export([parse_server/1]). RFC7231 103 -export([parse_set_cookie/1]). 104 % @todo -export([parse_strict_transport_security/1]). RFC6797 105 % @todo -export([parse_tcn/1]). RFC2295 106 -export([parse_te/1]). 107 -export([parse_trailer/1]). 108 -export([parse_transfer_encoding/1]). 109 -export([parse_upgrade/1]). 110 % @todo -export([parse_user_agent/1]). RFC7231 111 % @todo -export([parse_variant_vary/1]). RFC2295 112 -export([parse_variant_key/2]). 113 -export([variant_key/1]). 114 -export([parse_variants/1]). 115 -export([variants/1]). 116 -export([parse_vary/1]). 117 % @todo -export([parse_via/1]). RFC7230 118 % @todo -export([parse_want_digest/1]). RFC3230 119 % @todo -export([parse_warning/1]). RFC7234 120 -export([parse_www_authenticate/1]). 121 % @todo -export([parse_x_content_duration/1]). Gecko/MDN (value: float) 122 % @todo -export([parse_x_dns_prefetch_control/1]). Various (value: "on"|"off") 123 -export([parse_x_forwarded_for/1]). 124 % @todo -export([parse_x_frame_options/1]). RFC7034 125 126 -type etag() :: {weak | strong, binary()}. 127 -export_type([etag/0]). 128 129 -type media_type() :: {binary(), binary(), [{binary(), binary()}]}. 130 -export_type([media_type/0]). 131 132 -type qvalue() :: 0..1000. 133 -export_type([qvalue/0]). 134 135 -type websocket_version() :: 0..255. 136 -export_type([websocket_version/0]). 137 138 -include("cow_inline.hrl"). 139 -include("cow_parse.hrl"). 140 141 -ifdef(TEST). 142 -include_lib("proper/include/proper.hrl"). 143 144 vector(Min, Max, Dom) -> ?LET(N, choose(Min, Max), vector(N, Dom)). 145 small_list(Dom) -> vector(0, 10, Dom). 146 small_non_empty_list(Dom) -> vector(1, 10, Dom). 147 148 alpha_chars() -> "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ". 149 alphanum_chars() -> "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ". 150 digit_chars() -> "0123456789". 151 152 ows() -> list(elements([$\s, $\t])). 153 alpha() -> elements(alpha_chars()). 154 alphanum() -> elements(alphanum_chars()). 155 digit() -> elements(digit_chars()). 156 157 tchar() -> 158 frequency([ 159 {1, elements([$!, $#, $$, $%, $&, $', $*, $+, $-, $., $^, $_, $`, $|, $~])}, 160 {99, elements(alphanum_chars())} 161 ]). 162 163 token() -> 164 ?LET(T, 165 non_empty(list(tchar())), 166 list_to_binary(T)). 167 168 abnf_char() -> 169 integer(1, 127). 170 171 vchar() -> 172 integer(33, 126). 173 174 obs_text() -> 175 integer(128, 255). 176 177 qdtext() -> 178 frequency([ 179 {99, elements("\t\s!#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~")}, 180 {1, obs_text()} 181 ]). 182 183 quoted_pair() -> 184 [$\\, frequency([ 185 {99, elements("\t\s!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~")}, 186 {1, obs_text()} 187 ])]. 188 189 quoted_string() -> 190 [$", list(frequency([{100, qdtext()}, {1, quoted_pair()}])), $"]. 191 192 %% Helper function for ( token / quoted-string ) values. 193 unquote([$", V, $"]) -> unquote(V, <<>>); 194 unquote(V) -> V. 195 196 unquote([], Acc) -> Acc; 197 unquote([[$\\, C]|Tail], Acc) -> unquote(Tail, << Acc/binary, C >>); 198 unquote([C|Tail], Acc) -> unquote(Tail, << Acc/binary, C >>). 199 200 parameter() -> 201 ?SUCHTHAT({K, _, _, _}, 202 {token(), oneof([token(), quoted_string()]), ows(), ows()}, 203 K =/= <<"q">>). 204 205 weight() -> 206 frequency([ 207 {90, integer(0, 1000)}, 208 {10, undefined} 209 ]). 210 211 %% Helper function for weight's qvalue formatting. 212 qvalue_to_iodata(0) -> <<"0">>; 213 qvalue_to_iodata(Q) when Q < 10 -> [<<"0.00">>, integer_to_binary(Q)]; 214 qvalue_to_iodata(Q) when Q < 100 -> [<<"0.0">>, integer_to_binary(Q)]; 215 qvalue_to_iodata(Q) when Q < 1000 -> [<<"0.">>, integer_to_binary(Q)]; 216 qvalue_to_iodata(1000) -> <<"1">>. 217 -endif. 218 219 %% Accept header. 220 221 -spec parse_accept(binary()) -> [{media_type(), qvalue(), [binary() | {binary(), binary()}]}]. 222 parse_accept(<<"*/*">>) -> 223 [{{<<"*">>, <<"*">>, []}, 1000, []}]; 224 parse_accept(Accept) -> 225 media_range_list(Accept, []). 226 227 media_range_list(<< C, R/bits >>, Acc) when ?IS_TOKEN(C) -> ?LOWER(media_range_type, R, Acc, <<>>); 228 media_range_list(<< C, R/bits >>, Acc) when ?IS_WS_COMMA(C) -> media_range_list(R, Acc); 229 media_range_list(<<>>, Acc) -> lists:reverse(Acc). 230 231 media_range_type(<< C, R/bits >>, Acc, T) when ?IS_TOKEN(C) -> ?LOWER(media_range_type, R, Acc, T); 232 media_range_type(<< $/, C, R/bits >>, Acc, T) when ?IS_TOKEN(C) -> ?LOWER(media_range_subtype, R, Acc, T, <<>>); 233 %% Special clause for badly behaving user agents that send * instead of */*. 234 media_range_type(<< $;, R/bits >>, Acc, <<"*">>) -> media_range_before_param(R, Acc, <<"*">>, <<"*">>, []). 235 236 media_range_subtype(<< C, R/bits >>, Acc, T, S) when ?IS_TOKEN(C) -> ?LOWER(media_range_subtype, R, Acc, T, S); 237 media_range_subtype(R, Acc, T, S) -> media_range_param_sep(R, Acc, T, S, []). 238 239 media_range_param_sep(<<>>, Acc, T, S, P) -> lists:reverse([{{T, S, lists:reverse(P)}, 1000, []}|Acc]); 240 media_range_param_sep(<< $,, R/bits >>, Acc, T, S, P) -> media_range_list(R, [{{T, S, lists:reverse(P)}, 1000, []}|Acc]); 241 media_range_param_sep(<< $;, R/bits >>, Acc, T, S, P) -> media_range_before_param(R, Acc, T, S, P); 242 media_range_param_sep(<< C, R/bits >>, Acc, T, S, P) when ?IS_WS(C) -> media_range_param_sep(R, Acc, T, S, P). 243 244 media_range_before_param(<< C, R/bits >>, Acc, T, S, P) when ?IS_WS(C) -> media_range_before_param(R, Acc, T, S, P); 245 media_range_before_param(<< $q, $=, R/bits >>, Acc, T, S, P) -> media_range_weight(R, Acc, T, S, P); 246 media_range_before_param(<< "charset=", $", R/bits >>, Acc, T, S, P) -> media_range_charset_quoted(R, Acc, T, S, P, <<>>); 247 media_range_before_param(<< "charset=", R/bits >>, Acc, T, S, P) -> media_range_charset(R, Acc, T, S, P, <<>>); 248 media_range_before_param(<< C, R/bits >>, Acc, T, S, P) when ?IS_TOKEN(C) -> ?LOWER(media_range_param, R, Acc, T, S, P, <<>>). 249 250 media_range_charset_quoted(<< $", R/bits >>, Acc, T, S, P, V) -> 251 media_range_param_sep(R, Acc, T, S, [{<<"charset">>, V}|P]); 252 media_range_charset_quoted(<< $\\, C, R/bits >>, Acc, T, S, P, V) when ?IS_VCHAR_OBS(C) -> 253 ?LOWER(media_range_charset_quoted, R, Acc, T, S, P, V); 254 media_range_charset_quoted(<< C, R/bits >>, Acc, T, S, P, V) when ?IS_VCHAR_OBS(C) -> 255 ?LOWER(media_range_charset_quoted, R, Acc, T, S, P, V). 256 257 media_range_charset(<< C, R/bits >>, Acc, T, S, P, V) when ?IS_TOKEN(C) -> 258 ?LOWER(media_range_charset, R, Acc, T, S, P, V); 259 media_range_charset(R, Acc, T, S, P, V) -> 260 media_range_param_sep(R, Acc, T, S, [{<<"charset">>, V}|P]). 261 262 media_range_param(<< $=, $", R/bits >>, Acc, T, S, P, K) -> media_range_quoted(R, Acc, T, S, P, K, <<>>); 263 media_range_param(<< $=, C, R/bits >>, Acc, T, S, P, K) when ?IS_TOKEN(C) -> media_range_value(R, Acc, T, S, P, K, << C >>); 264 media_range_param(<< C, R/bits >>, Acc, T, S, P, K) when ?IS_TOKEN(C) -> ?LOWER(media_range_param, R, Acc, T, S, P, K). 265 266 media_range_quoted(<< $", R/bits >>, Acc, T, S, P, K, V) -> media_range_param_sep(R, Acc, T, S, [{K, V}|P]); 267 media_range_quoted(<< $\\, C, R/bits >>, Acc, T, S, P, K, V) when ?IS_VCHAR_OBS(C) -> media_range_quoted(R, Acc, T, S, P, K, << V/binary, C >>); 268 media_range_quoted(<< C, R/bits >>, Acc, T, S, P, K, V) when ?IS_VCHAR_OBS(C) -> media_range_quoted(R, Acc, T, S, P, K, << V/binary, C >>). 269 270 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 >>); 271 media_range_value(R, Acc, T, S, P, K, V) -> media_range_param_sep(R, Acc, T, S, [{K, V}|P]). 272 273 media_range_weight(<< "1.000", R/bits >>, Acc, T, S, P) -> accept_ext_sep(R, Acc, T, S, P, 1000, []); 274 media_range_weight(<< "1.00", R/bits >>, Acc, T, S, P) -> accept_ext_sep(R, Acc, T, S, P, 1000, []); 275 media_range_weight(<< "1.0", R/bits >>, Acc, T, S, P) -> accept_ext_sep(R, Acc, T, S, P, 1000, []); 276 media_range_weight(<< "1.", R/bits >>, Acc, T, S, P) -> accept_ext_sep(R, Acc, T, S, P, 1000, []); 277 media_range_weight(<< "1", R/bits >>, Acc, T, S, P) -> accept_ext_sep(R, Acc, T, S, P, 1000, []); 278 media_range_weight(<< "0.", A, B, C, R/bits >>, Acc, T, S, P) when ?IS_DIGIT(A), ?IS_DIGIT(B), ?IS_DIGIT(C) -> 279 accept_ext_sep(R, Acc, T, S, P, (A - $0) * 100 + (B - $0) * 10 + (C - $0), []); 280 media_range_weight(<< "0.", A, B, R/bits >>, Acc, T, S, P) when ?IS_DIGIT(A), ?IS_DIGIT(B) -> 281 accept_ext_sep(R, Acc, T, S, P, (A - $0) * 100 + (B - $0) * 10, []); 282 media_range_weight(<< "0.", A, R/bits >>, Acc, T, S, P) when ?IS_DIGIT(A) -> 283 accept_ext_sep(R, Acc, T, S, P, (A - $0) * 100, []); 284 media_range_weight(<< "0.", R/bits >>, Acc, T, S, P) -> accept_ext_sep(R, Acc, T, S, P, 0, []); 285 media_range_weight(<< "0", R/bits >>, Acc, T, S, P) -> accept_ext_sep(R, Acc, T, S, P, 0, []); 286 %% Special clauses for badly behaving user agents that send .123 instead of 0.123. 287 media_range_weight(<< ".", A, B, C, R/bits >>, Acc, T, S, P) when ?IS_DIGIT(A), ?IS_DIGIT(B), ?IS_DIGIT(C) -> 288 accept_ext_sep(R, Acc, T, S, P, (A - $0) * 100 + (B - $0) * 10 + (C - $0), []); 289 media_range_weight(<< ".", A, B, R/bits >>, Acc, T, S, P) when ?IS_DIGIT(A), ?IS_DIGIT(B) -> 290 accept_ext_sep(R, Acc, T, S, P, (A - $0) * 100 + (B - $0) * 10, []); 291 media_range_weight(<< ".", A, R/bits >>, Acc, T, S, P) when ?IS_DIGIT(A) -> 292 accept_ext_sep(R, Acc, T, S, P, (A - $0) * 100, []). 293 294 accept_ext_sep(<<>>, Acc, T, S, P, Q, E) -> lists:reverse([{{T, S, lists:reverse(P)}, Q, lists:reverse(E)}|Acc]); 295 accept_ext_sep(<< $,, R/bits >>, Acc, T, S, P, Q, E) -> media_range_list(R, [{{T, S, lists:reverse(P)}, Q, lists:reverse(E)}|Acc]); 296 accept_ext_sep(<< $;, R/bits >>, Acc, T, S, P, Q, E) -> accept_before_ext(R, Acc, T, S, P, Q, E); 297 accept_ext_sep(<< C, R/bits >>, Acc, T, S, P, Q, E) when ?IS_WS(C) -> accept_ext_sep(R, Acc, T, S, P, Q, E). 298 299 accept_before_ext(<< C, R/bits >>, Acc, T, S, P, Q, E) when ?IS_WS(C) -> accept_before_ext(R, Acc, T, S, P, Q, E); 300 accept_before_ext(<< C, R/bits >>, Acc, T, S, P, Q, E) when ?IS_TOKEN(C) -> ?LOWER(accept_ext, R, Acc, T, S, P, Q, E, <<>>). 301 302 accept_ext(<< $=, $", R/bits >>, Acc, T, S, P, Q, E, K) -> accept_quoted(R, Acc, T, S, P, Q, E, K, <<>>); 303 accept_ext(<< $=, C, R/bits >>, Acc, T, S, P, Q, E, K) when ?IS_TOKEN(C) -> accept_value(R, Acc, T, S, P, Q, E, K, << C >>); 304 accept_ext(<< C, R/bits >>, Acc, T, S, P, Q, E, K) when ?IS_TOKEN(C) -> ?LOWER(accept_ext, R, Acc, T, S, P, Q, E, K); 305 accept_ext(R, Acc, T, S, P, Q, E, K) -> accept_ext_sep(R, Acc, T, S, P, Q, [K|E]). 306 307 accept_quoted(<< $", R/bits >>, Acc, T, S, P, Q, E, K, V) -> accept_ext_sep(R, Acc, T, S, P, Q, [{K, V}|E]); 308 accept_quoted(<< $\\, C, R/bits >>, Acc, T, S, P, Q, E, K, V) when ?IS_VCHAR_OBS(C) -> accept_quoted(R, Acc, T, S, P, Q, E, K, << V/binary, C >>); 309 accept_quoted(<< C, R/bits >>, Acc, T, S, P, Q, E, K, V) when ?IS_VCHAR_OBS(C) -> accept_quoted(R, Acc, T, S, P, Q, E, K, << V/binary, C >>). 310 311 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 >>); 312 accept_value(R, Acc, T, S, P, Q, E, K, V) -> accept_ext_sep(R, Acc, T, S, P, Q, [{K, V}|E]). 313 314 -ifdef(TEST). 315 accept_ext() -> 316 oneof([token(), parameter()]). 317 318 accept_exts() -> 319 frequency([ 320 {90, []}, 321 {10, small_list(accept_ext())} 322 ]). 323 324 accept_param() -> 325 frequency([ 326 {90, parameter()}, 327 {10, {<<"charset">>, oneof([token(), quoted_string()]), <<>>, <<>>}} 328 ]). 329 330 accept_params() -> 331 small_list(accept_param()). 332 333 accept() -> 334 ?LET({T, S, P, W, E}, 335 {token(), token(), accept_params(), weight(), accept_exts()}, 336 {T, S, P, W, E, iolist_to_binary([T, $/, S, 337 [[OWS1, $;, OWS2, K, $=, V] || {K, V, OWS1, OWS2} <- P], 338 case W of 339 undefined -> []; 340 _ -> [ 341 [<<";q=">>, qvalue_to_iodata(W)], 342 [case Ext of 343 {K, V, OWS1, OWS2} -> [OWS1, $;, OWS2, K, $=, V]; 344 K -> [$;, K] 345 end || Ext <- E]] 346 end])} 347 ). 348 349 prop_parse_accept() -> 350 ?FORALL(L, 351 vector(1, 50, accept()), 352 begin 353 << _, Accept/binary >> = iolist_to_binary([[$,, A] || {_, _, _, _, _, A} <- L]), 354 ResL = parse_accept(Accept), 355 CheckedL = [begin 356 ExpectedP = [case ?LOWER(K) of 357 <<"charset">> -> {<<"charset">>, ?LOWER(unquote(V))}; 358 LowK -> {LowK, unquote(V)} 359 end || {K, V, _, _} <- P], 360 ExpectedE = [case Ext of 361 {K, V, _, _} -> {?LOWER(K), unquote(V)}; 362 K -> ?LOWER(K) 363 end || Ext <- E], 364 ResT =:= ?LOWER(T) 365 andalso ResS =:= ?LOWER(S) 366 andalso ResP =:= ExpectedP 367 andalso (ResW =:= W orelse (W =:= undefined andalso ResW =:= 1000)) 368 andalso ((W =:= undefined andalso ResE =:= []) orelse (W =/= undefined andalso ResE =:= ExpectedE)) 369 end || {{T, S, P, W, E, _}, {{ResT, ResS, ResP}, ResW, ResE}} <- lists:zip(L, ResL)], 370 [true] =:= lists:usort(CheckedL) 371 end 372 ). 373 374 parse_accept_test_() -> 375 Tests = [ 376 {<<>>, []}, 377 {<<" ">>, []}, 378 {<<"audio/*; q=0.2, audio/basic">>, [ 379 {{<<"audio">>, <<"*">>, []}, 200, []}, 380 {{<<"audio">>, <<"basic">>, []}, 1000, []} 381 ]}, 382 {<<"text/plain; q=0.5, text/html, " 383 "text/x-dvi; q=0.8, text/x-c">>, [ 384 {{<<"text">>, <<"plain">>, []}, 500, []}, 385 {{<<"text">>, <<"html">>, []}, 1000, []}, 386 {{<<"text">>, <<"x-dvi">>, []}, 800, []}, 387 {{<<"text">>, <<"x-c">>, []}, 1000, []} 388 ]}, 389 {<<"text/*, text/html, text/html;level=1, */*">>, [ 390 {{<<"text">>, <<"*">>, []}, 1000, []}, 391 {{<<"text">>, <<"html">>, []}, 1000, []}, 392 {{<<"text">>, <<"html">>, [{<<"level">>, <<"1">>}]}, 1000, []}, 393 {{<<"*">>, <<"*">>, []}, 1000, []} 394 ]}, 395 {<<"text/*;q=0.3, text/html;q=0.7, text/html;level=1, " 396 "text/html;level=2;q=0.4, */*;q=0.5">>, [ 397 {{<<"text">>, <<"*">>, []}, 300, []}, 398 {{<<"text">>, <<"html">>, []}, 700, []}, 399 {{<<"text">>, <<"html">>, [{<<"level">>, <<"1">>}]}, 1000, []}, 400 {{<<"text">>, <<"html">>, [{<<"level">>, <<"2">>}]}, 400, []}, 401 {{<<"*">>, <<"*">>, []}, 500, []} 402 ]}, 403 {<<"text/html;level=1;quoted=\"hi hi hi\";" 404 "q=0.123;standalone;complex=gits, text/plain">>, [ 405 {{<<"text">>, <<"html">>, 406 [{<<"level">>, <<"1">>}, {<<"quoted">>, <<"hi hi hi">>}]}, 123, 407 [<<"standalone">>, {<<"complex">>, <<"gits">>}]}, 408 {{<<"text">>, <<"plain">>, []}, 1000, []} 409 ]}, 410 {<<"text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2">>, [ 411 {{<<"text">>, <<"html">>, []}, 1000, []}, 412 {{<<"image">>, <<"gif">>, []}, 1000, []}, 413 {{<<"image">>, <<"jpeg">>, []}, 1000, []}, 414 {{<<"*">>, <<"*">>, []}, 200, []}, 415 {{<<"*">>, <<"*">>, []}, 200, []} 416 ]}, 417 {<<"text/plain; charset=UTF-8">>, [ 418 {{<<"text">>, <<"plain">>, [{<<"charset">>, <<"utf-8">>}]}, 1000, []} 419 ]} 420 ], 421 [{V, fun() -> R = parse_accept(V) end} || {V, R} <- Tests]. 422 423 parse_accept_error_test_() -> 424 Tests = [ 425 <<"audio/basic, */;q=0.5">>, 426 <<"audio/, audio/basic">>, 427 <<"aud\tio/basic">>, 428 <<"audio/basic;t=\"zero \\", 0, " woo\"">> 429 ], 430 [{V, fun() -> {'EXIT', _} = (catch parse_accept(V)) end} || V <- Tests]. 431 432 horse_parse_accept() -> 433 horse:repeat(20000, 434 parse_accept(<<"text/*;q=0.3, text/html;q=0.7, text/html;level=1, " 435 "text/html;level=2;q=0.4, */*;q=0.5">>) 436 ). 437 -endif. 438 439 %% Accept-Charset header. 440 441 -spec parse_accept_charset(binary()) -> [{binary(), qvalue()}]. 442 parse_accept_charset(Charset) -> 443 nonempty(conneg_list(Charset, [])). 444 445 conneg_list(<<>>, Acc) -> lists:reverse(Acc); 446 conneg_list(<< C, R/bits >>, Acc) when ?IS_WS_COMMA(C) -> conneg_list(R, Acc); 447 conneg_list(<< C, R/bits >>, Acc) when ?IS_TOKEN(C) -> ?LOWER(conneg, R, Acc, <<>>). 448 449 conneg(<< C, R/bits >>, Acc, T) when ?IS_TOKEN(C) -> ?LOWER(conneg, R, Acc, T); 450 conneg(R, Acc, T) -> conneg_param_sep(R, Acc, T). 451 452 conneg_param_sep(<<>>, Acc, T) -> lists:reverse([{T, 1000}|Acc]); 453 conneg_param_sep(<< $,, R/bits >>, Acc, T) -> conneg_list(R, [{T, 1000}|Acc]); 454 conneg_param_sep(<< $;, R/bits >>, Acc, T) -> conneg_before_weight(R, Acc, T); 455 conneg_param_sep(<< C, R/bits >>, Acc, T) when ?IS_WS(C) -> conneg_param_sep(R, Acc, T). 456 457 conneg_before_weight(<< C, R/bits >>, Acc, T) when ?IS_WS(C) -> conneg_before_weight(R, Acc, T); 458 conneg_before_weight(<< $q, $=, R/bits >>, Acc, T) -> conneg_weight(R, Acc, T); 459 %% Special clause for broken user agents that confuse ; and , separators. 460 conneg_before_weight(<< C, R/bits >>, Acc, T) when ?IS_TOKEN(C) -> ?LOWER(conneg, R, [{T, 1000}|Acc], <<>>). 461 462 conneg_weight(<< "1.000", R/bits >>, Acc, T) -> conneg_list_sep(R, [{T, 1000}|Acc]); 463 conneg_weight(<< "1.00", R/bits >>, Acc, T) -> conneg_list_sep(R, [{T, 1000}|Acc]); 464 conneg_weight(<< "1.0", R/bits >>, Acc, T) -> conneg_list_sep(R, [{T, 1000}|Acc]); 465 conneg_weight(<< "1.", R/bits >>, Acc, T) -> conneg_list_sep(R, [{T, 1000}|Acc]); 466 conneg_weight(<< "1", R/bits >>, Acc, T) -> conneg_list_sep(R, [{T, 1000}|Acc]); 467 conneg_weight(<< "0.", A, B, C, R/bits >>, Acc, T) when ?IS_DIGIT(A), ?IS_DIGIT(B), ?IS_DIGIT(C) -> 468 conneg_list_sep(R, [{T, (A - $0) * 100 + (B - $0) * 10 + (C - $0)}|Acc]); 469 conneg_weight(<< "0.", A, B, R/bits >>, Acc, T) when ?IS_DIGIT(A), ?IS_DIGIT(B) -> 470 conneg_list_sep(R, [{T, (A - $0) * 100 + (B - $0) * 10}|Acc]); 471 conneg_weight(<< "0.", A, R/bits >>, Acc, T) when ?IS_DIGIT(A) -> 472 conneg_list_sep(R, [{T, (A - $0) * 100}|Acc]); 473 conneg_weight(<< "0.", R/bits >>, Acc, T) -> conneg_list_sep(R, [{T, 0}|Acc]); 474 conneg_weight(<< "0", R/bits >>, Acc, T) -> conneg_list_sep(R, [{T, 0}|Acc]). 475 476 conneg_list_sep(<<>>, Acc) -> lists:reverse(Acc); 477 conneg_list_sep(<< C, R/bits >>, Acc) when ?IS_WS(C) -> conneg_list_sep(R, Acc); 478 conneg_list_sep(<< $,, R/bits >>, Acc) -> conneg_list(R, Acc). 479 480 -ifdef(TEST). 481 accept_charset() -> 482 ?LET({C, W}, 483 {token(), weight()}, 484 {C, W, iolist_to_binary([C, case W of 485 undefined -> []; 486 _ -> [<<";q=">>, qvalue_to_iodata(W)] 487 end])} 488 ). 489 490 prop_parse_accept_charset() -> 491 ?FORALL(L, 492 non_empty(list(accept_charset())), 493 begin 494 << _, AcceptCharset/binary >> = iolist_to_binary([[$,, A] || {_, _, A} <- L]), 495 ResL = parse_accept_charset(AcceptCharset), 496 CheckedL = [begin 497 ResC =:= ?LOWER(Ch) 498 andalso (ResW =:= W orelse (W =:= undefined andalso ResW =:= 1000)) 499 end || {{Ch, W, _}, {ResC, ResW}} <- lists:zip(L, ResL)], 500 [true] =:= lists:usort(CheckedL) 501 end). 502 503 parse_accept_charset_test_() -> 504 Tests = [ 505 {<<"iso-8859-5, unicode-1-1;q=0.8">>, [ 506 {<<"iso-8859-5">>, 1000}, 507 {<<"unicode-1-1">>, 800} 508 ]}, 509 %% Some user agents send this invalid value for the Accept-Charset header 510 {<<"ISO-8859-1;utf-8;q=0.7,*;q=0.7">>, [ 511 {<<"iso-8859-1">>, 1000}, 512 {<<"utf-8">>, 700}, 513 {<<"*">>, 700} 514 ]} 515 ], 516 [{V, fun() -> R = parse_accept_charset(V) end} || {V, R} <- Tests]. 517 518 parse_accept_charset_error_test_() -> 519 Tests = [ 520 <<>> 521 ], 522 [{V, fun() -> {'EXIT', _} = (catch parse_accept_charset(V)) end} || V <- Tests]. 523 524 horse_parse_accept_charset() -> 525 horse:repeat(20000, 526 parse_accept_charset(<<"iso-8859-5, unicode-1-1;q=0.8">>) 527 ). 528 -endif. 529 530 %% Accept-Encoding header. 531 532 -spec parse_accept_encoding(binary()) -> [{binary(), qvalue()}]. 533 parse_accept_encoding(Encoding) -> 534 conneg_list(Encoding, []). 535 536 -ifdef(TEST). 537 accept_encoding() -> 538 ?LET({E, W}, 539 {token(), weight()}, 540 {E, W, iolist_to_binary([E, case W of 541 undefined -> []; 542 _ -> [<<";q=">>, qvalue_to_iodata(W)] 543 end])} 544 ). 545 546 %% @todo This property seems useless, see prop_accept_charset. 547 prop_parse_accept_encoding() -> 548 ?FORALL(L, 549 non_empty(list(accept_encoding())), 550 begin 551 << _, AcceptEncoding/binary >> = iolist_to_binary([[$,, A] || {_, _, A} <- L]), 552 ResL = parse_accept_encoding(AcceptEncoding), 553 CheckedL = [begin 554 ResE =:= ?LOWER(E) 555 andalso (ResW =:= W orelse (W =:= undefined andalso ResW =:= 1000)) 556 end || {{E, W, _}, {ResE, ResW}} <- lists:zip(L, ResL)], 557 [true] =:= lists:usort(CheckedL) 558 end). 559 560 parse_accept_encoding_test_() -> 561 Tests = [ 562 {<<>>, []}, 563 {<<"*">>, [{<<"*">>, 1000}]}, 564 {<<"compress, gzip">>, [ 565 {<<"compress">>, 1000}, 566 {<<"gzip">>, 1000} 567 ]}, 568 {<<"compress;q=0.5, gzip;q=1.0">>, [ 569 {<<"compress">>, 500}, 570 {<<"gzip">>, 1000} 571 ]}, 572 {<<"gzip;q=1.0, identity; q=0.5, *;q=0">>, [ 573 {<<"gzip">>, 1000}, 574 {<<"identity">>, 500}, 575 {<<"*">>, 0} 576 ]} 577 ], 578 [{V, fun() -> R = parse_accept_encoding(V) end} || {V, R} <- Tests]. 579 580 horse_parse_accept_encoding() -> 581 horse:repeat(20000, 582 parse_accept_encoding(<<"gzip;q=1.0, identity; q=0.5, *;q=0">>) 583 ). 584 -endif. 585 586 %% Accept-Language header. 587 588 -spec parse_accept_language(binary()) -> [{binary(), qvalue()}]. 589 parse_accept_language(LanguageRange) -> 590 nonempty(language_range_list(LanguageRange, [])). 591 592 language_range_list(<<>>, Acc) -> lists:reverse(Acc); 593 language_range_list(<< C, R/bits >>, Acc) when ?IS_WS_COMMA(C) -> language_range_list(R, Acc); 594 language_range_list(<< $*, R/bits >>, Acc) -> language_range_param_sep(R, Acc, <<"*">>); 595 language_range_list(<< C, R/bits >>, Acc) when ?IS_ALPHA(C) -> 596 ?LOWER(language_range, R, Acc, 1, <<>>). 597 598 language_range(<< $-, C, R/bits >>, Acc, _, T) when ?IS_ALPHANUM(C) -> 599 ?LOWER(language_range_sub, R, Acc, 1, << T/binary, $- >>); 600 language_range(<< C, R/bits >>, Acc, N, T) when ?IS_ALPHA(C), N < 8 -> 601 ?LOWER(language_range, R, Acc, N + 1, T); 602 language_range(R, Acc, _, T) -> language_range_param_sep(R, Acc, T). 603 604 language_range_sub(<< $-, R/bits >>, Acc, _, T) -> language_range_sub(R, Acc, 0, << T/binary, $- >>); 605 language_range_sub(<< C, R/bits >>, Acc, N, T) when ?IS_ALPHANUM(C), N < 8 -> 606 ?LOWER(language_range_sub, R, Acc, N + 1, T); 607 language_range_sub(R, Acc, _, T) -> language_range_param_sep(R, Acc, T). 608 609 language_range_param_sep(<<>>, Acc, T) -> lists:reverse([{T, 1000}|Acc]); 610 language_range_param_sep(<< $,, R/bits >>, Acc, T) -> language_range_list(R, [{T, 1000}|Acc]); 611 language_range_param_sep(<< $;, R/bits >>, Acc, T) -> language_range_before_weight(R, Acc, T); 612 language_range_param_sep(<< C, R/bits >>, Acc, T) when ?IS_WS(C) -> language_range_param_sep(R, Acc, T). 613 614 language_range_before_weight(<< C, R/bits >>, Acc, T) when ?IS_WS(C) -> language_range_before_weight(R, Acc, T); 615 language_range_before_weight(<< $q, $=, R/bits >>, Acc, T) -> language_range_weight(R, Acc, T); 616 %% Special clause for broken user agents that confuse ; and , separators. 617 language_range_before_weight(<< C, R/bits >>, Acc, T) when ?IS_ALPHA(C) -> 618 ?LOWER(language_range, R, [{T, 1000}|Acc], 1, <<>>). 619 620 language_range_weight(<< "1.000", R/bits >>, Acc, T) -> language_range_list_sep(R, [{T, 1000}|Acc]); 621 language_range_weight(<< "1.00", R/bits >>, Acc, T) -> language_range_list_sep(R, [{T, 1000}|Acc]); 622 language_range_weight(<< "1.0", R/bits >>, Acc, T) -> language_range_list_sep(R, [{T, 1000}|Acc]); 623 language_range_weight(<< "1.", R/bits >>, Acc, T) -> language_range_list_sep(R, [{T, 1000}|Acc]); 624 language_range_weight(<< "1", R/bits >>, Acc, T) -> language_range_list_sep(R, [{T, 1000}|Acc]); 625 language_range_weight(<< "0.", A, B, C, R/bits >>, Acc, T) when ?IS_DIGIT(A), ?IS_DIGIT(B), ?IS_DIGIT(C) -> 626 language_range_list_sep(R, [{T, (A - $0) * 100 + (B - $0) * 10 + (C - $0)}|Acc]); 627 language_range_weight(<< "0.", A, B, R/bits >>, Acc, T) when ?IS_DIGIT(A), ?IS_DIGIT(B) -> 628 language_range_list_sep(R, [{T, (A - $0) * 100 + (B - $0) * 10}|Acc]); 629 language_range_weight(<< "0.", A, R/bits >>, Acc, T) when ?IS_DIGIT(A) -> 630 language_range_list_sep(R, [{T, (A - $0) * 100}|Acc]); 631 language_range_weight(<< "0.", R/bits >>, Acc, T) -> language_range_list_sep(R, [{T, 0}|Acc]); 632 language_range_weight(<< "0", R/bits >>, Acc, T) -> language_range_list_sep(R, [{T, 0}|Acc]). 633 634 language_range_list_sep(<<>>, Acc) -> lists:reverse(Acc); 635 language_range_list_sep(<< C, R/bits >>, Acc) when ?IS_WS(C) -> language_range_list_sep(R, Acc); 636 language_range_list_sep(<< $,, R/bits >>, Acc) -> language_range_list(R, Acc). 637 638 -ifdef(TEST). 639 language_range_tag() -> 640 vector(1, 8, alpha()). 641 642 language_range_subtag() -> 643 [$-, vector(1, 8, alphanum())]. 644 645 language_range() -> 646 [language_range_tag(), small_list(language_range_subtag())]. 647 648 accept_language() -> 649 ?LET({R, W}, 650 {language_range(), weight()}, 651 {iolist_to_binary(R), W, iolist_to_binary([R, case W of 652 undefined -> []; 653 _ -> [<<";q=">>, qvalue_to_iodata(W)] 654 end])} 655 ). 656 657 prop_parse_accept_language() -> 658 ?FORALL(L, 659 non_empty(list(accept_language())), 660 begin 661 << _, AcceptLanguage/binary >> = iolist_to_binary([[$,, A] || {_, _, A} <- L]), 662 ResL = parse_accept_language(AcceptLanguage), 663 CheckedL = [begin 664 ResR =:= ?LOWER(R) 665 andalso (ResW =:= W orelse (W =:= undefined andalso ResW =:= 1000)) 666 end || {{R, W, _}, {ResR, ResW}} <- lists:zip(L, ResL)], 667 [true] =:= lists:usort(CheckedL) 668 end). 669 670 parse_accept_language_test_() -> 671 Tests = [ 672 {<<"da, en-gb;q=0.8, en;q=0.7">>, [ 673 {<<"da">>, 1000}, 674 {<<"en-gb">>, 800}, 675 {<<"en">>, 700} 676 ]}, 677 {<<"en, en-US, en-cockney, i-cherokee, x-pig-latin, es-419">>, [ 678 {<<"en">>, 1000}, 679 {<<"en-us">>, 1000}, 680 {<<"en-cockney">>, 1000}, 681 {<<"i-cherokee">>, 1000}, 682 {<<"x-pig-latin">>, 1000}, 683 {<<"es-419">>, 1000} 684 ]} 685 ], 686 [{V, fun() -> R = parse_accept_language(V) end} || {V, R} <- Tests]. 687 688 parse_accept_language_error_test_() -> 689 Tests = [ 690 <<>>, 691 <<"loooooong">>, 692 <<"en-us-loooooong">>, 693 <<"419-en-us">> 694 ], 695 [{V, fun() -> {'EXIT', _} = (catch parse_accept_language(V)) end} || V <- Tests]. 696 697 horse_parse_accept_language() -> 698 horse:repeat(20000, 699 parse_accept_language(<<"da, en-gb;q=0.8, en;q=0.7">>) 700 ). 701 -endif. 702 703 %% Accept-Ranges header. 704 705 -spec parse_accept_ranges(binary()) -> [binary()]. 706 parse_accept_ranges(<<"none">>) -> []; 707 parse_accept_ranges(<<"bytes">>) -> [<<"bytes">>]; 708 parse_accept_ranges(AcceptRanges) -> 709 nonempty(token_ci_list(AcceptRanges, [])). 710 711 -ifdef(TEST). 712 parse_accept_ranges_test_() -> 713 Tests = [ 714 {<<"bytes">>, [<<"bytes">>]}, 715 {<<"none">>, []}, 716 {<<"bytes, pages, kilos">>, [<<"bytes">>, <<"pages">>, <<"kilos">>]} 717 ], 718 [{V, fun() -> R = parse_accept_ranges(V) end} || {V, R} <- Tests]. 719 720 parse_accept_ranges_error_test_() -> 721 Tests = [ 722 <<>> 723 ], 724 [{V, fun() -> {'EXIT', _} = (catch parse_accept_ranges(V)) end} || V <- Tests]. 725 726 horse_parse_accept_ranges_none() -> 727 horse:repeat(200000, 728 parse_accept_ranges(<<"none">>) 729 ). 730 731 horse_parse_accept_ranges_bytes() -> 732 horse:repeat(200000, 733 parse_accept_ranges(<<"bytes">>) 734 ). 735 736 horse_parse_accept_ranges_other() -> 737 horse:repeat(200000, 738 parse_accept_ranges(<<"bytes, pages, kilos">>) 739 ). 740 -endif. 741 742 %% Access-Control-Allow-Credentials header. 743 744 -spec access_control_allow_credentials() -> iodata(). 745 access_control_allow_credentials() -> <<"true">>. 746 747 %% Access-Control-Allow-Headers header. 748 749 -spec access_control_allow_headers([binary()]) -> iodata(). 750 access_control_allow_headers(Headers) -> 751 join_token_list(nonempty(Headers)). 752 753 -ifdef(TEST). 754 access_control_allow_headers_test_() -> 755 Tests = [ 756 {[<<"accept">>], <<"accept">>}, 757 {[<<"accept">>, <<"authorization">>, <<"content-type">>], <<"accept, authorization, content-type">>} 758 ], 759 [{lists:flatten(io_lib:format("~p", [V])), 760 fun() -> R = iolist_to_binary(access_control_allow_headers(V)) end} || {V, R} <- Tests]. 761 762 access_control_allow_headers_error_test_() -> 763 Tests = [ 764 [] 765 ], 766 [{lists:flatten(io_lib:format("~p", [V])), 767 fun() -> {'EXIT', _} = (catch access_control_allow_headers(V)) end} || V <- Tests]. 768 769 horse_access_control_allow_headers() -> 770 horse:repeat(200000, 771 access_control_allow_headers([<<"accept">>, <<"authorization">>, <<"content-type">>]) 772 ). 773 -endif. 774 775 %% Access-Control-Allow-Methods header. 776 777 -spec access_control_allow_methods([binary()]) -> iodata(). 778 access_control_allow_methods(Methods) -> 779 join_token_list(nonempty(Methods)). 780 781 -ifdef(TEST). 782 access_control_allow_methods_test_() -> 783 Tests = [ 784 {[<<"GET">>], <<"GET">>}, 785 {[<<"GET">>, <<"POST">>, <<"DELETE">>], <<"GET, POST, DELETE">>} 786 ], 787 [{lists:flatten(io_lib:format("~p", [V])), 788 fun() -> R = iolist_to_binary(access_control_allow_methods(V)) end} || {V, R} <- Tests]. 789 790 access_control_allow_methods_error_test_() -> 791 Tests = [ 792 [] 793 ], 794 [{lists:flatten(io_lib:format("~p", [V])), 795 fun() -> {'EXIT', _} = (catch access_control_allow_methods(V)) end} || V <- Tests]. 796 797 horse_access_control_allow_methods() -> 798 horse:repeat(200000, 799 access_control_allow_methods([<<"GET">>, <<"POST">>, <<"DELETE">>]) 800 ). 801 -endif. 802 803 %% Access-Control-Allow-Origin header. 804 805 -spec access_control_allow_origin({binary(), binary(), 0..65535} | reference() | '*') -> iodata(). 806 access_control_allow_origin({Scheme, Host, Port}) -> 807 case default_port(Scheme) of 808 Port -> [Scheme, <<"://">>, Host]; 809 _ -> [Scheme, <<"://">>, Host, <<":">>, integer_to_binary(Port)] 810 end; 811 access_control_allow_origin('*') -> <<$*>>; 812 access_control_allow_origin(Ref) when is_reference(Ref) -> <<"null">>. 813 814 -ifdef(TEST). 815 access_control_allow_origin_test_() -> 816 Tests = [ 817 {{<<"http">>, <<"www.example.org">>, 8080}, <<"http://www.example.org:8080">>}, 818 {{<<"http">>, <<"www.example.org">>, 80}, <<"http://www.example.org">>}, 819 {{<<"http">>, <<"192.0.2.1">>, 8080}, <<"http://192.0.2.1:8080">>}, 820 {{<<"http">>, <<"192.0.2.1">>, 80}, <<"http://192.0.2.1">>}, 821 {{<<"http">>, <<"[2001:db8::1]">>, 8080}, <<"http://[2001:db8::1]:8080">>}, 822 {{<<"http">>, <<"[2001:db8::1]">>, 80}, <<"http://[2001:db8::1]">>}, 823 {{<<"http">>, <<"[::ffff:192.0.2.1]">>, 8080}, <<"http://[::ffff:192.0.2.1]:8080">>}, 824 {{<<"http">>, <<"[::ffff:192.0.2.1]">>, 80}, <<"http://[::ffff:192.0.2.1]">>}, 825 {make_ref(), <<"null">>}, 826 {'*', <<$*>>} 827 ], 828 [{lists:flatten(io_lib:format("~p", [V])), 829 fun() -> R = iolist_to_binary(access_control_allow_origin(V)) end} || {V, R} <- Tests]. 830 831 horse_access_control_allow_origin() -> 832 horse:repeat(200000, 833 access_control_allow_origin({<<"http">>, <<"example.org">>, 8080}) 834 ). 835 -endif. 836 837 %% Access-Control-Expose-Headers header. 838 839 -spec access_control_expose_headers([binary()]) -> iodata(). 840 access_control_expose_headers(Headers) -> 841 join_token_list(nonempty(Headers)). 842 843 -ifdef(TEST). 844 access_control_expose_headers_test_() -> 845 Tests = [ 846 {[<<"accept">>], <<"accept">>}, 847 {[<<"accept">>, <<"authorization">>, <<"content-type">>], <<"accept, authorization, content-type">>} 848 ], 849 [{lists:flatten(io_lib:format("~p", [V])), 850 fun() -> R = iolist_to_binary(access_control_expose_headers(V)) end} || {V, R} <- Tests]. 851 852 access_control_expose_headers_error_test_() -> 853 Tests = [ 854 [] 855 ], 856 [{lists:flatten(io_lib:format("~p", [V])), 857 fun() -> {'EXIT', _} = (catch access_control_expose_headers(V)) end} || V <- Tests]. 858 859 horse_access_control_expose_headers() -> 860 horse:repeat(200000, 861 access_control_expose_headers([<<"accept">>, <<"authorization">>, <<"content-type">>]) 862 ). 863 -endif. 864 865 %% Access-Control-Max-Age header. 866 867 -spec access_control_max_age(non_neg_integer()) -> iodata(). 868 access_control_max_age(MaxAge) -> integer_to_binary(MaxAge). 869 870 -ifdef(TEST). 871 access_control_max_age_test_() -> 872 Tests = [ 873 {0, <<"0">>}, 874 {42, <<"42">>}, 875 {69, <<"69">>}, 876 {1337, <<"1337">>}, 877 {3495, <<"3495">>}, 878 {1234567890, <<"1234567890">>} 879 ], 880 [{V, fun() -> R = access_control_max_age(V) end} || {V, R} <- Tests]. 881 -endif. 882 883 %% Access-Control-Request-Headers header. 884 885 -spec parse_access_control_request_headers(binary()) -> [binary()]. 886 parse_access_control_request_headers(Headers) -> 887 token_ci_list(Headers, []). 888 889 -ifdef(TEST). 890 headers() -> 891 ?LET(L, 892 list({ows(), ows(), token()}), 893 case L of 894 [] -> {[], <<>>}; 895 _ -> 896 << _, Headers/binary >> = iolist_to_binary([[OWS1, $,, OWS2, M] || {OWS1, OWS2, M} <- L]), 897 {[?LOWER(M) || {_, _, M} <- L], Headers} 898 end). 899 900 prop_parse_access_control_request_headers() -> 901 ?FORALL({L, Headers}, 902 headers(), 903 L =:= parse_access_control_request_headers(Headers)). 904 905 parse_access_control_request_headers_test_() -> 906 Tests = [ 907 {<<>>, []}, 908 {<<"Content-Type">>, [<<"content-type">>]}, 909 {<<"accept, authorization, content-type">>, [<<"accept">>, <<"authorization">>, <<"content-type">>]}, 910 {<<"accept,, , authorization,content-type">>, [<<"accept">>, <<"authorization">>, <<"content-type">>]} 911 ], 912 [{V, fun() -> R = parse_access_control_request_headers(V) end} || {V, R} <- Tests]. 913 914 horse_parse_access_control_request_headers() -> 915 horse:repeat(200000, 916 parse_access_control_request_headers(<<"accept, authorization, content-type">>) 917 ). 918 -endif. 919 920 %% Access-Control-Request-Method header. 921 922 -spec parse_access_control_request_method(binary()) -> binary(). 923 parse_access_control_request_method(Method) -> 924 true = <<>> =/= Method, 925 ok = validate_token(Method), 926 Method. 927 928 validate_token(<< C, R/bits >>) when ?IS_TOKEN(C) -> validate_token(R); 929 validate_token(<<>>) -> ok. 930 931 -ifdef(TEST). 932 parse_access_control_request_method_test_() -> 933 Tests = [ 934 <<"GET">>, 935 <<"HEAD">>, 936 <<"POST">>, 937 <<"PUT">>, 938 <<"DELETE">>, 939 <<"TRACE">>, 940 <<"CONNECT">>, 941 <<"whatever">> 942 ], 943 [{V, fun() -> R = parse_access_control_request_method(V) end} || {V, R} <- Tests]. 944 945 parse_access_control_request_method_error_test_() -> 946 Tests = [ 947 <<>> 948 ], 949 [{V, fun() -> {'EXIT', _} = (catch parse_access_control_request_method(V)) end} || V <- Tests]. 950 951 horse_parse_access_control_request_method() -> 952 horse:repeat(200000, 953 parse_access_control_request_method(<<"POST">>) 954 ). 955 -endif. 956 957 %% Age header. 958 959 -spec parse_age(binary()) -> non_neg_integer(). 960 parse_age(Age) -> 961 I = binary_to_integer(Age), 962 true = I >= 0, 963 I. 964 965 -ifdef(TEST). 966 parse_age_test_() -> 967 Tests = [ 968 {<<"0">>, 0}, 969 {<<"42">>, 42}, 970 {<<"69">>, 69}, 971 {<<"1337">>, 1337}, 972 {<<"3495">>, 3495}, 973 {<<"1234567890">>, 1234567890} 974 ], 975 [{V, fun() -> R = parse_age(V) end} || {V, R} <- Tests]. 976 977 parse_age_error_test_() -> 978 Tests = [ 979 <<>>, 980 <<"123, 123">>, 981 <<"4.17">> 982 ], 983 [{V, fun() -> {'EXIT', _} = (catch parse_age(V)) end} || V <- Tests]. 984 -endif. 985 986 %% Allow header. 987 988 -spec parse_allow(binary()) -> [binary()]. 989 parse_allow(Allow) -> 990 token_list(Allow, []). 991 992 -ifdef(TEST). 993 allow() -> 994 ?LET(L, 995 list({ows(), ows(), token()}), 996 case L of 997 [] -> {[], <<>>}; 998 _ -> 999 << _, Allow/binary >> = iolist_to_binary([[OWS1, $,, OWS2, M] || {OWS1, OWS2, M} <- L]), 1000 {[M || {_, _, M} <- L], Allow} 1001 end). 1002 1003 prop_parse_allow() -> 1004 ?FORALL({L, Allow}, 1005 allow(), 1006 L =:= parse_allow(Allow)). 1007 1008 parse_allow_test_() -> 1009 Tests = [ 1010 {<<>>, []}, 1011 {<<"GET, HEAD, PUT">>, [<<"GET">>, <<"HEAD">>, <<"PUT">>]} 1012 ], 1013 [{V, fun() -> R = parse_allow(V) end} || {V, R} <- Tests]. 1014 1015 horse_parse_allow() -> 1016 horse:repeat(200000, 1017 parse_allow(<<"GET, HEAD, PUT">>) 1018 ). 1019 -endif. 1020 1021 %% Authorization header. 1022 %% 1023 %% We support Basic, Digest and Bearer schemes only. 1024 %% 1025 %% In the Digest case we do not validate that the mandatory 1026 %% fields are present. When parsing auth-params, we do not 1027 %% accept BWS characters around the "=". 1028 1029 -spec parse_authorization(binary()) 1030 -> {basic, binary(), binary()} 1031 | {bearer, binary()} 1032 | {digest, [{binary(), binary()}]}. 1033 parse_authorization(<<B, A, S, I, C, " ", R/bits >>) 1034 when ((B =:= $B) or (B =:= $b)), ((A =:= $A) or (A =:= $a)), 1035 ((S =:= $S) or (S =:= $s)), ((I =:= $I) or (I =:= $i)), 1036 ((C =:= $C) or (C =:= $c)) -> 1037 auth_basic(base64:decode(R), <<>>); 1038 parse_authorization(<<B, E1, A, R1, E2, R2, " ", R/bits >>) 1039 when (R =/= <<>>), ((B =:= $B) or (B =:= $b)), 1040 ((E1 =:= $E) or (E1 =:= $e)), ((A =:= $A) or (A =:= $a)), 1041 ((R1 =:= $R) or (R1 =:= $r)), ((E2 =:= $E) or (E2 =:= $e)), 1042 ((R2 =:= $R) or (R2 =:= $r)) -> 1043 validate_auth_bearer(R), 1044 {bearer, R}; 1045 parse_authorization(<<D, I, G, E, S, T, " ", R/bits >>) 1046 when ((D =:= $D) or (D =:= $d)), ((I =:= $I) or (I =:= $i)), 1047 ((G =:= $G) or (G =:= $g)), ((E =:= $E) or (E =:= $e)), 1048 ((S =:= $S) or (S =:= $s)), ((T =:= $T) or (T =:= $t)) -> 1049 {digest, nonempty(auth_digest_list(R, []))}. 1050 1051 auth_basic(<< $:, Password/bits >>, UserID) -> {basic, UserID, Password}; 1052 auth_basic(<< C, R/bits >>, UserID) -> auth_basic(R, << UserID/binary, C >>). 1053 1054 validate_auth_bearer(<< C, R/bits >>) when ?IS_TOKEN68(C) -> validate_auth_bearer(R); 1055 validate_auth_bearer(<< $=, R/bits >>) -> validate_auth_bearer_eq(R); 1056 validate_auth_bearer(<<>>) -> ok. 1057 1058 validate_auth_bearer_eq(<< $=, R/bits >>) -> validate_auth_bearer_eq(R); 1059 validate_auth_bearer_eq(<<>>) -> ok. 1060 1061 auth_digest_list(<<>>, Acc) -> lists:reverse(Acc); 1062 auth_digest_list(<< C, R/bits >>, Acc) when ?IS_WS_COMMA(C) -> auth_digest_list(R, Acc); 1063 auth_digest_list(<< "algorithm=", C, R/bits >>, Acc) when ?IS_TOKEN(C) -> auth_digest_token(R, Acc, <<"algorithm">>, << C >>); 1064 auth_digest_list(<< "cnonce=\"", R/bits >>, Acc) -> auth_digest_quoted(R, Acc, <<"cnonce">>, <<>>); 1065 auth_digest_list(<< "nc=", A, B, C, D, E, F, G, H, R/bits >>, Acc) 1066 when ?IS_LHEX(A), ?IS_LHEX(B), ?IS_LHEX(C), ?IS_LHEX(D), 1067 ?IS_LHEX(E), ?IS_LHEX(F), ?IS_LHEX(G), ?IS_LHEX(H) -> 1068 auth_digest_list_sep(R, [{<<"nc">>, << A, B, C, D, E, F, G, H >>}|Acc]); 1069 auth_digest_list(<< "nonce=\"", R/bits >>, Acc) -> auth_digest_quoted(R, Acc, <<"nonce">>, <<>>); 1070 auth_digest_list(<< "opaque=\"", R/bits >>, Acc) -> auth_digest_quoted(R, Acc, <<"opaque">>, <<>>); 1071 auth_digest_list(<< "qop=", C, R/bits >>, Acc) when ?IS_TOKEN(C) -> auth_digest_token(R, Acc, <<"qop">>, << C >>); 1072 auth_digest_list(<< "realm=\"", R/bits >>, Acc) -> auth_digest_quoted(R, Acc, <<"realm">>, <<>>); 1073 auth_digest_list(<< "response=\"", R/bits >>, Acc) -> auth_digest_quoted(R, Acc, <<"response">>, <<>>); 1074 auth_digest_list(<< "uri=\"", R/bits >>, Acc) -> auth_digest_quoted(R, Acc, <<"uri">>, <<>>); 1075 auth_digest_list(<< "username=\"", R/bits >>, Acc) -> auth_digest_quoted(R, Acc, <<"username">>, <<>>); 1076 auth_digest_list(<< C, R/bits >>, Acc) when ?IS_TOKEN(C) -> 1077 ?LOWER(auth_digest_param, R, Acc, <<>>). 1078 1079 auth_digest_param(<< $=, $", R/bits >>, Acc, K) -> auth_digest_quoted(R, Acc, K, <<>>); 1080 auth_digest_param(<< $=, C, R/bits >>, Acc, K) when ?IS_TOKEN(C) -> auth_digest_token(R, Acc, K, << C >>); 1081 auth_digest_param(<< C, R/bits >>, Acc, K) when ?IS_TOKEN(C) -> 1082 ?LOWER(auth_digest_param, R, Acc, K). 1083 1084 auth_digest_token(<< C, R/bits >>, Acc, K, V) when ?IS_TOKEN(C) -> auth_digest_token(R, Acc, K, << V/binary, C >>); 1085 auth_digest_token(R, Acc, K, V) -> auth_digest_list_sep(R, [{K, V}|Acc]). 1086 1087 auth_digest_quoted(<< $", R/bits >>, Acc, K, V) -> auth_digest_list_sep(R, [{K, V}|Acc]); 1088 auth_digest_quoted(<< $\\, C, R/bits >>, Acc, K, V) when ?IS_VCHAR_OBS(C) -> auth_digest_quoted(R, Acc, K, << V/binary, C >>); 1089 auth_digest_quoted(<< C, R/bits >>, Acc, K, V) when ?IS_VCHAR_OBS(C) -> auth_digest_quoted(R, Acc, K, << V/binary, C >>). 1090 1091 auth_digest_list_sep(<<>>, Acc) -> lists:reverse(Acc); 1092 auth_digest_list_sep(<< $,, R/bits >>, Acc) -> auth_digest_list(R, Acc); 1093 auth_digest_list_sep(<< C, R/bits >>, Acc) when ?IS_WS(C) -> auth_digest_list_sep(R, Acc). 1094 1095 -ifdef(TEST). 1096 parse_authorization_test_() -> 1097 Tests = [ 1098 {<<"Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==">>, {basic, <<"Aladdin">>, <<"open sesame">>}}, 1099 {<<"bAsIc QWxhZGRpbjpvcGVuIHNlc2FtZQ==">>, {basic, <<"Aladdin">>, <<"open sesame">>}}, 1100 {<<"Bearer mF_9.B5f-4.1JqM">>, {bearer, <<"mF_9.B5f-4.1JqM">>}}, 1101 {<<"bEaRer mF_9.B5f-4.1JqM">>, {bearer, <<"mF_9.B5f-4.1JqM">>}}, 1102 {<<"Digest username=\"Mufasa\"," 1103 "realm=\"testrealm@host.com\"," 1104 "nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\"," 1105 "uri=\"/dir/index.html\"," 1106 "qop=auth," 1107 "nc=00000001," 1108 "cnonce=\"0a4f113b\"," 1109 "response=\"6629fae49393a05397450978507c4ef1\"," 1110 "opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"">>, 1111 {digest, [ 1112 {<<"username">>, <<"Mufasa">>}, 1113 {<<"realm">>, <<"testrealm@host.com">>}, 1114 {<<"nonce">>, <<"dcd98b7102dd2f0e8b11d0f600bfb0c093">>}, 1115 {<<"uri">>, <<"/dir/index.html">>}, 1116 {<<"qop">>, <<"auth">>}, 1117 {<<"nc">>, <<"00000001">>}, 1118 {<<"cnonce">>, <<"0a4f113b">>}, 1119 {<<"response">>, <<"6629fae49393a05397450978507c4ef1">>}, 1120 {<<"opaque">>, <<"5ccc069c403ebaf9f0171e9517f40e41">>}]}} 1121 ], 1122 [{V, fun() -> R = parse_authorization(V) end} || {V, R} <- Tests]. 1123 1124 horse_parse_authorization_basic() -> 1125 horse:repeat(20000, 1126 parse_authorization(<<"Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==">>) 1127 ). 1128 1129 horse_parse_authorization_bearer() -> 1130 horse:repeat(20000, 1131 parse_authorization(<<"Bearer mF_9.B5f-4.1JqM">>) 1132 ). 1133 1134 horse_parse_authorization_digest() -> 1135 horse:repeat(20000, 1136 parse_authorization( 1137 <<"Digest username=\"Mufasa\"," 1138 "realm=\"testrealm@host.com\"," 1139 "nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\"," 1140 "uri=\"/dir/index.html\"," 1141 "qop=auth," 1142 "nc=00000001," 1143 "cnonce=\"0a4f113b\"," 1144 "response=\"6629fae49393a05397450978507c4ef1\"," 1145 "opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"">>) 1146 ). 1147 -endif. 1148 1149 %% Cache-Control header. 1150 %% 1151 %% In the fields list case, we do not support escaping, which shouldn't be needed anyway. 1152 1153 -spec parse_cache_control(binary()) 1154 -> [binary() | {binary(), binary()} | {binary(), non_neg_integer()} | {binary(), [binary()]}]. 1155 parse_cache_control(<<"no-cache">>) -> 1156 [<<"no-cache">>]; 1157 parse_cache_control(<<"max-age=0">>) -> 1158 [{<<"max-age">>, 0}]; 1159 parse_cache_control(CacheControl) -> 1160 nonempty(cache_directive_list(CacheControl, [])). 1161 1162 cache_directive_list(<<>>, Acc) -> lists:reverse(Acc); 1163 cache_directive_list(<< C, R/bits >>, Acc) when ?IS_WS_COMMA(C)-> cache_directive_list(R, Acc); 1164 cache_directive_list(<< C, R/bits >>, Acc) when ?IS_TOKEN(C) -> 1165 ?LOWER(cache_directive, R, Acc, <<>>). 1166 1167 cache_directive(<< $=, $", R/bits >>, Acc, T) 1168 when (T =:= <<"no-cache">>) or (T =:= <<"private">>) -> 1169 cache_directive_fields_list(R, Acc, T, []); 1170 cache_directive(<< $=, C, R/bits >>, Acc, T) 1171 when ?IS_DIGIT(C), (T =:= <<"max-age">>) or (T =:= <<"max-stale">>) 1172 or (T =:= <<"min-fresh">>) or (T =:= <<"s-maxage">>) -> 1173 cache_directive_delta(R, Acc, T, (C - $0)); 1174 cache_directive(<< $=, $", R/bits >>, Acc, T) -> cache_directive_quoted_string(R, Acc, T, <<>>); 1175 cache_directive(<< $=, C, R/bits >>, Acc, T) when ?IS_TOKEN(C) -> cache_directive_token(R, Acc, T, << C >>); 1176 cache_directive(<< C, R/bits >>, Acc, T) when ?IS_TOKEN(C) -> 1177 ?LOWER(cache_directive, R, Acc, T); 1178 cache_directive(R, Acc, T) -> cache_directive_list_sep(R, [T|Acc]). 1179 1180 cache_directive_delta(<< C, R/bits >>, Acc, K, V) when ?IS_DIGIT(C) -> cache_directive_delta(R, Acc, K, V * 10 + (C - $0)); 1181 cache_directive_delta(R, Acc, K, V) -> cache_directive_list_sep(R, [{K, V}|Acc]). 1182 1183 cache_directive_fields_list(<< C, R/bits >>, Acc, K, L) when ?IS_WS_COMMA(C) -> cache_directive_fields_list(R, Acc, K, L); 1184 cache_directive_fields_list(<< $", R/bits >>, Acc, K, L) -> cache_directive_list_sep(R, [{K, lists:reverse(L)}|Acc]); 1185 cache_directive_fields_list(<< C, R/bits >>, Acc, K, L) when ?IS_TOKEN(C) -> 1186 ?LOWER(cache_directive_field, R, Acc, K, L, <<>>). 1187 1188 cache_directive_field(<< C, R/bits >>, Acc, K, L, F) when ?IS_TOKEN(C) -> 1189 ?LOWER(cache_directive_field, R, Acc, K, L, F); 1190 cache_directive_field(R, Acc, K, L, F) -> cache_directive_fields_list_sep(R, Acc, K, [F|L]). 1191 1192 cache_directive_fields_list_sep(<< C, R/bits >>, Acc, K, L) when ?IS_WS(C) -> cache_directive_fields_list_sep(R, Acc, K, L); 1193 cache_directive_fields_list_sep(<< $,, R/bits >>, Acc, K, L) -> cache_directive_fields_list(R, Acc, K, L); 1194 cache_directive_fields_list_sep(<< $", R/bits >>, Acc, K, L) -> cache_directive_list_sep(R, [{K, lists:reverse(L)}|Acc]). 1195 1196 cache_directive_token(<< C, R/bits >>, Acc, K, V) when ?IS_TOKEN(C) -> cache_directive_token(R, Acc, K, << V/binary, C >>); 1197 cache_directive_token(R, Acc, K, V) -> cache_directive_list_sep(R, [{K, V}|Acc]). 1198 1199 cache_directive_quoted_string(<< $", R/bits >>, Acc, K, V) -> cache_directive_list_sep(R, [{K, V}|Acc]); 1200 cache_directive_quoted_string(<< $\\, C, R/bits >>, Acc, K, V) when ?IS_VCHAR_OBS(C) -> 1201 cache_directive_quoted_string(R, Acc, K, << V/binary, C >>); 1202 cache_directive_quoted_string(<< C, R/bits >>, Acc, K, V) when ?IS_VCHAR_OBS(C) -> 1203 cache_directive_quoted_string(R, Acc, K, << V/binary, C >>). 1204 1205 cache_directive_list_sep(<<>>, Acc) -> lists:reverse(Acc); 1206 cache_directive_list_sep(<< C, R/bits >>, Acc) when ?IS_WS(C) -> cache_directive_list_sep(R, Acc); 1207 cache_directive_list_sep(<< $,, R/bits >>, Acc) -> cache_directive_list(R, Acc). 1208 1209 -ifdef(TEST). 1210 cache_directive_unreserved_token() -> 1211 ?SUCHTHAT(T, 1212 token(), 1213 T =/= <<"max-age">> andalso T =/= <<"max-stale">> andalso T =/= <<"min-fresh">> 1214 andalso T =/= <<"s-maxage">> andalso T =/= <<"no-cache">> andalso T =/= <<"private">>). 1215 1216 cache_directive() -> 1217 oneof([ 1218 token(), 1219 {cache_directive_unreserved_token(), token()}, 1220 {cache_directive_unreserved_token(), quoted_string()}, 1221 {elements([<<"max-age">>, <<"max-stale">>, <<"min-fresh">>, <<"s-maxage">>]), non_neg_integer()}, 1222 {fields, elements([<<"no-cache">>, <<"private">>]), small_list(token())} 1223 ]). 1224 1225 cache_control() -> 1226 ?LET(L, 1227 non_empty(list(cache_directive())), 1228 begin 1229 << _, CacheControl/binary >> = iolist_to_binary([[$,, 1230 case C of 1231 {fields, K, V} -> [K, $=, $", [[F, $,] || F <- V], $"]; 1232 {K, V} when is_integer(V) -> [K, $=, integer_to_binary(V)]; 1233 {K, V} -> [K, $=, V]; 1234 K -> K 1235 end] || C <- L]), 1236 {L, CacheControl} 1237 end). 1238 1239 prop_parse_cache_control() -> 1240 ?FORALL({L, CacheControl}, 1241 cache_control(), 1242 begin 1243 ResL = parse_cache_control(CacheControl), 1244 CheckedL = [begin 1245 ExpectedCc = case Cc of 1246 {fields, K, V} -> {?LOWER(K), [?LOWER(F) || F <- V]}; 1247 {K, V} -> {?LOWER(K), unquote(V)}; 1248 K -> ?LOWER(K) 1249 end, 1250 ExpectedCc =:= ResCc 1251 end || {Cc, ResCc} <- lists:zip(L, ResL)], 1252 [true] =:= lists:usort(CheckedL) 1253 end). 1254 1255 parse_cache_control_test_() -> 1256 Tests = [ 1257 {<<"no-cache">>, [<<"no-cache">>]}, 1258 {<<"no-store">>, [<<"no-store">>]}, 1259 {<<"max-age=0">>, [{<<"max-age">>, 0}]}, 1260 {<<"max-age=30">>, [{<<"max-age">>, 30}]}, 1261 {<<"private, community=\"UCI\"">>, [<<"private">>, {<<"community">>, <<"UCI">>}]}, 1262 {<<"private=\"Content-Type, Content-Encoding, Content-Language\"">>, 1263 [{<<"private">>, [<<"content-type">>, <<"content-encoding">>, <<"content-language">>]}]} 1264 ], 1265 [{V, fun() -> R = parse_cache_control(V) end} || {V, R} <- Tests]. 1266 1267 parse_cache_control_error_test_() -> 1268 Tests = [ 1269 <<>> 1270 ], 1271 [{V, fun() -> {'EXIT', _} = (catch parse_cache_control(V)) end} || V <- Tests]. 1272 1273 horse_parse_cache_control_no_cache() -> 1274 horse:repeat(200000, 1275 parse_cache_control(<<"no-cache">>) 1276 ). 1277 1278 horse_parse_cache_control_max_age_0() -> 1279 horse:repeat(200000, 1280 parse_cache_control(<<"max-age=0">>) 1281 ). 1282 1283 horse_parse_cache_control_max_age_30() -> 1284 horse:repeat(200000, 1285 parse_cache_control(<<"max-age=30">>) 1286 ). 1287 1288 horse_parse_cache_control_custom() -> 1289 horse:repeat(200000, 1290 parse_cache_control(<<"private, community=\"UCI\"">>) 1291 ). 1292 1293 horse_parse_cache_control_fields() -> 1294 horse:repeat(200000, 1295 parse_cache_control(<<"private=\"Content-Type, Content-Encoding, Content-Language\"">>) 1296 ). 1297 -endif. 1298 1299 %% Connection header. 1300 1301 -spec parse_connection(binary()) -> [binary()]. 1302 parse_connection(<<"close">>) -> 1303 [<<"close">>]; 1304 parse_connection(<<"keep-alive">>) -> 1305 [<<"keep-alive">>]; 1306 parse_connection(Connection) -> 1307 nonempty(token_ci_list(Connection, [])). 1308 1309 -ifdef(TEST). 1310 prop_parse_connection() -> 1311 ?FORALL(L, 1312 non_empty(list(token())), 1313 begin 1314 << _, Connection/binary >> = iolist_to_binary([[$,, C] || C <- L]), 1315 ResL = parse_connection(Connection), 1316 CheckedL = [?LOWER(Co) =:= ResC || {Co, ResC} <- lists:zip(L, ResL)], 1317 [true] =:= lists:usort(CheckedL) 1318 end). 1319 1320 parse_connection_test_() -> 1321 Tests = [ 1322 {<<"close">>, [<<"close">>]}, 1323 {<<"ClOsE">>, [<<"close">>]}, 1324 {<<"Keep-Alive">>, [<<"keep-alive">>]}, 1325 {<<"keep-alive, Upgrade">>, [<<"keep-alive">>, <<"upgrade">>]} 1326 ], 1327 [{V, fun() -> R = parse_connection(V) end} || {V, R} <- Tests]. 1328 1329 parse_connection_error_test_() -> 1330 Tests = [ 1331 <<>> 1332 ], 1333 [{V, fun() -> {'EXIT', _} = (catch parse_connection(V)) end} || V <- Tests]. 1334 1335 horse_parse_connection_close() -> 1336 horse:repeat(200000, 1337 parse_connection(<<"close">>) 1338 ). 1339 1340 horse_parse_connection_keepalive() -> 1341 horse:repeat(200000, 1342 parse_connection(<<"keep-alive">>) 1343 ). 1344 1345 horse_parse_connection_keepalive_upgrade() -> 1346 horse:repeat(200000, 1347 parse_connection(<<"keep-alive, upgrade">>) 1348 ). 1349 -endif. 1350 1351 %% Content-Encoding header. 1352 1353 -spec parse_content_encoding(binary()) -> [binary()]. 1354 parse_content_encoding(ContentEncoding) -> 1355 nonempty(token_ci_list(ContentEncoding, [])). 1356 1357 -ifdef(TEST). 1358 parse_content_encoding_test_() -> 1359 Tests = [ 1360 {<<"gzip">>, [<<"gzip">>]} 1361 ], 1362 [{V, fun() -> R = parse_content_encoding(V) end} || {V, R} <- Tests]. 1363 1364 parse_content_encoding_error_test_() -> 1365 Tests = [ 1366 <<>> 1367 ], 1368 [{V, fun() -> {'EXIT', _} = (catch parse_content_encoding(V)) end} || V <- Tests]. 1369 1370 horse_parse_content_encoding() -> 1371 horse:repeat(200000, 1372 parse_content_encoding(<<"gzip">>) 1373 ). 1374 -endif. 1375 1376 %% Content-Language header. 1377 %% 1378 %% We do not support irregular deprecated tags that do not match the ABNF. 1379 1380 -spec parse_content_language(binary()) -> [binary()]. 1381 parse_content_language(ContentLanguage) -> 1382 nonempty(langtag_list(ContentLanguage, [])). 1383 1384 langtag_list(<<>>, Acc) -> lists:reverse(Acc); 1385 langtag_list(<< C, R/bits >>, Acc) when ?IS_WS_COMMA(C) -> langtag_list(R, Acc); 1386 langtag_list(<< A, B, C, R/bits >>, Acc) when ?IS_ALPHA(A), ?IS_ALPHA(B), ?IS_ALPHA(C) -> 1387 langtag_extlang(R, Acc, << ?LC(A), ?LC(B), ?LC(C) >>, 0); 1388 langtag_list(<< A, B, R/bits >>, Acc) when ?IS_ALPHA(A), ?IS_ALPHA(B) -> 1389 langtag_extlang(R, Acc, << ?LC(A), ?LC(B) >>, 0); 1390 langtag_list(<< X, R/bits >>, Acc) when X =:= $x; X =:= $X -> langtag_privateuse_sub(R, Acc, << $x >>, 0). 1391 1392 langtag_extlang(<< $-, A, B, C, D, E, F, G, H, R/bits >>, Acc, T, _) 1393 when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D), 1394 ?IS_ALPHANUM(E), ?IS_ALPHANUM(F), ?IS_ALPHANUM(G), ?IS_ALPHANUM(H) -> 1395 langtag_variant(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E), ?LC(F), ?LC(G), ?LC(H) >>); 1396 langtag_extlang(<< $-, A, B, C, D, E, F, G, R/bits >>, Acc, T, _) 1397 when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D), 1398 ?IS_ALPHANUM(E), ?IS_ALPHANUM(F), ?IS_ALPHANUM(G) -> 1399 langtag_variant(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E), ?LC(F), ?LC(G) >>); 1400 langtag_extlang(<< $-, A, B, C, D, E, F, R/bits >>, Acc, T, _) 1401 when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D), 1402 ?IS_ALPHANUM(E), ?IS_ALPHANUM(F) -> 1403 langtag_variant(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E), ?LC(F) >>); 1404 langtag_extlang(<< $-, A, B, C, D, E, R/bits >>, Acc, T, _) 1405 when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D), ?IS_ALPHANUM(E) -> 1406 langtag_variant(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E) >>); 1407 langtag_extlang(<< $-, A, B, C, D, R/bits >>, Acc, T, _) 1408 when ?IS_ALPHA(A), ?IS_ALPHA(B), ?IS_ALPHA(C), ?IS_ALPHA(D) -> 1409 langtag_region(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D) >>); 1410 langtag_extlang(<< $-, A, B, C, R/bits >>, Acc, T, N) 1411 when ?IS_ALPHA(A), ?IS_ALPHA(B), ?IS_ALPHA(C) -> 1412 case N of 1413 2 -> langtag_script(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C) >>); 1414 _ -> langtag_extlang(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C) >>, N + 1) 1415 end; 1416 langtag_extlang(R, Acc, T, _) -> langtag_region(R, Acc, T). 1417 1418 langtag_script(<< $-, A, B, C, D, E, F, G, H, R/bits >>, Acc, T) 1419 when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D), 1420 ?IS_ALPHANUM(E), ?IS_ALPHANUM(F), ?IS_ALPHANUM(G), ?IS_ALPHANUM(H) -> 1421 langtag_variant(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E), ?LC(F), ?LC(G), ?LC(H) >>); 1422 langtag_script(<< $-, A, B, C, D, E, F, G, R/bits >>, Acc, T) 1423 when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D), 1424 ?IS_ALPHANUM(E), ?IS_ALPHANUM(F), ?IS_ALPHANUM(G) -> 1425 langtag_variant(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E), ?LC(F), ?LC(G) >>); 1426 langtag_script(<< $-, A, B, C, D, E, F, R/bits >>, Acc, T) 1427 when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D), 1428 ?IS_ALPHANUM(E), ?IS_ALPHANUM(F) -> 1429 langtag_variant(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E), ?LC(F) >>); 1430 langtag_script(<< $-, A, B, C, D, E, R/bits >>, Acc, T) 1431 when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D), ?IS_ALPHANUM(E) -> 1432 langtag_variant(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E) >>); 1433 langtag_script(<< $-, A, B, C, D, R/bits >>, Acc, T) 1434 when ?IS_ALPHA(A), ?IS_ALPHA(B), ?IS_ALPHA(C), ?IS_ALPHA(D) -> 1435 langtag_region(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D) >>); 1436 langtag_script(R, Acc, T) -> 1437 langtag_region(R, Acc, T). 1438 1439 langtag_region(<< $-, A, B, C, D, E, F, G, H, R/bits >>, Acc, T) 1440 when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D), 1441 ?IS_ALPHANUM(E), ?IS_ALPHANUM(F), ?IS_ALPHANUM(G), ?IS_ALPHANUM(H) -> 1442 langtag_variant(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E), ?LC(F), ?LC(G), ?LC(H) >>); 1443 langtag_region(<< $-, A, B, C, D, E, F, G, R/bits >>, Acc, T) 1444 when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D), 1445 ?IS_ALPHANUM(E), ?IS_ALPHANUM(F), ?IS_ALPHANUM(G) -> 1446 langtag_variant(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E), ?LC(F), ?LC(G) >>); 1447 langtag_region(<< $-, A, B, C, D, E, F, R/bits >>, Acc, T) 1448 when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D), 1449 ?IS_ALPHANUM(E), ?IS_ALPHANUM(F) -> 1450 langtag_variant(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E), ?LC(F) >>); 1451 langtag_region(<< $-, A, B, C, D, E, R/bits >>, Acc, T) 1452 when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D), ?IS_ALPHANUM(E) -> 1453 langtag_variant(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E) >>); 1454 langtag_region(<< $-, A, B, C, D, R/bits >>, Acc, T) 1455 when ?IS_DIGIT(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D) -> 1456 langtag_variant(R, Acc, << T/binary, $-, A, ?LC(B), ?LC(C), ?LC(D) >>); 1457 langtag_region(<< $-, A, B, R/bits >>, Acc, T) when ?IS_ALPHA(A), ?IS_ALPHA(B) -> 1458 langtag_variant(R, Acc, << T/binary, $-, ?LC(A), ?LC(B) >>); 1459 langtag_region(<< $-, A, B, C, R/bits >>, Acc, T) when ?IS_DIGIT(A), ?IS_DIGIT(B), ?IS_DIGIT(C) -> 1460 langtag_variant(R, Acc, << T/binary, $-, A, B, C >>); 1461 langtag_region(R, Acc, T) -> 1462 langtag_variant(R, Acc, T). 1463 1464 langtag_variant(<< $-, A, B, C, D, E, F, G, H, R/bits >>, Acc, T) 1465 when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D), 1466 ?IS_ALPHANUM(E), ?IS_ALPHANUM(F), ?IS_ALPHANUM(G), ?IS_ALPHANUM(H) -> 1467 langtag_variant(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E), ?LC(F), ?LC(G), ?LC(H) >>); 1468 langtag_variant(<< $-, A, B, C, D, E, F, G, R/bits >>, Acc, T) 1469 when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D), 1470 ?IS_ALPHANUM(E), ?IS_ALPHANUM(F), ?IS_ALPHANUM(G) -> 1471 langtag_variant(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E), ?LC(F), ?LC(G) >>); 1472 langtag_variant(<< $-, A, B, C, D, E, F, R/bits >>, Acc, T) 1473 when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D), 1474 ?IS_ALPHANUM(E), ?IS_ALPHANUM(F) -> 1475 langtag_variant(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E), ?LC(F) >>); 1476 langtag_variant(<< $-, A, B, C, D, E, R/bits >>, Acc, T) 1477 when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D), ?IS_ALPHANUM(E) -> 1478 langtag_variant(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E) >>); 1479 langtag_variant(<< $-, A, B, C, D, R/bits >>, Acc, T) 1480 when ?IS_DIGIT(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D) -> 1481 langtag_variant(R, Acc, << T/binary, $-, A, ?LC(B), ?LC(C), ?LC(D) >>); 1482 langtag_variant(R, Acc, T) -> 1483 langtag_extension(R, Acc, T). 1484 1485 langtag_extension(<< $-, X, R/bits >>, Acc, T) when X =:= $x; X =:= $X -> langtag_privateuse_sub(R, Acc, << T/binary, $-, $x >>, 0); 1486 langtag_extension(<< $-, S, R/bits >>, Acc, T) when ?IS_ALPHANUM(S) -> langtag_extension_sub(R, Acc, << T/binary, $-, ?LC(S) >>, 0); 1487 langtag_extension(R, Acc, T) -> langtag_list_sep(R, [T|Acc]). 1488 1489 langtag_extension_sub(<< $-, A, B, C, D, E, F, G, H, R/bits >>, Acc, T, N) 1490 when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D), 1491 ?IS_ALPHANUM(E), ?IS_ALPHANUM(F), ?IS_ALPHANUM(G), ?IS_ALPHANUM(H) -> 1492 langtag_extension_sub(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E), ?LC(F), ?LC(G), ?LC(H) >>, N + 1); 1493 langtag_extension_sub(<< $-, A, B, C, D, E, F, G, R/bits >>, Acc, T, N) 1494 when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D), 1495 ?IS_ALPHANUM(E), ?IS_ALPHANUM(F), ?IS_ALPHANUM(G) -> 1496 langtag_extension_sub(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E), ?LC(F), ?LC(G) >>, N + 1); 1497 langtag_extension_sub(<< $-, A, B, C, D, E, F, R/bits >>, Acc, T, N) 1498 when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D), 1499 ?IS_ALPHANUM(E), ?IS_ALPHANUM(F) -> 1500 langtag_extension_sub(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E), ?LC(F) >>, N + 1); 1501 langtag_extension_sub(<< $-, A, B, C, D, E, R/bits >>, Acc, T, N) 1502 when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D), ?IS_ALPHANUM(E) -> 1503 langtag_extension_sub(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E) >>, N + 1); 1504 langtag_extension_sub(<< $-, A, B, C, D, R/bits >>, Acc, T, N) 1505 when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D) -> 1506 langtag_extension_sub(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D) >>, N + 1); 1507 langtag_extension_sub(<< $-, A, B, C, R/bits >>, Acc, T, N) 1508 when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C) -> 1509 langtag_extension_sub(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C) >>, N + 1); 1510 langtag_extension_sub(<< $-, A, B, R/bits >>, Acc, T, N) 1511 when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B) -> 1512 langtag_extension_sub(R, Acc, << T/binary, $-, ?LC(A), ?LC(B) >>, N + 1); 1513 langtag_extension_sub(R, Acc, T, N) when N > 0 -> 1514 langtag_extension(R, Acc, T). 1515 1516 langtag_privateuse_sub(<< $-, A, B, C, D, E, F, G, H, R/bits >>, Acc, T, N) 1517 when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D), 1518 ?IS_ALPHANUM(E), ?IS_ALPHANUM(F), ?IS_ALPHANUM(G), ?IS_ALPHANUM(H) -> 1519 langtag_privateuse_sub(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E), ?LC(F), ?LC(G), ?LC(H) >>, N + 1); 1520 langtag_privateuse_sub(<< $-, A, B, C, D, E, F, G, R/bits >>, Acc, T, N) 1521 when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D), 1522 ?IS_ALPHANUM(E), ?IS_ALPHANUM(F), ?IS_ALPHANUM(G) -> 1523 langtag_privateuse_sub(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E), ?LC(F), ?LC(G) >>, N + 1); 1524 langtag_privateuse_sub(<< $-, A, B, C, D, E, F, R/bits >>, Acc, T, N) 1525 when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D), 1526 ?IS_ALPHANUM(E), ?IS_ALPHANUM(F) -> 1527 langtag_privateuse_sub(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E), ?LC(F) >>, N + 1); 1528 langtag_privateuse_sub(<< $-, A, B, C, D, E, R/bits >>, Acc, T, N) 1529 when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D), ?IS_ALPHANUM(E) -> 1530 langtag_privateuse_sub(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E) >>, N + 1); 1531 langtag_privateuse_sub(<< $-, A, B, C, D, R/bits >>, Acc, T, N) 1532 when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D) -> 1533 langtag_privateuse_sub(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D) >>, N + 1); 1534 langtag_privateuse_sub(<< $-, A, B, C, R/bits >>, Acc, T, N) 1535 when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C) -> 1536 langtag_privateuse_sub(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C) >>, N + 1); 1537 langtag_privateuse_sub(<< $-, A, B, R/bits >>, Acc, T, N) 1538 when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B) -> 1539 langtag_privateuse_sub(R, Acc, << T/binary, $-, ?LC(A), ?LC(B) >>, N + 1); 1540 langtag_privateuse_sub(<< $-, A, R/bits >>, Acc, T, N) 1541 when ?IS_ALPHANUM(A) -> 1542 langtag_privateuse_sub(R, Acc, << T/binary, $-, ?LC(A) >>, N + 1); 1543 langtag_privateuse_sub(R, Acc, T, N) when N > 0 -> langtag_list_sep(R, [T|Acc]). 1544 1545 langtag_list_sep(<<>>, Acc) -> lists:reverse(Acc); 1546 langtag_list_sep(<< $,, R/bits >>, Acc) -> langtag_list(R, Acc); 1547 langtag_list_sep(<< C, R/bits >>, Acc) when ?IS_WS(C) -> langtag_list_sep(R, Acc). 1548 1549 -ifdef(TEST). 1550 langtag_language() -> vector(2, 3, alpha()). 1551 langtag_extlang() -> vector(0, 3, [$-, alpha(), alpha(), alpha()]). 1552 langtag_script() -> oneof([[], [$-, alpha(), alpha(), alpha(), alpha()]]). 1553 langtag_region() -> oneof([[], [$-, alpha(), alpha()], [$-, digit(), digit(), digit()]]). 1554 1555 langtag_variant() -> 1556 small_list(frequency([ 1557 {4, [$-, vector(5, 8, alphanum())]}, 1558 {1, [$-, digit(), alphanum(), alphanum(), alphanum()]} 1559 ])). 1560 1561 langtag_extension() -> 1562 small_list([$-, ?SUCHTHAT(S, alphanum(), S =/= $x andalso S =/= $X), 1563 small_non_empty_list([$-, vector(2, 8, alphanum())]) 1564 ]). 1565 1566 langtag_privateuse() -> oneof([[], [$-, langtag_privateuse_nodash()]]). 1567 langtag_privateuse_nodash() -> [elements([$x, $X]), small_non_empty_list([$-, vector(1, 8, alphanum())])]. 1568 private_language_tag() -> ?LET(T, langtag_privateuse_nodash(), iolist_to_binary(T)). 1569 1570 language_tag() -> 1571 ?LET(IoList, 1572 [langtag_language(), langtag_extlang(), langtag_script(), langtag_region(), 1573 langtag_variant(), langtag_extension(), langtag_privateuse()], 1574 iolist_to_binary(IoList)). 1575 1576 content_language() -> 1577 ?LET(L, 1578 non_empty(list(frequency([ 1579 {90, language_tag()}, 1580 {10, private_language_tag()} 1581 ]))), 1582 begin 1583 << _, ContentLanguage/binary >> = iolist_to_binary([[$,, T] || T <- L]), 1584 {L, ContentLanguage} 1585 end). 1586 1587 prop_parse_content_language() -> 1588 ?FORALL({L, ContentLanguage}, 1589 content_language(), 1590 begin 1591 ResL = parse_content_language(ContentLanguage), 1592 CheckedL = [?LOWER(T) =:= ResT || {T, ResT} <- lists:zip(L, ResL)], 1593 [true] =:= lists:usort(CheckedL) 1594 end). 1595 1596 parse_content_language_test_() -> 1597 Tests = [ 1598 {<<"de">>, [<<"de">>]}, 1599 {<<"fr">>, [<<"fr">>]}, 1600 {<<"ja">>, [<<"ja">>]}, 1601 {<<"zh-Hant">>, [<<"zh-hant">>]}, 1602 {<<"zh-Hans">>, [<<"zh-hans">>]}, 1603 {<<"sr-Cyrl">>, [<<"sr-cyrl">>]}, 1604 {<<"sr-Latn">>, [<<"sr-latn">>]}, 1605 {<<"zh-cmn-Hans-CN">>, [<<"zh-cmn-hans-cn">>]}, 1606 {<<"cmn-Hans-CN">>, [<<"cmn-hans-cn">>]}, 1607 {<<"zh-yue-HK">>, [<<"zh-yue-hk">>]}, 1608 {<<"yue-HK">>, [<<"yue-hk">>]}, 1609 {<<"zh-Hans-CN">>, [<<"zh-hans-cn">>]}, 1610 {<<"sr-Latn-RS">>, [<<"sr-latn-rs">>]}, 1611 {<<"sl-rozaj">>, [<<"sl-rozaj">>]}, 1612 {<<"sl-rozaj-biske">>, [<<"sl-rozaj-biske">>]}, 1613 {<<"sl-nedis">>, [<<"sl-nedis">>]}, 1614 {<<"de-CH-1901">>, [<<"de-ch-1901">>]}, 1615 {<<"sl-IT-nedis">>, [<<"sl-it-nedis">>]}, 1616 {<<"hy-Latn-IT-arevela">>, [<<"hy-latn-it-arevela">>]}, 1617 {<<"de-DE">>, [<<"de-de">>]}, 1618 {<<"en-US">>, [<<"en-us">>]}, 1619 {<<"es-419">>, [<<"es-419">>]}, 1620 {<<"de-CH-x-phonebk">>, [<<"de-ch-x-phonebk">>]}, 1621 {<<"az-Arab-x-AZE-derbend">>, [<<"az-arab-x-aze-derbend">>]}, 1622 {<<"x-whatever">>, [<<"x-whatever">>]}, 1623 {<<"qaa-Qaaa-QM-x-southern">>, [<<"qaa-qaaa-qm-x-southern">>]}, 1624 {<<"de-Qaaa">>, [<<"de-qaaa">>]}, 1625 {<<"sr-Latn-QM">>, [<<"sr-latn-qm">>]}, 1626 {<<"sr-Qaaa-RS">>, [<<"sr-qaaa-rs">>]}, 1627 {<<"en-US-u-islamcal">>, [<<"en-us-u-islamcal">>]}, 1628 {<<"zh-CN-a-myext-x-private">>, [<<"zh-cn-a-myext-x-private">>]}, 1629 {<<"en-a-myext-b-another">>, [<<"en-a-myext-b-another">>]}, 1630 {<<"mn-Cyrl-MN">>, [<<"mn-cyrl-mn">>]}, 1631 {<<"MN-cYRL-mn">>, [<<"mn-cyrl-mn">>]}, 1632 {<<"mN-cYrL-Mn">>, [<<"mn-cyrl-mn">>]}, 1633 {<<"az-Arab-IR">>, [<<"az-arab-ir">>]}, 1634 {<<"zh-gan">>, [<<"zh-gan">>]}, 1635 {<<"zh-yue">>, [<<"zh-yue">>]}, 1636 {<<"zh-cmn">>, [<<"zh-cmn">>]}, 1637 {<<"de-AT">>, [<<"de-at">>]}, 1638 {<<"de-CH-1996">>, [<<"de-ch-1996">>]}, 1639 {<<"en-Latn-GB-boont-r-extended-sequence-x-private">>, 1640 [<<"en-latn-gb-boont-r-extended-sequence-x-private">>]}, 1641 {<<"el-x-koine">>, [<<"el-x-koine">>]}, 1642 {<<"el-x-attic">>, [<<"el-x-attic">>]}, 1643 {<<"fr, en-US, es-419, az-Arab, x-pig-latin, man-Nkoo-GN">>, 1644 [<<"fr">>, <<"en-us">>, <<"es-419">>, <<"az-arab">>, <<"x-pig-latin">>, <<"man-nkoo-gn">>]}, 1645 {<<"da">>, [<<"da">>]}, 1646 {<<"mi, en">>, [<<"mi">>, <<"en">>]} 1647 ], 1648 [{V, fun() -> R = parse_content_language(V) end} || {V, R} <- Tests]. 1649 1650 parse_content_language_error_test_() -> 1651 Tests = [ 1652 <<>> 1653 ], 1654 [{V, fun() -> {'EXIT', _} = (catch parse_content_language(V)) end} || V <- Tests]. 1655 1656 horse_parse_content_language() -> 1657 horse:repeat(100000, 1658 parse_content_language(<<"fr, en-US, es-419, az-Arab, x-pig-latin, man-Nkoo-GN">>) 1659 ). 1660 -endif. 1661 1662 %% Content-Length header. 1663 1664 -spec parse_content_length(binary()) -> non_neg_integer(). 1665 parse_content_length(ContentLength) -> 1666 I = binary_to_integer(ContentLength), 1667 true = I >= 0, 1668 I. 1669 1670 -ifdef(TEST). 1671 prop_parse_content_length() -> 1672 ?FORALL( 1673 X, 1674 non_neg_integer(), 1675 X =:= parse_content_length(integer_to_binary(X)) 1676 ). 1677 1678 parse_content_length_test_() -> 1679 Tests = [ 1680 {<<"0">>, 0}, 1681 {<<"42">>, 42}, 1682 {<<"69">>, 69}, 1683 {<<"1337">>, 1337}, 1684 {<<"3495">>, 3495}, 1685 {<<"1234567890">>, 1234567890} 1686 ], 1687 [{V, fun() -> R = parse_content_length(V) end} || {V, R} <- Tests]. 1688 1689 parse_content_length_error_test_() -> 1690 Tests = [ 1691 <<>>, 1692 <<"-1">>, 1693 <<"123, 123">>, 1694 <<"4.17">> 1695 ], 1696 [{V, fun() -> {'EXIT', _} = (catch parse_content_length(V)) end} || V <- Tests]. 1697 1698 horse_parse_content_length_zero() -> 1699 horse:repeat(100000, 1700 parse_content_length(<<"0">>) 1701 ). 1702 1703 horse_parse_content_length_giga() -> 1704 horse:repeat(100000, 1705 parse_content_length(<<"1234567890">>) 1706 ). 1707 -endif. 1708 1709 %% Content-Range header. 1710 1711 -spec parse_content_range(binary()) 1712 -> {bytes, non_neg_integer(), non_neg_integer(), non_neg_integer() | '*'} 1713 | {bytes, '*', non_neg_integer()} | {binary(), binary()}. 1714 parse_content_range(<<"bytes */", C, R/bits >>) when ?IS_DIGIT(C) -> unsatisfied_range(R, C - $0); 1715 parse_content_range(<<"bytes ", C, R/bits >>) when ?IS_DIGIT(C) -> byte_range_first(R, C - $0); 1716 parse_content_range(<< C, R/bits >>) when ?IS_TOKEN(C) -> 1717 ?LOWER(other_content_range_unit, R, <<>>). 1718 1719 byte_range_first(<< $-, C, R/bits >>, First) when ?IS_DIGIT(C) -> byte_range_last(R, First, C - $0); 1720 byte_range_first(<< C, R/bits >>, First) when ?IS_DIGIT(C) -> byte_range_first(R, First * 10 + C - $0). 1721 1722 byte_range_last(<<"/*">>, First, Last) -> {bytes, First, Last, '*'}; 1723 byte_range_last(<< $/, C, R/bits >>, First, Last) when ?IS_DIGIT(C) -> byte_range_complete(R, First, Last, C - $0); 1724 byte_range_last(<< C, R/bits >>, First, Last) when ?IS_DIGIT(C) -> byte_range_last(R, First, Last * 10 + C - $0). 1725 1726 byte_range_complete(<<>>, First, Last, Complete) -> {bytes, First, Last, Complete}; 1727 byte_range_complete(<< C, R/bits >>, First, Last, Complete) when ?IS_DIGIT(C) -> 1728 byte_range_complete(R, First, Last, Complete * 10 + C - $0). 1729 1730 unsatisfied_range(<<>>, Complete) -> {bytes, '*', Complete}; 1731 unsatisfied_range(<< C, R/bits >>, Complete) when ?IS_DIGIT(C) -> unsatisfied_range(R, Complete * 10 + C - $0). 1732 1733 other_content_range_unit(<< $\s, R/bits >>, Unit) -> other_content_range_resp(R, Unit, <<>>); 1734 other_content_range_unit(<< C, R/bits >>, Unit) when ?IS_TOKEN(C) -> 1735 ?LOWER(other_content_range_unit, R, Unit). 1736 1737 other_content_range_resp(<<>>, Unit, Resp) -> {Unit, Resp}; 1738 other_content_range_resp(<< C, R/bits >>, Unit, Resp) when ?IS_CHAR(C) -> other_content_range_resp(R, Unit, << Resp/binary, C >>). 1739 1740 -ifdef(TEST). 1741 content_range() -> 1742 ?LET(ContentRange, 1743 oneof([ 1744 ?SUCHTHAT({bytes, First, Last, Complete}, 1745 {bytes, non_neg_integer(), non_neg_integer(), non_neg_integer()}, 1746 First =< Last andalso Last < Complete), 1747 ?SUCHTHAT({bytes, First, Last, '*'}, 1748 {bytes, non_neg_integer(), non_neg_integer(), '*'}, 1749 First =< Last), 1750 {bytes, '*', non_neg_integer()}, 1751 {token(), ?LET(L, list(abnf_char()), list_to_binary(L))} 1752 ]), 1753 {case ContentRange of 1754 {Unit, Resp} when is_binary(Unit) -> {?LOWER(Unit), Resp}; 1755 _ -> ContentRange 1756 end, case ContentRange of 1757 {bytes, First, Last, '*'} -> 1758 << "bytes ", (integer_to_binary(First))/binary, "-", 1759 (integer_to_binary(Last))/binary, "/*">>; 1760 {bytes, First, Last, Complete} -> 1761 << "bytes ", (integer_to_binary(First))/binary, "-", 1762 (integer_to_binary(Last))/binary, "/", (integer_to_binary(Complete))/binary >>; 1763 {bytes, '*', Complete} -> 1764 << "bytes */", (integer_to_binary(Complete))/binary >>; 1765 {Unit, Resp} -> 1766 << Unit/binary, $\s, Resp/binary >> 1767 end}). 1768 1769 prop_parse_content_range() -> 1770 ?FORALL({Res, ContentRange}, 1771 content_range(), 1772 Res =:= parse_content_range(ContentRange)). 1773 1774 parse_content_range_test_() -> 1775 Tests = [ 1776 {<<"bytes 21010-47021/47022">>, {bytes, 21010, 47021, 47022}}, 1777 {<<"bytes 500-999/8000">>, {bytes, 500, 999, 8000}}, 1778 {<<"bytes 7000-7999/8000">>, {bytes, 7000, 7999, 8000}}, 1779 {<<"bytes 42-1233/1234">>, {bytes, 42, 1233, 1234}}, 1780 {<<"bytes 42-1233/*">>, {bytes, 42, 1233, '*'}}, 1781 {<<"bytes */1234">>, {bytes, '*', 1234}}, 1782 {<<"bytes 0-499/1234">>, {bytes, 0, 499, 1234}}, 1783 {<<"bytes 500-999/1234">>, {bytes, 500, 999, 1234}}, 1784 {<<"bytes 500-1233/1234">>, {bytes, 500, 1233, 1234}}, 1785 {<<"bytes 734-1233/1234">>, {bytes, 734, 1233, 1234}}, 1786 {<<"bytes */47022">>, {bytes, '*', 47022}}, 1787 {<<"exampleunit 1.2-4.3/25">>, {<<"exampleunit">>, <<"1.2-4.3/25">>}}, 1788 {<<"exampleunit 11.2-14.3/25">>, {<<"exampleunit">>, <<"11.2-14.3/25">>}} 1789 ], 1790 [{V, fun() -> R = parse_content_range(V) end} || {V, R} <- Tests]. 1791 1792 parse_content_range_error_test_() -> 1793 Tests = [ 1794 <<>> 1795 ], 1796 [{V, fun() -> {'EXIT', _} = (catch parse_content_range(V)) end} || V <- Tests]. 1797 1798 horse_parse_content_range_bytes() -> 1799 horse:repeat(200000, 1800 parse_content_range(<<"bytes 21010-47021/47022">>) 1801 ). 1802 1803 horse_parse_content_range_other() -> 1804 horse:repeat(200000, 1805 parse_content_range(<<"exampleunit 11.2-14.3/25">>) 1806 ). 1807 -endif. 1808 1809 %% Content-Type header. 1810 1811 -spec parse_content_type(binary()) -> media_type(). 1812 parse_content_type(<< C, R/bits >>) when ?IS_TOKEN(C) -> 1813 ?LOWER(media_type, R, <<>>). 1814 1815 media_type(<< $/, C, R/bits >>, T) when ?IS_TOKEN(C) -> 1816 ?LOWER(media_subtype, R, T, <<>>); 1817 media_type(<< C, R/bits >>, T) when ?IS_TOKEN(C) -> 1818 ?LOWER(media_type, R, T). 1819 1820 media_subtype(<< C, R/bits >>, T, S) when ?IS_TOKEN(C) -> 1821 ?LOWER(media_subtype, R, T, S); 1822 media_subtype(R, T, S) -> media_param_sep(R, T, S, []). 1823 1824 media_param_sep(<<>>, T, S, P) -> {T, S, lists:reverse(P)}; 1825 media_param_sep(<< $;, R/bits >>, T, S, P) -> media_before_param(R, T, S, P); 1826 media_param_sep(<< C, R/bits >>, T, S, P) when ?IS_WS(C) -> media_param_sep(R, T, S, P). 1827 1828 media_before_param(<< C, R/bits >>, T, S, P) when ?IS_WS(C)-> media_before_param(R, T, S, P); 1829 media_before_param(<< "charset=", $", R/bits >>, T, S, P) -> media_charset_quoted(R, T, S, P, <<>>); 1830 media_before_param(<< "charset=", R/bits >>, T, S, P) -> media_charset(R, T, S, P, <<>>); 1831 media_before_param(<< C, R/bits >>, T, S, P) when ?IS_TOKEN(C) -> 1832 ?LOWER(media_param, R, T, S, P, <<>>). 1833 1834 media_charset_quoted(<< $", R/bits >>, T, S, P, V) -> 1835 media_param_sep(R, T, S, [{<<"charset">>, V}|P]); 1836 media_charset_quoted(<< $\\, C, R/bits >>, T, S, P, V) when ?IS_VCHAR_OBS(C) -> 1837 ?LOWER(media_charset_quoted, R, T, S, P, V); 1838 media_charset_quoted(<< C, R/bits >>, T, S, P, V) when ?IS_VCHAR_OBS(C) -> 1839 ?LOWER(media_charset_quoted, R, T, S, P, V). 1840 1841 media_charset(<< C, R/bits >>, T, S, P, V) when ?IS_TOKEN(C) -> 1842 ?LOWER(media_charset, R, T, S, P, V); 1843 media_charset(R, T, S, P, V) -> media_param_sep(R, T, S, [{<<"charset">>, V}|P]). 1844 1845 media_param(<< $=, $", R/bits >>, T, S, P, K) -> media_quoted(R, T, S, P, K, <<>>); 1846 media_param(<< $=, C, R/bits >>, T, S, P, K) when ?IS_TOKEN(C) -> media_value(R, T, S, P, K, << C >>); 1847 media_param(<< C, R/bits >>, T, S, P, K) when ?IS_TOKEN(C) -> 1848 ?LOWER(media_param, R, T, S, P, K). 1849 1850 media_quoted(<< $", R/bits >>, T, S, P, K, V) -> media_param_sep(R, T, S, [{K, V}|P]); 1851 media_quoted(<< $\\, C, R/bits >>, T, S, P, K, V) when ?IS_VCHAR_OBS(C) -> media_quoted(R, T, S, P, K, << V/binary, C >>); 1852 media_quoted(<< C, R/bits >>, T, S, P, K, V) when ?IS_VCHAR_OBS(C) -> media_quoted(R, T, S, P, K, << V/binary, C >>). 1853 1854 media_value(<< C, R/bits >>, T, S, P, K, V) when ?IS_TOKEN(C) -> media_value(R, T, S, P, K, << V/binary, C >>); 1855 media_value(R, T, S, P, K, V) -> media_param_sep(R, T, S, [{K, V}|P]). 1856 1857 -ifdef(TEST). 1858 media_type_parameter() -> 1859 frequency([ 1860 {90, parameter()}, 1861 {10, {<<"charset">>, oneof([token(), quoted_string()]), <<>>, <<>>}} 1862 ]). 1863 1864 media_type() -> 1865 ?LET({T, S, P}, 1866 {token(), token(), small_list(media_type_parameter())}, 1867 {T, S, P, iolist_to_binary([T, $/, S, [[OWS1, $;, OWS2, K, $=, V] || {K, V, OWS1, OWS2} <- P]])} 1868 ). 1869 1870 prop_parse_content_type() -> 1871 ?FORALL({T, S, P, MediaType}, 1872 media_type(), 1873 begin 1874 {ResT, ResS, ResP} = parse_content_type(MediaType), 1875 ExpectedP = [case ?LOWER(K) of 1876 <<"charset">> -> {<<"charset">>, ?LOWER(unquote(V))}; 1877 LowK -> {LowK, unquote(V)} 1878 end || {K, V, _, _} <- P], 1879 ResT =:= ?LOWER(T) 1880 andalso ResS =:= ?LOWER(S) 1881 andalso ResP =:= ExpectedP 1882 end 1883 ). 1884 1885 parse_content_type_test_() -> 1886 Tests = [ 1887 {<<"text/html;charset=utf-8">>, 1888 {<<"text">>, <<"html">>, [{<<"charset">>, <<"utf-8">>}]}}, 1889 {<<"text/html;charset=UTF-8">>, 1890 {<<"text">>, <<"html">>, [{<<"charset">>, <<"utf-8">>}]}}, 1891 {<<"Text/HTML;Charset=\"utf-8\"">>, 1892 {<<"text">>, <<"html">>, [{<<"charset">>, <<"utf-8">>}]}}, 1893 {<<"text/html; charset=\"utf-8\"">>, 1894 {<<"text">>, <<"html">>, [{<<"charset">>, <<"utf-8">>}]}}, 1895 {<<"text/html; charset=ISO-8859-4">>, 1896 {<<"text">>, <<"html">>, [{<<"charset">>, <<"iso-8859-4">>}]}}, 1897 {<<"text/plain; charset=iso-8859-4">>, 1898 {<<"text">>, <<"plain">>, [{<<"charset">>, <<"iso-8859-4">>}]}}, 1899 {<<"multipart/form-data \t;Boundary=\"MultipartIsUgly\"">>, 1900 {<<"multipart">>, <<"form-data">>, [ 1901 {<<"boundary">>, <<"MultipartIsUgly">>} 1902 ]}}, 1903 {<<"foo/bar; one=FirstParam; two=SecondParam">>, 1904 {<<"foo">>, <<"bar">>, [ 1905 {<<"one">>, <<"FirstParam">>}, 1906 {<<"two">>, <<"SecondParam">>} 1907 ]}} 1908 ], 1909 [{V, fun() -> R = parse_content_type(V) end} || {V, R} <- Tests]. 1910 1911 horse_parse_content_type() -> 1912 horse:repeat(200000, 1913 parse_content_type(<<"text/html;charset=utf-8">>) 1914 ). 1915 -endif. 1916 1917 %% Cookie header. 1918 1919 -spec parse_cookie(binary()) -> [{binary(), binary()}]. 1920 parse_cookie(Cookie) -> 1921 cow_cookie:parse_cookie(Cookie). 1922 1923 %% Date header. 1924 1925 -spec parse_date(binary()) -> calendar:datetime(). 1926 parse_date(Date) -> 1927 cow_date:parse_date(Date). 1928 1929 -ifdef(TEST). 1930 parse_date_test_() -> 1931 Tests = [ 1932 {<<"Tue, 15 Nov 1994 08:12:31 GMT">>, {{1994, 11, 15}, {8, 12, 31}}} 1933 ], 1934 [{V, fun() -> R = parse_date(V) end} || {V, R} <- Tests]. 1935 -endif. 1936 1937 %% ETag header. 1938 1939 -spec parse_etag(binary()) -> etag(). 1940 parse_etag(<< $W, $/, $", R/bits >>) -> 1941 etag(R, weak, <<>>); 1942 parse_etag(<< $", R/bits >>) -> 1943 etag(R, strong, <<>>). 1944 1945 etag(<< $" >>, Strength, Tag) -> 1946 {Strength, Tag}; 1947 etag(<< C, R/bits >>, Strength, Tag) when ?IS_ETAGC(C) -> 1948 etag(R, Strength, << Tag/binary, C >>). 1949 1950 -ifdef(TEST). 1951 etagc() -> 1952 ?SUCHTHAT(C, integer(16#21, 16#ff), C =/= 16#22 andalso C =/= 16#7f). 1953 1954 etag() -> 1955 ?LET({Strength, Tag}, 1956 {elements([weak, strong]), list(etagc())}, 1957 begin 1958 TagBin = list_to_binary(Tag), 1959 {{Strength, TagBin}, 1960 case Strength of 1961 weak -> << $W, $/, $", TagBin/binary, $" >>; 1962 strong -> << $", TagBin/binary, $" >> 1963 end} 1964 end). 1965 1966 prop_parse_etag() -> 1967 ?FORALL({Tag, TagBin}, 1968 etag(), 1969 Tag =:= parse_etag(TagBin)). 1970 1971 parse_etag_test_() -> 1972 Tests = [ 1973 {<<"\"xyzzy\"">>, {strong, <<"xyzzy">>}}, 1974 {<<"W/\"xyzzy\"">>, {weak, <<"xyzzy">>}}, 1975 {<<"\"\"">>, {strong, <<>>}} 1976 ], 1977 [{V, fun() -> R = parse_etag(V) end} || {V, R} <- Tests]. 1978 1979 parse_etag_error_test_() -> 1980 Tests = [ 1981 <<>>, 1982 <<"\"">>, 1983 <<"W">>, 1984 <<"W/">> 1985 ], 1986 [{V, fun() -> {'EXIT', _} = (catch parse_etag(V)) end} || V <- Tests]. 1987 1988 horse_parse_etag() -> 1989 horse:repeat(200000, 1990 parse_etag(<<"W/\"xyzzy\"">>) 1991 ). 1992 -endif. 1993 1994 %% Expect header. 1995 1996 -spec parse_expect(binary()) -> continue. 1997 parse_expect(<<"100-continue">>) -> 1998 continue; 1999 parse_expect(<<"100-", C, O, N, T, I, M, U, E >>) 2000 when (C =:= $C) or (C =:= $c), (O =:= $O) or (O =:= $o), 2001 (N =:= $N) or (N =:= $n), (T =:= $T) or (T =:= $t), 2002 (I =:= $I) or (I =:= $i), (M =:= $N) or (M =:= $n), 2003 (U =:= $U) or (U =:= $u), (E =:= $E) or (E =:= $e) -> 2004 continue. 2005 2006 -ifdef(TEST). 2007 expect() -> 2008 ?LET(E, 2009 [$1, $0, $0, $-, 2010 elements([$c, $C]), elements([$o, $O]), elements([$n, $N]), 2011 elements([$t, $T]), elements([$i, $I]), elements([$n, $N]), 2012 elements([$u, $U]), elements([$e, $E])], 2013 list_to_binary(E)). 2014 2015 prop_parse_expect() -> 2016 ?FORALL(E, expect(), continue =:= parse_expect(E)). 2017 2018 parse_expect_test_() -> 2019 Tests = [ 2020 <<"100-continue">>, 2021 <<"100-CONTINUE">>, 2022 <<"100-Continue">>, 2023 <<"100-CoNtInUe">> 2024 ], 2025 [{V, fun() -> continue = parse_expect(V) end} || V <- Tests]. 2026 2027 parse_expect_error_test_() -> 2028 Tests = [ 2029 <<>>, 2030 <<" ">>, 2031 <<"200-OK">>, 2032 <<"Cookies">> 2033 ], 2034 [{V, fun() -> {'EXIT', _} = (catch parse_expect(V)) end} || V <- Tests]. 2035 2036 horse_parse_expect() -> 2037 horse:repeat(200000, 2038 parse_expect(<<"100-continue">>) 2039 ). 2040 -endif. 2041 2042 %% Expires header. 2043 %% 2044 %% Recipients must interpret invalid date formats as a date 2045 %% in the past. The value "0" is commonly used. 2046 2047 -spec parse_expires(binary()) -> calendar:datetime(). 2048 parse_expires(<<"0">>) -> 2049 {{1, 1, 1}, {0, 0, 0}}; 2050 parse_expires(Expires) -> 2051 try 2052 cow_date:parse_date(Expires) 2053 catch _:_ -> 2054 {{1, 1, 1}, {0, 0, 0}} 2055 end. 2056 2057 -ifdef(TEST). 2058 parse_expires_test_() -> 2059 Tests = [ 2060 {<<"0">>, {{1, 1, 1}, {0, 0, 0}}}, 2061 {<<"Thu, 01 Dec 1994 nope invalid">>, {{1, 1, 1}, {0, 0, 0}}}, 2062 {<<"Thu, 01 Dec 1994 16:00:00 GMT">>, {{1994, 12, 1}, {16, 0, 0}}} 2063 ], 2064 [{V, fun() -> R = parse_expires(V) end} || {V, R} <- Tests]. 2065 2066 horse_parse_expires_0() -> 2067 horse:repeat(200000, 2068 parse_expires(<<"0">>) 2069 ). 2070 2071 horse_parse_expires_invalid() -> 2072 horse:repeat(200000, 2073 parse_expires(<<"Thu, 01 Dec 1994 nope invalid">>) 2074 ). 2075 -endif. 2076 2077 %% Host header. 2078 %% 2079 %% We only seek to have legal characters and separate the 2080 %% host and port values. The number of segments in the host 2081 %% or the size of each segment is not checked. 2082 %% 2083 %% There is no way to distinguish IPv4 addresses from regular 2084 %% names until the last segment is reached therefore we do not 2085 %% differentiate them. 2086 %% 2087 %% The following valid hosts are currently rejected: IPv6 2088 %% addresses with a zone identifier; IPvFuture addresses; 2089 %% and percent-encoded addresses. 2090 2091 -spec parse_host(binary()) -> {binary(), 0..65535 | undefined}. 2092 parse_host(<< $[, R/bits >>) -> 2093 ipv6_address(R, << $[ >>); 2094 parse_host(Host) -> 2095 reg_name(Host, <<>>). 2096 2097 ipv6_address(<< $] >>, IP) -> {<< IP/binary, $] >>, undefined}; 2098 ipv6_address(<< $], $:, Port/bits >>, IP) -> {<< IP/binary, $] >>, binary_to_integer(Port)}; 2099 ipv6_address(<< C, R/bits >>, IP) when ?IS_HEX(C) or (C =:= $:) or (C =:= $.) -> 2100 ?LOWER(ipv6_address, R, IP). 2101 2102 reg_name(<<>>, Name) -> {Name, undefined}; 2103 reg_name(<< $:, Port/bits >>, Name) -> {Name, binary_to_integer(Port)}; 2104 reg_name(<< C, R/bits >>, Name) when ?IS_URI_UNRESERVED(C) or ?IS_URI_SUB_DELIMS(C) -> 2105 ?LOWER(reg_name, R, Name). 2106 2107 -ifdef(TEST). 2108 host_chars() -> "!$&'()*+,-.0123456789;=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~". 2109 host() -> vector(1, 255, elements(host_chars())). 2110 2111 host_port() -> 2112 ?LET({Host, Port}, 2113 {host(), oneof([undefined, integer(1, 65535)])}, 2114 begin 2115 HostBin = list_to_binary(Host), 2116 {{?LOWER(HostBin), Port}, 2117 case Port of 2118 undefined -> HostBin; 2119 _ -> << HostBin/binary, $:, (integer_to_binary(Port))/binary >> 2120 end} 2121 end). 2122 2123 prop_parse_host() -> 2124 ?FORALL({Res, Host}, host_port(), Res =:= parse_host(Host)). 2125 2126 parse_host_test_() -> 2127 Tests = [ 2128 {<<>>, {<<>>, undefined}}, 2129 {<<"www.example.org:8080">>, {<<"www.example.org">>, 8080}}, 2130 {<<"www.example.org">>, {<<"www.example.org">>, undefined}}, 2131 {<<"192.0.2.1:8080">>, {<<"192.0.2.1">>, 8080}}, 2132 {<<"192.0.2.1">>, {<<"192.0.2.1">>, undefined}}, 2133 {<<"[2001:db8::1]:8080">>, {<<"[2001:db8::1]">>, 8080}}, 2134 {<<"[2001:db8::1]">>, {<<"[2001:db8::1]">>, undefined}}, 2135 {<<"[::ffff:192.0.2.1]:8080">>, {<<"[::ffff:192.0.2.1]">>, 8080}}, 2136 {<<"[::ffff:192.0.2.1]">>, {<<"[::ffff:192.0.2.1]">>, undefined}} 2137 ], 2138 [{V, fun() -> R = parse_host(V) end} || {V, R} <- Tests]. 2139 2140 horse_parse_host_blue_example_org() -> 2141 horse:repeat(200000, 2142 parse_host(<<"blue.example.org:8080">>) 2143 ). 2144 2145 horse_parse_host_ipv4() -> 2146 horse:repeat(200000, 2147 parse_host(<<"192.0.2.1:8080">>) 2148 ). 2149 2150 horse_parse_host_ipv6() -> 2151 horse:repeat(200000, 2152 parse_host(<<"[2001:db8::1]:8080">>) 2153 ). 2154 2155 horse_parse_host_ipv6_v4() -> 2156 horse:repeat(200000, 2157 parse_host(<<"[::ffff:192.0.2.1]:8080">>) 2158 ). 2159 -endif. 2160 2161 %% HTTP2-Settings header. 2162 2163 -spec parse_http2_settings(binary()) -> map(). 2164 parse_http2_settings(HTTP2Settings) -> 2165 cow_http2:parse_settings_payload(base64:decode(HTTP2Settings)). 2166 2167 %% If-Match header. 2168 2169 -spec parse_if_match(binary()) -> '*' | [etag()]. 2170 parse_if_match(<<"*">>) -> 2171 '*'; 2172 parse_if_match(IfMatch) -> 2173 nonempty(etag_list(IfMatch, [])). 2174 2175 etag_list(<<>>, Acc) -> lists:reverse(Acc); 2176 etag_list(<< C, R/bits >>, Acc) when ?IS_WS_COMMA(C) -> etag_list(R, Acc); 2177 etag_list(<< $W, $/, $", R/bits >>, Acc) -> etag(R, Acc, weak, <<>>); 2178 etag_list(<< $", R/bits >>, Acc) -> etag(R, Acc, strong, <<>>). 2179 2180 etag(<< $", R/bits >>, Acc, Strength, Tag) -> etag_list_sep(R, [{Strength, Tag}|Acc]); 2181 etag(<< C, R/bits >>, Acc, Strength, Tag) when ?IS_ETAGC(C) -> etag(R, Acc, Strength, << Tag/binary, C >>). 2182 2183 etag_list_sep(<<>>, Acc) -> lists:reverse(Acc); 2184 etag_list_sep(<< C, R/bits >>, Acc) when ?IS_WS(C) -> etag_list_sep(R, Acc); 2185 etag_list_sep(<< $,, R/bits >>, Acc) -> etag_list(R, Acc). 2186 2187 -ifdef(TEST). 2188 prop_parse_if_match() -> 2189 ?FORALL(L, 2190 non_empty(list(etag())), 2191 begin 2192 << _, IfMatch/binary >> = iolist_to_binary([[$,, T] || {_, T} <- L]), 2193 ResL = parse_if_match(IfMatch), 2194 CheckedL = [T =:= ResT || {{T, _}, ResT} <- lists:zip(L, ResL)], 2195 [true] =:= lists:usort(CheckedL) 2196 end). 2197 2198 parse_if_match_test_() -> 2199 Tests = [ 2200 {<<"\"xyzzy\"">>, [{strong, <<"xyzzy">>}]}, 2201 {<<"\"xyzzy\", \"r2d2xxxx\", \"c3piozzzz\"">>, 2202 [{strong, <<"xyzzy">>}, {strong, <<"r2d2xxxx">>}, {strong, <<"c3piozzzz">>}]}, 2203 {<<"*">>, '*'} 2204 ], 2205 [{V, fun() -> R = parse_if_match(V) end} || {V, R} <- Tests]. 2206 2207 parse_if_match_error_test_() -> 2208 Tests = [ 2209 <<>> 2210 ], 2211 [{V, fun() -> {'EXIT', _} = (catch parse_if_match(V)) end} || V <- Tests]. 2212 2213 horse_parse_if_match() -> 2214 horse:repeat(200000, 2215 parse_if_match(<<"\"xyzzy\", \"r2d2xxxx\", \"c3piozzzz\"">>) 2216 ). 2217 -endif. 2218 2219 %% If-Modified-Since header. 2220 2221 -spec parse_if_modified_since(binary()) -> calendar:datetime(). 2222 parse_if_modified_since(IfModifiedSince) -> 2223 cow_date:parse_date(IfModifiedSince). 2224 2225 -ifdef(TEST). 2226 parse_if_modified_since_test_() -> 2227 Tests = [ 2228 {<<"Sat, 29 Oct 1994 19:43:31 GMT">>, {{1994, 10, 29}, {19, 43, 31}}} 2229 ], 2230 [{V, fun() -> R = parse_if_modified_since(V) end} || {V, R} <- Tests]. 2231 -endif. 2232 2233 %% If-None-Match header. 2234 2235 -spec parse_if_none_match(binary()) -> '*' | [etag()]. 2236 parse_if_none_match(<<"*">>) -> 2237 '*'; 2238 parse_if_none_match(IfNoneMatch) -> 2239 nonempty(etag_list(IfNoneMatch, [])). 2240 2241 -ifdef(TEST). 2242 parse_if_none_match_test_() -> 2243 Tests = [ 2244 {<<"\"xyzzy\"">>, [{strong, <<"xyzzy">>}]}, 2245 {<<"W/\"xyzzy\"">>, [{weak, <<"xyzzy">>}]}, 2246 {<<"\"xyzzy\", \"r2d2xxxx\", \"c3piozzzz\"">>, 2247 [{strong, <<"xyzzy">>}, {strong, <<"r2d2xxxx">>}, {strong, <<"c3piozzzz">>}]}, 2248 {<<"W/\"xyzzy\", W/\"r2d2xxxx\", W/\"c3piozzzz\"">>, 2249 [{weak, <<"xyzzy">>}, {weak, <<"r2d2xxxx">>}, {weak, <<"c3piozzzz">>}]}, 2250 {<<"*">>, '*'} 2251 ], 2252 [{V, fun() -> R = parse_if_none_match(V) end} || {V, R} <- Tests]. 2253 2254 parse_if_none_match_error_test_() -> 2255 Tests = [ 2256 <<>> 2257 ], 2258 [{V, fun() -> {'EXIT', _} = (catch parse_if_none_match(V)) end} || V <- Tests]. 2259 2260 horse_parse_if_none_match() -> 2261 horse:repeat(200000, 2262 parse_if_none_match(<<"W/\"xyzzy\", W/\"r2d2xxxx\", W/\"c3piozzzz\"">>) 2263 ). 2264 -endif. 2265 2266 %% If-Range header. 2267 2268 -spec parse_if_range(binary()) -> etag() | calendar:datetime(). 2269 parse_if_range(<< $W, $/, $", R/bits >>) -> 2270 etag(R, weak, <<>>); 2271 parse_if_range(<< $", R/bits >>) -> 2272 etag(R, strong, <<>>); 2273 parse_if_range(IfRange) -> 2274 cow_date:parse_date(IfRange). 2275 2276 -ifdef(TEST). 2277 parse_if_range_test_() -> 2278 Tests = [ 2279 {<<"W/\"xyzzy\"">>, {weak, <<"xyzzy">>}}, 2280 {<<"\"xyzzy\"">>, {strong, <<"xyzzy">>}}, 2281 {<<"Sat, 29 Oct 1994 19:43:31 GMT">>, {{1994, 10, 29}, {19, 43, 31}}} 2282 ], 2283 [{V, fun() -> R = parse_if_range(V) end} || {V, R} <- Tests]. 2284 2285 parse_if_range_error_test_() -> 2286 Tests = [ 2287 <<>> 2288 ], 2289 [{V, fun() -> {'EXIT', _} = (catch parse_if_range(V)) end} || V <- Tests]. 2290 2291 horse_parse_if_range_etag() -> 2292 horse:repeat(200000, 2293 parse_if_range(<<"\"xyzzy\"">>) 2294 ). 2295 2296 horse_parse_if_range_date() -> 2297 horse:repeat(200000, 2298 parse_if_range(<<"Sat, 29 Oct 1994 19:43:31 GMT">>) 2299 ). 2300 -endif. 2301 2302 %% If-Unmodified-Since header. 2303 2304 -spec parse_if_unmodified_since(binary()) -> calendar:datetime(). 2305 parse_if_unmodified_since(IfModifiedSince) -> 2306 cow_date:parse_date(IfModifiedSince). 2307 2308 -ifdef(TEST). 2309 parse_if_unmodified_since_test_() -> 2310 Tests = [ 2311 {<<"Sat, 29 Oct 1994 19:43:31 GMT">>, {{1994, 10, 29}, {19, 43, 31}}} 2312 ], 2313 [{V, fun() -> R = parse_if_unmodified_since(V) end} || {V, R} <- Tests]. 2314 -endif. 2315 2316 %% Last-Modified header. 2317 2318 -spec parse_last_modified(binary()) -> calendar:datetime(). 2319 parse_last_modified(LastModified) -> 2320 cow_date:parse_date(LastModified). 2321 2322 -ifdef(TEST). 2323 parse_last_modified_test_() -> 2324 Tests = [ 2325 {<<"Tue, 15 Nov 1994 12:45:26 GMT">>, {{1994, 11, 15}, {12, 45, 26}}} 2326 ], 2327 [{V, fun() -> R = parse_last_modified(V) end} || {V, R} <- Tests]. 2328 -endif. 2329 2330 %% Link header. 2331 2332 -spec parse_link(binary()) -> [cow_link:link()]. 2333 parse_link(Link) -> 2334 cow_link:parse_link(Link). 2335 2336 %% Max-Forwards header. 2337 2338 -spec parse_max_forwards(binary()) -> non_neg_integer(). 2339 parse_max_forwards(MaxForwards) -> 2340 I = binary_to_integer(MaxForwards), 2341 true = I >= 0, 2342 I. 2343 2344 -ifdef(TEST). 2345 prop_parse_max_forwards() -> 2346 ?FORALL( 2347 X, 2348 non_neg_integer(), 2349 X =:= parse_max_forwards(integer_to_binary(X)) 2350 ). 2351 2352 parse_max_forwards_test_() -> 2353 Tests = [ 2354 {<<"0">>, 0}, 2355 {<<"42">>, 42}, 2356 {<<"69">>, 69}, 2357 {<<"1337">>, 1337}, 2358 {<<"1234567890">>, 1234567890} 2359 ], 2360 [{V, fun() -> R = parse_max_forwards(V) end} || {V, R} <- Tests]. 2361 2362 parse_max_forwards_error_test_() -> 2363 Tests = [ 2364 <<>>, 2365 <<"123, 123">>, 2366 <<"4.17">> 2367 ], 2368 [{V, fun() -> {'EXIT', _} = (catch parse_max_forwards(V)) end} || V <- Tests]. 2369 -endif. 2370 2371 %% Origin header. 2372 2373 %% According to the RFC6454 we should generate 2374 %% a fresh globally unique identifier and return that value if: 2375 %% - URI does not use a hierarchical element as a naming authority 2376 %% or the URI is not an absolute URI 2377 %% - the implementation doesn't support the protocol given by uri-scheme 2378 %% Thus, erlang reference represents a GUID here. 2379 %% 2380 %% We only seek to have legal characters and separate the 2381 %% host and port values. The number of segments in the host 2382 %% or the size of each segment is not checked. 2383 %% 2384 %% There is no way to distinguish IPv4 addresses from regular 2385 %% names until the last segment is reached therefore we do not 2386 %% differentiate them. 2387 %% 2388 %% @todo The following valid hosts are currently rejected: IPv6 2389 %% addresses with a zone identifier; IPvFuture addresses; 2390 %% and percent-encoded addresses. 2391 2392 -spec parse_origin(binary()) -> [{binary(), binary(), 0..65535} | reference()]. 2393 parse_origin(Origins) -> 2394 nonempty(origin_scheme(Origins, [])). 2395 2396 origin_scheme(<<>>, Acc) -> Acc; 2397 origin_scheme(<< "http://", R/bits >>, Acc) -> origin_host(R, Acc, <<"http">>); 2398 origin_scheme(<< "https://", R/bits >>, Acc) -> origin_host(R, Acc, <<"https">>); 2399 origin_scheme(<< C, R/bits >>, Acc) when ?IS_TOKEN(C) -> origin_scheme(next_origin(R), [make_ref()|Acc]). 2400 2401 origin_host(<< $[, R/bits >>, Acc, Scheme) -> origin_ipv6_address(R, Acc, Scheme, << $[ >>); 2402 origin_host(Host, Acc, Scheme) -> origin_reg_name(Host, Acc, Scheme, <<>>). 2403 2404 origin_ipv6_address(<< $] >>, Acc, Scheme, IP) -> 2405 lists:reverse([{Scheme, << IP/binary, $] >>, default_port(Scheme)}|Acc]); 2406 origin_ipv6_address(<< $], $\s, R/bits >>, Acc, Scheme, IP) -> 2407 origin_scheme(R, [{Scheme, << IP/binary, $] >>, default_port(Scheme)}|Acc]); 2408 origin_ipv6_address(<< $], $:, Port/bits >>, Acc, Scheme, IP) -> 2409 origin_port(Port, Acc, Scheme, << IP/binary, $] >>, <<>>); 2410 origin_ipv6_address(<< C, R/bits >>, Acc, Scheme, IP) when ?IS_HEX(C) or (C =:= $:) or (C =:= $.) -> 2411 ?LOWER(origin_ipv6_address, R, Acc, Scheme, IP). 2412 2413 origin_reg_name(<<>>, Acc, Scheme, Name) -> 2414 lists:reverse([{Scheme, Name, default_port(Scheme)}|Acc]); 2415 origin_reg_name(<< $\s, R/bits >>, Acc, Scheme, Name) -> 2416 origin_scheme(R, [{Scheme, Name, default_port(Scheme)}|Acc]); 2417 origin_reg_name(<< $:, Port/bits >>, Acc, Scheme, Name) -> 2418 origin_port(Port, Acc, Scheme, Name, <<>>); 2419 origin_reg_name(<< C, R/bits >>, Acc, Scheme, Name) when ?IS_URI_UNRESERVED(C) or ?IS_URI_SUB_DELIMS(C) -> 2420 ?LOWER(origin_reg_name, R, Acc, Scheme, Name). 2421 2422 origin_port(<<>>, Acc, Scheme, Host, Port) -> 2423 lists:reverse([{Scheme, Host, binary_to_integer(Port)}|Acc]); 2424 origin_port(<< $\s, R/bits >>, Acc, Scheme, Host, Port) -> 2425 origin_scheme(R, [{Scheme, Host, binary_to_integer(Port)}|Acc]); 2426 origin_port(<< C, R/bits >>, Acc, Scheme, Host, Port) when ?IS_DIGIT(C) -> 2427 origin_port(R, Acc, Scheme, Host, << Port/binary, C >>). 2428 2429 next_origin(<<>>) -> <<>>; 2430 next_origin(<< $\s, C, R/bits >>) when ?IS_TOKEN(C) -> << C, R/bits >>; 2431 next_origin(<< C, R/bits >>) when ?IS_TOKEN(C) or (C =:= $:) or (C =:= $/) -> next_origin(R). 2432 2433 default_port(<< "http" >>) -> 80; 2434 default_port(<< "https" >>) -> 443. 2435 2436 -ifdef(TEST). 2437 scheme() -> oneof([<<"http">>, <<"https">>]). 2438 2439 scheme_host_port() -> 2440 ?LET({Scheme, Host, Port}, 2441 {scheme(), host(), integer(1, 65535)}, 2442 begin 2443 HostBin = list_to_binary(Host), 2444 {[{Scheme, ?LOWER(HostBin), Port}], 2445 case default_port(Scheme) of 2446 Port -> << Scheme/binary, "://", HostBin/binary>>; 2447 _ -> << Scheme/binary, "://", HostBin/binary, $:, (integer_to_binary(Port))/binary >> 2448 end} 2449 end). 2450 2451 prop_parse_origin() -> 2452 ?FORALL({Res, Origin}, scheme_host_port(), Res =:= parse_origin(Origin)). 2453 2454 parse_origin_test_() -> 2455 Tests = [ 2456 {<<"http://www.example.org:8080">>, [{<<"http">>, <<"www.example.org">>, 8080}]}, 2457 {<<"http://www.example.org">>, [{<<"http">>, <<"www.example.org">>, 80}]}, 2458 {<<"http://192.0.2.1:8080">>, [{<<"http">>, <<"192.0.2.1">>, 8080}]}, 2459 {<<"http://192.0.2.1">>, [{<<"http">>, <<"192.0.2.1">>, 80}]}, 2460 {<<"http://[2001:db8::1]:8080">>, [{<<"http">>, <<"[2001:db8::1]">>, 8080}]}, 2461 {<<"http://[2001:db8::1]">>, [{<<"http">>, <<"[2001:db8::1]">>, 80}]}, 2462 {<<"http://[::ffff:192.0.2.1]:8080">>, [{<<"http">>, <<"[::ffff:192.0.2.1]">>, 8080}]}, 2463 {<<"http://[::ffff:192.0.2.1]">>, [{<<"http">>, <<"[::ffff:192.0.2.1]">>, 80}]}, 2464 {<<"http://example.org https://blue.example.com:8080">>, 2465 [{<<"http">>, <<"example.org">>, 80}, 2466 {<<"https">>, <<"blue.example.com">>, 8080}]} 2467 ], 2468 [{V, fun() -> R = parse_origin(V) end} || {V, R} <- Tests]. 2469 2470 parse_origin_reference_test_() -> 2471 Tests = [ 2472 <<"null">>, 2473 <<"httpx://example.org:80">>, 2474 <<"httpx://example.org:80 null">>, 2475 <<"null null">> 2476 ], 2477 [{V, fun() -> [true = is_reference(Ref) || Ref <- parse_origin(V)] end} || V <- Tests]. 2478 2479 parse_origin_error_test_() -> 2480 Tests = [ 2481 <<>>, 2482 <<"null", $\t, "null">>, 2483 <<"null", $\s, $\s, "null">> 2484 ], 2485 [{V, fun() -> {'EXIT', _} = (catch parse_origin(V)) end} || V <- Tests]. 2486 2487 horse_parse_origin_blue_example_org() -> 2488 horse:repeat(200000, 2489 parse_origin(<<"http://blue.example.org:8080">>) 2490 ). 2491 2492 horse_parse_origin_ipv4() -> 2493 horse:repeat(200000, 2494 parse_origin(<<"http://192.0.2.1:8080">>) 2495 ). 2496 2497 horse_parse_origin_ipv6() -> 2498 horse:repeat(200000, 2499 parse_origin(<<"http://[2001:db8::1]:8080">>) 2500 ). 2501 2502 horse_parse_origin_ipv6_v4() -> 2503 horse:repeat(200000, 2504 parse_origin(<<"http://[::ffff:192.0.2.1]:8080">>) 2505 ). 2506 2507 horse_parse_origin_null() -> 2508 horse:repeat(200000, 2509 parse_origin(<<"null">>) 2510 ). 2511 -endif. 2512 2513 %% Pragma header. 2514 %% 2515 %% Legacy header kept for backward compatibility with HTTP/1.0 caches. 2516 %% Only the "no-cache" directive was ever specified, and only for 2517 %% request messages. 2518 %% 2519 %% We take a large shortcut in the parsing of this header, expecting 2520 %% an exact match of "no-cache". 2521 2522 -spec parse_pragma(binary()) -> cache | no_cache. 2523 parse_pragma(<<"no-cache">>) -> no_cache; 2524 parse_pragma(_) -> cache. 2525 2526 %% Proxy-Authenticate header. 2527 %% 2528 %% Alias of parse_www_authenticate/1 due to identical syntax. 2529 2530 -spec parse_proxy_authenticate(binary()) -> [{basic, binary()} 2531 | {bearer | digest | binary(), [{binary(), binary()}]}]. 2532 parse_proxy_authenticate(ProxyAuthenticate) -> 2533 parse_www_authenticate(ProxyAuthenticate). 2534 2535 %% Proxy-Authorization header. 2536 %% 2537 %% Alias of parse_authorization/1 due to identical syntax. 2538 2539 -spec parse_proxy_authorization(binary()) 2540 -> {basic, binary(), binary()} 2541 | {bearer, binary()} 2542 | {digest, [{binary(), binary()}]}. 2543 parse_proxy_authorization(ProxyAuthorization) -> 2544 parse_authorization(ProxyAuthorization). 2545 2546 %% Range header. 2547 2548 -spec parse_range(binary()) 2549 -> {bytes, [{non_neg_integer(), non_neg_integer() | infinity} | neg_integer()]} 2550 | {binary(), binary()}. 2551 parse_range(<<"bytes=", R/bits >>) -> 2552 bytes_range_set(R, []); 2553 parse_range(<< C, R/bits >>) when ?IS_TOKEN(C) -> 2554 ?LOWER(other_range_unit, R, <<>>). 2555 2556 bytes_range_set(<<>>, Acc) -> {bytes, lists:reverse(Acc)}; 2557 bytes_range_set(<< C, R/bits >>, Acc) when ?IS_WS_COMMA(C) -> bytes_range_set(R, Acc); 2558 bytes_range_set(<< $-, C, R/bits >>, Acc) when ?IS_DIGIT(C) -> bytes_range_suffix_spec(R, Acc, C - $0); 2559 bytes_range_set(<< C, R/bits >>, Acc) when ?IS_DIGIT(C) -> bytes_range_spec(R, Acc, C - $0). 2560 2561 bytes_range_spec(<< $-, C, R/bits >>, Acc, First) when ?IS_DIGIT(C) -> bytes_range_spec_last(R, Acc, First, C - $0); 2562 bytes_range_spec(<< $-, R/bits >>, Acc, First) -> bytes_range_set_sep(R, [{First, infinity}|Acc]); 2563 bytes_range_spec(<< C, R/bits >>, Acc, First) when ?IS_DIGIT(C) -> bytes_range_spec(R, Acc, First * 10 + C - $0). 2564 2565 bytes_range_spec_last(<< C, R/bits >>, Acc, First, Last) when ?IS_DIGIT(C) -> bytes_range_spec_last(R, Acc, First, Last * 10 + C - $0); 2566 bytes_range_spec_last(R, Acc, First, Last) -> bytes_range_set_sep(R, [{First, Last}|Acc]). 2567 2568 bytes_range_suffix_spec(<< C, R/bits >>, Acc, Suffix) when ?IS_DIGIT(C) -> bytes_range_suffix_spec(R, Acc, Suffix * 10 + C - $0); 2569 bytes_range_suffix_spec(R, Acc, Suffix) -> bytes_range_set_sep(R, [-Suffix|Acc]). 2570 2571 bytes_range_set_sep(<<>>, Acc) -> {bytes, lists:reverse(Acc)}; 2572 bytes_range_set_sep(<< C, R/bits >>, Acc) when ?IS_WS(C) -> bytes_range_set_sep(R, Acc); 2573 bytes_range_set_sep(<< $,, R/bits >>, Acc) -> bytes_range_set(R, Acc). 2574 2575 other_range_unit(<< $=, C, R/bits >>, U) when ?IS_VCHAR(C) -> 2576 other_range_set(R, U, << C >>); 2577 other_range_unit(<< C, R/bits >>, U) when ?IS_TOKEN(C) -> 2578 ?LOWER(other_range_unit, R, U). 2579 2580 other_range_set(<<>>, U, S) -> 2581 {U, S}; 2582 other_range_set(<< C, R/bits >>, U, S) when ?IS_VCHAR(C) -> 2583 other_range_set(R, U, << S/binary, C >>). 2584 2585 -ifdef(TEST). 2586 bytes_range() -> 2587 ?LET(BytesSet, 2588 non_empty(list(oneof([ 2589 ?SUCHTHAT({First, Last}, {pos_integer(), pos_integer()}, First =< Last), 2590 {pos_integer(), infinity}, 2591 ?LET(I, pos_integer(), -I) 2592 ]))), 2593 {{bytes, BytesSet}, begin 2594 << _, Set/bits >> = iolist_to_binary([ 2595 case Spec of 2596 {First, infinity} -> [$,, integer_to_binary(First), $-]; 2597 {First, Last} -> [$,, integer_to_binary(First), $-, integer_to_binary(Last)]; 2598 Suffix -> [$,, integer_to_binary(Suffix)] 2599 end || Spec <- BytesSet]), 2600 <<"bytes=", Set/binary >> 2601 end}). 2602 2603 other_range() -> 2604 ?LET(Range = {Unit, Set}, 2605 {token(), ?LET(L, non_empty(list(vchar())), list_to_binary(L))}, 2606 {Range, << Unit/binary, $=, Set/binary >>}). 2607 2608 range() -> 2609 oneof([ 2610 bytes_range(), 2611 other_range() 2612 ]). 2613 2614 prop_parse_range() -> 2615 ?FORALL({Range, RangeBin}, 2616 range(), 2617 begin 2618 Range2 = case Range of 2619 {bytes, _} -> Range; 2620 {Unit, Set} -> {?LOWER(Unit), Set} 2621 end, 2622 Range2 =:= parse_range(RangeBin) 2623 end). 2624 2625 parse_range_test_() -> 2626 Tests = [ 2627 {<<"bytes=0-499">>, {bytes, [{0, 499}]}}, 2628 {<<"bytes=500-999">>, {bytes, [{500, 999}]}}, 2629 {<<"bytes=-500">>, {bytes, [-500]}}, 2630 {<<"bytes=9500-">>, {bytes, [{9500, infinity}]}}, 2631 {<<"bytes=0-0,-1">>, {bytes, [{0, 0}, -1]}}, 2632 {<<"bytes=500-600,601-999">>, {bytes, [{500, 600}, {601, 999}]}}, 2633 {<<"bytes=500-700,601-999">>, {bytes, [{500, 700}, {601, 999}]}}, 2634 {<<"books=I-III,V-IX">>, {<<"books">>, <<"I-III,V-IX">>}} 2635 ], 2636 [{V, fun() -> R = parse_range(V) end} || {V, R} <- Tests]. 2637 2638 parse_range_error_test_() -> 2639 Tests = [ 2640 <<>> 2641 ], 2642 [{V, fun() -> {'EXIT', _} = (catch parse_range(V)) end} || V <- Tests]. 2643 2644 horse_parse_range_first_last() -> 2645 horse:repeat(200000, 2646 parse_range(<<"bytes=500-999">>) 2647 ). 2648 2649 horse_parse_range_infinity() -> 2650 horse:repeat(200000, 2651 parse_range(<<"bytes=9500-">>) 2652 ). 2653 2654 horse_parse_range_suffix() -> 2655 horse:repeat(200000, 2656 parse_range(<<"bytes=-500">>) 2657 ). 2658 2659 horse_parse_range_two() -> 2660 horse:repeat(200000, 2661 parse_range(<<"bytes=500-700,601-999">>) 2662 ). 2663 2664 horse_parse_range_other() -> 2665 horse:repeat(200000, 2666 parse_range(<<"books=I-III,V-IX">>) 2667 ). 2668 -endif. 2669 2670 %% Retry-After header. 2671 2672 -spec parse_retry_after(binary()) -> non_neg_integer() | calendar:datetime(). 2673 parse_retry_after(RetryAfter = << D, _/bits >>) when ?IS_DIGIT(D) -> 2674 I = binary_to_integer(RetryAfter), 2675 true = I >= 0, 2676 I; 2677 parse_retry_after(RetryAfter) -> 2678 cow_date:parse_date(RetryAfter). 2679 2680 -ifdef(TEST). 2681 parse_retry_after_test_() -> 2682 Tests = [ 2683 {<<"Fri, 31 Dec 1999 23:59:59 GMT">>, {{1999, 12, 31}, {23, 59, 59}}}, 2684 {<<"120">>, 120} 2685 ], 2686 [{V, fun() -> R = parse_retry_after(V) end} || {V, R} <- Tests]. 2687 2688 parse_retry_after_error_test_() -> 2689 Tests = [ 2690 <<>> 2691 ], 2692 [{V, fun() -> {'EXIT', _} = (catch parse_retry_after(V)) end} || V <- Tests]. 2693 2694 horse_parse_retry_after_date() -> 2695 horse:repeat(200000, 2696 parse_retry_after(<<"Fri, 31 Dec 1999 23:59:59 GMT">>) 2697 ). 2698 2699 horse_parse_retry_after_delay_seconds() -> 2700 horse:repeat(200000, 2701 parse_retry_after(<<"120">>) 2702 ). 2703 -endif. 2704 2705 %% Sec-WebSocket-Accept header. 2706 %% 2707 %% The argument is returned without any processing. This value is 2708 %% expected to be matched directly by the client so no parsing is 2709 %% needed. 2710 2711 -spec parse_sec_websocket_accept(binary()) -> binary(). 2712 parse_sec_websocket_accept(SecWebSocketAccept) -> 2713 SecWebSocketAccept. 2714 2715 %% Sec-WebSocket-Extensions header. 2716 2717 -spec parse_sec_websocket_extensions(binary()) -> [{binary(), [binary() | {binary(), binary()}]}]. 2718 parse_sec_websocket_extensions(SecWebSocketExtensions) -> 2719 nonempty(ws_extension_list(SecWebSocketExtensions, [])). 2720 2721 ws_extension_list(<<>>, Acc) -> lists:reverse(Acc); 2722 ws_extension_list(<< C, R/bits >>, Acc) when ?IS_WS_COMMA(C) -> ws_extension_list(R, Acc); 2723 ws_extension_list(<< C, R/bits >>, Acc) when ?IS_TOKEN(C) -> ws_extension(R, Acc, << C >>). 2724 2725 ws_extension(<< C, R/bits >>, Acc, E) when ?IS_TOKEN(C) -> ws_extension(R, Acc, << E/binary, C >>); 2726 ws_extension(R, Acc, E) -> ws_extension_param_sep(R, Acc, E, []). 2727 2728 ws_extension_param_sep(<<>>, Acc, E, P) -> lists:reverse([{E, lists:reverse(P)}|Acc]); 2729 ws_extension_param_sep(<< $,, R/bits >>, Acc, E, P) -> ws_extension_list(R, [{E, lists:reverse(P)}|Acc]); 2730 ws_extension_param_sep(<< $;, R/bits >>, Acc, E, P) -> ws_extension_before_param(R, Acc, E, P); 2731 ws_extension_param_sep(<< C, R/bits >>, Acc, E, P) when ?IS_WS(C) -> ws_extension_param_sep(R, Acc, E, P). 2732 2733 ws_extension_before_param(<< C, R/bits >>, Acc, E, P) when ?IS_WS(C) -> ws_extension_before_param(R, Acc, E, P); 2734 ws_extension_before_param(<< C, R/bits >>, Acc, E, P) when ?IS_TOKEN(C) -> ws_extension_param(R, Acc, E, P, << C >>). 2735 2736 ws_extension_param(<< $=, $", R/bits >>, Acc, E, P, K) -> ws_extension_quoted(R, Acc, E, P, K, <<>>); 2737 ws_extension_param(<< $=, C, R/bits >>, Acc, E, P, K) when ?IS_TOKEN(C) -> ws_extension_value(R, Acc, E, P, K, << C >>); 2738 ws_extension_param(<< C, R/bits >>, Acc, E, P, K) when ?IS_TOKEN(C) -> ws_extension_param(R, Acc, E, P, << K/binary, C >>); 2739 ws_extension_param(R, Acc, E, P, K) -> ws_extension_param_sep(R, Acc, E, [K|P]). 2740 2741 ws_extension_quoted(<< $", R/bits >>, Acc, E, P, K, V) -> ws_extension_param_sep(R, Acc, E, [{K, V}|P]); 2742 ws_extension_quoted(<< $\\, C, R/bits >>, Acc, E, P, K, V) when ?IS_TOKEN(C) -> ws_extension_quoted(R, Acc, E, P, K, << V/binary, C >>); 2743 ws_extension_quoted(<< C, R/bits >>, Acc, E, P, K, V) when ?IS_TOKEN(C) -> ws_extension_quoted(R, Acc, E, P, K, << V/binary, C >>). 2744 2745 ws_extension_value(<< C, R/bits >>, Acc, E, P, K, V) when ?IS_TOKEN(C) -> ws_extension_value(R, Acc, E, P, K, << V/binary, C >>); 2746 ws_extension_value(R, Acc, E, P, K, V) -> ws_extension_param_sep(R, Acc, E, [{K, V}|P]). 2747 2748 -ifdef(TEST). 2749 quoted_token() -> 2750 ?LET(T, 2751 non_empty(list(frequency([ 2752 {99, tchar()}, 2753 {1, [$\\, tchar()]} 2754 ]))), 2755 [$", T, $"]). 2756 2757 ws_extension() -> 2758 ?LET({E, PL}, 2759 {token(), small_list({ows(), ows(), oneof([token(), {token(), oneof([token(), quoted_token()])}])})}, 2760 {E, PL, iolist_to_binary([E, 2761 [case P of 2762 {OWS1, OWS2, {K, V}} -> [OWS1, $;, OWS2, K, $=, V]; 2763 {OWS1, OWS2, K} -> [OWS1, $;, OWS2, K] 2764 end || P <- PL] 2765 ])}). 2766 2767 prop_parse_sec_websocket_extensions() -> 2768 ?FORALL(L, 2769 vector(1, 50, ws_extension()), 2770 begin 2771 << _, SecWebsocketExtensions/binary >> = iolist_to_binary([[$,, E] || {_, _, E} <- L]), 2772 ResL = parse_sec_websocket_extensions(SecWebsocketExtensions), 2773 CheckedL = [begin 2774 ExpectedPL = [case P of 2775 {_, _, {K, V}} -> {K, unquote(V)}; 2776 {_, _, K} -> K 2777 end || P <- PL], 2778 E =:= ResE andalso ExpectedPL =:= ResPL 2779 end || {{E, PL, _}, {ResE, ResPL}} <- lists:zip(L, ResL)], 2780 [true] =:= lists:usort(CheckedL) 2781 end). 2782 2783 parse_sec_websocket_extensions_test_() -> 2784 Tests = [ 2785 {<<"foo">>, [{<<"foo">>, []}]}, 2786 {<<"bar; baz=2">>, [{<<"bar">>, [{<<"baz">>, <<"2">>}]}]}, 2787 {<<"foo, bar; baz=2">>, [{<<"foo">>, []}, {<<"bar">>, [{<<"baz">>, <<"2">>}]}]}, 2788 {<<"deflate-stream">>, [{<<"deflate-stream">>, []}]}, 2789 {<<"mux; max-channels=4; flow-control, deflate-stream">>, 2790 [{<<"mux">>, [{<<"max-channels">>, <<"4">>}, <<"flow-control">>]}, {<<"deflate-stream">>, []}]}, 2791 {<<"private-extension">>, [{<<"private-extension">>, []}]} 2792 ], 2793 [{V, fun() -> R = parse_sec_websocket_extensions(V) end} || {V, R} <- Tests]. 2794 2795 parse_sec_websocket_extensions_error_test_() -> 2796 Tests = [ 2797 <<>> 2798 ], 2799 [{V, fun() -> {'EXIT', _} = (catch parse_sec_websocket_extensions(V)) end} 2800 || V <- Tests]. 2801 2802 horse_parse_sec_websocket_extensions() -> 2803 horse:repeat(200000, 2804 parse_sec_websocket_extensions(<<"mux; max-channels=4; flow-control, deflate-stream">>) 2805 ). 2806 -endif. 2807 2808 %% Sec-WebSocket-Key header. 2809 %% 2810 %% The argument is returned without any processing. This value is 2811 %% expected to be prepended to a static value, the result of which 2812 %% hashed to form a new base64 value returned in Sec-WebSocket-Accept, 2813 %% therefore no parsing is needed. 2814 2815 -spec parse_sec_websocket_key(binary()) -> binary(). 2816 parse_sec_websocket_key(SecWebSocketKey) -> 2817 SecWebSocketKey. 2818 2819 %% Sec-WebSocket-Protocol request header. 2820 2821 -spec parse_sec_websocket_protocol_req(binary()) -> [binary()]. 2822 parse_sec_websocket_protocol_req(SecWebSocketProtocol) -> 2823 nonempty(token_list(SecWebSocketProtocol, [])). 2824 2825 -ifdef(TEST). 2826 parse_sec_websocket_protocol_req_test_() -> 2827 Tests = [ 2828 {<<"chat, superchat">>, [<<"chat">>, <<"superchat">>]}, 2829 {<<"Chat, SuperChat">>, [<<"Chat">>, <<"SuperChat">>]} 2830 ], 2831 [{V, fun() -> R = parse_sec_websocket_protocol_req(V) end} || {V, R} <- Tests]. 2832 2833 parse_sec_websocket_protocol_req_error_test_() -> 2834 Tests = [ 2835 <<>> 2836 ], 2837 [{V, fun() -> {'EXIT', _} = (catch parse_sec_websocket_protocol_req(V)) end} 2838 || V <- Tests]. 2839 2840 horse_parse_sec_websocket_protocol_req() -> 2841 horse:repeat(200000, 2842 parse_sec_websocket_protocol_req(<<"chat, superchat">>) 2843 ). 2844 -endif. 2845 2846 %% Sec-Websocket-Protocol response header. 2847 2848 -spec parse_sec_websocket_protocol_resp(binary()) -> binary(). 2849 parse_sec_websocket_protocol_resp(Protocol) -> 2850 true = <<>> =/= Protocol, 2851 ok = validate_token(Protocol), 2852 Protocol. 2853 2854 -ifdef(TEST). 2855 prop_parse_sec_websocket_protocol_resp() -> 2856 ?FORALL(T, 2857 token(), 2858 T =:= parse_sec_websocket_protocol_resp(T)). 2859 2860 parse_sec_websocket_protocol_resp_test_() -> 2861 Tests = [ 2862 {<<"chat">>, <<"chat">>}, 2863 {<<"CHAT">>, <<"CHAT">>} 2864 ], 2865 [{V, fun() -> R = parse_sec_websocket_protocol_resp(V) end} || {V, R} <- Tests]. 2866 2867 parse_sec_websocket_protocol_resp_error_test_() -> 2868 Tests = [ 2869 <<>> 2870 ], 2871 [{V, fun() -> {'EXIT', _} = (catch parse_sec_websocket_protocol_resp(V)) end} 2872 || V <- Tests]. 2873 2874 horse_parse_sec_websocket_protocol_resp() -> 2875 horse:repeat(200000, 2876 parse_sec_websocket_protocol_resp(<<"chat">>) 2877 ). 2878 -endif. 2879 2880 %% Sec-WebSocket-Version request header. 2881 2882 -spec parse_sec_websocket_version_req(binary()) -> websocket_version(). 2883 parse_sec_websocket_version_req(SecWebSocketVersion) when byte_size(SecWebSocketVersion) < 4 -> 2884 Version = binary_to_integer(SecWebSocketVersion), 2885 true = Version >= 0 andalso Version =< 255, 2886 Version. 2887 2888 -ifdef(TEST). 2889 prop_parse_sec_websocket_version_req() -> 2890 ?FORALL(Version, 2891 integer(0, 255), 2892 Version =:= parse_sec_websocket_version_req(integer_to_binary(Version))). 2893 2894 parse_sec_websocket_version_req_test_() -> 2895 Tests = [ 2896 {<<"13">>, 13}, 2897 {<<"25">>, 25} 2898 ], 2899 [{V, fun() -> R = parse_sec_websocket_version_req(V) end} || {V, R} <- Tests]. 2900 2901 parse_sec_websocket_version_req_error_test_() -> 2902 Tests = [ 2903 <<>>, 2904 <<" ">>, 2905 <<"7, 8, 13">>, 2906 <<"invalid">> 2907 ], 2908 [{V, fun() -> {'EXIT', _} = (catch parse_sec_websocket_version_req(V)) end} 2909 || V <- Tests]. 2910 2911 horse_parse_sec_websocket_version_req_13() -> 2912 horse:repeat(200000, 2913 parse_sec_websocket_version_req(<<"13">>) 2914 ). 2915 2916 horse_parse_sec_websocket_version_req_255() -> 2917 horse:repeat(200000, 2918 parse_sec_websocket_version_req(<<"255">>) 2919 ). 2920 -endif. 2921 2922 %% Sec-WebSocket-Version response header. 2923 2924 -spec parse_sec_websocket_version_resp(binary()) -> [websocket_version()]. 2925 parse_sec_websocket_version_resp(SecWebSocketVersion) -> 2926 nonempty(ws_version_list(SecWebSocketVersion, [])). 2927 2928 ws_version_list(<<>>, Acc) -> lists:reverse(Acc); 2929 ws_version_list(<< C, R/bits >>, Acc) when ?IS_WS_COMMA(C) -> ws_version_list(R, Acc); 2930 ws_version_list(<< C, R/bits >>, Acc) when ?IS_DIGIT(C) -> ws_version(R, Acc, C - $0). 2931 2932 ws_version(<< C, R/bits >>, Acc, V) when ?IS_DIGIT(C) -> ws_version(R, Acc, V * 10 + C - $0); 2933 ws_version(R, Acc, V) -> ws_version_list_sep(R, [V|Acc]). 2934 2935 ws_version_list_sep(<<>>, Acc) -> lists:reverse(Acc); 2936 ws_version_list_sep(<< C, R/bits >>, Acc) when ?IS_WS(C) -> ws_version_list_sep(R, Acc); 2937 ws_version_list_sep(<< $,, R/bits >>, Acc) -> ws_version_list(R, Acc). 2938 2939 -ifdef(TEST). 2940 sec_websocket_version_resp() -> 2941 ?LET(L, 2942 non_empty(list({ows(), ows(), integer(0, 255)})), 2943 begin 2944 << _, SecWebSocketVersion/binary >> = iolist_to_binary( 2945 [[OWS1, $,, OWS2, integer_to_binary(V)] || {OWS1, OWS2, V} <- L]), 2946 {[V || {_, _, V} <- L], SecWebSocketVersion} 2947 end). 2948 2949 prop_parse_sec_websocket_version_resp() -> 2950 ?FORALL({L, SecWebSocketVersion}, 2951 sec_websocket_version_resp(), 2952 L =:= parse_sec_websocket_version_resp(SecWebSocketVersion)). 2953 2954 parse_sec_websocket_version_resp_test_() -> 2955 Tests = [ 2956 {<<"13, 8, 7">>, [13, 8, 7]} 2957 ], 2958 [{V, fun() -> R = parse_sec_websocket_version_resp(V) end} || {V, R} <- Tests]. 2959 2960 parse_sec_websocket_version_resp_error_test_() -> 2961 Tests = [ 2962 <<>> 2963 ], 2964 [{V, fun() -> {'EXIT', _} = (catch parse_sec_websocket_version_resp(V)) end} 2965 || V <- Tests]. 2966 2967 horse_parse_sec_websocket_version_resp() -> 2968 horse:repeat(200000, 2969 parse_sec_websocket_version_resp(<<"13, 8, 7">>) 2970 ). 2971 -endif. 2972 2973 %% Set-Cookie header. 2974 2975 -spec parse_set_cookie(binary()) 2976 -> {ok, binary(), binary(), cow_cookie:cookie_attrs()} 2977 | ignore. 2978 parse_set_cookie(SetCookie) -> 2979 cow_cookie:parse_set_cookie(SetCookie). 2980 2981 %% TE header. 2982 %% 2983 %% This function does not support parsing of transfer-parameter. 2984 2985 -spec parse_te(binary()) -> {trailers | no_trailers, [{binary(), qvalue()}]}. 2986 parse_te(TE) -> 2987 te_list(TE, no_trailers, []). 2988 2989 te_list(<<>>, Trail, Acc) -> {Trail, lists:reverse(Acc)}; 2990 te_list(<< C, R/bits >>, Trail, Acc) when ?IS_WS_COMMA(C) -> te_list(R, Trail, Acc); 2991 te_list(<< "trailers", R/bits >>, Trail, Acc) -> te(R, Trail, Acc, <<"trailers">>); 2992 te_list(<< "compress", R/bits >>, Trail, Acc) -> te(R, Trail, Acc, <<"compress">>); 2993 te_list(<< "deflate", R/bits >>, Trail, Acc) -> te(R, Trail, Acc, <<"deflate">>); 2994 te_list(<< "gzip", R/bits >>, Trail, Acc) -> te(R, Trail, Acc, <<"gzip">>); 2995 te_list(<< C, R/bits >>, Trail, Acc) when ?IS_TOKEN(C) -> 2996 ?LOWER(te, R, Trail, Acc, <<>>). 2997 2998 te(<<>>, _, Acc, <<"trailers">>) -> {trailers, lists:reverse(Acc)}; 2999 te(<< $,, R/bits >>, _, Acc, <<"trailers">>) -> te_list(R, trailers, Acc); 3000 te(<< $;, R/bits >>, Trail, Acc, T) when T =/= <<"trailers">> -> te_before_weight(R, Trail, Acc, T); 3001 te(<< C, R/bits >>, _, Acc, <<"trailers">>) when ?IS_WS(C) -> te_list_sep(R, trailers, Acc); 3002 te(<< C, R/bits >>, Trail, Acc, T) when ?IS_TOKEN(C) -> 3003 ?LOWER(te, R, Trail, Acc, T); 3004 te(R, Trail, Acc, T) -> te_param_sep(R, Trail, Acc, T). 3005 3006 te_param_sep(<<>>, Trail, Acc, T) -> {Trail, lists:reverse([{T, 1000}|Acc])}; 3007 te_param_sep(<< $,, R/bits >>, Trail, Acc, T) -> te_list(R, Trail, [{T, 1000}|Acc]); 3008 te_param_sep(<< C, R/bits >>, Trail, Acc, T) when ?IS_WS(C) -> te_param_sep(R, Trail, Acc, T). 3009 3010 te_before_weight(<< C, R/bits >>, Trail, Acc, T) when ?IS_WS(C) -> te_before_weight(R, Trail, Acc, T); 3011 te_before_weight(<< $q, $=, R/bits >>, Trail, Acc, T) -> te_weight(R, Trail, Acc, T). 3012 3013 te_weight(<< "1.000", R/bits >>, Trail, Acc, T) -> te_list_sep(R, Trail, [{T, 1000}|Acc]); 3014 te_weight(<< "1.00", R/bits >>, Trail, Acc, T) -> te_list_sep(R, Trail, [{T, 1000}|Acc]); 3015 te_weight(<< "1.0", R/bits >>, Trail, Acc, T) -> te_list_sep(R, Trail, [{T, 1000}|Acc]); 3016 te_weight(<< "1.", R/bits >>, Trail, Acc, T) -> te_list_sep(R, Trail, [{T, 1000}|Acc]); 3017 te_weight(<< "1", R/bits >>, Trail, Acc, T) -> te_list_sep(R, Trail, [{T, 1000}|Acc]); 3018 te_weight(<< "0.", A, B, C, R/bits >>, Trail, Acc, T) when ?IS_DIGIT(A), ?IS_DIGIT(B), ?IS_DIGIT(C) -> 3019 te_list_sep(R, Trail, [{T, (A - $0) * 100 + (B - $0) * 10 + (C - $0)}|Acc]); 3020 te_weight(<< "0.", A, B, R/bits >>, Trail, Acc, T) when ?IS_DIGIT(A), ?IS_DIGIT(B) -> 3021 te_list_sep(R, Trail, [{T, (A - $0) * 100 + (B - $0) * 10}|Acc]); 3022 te_weight(<< "0.", A, R/bits >>, Trail, Acc, T) when ?IS_DIGIT(A) -> 3023 te_list_sep(R, Trail, [{T, (A - $0) * 100}|Acc]); 3024 te_weight(<< "0.", R/bits >>, Trail, Acc, T) -> te_list_sep(R, Trail, [{T, 0}|Acc]); 3025 te_weight(<< "0", R/bits >>, Trail, Acc, T) -> te_list_sep(R, Trail, [{T, 0}|Acc]). 3026 3027 te_list_sep(<<>>, Trail, Acc) -> {Trail, lists:reverse(Acc)}; 3028 te_list_sep(<< C, R/bits >>, Trail, Acc) when ?IS_WS(C) -> te_list_sep(R, Trail, Acc); 3029 te_list_sep(<< $,, R/bits >>, Trail, Acc) -> te_list(R, Trail, Acc). 3030 3031 -ifdef(TEST). 3032 te() -> 3033 ?LET({Trail, L}, 3034 {elements([trailers, no_trailers]), 3035 small_non_empty_list({?SUCHTHAT(T, token(), T =/= <<"trailers">>), weight()})}, 3036 {Trail, L, begin 3037 L2 = case Trail of 3038 no_trailers -> L; 3039 trailers -> 3040 Rand = rand:uniform(length(L) + 1) - 1, 3041 {Before, After} = lists:split(Rand, L), 3042 Before ++ [{<<"trailers">>, undefined}|After] 3043 end, 3044 << _, TE/binary >> = iolist_to_binary([case W of 3045 undefined -> [$,, T]; 3046 _ -> [$,, T, <<";q=">>, qvalue_to_iodata(W)] 3047 end || {T, W} <- L2]), 3048 TE 3049 end} 3050 ). 3051 3052 prop_parse_te() -> 3053 ?FORALL({Trail, L, TE}, 3054 te(), 3055 begin 3056 {ResTrail, ResL} = parse_te(TE), 3057 CheckedL = [begin 3058 ResT =:= ?LOWER(T) 3059 andalso (ResW =:= W orelse (W =:= undefined andalso ResW =:= 1000)) 3060 end || {{T, W}, {ResT, ResW}} <- lists:zip(L, ResL)], 3061 ResTrail =:= Trail andalso [true] =:= lists:usort(CheckedL) 3062 end). 3063 3064 parse_te_test_() -> 3065 Tests = [ 3066 {<<"deflate">>, {no_trailers, [{<<"deflate">>, 1000}]}}, 3067 {<<>>, {no_trailers, []}}, 3068 {<<"trailers, deflate;q=0.5">>, {trailers, [{<<"deflate">>, 500}]}} 3069 ], 3070 [{V, fun() -> R = parse_te(V) end} || {V, R} <- Tests]. 3071 3072 horse_parse_te() -> 3073 horse:repeat(200000, 3074 parse_te(<<"trailers, deflate;q=0.5">>) 3075 ). 3076 -endif. 3077 3078 %% Trailer header. 3079 3080 -spec parse_trailer(binary()) -> [binary()]. 3081 parse_trailer(Trailer) -> 3082 nonempty(token_ci_list(Trailer, [])). 3083 3084 -ifdef(TEST). 3085 parse_trailer_test_() -> 3086 Tests = [ 3087 {<<"Date, Content-MD5">>, [<<"date">>, <<"content-md5">>]} 3088 ], 3089 [{V, fun() -> R = parse_trailer(V) end} || {V, R} <- Tests]. 3090 3091 parse_trailer_error_test_() -> 3092 Tests = [ 3093 <<>> 3094 ], 3095 [{V, fun() -> {'EXIT', _} = (catch parse_trailer(V)) end} || V <- Tests]. 3096 3097 horse_parse_trailer() -> 3098 horse:repeat(200000, 3099 parse_trailer(<<"Date, Content-MD5">>) 3100 ). 3101 -endif. 3102 3103 %% Transfer-Encoding header. 3104 %% 3105 %% This function does not support parsing of transfer-parameter. 3106 3107 -spec parse_transfer_encoding(binary()) -> [binary()]. 3108 parse_transfer_encoding(<<"chunked">>) -> 3109 [<<"chunked">>]; 3110 parse_transfer_encoding(TransferEncoding) -> 3111 nonempty(token_ci_list(TransferEncoding, [])). 3112 3113 -ifdef(TEST). 3114 prop_parse_transfer_encoding() -> 3115 ?FORALL(L, 3116 non_empty(list(token())), 3117 begin 3118 << _, TransferEncoding/binary >> = iolist_to_binary([[$,, C] || C <- L]), 3119 ResL = parse_transfer_encoding(TransferEncoding), 3120 CheckedL = [?LOWER(Co) =:= ResC || {Co, ResC} <- lists:zip(L, ResL)], 3121 [true] =:= lists:usort(CheckedL) 3122 end). 3123 3124 parse_transfer_encoding_test_() -> 3125 Tests = [ 3126 {<<"a , , , ">>, [<<"a">>]}, 3127 {<<" , , , a">>, [<<"a">>]}, 3128 {<<"a , , b">>, [<<"a">>, <<"b">>]}, 3129 {<<"chunked">>, [<<"chunked">>]}, 3130 {<<"chunked, something">>, [<<"chunked">>, <<"something">>]}, 3131 {<<"gzip, chunked">>, [<<"gzip">>, <<"chunked">>]} 3132 ], 3133 [{V, fun() -> R = parse_transfer_encoding(V) end} || {V, R} <- Tests]. 3134 3135 parse_transfer_encoding_error_test_() -> 3136 Tests = [ 3137 <<>>, 3138 <<" ">>, 3139 <<" , ">>, 3140 <<",,,">>, 3141 <<"a b">> 3142 ], 3143 [{V, fun() -> {'EXIT', _} = (catch parse_transfer_encoding(V)) end} 3144 || V <- Tests]. 3145 3146 horse_parse_transfer_encoding_chunked() -> 3147 horse:repeat(200000, 3148 parse_transfer_encoding(<<"chunked">>) 3149 ). 3150 3151 horse_parse_transfer_encoding_custom() -> 3152 horse:repeat(200000, 3153 parse_transfer_encoding(<<"chunked, something">>) 3154 ). 3155 -endif. 3156 3157 %% Upgrade header. 3158 %% 3159 %% It is unclear from the RFC whether the values here are 3160 %% case sensitive. 3161 %% 3162 %% We handle them in a case insensitive manner because they 3163 %% are described as case insensitive in the Websocket RFC. 3164 3165 -spec parse_upgrade(binary()) -> [binary()]. 3166 parse_upgrade(Upgrade) -> 3167 nonempty(protocol_list(Upgrade, [])). 3168 3169 protocol_list(<<>>, Acc) -> lists:reverse(Acc); 3170 protocol_list(<< C, R/bits >>, Acc) when ?IS_WS_COMMA(C) -> protocol_list(R, Acc); 3171 protocol_list(<< C, R/bits >>, Acc) when ?IS_TOKEN(C) -> 3172 ?LOWER(protocol_name, R, Acc, <<>>). 3173 3174 protocol_name(<< $/, C, R/bits >>, Acc, P) -> 3175 ?LOWER(protocol_version, R, Acc, << P/binary, $/ >>); 3176 protocol_name(<< C, R/bits >>, Acc, P) when ?IS_TOKEN(C) -> 3177 ?LOWER(protocol_name, R, Acc, P); 3178 protocol_name(R, Acc, P) -> protocol_list_sep(R, [P|Acc]). 3179 3180 protocol_version(<< C, R/bits >>, Acc, P) when ?IS_TOKEN(C) -> 3181 ?LOWER(protocol_version, R, Acc, P); 3182 protocol_version(R, Acc, P) -> protocol_list_sep(R, [P|Acc]). 3183 3184 protocol_list_sep(<<>>, Acc) -> lists:reverse(Acc); 3185 protocol_list_sep(<< C, R/bits >>, Acc) when ?IS_WS(C) -> protocol_list_sep(R, Acc); 3186 protocol_list_sep(<< $,, R/bits >>, Acc) -> protocol_list(R, Acc). 3187 3188 -ifdef(TEST). 3189 protocols() -> 3190 ?LET(P, 3191 oneof([token(), [token(), $/, token()]]), 3192 iolist_to_binary(P)). 3193 3194 prop_parse_upgrade() -> 3195 ?FORALL(L, 3196 non_empty(list(protocols())), 3197 begin 3198 << _, Upgrade/binary >> = iolist_to_binary([[$,, P] || P <- L]), 3199 ResL = parse_upgrade(Upgrade), 3200 CheckedL = [?LOWER(P) =:= ResP || {P, ResP} <- lists:zip(L, ResL)], 3201 [true] =:= lists:usort(CheckedL) 3202 end). 3203 3204 parse_upgrade_test_() -> 3205 Tests = [ 3206 {<<"HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11">>, 3207 [<<"http/2.0">>, <<"shttp/1.3">>, <<"irc/6.9">>, <<"rta/x11">>]}, 3208 {<<"HTTP/2.0">>, [<<"http/2.0">>]} 3209 ], 3210 [{V, fun() -> R = parse_upgrade(V) end} || {V, R} <- Tests]. 3211 3212 parse_upgrade_error_test_() -> 3213 Tests = [ 3214 <<>> 3215 ], 3216 [{V, fun() -> {'EXIT', _} = (catch parse_upgrade(V)) end} 3217 || V <- Tests]. 3218 -endif. 3219 3220 %% Variant-Key-06 (draft) header. 3221 %% 3222 %% The Variants header must be parsed first in order to know 3223 %% the NumMembers argument as it is the number of members in 3224 %% the Variants dictionary. 3225 3226 -spec parse_variant_key(binary(), pos_integer()) -> [[binary()]]. 3227 parse_variant_key(VariantKey, NumMembers) -> 3228 List = cow_http_struct_hd:parse_list(VariantKey), 3229 [case Inner of 3230 {with_params, InnerList, #{}} -> 3231 NumMembers = length(InnerList), 3232 [case Item of 3233 {with_params, {token, Value}, #{}} -> Value; 3234 {with_params, {string, Value}, #{}} -> Value 3235 end || Item <- InnerList] 3236 end || Inner <- List]. 3237 3238 -ifdef(TEST). 3239 parse_variant_key_test_() -> 3240 Tests = [ 3241 {<<"(en)">>, 1, [[<<"en">>]]}, 3242 {<<"(gzip fr)">>, 2, [[<<"gzip">>, <<"fr">>]]}, 3243 {<<"(gzip fr), (\"identity\" fr)">>, 2, [[<<"gzip">>, <<"fr">>], [<<"identity">>, <<"fr">>]]}, 3244 {<<"(\"gzip \" fr)">>, 2, [[<<"gzip ">>, <<"fr">>]]}, 3245 {<<"(en br)">>, 2, [[<<"en">>, <<"br">>]]}, 3246 {<<"(\"0\")">>, 1, [[<<"0">>]]}, 3247 {<<"(silver), (\"bronze\")">>, 1, [[<<"silver">>], [<<"bronze">>]]}, 3248 {<<"(some_person)">>, 1, [[<<"some_person">>]]}, 3249 {<<"(gold europe)">>, 2, [[<<"gold">>, <<"europe">>]]} 3250 ], 3251 [{V, fun() -> R = parse_variant_key(V, N) end} || {V, N, R} <- Tests]. 3252 3253 parse_variant_key_error_test_() -> 3254 Tests = [ 3255 {<<"(gzip fr), (identity fr), (br fr oops)">>, 2} 3256 ], 3257 [{V, fun() -> {'EXIT', _} = (catch parse_variant_key(V, N)) end} || {V, N} <- Tests]. 3258 -endif. 3259 3260 -spec variant_key([[binary()]]) -> iolist(). 3261 %% We assume that the lists are of correct length. 3262 variant_key(VariantKeys) -> 3263 cow_http_struct_hd:list([ 3264 {with_params, [ 3265 {with_params, {string, Value}, #{}} 3266 || Value <- InnerList], #{}} 3267 || InnerList <- VariantKeys]). 3268 3269 -ifdef(TEST). 3270 variant_key_identity_test_() -> 3271 Tests = [ 3272 {1, [[<<"en">>]]}, 3273 {2, [[<<"gzip">>, <<"fr">>]]}, 3274 {2, [[<<"gzip">>, <<"fr">>], [<<"identity">>, <<"fr">>]]}, 3275 {2, [[<<"gzip ">>, <<"fr">>]]}, 3276 {2, [[<<"en">>, <<"br">>]]}, 3277 {1, [[<<"0">>]]}, 3278 {1, [[<<"silver">>], [<<"bronze">>]]}, 3279 {1, [[<<"some_person">>]]}, 3280 {2, [[<<"gold">>, <<"europe">>]]} 3281 ], 3282 [{lists:flatten(io_lib:format("~p", [V])), 3283 fun() -> V = parse_variant_key(iolist_to_binary(variant_key(V)), N) end} || {N, V} <- Tests]. 3284 -endif. 3285 3286 %% Variants-06 (draft) header. 3287 3288 -spec parse_variants(binary()) -> [{binary(), [binary()]}]. 3289 parse_variants(Variants) -> 3290 {Dict0, Order} = cow_http_struct_hd:parse_dictionary(Variants), 3291 Dict = maps:map(fun(_, {with_params, List, #{}}) -> 3292 [case Item of 3293 {with_params, {token, Value}, #{}} -> Value; 3294 {with_params, {string, Value}, #{}} -> Value 3295 end || Item <- List] 3296 end, Dict0), 3297 [{Key, maps:get(Key, Dict)} || Key <- Order]. 3298 3299 -ifdef(TEST). 3300 parse_variants_test_() -> 3301 Tests = [ 3302 {<<"accept-language=(de en jp)">>, [{<<"accept-language">>, [<<"de">>, <<"en">>, <<"jp">>]}]}, 3303 {<<"accept-encoding=(gzip)">>, [{<<"accept-encoding">>, [<<"gzip">>]}]}, 3304 {<<"accept-encoding=()">>, [{<<"accept-encoding">>, []}]}, 3305 {<<"accept-encoding=(gzip br), accept-language=(en fr)">>, [ 3306 {<<"accept-encoding">>, [<<"gzip">>, <<"br">>]}, 3307 {<<"accept-language">>, [<<"en">>, <<"fr">>]} 3308 ]}, 3309 {<<"accept-language=(en fr de), accept-encoding=(gzip br)">>, [ 3310 {<<"accept-language">>, [<<"en">>, <<"fr">>, <<"de">>]}, 3311 {<<"accept-encoding">>, [<<"gzip">>, <<"br">>]} 3312 ]} 3313 ], 3314 [{V, fun() -> R = parse_variants(V) end} || {V, R} <- Tests]. 3315 -endif. 3316 3317 -spec variants([{binary(), [binary()]}]) -> iolist(). 3318 variants(Variants) -> 3319 cow_http_struct_hd:dictionary([ 3320 {Key, {with_params, [ 3321 {with_params, {string, Value}, #{}} 3322 || Value <- List], #{}}} 3323 || {Key, List} <- Variants]). 3324 3325 -ifdef(TEST). 3326 variants_identity_test_() -> 3327 Tests = [ 3328 [{<<"accept-language">>, [<<"de">>, <<"en">>, <<"jp">>]}], 3329 [{<<"accept-encoding">>, [<<"gzip">>]}], 3330 [{<<"accept-encoding">>, []}], 3331 [ 3332 {<<"accept-encoding">>, [<<"gzip">>, <<"br">>]}, 3333 {<<"accept-language">>, [<<"en">>, <<"fr">>]} 3334 ], 3335 [ 3336 {<<"accept-language">>, [<<"en">>, <<"fr">>, <<"de">>]}, 3337 {<<"accept-encoding">>, [<<"gzip">>, <<"br">>]} 3338 ] 3339 ], 3340 [{lists:flatten(io_lib:format("~p", [V])), 3341 fun() -> V = parse_variants(iolist_to_binary(variants(V))) end} || V <- Tests]. 3342 -endif. 3343 3344 %% Vary header. 3345 3346 -spec parse_vary(binary()) -> '*' | [binary()]. 3347 parse_vary(<<"*">>) -> 3348 '*'; 3349 parse_vary(Vary) -> 3350 nonempty(token_ci_list(Vary, [])). 3351 3352 -ifdef(TEST). 3353 parse_vary_test_() -> 3354 Tests = [ 3355 {<<"*">>, '*'}, 3356 {<<"Accept-Encoding">>, [<<"accept-encoding">>]}, 3357 {<<"accept-encoding, accept-language">>, [<<"accept-encoding">>, <<"accept-language">>]} 3358 ], 3359 [{V, fun() -> R = parse_vary(V) end} || {V, R} <- Tests]. 3360 3361 parse_vary_error_test_() -> 3362 Tests = [ 3363 <<>> 3364 ], 3365 [{V, fun() -> {'EXIT', _} = (catch parse_vary(V)) end} || V <- Tests]. 3366 -endif. 3367 3368 %% WWW-Authenticate header. 3369 %% 3370 %% Unknown schemes are represented as the lowercase binary 3371 %% instead of an atom. Unlike with parse_authorization/1, 3372 %% we do not crash on unknown schemes. 3373 %% 3374 %% When parsing auth-params, we do not accept BWS characters around the "=". 3375 3376 -spec parse_www_authenticate(binary()) -> [{basic, binary()} 3377 | {bearer | digest | binary(), [{binary(), binary()}]}]. 3378 parse_www_authenticate(Authenticate) -> 3379 nonempty(www_auth_list(Authenticate, [])). 3380 3381 www_auth_list(<<>>, Acc) -> lists:reverse(Acc); 3382 www_auth_list(<< C, R/bits >>, Acc) when ?IS_WS_COMMA(C) -> www_auth_list(R, Acc); 3383 www_auth_list(<< C, R/bits >>, Acc) when ?IS_TOKEN(C) -> 3384 ?LOWER(www_auth_scheme, R, Acc, <<>>). 3385 3386 www_auth_basic_before_realm(<< C, R/bits >>, Acc) when ?IS_WS(C) -> www_auth_basic_before_realm(R, Acc); 3387 www_auth_basic_before_realm(<< "realm=\"", R/bits >>, Acc) -> www_auth_basic(R, Acc, <<>>). 3388 3389 www_auth_basic(<< $", R/bits >>, Acc, Realm) -> www_auth_list_sep(R, [{basic, Realm}|Acc]); 3390 www_auth_basic(<< $\\, C, R/bits >>, Acc, Realm) when ?IS_VCHAR_OBS(C) -> www_auth_basic(R, Acc, << Realm/binary, C >>); 3391 www_auth_basic(<< C, R/bits >>, Acc, Realm) when ?IS_VCHAR_OBS(C) -> www_auth_basic(R, Acc, << Realm/binary, C >>). 3392 3393 www_auth_scheme(<< C, R/bits >>, Acc, Scheme) when ?IS_WS(C) -> 3394 case Scheme of 3395 <<"basic">> -> www_auth_basic_before_realm(R, Acc); 3396 <<"bearer">> -> www_auth_params_list(R, Acc, bearer, []); 3397 <<"digest">> -> www_auth_params_list(R, Acc, digest, []); 3398 _ -> www_auth_params_list(R, Acc, Scheme, []) 3399 end; 3400 www_auth_scheme(<< C, R/bits >>, Acc, Scheme) when ?IS_TOKEN(C) -> 3401 ?LOWER(www_auth_scheme, R, Acc, Scheme). 3402 3403 www_auth_list_sep(<<>>, Acc) -> lists:reverse(Acc); 3404 www_auth_list_sep(<< C, R/bits >>, Acc) when ?IS_WS(C) -> www_auth_list_sep(R, Acc); 3405 www_auth_list_sep(<< $,, R/bits >>, Acc) -> www_auth_list(R, Acc). 3406 3407 www_auth_params_list(<<>>, Acc, Scheme, Params) -> 3408 lists:reverse([{Scheme, lists:reverse(nonempty(Params))}|Acc]); 3409 www_auth_params_list(<< C, R/bits >>, Acc, Scheme, Params) when ?IS_WS_COMMA(C) -> 3410 www_auth_params_list(R, Acc, Scheme, Params); 3411 www_auth_params_list(<< "algorithm=", C, R/bits >>, Acc, Scheme, Params) when ?IS_TOKEN(C) -> 3412 www_auth_token(R, Acc, Scheme, Params, <<"algorithm">>, << C >>); 3413 www_auth_params_list(<< "domain=\"", R/bits >>, Acc, Scheme, Params) -> 3414 www_auth_quoted(R, Acc, Scheme, Params, <<"domain">>, <<>>); 3415 www_auth_params_list(<< "error=\"", R/bits >>, Acc, Scheme, Params) -> 3416 www_auth_quoted(R, Acc, Scheme, Params, <<"error">>, <<>>); 3417 www_auth_params_list(<< "error_description=\"", R/bits >>, Acc, Scheme, Params) -> 3418 www_auth_quoted(R, Acc, Scheme, Params, <<"error_description">>, <<>>); 3419 www_auth_params_list(<< "error_uri=\"", R/bits >>, Acc, Scheme, Params) -> 3420 www_auth_quoted(R, Acc, Scheme, Params, <<"error_uri">>, <<>>); 3421 www_auth_params_list(<< "nonce=\"", R/bits >>, Acc, Scheme, Params) -> 3422 www_auth_quoted(R, Acc, Scheme, Params, <<"nonce">>, <<>>); 3423 www_auth_params_list(<< "opaque=\"", R/bits >>, Acc, Scheme, Params) -> 3424 www_auth_quoted(R, Acc, Scheme, Params, <<"opaque">>, <<>>); 3425 www_auth_params_list(<< "qop=\"", R/bits >>, Acc, Scheme, Params) -> 3426 www_auth_quoted(R, Acc, Scheme, Params, <<"qop">>, <<>>); 3427 www_auth_params_list(<< "realm=\"", R/bits >>, Acc, Scheme, Params) -> 3428 www_auth_quoted(R, Acc, Scheme, Params, <<"realm">>, <<>>); 3429 www_auth_params_list(<< "scope=\"", R/bits >>, Acc, Scheme, Params) -> 3430 www_auth_quoted(R, Acc, Scheme, Params, <<"scope">>, <<>>); 3431 www_auth_params_list(<< "stale=false", R/bits >>, Acc, Scheme, Params) -> 3432 www_auth_params_list_sep(R, Acc, Scheme, [{<<"stale">>, <<"false">>}|Params]); 3433 www_auth_params_list(<< "stale=true", R/bits >>, Acc, Scheme, Params) -> 3434 www_auth_params_list_sep(R, Acc, Scheme, [{<<"stale">>, <<"true">>}|Params]); 3435 www_auth_params_list(<< C, R/bits >>, Acc, Scheme, Params) when ?IS_TOKEN(C) -> 3436 ?LOWER(www_auth_param, R, Acc, Scheme, Params, <<>>). 3437 3438 www_auth_param(<< $=, $", R/bits >>, Acc, Scheme, Params, K) -> 3439 www_auth_quoted(R, Acc, Scheme, Params, K, <<>>); 3440 www_auth_param(<< $=, C, R/bits >>, Acc, Scheme, Params, K) when ?IS_TOKEN(C) -> 3441 www_auth_token(R, Acc, Scheme, Params, K, << C >>); 3442 www_auth_param(<< C, R/bits >>, Acc, Scheme, Params, K) when ?IS_TOKEN(C) -> 3443 ?LOWER(www_auth_param, R, Acc, Scheme, Params, K); 3444 www_auth_param(R, Acc, Scheme, Params, NewScheme) -> 3445 www_auth_scheme(R, [{Scheme, lists:reverse(Params)}|Acc], NewScheme). 3446 3447 www_auth_token(<< C, R/bits >>, Acc, Scheme, Params, K, V) when ?IS_TOKEN(C) -> 3448 www_auth_token(R, Acc, Scheme, Params, K, << V/binary, C >>); 3449 www_auth_token(R, Acc, Scheme, Params, K, V) -> 3450 www_auth_params_list_sep(R, Acc, Scheme, [{K, V}|Params]). 3451 3452 www_auth_quoted(<< $", R/bits >>, Acc, Scheme, Params, K, V) -> 3453 www_auth_params_list_sep(R, Acc, Scheme, [{K, V}|Params]); 3454 www_auth_quoted(<< $\\, C, R/bits >>, Acc, Scheme, Params, K, V) when ?IS_VCHAR_OBS(C) -> 3455 www_auth_quoted(R, Acc, Scheme, Params, K, << V/binary, C >>); 3456 www_auth_quoted(<< C, R/bits >>, Acc, Scheme, Params, K, V) when ?IS_VCHAR_OBS(C) -> 3457 www_auth_quoted(R, Acc, Scheme, Params, K, << V/binary, C >>). 3458 3459 www_auth_params_list_sep(<<>>, Acc, Scheme, Params) -> 3460 lists:reverse([{Scheme, lists:reverse(Params)}|Acc]); 3461 www_auth_params_list_sep(<< C, R/bits >>, Acc, Scheme, Params) when ?IS_WS(C) -> 3462 www_auth_params_list_sep(R, Acc, Scheme, Params); 3463 www_auth_params_list_sep(<< $,, R/bits >>, Acc, Scheme, Params) -> 3464 www_auth_params_list_after_sep(R, Acc, Scheme, Params). 3465 3466 www_auth_params_list_after_sep(<<>>, Acc, Scheme, Params) -> 3467 lists:reverse([{Scheme, lists:reverse(Params)}|Acc]); 3468 www_auth_params_list_after_sep(<< C, R/bits >>, Acc, Scheme, Params) when ?IS_WS_COMMA(C) -> 3469 www_auth_params_list_after_sep(R, Acc, Scheme, Params); 3470 www_auth_params_list_after_sep(R, Acc, Scheme, Params) -> 3471 www_auth_params_list(R, Acc, Scheme, Params). 3472 3473 -ifdef(TEST). 3474 parse_www_authenticate_test_() -> 3475 Tests = [ 3476 {<<"Newauth realm=\"apps\", type=1, title=\"Login to \\\"apps\\\"\", Basic realm=\"simple\"">>, 3477 [{<<"newauth">>, [ 3478 {<<"realm">>, <<"apps">>}, 3479 {<<"type">>, <<"1">>}, 3480 {<<"title">>, <<"Login to \"apps\"">>}]}, 3481 {basic, <<"simple">>}]}, 3482 %% Same test, different order. 3483 {<<"Basic realm=\"simple\", Newauth realm=\"apps\", type=1, title=\"Login to \\\"apps\\\"\"">>, 3484 [{basic, <<"simple">>}, 3485 {<<"newauth">>, [ 3486 {<<"realm">>, <<"apps">>}, 3487 {<<"type">>, <<"1">>}, 3488 {<<"title">>, <<"Login to \"apps\"">>}]}]}, 3489 {<<"Bearer realm=\"example\"">>, 3490 [{bearer, [{<<"realm">>, <<"example">>}]}]}, 3491 {<<"Bearer realm=\"example\", error=\"invalid_token\", error_description=\"The access token expired\"">>, 3492 [{bearer, [ 3493 {<<"realm">>, <<"example">>}, 3494 {<<"error">>, <<"invalid_token">>}, 3495 {<<"error_description">>, <<"The access token expired">>} 3496 ]}]}, 3497 {<<"Basic realm=\"WallyWorld\"">>, 3498 [{basic, <<"WallyWorld">>}]}, 3499 {<<"Digest realm=\"testrealm@host.com\", qop=\"auth,auth-int\", " 3500 "nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\", " 3501 "opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"">>, 3502 [{digest, [ 3503 {<<"realm">>, <<"testrealm@host.com">>}, 3504 {<<"qop">>, <<"auth,auth-int">>}, 3505 {<<"nonce">>, <<"dcd98b7102dd2f0e8b11d0f600bfb0c093">>}, 3506 {<<"opaque">>, <<"5ccc069c403ebaf9f0171e9517f40e41">>} 3507 ]}]} 3508 ], 3509 [{V, fun() -> R = parse_www_authenticate(V) end} || {V, R} <- Tests]. 3510 3511 parse_www_authenticate_error_test_() -> 3512 Tests = [ 3513 <<>> 3514 ], 3515 [{V, fun() -> {'EXIT', _} = (catch parse_www_authenticate(V)) end} || V <- Tests]. 3516 3517 horse_parse_www_authenticate() -> 3518 horse:repeat(200000, 3519 parse_www_authenticate(<<"Newauth realm=\"apps\", type=1, title=\"Login to \\\"apps\\\"\", Basic realm=\"simple\"">>) 3520 ). 3521 -endif. 3522 3523 %% X-Forwarded-For header. 3524 %% 3525 %% This header has no specification but *looks like* it is 3526 %% a list of tokens. 3527 %% 3528 %% This header is deprecated in favor of the Forwarded header. 3529 3530 -spec parse_x_forwarded_for(binary()) -> [binary()]. 3531 parse_x_forwarded_for(XForwardedFor) -> 3532 nonempty(nodeid_list(XForwardedFor, [])). 3533 3534 -define(IS_NODEID_TOKEN(C), 3535 ?IS_ALPHA(C) or ?IS_DIGIT(C) 3536 or (C =:= $:) or (C =:= $.) or (C =:= $_) 3537 or (C =:= $-) or (C =:= $[) or (C =:= $])). 3538 3539 nodeid_list(<<>>, Acc) -> lists:reverse(Acc); 3540 nodeid_list(<<C, R/bits>>, Acc) when ?IS_WS_COMMA(C) -> nodeid_list(R, Acc); 3541 nodeid_list(<<C, R/bits>>, Acc) when ?IS_NODEID_TOKEN(C) -> nodeid(R, Acc, <<C>>). 3542 3543 nodeid(<<C, R/bits>>, Acc, T) when ?IS_NODEID_TOKEN(C) -> nodeid(R, Acc, <<T/binary, C>>); 3544 nodeid(R, Acc, T) -> nodeid_list_sep(R, [T|Acc]). 3545 3546 nodeid_list_sep(<<>>, Acc) -> lists:reverse(Acc); 3547 nodeid_list_sep(<<C, R/bits>>, Acc) when ?IS_WS(C) -> nodeid_list_sep(R, Acc); 3548 nodeid_list_sep(<<$,, R/bits>>, Acc) -> nodeid_list(R, Acc). 3549 3550 -ifdef(TEST). 3551 parse_x_forwarded_for_test_() -> 3552 Tests = [ 3553 {<<"client, proxy1, proxy2">>, 3554 [<<"client">>, <<"proxy1">>, <<"proxy2">>]}, 3555 {<<"128.138.243.150, unknown, 192.52.106.30">>, 3556 [<<"128.138.243.150">>, <<"unknown">>, <<"192.52.106.30">>]}, 3557 %% Examples from Mozilla DN. 3558 {<<"2001:db8:85a3:8d3:1319:8a2e:370:7348">>, 3559 [<<"2001:db8:85a3:8d3:1319:8a2e:370:7348">>]}, 3560 {<<"203.0.113.195">>, 3561 [<<"203.0.113.195">>]}, 3562 {<<"203.0.113.195, 70.41.3.18, 150.172.238.178">>, 3563 [<<"203.0.113.195">>, <<"70.41.3.18">>, <<"150.172.238.178">>]}, 3564 %% Examples from RFC7239 modified for x-forwarded-for. 3565 {<<"[2001:db8:cafe::17]:4711">>, 3566 [<<"[2001:db8:cafe::17]:4711">>]}, 3567 {<<"192.0.2.43, 198.51.100.17">>, 3568 [<<"192.0.2.43">>, <<"198.51.100.17">>]}, 3569 {<<"_hidden">>, 3570 [<<"_hidden">>]}, 3571 {<<"192.0.2.43,[2001:db8:cafe::17],unknown">>, 3572 [<<"192.0.2.43">>, <<"[2001:db8:cafe::17]">>, <<"unknown">>]}, 3573 {<<"192.0.2.43, [2001:db8:cafe::17], unknown">>, 3574 [<<"192.0.2.43">>, <<"[2001:db8:cafe::17]">>, <<"unknown">>]}, 3575 {<<"192.0.2.43, 2001:db8:cafe::17">>, 3576 [<<"192.0.2.43">>, <<"2001:db8:cafe::17">>]}, 3577 {<<"192.0.2.43, [2001:db8:cafe::17]">>, 3578 [<<"192.0.2.43">>, <<"[2001:db8:cafe::17]">>]} 3579 ], 3580 [{V, fun() -> R = parse_x_forwarded_for(V) end} || {V, R} <- Tests]. 3581 3582 parse_x_forwarded_for_error_test_() -> 3583 Tests = [ 3584 <<>> 3585 ], 3586 [{V, fun() -> {'EXIT', _} = (catch parse_x_forwarded_for(V)) end} || V <- Tests]. 3587 -endif. 3588 3589 %% Internal. 3590 3591 %% Only return if the list is not empty. 3592 nonempty(L) when L =/= [] -> L. 3593 3594 %% Parse a list of case sensitive tokens. 3595 token_list(<<>>, Acc) -> lists:reverse(Acc); 3596 token_list(<< C, R/bits >>, Acc) when ?IS_WS_COMMA(C) -> token_list(R, Acc); 3597 token_list(<< C, R/bits >>, Acc) when ?IS_TOKEN(C) -> token(R, Acc, << C >>). 3598 3599 token(<< C, R/bits >>, Acc, T) when ?IS_TOKEN(C) -> token(R, Acc, << T/binary, C >>); 3600 token(R, Acc, T) -> token_list_sep(R, [T|Acc]). 3601 3602 token_list_sep(<<>>, Acc) -> lists:reverse(Acc); 3603 token_list_sep(<< C, R/bits >>, Acc) when ?IS_WS(C) -> token_list_sep(R, Acc); 3604 token_list_sep(<< $,, R/bits >>, Acc) -> token_list(R, Acc). 3605 3606 %% Parse a list of case insensitive tokens. 3607 token_ci_list(<<>>, Acc) -> lists:reverse(Acc); 3608 token_ci_list(<< C, R/bits >>, Acc) when ?IS_WS_COMMA(C) -> token_ci_list(R, Acc); 3609 token_ci_list(<< C, R/bits >>, Acc) when ?IS_TOKEN(C) -> ?LOWER(token_ci, R, Acc, <<>>). 3610 3611 token_ci(<< C, R/bits >>, Acc, T) when ?IS_TOKEN(C) -> ?LOWER(token_ci, R, Acc, T); 3612 token_ci(R, Acc, T) -> token_ci_list_sep(R, [T|Acc]). 3613 3614 token_ci_list_sep(<<>>, Acc) -> lists:reverse(Acc); 3615 token_ci_list_sep(<< C, R/bits >>, Acc) when ?IS_WS(C) -> token_ci_list_sep(R, Acc); 3616 token_ci_list_sep(<< $,, R/bits >>, Acc) -> token_ci_list(R, Acc). 3617 3618 join_token_list([]) -> []; 3619 join_token_list([H|T]) -> join_token_list(T, [H]). 3620 3621 join_token_list([], Acc) -> lists:reverse(Acc); 3622 join_token_list([H|T], Acc) -> join_token_list(T, [H,<<", ">>|Acc]).