25
1
mirror of https://github.com/processone/ejabberd.git synced 2024-11-02 15:27:09 +01:00

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.
This commit is contained in:
Paweł Chmielowski 2012-04-11 17:59:57 +02:00
parent ef0cf5d3d7
commit cf6d67ceed
2 changed files with 27 additions and 25 deletions

View File

@ -182,7 +182,7 @@ receive_headers(#state{trail=Trail} = State) ->
Data = SockMod:recv(Socket, 0, 300000), Data = SockMod:recv(Socket, 0, 300000),
case State#state.sockmod of case State#state.sockmod of
gen_tcp -> gen_tcp ->
NewState = process_header(State, Data), NewState = process_header(State, Data, true),
case NewState#state.end_of_request of case NewState#state.end_of_request of
true -> true ->
ok; ok;
@ -207,7 +207,7 @@ parse_headers(#state{request_method = Method, trail = Data} = State) ->
end, end,
case decode_packet(PktType, Data) of case decode_packet(PktType, Data) of
{ok, Pkt, Rest} -> {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 case NewState#state.end_of_request of
true -> true ->
ok; ok;
@ -220,7 +220,7 @@ parse_headers(#state{request_method = Method, trail = Data} = State) ->
ok ok
end. end.
process_header(State, Data) -> process_header(State, Data, Normalize) ->
SockMod = State#state.sockmod, SockMod = State#state.sockmod,
Socket = State#state.socket, Socket = State#state.socket,
case Data of case Data of
@ -267,6 +267,8 @@ process_header(State, Data) ->
{ok, {http_header, _, 'Host'=Name, _, Host}} -> {ok, {http_header, _, 'Host'=Name, _, Host}} ->
State#state{request_host = Host, State#state{request_host = Host,
request_headers=add_header(Name, Host, State)}; 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}} -> {ok, {http_header, _, Name, _, Value}} ->
State#state{request_headers=add_header(Name, Value, State)}; State#state{request_headers=add_header(Name, Value, State)};
{ok, http_eoh} when State#state.request_host == undefined -> {ok, http_eoh} when State#state.request_host == undefined ->
@ -1117,14 +1119,28 @@ tolower(C) when C >= $A andalso C =< $Z ->
tolower(C) -> tolower(C) ->
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) ->
parse_header_line(Line, "", true). case parse_header_line(Line, "", true) of
{ok, Name, Rest} ->
parse_header_line("", _, _) ->
bad_request;
parse_header_line(":" ++ Rest, Name, _) ->
encode_header(lists:reverse(Name), Rest); encode_header(lists:reverse(Name), Rest);
_ ->
bad_request
end.
parse_header_line("", Name, _) ->
{eol, Name};
parse_header_line(":" ++ Rest, Name, _) ->
{ok, Name, Rest};
parse_header_line("-" ++ Rest, Name, _) -> parse_header_line("-" ++ Rest, Name, _) ->
parse_header_line(Rest, "-" ++ Name, true); parse_header_line(Rest, "-" ++ Name, true);
parse_header_line([C | Rest], Name, true) -> parse_header_line([C | Rest], Name, true) ->

View File

@ -54,7 +54,7 @@ check(_Path, Headers)->
% If origins not set, access is open. % If origins not set, access is open.
is_acceptable(#ws{origin=Origin, protocol=Protocol, is_acceptable(#ws{origin=Origin, protocol=Protocol,
headers = Headers, acceptable_origins = Origins, auth_module=undefined})-> 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 case {(Origins == []) or lists:member(Origin, Origins), ClientProtocol, Protocol } of
{false, _, _} -> {false, _, _} ->
?INFO_MSG("client does not come from authorized origin", []), ?INFO_MSG("client does not come from authorized origin", []),
@ -136,7 +136,7 @@ check_websocket({'draft-hybi', 8} = Vsn, Headers) ->
% set required headers % set required headers
RequiredHeaders = [ RequiredHeaders = [
{'Upgrade', "websocket"}, {'Connection', ignore}, {'Host', ignore}, {'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 % check for headers existance
case check_headers(Headers, RequiredHeaders) of case check_headers(Headers, RequiredHeaders) of
@ -150,7 +150,7 @@ check_websocket({'draft-hybi', 13} = Vsn, Headers) ->
% set required headers % set required headers
RequiredHeaders = [ RequiredHeaders = [
{'Upgrade', "websocket"}, {'Connection', ignore}, {'Host', ignore}, {'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 % check for headers existance
case check_headers(Headers, RequiredHeaders) of case check_headers(Headers, RequiredHeaders) of
@ -161,26 +161,12 @@ check_websocket({'draft-hybi', 13} = Vsn, Headers) ->
end; end;
check_websocket(_Vsn, _Headers) -> false. % not implemented 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}, ..] % Function: true | [{RequiredTag, RequiredVal}, ..]
% Description: Check if headers correspond to headers requirements. % Description: Check if headers correspond to headers requirements.
check_headers(Headers, RequiredHeaders) -> check_headers(Headers, RequiredHeaders) ->
F = fun({Tag, Val}) -> F = fun({Tag, Val}) ->
% see if the required Tag is in the Headers % 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 false -> true; % header not found, keep in list
{_, HVal} -> {_, HVal} ->
%?DEBUG("check: ~p", [{Tag, HVal,Val }]), %?DEBUG("check: ~p", [{Tag, HVal,Val }]),