24
1
mirror of https://github.com/processone/ejabberd.git synced 2024-06-06 21:37:17 +02:00

Parse correctly https request split into multiple packets

This fixes case when SockMod:recv() calls returns only part of first line
of http request (GET/POST/OPTION/HEAD line). Before that change request
like that (and if keep-alive was active, all further request) were dropped.

This fixes EJAB-1537.
This commit is contained in:
Paweł Chmielowski 2012-04-06 16:22:08 +02:00
parent 09c75af15c
commit 5f82846732

View File

@ -66,7 +66,7 @@
request_headers = [], request_headers = [],
end_of_request = false, end_of_request = false,
default_host, default_host,
trail = "" trail = <<>>
}). }).
@ -168,12 +168,12 @@ send_text(State, Text) ->
exit(normal) exit(normal)
end. end.
receive_headers(State) -> receive_headers(#state{trail=Trail} = State) ->
SockMod = State#state.sockmod, SockMod = State#state.sockmod,
Socket = State#state.socket, Socket = State#state.socket,
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),
case NewState#state.end_of_request of case NewState#state.end_of_request of
true -> true ->
@ -181,31 +181,35 @@ receive_headers(State) ->
_ -> _ ->
receive_headers(NewState) receive_headers(NewState)
end; end;
_ -> _ ->
case Data of case Data of
{ok, Binary} -> {ok, D} ->
{Request, Trail} = parse_request( parse_headers(State#state{trail = <<Trail/binary, D/binary>>});
State, {error, _} ->
State#state.trail ++ binary_to_list(Binary)), ok
State1 = State#state{trail = Trail}, end
NewState = lists:foldl( end.
fun(D, S) ->
case S#state.end_of_request of parse_headers(#state{trail = <<>>} = State) ->
true -> receive_headers(State);
S; parse_headers(#state{request_method = Method, trail = Data} = State) ->
_ -> PktType = case Method of
process_header(S, D) undefined -> http;
end _ -> httph
end, State1, Request), end,
case NewState#state.end_of_request of case decode_packet(PktType, Data) of
true -> {ok, Pkt, Rest} ->
ok; NewState = process_header(State#state{trail = Rest}, {ok, Pkt}),
_ -> case NewState#state.end_of_request of
receive_headers(NewState) true ->
end; ok;
_ -> _ ->
ok parse_headers(NewState)
end end;
{more, _} ->
receive_headers(State#state{trail = Data});
_ ->
ok
end. end.
process_header(State, Data) -> process_header(State, Data) ->
@ -519,16 +523,16 @@ recv_data(_State, 0, Acc) ->
binary_to_list(list_to_binary(Acc)); binary_to_list(list_to_binary(Acc));
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, 300000) of case (State#state.sockmod):recv(State#state.socket, Len, 300000) of
{ok, Data} -> {ok, Data} ->
recv_data(State, Len - size(Data), [Acc | [Data]]); recv_data(State, Len - size(Data), [Acc | [Data]]);
_ -> _ ->
"" ""
end; end;
_ -> _ ->
Trail = State#state.trail, Trail = binary_to_list(State#state.trail),
recv_data(State#state{trail = ""}, Len - length(Trail), [Acc | Trail]) recv_data(State#state{trail = <<>>}, Len - length(Trail), [Acc | Trail])
end. end.
@ -809,7 +813,7 @@ parse_auth(_) ->
decode_base64([]) -> decode_base64([]) ->
[]; [];
decode_base64([Sextet1,Sextet2,$=,$=|Rest]) -> decode_base64([Sextet1,Sextet2,$=,$=|Rest]) ->
Bits2x6= Bits2x6=
(d(Sextet1) bsl 18) bor (d(Sextet1) bsl 18) bor
(d(Sextet2) bsl 12), (d(Sextet2) bsl 12),
Octet1=Bits2x6 bsr 16, Octet1=Bits2x6 bsr 16,
@ -919,42 +923,29 @@ old_integer_to_hex(I) when I>=16 ->
% The following code is mostly taken from yaws_ssl.erl % The following code is mostly taken from yaws_ssl.erl
parse_request(State, Data) -> decode_packet(_, <<"\r\n", Rest/binary>>) ->
case Data of {ok, http_eoh, Rest};
[] -> decode_packet(Type, Data) ->
{[], []}; case binary:match(Data, <<"\r\n">>) of
_ -> {Start, _Len} ->
?DEBUG("GOT ssl data ~p~n", [Data]), <<LineB:Start/binary, _:2/binary, Rest/binary>> = Data,
{R, Trail} = case State#state.request_method of Line = binary_to_list(LineB),
undefined -> Result = case Type of
{R1, Trail1} = get_req(Data), http ->
?DEBUG("Parsed request ~p~n", [R1]), parse_req(Line);
{[R1], Trail1}; httph ->
_ -> parse_line(Line)
{[], Data} end,
end, case Result of
{H, Trail2} = get_headers(Trail), {ok, H} ->
{R ++ H, Trail2} {ok, H, Rest};
Err ->
{error, Err}
end;
_ ->
{more, undefined}
end. end.
get_req("\r\n\r\n" ++ _) ->
bad_request;
get_req("\r\n" ++ Data) ->
get_req(Data);
get_req(Data) ->
{FirstLine, Trail} = lists:splitwith(fun not_eol/1, Data),
R = parse_req(FirstLine),
{R, Trail}.
not_eol($\r)->
false;
not_eol($\n) ->
false;
not_eol(_) ->
true.
get_word(Line)-> get_word(Line)->
{Word, T} = lists:splitwith(fun(X)-> X /= $\ end, Line), {Word, T} = lists:splitwith(fun(X)-> X /= $\ end, Line),
{Word, lists:dropwhile(fun(X) -> X == $\ end, T)}. {Word, lists:dropwhile(fun(X) -> X == $\ end, T)}.
@ -1025,68 +1016,54 @@ parse_req(Line) ->
end. end.
get_headers(Tail) ->
get_headers([], Tail).
get_headers(H, Tail) ->
case get_line(Tail) of
{incomplete, Tail2} ->
{H, Tail2};
{line, Line, Tail2} ->
get_headers(H ++ parse_line(Line), Tail2);
{lastline, Line, Tail2} ->
{H ++ parse_line(Line) ++ [{ok, http_eoh}], Tail2}
end.
parse_line("Connection:" ++ Con) -> parse_line("Connection:" ++ Con) ->
[{ok, {http_header, undefined, 'Connection', undefined, strip_spaces(Con)}}]; {ok, {http_header, undefined, 'Connection', undefined, strip_spaces(Con)}};
parse_line("Host:" ++ Con) -> parse_line("Host:" ++ Con) ->
[{ok, {http_header, undefined, 'Host', undefined, strip_spaces(Con)}}]; {ok, {http_header, undefined, 'Host', undefined, strip_spaces(Con)}};
parse_line("Accept:" ++ Con) -> parse_line("Accept:" ++ Con) ->
[{ok, {http_header, undefined, 'Accept', undefined, strip_spaces(Con)}}]; {ok, {http_header, undefined, 'Accept', undefined, strip_spaces(Con)}};
parse_line("If-Modified-Since:" ++ Con) -> parse_line("If-Modified-Since:" ++ Con) ->
[{ok, {http_header, undefined, 'If-Modified-Since', undefined, strip_spaces(Con)}}]; {ok, {http_header, undefined, 'If-Modified-Since', undefined, strip_spaces(Con)}};
parse_line("If-Match:" ++ Con) -> parse_line("If-Match:" ++ Con) ->
[{ok, {http_header, undefined, 'If-Match', undefined, strip_spaces(Con)}}]; {ok, {http_header, undefined, 'If-Match', undefined, strip_spaces(Con)}};
parse_line("If-None-Match:" ++ Con) -> parse_line("If-None-Match:" ++ Con) ->
[{ok, {http_header, undefined, 'If-None-Match', undefined, strip_spaces(Con)}}]; {ok, {http_header, undefined, 'If-None-Match', undefined, strip_spaces(Con)}};
parse_line("If-Range:" ++ Con) -> parse_line("If-Range:" ++ Con) ->
[{ok, {http_header, undefined, 'If-Range', undefined, strip_spaces(Con)}}]; {ok, {http_header, undefined, 'If-Range', undefined, strip_spaces(Con)}};
parse_line("If-Unmodified-Since:" ++ Con) -> parse_line("If-Unmodified-Since:" ++ Con) ->
[{ok, {http_header, undefined, 'If-Unmodified-Since', undefined, strip_spaces(Con)}}]; {ok, {http_header, undefined, 'If-Unmodified-Since', undefined, strip_spaces(Con)}};
parse_line("Range:" ++ Con) -> parse_line("Range:" ++ Con) ->
[{ok, {http_header, undefined, 'Range', undefined, strip_spaces(Con)}}]; {ok, {http_header, undefined, 'Range', undefined, strip_spaces(Con)}};
parse_line("User-Agent:" ++ Con) -> parse_line("User-Agent:" ++ Con) ->
[{ok, {http_header, undefined, 'User-Agent', undefined, strip_spaces(Con)}}]; {ok, {http_header, undefined, 'User-Agent', undefined, strip_spaces(Con)}};
parse_line("Accept-Ranges:" ++ Con) -> parse_line("Accept-Ranges:" ++ Con) ->
[{ok, {http_header, undefined, 'Accept-Ranges', undefined, strip_spaces(Con)}}]; {ok, {http_header, undefined, 'Accept-Ranges', undefined, strip_spaces(Con)}};
parse_line("Authorization:" ++ Con) -> parse_line("Authorization:" ++ Con) ->
[{ok, {http_header, undefined, 'Authorization', undefined, strip_spaces(Con)}}]; {ok, {http_header, undefined, 'Authorization', undefined, strip_spaces(Con)}};
parse_line("Keep-Alive:" ++ Con) -> parse_line("Keep-Alive:" ++ Con) ->
[{ok, {http_header, undefined, 'Keep-Alive', undefined, strip_spaces(Con)}}]; {ok, {http_header, undefined, 'Keep-Alive', undefined, strip_spaces(Con)}};
parse_line("Referer:" ++ Con) -> parse_line("Referer:" ++ Con) ->
[{ok, {http_header, undefined, 'Referer', undefined, strip_spaces(Con)}}]; {ok, {http_header, undefined, 'Referer', undefined, strip_spaces(Con)}};
parse_line("Content-type:"++Con) -> parse_line("Content-type:"++Con) ->
[{ok, {http_header, undefined, 'Content-Type', undefined, strip_spaces(Con)}}]; {ok, {http_header, undefined, 'Content-Type', undefined, strip_spaces(Con)}};
parse_line("Content-Type:"++Con) -> parse_line("Content-Type:"++Con) ->
[{ok, {http_header, undefined, 'Content-Type', undefined, strip_spaces(Con)}}]; {ok, {http_header, undefined, 'Content-Type', undefined, strip_spaces(Con)}};
parse_line("Content-Length:"++Con) -> parse_line("Content-Length:"++Con) ->
[{ok, {http_header, undefined, 'Content-Length', undefined, strip_spaces(Con)}}]; {ok, {http_header, undefined, 'Content-Length', undefined, strip_spaces(Con)}};
parse_line("Content-length:"++Con) -> parse_line("Content-length:"++Con) ->
[{ok, {http_header, undefined, 'Content-Length', undefined, strip_spaces(Con)}}]; {ok, {http_header, undefined, 'Content-Length', undefined, strip_spaces(Con)}};
parse_line("Cookie:"++Con) -> parse_line("Cookie:"++Con) ->
[{ok, {http_header, undefined, 'Cookie', undefined, strip_spaces(Con)}}]; {ok, {http_header, undefined, 'Cookie', undefined, strip_spaces(Con)}};
parse_line("Accept-Language:"++Con) -> parse_line("Accept-Language:"++Con) ->
[{ok, {http_header, undefined, 'Accept-Language', undefined, strip_spaces(Con)}}]; {ok, {http_header, undefined, 'Accept-Language', undefined, strip_spaces(Con)}};
parse_line("Accept-Encoding:"++Con) -> parse_line("Accept-Encoding:"++Con) ->
[{ok, {http_header, undefined, 'Accept-Encoding', undefined, strip_spaces(Con)}}]; {ok, {http_header, undefined, 'Accept-Encoding', undefined, strip_spaces(Con)}};
parse_line(S) -> parse_line(S) ->
case lists:splitwith(fun(C)->C /= $: end, S) of case lists:splitwith(fun(C)->C /= $: end, S) of
{Name, [$:|Val]} -> {Name, [$:|Val]} ->
[{ok, {http_header, undefined, Name, undefined, strip_spaces(Val)}}]; {ok, {http_header, undefined, Name, undefined, strip_spaces(Val)}};
_ -> _ ->
[] bad_request
end. end.
@ -1121,30 +1098,3 @@ drop_spaces(YS=[X|XS]) ->
false -> false ->
YS YS
end. end.
is_nb_space(X) ->
lists:member(X, [$\s, $\t]).
% ret: {line, Line, Trail} | {lastline, Line, Trail}
get_line(L) ->
get_line(L, []).
get_line("\r\n\r\n" ++ Tail, Cur) ->
{lastline, lists:reverse(Cur), Tail};
get_line("\r\n" ++ Tail, Cur) ->
case Tail of
[] ->
{incomplete, lists:reverse(Cur) ++ "\r\n"};
_ ->
case is_nb_space(hd(Tail)) of
true -> %% multiline ... continue
get_line(Tail, [$\n, $\r | Cur]);
false ->
{line, lists:reverse(Cur), Tail}
end
end;
get_line([H|T], Cur) ->
get_line(T, [H|Cur]);
get_line([], Cur) ->
{incomplete, lists:reverse(Cur)}.