diff options
author | Loïc Hoguin <[email protected]> | 2011-10-05 03:17:13 +0200 |
---|---|---|
committer | Loïc Hoguin <[email protected]> | 2011-10-05 13:32:20 +0200 |
commit | bf5c2717bc49d82f6415536c7ff0be2e1d8361a5 (patch) | |
tree | b5bbbf1e3e25315438c5afc1d21cf017c6e2225b /src/cowboy_http.erl | |
parent | 9a775cce3c2cdab064dd79df29914296cf642a8d (diff) | |
download | cowboy-bf5c2717bc49d82f6415536c7ff0be2e1d8361a5.tar.gz cowboy-bf5c2717bc49d82f6415536c7ff0be2e1d8361a5.tar.bz2 cowboy-bf5c2717bc49d82f6415536c7ff0be2e1d8361a5.zip |
Parse 'Connection' headers as a list of tokens
Replaces the 'Connection' interpretation in cowboy_http_protocol
from raw value to the parsed value, looking for a single token
matching close/keep-alive instead of the whole raw value (which
could contain more than one token, for example with Firefox 6+
using websocket).
Introduce the functions cowboy_http_req:parse_header/2 and /3
to semantically parse the header values and return a proper
Erlang term.
Diffstat (limited to 'src/cowboy_http.erl')
-rw-r--r-- | src/cowboy_http.erl | 124 |
1 files changed, 124 insertions, 0 deletions
diff --git a/src/cowboy_http.erl b/src/cowboy_http.erl new file mode 100644 index 0000000..8d60f82 --- /dev/null +++ b/src/cowboy_http.erl @@ -0,0 +1,124 @@ +%% Copyright (c) 2011, Loïc Hoguin <[email protected]> +%% +%% 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: +%% <blockquote>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.</blockquote> +-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. |