Fix problem with pipelined http requests arriving in single packet

This fixes github issue ejabberd#835
This commit is contained in:
Paweł Chmielowski 2015-11-12 17:23:39 +01:00
parent 55a92c2983
commit 3ee5195b7a
1 changed files with 64 additions and 58 deletions

View File

@ -287,16 +287,18 @@ process_header(State, Data) ->
HostProvided), HostProvided),
State2 = State#state{request_host = Host, State2 = State#state{request_host = Host,
request_port = Port, request_tp = TP}, request_port = Port, request_tp = TP},
Out = process_request(State2), {State3, Out} = process_request(State2),
send_text(State2, Out), send_text(State3, Out),
case State2#state.request_keepalive of case State3#state.request_keepalive of
true -> true ->
#state{sockmod = SockMod, socket = Socket, #state{sockmod = SockMod, socket = Socket,
trail = State3#state.trail,
options = State#state.options, options = State#state.options,
default_host = State#state.default_host, default_host = State#state.default_host,
request_handlers = State#state.request_handlers}; request_handlers = State#state.request_handlers};
_ -> _ ->
#state{end_of_request = true, #state{end_of_request = true,
trail = State3#state.trail,
options = State#state.options, options = State#state.options,
default_host = State#state.default_host, default_host = State#state.default_host,
request_handlers = State#state.request_handlers} request_handlers = State#state.request_handlers}
@ -366,20 +368,20 @@ process(Handlers, Request, Socket, SockMod, Trail) ->
end. end.
extract_path_query(#state{request_method = Method, extract_path_query(#state{request_method = Method,
request_path = {abs_path, Path}}) request_path = {abs_path, Path}} = State)
when Method =:= 'GET' orelse when Method =:= 'GET' orelse
Method =:= 'HEAD' orelse Method =:= 'HEAD' orelse
Method =:= 'DELETE' orelse Method =:= 'OPTIONS' -> Method =:= 'DELETE' orelse Method =:= 'OPTIONS' ->
case catch url_decode_q_split(Path) of case catch url_decode_q_split(Path) of
{'EXIT', _} -> false; {'EXIT', _} -> {State, false};
{NPath, Query} -> {NPath, Query} ->
LPath = normalize_path([NPE LPath = normalize_path([NPE
|| NPE <- str:tokens(path_decode(NPath), <<"/">>)]), || NPE <- str:tokens(path_decode(NPath), <<"/">>)]),
LQuery = case catch parse_urlencoded(Query) of LQuery = case catch parse_urlencoded(Query) of
{'EXIT', _Reason} -> []; {'EXIT', _Reason} -> [];
LQ -> LQ LQ -> LQ
end, end,
{LPath, LQuery, <<"">>} {State, {LPath, LQuery, <<"">>}}
end; end;
extract_path_query(#state{request_method = Method, extract_path_query(#state{request_method = Method,
request_path = {abs_path, Path}, request_path = {abs_path, Path},
@ -388,21 +390,21 @@ extract_path_query(#state{request_method = Method,
socket = _Socket} = State) socket = _Socket} = State)
when (Method =:= 'POST' orelse Method =:= 'PUT') andalso when (Method =:= 'POST' orelse Method =:= 'PUT') andalso
is_integer(Len) -> is_integer(Len) ->
Data = recv_data(State, Len), {NewState, Data} = recv_data(State, Len),
?DEBUG("client data: ~p~n", [Data]), ?DEBUG("client data: ~p~n", [Data]),
case catch url_decode_q_split(Path) of case catch url_decode_q_split(Path) of
{'EXIT', _} -> false; {'EXIT', _} -> {NewState, false};
{NPath, _Query} -> {NPath, _Query} ->
LPath = normalize_path([NPE LPath = normalize_path([NPE
|| NPE <- str:tokens(path_decode(NPath), <<"/">>)]), || NPE <- str:tokens(path_decode(NPath), <<"/">>)]),
LQuery = case catch parse_urlencoded(Data) of LQuery = case catch parse_urlencoded(Data) of
{'EXIT', _Reason} -> []; {'EXIT', _Reason} -> [];
LQ -> LQ LQ -> LQ
end, end,
{LPath, LQuery, Data} {NewState, {LPath, LQuery, Data}}
end; end;
extract_path_query(_State) -> extract_path_query(State) ->
false. {State, false}.
process_request(#state{request_method = Method, process_request(#state{request_method = Method,
request_auth = Auth, request_auth = Auth,
@ -417,9 +419,9 @@ process_request(#state{request_method = Method,
request_handlers = RequestHandlers, request_handlers = RequestHandlers,
trail = Trail} = State) -> trail = Trail} = State) ->
case extract_path_query(State) of case extract_path_query(State) of
false -> {State2, false} ->
make_bad_request(State); {State2, make_bad_request(State)};
{LPath, LQuery, Data} -> {State2, {LPath, LQuery, Data}} ->
PeerName = PeerName =
case SockMod of case SockMod of
gen_tcp -> gen_tcp ->
@ -445,23 +447,24 @@ process_request(#state{request_method = Method,
opts = Options, opts = Options,
headers = RequestHeaders, headers = RequestHeaders,
ip = IP}, ip = IP},
case process(RequestHandlers, Request, Socket, SockMod, Trail) of Res = case process(RequestHandlers, Request, Socket, SockMod, Trail) of
El when is_record(El, xmlel) -> El when is_record(El, xmlel) ->
make_xhtml_output(State, 200, [], El); make_xhtml_output(State, 200, [], El);
{Status, Headers, El} {Status, Headers, El}
when is_record(El, xmlel) -> when is_record(El, xmlel) ->
make_xhtml_output(State, Status, Headers, El); make_xhtml_output(State, Status, Headers, El);
Output when is_binary(Output) or is_list(Output) -> Output when is_binary(Output) or is_list(Output) ->
make_text_output(State, 200, [], Output); make_text_output(State, 200, [], Output);
{Status, Headers, Output} {Status, Headers, Output}
when is_binary(Output) or is_list(Output) -> when is_binary(Output) or is_list(Output) ->
make_text_output(State, Status, Headers, Output); make_text_output(State, Status, Headers, Output);
{Status, Reason, Headers, Output} {Status, Reason, Headers, Output}
when is_binary(Output) or is_list(Output) -> when is_binary(Output) or is_list(Output) ->
make_text_output(State, Status, Reason, Headers, Output); make_text_output(State, Status, Reason, Headers, Output);
_ -> _ ->
none none
end end,
{State2, Res}
end. end.
make_bad_request(State) -> make_bad_request(State) ->
@ -501,21 +504,24 @@ is_ipchain_trusted(UserIPs, TrustedIPs) ->
recv_data(State, Len) -> recv_data(State, Len, <<>>). recv_data(State, Len) -> recv_data(State, Len, <<>>).
recv_data(_State, 0, Acc) -> (iolist_to_binary(Acc)); recv_data(State, 0, Acc) -> {State, Acc};
recv_data(#state{trail = Trail} = State, Len, <<>>) when byte_size(Trail) > Len ->
<<Data:Len/binary, Rest/binary>> = Trail,
{State#state{trail = Rest}, Data};
recv_data(State, Len, Acc) -> recv_data(State, Len, Acc) ->
case State#state.trail of case State#state.trail of
<<>> -> <<>> ->
case (State#state.sockmod):recv(State#state.socket, Len, case (State#state.sockmod):recv(State#state.socket, Len,
300000) 300000)
of of
{ok, Data} -> {ok, Data} ->
recv_data(State, Len - byte_size(Data), <<Acc/binary, Data/binary>>); recv_data(State, Len - byte_size(Data), <<Acc/binary, Data/binary>>);
_ -> <<"">> _ -> <<"">>
end; end;
_ -> _ ->
Trail = (State#state.trail), Trail = (State#state.trail),
recv_data(State#state{trail = <<>>}, recv_data(State#state{trail = <<>>},
Len - byte_size(Trail), <<Acc/binary, Trail/binary>>) Len - byte_size(Trail), <<Acc/binary, Trail/binary>>)
end. end.
make_xhtml_output(State, Status, Headers, XHTML) -> make_xhtml_output(State, Status, Headers, XHTML) ->