From c589922ebdc7cc6b843c0dbc9ec18a9b7f902586 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= Date: Thu, 20 Oct 2011 19:04:49 +0200 Subject: Parse Connection header tokens in a case-insensitive manner --- src/cowboy_http.erl | 49 ++++++++++++++++++++++++++----------------- src/cowboy_http_req.erl | 2 +- src/cowboy_http_websocket.erl | 2 +- 3 files changed, 32 insertions(+), 21 deletions(-) diff --git a/src/cowboy_http.erl b/src/cowboy_http.erl index ae73ba5..b05611b 100644 --- a/src/cowboy_http.erl +++ b/src/cowboy_http.erl @@ -16,7 +16,7 @@ -module(cowboy_http). %% Parsing. --export([list/2, nonempty_list/2, token/2]). +-export([list/2, nonempty_list/2, token/2, token_ci/2]). %% Interpretation. -export([connection_to_atom/1]). @@ -73,31 +73,47 @@ list_separator(<< C, Rest/bits >>, Fun) list_separator(_Data, _Fun) -> {error, badarg}. +%% @doc Parse a case-insensitive token. +%% +%% Changes all characters to lowercase. +-spec token_ci(binary(), fun()) -> any(). +token_ci(Data, Fun) -> + token(Data, Fun, ci). + %% @doc Parse a token. -spec token(binary(), fun()) -> any(). -token(<< C, Rest/bits >>, Fun) - when C =:= $\s; C =:= $\t -> - token(Rest, Fun); token(Data, Fun) -> - token(Data, Fun, <<>>). + token(Data, Fun, cs). --spec token(binary(), fun(), binary()) -> any(). -token(<<>>, Fun, Acc) -> +-spec token(binary(), fun(), ci | cs) -> any(). +token(<< C, Rest/bits >>, Fun, Case) + when C =:= $\s; C =:= $\t -> + token(Rest, Fun, Case); +token(Data, Fun, Case) -> + token(Data, Fun, Case, <<>>). + +-spec token(binary(), fun(), ci | cs, binary()) -> any(). +token(<<>>, Fun, _Case, Acc) -> Fun(<<>>, Acc); -token(Data = << C, _Rest/bits >>, Fun, Acc) +token(Data = << C, _Rest/bits >>, Fun, _Case, Acc) when C =:= $(; C =:= $); C =:= $<; C =:= $>; C =:= $@; C =:= $,; C =:= $;; C =:= $:; C =:= $\\; C =:= $"; C =:= $/; C =:= $[; C =:= $]; C =:= $?; C =:= $=; C =:= ${; C =:= $}; C =:= $\s; C =:= $\t; C < 32; C =:= 127 -> Fun(Data, Acc); -token(<< C, Rest/bits >>, Fun, Acc) -> - token(Rest, Fun, << Acc/binary, C >>). +token(<< C, Rest/bits >>, Fun, Case = ci, Acc) -> + C2 = cowboy_bstr:char_to_lower(C), + token(Rest, Fun, Case, << Acc/binary, C2 >>); +token(<< C, Rest/bits >>, Fun, Case, Acc) -> + token(Rest, Fun, Case, << Acc/binary, C >>). %% Interpretation. %% @doc Walk through a tokens list and return whether %% the connection is keepalive or closed. +%% +%% The connection token is expected to be lower-case. -spec connection_to_atom([binary()]) -> keepalive | close. connection_to_atom([]) -> keepalive; @@ -105,12 +121,8 @@ 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. +connection_to_atom([_Any|Tail]) -> + connection_to_atom(Tail). %% Tests. @@ -136,9 +148,8 @@ connection_to_atom_test_() -> %% {Tokens, Result} Tests = [ {[<<"close">>], close}, - {[<<"ClOsE">>], close}, - {[<<"Keep-Alive">>], keepalive}, - {[<<"Keep-Alive">>, <<"Upgrade">>], keepalive} + {[<<"keep-alive">>], keepalive}, + {[<<"keep-alive">>, <<"upgrade">>], keepalive} ], [{lists:flatten(io_lib:format("~p", [T])), fun() -> R = connection_to_atom(T) end} || {T, R} <- Tests]. diff --git a/src/cowboy_http_req.erl b/src/cowboy_http_req.erl index 06f1e6b..539c7f0 100644 --- a/src/cowboy_http_req.erl +++ b/src/cowboy_http_req.erl @@ -221,7 +221,7 @@ parse_header(Name, Req=#http_req{p_headers=PHeaders}, Default) case header(Name, Req) of {undefined, Req2} -> {tokens, Default, Req2}; {Value, Req2} -> - case cowboy_http:nonempty_list(Value, fun cowboy_http:token/2) of + case cowboy_http:nonempty_list(Value, fun cowboy_http:token_ci/2) of {error, badarg} -> {error, badarg}; P -> diff --git a/src/cowboy_http_websocket.erl b/src/cowboy_http_websocket.erl index 8562061..81cd7cd 100644 --- a/src/cowboy_http_websocket.erl +++ b/src/cowboy_http_websocket.erl @@ -79,7 +79,7 @@ upgrade(ListenerPid, Handler, Opts, Req) -> websocket_upgrade(State, Req) -> {tokens, ConnTokens, Req2} = cowboy_http_req:parse_header('Connection', Req), - true = lists:member(<<"Upgrade">>, ConnTokens), + true = lists:member(<<"upgrade">>, ConnTokens), {WS, Req3} = cowboy_http_req:header('Upgrade', Req2), <<"websocket">> = cowboy_bstr:to_lower(WS), {Version, Req4} = cowboy_http_req:header(<<"Sec-Websocket-Version">>, Req3), -- cgit v1.2.3