|
|
@ -3,7 +3,7 @@ |
|
|
|
%% An elli_http process blocks in elli_tcp:accept/2 until a client |
|
|
|
%% connects. It then handles requests on that connection until it's |
|
|
|
%% closed either by the client timing out or explicitly by the user. |
|
|
|
-module(elli_http). |
|
|
|
-module(wsHttp). |
|
|
|
-include("eWSrv.hrl"). |
|
|
|
-include("wsCom.hrl"). |
|
|
|
|
|
|
@ -41,7 +41,7 @@ |
|
|
|
|
|
|
|
-spec start_link(Server, ListenSocket, Options, Callback) -> pid() when |
|
|
|
Server :: pid(), |
|
|
|
ListenSocket :: elli_tcp:socket(), |
|
|
|
ListenSocket :: wsNet:socket(), |
|
|
|
Options :: proplists:proplist(), |
|
|
|
Callback :: wsHer:callback(). |
|
|
|
start_link(Server, ListenSocket, Options, Callback) -> |
|
|
@ -54,11 +54,11 @@ start_link(Server, ListenSocket, Options, Callback) -> |
|
|
|
%% timeout, loop to allow code upgrades of this module. |
|
|
|
-spec accept(Server, ListenSocket, Options, Callback) -> ok when |
|
|
|
Server :: pid(), |
|
|
|
ListenSocket :: elli_tcp:socket(), |
|
|
|
ListenSocket :: wsNet:socket(), |
|
|
|
Options :: proplists:proplist(), |
|
|
|
Callback :: wsHer:callback(). |
|
|
|
accept(Server, ListenSocket, Options, Callback) -> |
|
|
|
case catch elli_tcp:accept(ListenSocket, Server, accept_timeout(Options)) of |
|
|
|
case catch wsNet:accept(ListenSocket, Server, accept_timeout(Options)) of |
|
|
|
{ok, Socket} -> |
|
|
|
t(accepted), |
|
|
|
?MODULE:keepalive_loop(Socket, Options, Callback); |
|
|
@ -85,7 +85,7 @@ keepalive_loop(Socket, NumRequests, Buffer, Options, Callback) -> |
|
|
|
?MODULE:keepalive_loop(Socket, NumRequests + 1, |
|
|
|
NewBuffer, Options, Callback); |
|
|
|
{close, _} -> |
|
|
|
elli_tcp:close(Socket), |
|
|
|
wsNet:close(Socket), |
|
|
|
ok |
|
|
|
end. |
|
|
|
|
|
|
@ -93,7 +93,7 @@ keepalive_loop(Socket, NumRequests, Buffer, Options, Callback) -> |
|
|
|
%% Returns the appropriate connection token and any buffer containing (parts of) |
|
|
|
%% the next request. |
|
|
|
-spec handle_request(Socket, PrevBin, Options, Callback) -> ConnToken when |
|
|
|
Socket :: elli_tcp:socket(), |
|
|
|
Socket :: wsNet:socket(), |
|
|
|
PrevBin :: binary(), |
|
|
|
Options :: proplists:proplist(), |
|
|
|
Callback :: wsHer:callback(), |
|
|
@ -182,7 +182,7 @@ handle_response(Req, Buffer, {file, ResponseCode, UserHeaders, |
|
|
|
{error, FileError} -> |
|
|
|
handle_event(Mod, file_error, [FileError], Args), |
|
|
|
send_server_error(Req#req.socket), |
|
|
|
elli_tcp:close(Req#req.socket), |
|
|
|
wsNet:close(Req#req.socket), |
|
|
|
exit(normal); |
|
|
|
Size -> |
|
|
|
t(send_start), |
|
|
@ -235,7 +235,7 @@ send_response(Req, Code, Headers, UserBody) -> |
|
|
|
Response = [ResponseHeaders, |
|
|
|
Body], |
|
|
|
|
|
|
|
case elli_tcp:send(Req#req.socket, Response) of |
|
|
|
case wsNet:send(Req#req.socket, Response) of |
|
|
|
ok -> ok; |
|
|
|
{error, Closed} when Closed =:= closed orelse Closed =:= enotconn -> |
|
|
|
#req{callback = {Mod, Args}} = Req, |
|
|
@ -260,15 +260,15 @@ send_file(#req{callback = {Mod, Args}} = Req, Code, Headers, Filename, Range) -> |
|
|
|
{error, FileError} -> |
|
|
|
handle_event(Mod, file_error, [FileError], Args), |
|
|
|
send_server_error(Req#req.socket), |
|
|
|
elli_tcp:close(Req#req.socket), |
|
|
|
wsNet:close(Req#req.socket), |
|
|
|
exit(normal) |
|
|
|
end, |
|
|
|
ok. |
|
|
|
|
|
|
|
do_send_file(Fd, {Offset, Length}, #req{callback = {Mod, Args}} = Req, Headers) -> |
|
|
|
try elli_tcp:send(Req#req.socket, Headers) of |
|
|
|
try wsNet:send(Req#req.socket, Headers) of |
|
|
|
ok -> |
|
|
|
case elli_tcp:sendfile(Fd, Req#req.socket, Offset, Length, []) of |
|
|
|
case wsNet:sendfile(Fd, Req#req.socket, Offset, Length, []) of |
|
|
|
{ok, BytesSent} -> s(file, BytesSent), ok; |
|
|
|
{error, Closed} when Closed =:= closed orelse |
|
|
|
Closed =:= enotconn -> |
|
|
@ -292,7 +292,7 @@ send_server_error(Socket) -> |
|
|
|
|
|
|
|
send_rescue_response(Socket, Code, Body) -> |
|
|
|
Response = http_response(Code, Body), |
|
|
|
elli_tcp:send(Socket, Response). |
|
|
|
wsNet:send(Socket, Response). |
|
|
|
|
|
|
|
%% @doc Execute the user callback, translating failure into a proper response. |
|
|
|
execute_callback(#req{callback = {Mod, Args}} = Req) -> |
|
|
@ -346,7 +346,7 @@ end . |
|
|
|
start_chunk_loop(Socket) -> |
|
|
|
%% Set the socket to active so we receive the tcp_closed message |
|
|
|
%% if the client closes the connection |
|
|
|
elli_tcp:setopts(Socket, [{active, once}]), |
|
|
|
wsNet:setopts(Socket, [{active, once}]), |
|
|
|
?MODULE:chunk_loop(Socket). |
|
|
|
|
|
|
|
chunk_loop(Socket) -> |
|
|
@ -356,18 +356,18 @@ chunk_loop(Socket) -> |
|
|
|
{error, client_closed}; |
|
|
|
|
|
|
|
{chunk, close} -> |
|
|
|
case elli_tcp:send(Socket, <<"0\r\n\r\n">>) of |
|
|
|
case wsNet:send(Socket, <<"0\r\n\r\n">>) of |
|
|
|
ok -> |
|
|
|
elli_tcp:close(Socket), |
|
|
|
wsNet:close(Socket), |
|
|
|
ok; |
|
|
|
{error, Closed} when Closed =:= closed orelse |
|
|
|
Closed =:= enotconn -> |
|
|
|
{error, client_closed} |
|
|
|
end; |
|
|
|
{chunk, close, From} -> |
|
|
|
case elli_tcp:send(Socket, <<"0\r\n\r\n">>) of |
|
|
|
case wsNet:send(Socket, <<"0\r\n\r\n">>) of |
|
|
|
ok -> |
|
|
|
elli_tcp:close(Socket), |
|
|
|
wsNet:close(Socket), |
|
|
|
From ! {self(), ok}, |
|
|
|
ok; |
|
|
|
{error, Closed} when Closed =:= closed orelse |
|
|
@ -400,7 +400,7 @@ send_chunk(Socket, Data) -> |
|
|
|
Response = [integer_to_list(Size, 16), |
|
|
|
<<"\r\n">>, Data, <<"\r\n">>], |
|
|
|
s(chunks, iolist_size(Response)), |
|
|
|
elli_tcp:send(Socket, Response) |
|
|
|
wsNet:send(Socket, Response) |
|
|
|
end. |
|
|
|
|
|
|
|
|
|
|
@ -426,30 +426,30 @@ get_request_(Socket, Buffer, Options, {Mod, Args} = Callback) -> |
|
|
|
{ok, {http_error, _}, _} -> |
|
|
|
handle_event(Mod, request_parse_error, [Buffer], Args), |
|
|
|
send_bad_request(Socket), |
|
|
|
elli_tcp:close(Socket), |
|
|
|
wsNet:close(Socket), |
|
|
|
exit(normal); |
|
|
|
{ok, {http_response, _, _, _}, _} -> |
|
|
|
elli_tcp:close(Socket), |
|
|
|
wsNet:close(Socket), |
|
|
|
exit(normal) |
|
|
|
end. |
|
|
|
|
|
|
|
recv_request(Socket, Buffer, Options, {Mod, Args} = _Callback) -> |
|
|
|
case elli_tcp:recv(Socket, 0, request_timeout(Options)) of |
|
|
|
case wsNet:recv(Socket, 0, request_timeout(Options)) of |
|
|
|
{ok, Data} -> |
|
|
|
<<Buffer/binary, Data/binary>>; |
|
|
|
{error, timeout} -> |
|
|
|
handle_event(Mod, request_timeout, [], Args), |
|
|
|
elli_tcp:close(Socket), |
|
|
|
wsNet:close(Socket), |
|
|
|
exit(normal); |
|
|
|
{error, Closed} when Closed =:= closed orelse |
|
|
|
Closed =:= enotconn -> |
|
|
|
handle_event(Mod, request_closed, [], Args), |
|
|
|
elli_tcp:close(Socket), |
|
|
|
wsNet:close(Socket), |
|
|
|
exit(normal) |
|
|
|
end. |
|
|
|
|
|
|
|
-spec get_headers(Socket, V, Buffer, Opts, Callback) -> Headers when |
|
|
|
Socket :: elli_tcp:socket(), |
|
|
|
Socket :: wsNet:socket(), |
|
|
|
V :: version(), |
|
|
|
Buffer :: binary(), |
|
|
|
Opts :: proplists:proplist(), |
|
|
@ -464,7 +464,7 @@ get_headers(Socket, _, {Headers, _}, HeadersCount, _Opts, {Mod, Args}) |
|
|
|
when HeadersCount >= 100 -> |
|
|
|
handle_event(Mod, bad_request, [{too_many_headers, Headers}], Args), |
|
|
|
send_bad_request(Socket), |
|
|
|
elli_tcp:close(Socket), |
|
|
|
wsNet:close(Socket), |
|
|
|
exit(normal); |
|
|
|
get_headers(Socket, Buffer, {Headers, ParsedHeaders}, Count, Opts, {Mod, Args} = Callback) -> |
|
|
|
case erlang:decode_packet(httph_bin, Buffer, []) of |
|
|
@ -478,19 +478,19 @@ get_headers(Socket, Buffer, {Headers, ParsedHeaders}, Count, Opts, {Mod, Args} = |
|
|
|
{ok, {http_error, _}, Rest} -> |
|
|
|
get_headers(Socket, Rest, {Headers, ParsedHeaders}, Count, Opts, Callback); |
|
|
|
{more, _} -> |
|
|
|
case elli_tcp:recv(Socket, 0, header_timeout(Opts)) of |
|
|
|
case wsNet:recv(Socket, 0, header_timeout(Opts)) of |
|
|
|
{ok, Data} -> |
|
|
|
get_headers(Socket, <<Buffer/binary, Data/binary>>, |
|
|
|
{Headers, ParsedHeaders}, Count, Opts, Callback); |
|
|
|
{error, Closed} when Closed =:= closed orelse |
|
|
|
Closed =:= enotconn -> |
|
|
|
handle_event(Mod, client_closed, [receiving_headers], Args), |
|
|
|
elli_tcp:close(Socket), |
|
|
|
wsNet:close(Socket), |
|
|
|
exit(normal); |
|
|
|
{error, timeout} -> |
|
|
|
handle_event(Mod, client_timeout, |
|
|
|
[receiving_headers], Args), |
|
|
|
elli_tcp:close(Socket), |
|
|
|
wsNet:close(Socket), |
|
|
|
exit(normal) |
|
|
|
end |
|
|
|
end. |
|
|
@ -506,7 +506,7 @@ get_headers(Socket, Buffer, {Headers, ParsedHeaders}, Count, Opts, {Mod, Args} = |
|
|
|
%% buffered too much and get parts of the next pipelined request. In |
|
|
|
%% that case, push it back in the buffer and handle the first request. |
|
|
|
-spec get_body(Socket, Headers, Buffer, Opts, Callback) -> FullBody when |
|
|
|
Socket :: undefined | elli_tcp:socket(), |
|
|
|
Socket :: undefined | wsNet:socket(), |
|
|
|
Headers :: elli:headers(), |
|
|
|
Buffer :: binary(), |
|
|
|
Opts :: proplists:proplist(), |
|
|
@ -541,16 +541,16 @@ get_body(Socket, Headers, Buffer, Opts, Callback) -> |
|
|
|
end. |
|
|
|
|
|
|
|
do_get_body(Socket, Buffer, Opts, N, {Mod, Args}) -> |
|
|
|
case elli_tcp:recv(Socket, N, body_timeout(Opts)) of |
|
|
|
case wsNet:recv(Socket, N, body_timeout(Opts)) of |
|
|
|
{ok, Data} -> |
|
|
|
{<<Buffer/binary, Data/binary>>, <<>>}; |
|
|
|
{error, Closed} when Closed =:= closed orelse Closed =:= enotconn -> |
|
|
|
handle_event(Mod, client_closed, [receiving_body], Args), |
|
|
|
ok = elli_tcp:close(Socket), |
|
|
|
ok = wsNet:close(Socket), |
|
|
|
exit(normal); |
|
|
|
{error, timeout} -> |
|
|
|
handle_event(Mod, client_timeout, [receiving_body], Args), |
|
|
|
ok = elli_tcp:close(Socket), |
|
|
|
ok = wsNet:close(Socket), |
|
|
|
exit(normal) |
|
|
|
end. |
|
|
|
|
|
|
@ -564,7 +564,7 @@ maybe_send_continue(Socket, Headers) -> |
|
|
|
case get_header(?EXPECT_HEADER, Headers, undefined) of |
|
|
|
<<"100-continue">> -> |
|
|
|
Response = http_response(100), |
|
|
|
elli_tcp:send(Socket, Response); |
|
|
|
wsNet:send(Socket, Response); |
|
|
|
_Other -> |
|
|
|
ok |
|
|
|
end. |
|
|
@ -580,16 +580,16 @@ do_check_max_size(Socket, ContentLength, Buffer, MaxSize, {Mod, Args}) |
|
|
|
when ContentLength > MaxSize -> |
|
|
|
handle_event(Mod, bad_request, [{body_size, ContentLength}], Args), |
|
|
|
do_check_max_size_x2(Socket, ContentLength, Buffer, MaxSize), |
|
|
|
elli_tcp:close(Socket), |
|
|
|
wsNet:close(Socket), |
|
|
|
exit(normal); |
|
|
|
do_check_max_size(_, _, _, _, _) -> ok. |
|
|
|
|
|
|
|
do_check_max_size_x2(Socket, ContentLength, Buffer, MaxSize) |
|
|
|
when ContentLength < MaxSize * 2 -> |
|
|
|
OnSocket = ContentLength - size(Buffer), |
|
|
|
elli_tcp:recv(Socket, OnSocket, 60000), |
|
|
|
wsNet:recv(Socket, OnSocket, 60000), |
|
|
|
Response = http_response(413), |
|
|
|
elli_tcp:send(Socket, Response); |
|
|
|
wsNet:send(Socket, Response); |
|
|
|
do_check_max_size_x2(_, _, _, _) -> ok. |
|
|
|
|
|
|
|
-spec mk_req(Method, PathTuple, Headers, Headers, Body, V, Socket, Callback) -> Req when |
|
|
@ -598,7 +598,7 @@ do_check_max_size_x2(_, _, _, _) -> ok. |
|
|
|
Headers :: elli:headers(), |
|
|
|
Body :: elli:body(), |
|
|
|
V :: version(), |
|
|
|
Socket :: elli_tcp:socket() | undefined, |
|
|
|
Socket :: wsNet:socket() | undefined, |
|
|
|
Callback :: wsHer:callback(), |
|
|
|
Req :: elli:req(). |
|
|
|
mk_req(Method, PathTuple, Headers, ParsedHeaders, Body, V, Socket, {Mod, Args} = Callback) -> |
|
|
@ -613,7 +613,7 @@ mk_req(Method, PathTuple, Headers, ParsedHeaders, Body, V, Socket, {Mod, Args} = |
|
|
|
handle_event(Mod, request_parse_error, |
|
|
|
[{Reason, {Method, PathTuple}}], Args), |
|
|
|
send_bad_request(Socket), |
|
|
|
elli_tcp:close(Socket), |
|
|
|
wsNet:close(Socket), |
|
|
|
exit(normal) |
|
|
|
end. |
|
|
|
|