|
|
@ -6,11 +6,13 @@ |
|
|
|
-export([ |
|
|
|
start_link/1 |
|
|
|
, sendResponse/5 |
|
|
|
, send_file/5 |
|
|
|
, sendFile/5 |
|
|
|
%% Exported for looping with a fully-qualified module name |
|
|
|
, chunk_loop/1 |
|
|
|
, split_args/1 |
|
|
|
, chunkLoop/1 |
|
|
|
, spellHeaders/1 |
|
|
|
, splitArgs/1 |
|
|
|
, closeOrKeepAlive/2 |
|
|
|
, maybeSendContinue/2 |
|
|
|
]). |
|
|
|
|
|
|
|
%% eNet callback |
|
|
@ -25,7 +27,7 @@ |
|
|
|
]). |
|
|
|
|
|
|
|
newCon(_Sock, WsMod) -> |
|
|
|
wsHttp:start_link(WsMod). |
|
|
|
?MODULE:start_link(WsMod). |
|
|
|
|
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% genActor start %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
|
|
|
-spec(start_link(atom()) -> {ok, pid()} | ignore | {error, term()}). |
|
|
@ -81,62 +83,60 @@ loop(Parent, State) -> |
|
|
|
end. |
|
|
|
|
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% genActor end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
|
|
|
%% ************************************************ API *************************************************************** |
|
|
|
|
|
|
|
|
|
|
|
%% ******************************************** API ******************************************************************* |
|
|
|
|
|
|
|
|
|
|
|
%% ******************************************** callback ************************************************************** |
|
|
|
init(WsMod) -> |
|
|
|
{ok, #wsState{wsMod = WsMod}}. |
|
|
|
|
|
|
|
handleMsg({tcp, _Socket, Data}, State) -> |
|
|
|
#wsState{stage = Stage, buffer = Buffer, socket = Socket} = State, |
|
|
|
case wsHttpProtocol:request(Stage, <<Buffer/binary, Data/binary>>, State) of |
|
|
|
{ok, NewState} -> |
|
|
|
{noreply, NewState}; |
|
|
|
{ok, _NewState} = LRet -> |
|
|
|
LRet; |
|
|
|
{done, NewState} -> |
|
|
|
Response = doHandle(NewState), |
|
|
|
#wsState{buffer = Buffer, socket = Socket, temHeader = TemHeader, method = Method} = NewState, |
|
|
|
case doResponse(Response, Socket, TemHeader, Method) of |
|
|
|
keep_alive -> |
|
|
|
handleMsg({tcp, _Socket, Buffer}, newWsState(NewState)); |
|
|
|
{close, _} -> |
|
|
|
wsNet:close(Socket), |
|
|
|
ok |
|
|
|
close -> |
|
|
|
{stop, close} |
|
|
|
end; |
|
|
|
Err -> |
|
|
|
?wsErr("recv the http data error ~p~n", [Err]), |
|
|
|
send_bad_request(Socket), |
|
|
|
gen_tcp:close(Socket) |
|
|
|
sendBadRequest(Socket), |
|
|
|
{stop, close} |
|
|
|
end; |
|
|
|
handleMsg({tcp_closed, _Socket}, _State) -> |
|
|
|
ok; |
|
|
|
{stop, tcp_closed}; |
|
|
|
handleMsg({tcp_error, _Socket, Reason}, _State) -> |
|
|
|
?wsErr("the http tcp socket error ~p~n", [Reason]), |
|
|
|
{stop, tcp_error}; |
|
|
|
|
|
|
|
handleMsg({tcp_error, Socket, Reason}, _State) -> |
|
|
|
?wsErr("the http socket error ~p~n", [Reason]), |
|
|
|
gen_tcp:close(Socket), |
|
|
|
kpS; |
|
|
|
|
|
|
|
handleMsg({ssl, Socket, Data}, State) -> |
|
|
|
handleMsg({ssl, _Socket, Data}, State) -> |
|
|
|
#wsState{stage = Stage, buffer = Buffer, socket = Socket} = State, |
|
|
|
case wsHttpProtocol:request(Stage, <<Buffer/binary, Data/binary>>, State) of |
|
|
|
{ok, NewState} -> |
|
|
|
{noreply, NewState}; |
|
|
|
{ok, _NewState} = LRet -> |
|
|
|
LRet; |
|
|
|
{done, NewState} -> |
|
|
|
ok; |
|
|
|
Response = doHandle(NewState), |
|
|
|
#wsState{buffer = Buffer, temHeader = TemHeader, method = Method} = NewState, |
|
|
|
case doResponse(Response, Socket, TemHeader, Method) of |
|
|
|
keep_alive -> |
|
|
|
handleMsg({tcp, Socket, Buffer}, newWsState(NewState)); |
|
|
|
close -> |
|
|
|
{stop, close} |
|
|
|
end; |
|
|
|
Err -> |
|
|
|
?wsErr("recv the http data error ~p~n", [Err]), |
|
|
|
send_bad_request(Socket), |
|
|
|
ssl:close(Socket) |
|
|
|
sendBadRequest(Socket), |
|
|
|
{stop, close} |
|
|
|
end; |
|
|
|
handleMsg({ssl_closed, Socket}, _State) -> |
|
|
|
ok; |
|
|
|
|
|
|
|
handleMsg({ssl_error, Socket, Reason}, _State) -> |
|
|
|
?wsErr("the http socket error ~p~n", [Reason]), |
|
|
|
ssl:close(Socket), |
|
|
|
kpS; |
|
|
|
handleMsg({ssl_closed, _Socket}, _State) -> |
|
|
|
{stop, ssl_closed}; |
|
|
|
handleMsg({ssl_error, _Socket, Reason}, _State) -> |
|
|
|
?wsErr("the http ssl socket error ~p~n", [Reason]), |
|
|
|
{stop, ssl_error}; |
|
|
|
handleMsg({?mSockReady, Sock}, _State) -> |
|
|
|
inet:setopts(Sock, [{packet, raw}, {active, true}]), |
|
|
|
{ok, #wsState{socket = Sock}}; |
|
|
@ -147,13 +147,14 @@ handleMsg({?mSockReady, Sock, SslOpts, SslHSTet}, State) -> |
|
|
|
{ok, State#wsState{socket = SslSock, isSsl = true}}; |
|
|
|
_Err -> |
|
|
|
?wsErr("ssl handshake error ~p~n", [_Err]), |
|
|
|
{stop, _Err, State} |
|
|
|
{stop, handshake_error} |
|
|
|
end; |
|
|
|
handleMsg(_Msg, _State) -> |
|
|
|
?wsErr("~p info receive unexpect msg ~p ~n ", [?MODULE, _Msg]), |
|
|
|
kpS. |
|
|
|
|
|
|
|
terminate(_Reason, _State) -> |
|
|
|
terminate(_Reason, #wsState{socket = Socket} = _State) -> |
|
|
|
wsNet:close(Socket), |
|
|
|
ok. |
|
|
|
|
|
|
|
newWsState(WsState) -> |
|
|
@ -213,35 +214,39 @@ doResponse({response, Code, UserHeaders, Body}, Socket, TemHeader, Method) -> |
|
|
|
sendResponse(Socket, Method, Code, Headers, Body), |
|
|
|
closeOrKeepAlive(UserHeaders, TemHeader); |
|
|
|
doResponse({chunk, UserHeaders, Initial}, Socket, TemHeader, Method) -> |
|
|
|
ResponseHeaders = [{?TRANSFER_ENCODING_HEADER, <<"chunked">>}, connection(UserHeaders, TemHeader) | UserHeaders], |
|
|
|
ResponseHeaders = [transferEncoding(UserHeaders), connection(UserHeaders, TemHeader) | UserHeaders], |
|
|
|
sendResponse(Socket, Method, 200, ResponseHeaders, <<>>), |
|
|
|
Initial =:= <<"">> orelse send_chunk(Socket, Initial), |
|
|
|
case start_chunk_loop(Socket) of |
|
|
|
Initial =:= <<"">> orelse sendChunk(Socket, Initial), |
|
|
|
case startChunkLoop(Socket) of |
|
|
|
{error, client_closed} -> client; |
|
|
|
ok -> server |
|
|
|
end, |
|
|
|
close; |
|
|
|
doResponse({file, ResponseCode, UserHeaders, Filename, Range}, Socket, TemHeader, Method) -> |
|
|
|
ResponseHeaders = [connection(UserHeaders, TemHeader) | UserHeaders], |
|
|
|
case wsUtil:file_size(Filename) of |
|
|
|
case wsUtil:fileSize(Filename) of |
|
|
|
{error, _FileError} -> |
|
|
|
send_server_error(Socket), |
|
|
|
wsNet:close(Socket), |
|
|
|
exit(normal); |
|
|
|
sendSrvError(Socket), |
|
|
|
{stop, file_error}; |
|
|
|
Size -> |
|
|
|
case wsUtil:normalize_range(Range, Size) of |
|
|
|
undefined -> |
|
|
|
send_file(Socket, ResponseCode, [{<<"Content-Length">>, Size} | ResponseHeaders], Filename, {0, 0}); |
|
|
|
{Offset, Length} -> |
|
|
|
ERange = wsUtil:encode_range({Offset, Length}, Size), |
|
|
|
send_file(Socket, 206, |
|
|
|
lists:append(ResponseHeaders, [{<<"Content-Length">>, Length}, {<<"Content-Range">>, ERange}]), |
|
|
|
Filename, {Offset, Length}); |
|
|
|
invalid_range -> |
|
|
|
ERange = wsUtil:encode_range(invalid_range, Size), |
|
|
|
sendResponse(Socket, Method, 416, lists:append(ResponseHeaders, [{<<"Content-Length">>, 0}, {<<"Content-Range">>, ERange}]), <<>>) |
|
|
|
end, |
|
|
|
closeOrKeepAlive(UserHeaders, TemHeader) |
|
|
|
Ret = |
|
|
|
case wsUtil:normalizeRange(Range, Size) of |
|
|
|
undefined -> |
|
|
|
sendFile(Socket, ResponseCode, [{?CONTENT_LENGTH_HEADER, Size} | ResponseHeaders], Filename, {0, 0}); |
|
|
|
{Offset, Length} -> |
|
|
|
ERange = wsUtil:encodeRange({Offset, Length}, Size), |
|
|
|
sendFile(Socket, 206, lists:append(ResponseHeaders, [{?CONTENT_LENGTH_HEADER, Length}, {<<"Content-Range">>, ERange}]), Filename, {Offset, Length}); |
|
|
|
invalid_range -> |
|
|
|
ERange = wsUtil:encodeRange(invalid_range, Size), |
|
|
|
sendResponse(Socket, Method, 416, lists:append(ResponseHeaders, [{<<"Content-Length">>, 0}, {<<"Content-Range">>, ERange}]), <<>>), |
|
|
|
{error, range} |
|
|
|
end, |
|
|
|
case Ret of |
|
|
|
ok -> |
|
|
|
closeOrKeepAlive(UserHeaders, TemHeader); |
|
|
|
{error, Reason} -> |
|
|
|
{stop, Reason} |
|
|
|
end |
|
|
|
end. |
|
|
|
|
|
|
|
%% @doc Generate a HTTP response and send it to the client. |
|
|
@ -261,7 +266,7 @@ sendResponse(Socket, Method, Code, Headers, UserBody) -> |
|
|
|
end |
|
|
|
end, |
|
|
|
|
|
|
|
Response = http_response(Code, Headers, Body), |
|
|
|
Response = httpResponse(Code, Headers, Body), |
|
|
|
|
|
|
|
case wsNet:send(Socket, Response) of |
|
|
|
ok -> |
|
|
@ -270,38 +275,36 @@ sendResponse(Socket, Method, Code, Headers, UserBody) -> |
|
|
|
?wsErr("send_response error ~p~n", [_Err]) |
|
|
|
end. |
|
|
|
|
|
|
|
|
|
|
|
%% @doc Send a HTTP response to the client where the body is the |
|
|
|
%% contents of the given file. Assumes correctly set response code |
|
|
|
%% and headers. |
|
|
|
-spec send_file(Req, Code, Headers, Filename, Range) -> ok when |
|
|
|
-spec sendFile(Req, Code, Headers, Filename, Range) -> ok when |
|
|
|
Req :: elli:wsReq(), |
|
|
|
Code :: elli:httpCode(), |
|
|
|
Headers :: elli:headers(), |
|
|
|
Filename :: file:filename(), |
|
|
|
Range :: wsUtil:range(). |
|
|
|
send_file(Socket, Code, Headers, Filename, Range) -> |
|
|
|
ResponseHeaders = assemble_response_headers(Code, Headers), |
|
|
|
|
|
|
|
sendFile(Socket, Code, Headers, Filename, Range) -> |
|
|
|
ResponseHeaders = httpResponse(Code, Headers, <<>>), |
|
|
|
case file:open(Filename, [read, raw, binary]) of |
|
|
|
{ok, Fd} -> do_send_file(Fd, Range, Socket, ResponseHeaders); |
|
|
|
{error, FileError} -> |
|
|
|
send_server_error(Socket), |
|
|
|
wsNet:close(Socket), |
|
|
|
exit(normal) |
|
|
|
end, |
|
|
|
ok. |
|
|
|
{ok, Fd} -> doSendFile(Fd, Range, Socket, ResponseHeaders); |
|
|
|
{error, _FileError} = Err -> |
|
|
|
sendSrvError(Socket), |
|
|
|
Err |
|
|
|
end. |
|
|
|
|
|
|
|
do_send_file(Fd, {Offset, Length}, Socket, Headers) -> |
|
|
|
doSendFile(Fd, {Offset, Length}, Socket, Headers) -> |
|
|
|
try wsNet:send(Socket, Headers) of |
|
|
|
ok -> |
|
|
|
case wsNet:sendfile(Fd, Socket, Offset, Length, []) of |
|
|
|
{ok, BytesSent} -> s(file, BytesSent), ok; |
|
|
|
{error, Closed} when Closed =:= closed orelse Closed =:= enotconn -> |
|
|
|
?wsErr("send file error") |
|
|
|
{ok, _BytesSent} -> ok; |
|
|
|
{error, Closed} = LErr when Closed =:= closed orelse Closed =:= enotconn -> |
|
|
|
?wsErr("send file error"), |
|
|
|
LErr |
|
|
|
end; |
|
|
|
{error, Closed} when Closed =:= closed orelse Closed =:= enotconn -> |
|
|
|
?wsErr("send file error") |
|
|
|
{error, Closed} = LErr when Closed =:= closed orelse Closed =:= enotconn -> |
|
|
|
?wsErr("send file error"), |
|
|
|
LErr |
|
|
|
after |
|
|
|
file:close(Fd) |
|
|
|
end. |
|
|
@ -309,115 +312,101 @@ do_send_file(Fd, {Offset, Length}, Socket, Headers) -> |
|
|
|
%% @doc To send a response, we must first have received everything the |
|
|
|
%% client is sending. If this is not the case, {@link send_bad_request/1} |
|
|
|
%% might reset the client connection. |
|
|
|
send_bad_request(Socket) -> |
|
|
|
send_rescue_response(Socket, 400, <<"Bad Request">>). |
|
|
|
sendBadRequest(Socket) -> |
|
|
|
sendRescueResponse(Socket, 400, <<"Bad Request">>). |
|
|
|
|
|
|
|
send_server_error(Socket) -> |
|
|
|
send_rescue_response(Socket, 500, <<"Server Error">>). |
|
|
|
sendSrvError(Socket) -> |
|
|
|
sendRescueResponse(Socket, 500, <<"Server Error">>). |
|
|
|
|
|
|
|
send_rescue_response(Socket, Code, Body) -> |
|
|
|
Response = http_response(Code, Body), |
|
|
|
sendRescueResponse(Socket, Code, Body) -> |
|
|
|
Response = httpResponse(Code, Body), |
|
|
|
wsNet:send(Socket, Response). |
|
|
|
|
|
|
|
|
|
|
|
%% |
|
|
|
%% CHUNKED-TRANSFER |
|
|
|
%% |
|
|
|
|
|
|
|
|
|
|
|
%% @doc The chunk loop is an intermediary between the socket and the |
|
|
|
%% user. We forward anything the user sends until the user sends an |
|
|
|
%% empty response, which signals that the connection should be |
|
|
|
%% closed. When the client closes the socket, the loop exits. |
|
|
|
start_chunk_loop(Socket) -> |
|
|
|
startChunkLoop(Socket) -> |
|
|
|
%% Set the socket to active so we receive the tcp_closed message |
|
|
|
%% if the client closes the connection |
|
|
|
wsNet:setopts(Socket, [{active, once}]), |
|
|
|
?MODULE:chunk_loop(Socket). |
|
|
|
?MODULE:chunkLoop(Socket). |
|
|
|
|
|
|
|
chunk_loop(Socket) -> |
|
|
|
{_SockType, InnerSocket} = Socket, |
|
|
|
chunkLoop(Socket) -> |
|
|
|
receive |
|
|
|
{tcp_closed, InnerSocket} -> |
|
|
|
{tcp_closed, Socket} -> |
|
|
|
{error, client_closed}; |
|
|
|
|
|
|
|
{chunk, close} -> |
|
|
|
case wsNet:send(Socket, <<"0\r\n\r\n">>) of |
|
|
|
ok -> |
|
|
|
wsNet:close(Socket), |
|
|
|
ok; |
|
|
|
{error, Closed} when Closed =:= closed orelse |
|
|
|
Closed =:= enotconn -> |
|
|
|
{error, Closed} when Closed =:= closed orelse Closed =:= enotconn -> |
|
|
|
{error, client_closed} |
|
|
|
end; |
|
|
|
{chunk, close, From} -> |
|
|
|
case wsNet:send(Socket, <<"0\r\n\r\n">>) of |
|
|
|
ok -> |
|
|
|
wsNet:close(Socket), |
|
|
|
From ! {self(), ok}, |
|
|
|
ok; |
|
|
|
{error, Closed} when Closed =:= closed orelse |
|
|
|
Closed =:= enotconn -> |
|
|
|
{error, Closed} when Closed =:= closed orelse Closed =:= enotconn -> |
|
|
|
From ! {self(), {error, closed}}, |
|
|
|
ok |
|
|
|
end; |
|
|
|
|
|
|
|
{chunk, Data} -> |
|
|
|
send_chunk(Socket, Data), |
|
|
|
?MODULE:chunk_loop(Socket); |
|
|
|
sendChunk(Socket, Data), |
|
|
|
?MODULE:chunkLoop(Socket); |
|
|
|
{chunk, Data, From} -> |
|
|
|
case send_chunk(Socket, Data) of |
|
|
|
case sendChunk(Socket, Data) of |
|
|
|
ok -> |
|
|
|
From ! {self(), ok}; |
|
|
|
{error, Closed} when Closed =:= closed orelse |
|
|
|
Closed =:= enotconn -> |
|
|
|
{error, Closed} when Closed =:= closed orelse Closed =:= enotconn -> |
|
|
|
From ! {self(), {error, closed}} |
|
|
|
end, |
|
|
|
?MODULE:chunk_loop(Socket) |
|
|
|
?MODULE:chunkLoop(Socket) |
|
|
|
after 10000 -> |
|
|
|
?MODULE:chunk_loop(Socket) |
|
|
|
?MODULE:chunkLoop(Socket) |
|
|
|
end. |
|
|
|
|
|
|
|
|
|
|
|
send_chunk(Socket, Data) -> |
|
|
|
sendChunk(Socket, Data) -> |
|
|
|
case iolist_size(Data) of |
|
|
|
0 -> ok; |
|
|
|
Size -> |
|
|
|
Response = [integer_to_list(Size, 16), |
|
|
|
<<"\r\n">>, Data, <<"\r\n">>], |
|
|
|
s(chunks, iolist_size(Response)), |
|
|
|
Response = [integer_to_binary(Size, 16), <<"\r\n">>, Data, <<"\r\n">>], |
|
|
|
wsNet:send(Socket, Response) |
|
|
|
end. |
|
|
|
|
|
|
|
maybe_send_continue(Socket, Headers) -> |
|
|
|
maybeSendContinue(Socket, Headers) -> |
|
|
|
% According to RFC2616 section 8.2.3 an origin server must respond with |
|
|
|
% either a "100 Continue" or a final response code when the client |
|
|
|
% headers contains "Expect:100-continue" |
|
|
|
case lists:keyfind(?EXPECT_HEADER, 1, Headers) of |
|
|
|
<<"100-continue">> -> |
|
|
|
Response = http_response(100), |
|
|
|
Response = httpResponse(100), |
|
|
|
wsNet:send(Socket, Response); |
|
|
|
_Other -> |
|
|
|
ok |
|
|
|
end. |
|
|
|
|
|
|
|
http_response(Code) -> |
|
|
|
http_response(Code, <<>>). |
|
|
|
httpResponse(Code) -> |
|
|
|
httpResponse(Code, <<>>). |
|
|
|
|
|
|
|
http_response(Code, Body) -> |
|
|
|
http_response(Code, [{?CONTENT_LENGTH_HEADER, size(Body)}], Body). |
|
|
|
httpResponse(Code, Body) -> |
|
|
|
httpResponse(Code, [{?CONTENT_LENGTH_HEADER, size(Body)}], Body). |
|
|
|
|
|
|
|
http_response(Code, Headers, Body) -> |
|
|
|
httpResponse(Code, Headers, Body) -> |
|
|
|
[<<"HTTP/1.1 ">>, status(Code), <<"\r\n">>, spellHeaders(Headers), <<"\r\n">>, Body]. |
|
|
|
|
|
|
|
assemble_response_headers(Code, Headers) -> |
|
|
|
ResponseHeaders = http_response(Code, Headers, <<>>), |
|
|
|
s(resp_headers, iolist_size(ResponseHeaders)), |
|
|
|
ResponseHeaders. |
|
|
|
|
|
|
|
spellHeaders(Headers) -> |
|
|
|
<<<<(toBinStr(Key))/binary, ": ", (toBinStr(Value))/binary, "\r\n">> || {Key, Value} <- Headers>>. |
|
|
|
|
|
|
|
-spec splitArgs(binary()) -> list({binary(), binary() | true}). |
|
|
|
splitArgs(<<>>) -> []; |
|
|
|
splitArgs(Qs) -> |
|
|
|
Tokens = binary:split(Qs, <<"&">>, [global, trim]), |
|
|
|
[case binary:split(Token, <<"=">>) of [Token] -> {Token, true}; [Name, Value] -> |
|
|
|
{Name, Value} end || Token <- Tokens]. |
|
|
|
|
|
|
|
toBinStr(V) when is_integer(V) -> integer_to_binary(V); |
|
|
|
toBinStr(V) when is_binary(V) -> V; |
|
|
|
toBinStr(V) when is_list(V) -> list_to_binary(V); |
|
|
@ -425,27 +414,29 @@ toBinStr(V) when is_atom(V) -> atom_to_binary(V). |
|
|
|
|
|
|
|
closeOrKeepAlive(UserHeaders, ReqHeader) -> |
|
|
|
case lists:keyfind(?CONNECTION_HEADER, 1, UserHeaders) of |
|
|
|
{_, <<"Close">>} -> |
|
|
|
close; |
|
|
|
{_, <<"close">>} -> |
|
|
|
close; |
|
|
|
_ -> |
|
|
|
case lists:keyfind(?CONNECTION_HEADER, 1, ReqHeader) of |
|
|
|
{_, <<"Close">>} -> |
|
|
|
close; |
|
|
|
{_, <<"close">>} -> |
|
|
|
close; |
|
|
|
_ -> |
|
|
|
keep_alive |
|
|
|
end; |
|
|
|
_ -> |
|
|
|
keep_alive |
|
|
|
end |
|
|
|
end. |
|
|
|
|
|
|
|
connection(UserHeaders, ReqHeader) -> |
|
|
|
case lists:keyfind(?CONNECTION_HEADER, 1, UserHeaders) of |
|
|
|
false -> |
|
|
|
case lists:keyfind(?CONNECTION_HEADER, 1, ReqHeader) of |
|
|
|
{_, <<"close">>} -> |
|
|
|
{?CONNECTION_HEADER, <<"Close">>}; |
|
|
|
{_, <<"Close">>} -> |
|
|
|
{?CONNECTION_HEADER, <<"Close">>}; |
|
|
|
_ -> |
|
|
|
{?CONNECTION_HEADER, <<"Keep-Alive">>} |
|
|
|
false -> |
|
|
|
{?CONNECTION_HEADER, <<"Keep-Alive">>}; |
|
|
|
Tuple -> |
|
|
|
Tuple |
|
|
|
end; |
|
|
|
_ -> |
|
|
|
[] |
|
|
@ -454,49 +445,20 @@ connection(UserHeaders, ReqHeader) -> |
|
|
|
contentLength(Headers, Body) -> |
|
|
|
case lists:keyfind(?CONTENT_LENGTH_HEADER, Headers) of |
|
|
|
false -> |
|
|
|
[]; |
|
|
|
{?CONTENT_LENGTH_HEADER, iolist_size(Body)}; |
|
|
|
_ -> |
|
|
|
{?CONTENT_LENGTH_HEADER, iolist_size(Body)} |
|
|
|
[] |
|
|
|
end. |
|
|
|
|
|
|
|
%% @doc Split the URL arguments into a proplist. |
|
|
|
%% Lifted from `cowboy_http:x_www_form_urlencoded/2'. |
|
|
|
-spec split_args(binary()) -> list({binary(), binary() | true}). |
|
|
|
split_args(<<>>) -> |
|
|
|
[]; |
|
|
|
split_args(Qs) -> |
|
|
|
Tokens = binary:split(Qs, <<"&">>, [global, trim]), |
|
|
|
[case binary:split(Token, <<"=">>) of |
|
|
|
[Token] -> {Token, true}; |
|
|
|
[Name, Value] -> {Name, Value} |
|
|
|
end || Token <- Tokens]. |
|
|
|
|
|
|
|
%% |
|
|
|
%% SIZE HELPERS |
|
|
|
%% |
|
|
|
|
|
|
|
%% @doc stores response part size in bytes |
|
|
|
s(chunks, Size) -> |
|
|
|
case get({size, chunks}) of |
|
|
|
undefined -> |
|
|
|
put({size, chunks}, Size); |
|
|
|
Sum -> |
|
|
|
put({size, chunks}, Size + Sum) |
|
|
|
end; |
|
|
|
s(Key, Size) -> |
|
|
|
put({size, Key}, Size). |
|
|
|
|
|
|
|
get_sizes({{size, Key}, Value}) -> |
|
|
|
erase({size, Key}), |
|
|
|
{true, {Key, Value}}; |
|
|
|
get_sizes(_) -> |
|
|
|
false. |
|
|
|
transferEncoding(Headers) -> |
|
|
|
case lists:keyfind(?TRANSFER_ENCODING_HEADER, Headers) of |
|
|
|
false -> |
|
|
|
{?TRANSFER_ENCODING_HEADER, <<"chunked">>}; |
|
|
|
_ -> |
|
|
|
[] |
|
|
|
end. |
|
|
|
|
|
|
|
%% |
|
|
|
%% HTTP STATUS CODES |
|
|
|
%% |
|
|
|
|
|
|
|
%% @doc Response code string. Lifted from `cowboy_http_req.erl'. |
|
|
|
status(100) -> <<"100 Continue">>; |
|
|
|
status(101) -> <<"101 Switching Protocols">>; |
|
|
|
status(102) -> <<"102 Processing">>; |
|
|
|