Browse Source

Reorganize cow_http_hd

Loïc Hoguin 5 years ago
parent
commit
8aa74cb77a
1 changed files with 258 additions and 266 deletions
  1. 258 266
      src/cow_http_hd.erl

+ 258 - 266
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.