From cf6d67ceed5d627ae61f78c52853ae9ec10fdb29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 11 Apr 2012 17:59:57 +0200 Subject: [PATCH] Always normalize case of http header names and use that fact in websocket handler Native http parser code don't normalize header names when name is 19 or more characters long. In websocket header module headers like that are used, by having those headers in consistent form, code for finding header with mixed case can be dropped. --- src/web/ejabberd_http.erl | 30 +++++++++++++++++++++++------- src/web/ejabberd_websocket.erl | 22 ++++------------------ 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/src/web/ejabberd_http.erl b/src/web/ejabberd_http.erl index f26be0f1c..ffc78449f 100644 --- a/src/web/ejabberd_http.erl +++ b/src/web/ejabberd_http.erl @@ -182,7 +182,7 @@ receive_headers(#state{trail=Trail} = State) -> Data = SockMod:recv(Socket, 0, 300000), case State#state.sockmod of gen_tcp -> - NewState = process_header(State, Data), + NewState = process_header(State, Data, true), case NewState#state.end_of_request of true -> ok; @@ -207,7 +207,7 @@ parse_headers(#state{request_method = Method, trail = Data} = State) -> end, case decode_packet(PktType, Data) of {ok, Pkt, Rest} -> - NewState = process_header(State#state{trail = Rest}, {ok, Pkt}), + NewState = process_header(State#state{trail = Rest}, {ok, Pkt}, false), case NewState#state.end_of_request of true -> ok; @@ -220,7 +220,7 @@ parse_headers(#state{request_method = Method, trail = Data} = State) -> ok end. -process_header(State, Data) -> +process_header(State, Data, Normalize) -> SockMod = State#state.sockmod, Socket = State#state.socket, case Data of @@ -267,6 +267,8 @@ process_header(State, Data) -> {ok, {http_header, _, 'Host'=Name, _, Host}} -> State#state{request_host = Host, request_headers=add_header(Name, Host, State)}; + {ok, {http_header, _, Name, _, Value}} when is_list(Name) andalso Normalize -> + State#state{request_headers=add_header(normalize_header_name(Name), Value, State)}; {ok, {http_header, _, Name, _, Value}} -> State#state{request_headers=add_header(Name, Value, State)}; {ok, http_eoh} when State#state.request_host == undefined -> @@ -1117,14 +1119,28 @@ tolower(C) when C >= $A andalso C =< $Z -> tolower(C) -> C. +normalize_header_name(Name) -> + case parse_header_line(Name, "", true) of + {ok, RName, _} -> + lists:reverse(RName); + {eol, RName} -> + lists:reverse(RName) + end. + parse_header_line(Line) -> - parse_header_line(Line, "", true). + case parse_header_line(Line, "", true) of + {ok, Name, Rest} -> + encode_header(lists:reverse(Name), Rest); + _ -> + bad_request + end. -parse_header_line("", _, _) -> - bad_request; + +parse_header_line("", Name, _) -> + {eol, Name}; parse_header_line(":" ++ Rest, Name, _) -> - encode_header(lists:reverse(Name), Rest); + {ok, Name, Rest}; parse_header_line("-" ++ Rest, Name, _) -> parse_header_line(Rest, "-" ++ Name, true); parse_header_line([C | Rest], Name, true) -> diff --git a/src/web/ejabberd_websocket.erl b/src/web/ejabberd_websocket.erl index 83f8198fb..0d6af5977 100644 --- a/src/web/ejabberd_websocket.erl +++ b/src/web/ejabberd_websocket.erl @@ -54,7 +54,7 @@ check(_Path, Headers)-> % If origins not set, access is open. is_acceptable(#ws{origin=Origin, protocol=Protocol, headers = Headers, acceptable_origins = Origins, auth_module=undefined})-> - ClientProtocol = lists:keyfind("Sec-WebSocket-Protocol",1, Headers), + ClientProtocol = lists:keyfind("Sec-Websocket-Protocol",1, Headers), case {(Origins == []) or lists:member(Origin, Origins), ClientProtocol, Protocol } of {false, _, _} -> ?INFO_MSG("client does not come from authorized origin", []), @@ -136,7 +136,7 @@ check_websocket({'draft-hybi', 8} = Vsn, Headers) -> % set required headers RequiredHeaders = [ {'Upgrade', "websocket"}, {'Connection', ignore}, {'Host', ignore}, - {"Sec-WebSocket-Key", ignore}, {"Sec-WebSocket-Version", "8"} + {"Sec-Websocket-Key", ignore}, {"Sec-Websocket-Version", "8"} ], % check for headers existance case check_headers(Headers, RequiredHeaders) of @@ -150,7 +150,7 @@ check_websocket({'draft-hybi', 13} = Vsn, Headers) -> % set required headers RequiredHeaders = [ {'Upgrade', "websocket"}, {'Connection', ignore}, {'Host', ignore}, - {"Sec-WebSocket-Key", ignore}, {"Sec-WebSocket-Version", "13"} + {"Sec-Websocket-Key", ignore}, {"Sec-Websocket-Version", "13"} ], % check for headers existance case check_headers(Headers, RequiredHeaders) of @@ -161,26 +161,12 @@ check_websocket({'draft-hybi', 13} = Vsn, Headers) -> end; check_websocket(_Vsn, _Headers) -> false. % not implemented -tolower(T) when is_list(T) -> stringprep:tolower(T); -tolower(T) -> T. - -case_insensitive_keyfind(Name, List) when is_atom(Name) -> - lists:keyfind(Name, 1, List); -case_insensitive_keyfind(Name, []) -> - false; -case_insensitive_keyfind(Name, [{FName, Val}|T]) -> - FNameL = tolower(FName), - if - FNameL == Name -> {Name, Val}; - true -> case_insensitive_keyfind(Name, T) - end. - % Function: true | [{RequiredTag, RequiredVal}, ..] % Description: Check if headers correspond to headers requirements. check_headers(Headers, RequiredHeaders) -> F = fun({Tag, Val}) -> % see if the required Tag is in the Headers - case case_insensitive_keyfind(tolower(Tag), Headers) of + case lists:keyfind(Tag, 1, Headers) of false -> true; % header not found, keep in list {_, HVal} -> %?DEBUG("check: ~p", [{Tag, HVal,Val }]),