%% Copyright (c) 2011, Loïc Hoguin %% %% Permission to use, copy, modify, and/or distribute this software for any %% purpose with or without fee is hereby granted, provided that the above %% copyright notice and this permission notice appear in all copies. %% %% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES %% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF %% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR %% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES %% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN %% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -module(cowboy_http). %% Parsing. -export([parse_tokens_list/1]). %% Interpretation. -export([connection_to_atom/1]). -include("include/http.hrl"). -include_lib("eunit/include/eunit.hrl"). %% Parsing. %% @doc Parse a list of tokens, as is often found in HTTP headers. %% %% From the RFC: %%
Wherever this construct is used, null elements are allowed, %% but do not contribute to the count of elements present. %% That is, "(element), , (element) " is permitted, but counts %% as only two elements. Therefore, where at least one element is required, %% at least one non-null element MUST be present.
-spec parse_tokens_list(binary()) -> [binary()] | {error, badarg}. parse_tokens_list(Value) -> case parse_tokens_list(Value, ws_or_sep, <<>>, []) of {error, badarg} -> {error, badarg}; L when length(L) =:= 0 -> {error, badarg}; L -> lists:reverse(L) end. -spec parse_tokens_list(binary(), token | ws | ws_or_sep, binary(), [binary()]) -> [binary()] | {error, badarg}. parse_tokens_list(<<>>, token, Token, Acc) -> [Token|Acc]; parse_tokens_list(<< C, Rest/bits >>, token, Token, Acc) when C =:= $\s; C =:= $\t -> parse_tokens_list(Rest, ws, <<>>, [Token|Acc]); parse_tokens_list(<< $,, Rest/bits >>, token, Token, Acc) -> parse_tokens_list(Rest, ws_or_sep, <<>>, [Token|Acc]); parse_tokens_list(<< C, Rest/bits >>, token, Token, Acc) -> parse_tokens_list(Rest, token, << Token/binary, C >>, Acc); parse_tokens_list(<< C, Rest/bits >>, ws, <<>>, Acc) when C =:= $\s; C =:= $\t -> parse_tokens_list(Rest, ws, <<>>, Acc); parse_tokens_list(<< $,, Rest/bits >>, ws, <<>>, Acc) -> parse_tokens_list(Rest, ws_or_sep, <<>>, Acc); parse_tokens_list(<<>>, ws_or_sep, <<>>, Acc) -> Acc; parse_tokens_list(<< C, Rest/bits >>, ws_or_sep, <<>>, Acc) when C =:= $\s; C =:= $\t -> parse_tokens_list(Rest, ws_or_sep, <<>>, Acc); parse_tokens_list(<< $,, Rest/bits >>, ws_or_sep, <<>>, Acc) -> parse_tokens_list(Rest, ws_or_sep, <<>>, Acc); parse_tokens_list(<< C, Rest/bits >>, ws_or_sep, <<>>, Acc) -> parse_tokens_list(Rest, token, << C >>, Acc); parse_tokens_list(_Value, _State, _Token, _Acc) -> {error, badarg}. %% Interpretation. %% @doc Walk through a tokens list and return whether %% the connection is keepalive or closed. -spec connection_to_atom([binary()]) -> keepalive | close. connection_to_atom([]) -> keepalive; connection_to_atom([<<"keep-alive">>|_Tail]) -> keepalive; connection_to_atom([<<"close">>|_Tail]) -> close; connection_to_atom([Connection|Tail]) -> case cowboy_bstr:to_lower(Connection) of <<"close">> -> close; <<"keep-alive">> -> keepalive; _Any -> connection_to_atom(Tail) end. %% Tests. -ifdef(TEST). parse_tokens_list_test_() -> %% {Value, Result} Tests = [ {<<>>, {error, badarg}}, {<<" ">>, {error, badarg}}, {<<" , ">>, {error, badarg}}, {<<",,,">>, {error, badarg}}, {<<"a b">>, {error, badarg}}, {<<"a , , , ">>, [<<"a">>]}, {<<" , , , a">>, [<<"a">>]}, {<<"a, , b">>, [<<"a">>, <<"b">>]}, {<<"close">>, [<<"close">>]}, {<<"keep-alive, upgrade">>, [<<"keep-alive">>, <<"upgrade">>]} ], [{V, fun() -> R = parse_tokens_list(V) end} || {V, R} <- Tests]. connection_to_atom_test_() -> %% {Tokens, Result} Tests = [ {[<<"close">>], close}, {[<<"ClOsE">>], close}, {[<<"Keep-Alive">>], keepalive}, {[<<"Keep-Alive">>, <<"Upgrade">>], keepalive} ], [{lists:flatten(io_lib:format("~p", [T])), fun() -> R = connection_to_atom(T) end} || {T, R} <- Tests]. -endif.