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:
parent
ef0cf5d3d7
commit
cf6d67ceed
@ -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) ->
|
||||||
|
@ -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 }]),
|
||||||
|
Loading…
Reference in New Issue
Block a user