request_headers = [],
end_of_request = false,
default_host,
- trail = ""
+ trail = <<>>
}).
exit(normal)
end.
-receive_headers(State) ->
+receive_headers(#state{trail=Trail} = State) ->
SockMod = State#state.sockmod,
Socket = State#state.socket,
Data = SockMod:recv(Socket, 0, 300000),
case State#state.sockmod of
- gen_tcp ->
+ gen_tcp ->
NewState = process_header(State, Data),
case NewState#state.end_of_request of
true ->
_ ->
receive_headers(NewState)
end;
- _ ->
- case Data of
- {ok, Binary} ->
- {Request, Trail} = parse_request(
- State,
- State#state.trail ++ binary_to_list(Binary)),
- State1 = State#state{trail = Trail},
- NewState = lists:foldl(
- fun(D, S) ->
- case S#state.end_of_request of
- true ->
- S;
- _ ->
- process_header(S, D)
- end
- end, State1, Request),
- case NewState#state.end_of_request of
- true ->
- ok;
- _ ->
- receive_headers(NewState)
- end;
+ _ ->
+ case Data of
+ {ok, D} ->
+ parse_headers(State#state{trail = <<Trail/binary, D/binary>>});
+ {error, _} ->
+ ok
+ end
+ end.
+
+parse_headers(#state{trail = <<>>} = State) ->
+ receive_headers(State);
+parse_headers(#state{request_method = Method, trail = Data} = State) ->
+ PktType = case Method of
+ undefined -> http;
+ _ -> httph
+ end,
+ case decode_packet(PktType, Data) of
+ {ok, Pkt, Rest} ->
+ NewState = process_header(State#state{trail = Rest}, {ok, Pkt}),
+ case NewState#state.end_of_request of
+ true ->
+ ok;
_ ->
- ok
- end
+ parse_headers(NewState)
+ end;
+ {more, _} ->
+ receive_headers(State#state{trail = Data});
+ _ ->
+ ok
end.
process_header(State, Data) ->
binary_to_list(list_to_binary(Acc));
recv_data(State, Len, Acc) ->
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} ->
recv_data(State, Len - size(Data), [Acc | [Data]]);
_ ->
""
end;
_ ->
- Trail = State#state.trail,
- recv_data(State#state{trail = ""}, Len - length(Trail), [Acc | Trail])
+ Trail = binary_to_list(State#state.trail),
+ recv_data(State#state{trail = <<>>}, Len - length(Trail), [Acc | Trail])
end.
decode_base64([]) ->
[];
decode_base64([Sextet1,Sextet2,$=,$=|Rest]) ->
- Bits2x6=
+ Bits2x6=
(d(Sextet1) bsl 18) bor
(d(Sextet2) bsl 12),
Octet1=Bits2x6 bsr 16,
% The following code is mostly taken from yaws_ssl.erl
-parse_request(State, Data) ->
- case Data of
- [] ->
- {[], []};
- _ ->
- ?DEBUG("GOT ssl data ~p~n", [Data]),
- {R, Trail} = case State#state.request_method of
- undefined ->
- {R1, Trail1} = get_req(Data),
- ?DEBUG("Parsed request ~p~n", [R1]),
- {[R1], Trail1};
- _ ->
- {[], Data}
- end,
- {H, Trail2} = get_headers(Trail),
- {R ++ H, Trail2}
+decode_packet(_, <<"\r\n", Rest/binary>>) ->
+ {ok, http_eoh, Rest};
+decode_packet(Type, Data) ->
+ case binary:match(Data, <<"\r\n">>) of
+ {Start, _Len} ->
+ <<LineB:Start/binary, _:2/binary, Rest/binary>> = Data,
+ Line = binary_to_list(LineB),
+ Result = case Type of
+ http ->
+ parse_req(Line);
+ httph ->
+ parse_line(Line)
+ end,
+ case Result of
+ {ok, H} ->
+ {ok, H, Rest};
+ Err ->
+ {error, Err}
+ end;
+ _ ->
+ {more, undefined}
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)->
{Word, T} = lists:splitwith(fun(X)-> X /= $\ end, Line),
{Word, lists:dropwhile(fun(X) -> X == $\ end, T)}.
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) ->
- [{ok, {http_header, undefined, 'Connection', undefined, strip_spaces(Con)}}];
+ {ok, {http_header, undefined, 'Connection', undefined, strip_spaces(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) ->
- [{ok, {http_header, undefined, 'Accept', undefined, strip_spaces(Con)}}];
+ {ok, {http_header, undefined, 'Accept', undefined, strip_spaces(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) ->
- [{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) ->
- [{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) ->
- [{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) ->
- [{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) ->
- [{ok, {http_header, undefined, 'Range', undefined, strip_spaces(Con)}}];
+ {ok, {http_header, undefined, 'Range', undefined, strip_spaces(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) ->
- [{ok, {http_header, undefined, 'Accept-Ranges', undefined, strip_spaces(Con)}}];
+ {ok, {http_header, undefined, 'Accept-Ranges', undefined, strip_spaces(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) ->
- [{ok, {http_header, undefined, 'Keep-Alive', undefined, strip_spaces(Con)}}];
+ {ok, {http_header, undefined, 'Keep-Alive', undefined, strip_spaces(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) ->
- [{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) ->
- [{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) ->
- [{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) ->
- [{ok, {http_header, undefined, 'Content-Length', undefined, strip_spaces(Con)}}];
+ {ok, {http_header, undefined, 'Content-Length', undefined, strip_spaces(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) ->
- [{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) ->
- [{ok, {http_header, undefined, 'Accept-Encoding', undefined, strip_spaces(Con)}}];
+ {ok, {http_header, undefined, 'Accept-Encoding', undefined, strip_spaces(Con)}};
parse_line(S) ->
case lists:splitwith(fun(C)->C /= $: end, S) of
{Name, [$:|Val]} ->
- [{ok, {http_header, undefined, Name, undefined, strip_spaces(Val)}}];
+ {ok, {http_header, undefined, Name, undefined, strip_spaces(Val)}};
_ ->
- []
+ bad_request
end.
false ->
YS
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)}.