|
|
@ -23,28 +23,28 @@ handle('GET', <<"/hello/world">>, WsReq) -> |
|
|
|
handle('GET', <<"/hello">>, WsReq) -> |
|
|
|
io:format("receive WsReq: ~p~n", [WsReq]), |
|
|
|
%% Fetch a GET argument from the URL. |
|
|
|
Name = wsReq:get_arg(<<"name">>, WsReq, <<"undefined">>), |
|
|
|
Name = proplists:get_value(<<"name">>, WsReq, <<"undefined">>), |
|
|
|
{ok, [], <<"Hello ", Name/binary>>}; |
|
|
|
|
|
|
|
handle('POST', <<"hello">>, WsReq) -> |
|
|
|
io:format("receive WsReq: ~p~n", [WsReq]), |
|
|
|
%% Fetch a POST argument from the POST body. |
|
|
|
Name = wsReq:post_arg(<<"name">>, WsReq, <<"undefined">>), |
|
|
|
Name = proplists:get_value(<<"name">>, WsReq, <<"undefined">>), |
|
|
|
%% Fetch and decode |
|
|
|
City = wsReq:post_arg_decoded(<<"city">>, WsReq, <<"undefined">>), |
|
|
|
City = proplists:get_value(<<"city">>, WsReq, <<"undefined">>), |
|
|
|
{ok, [], <<"Hello ", Name/binary, " of ", City/binary>>}; |
|
|
|
|
|
|
|
handle('GET', [<<"hello">>, <<"iolist">>], WsReq) -> |
|
|
|
io:format("receive WsReq: ~p~n", [WsReq]), |
|
|
|
%% Iolists will be kept as iolists all the way to the socket. |
|
|
|
Name = wsReq:get_arg(<<"name">>, WsReq), |
|
|
|
Name = proplists:get_value(<<"name">>, WsReq), |
|
|
|
{ok, [], [<<"Hello ">>, Name]}; |
|
|
|
|
|
|
|
handle('GET', [<<"type">>], WsReq) -> |
|
|
|
io:format("receive WsReq: ~p~n", [WsReq]), |
|
|
|
Name = wsReq:get_arg(<<"name">>, WsReq), |
|
|
|
Name = proplists:get_value(<<"name">>, WsReq), |
|
|
|
%% Fetch a header. |
|
|
|
case wsReq:get_header(<<"Accept">>, WsReq, <<"text/plain">>) of |
|
|
|
case proplists:get_value(<<"Accept">>, WsReq, <<"text/plain">>) of |
|
|
|
<<"text/plain">> -> |
|
|
|
{ok, [{<<"content-type">>, <<"text/plain; charset=ISO-8859-1">>}], |
|
|
|
<<"name: ", Name/binary>>}; |
|
|
@ -79,15 +79,12 @@ handle('GET', [<<"crash">>], WsReq) -> |
|
|
|
handle('GET', [<<"decoded-hello">>], WsReq) -> |
|
|
|
io:format("receive WsReq: ~p~n", [WsReq]), |
|
|
|
%% Fetch a URI decoded GET argument from the URL. |
|
|
|
Name = wsReq:get_arg_decoded(<<"name">>, WsReq, <<"undefined">>), |
|
|
|
Name = proplists:get_value(<<"name">>, WsReq, <<"undefined">>), |
|
|
|
{ok, [], <<"Hello ", Name/binary>>}; |
|
|
|
|
|
|
|
handle('GET', [<<"decoded-list">>], WsReq) -> |
|
|
|
io:format("receive WsReq: ~p~n", [WsReq]), |
|
|
|
%% Fetch a URI decoded GET argument from the URL. |
|
|
|
[{<<"name">>, Name}, {<<"foo">>, true}] = |
|
|
|
wsReq:get_args_decoded(WsReq), |
|
|
|
{ok, [], <<"Hello ", Name/binary>>}; |
|
|
|
{ok, [], <<"Hello">>}; |
|
|
|
|
|
|
|
|
|
|
|
handle('GET', [<<"sendfile">>], WsReq) -> |
|
|
@ -115,7 +112,7 @@ handle('GET', [<<"sendfile">>, <<"range">>], WsReq) -> |
|
|
|
%% range with sendfile, otherwise send the entire file when |
|
|
|
%% no range is present, or respond with a 416 if the range is invalid. |
|
|
|
F = "README.md", |
|
|
|
{ok, [], {file, F, wsReq:get_range(WsReq)}}; |
|
|
|
{ok, [], {file, F, get_range(WsReq)}}; |
|
|
|
|
|
|
|
handle('GET', [<<"compressed">>], WsReq) -> |
|
|
|
io:format("receive WsReq: ~p~n", [WsReq]), |
|
|
@ -144,8 +141,7 @@ handle('GET', [<<"chunked">>], WsReq) -> |
|
|
|
%% close the response. |
|
|
|
%% |
|
|
|
%% Return immediately {chunk, Headers} to signal we want to chunk. |
|
|
|
Ref = wsReq:chunk_ref(WsReq), |
|
|
|
spawn(fun() -> ?MODULE:chunk_loop(Ref) end), |
|
|
|
spawn(fun() -> ?MODULE:chunk_loop(not_support) end), |
|
|
|
{chunk, [{<<"Content-Type">>, <<"text/event-stream">>}]}; |
|
|
|
|
|
|
|
handle('GET', [<<"shorthand">>], WsReq) -> |
|
|
@ -154,7 +150,7 @@ handle('GET', [<<"shorthand">>], WsReq) -> |
|
|
|
|
|
|
|
handle('GET', [<<"ip">>], WsReq) -> |
|
|
|
io:format("receive WsReq: ~p~n", [WsReq]), |
|
|
|
{<<"200 OK">>, wsReq:peer(WsReq)}; |
|
|
|
{<<"200 OK">>, wsNet:peername(WsReq)}; |
|
|
|
|
|
|
|
handle('GET', [<<"304">>], WsReq) -> |
|
|
|
io:format("receive WsReq: ~p~n", [WsReq]), |
|
|
@ -182,6 +178,62 @@ handle(_, _, WsReq) -> |
|
|
|
{404, [], <<"Not Found">>}. |
|
|
|
|
|
|
|
|
|
|
|
%% @doc Parse the `Range' header from the request. |
|
|
|
%% The result is either a `byte_range_set()' or the atom `parse_error'. |
|
|
|
%% Use {@link elli_util:normalize_range/2} to get a validated, normalized range. |
|
|
|
-spec get_range(elli:wsReq()) -> [http_range()] | parse_error. |
|
|
|
get_range(#wsReq{headers = Headers}) -> |
|
|
|
case proplists:get_value(<<"range">>, Headers) of |
|
|
|
<<"bytes=", RangeSetBin/binary>> -> |
|
|
|
parse_range_set(RangeSetBin); |
|
|
|
_ -> [] |
|
|
|
end. |
|
|
|
|
|
|
|
|
|
|
|
-spec parse_range_set(Bin :: binary()) -> [http_range()] | parse_error. |
|
|
|
parse_range_set(<<ByteRangeSet/binary>>) -> |
|
|
|
RangeBins = binary:split(ByteRangeSet, <<",">>, [global]), |
|
|
|
Parsed = [parse_range(remove_whitespace(RangeBin)) |
|
|
|
|| RangeBin <- RangeBins], |
|
|
|
case lists:member(parse_error, Parsed) of |
|
|
|
true -> parse_error; |
|
|
|
false -> Parsed |
|
|
|
end. |
|
|
|
|
|
|
|
-spec remove_whitespace(binary()) -> binary(). |
|
|
|
remove_whitespace(Bin) -> |
|
|
|
binary:replace(Bin, <<" ">>, <<>>, [global]). |
|
|
|
|
|
|
|
-type http_range() :: {First :: non_neg_integer(), Last :: non_neg_integer()} |
|
|
|
| {offset, Offset :: non_neg_integer()} |
|
|
|
| {suffix, Length :: pos_integer()}. |
|
|
|
|
|
|
|
-spec parse_range(Bin :: binary()) -> http_range() | parse_error. |
|
|
|
parse_range(<<$-, SuffixBin/binary>>) -> |
|
|
|
%% suffix-byte-range |
|
|
|
try {suffix, binary_to_integer(SuffixBin)} |
|
|
|
catch |
|
|
|
error:badarg -> parse_error |
|
|
|
end; |
|
|
|
parse_range(<<ByteRange/binary>>) -> |
|
|
|
case binary:split(ByteRange, <<"-">>) of |
|
|
|
%% byte-range without last-byte-pos |
|
|
|
[FirstBytePosBin, <<>>] -> |
|
|
|
try {offset, binary_to_integer(FirstBytePosBin)} |
|
|
|
catch |
|
|
|
error:badarg -> parse_error |
|
|
|
end; |
|
|
|
%% full byte-range |
|
|
|
[FirstBytePosBin, LastBytePosBin] -> |
|
|
|
try {bytes, |
|
|
|
binary_to_integer(FirstBytePosBin), |
|
|
|
binary_to_integer(LastBytePosBin)} |
|
|
|
catch |
|
|
|
error:badarg -> parse_error |
|
|
|
end; |
|
|
|
_ -> parse_error |
|
|
|
end. |
|
|
|
|
|
|
|
%% @doc Send 10 separate chunks to the client. |
|
|
|
%% @equiv chunk_loop(Ref, 10) |
|
|
|
chunk_loop(Ref) -> |
|
|
@ -192,12 +244,51 @@ chunk_loop(Ref) -> |
|
|
|
%% When `N == 0', call {@link elli_request:close_chunk/1. |
|
|
|
%% elli_request:close_chunk(Ref)}. |
|
|
|
chunk_loop(Ref, 0) -> |
|
|
|
wsReq:close_chunk(Ref); |
|
|
|
close_chunk(Ref); |
|
|
|
chunk_loop(Ref, N) -> |
|
|
|
timer:sleep(10), |
|
|
|
|
|
|
|
case wsReq:send_chunk(Ref, [<<"chunk">>, integer_to_binary(N)]) of |
|
|
|
case send_chunk(Ref, [<<"chunk">>, integer_to_binary(N)]) of |
|
|
|
ok -> ok; |
|
|
|
{error, Reason} -> ?wsErr("error in sending chunk: ~p~n", [Reason]) |
|
|
|
end, |
|
|
|
chunk_loop(Ref, N - 1). |
|
|
|
chunk_loop(Ref, N - 1). |
|
|
|
|
|
|
|
%% @doc Return a reference that can be used to send chunks to the client. |
|
|
|
%% If the protocol does not support it, return `{error, not_supported}'. |
|
|
|
% chunk_ref(#wsReq{}) -> |
|
|
|
% {error, not_supported}. |
|
|
|
|
|
|
|
%% @doc Explicitly close the chunked connection. |
|
|
|
%% Return `{error, closed}' if the client already closed the connection. |
|
|
|
%% @equiv send_chunk(Ref, close) |
|
|
|
close_chunk(Ref) -> |
|
|
|
send_chunk(Ref, close). |
|
|
|
|
|
|
|
% %% @doc Send a chunk asynchronously. |
|
|
|
% async_send_chunk(Ref, Data) -> |
|
|
|
% Ref ! {chunk, Data}. |
|
|
|
|
|
|
|
%% @doc Send a chunk synchronously. |
|
|
|
%% If the referenced process is dead, return early with `{error, closed}', |
|
|
|
%% instead of timing out. |
|
|
|
send_chunk(Ref, Data) -> |
|
|
|
?IIF(is_ref_alive(Ref), |
|
|
|
send_chunk(Ref, Data, 5000), |
|
|
|
{error, closed}). |
|
|
|
|
|
|
|
is_ref_alive(Ref) -> |
|
|
|
?IIF(node(Ref) =:= node(), |
|
|
|
is_process_alive(Ref), |
|
|
|
rpc:call(node(Ref), erlang, is_process_alive, [Ref])). |
|
|
|
|
|
|
|
send_chunk(Ref, Data, Timeout) -> |
|
|
|
Ref ! {chunk, Data, self()}, |
|
|
|
receive |
|
|
|
{Ref, ok} -> |
|
|
|
ok; |
|
|
|
{Ref, {error, Reason}} -> |
|
|
|
{error, Reason} |
|
|
|
after Timeout -> |
|
|
|
{error, timeout} |
|
|
|
end. |