aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLoïc Hoguin <[email protected]>2019-11-25 17:29:24 +0100
committerLoïc Hoguin <[email protected]>2019-11-25 17:29:24 +0100
commit8aa74cb77a672b5b0ba528df77e8b9d0e53ee8a7 (patch)
tree3ca9a839c30a51a885fbc5f79110395d7b3648eb
parentd6f2a39ee0576719d39b37bb264b4e08c04571d2 (diff)
downloadcowlib-8aa74cb77a672b5b0ba528df77e8b9d0e53ee8a7.tar.gz
cowlib-8aa74cb77a672b5b0ba528df77e8b9d0e53ee8a7.tar.bz2
cowlib-8aa74cb77a672b5b0ba528df77e8b9d0e53ee8a7.zip
Reorganize cow_http_hd
-rw-r--r--src/cow_http_hd.erl524
1 files changed, 258 insertions, 266 deletions
diff --git a/src/cow_http_hd.erl b/src/cow_http_hd.erl
index 3189b01..2b28c41 100644
--- a/src/cow_http_hd.erl
+++ b/src/cow_http_hd.erl
@@ -14,7 +14,9 @@
-module(cow_http_hd).
-%% Parsing.
+%% Functions are ordered by header name, with the parse
+%% function before the build function.
+
-export([parse_accept/1]).
-export([parse_accept_charset/1]).
% @todo -export([parse_accept_datetime/1]). RFC7089
@@ -23,11 +25,17 @@
-export([parse_accept_language/1]).
-export([parse_accept_ranges/1]).
% @todo -export([parse_access_control_allow_credentials/1]). CORS
+-export([access_control_allow_credentials/0]).
% @todo -export([parse_access_control_allow_headers/1]). CORS
+-export([access_control_allow_headers/1]).
% @todo -export([parse_access_control_allow_methods/1]). CORS
+-export([access_control_allow_methods/1]).
% @todo -export([parse_access_control_allow_origin/1]). CORS
+-export([access_control_allow_origin/1]).
% @todo -export([parse_access_control_expose_headers/1]). CORS
+-export([access_control_expose_headers/1]).
% @todo -export([parse_access_control_max_age/1]). CORS
+-export([access_control_max_age/1]).
-export([parse_access_control_request_headers/1]).
-export([parse_access_control_request_method/1]).
-export([parse_age/1]).
@@ -102,7 +110,9 @@
% @todo -export([parse_user_agent/1]). RFC7231
% @todo -export([parse_variant_vary/1]). RFC2295
-export([parse_variant_key/2]).
+-export([variant_key/1]).
-export([parse_variants/1]).
+-export([variants/1]).
-export([parse_vary/1]).
% @todo -export([parse_via/1]). RFC7230
% @todo -export([parse_want_digest/1]). RFC3230
@@ -113,16 +123,6 @@
-export([parse_x_forwarded_for/1]).
% @todo -export([parse_x_frame_options/1]). RFC7034
-%% Building.
--export([access_control_allow_credentials/0]).
--export([access_control_allow_headers/1]).
--export([access_control_allow_methods/1]).
--export([access_control_allow_origin/1]).
--export([access_control_expose_headers/1]).
--export([access_control_max_age/1]).
--export([variant_key/1]).
--export([variants/1]).
-
-type etag() :: {weak | strong, binary()}.
-export_type([etag/0]).
@@ -216,9 +216,7 @@ qvalue_to_iodata(Q) when Q < 1000 -> [<<"0.">>, integer_to_binary(Q)];
qvalue_to_iodata(1000) -> <<"1">>.
-endif.
-%% Parsing.
-
-%% @doc Parse the Accept header.
+%% Accept header.
-spec parse_accept(binary()) -> [{media_type(), qvalue(), [binary() | {binary(), binary()}]}].
parse_accept(<<"*/*">>) ->
@@ -438,7 +436,7 @@ horse_parse_accept() ->
).
-endif.
-%% @doc Parse the Accept-Charset header.
+%% Accept-Charset header.
-spec parse_accept_charset(binary()) -> [{binary(), qvalue()}].
parse_accept_charset(Charset) ->
@@ -529,7 +527,7 @@ horse_parse_accept_charset() ->
).
-endif.
-%% @doc Parse the Accept-Encoding header.
+%% Accept-Encoding header.
-spec parse_accept_encoding(binary()) -> [{binary(), qvalue()}].
parse_accept_encoding(Encoding) ->
@@ -585,7 +583,7 @@ horse_parse_accept_encoding() ->
).
-endif.
-%% @doc Parse the Accept-Language header.
+%% Accept-Language header.
-spec parse_accept_language(binary()) -> [{binary(), qvalue()}].
parse_accept_language(LanguageRange) ->
@@ -702,7 +700,7 @@ horse_parse_accept_language() ->
).
-endif.
-%% @doc Parse the Accept-Ranges header.
+%% Accept-Ranges header.
-spec parse_accept_ranges(binary()) -> [binary()].
parse_accept_ranges(<<"none">>) -> [];
@@ -741,7 +739,148 @@ horse_parse_accept_ranges_other() ->
).
-endif.
-%% @doc Parse the Access-Control-Request-Headers header.
+%% Access-Control-Allow-Credentials header.
+
+-spec access_control_allow_credentials() -> iodata().
+access_control_allow_credentials() -> <<"true">>.
+
+%% Access-Control-Allow-Headers header.
+
+-spec access_control_allow_headers([binary()]) -> iodata().
+access_control_allow_headers(Headers) ->
+ join_token_list(nonempty(Headers)).
+
+-ifdef(TEST).
+access_control_allow_headers_test_() ->
+ Tests = [
+ {[<<"accept">>], <<"accept">>},
+ {[<<"accept">>, <<"authorization">>, <<"content-type">>], <<"accept, authorization, content-type">>}
+ ],
+ [{lists:flatten(io_lib:format("~p", [V])),
+ fun() -> R = iolist_to_binary(access_control_allow_headers(V)) end} || {V, R} <- Tests].
+
+access_control_allow_headers_error_test_() ->
+ Tests = [
+ []
+ ],
+ [{lists:flatten(io_lib:format("~p", [V])),
+ fun() -> {'EXIT', _} = (catch access_control_allow_headers(V)) end} || V <- Tests].
+
+horse_access_control_allow_headers() ->
+ horse:repeat(200000,
+ access_control_allow_headers([<<"accept">>, <<"authorization">>, <<"content-type">>])
+ ).
+-endif.
+
+%% Access-Control-Allow-Methods header.
+
+-spec access_control_allow_methods([binary()]) -> iodata().
+access_control_allow_methods(Methods) ->
+ join_token_list(nonempty(Methods)).
+
+-ifdef(TEST).
+access_control_allow_methods_test_() ->
+ Tests = [
+ {[<<"GET">>], <<"GET">>},
+ {[<<"GET">>, <<"POST">>, <<"DELETE">>], <<"GET, POST, DELETE">>}
+ ],
+ [{lists:flatten(io_lib:format("~p", [V])),
+ fun() -> R = iolist_to_binary(access_control_allow_methods(V)) end} || {V, R} <- Tests].
+
+access_control_allow_methods_error_test_() ->
+ Tests = [
+ []
+ ],
+ [{lists:flatten(io_lib:format("~p", [V])),
+ fun() -> {'EXIT', _} = (catch access_control_allow_methods(V)) end} || V <- Tests].
+
+horse_access_control_allow_methods() ->
+ horse:repeat(200000,
+ access_control_allow_methods([<<"GET">>, <<"POST">>, <<"DELETE">>])
+ ).
+-endif.
+
+%% Access-Control-Allow-Origin header.
+
+-spec access_control_allow_origin({binary(), binary(), 0..65535} | reference() | '*') -> iodata().
+access_control_allow_origin({Scheme, Host, Port}) ->
+ case default_port(Scheme) of
+ Port -> [Scheme, <<"://">>, Host];
+ _ -> [Scheme, <<"://">>, Host, <<":">>, integer_to_binary(Port)]
+ end;
+access_control_allow_origin('*') -> <<$*>>;
+access_control_allow_origin(Ref) when is_reference(Ref) -> <<"null">>.
+
+-ifdef(TEST).
+access_control_allow_origin_test_() ->
+ Tests = [
+ {{<<"http">>, <<"www.example.org">>, 8080}, <<"http://www.example.org:8080">>},
+ {{<<"http">>, <<"www.example.org">>, 80}, <<"http://www.example.org">>},
+ {{<<"http">>, <<"192.0.2.1">>, 8080}, <<"http://192.0.2.1:8080">>},
+ {{<<"http">>, <<"192.0.2.1">>, 80}, <<"http://192.0.2.1">>},
+ {{<<"http">>, <<"[2001:db8::1]">>, 8080}, <<"http://[2001:db8::1]:8080">>},
+ {{<<"http">>, <<"[2001:db8::1]">>, 80}, <<"http://[2001:db8::1]">>},
+ {{<<"http">>, <<"[::ffff:192.0.2.1]">>, 8080}, <<"http://[::ffff:192.0.2.1]:8080">>},
+ {{<<"http">>, <<"[::ffff:192.0.2.1]">>, 80}, <<"http://[::ffff:192.0.2.1]">>},
+ {make_ref(), <<"null">>},
+ {'*', <<$*>>}
+ ],
+ [{lists:flatten(io_lib:format("~p", [V])),
+ fun() -> R = iolist_to_binary(access_control_allow_origin(V)) end} || {V, R} <- Tests].
+
+horse_access_control_allow_origin() ->
+ horse:repeat(200000,
+ access_control_allow_origin({<<"http">>, <<"example.org">>, 8080})
+ ).
+-endif.
+
+%% Access-Control-Expose-Headers header.
+
+-spec access_control_expose_headers([binary()]) -> iodata().
+access_control_expose_headers(Headers) ->
+ join_token_list(nonempty(Headers)).
+
+-ifdef(TEST).
+access_control_expose_headers_test_() ->
+ Tests = [
+ {[<<"accept">>], <<"accept">>},
+ {[<<"accept">>, <<"authorization">>, <<"content-type">>], <<"accept, authorization, content-type">>}
+ ],
+ [{lists:flatten(io_lib:format("~p", [V])),
+ fun() -> R = iolist_to_binary(access_control_expose_headers(V)) end} || {V, R} <- Tests].
+
+access_control_expose_headers_error_test_() ->
+ Tests = [
+ []
+ ],
+ [{lists:flatten(io_lib:format("~p", [V])),
+ fun() -> {'EXIT', _} = (catch access_control_expose_headers(V)) end} || V <- Tests].
+
+horse_access_control_expose_headers() ->
+ horse:repeat(200000,
+ access_control_expose_headers([<<"accept">>, <<"authorization">>, <<"content-type">>])
+ ).
+-endif.
+
+%% Access-Control-Max-Age header.
+
+-spec access_control_max_age(non_neg_integer()) -> iodata().
+access_control_max_age(MaxAge) -> integer_to_binary(MaxAge).
+
+-ifdef(TEST).
+access_control_max_age_test_() ->
+ Tests = [
+ {0, <<"0">>},
+ {42, <<"42">>},
+ {69, <<"69">>},
+ {1337, <<"1337">>},
+ {3495, <<"3495">>},
+ {1234567890, <<"1234567890">>}
+ ],
+ [{V, fun() -> R = access_control_max_age(V) end} || {V, R} <- Tests].
+-endif.
+
+%% Access-Control-Request-Headers header.
-spec parse_access_control_request_headers(binary()) -> [binary()].
parse_access_control_request_headers(Headers) ->
@@ -778,7 +917,7 @@ horse_parse_access_control_request_headers() ->
).
-endif.
-%% @doc Parse the Access-Control-Request-Method header.
+%% Access-Control-Request-Method header.
-spec parse_access_control_request_method(binary()) -> binary().
parse_access_control_request_method(Method) ->
@@ -815,7 +954,7 @@ horse_parse_access_control_request_method() ->
).
-endif.
-%% @doc Parse the Age header.
+%% Age header.
-spec parse_age(binary()) -> non_neg_integer().
parse_age(Age) ->
@@ -844,7 +983,7 @@ parse_age_error_test_() ->
[{V, fun() -> {'EXIT', _} = (catch parse_age(V)) end} || V <- Tests].
-endif.
-%% @doc Parse the Allow header.
+%% Allow header.
-spec parse_allow(binary()) -> [binary()].
parse_allow(Allow) ->
@@ -879,7 +1018,7 @@ horse_parse_allow() ->
).
-endif.
-%% @doc Parse the Authorization header.
+%% Authorization header.
%%
%% We support Basic, Digest and Bearer schemes only.
%%
@@ -1007,7 +1146,7 @@ horse_parse_authorization_digest() ->
).
-endif.
-%% @doc Parse the Cache-Control header.
+%% Cache-Control header.
%%
%% In the fields list case, we do not support escaping, which shouldn't be needed anyway.
@@ -1157,7 +1296,7 @@ horse_parse_cache_control_fields() ->
).
-endif.
-%% @doc Parse the Connection header.
+%% Connection header.
-spec parse_connection(binary()) -> [binary()].
parse_connection(<<"close">>) ->
@@ -1209,7 +1348,7 @@ horse_parse_connection_keepalive_upgrade() ->
).
-endif.
-%% @doc Parse the Content-Encoding header.
+%% Content-Encoding header.
-spec parse_content_encoding(binary()) -> [binary()].
parse_content_encoding(ContentEncoding) ->
@@ -1234,7 +1373,7 @@ horse_parse_content_encoding() ->
).
-endif.
-%% @doc Parse the Content-Language header.
+%% Content-Language header.
%%
%% We do not support irregular deprecated tags that do not match the ABNF.
@@ -1520,7 +1659,7 @@ horse_parse_content_language() ->
).
-endif.
-%% @doc Parse the Content-Length header.
+%% Content-Length header.
-spec parse_content_length(binary()) -> non_neg_integer().
parse_content_length(ContentLength) ->
@@ -1567,7 +1706,7 @@ horse_parse_content_length_giga() ->
).
-endif.
-%% @doc Parse the Content-Range header.
+%% Content-Range header.
-spec parse_content_range(binary())
-> {bytes, non_neg_integer(), non_neg_integer(), non_neg_integer() | '*'}
@@ -1667,7 +1806,7 @@ horse_parse_content_range_other() ->
).
-endif.
-%% @doc Parse the Content-Type header.
+%% Content-Type header.
-spec parse_content_type(binary()) -> media_type().
parse_content_type(<< C, R/bits >>) when ?IS_TOKEN(C) ->
@@ -1775,7 +1914,7 @@ horse_parse_content_type() ->
).
-endif.
-%% @doc Parse the Date header.
+%% Date header.
-spec parse_date(binary()) -> calendar:datetime().
parse_date(Date) ->
@@ -1789,7 +1928,7 @@ parse_date_test_() ->
[{V, fun() -> R = parse_date(V) end} || {V, R} <- Tests].
-endif.
-%% @doc Parse the ETag header.
+%% ETag header.
-spec parse_etag(binary()) -> etag().
parse_etag(<< $W, $/, $", R/bits >>) ->
@@ -1846,7 +1985,7 @@ horse_parse_etag() ->
).
-endif.
-%% @doc Parse the Expect header.
+%% Expect header.
-spec parse_expect(binary()) -> continue.
parse_expect(<<"100-continue">>) ->
@@ -1894,7 +2033,7 @@ horse_parse_expect() ->
).
-endif.
-%% @doc Parse the Expires header.
+%% Expires header.
%%
%% Recipients must interpret invalid date formats as a date
%% in the past. The value "0" is commonly used.
@@ -1929,7 +2068,7 @@ horse_parse_expires_invalid() ->
).
-endif.
-%% @doc Parse the Host header.
+%% Host header.
%%
%% We only seek to have legal characters and separate the
%% host and port values. The number of segments in the host
@@ -2013,13 +2152,13 @@ horse_parse_host_ipv6_v4() ->
).
-endif.
-%% @doc Parse the HTTP2-Settings header.
+%% HTTP2-Settings header.
-spec parse_http2_settings(binary()) -> map().
parse_http2_settings(HTTP2Settings) ->
cow_http2:parse_settings_payload(base64:decode(HTTP2Settings)).
-%% @doc Parse the If-Match header.
+%% If-Match header.
-spec parse_if_match(binary()) -> '*' | [etag()].
parse_if_match(<<"*">>) ->
@@ -2071,7 +2210,7 @@ horse_parse_if_match() ->
).
-endif.
-%% @doc Parse the If-Modified-Since header.
+%% If-Modified-Since header.
-spec parse_if_modified_since(binary()) -> calendar:datetime().
parse_if_modified_since(IfModifiedSince) ->
@@ -2085,7 +2224,7 @@ parse_if_modified_since_test_() ->
[{V, fun() -> R = parse_if_modified_since(V) end} || {V, R} <- Tests].
-endif.
-%% @doc Parse the If-None-Match header.
+%% If-None-Match header.
-spec parse_if_none_match(binary()) -> '*' | [etag()].
parse_if_none_match(<<"*">>) ->
@@ -2118,7 +2257,7 @@ horse_parse_if_none_match() ->
).
-endif.
-%% @doc Parse the If-Range header.
+%% If-Range header.
-spec parse_if_range(binary()) -> etag() | calendar:datetime().
parse_if_range(<< $W, $/, $", R/bits >>) ->
@@ -2154,7 +2293,7 @@ horse_parse_if_range_date() ->
).
-endif.
-%% @doc Parse the If-Unmodified-Since header.
+%% If-Unmodified-Since header.
-spec parse_if_unmodified_since(binary()) -> calendar:datetime().
parse_if_unmodified_since(IfModifiedSince) ->
@@ -2168,7 +2307,7 @@ parse_if_unmodified_since_test_() ->
[{V, fun() -> R = parse_if_unmodified_since(V) end} || {V, R} <- Tests].
-endif.
-%% @doc Parse the Last-Modified header.
+%% Last-Modified header.
-spec parse_last_modified(binary()) -> calendar:datetime().
parse_last_modified(LastModified) ->
@@ -2182,13 +2321,13 @@ parse_last_modified_test_() ->
[{V, fun() -> R = parse_last_modified(V) end} || {V, R} <- Tests].
-endif.
-%% @doc Parse the Link header.
+%% Link header.
-spec parse_link(binary()) -> [cow_link:link()].
parse_link(Link) ->
cow_link:parse_link(Link).
-%% @doc Parse the Max-Forwards header.
+%% Max-Forwards header.
-spec parse_max_forwards(binary()) -> non_neg_integer().
parse_max_forwards(MaxForwards) ->
@@ -2223,7 +2362,7 @@ parse_max_forwards_error_test_() ->
[{V, fun() -> {'EXIT', _} = (catch parse_max_forwards(V)) end} || V <- Tests].
-endif.
-%% @doc Parse the Origin header.
+%% Origin header.
%% According to the RFC6454 we should generate
%% a fresh globally unique identifier and return that value if:
@@ -2365,7 +2504,7 @@ horse_parse_origin_null() ->
).
-endif.
-%% @doc Parse the Pragma header.
+%% Pragma header.
%%
%% Legacy header kept for backward compatibility with HTTP/1.0 caches.
%% Only the "no-cache" directive was ever specified, and only for
@@ -2378,7 +2517,7 @@ horse_parse_origin_null() ->
parse_pragma(<<"no-cache">>) -> no_cache;
parse_pragma(_) -> cache.
-%% @doc Parse the Proxy-Authenticate header.
+%% Proxy-Authenticate header.
%%
%% Alias of parse_www_authenticate/1 due to identical syntax.
@@ -2387,7 +2526,7 @@ parse_pragma(_) -> cache.
parse_proxy_authenticate(ProxyAuthenticate) ->
parse_www_authenticate(ProxyAuthenticate).
-%% @doc Parse the Proxy-Authorization header.
+%% Proxy-Authorization header.
%%
%% Alias of parse_authorization/1 due to identical syntax.
@@ -2398,7 +2537,7 @@ parse_proxy_authenticate(ProxyAuthenticate) ->
parse_proxy_authorization(ProxyAuthorization) ->
parse_authorization(ProxyAuthorization).
-%% @doc Parse the Range header.
+%% Range header.
-spec parse_range(binary())
-> {bytes, [{non_neg_integer(), non_neg_integer() | infinity} | neg_integer()]}
@@ -2522,7 +2661,7 @@ horse_parse_range_other() ->
).
-endif.
-%% @doc Parse the Retry-After header.
+%% Retry-After header.
-spec parse_retry_after(binary()) -> non_neg_integer() | calendar:datetime().
parse_retry_after(RetryAfter = << D, _/bits >>) when ?IS_DIGIT(D) ->
@@ -2557,7 +2696,7 @@ horse_parse_retry_after_delay_seconds() ->
).
-endif.
-%% @doc Dummy parsing function for the Sec-WebSocket-Accept header.
+%% Sec-WebSocket-Accept header.
%%
%% The argument is returned without any processing. This value is
%% expected to be matched directly by the client so no parsing is
@@ -2567,7 +2706,7 @@ horse_parse_retry_after_delay_seconds() ->
parse_sec_websocket_accept(SecWebSocketAccept) ->
SecWebSocketAccept.
-%% @doc Parse the Sec-WebSocket-Extensions request header.
+%% Sec-WebSocket-Extensions header.
-spec parse_sec_websocket_extensions(binary()) -> [{binary(), [binary() | {binary(), binary()}]}].
parse_sec_websocket_extensions(SecWebSocketExtensions) ->
@@ -2660,7 +2799,7 @@ horse_parse_sec_websocket_extensions() ->
).
-endif.
-%% @doc Dummy parsing function for the Sec-WebSocket-Key header.
+%% Sec-WebSocket-Key header.
%%
%% The argument is returned without any processing. This value is
%% expected to be prepended to a static value, the result of which
@@ -2671,7 +2810,7 @@ horse_parse_sec_websocket_extensions() ->
parse_sec_websocket_key(SecWebSocketKey) ->
SecWebSocketKey.
-%% @doc Parse the Sec-WebSocket-Protocol request header.
+%% Sec-WebSocket-Protocol request header.
-spec parse_sec_websocket_protocol_req(binary()) -> [binary()].
parse_sec_websocket_protocol_req(SecWebSocketProtocol) ->
@@ -2698,7 +2837,7 @@ horse_parse_sec_websocket_protocol_req() ->
).
-endif.
-%% @doc Parse the Sec-Websocket-Protocol response header.
+%% Sec-Websocket-Protocol response header.
-spec parse_sec_websocket_protocol_resp(binary()) -> binary().
parse_sec_websocket_protocol_resp(Protocol) ->
@@ -2732,7 +2871,7 @@ horse_parse_sec_websocket_protocol_resp() ->
).
-endif.
-%% @doc Parse the Sec-WebSocket-Version request header.
+%% Sec-WebSocket-Version request header.
-spec parse_sec_websocket_version_req(binary()) -> websocket_version().
parse_sec_websocket_version_req(SecWebSocketVersion) when byte_size(SecWebSocketVersion) < 4 ->
@@ -2774,7 +2913,7 @@ horse_parse_sec_websocket_version_req_255() ->
).
-endif.
-%% @doc Parse the Sec-WebSocket-Version response header.
+%% Sec-WebSocket-Version response header.
-spec parse_sec_websocket_version_resp(binary()) -> [websocket_version()].
parse_sec_websocket_version_resp(SecWebSocketVersion) ->
@@ -2825,7 +2964,7 @@ horse_parse_sec_websocket_version_resp() ->
).
-endif.
-%% @doc Parse the TE header.
+%% TE header.
%%
%% This function does not support parsing of transfer-parameter.
@@ -2922,7 +3061,7 @@ horse_parse_te() ->
).
-endif.
-%% @doc Parse the Trailer header.
+%% Trailer header.
-spec parse_trailer(binary()) -> [binary()].
parse_trailer(Trailer) ->
@@ -2947,7 +3086,7 @@ horse_parse_trailer() ->
).
-endif.
-%% @doc Parse the Transfer-Encoding header.
+%% Transfer-Encoding header.
%%
%% This function does not support parsing of transfer-parameter.
@@ -3001,7 +3140,7 @@ horse_parse_transfer_encoding_custom() ->
).
-endif.
-%% @doc Parse the Upgrade header.
+%% Upgrade header.
%%
%% It is unclear from the RFC whether the values here are
%% case sensitive.
@@ -3064,7 +3203,7 @@ parse_upgrade_error_test_() ->
|| V <- Tests].
-endif.
-%% @doc Parse the Variant-Key header.
+%% Variant-Key-06 (draft) header.
%%
%% The Variants header must be parsed first in order to know
%% the NumMembers argument as it is the number of members in
@@ -3104,7 +3243,33 @@ parse_variant_key_error_test_() ->
[{V, fun() -> {'EXIT', _} = (catch parse_variant_key(V, N)) end} || {V, N} <- Tests].
-endif.
-%% @doc Parse the Variants header.
+-spec variant_key([[binary()]]) -> iolist().
+%% We assume that the lists are of correct length.
+variant_key(VariantKeys) ->
+ cow_http_struct_hd:list([
+ {with_params, [
+ {with_params, {string, Value}, #{}}
+ || Value <- InnerList], #{}}
+ || InnerList <- VariantKeys]).
+
+-ifdef(TEST).
+variant_key_identity_test_() ->
+ Tests = [
+ {1, [[<<"en">>]]},
+ {2, [[<<"gzip">>, <<"fr">>]]},
+ {2, [[<<"gzip">>, <<"fr">>], [<<"identity">>, <<"fr">>]]},
+ {2, [[<<"gzip ">>, <<"fr">>]]},
+ {2, [[<<"en">>, <<"br">>]]},
+ {1, [[<<"0">>]]},
+ {1, [[<<"silver">>], [<<"bronze">>]]},
+ {1, [[<<"some_person">>]]},
+ {2, [[<<"gold">>, <<"europe">>]]}
+ ],
+ [{lists:flatten(io_lib:format("~p", [V])),
+ fun() -> V = parse_variant_key(iolist_to_binary(variant_key(V)), N) end} || {N, V} <- Tests].
+-endif.
+
+%% Variants-06 (draft) header.
-spec parse_variants(binary()) -> [{binary(), [binary()]}].
parse_variants(Variants) ->
@@ -3135,7 +3300,34 @@ parse_variants_test_() ->
[{V, fun() -> R = parse_variants(V) end} || {V, R} <- Tests].
-endif.
-%% @doc Parse the Vary header.
+-spec variants([{binary(), [binary()]}]) -> iolist().
+variants(Variants) ->
+ cow_http_struct_hd:dictionary([
+ {Key, {with_params, [
+ {with_params, {string, Value}, #{}}
+ || Value <- List], #{}}}
+ || {Key, List} <- Variants]).
+
+-ifdef(TEST).
+variants_identity_test_() ->
+ Tests = [
+ [{<<"accept-language">>, [<<"de">>, <<"en">>, <<"jp">>]}],
+ [{<<"accept-encoding">>, [<<"gzip">>]}],
+ [{<<"accept-encoding">>, []}],
+ [
+ {<<"accept-encoding">>, [<<"gzip">>, <<"br">>]},
+ {<<"accept-language">>, [<<"en">>, <<"fr">>]}
+ ],
+ [
+ {<<"accept-language">>, [<<"en">>, <<"fr">>, <<"de">>]},
+ {<<"accept-encoding">>, [<<"gzip">>, <<"br">>]}
+ ]
+ ],
+ [{lists:flatten(io_lib:format("~p", [V])),
+ fun() -> V = parse_variants(iolist_to_binary(variants(V))) end} || V <- Tests].
+-endif.
+
+%% Vary header.
-spec parse_vary(binary()) -> '*' | [binary()].
parse_vary(<<"*">>) ->
@@ -3159,7 +3351,7 @@ parse_vary_error_test_() ->
[{V, fun() -> {'EXIT', _} = (catch parse_vary(V)) end} || V <- Tests].
-endif.
-%% @doc Parse the WWW-Authenticate header.
+%% WWW-Authenticate header.
%%
%% Unknown schemes are represented as the lowercase binary
%% instead of an atom. Unlike with parse_authorization/1,
@@ -3314,7 +3506,7 @@ horse_parse_www_authenticate() ->
).
-endif.
-%% @doc Parse the X-Forwarded-For header.
+%% X-Forwarded-For header.
%%
%% This header has no specification but *looks like* it is
%% a list of tokens.
@@ -3380,206 +3572,6 @@ parse_x_forwarded_for_error_test_() ->
[{V, fun() -> {'EXIT', _} = (catch parse_x_forwarded_for(V)) end} || V <- Tests].
-endif.
-%% Building.
-
-%% @doc Build the Access-Control-Allow-Credentials header.
-
--spec access_control_allow_credentials() -> iodata().
-access_control_allow_credentials() -> <<"true">>.
-
-%% @doc Build the Access-Control-Allow-Headers header.
-
--spec access_control_allow_headers([binary()]) -> iodata().
-access_control_allow_headers(Headers) ->
- join_token_list(nonempty(Headers)).
-
--ifdef(TEST).
-access_control_allow_headers_test_() ->
- Tests = [
- {[<<"accept">>], <<"accept">>},
- {[<<"accept">>, <<"authorization">>, <<"content-type">>], <<"accept, authorization, content-type">>}
- ],
- [{lists:flatten(io_lib:format("~p", [V])),
- fun() -> R = iolist_to_binary(access_control_allow_headers(V)) end} || {V, R} <- Tests].
-
-access_control_allow_headers_error_test_() ->
- Tests = [
- []
- ],
- [{lists:flatten(io_lib:format("~p", [V])),
- fun() -> {'EXIT', _} = (catch access_control_allow_headers(V)) end} || V <- Tests].
-
-horse_access_control_allow_headers() ->
- horse:repeat(200000,
- access_control_allow_headers([<<"accept">>, <<"authorization">>, <<"content-type">>])
- ).
--endif.
-
-%% @doc Build the Access-Control-Allow-Methods header.
-
--spec access_control_allow_methods([binary()]) -> iodata().
-access_control_allow_methods(Methods) ->
- join_token_list(nonempty(Methods)).
-
--ifdef(TEST).
-access_control_allow_methods_test_() ->
- Tests = [
- {[<<"GET">>], <<"GET">>},
- {[<<"GET">>, <<"POST">>, <<"DELETE">>], <<"GET, POST, DELETE">>}
- ],
- [{lists:flatten(io_lib:format("~p", [V])),
- fun() -> R = iolist_to_binary(access_control_allow_methods(V)) end} || {V, R} <- Tests].
-
-access_control_allow_methods_error_test_() ->
- Tests = [
- []
- ],
- [{lists:flatten(io_lib:format("~p", [V])),
- fun() -> {'EXIT', _} = (catch access_control_allow_methods(V)) end} || V <- Tests].
-
-horse_access_control_allow_methods() ->
- horse:repeat(200000,
- access_control_allow_methods([<<"GET">>, <<"POST">>, <<"DELETE">>])
- ).
--endif.
-
-%% @doc Build the Access-Control-Allow-Origin header.
-
--spec access_control_allow_origin({binary(), binary(), 0..65535} | reference() | '*') -> iodata().
-access_control_allow_origin({Scheme, Host, Port}) ->
- case default_port(Scheme) of
- Port -> [Scheme, <<"://">>, Host];
- _ -> [Scheme, <<"://">>, Host, <<":">>, integer_to_binary(Port)]
- end;
-access_control_allow_origin('*') -> <<$*>>;
-access_control_allow_origin(Ref) when is_reference(Ref) -> <<"null">>.
-
--ifdef(TEST).
-access_control_allow_origin_test_() ->
- Tests = [
- {{<<"http">>, <<"www.example.org">>, 8080}, <<"http://www.example.org:8080">>},
- {{<<"http">>, <<"www.example.org">>, 80}, <<"http://www.example.org">>},
- {{<<"http">>, <<"192.0.2.1">>, 8080}, <<"http://192.0.2.1:8080">>},
- {{<<"http">>, <<"192.0.2.1">>, 80}, <<"http://192.0.2.1">>},
- {{<<"http">>, <<"[2001:db8::1]">>, 8080}, <<"http://[2001:db8::1]:8080">>},
- {{<<"http">>, <<"[2001:db8::1]">>, 80}, <<"http://[2001:db8::1]">>},
- {{<<"http">>, <<"[::ffff:192.0.2.1]">>, 8080}, <<"http://[::ffff:192.0.2.1]:8080">>},
- {{<<"http">>, <<"[::ffff:192.0.2.1]">>, 80}, <<"http://[::ffff:192.0.2.1]">>},
- {make_ref(), <<"null">>},
- {'*', <<$*>>}
- ],
- [{lists:flatten(io_lib:format("~p", [V])),
- fun() -> R = iolist_to_binary(access_control_allow_origin(V)) end} || {V, R} <- Tests].
-
-horse_access_control_allow_origin() ->
- horse:repeat(200000,
- access_control_allow_origin({<<"http">>, <<"example.org">>, 8080})
- ).
--endif.
-
-%% @doc Build the Access-Control-Expose-Headers header.
-
--spec access_control_expose_headers([binary()]) -> iodata().
-access_control_expose_headers(Headers) ->
- join_token_list(nonempty(Headers)).
-
--ifdef(TEST).
-access_control_expose_headers_test_() ->
- Tests = [
- {[<<"accept">>], <<"accept">>},
- {[<<"accept">>, <<"authorization">>, <<"content-type">>], <<"accept, authorization, content-type">>}
- ],
- [{lists:flatten(io_lib:format("~p", [V])),
- fun() -> R = iolist_to_binary(access_control_expose_headers(V)) end} || {V, R} <- Tests].
-
-access_control_expose_headers_error_test_() ->
- Tests = [
- []
- ],
- [{lists:flatten(io_lib:format("~p", [V])),
- fun() -> {'EXIT', _} = (catch access_control_expose_headers(V)) end} || V <- Tests].
-
-horse_access_control_expose_headers() ->
- horse:repeat(200000,
- access_control_expose_headers([<<"accept">>, <<"authorization">>, <<"content-type">>])
- ).
--endif.
-
-%% @doc Build the Access-Control-Max-Age header.
-
--spec access_control_max_age(non_neg_integer()) -> iodata().
-access_control_max_age(MaxAge) -> integer_to_binary(MaxAge).
-
--ifdef(TEST).
-access_control_max_age_test_() ->
- Tests = [
- {0, <<"0">>},
- {42, <<"42">>},
- {69, <<"69">>},
- {1337, <<"1337">>},
- {3495, <<"3495">>},
- {1234567890, <<"1234567890">>}
- ],
- [{V, fun() -> R = access_control_max_age(V) end} || {V, R} <- Tests].
--endif.
-
-%% @doc Build the Variant-Key-06 (draft) header.
-
--spec variant_key([[binary()]]) -> iolist().
-%% We assume that the lists are of correct length.
-variant_key(VariantKeys) ->
- cow_http_struct_hd:list([
- {with_params, [
- {with_params, {string, Value}, #{}}
- || Value <- InnerList], #{}}
- || InnerList <- VariantKeys]).
-
--ifdef(TEST).
-variant_key_identity_test_() ->
- Tests = [
- {1, [[<<"en">>]]},
- {2, [[<<"gzip">>, <<"fr">>]]},
- {2, [[<<"gzip">>, <<"fr">>], [<<"identity">>, <<"fr">>]]},
- {2, [[<<"gzip ">>, <<"fr">>]]},
- {2, [[<<"en">>, <<"br">>]]},
- {1, [[<<"0">>]]},
- {1, [[<<"silver">>], [<<"bronze">>]]},
- {1, [[<<"some_person">>]]},
- {2, [[<<"gold">>, <<"europe">>]]}
- ],
- [{lists:flatten(io_lib:format("~p", [V])),
- fun() -> V = parse_variant_key(iolist_to_binary(variant_key(V)), N) end} || {N, V} <- Tests].
--endif.
-
-%% @doc Build the Variants-06 (draft) header.
-
--spec variants([{binary(), [binary()]}]) -> iolist().
-variants(Variants) ->
- cow_http_struct_hd:dictionary([
- {Key, {with_params, [
- {with_params, {string, Value}, #{}}
- || Value <- List], #{}}}
- || {Key, List} <- Variants]).
-
--ifdef(TEST).
-variants_identity_test_() ->
- Tests = [
- [{<<"accept-language">>, [<<"de">>, <<"en">>, <<"jp">>]}],
- [{<<"accept-encoding">>, [<<"gzip">>]}],
- [{<<"accept-encoding">>, []}],
- [
- {<<"accept-encoding">>, [<<"gzip">>, <<"br">>]},
- {<<"accept-language">>, [<<"en">>, <<"fr">>]}
- ],
- [
- {<<"accept-language">>, [<<"en">>, <<"fr">>, <<"de">>]},
- {<<"accept-encoding">>, [<<"gzip">>, <<"br">>]}
- ]
- ],
- [{lists:flatten(io_lib:format("~p", [V])),
- fun() -> V = parse_variants(iolist_to_binary(variants(V))) end} || V <- Tests].
--endif.
-
%% Internal.
%% Only return if the list is not empty.