소스 검색

ft: 代码修改

master
SisMaker 3 년 전
부모
커밋
5b5a5b5d17
11개의 변경된 파일510개의 추가작업 그리고 161개의 파일을 삭제
  1. +20
    -11
      include/eWSrv.hrl
  2. +2
    -1
      src/eWSrv.erl
  3. +2
    -2
      src/example/wsEgHer.erl
  4. +1
    -1
      src/test/elli_test.erl
  5. +11
    -11
      src/test/elli_tests.erl
  6. +56
    -0
      src/test/wsTest.erl
  7. +6
    -6
      src/wsSrv/wsConSup.erl
  8. +137
    -65
      src/wsSrv/wsHttp.erl
  9. +216
    -0
      src/wsSrv/wsHttpProtocol.erl
  10. +26
    -31
      src/wsSrv/wsHttps.erl
  11. +33
    -33
      src/wsSrv/wsReq.erl

+ 20
- 11
include/eWSrv.hrl 파일 보기

@ -1,5 +1,10 @@
-include_lib("eNet/include/eNet.hrl").
%% @type version(). HTTP version as a tuple, i.e. `{0, 9} | {1, 0} | {1, 1}'.
-type version() :: {0, 9} | {1, 0} | {1, 1}.
-export_type([version/0]).
-define(DefWsOpts, [
binary
, {packet, 4}
@ -18,8 +23,8 @@
listenOpt() |
{wsMod, module()}.
-record(req, {
method :: elli:http_method(),
-record(wsReq, {
method :: method(),
scheme :: undefined | binary(),
host :: undefined | binary(),
port :: undefined | 1..65535,
@ -27,22 +32,20 @@
args :: [{binary(), any()}],
raw_path :: binary(),
version :: wsHttp:version(),
headers :: elli:headers(),
original_headers :: elli:headers(),
body :: elli:body(),
pid :: pid(),
socket :: undefined | wsNet:socket(),
callback :: wsHer:callback()
headers :: headers(),
original_headers :: headers(),
body :: body(),
pid :: pid()
}).
-export_type([req/0, http_method/0, body/0, headers/0, response_code/0]).
-export_type([req/0, method/0, body/0, headers/0, response_code/0]).
%% @type req(). A record representing an HTTP request.
-type req() :: #req{}.
-type req() :: #wsReq{}.
%% @type http_method(). An uppercase atom representing a known HTTP verb or a
%% binary for other verbs.
-type http_method() :: 'OPTIONS' | 'GET' | 'HEAD' | 'POST'| 'PUT' | 'DELETE' | 'TRACE' | binary().
-type method() :: 'OPTIONS' | 'GET' | 'HEAD' | 'POST'| 'PUT' | 'DELETE' | 'TRACE' | binary().
%% @type body(). A binary or iolist.
-type body() :: binary() | iolist().
@ -55,6 +58,12 @@
-define(EXAMPLE_CONF, [{callback, elli_example_callback}, {callback_args, []}]).
-define(CONTENT_LENGTH_HEADER, <<"content-length">>).
-define(EXPECT_HEADER, <<"expect">>).
-define(CONNECTION_HEADER, <<"connection">>).
-define(TRANSFER_ENCODING_HEADER, <<"Transfer-Encoding">>).
-export_type([callback/0, callback_mod/0, callback_args/0, event/0, result/0]).

+ 2
- 1
src/eWSrv.erl 파일 보기

@ -11,6 +11,7 @@ startWSrv(WSrvName, Port, WsOpts) ->
ListenName = wsUtil:lsName(WSrvName),
TWsOpts = lists:keystore(conMod, 1, WsOpts, {conMod, wsHttp}),
LWsOpts = lists:keystore(conArgs, 1, TWsOpts, {conArgs, WSrvName}),
WsMod = ?wsGLV(wsMod, WsOpts, wsEgHer),
case ?wsGLV(sslOpts, WsOpts, false) of
false ->
{ok, _} = eNet:openTcp(ListenName, Port, LWsOpts);
@ -20,7 +21,7 @@ startWSrv(WSrvName, Port, WsOpts) ->
ConSupSpec = #{
id => WSrvName,
start => {wsConSup, start_link, [WSrvName]},
start => {wsConSup, start_link, [WSrvName, WsMod]},
restart => permanent,
shutdown => infinity,
type => supervisor,

+ 2
- 2
src/example/wsEgHer.erl 파일 보기

@ -27,7 +27,7 @@
Req :: elli:req(),
_Args :: wsHer:callback_args(),
Result :: wsHer:result().
handle(Req, _Args) -> handle(Req#req.method, wsReq:path(Req), Req).
handle(Req, _Args) -> handle(Req#wsReq.method, wsReq:path(Req), Req).
%% @doc Route `Method' and `Path' to the appropriate clause.
@ -63,7 +63,7 @@ handle(Req, _Args) -> handle(Req#req.method, wsReq:path(Req), Req).
%% @see elli_request:chunk_ref/1
%% @see chunk_loop/1
-spec handle(Method, Path, Req) -> wsHer:result() when
Method :: elli:http_method(),
Method :: elli:method(),
Path :: [binary()],
Req :: elli:req().
handle('GET', [<<"hello">>, <<"world">>], _Req) ->

+ 1
- 1
src/test/elli_test.erl 파일 보기

@ -13,7 +13,7 @@
-export([call/5]).
-spec call(Method, Path, Headers, Body, Opts) -> wsHer:result() when
Method :: elli:http_method(),
Method :: elli:method(),
Path :: binary(),
Headers :: elli:headers(),
Body :: elli:body(),

+ 11
- 11
src/test/elli_tests.erl 파일 보기

@ -112,13 +112,13 @@ accessors_test_() ->
Method = 'POST',
Body = <<"name=knut%3D">>,
Name = <<"knut=">>,
Req1 = #req{raw_path = RawPath,
Req1 = #wsReq{raw_path = RawPath,
original_headers = Headers,
headers = Headers,
method = Method,
body = Body},
Args = [{<<"name">>, Name}],
Req2 = #req{original_headers = Headers, headers = Headers, args = Args, body = <<>>},
Req2 = #wsReq{original_headers = Headers, headers = Headers, args = Args, body = <<>>},
[
%% POST /foo/bar
@ -138,7 +138,7 @@ accessors_test_() ->
?_assertMatch(Name, wsReq:get_arg_decoded(<<"name">>, Req2)),
?_assertMatch([], wsReq:post_args(Req2)),
?_assertMatch({error, not_supported}, wsReq:chunk_ref(#req{}))
?_assertMatch({error, not_supported}, wsReq:chunk_ref(#wsReq{}))
].
@ -628,12 +628,12 @@ body_qs_test() ->
{<<"found">>, true}],
Body = <<"foo=bar&baz=bang&found">>,
Headers = [{<<"content-type">>, <<"application/x-www-form-urlencoded">>}],
?assertMatch(Expected, wsReq:body_qs(#req{body = Body,
?assertMatch(Expected, wsReq:body_qs(#wsReq{body = Body,
original_headers = Headers,
headers = Headers})).
to_proplist_test() ->
Req = #req{method = 'GET',
Req = #wsReq{method = 'GET',
path = [<<"crash">>],
args = [],
version = {1, 1},
@ -662,12 +662,12 @@ to_proplist_test() ->
?assertEqual(Prop, wsReq:to_proplist(Req)).
is_request_test() ->
?assert(wsReq:is_request(#req{})),
?assert(wsReq:is_request(#wsReq{})),
?assertNot(wsReq:is_request({req, foobar})).
query_str_test_() ->
MakeReq = fun(Path) -> #req{raw_path = Path} end,
MakeReq = fun(Path) -> #wsReq{raw_path = Path} end,
[
%% For empty query strings, expect `query_str` to return an empty binary.
?_assertMatch(<<>>, wsReq:query_str(MakeReq(<<"/foo">>))),
@ -679,11 +679,11 @@ query_str_test_() ->
get_range_test_() ->
Req = #req{headers = [{<<"range">>,
Req = #wsReq{headers = [{<<"range">>,
<<"bytes=0-99 ,500-999 , -800">>}]},
OffsetReq = #req{headers = [{<<"range">>, <<"bytes=200-">>}]},
UndefReq = #req{headers = []},
BadReq = #req{headers = [{<<"range">>, <<"bytes=--99,hallo-world">>}]},
OffsetReq = #wsReq{headers = [{<<"range">>, <<"bytes=200-">>}]},
UndefReq = #wsReq{headers = []},
BadReq = #wsReq{headers = [{<<"range">>, <<"bytes=--99,hallo-world">>}]},
ByteRangeSet = [{bytes, 0, 99}, {bytes, 500, 999}, {suffix, 800}],

+ 56
- 0
src/test/wsTest.erl 파일 보기

@ -0,0 +1,56 @@
-module(wsTest).
-compile([export_all]).
-define(Line, <<"GET /test/tttt HTTP/1.1\r\n">>).
test1(Rn) ->
parseRequestLine(?Line, Rn).
test2() ->
erlang:decode_packet(http_bin, ?Line, []).
parseRequestLine(Data, Rn) ->
case binary:split(Data, Rn) of
[Data] ->
not_enough_data;
[Line, Rest] ->
case binary:split(Line, <<" ">>, [global]) of
[Method, RawPath, V] ->
{Method, RawPath, V, Rest};
_ ->
{error, request_line}
end
end.
%% <<"Content-Type: application/json; charset=utf-8">>,
-spec request(boolean(), body(), method(), host(), binary(), path(), headers()) -> iolist().
request(true, undefined, Method, Host, _DbName, Path, Headers) ->
[
Method, <<"/_db/_system">>, Path, <<" HTTP/1.1\r\nHost: ">>, Host,
<<"\r\nContent-Type: application/json; charset=utf-8\r\nContent-Length: 0\r\n">>,
spellHeaders(Headers), <<"\r\n">>
];
request(false, undefined, Method, Host, DbName, Path, Headers) ->
[
Method, DbName, Path, <<" HTTP/1.1\r\nHost: ">>, Host,
<<"\r\nContent-Type: application/json; charset=utf-8\r\nContent-Length: 0\r\n">>,
spellHeaders(Headers), <<"\r\n">>
];
request(false, Body, Method, Host, DbName, Path, Headers) ->
ContentLength = integer_to_binary(iolist_size(Body)),
NewHeaders = [{<<"Content-Length">>, ContentLength} | Headers],
[
Method, DbName, Path, <<" HTTP/1.1\r\nHost: ">>, Host,
<<"\r\nContent-Type: application/json; charset=utf-8\r\n">>,
spellHeaders(NewHeaders), <<"\r\n">>, Body
];
request(true, Body, Method, Host, _DbName, Path, Headers) ->
ContentLength = integer_to_binary(iolist_size(Body)),
NewHeaders = [{<<"Content-Length">>, ContentLength} | Headers],
[
Method, <<"/_db/_system">>, Path, <<" HTTP/1.1\r\nHost: ">>, Host,
<<"\r\nContent-Type: application/json; charset=utf-8\r\n">>,
spellHeaders(NewHeaders), <<"\r\n">>, Body
].

+ 6
- 6
src/wsSrv/wsConSup.erl 파일 보기

@ -3,23 +3,23 @@
-behaviour(supervisor).
-export([
start_link/1
start_link/2
]).
-export([
init/1
]).
-spec(start_link(SupName :: atom()) -> {ok, pid()}).
start_link(SupName) ->
supervisor:start_link({local, SupName}, ?MODULE, undefined).
-spec(start_link(SupName :: atom(), WsMod :: module()) -> {ok, pid()}).
start_link(SupName, WsMod) ->
supervisor:start_link({local, SupName}, ?MODULE, WsMod).
init(_Args) ->
init(WsMod) ->
SupFlags = #{strategy => simple_one_for_one, intensity => 100, period => 3600},
WsHttpSpec = #{
id => wsHttp,
start => {wsHttp, start_link, []},
start => {wsHttp, start_link, [WsMod]},
restart => transient,
shutdown => 3000,
type => worker,

+ 137
- 65
src/wsSrv/wsHttp.erl 파일 보기

@ -1,51 +1,128 @@
%% @doc: Elli HTTP request implementation
%%
%% 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(wsHttp).
-behavior(gen_srv).
-include_lib("eNet/include/eNet.hrl").
-include("eWSrv.hrl").
-include("wsCom.hrl").
-export([
start_link/1
, send_response/4
, send_file/5
, mk_req/8
, mk_req/11
%% Exported for looping with a fully-qualified module name
, accept/4
, handle_request/4
, chunk_loop/1
, split_args/1
, parse_path/1
, keepalive_loop/3
, keepalive_loop/5
, close_or_keepalive/2
]).
%% eNet callback
-export([newCon/2]).
-export([
init/1
, handleCall/3
, handleCast/2
, handleInfo/2
, terminate/2
, code_change/3
]).
-define(SERVER, ?MODULE).
-record(state, {
stage = reqLine :: reqLine | header | body | done | error %% http请求可能会有多个包
, buffer = <<>> :: binary()
, wsReq :: undefined | #wsReq{}
, contentLength :: undefined | non_neg_integer() | chunked
, wsMod :: callback()
, socket :: undefined | wsNet:socket()
, isSsl = false :: boolean()
}).
newCon(_Sock, SupPid) ->
supervisor:start_link(SupPid, []).
%% ******************************************** API *******************************************************************
start_link(WsMod) ->
gen_srv:start_link(?MODULE, WsMod, []).
%% ******************************************** callback **************************************************************
init(WsMod) ->
{ok, #state{wsMod = WsMod}}.
handleCall(_Msg, _State, _FROM) ->
?wsErr("~p call receive unexpect msg ~p ~n ", [?MODULE, _Msg]),
{reply, ok}.
%%
handleCast(_Msg, _State) ->
?wsErr("~p cast receive unexpect msg ~p ~n ", [?MODULE, _Msg]),
kpS.
handleInfo({tcp, Socket, Data}, #state{stage = Stage, buffer = Buffer} = State) ->
ok;
handleInfo({tcp_closed, Socket}, _State) ->
ok;
handleInfo({tcp_error, Socket, Reason}, _State) ->
ok;
handleInfo({ssl, Socket, Data}, _State) ->
ok;
handleInfo({ssl_closed, Socket}, _State) ->
ok;
handleInfo({ssl_error, Socket, Reason}, _State) ->
ok;
handleInfo({ssl, Socket, Data}, _State) ->
ok;
handleInfo({?mSockReady, Sock}, _State) ->
inet:setopts(Sock, [{packet, raw}, {active, true}]),
{ok, #state{socket = Sock}};
handleInfo({?mSockReady, Sock, SslOpts, SslHSTet}, State) ->
case ntSslAcceptor:handshake(Sock, SslOpts, SslHSTet) of
{ok, SslSock} ->
ssl:setopts(Sock, [{packet, raw}, {active, true}]),
{ok, State#state{socket = SslSock, isSsl = true}};
_Err ->
?wsErr("ssl handshake error ~p~n", [_Err]),
{stop, _Err, State}
end;
handleInfo(_Msg, _State) ->
?wsErr("~p info receive unexpect msg ~p ~n ", [?MODULE, _Msg]),
kpS.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%% API
-export([start_link/4]).
-export([send_response/4]).
-export([send_file/5]).
-export([mk_req/8, mk_req/11]). %% useful when testing.
%% Exported for looping with a fully-qualified module name
-export([accept/4, handle_request/4, chunk_loop/1, split_args/1,
parse_path/1, keepalive_loop/3, keepalive_loop/5]).
%% Exported for correctly handling session keep-alive for handlers
%% operating in handler mode.
-export([close_or_keepalive/2]).
-export_type([version/0]).
%% @type version(). HTTP version as a tuple, i.e. `{0, 9} | {1, 0} | {1, 1}'.
-type version() :: {0, 9} | {1, 0} | {1, 1}.
-define(CONTENT_LENGTH_HEADER, <<"content-length">>).
-define(EXPECT_HEADER, <<"expect">>).
-define(CONNECTION_HEADER, <<"connection">>).
-define(TRANSFER_ENCODING_HEADER, <<"Transfer-Encoding">>).
%% TODO: use this
%% -type connection_token() :: keep_alive | close.
-spec start_link(Server, ListenSocket, Options, Callback) -> pid() when
Server :: pid(),
ListenSocket :: wsNet:socket(),
Options :: proplists:proplist(),
Callback :: wsHer:callback().
start_link(Server, ListenSocket, Options, Callback) ->
proc_lib:spawn_link(?MODULE, accept, [Server, ListenSocket, Options, Callback]).
%% @doc Accept on the socket until a client connects.
%% Handle the request, then loop if we're using keep alive or chunked transfer.
@ -108,7 +185,7 @@ handle_request(S, PrevB, Opts, {Mod, Args} = Callback) ->
t(body_start),
{RequestBody, B2} = get_body(S, ParsedRequestHeaders, B1, Opts, Callback),
t(body_end),
Req1 = Req#req{body = RequestBody},
Req1 = Req#wsReq{body = RequestBody},
t(user_start),
Response = execute_callback(Req1),
@ -116,7 +193,7 @@ handle_request(S, PrevB, Opts, {Mod, Args} = Callback) ->
handle_response(Req1, B2, Response);
{ok, handover} ->
Req1 = Req#req{body = B1},
Req1 = Req#wsReq{body = B1},
t(user_start),
Response = Mod:handle(Req1, Args),
@ -128,7 +205,7 @@ handle_request(S, PrevB, Opts, {Mod, Args} = Callback) ->
end.
handle_response(Req, Buffer, {response, Code, UserHeaders, Body}) ->
#req{callback = {Mod, Args}} = Req,
#wsReq{callback = {Mod, Args}} = Req,
Headers = [connection(Req, UserHeaders),
content_length(UserHeaders, Body)
@ -145,7 +222,7 @@ handle_response(Req, Buffer, {response, Code, UserHeaders, Body}) ->
handle_response(Req, _Buffer, {chunk, UserHeaders, Initial}) ->
#req{callback = {Mod, Args}} = Req,
#wsReq{callback = {Mod, Args}} = Req,
ResponseHeaders = [{?TRANSFER_ENCODING_HEADER, <<"chunked">>},
connection(Req, UserHeaders)
@ -153,8 +230,8 @@ handle_response(Req, _Buffer, {chunk, UserHeaders, Initial}) ->
send_response(Req, 200, ResponseHeaders, <<"">>),
t(send_start),
Initial =:= <<"">> orelse send_chunk(Req#req.socket, Initial),
ClosingEnd = case start_chunk_loop(Req#req.socket) of
Initial =:= <<"">> orelse send_chunk(Req#wsReq.socket, Initial),
ClosingEnd = case start_chunk_loop(Req#wsReq.socket) of
{error, client_closed} -> client;
ok -> server
end,
@ -170,15 +247,15 @@ handle_response(Req, _Buffer, {chunk, UserHeaders, Initial}) ->
handle_response(Req, Buffer, {file, ResponseCode, UserHeaders,
Filename, Range}) ->
#req{callback = {Mod, Args}} = Req,
#wsReq{callback = {Mod, Args}} = Req,
ResponseHeaders = [connection(Req, UserHeaders) | UserHeaders],
case wsUtil:file_size(Filename) of
{error, FileError} ->
handle_event(Mod, file_error, [FileError], Args),
send_server_error(Req#req.socket),
wsNet:close(Req#req.socket),
send_server_error(Req#wsReq.socket),
wsNet:close(Req#wsReq.socket),
exit(normal);
Size ->
t(send_start),
@ -206,11 +283,7 @@ handle_response(Req, Buffer, {file, ResponseCode, UserHeaders,
t(send_end),
t(request_end),
handle_event(Mod, request_complete,
[Req, ResponseCode, ResponseHeaders, <<>>,
{get_timings(),
get_sizes()}],
Args),
handle_event(Mod, request_complete, [Req, ResponseCode, ResponseHeaders, <<>>, {get_timings(), get_sizes()}], Args),
{close_or_keepalive(Req, UserHeaders), Buffer}
end.
@ -220,7 +293,7 @@ handle_response(Req, Buffer, {file, ResponseCode, UserHeaders,
send_response(Req, Code, Headers, UserBody) ->
ResponseHeaders = assemble_response_headers(Code, Headers),
Body = case {Req#req.method, Code} of
Body = case {Req#wsReq.method, Code} of
{'HEAD', _} -> <<>>;
{_, 304} -> <<>>;
{_, 204} -> <<>>;
@ -231,10 +304,10 @@ send_response(Req, Code, Headers, UserBody) ->
Response = [ResponseHeaders,
Body],
case wsNet:send(Req#req.socket, Response) of
case wsNet:send(Req#wsReq.socket, Response) of
ok -> ok;
{error, Closed} when Closed =:= closed orelse Closed =:= enotconn ->
#req{callback = {Mod, Args}} = Req,
#wsReq{callback = {Mod, Args}} = Req,
handle_event(Mod, client_closed, [before_response], Args),
ok
end.
@ -248,23 +321,23 @@ send_response(Req, Code, Headers, UserBody) ->
Headers :: elli:headers(),
Filename :: file:filename(),
Range :: wsUtil:range().
send_file(#req{callback = {Mod, Args}} = Req, Code, Headers, Filename, Range) ->
send_file(#wsReq{callback = {Mod, Args}} = Req, Code, Headers, Filename, Range) ->
ResponseHeaders = assemble_response_headers(Code, Headers),
case file:open(Filename, [read, raw, binary]) of
{ok, Fd} -> do_send_file(Fd, Range, Req, ResponseHeaders);
{error, FileError} ->
handle_event(Mod, file_error, [FileError], Args),
send_server_error(Req#req.socket),
wsNet:close(Req#req.socket),
send_server_error(Req#wsReq.socket),
wsNet:close(Req#wsReq.socket),
exit(normal)
end,
ok.
do_send_file(Fd, {Offset, Length}, #req{callback = {Mod, Args}} = Req, Headers) ->
try wsNet:send(Req#req.socket, Headers) of
do_send_file(Fd, {Offset, Length}, #wsReq{callback = {Mod, Args}} = Req, Headers) ->
try wsNet:send(Req#wsReq.socket, Headers) of
ok ->
case wsNet:sendfile(Fd, Req#req.socket, Offset, Length, []) of
case wsNet:sendfile(Fd, Req#wsReq.socket, Offset, Length, []) of
{ok, BytesSent} -> s(file, BytesSent), ok;
{error, Closed} when Closed =:= closed orelse
Closed =:= enotconn ->
@ -291,7 +364,7 @@ send_rescue_response(Socket, Code, Body) ->
wsNet:send(Socket, Response).
%% @doc Execute the user callback, translating failure into a proper response.
execute_callback(#req{callback = {Mod, Args}} = Req) ->
execute_callback(#wsReq{callback = {Mod, Args}} = Req) ->
try Mod:handle(Req, Args) of
%% {ok,...{file,...}}
{ok, Headers, {file, Filename}} ->
@ -588,7 +661,7 @@ do_check_max_size_x2(Socket, ContentLength, Buffer, MaxSize)
do_check_max_size_x2(_, _, _, _) -> ok.
-spec mk_req(Method, PathTuple, Headers, Headers, Body, V, Socket, Callback) -> Req when
Method :: elli:http_method(),
Method :: elli:method(),
PathTuple :: {PathType :: atom(), RawPath :: binary()},
Headers :: elli:headers(),
Body :: elli:body(),
@ -599,7 +672,7 @@ do_check_max_size_x2(_, _, _, _) -> ok.
mk_req(Method, PathTuple, Headers, ParsedHeaders, Body, V, Socket, {Mod, Args} = Callback) ->
case parse_path(PathTuple) of
{ok, {Scheme, Host, Port}, {Path, URL, URLArgs}} ->
#req{method = Method, scheme = Scheme, host = Host,
#wsReq{method = Method, scheme = Scheme, host = Host,
port = Port, path = URL, args = URLArgs,
version = V, raw_path = Path, original_headers = Headers,
body = Body, pid = self(), socket = Socket,
@ -614,7 +687,7 @@ mk_req(Method, PathTuple, Headers, ParsedHeaders, Body, V, Socket, {Mod, Args} =
mk_req(Method, Scheme, Host, Port, PathTuple, Headers, ParsedHeaders, Body, V, Socket, Callback) ->
Req = mk_req(Method, PathTuple, Headers, ParsedHeaders, Body, V, Socket, Callback),
Req#req{scheme = Scheme, host = Host, port = Port}.
Req#wsReq{scheme = Scheme, host = Host, port = Port}.
%%
%% HEADERS
@ -649,18 +722,18 @@ encode_value(V) when is_integer(V) -> integer_to_binary(V);
encode_value(V) when is_binary(V) -> V;
encode_value(V) when is_list(V) -> list_to_binary(V).
connection_token(#req{version = {1, 1}, headers = Headers}) ->
connection_token(#wsReq{version = {1, 1}, headers = Headers}) ->
case get_header(?CONNECTION_HEADER, Headers) of
<<"close">> -> <<"close">>;
<<"Close">> -> <<"close">>;
_ -> <<"Keep-Alive">>
end;
connection_token(#req{version = {1, 0}, headers = Headers}) ->
connection_token(#wsReq{version = {1, 0}, headers = Headers}) ->
case get_header(?CONNECTION_HEADER, Headers) of
<<"Keep-Alive">> -> <<"Keep-Alive">>;
_ -> <<"close">>
end;
connection_token(#req{version = {0, 9}}) ->
connection_token(#wsReq{version = {0, 9}}) ->
<<"close">>.
%% @doc Return the preferred session handling setting to close or keep the
@ -790,7 +863,7 @@ split_args(Qs) ->
%% CALLBACK HELPERS
%%
init(#req{callback = {Mod, Args}} = Req) ->
init(#wsReq{callback = {Mod, Args}} = Req) ->
?IIF(erlang:function_exported(Mod, init, 2),
case Mod:init(Req, Args) of
ignore -> {ok, standard};
@ -803,8 +876,7 @@ handle_event(Mod, Name, EventArgs, ElliArgs) ->
Mod:handle_event(Name, EventArgs, ElliArgs)
catch
EvClass:EvError:Stacktrace ->
?wsErr("~p:handle_event/3 crashed ~p:~p~n~p",
[Mod, EvClass, EvError, Stacktrace])
?wsErr("~p:handle_event/3 crashed ~p:~p~n~p", [Mod, EvClass, EvError, Stacktrace])
end.
%%

+ 216
- 0
src/wsSrv/wsHttpProtocol.erl 파일 보기

@ -0,0 +1,216 @@
-module(wsHttpProtocol).
-include("eWSrv.hrl").
-include("wsCom.hrl").
-compile(inline).
-compile({inline_size, 128}).
-export([
request/2
]).
-spec request(undefined | recvState(), binary()) -> {ok, recvState()} | error().
request(reqLine, Data) ->
case erlang:decode_packet(http_bin, Data, []) of
{more, _} ->
reqLine;
{ok, {http_request, Method, RawPath, Version}, Rest} ->
case byte_size(Rest) > 0 of
true ->
request(header, Rest);
_ ->
{header, Method, RawPath, Version}
end;
{ok, {http_error, ErrStr}, _} ->
{error, ErrStr};
{ok, {http_response, _, _, _}, _} ->
{error, http_response};
{error, _Reason} = Ret ->
Ret
end.
case parseRequestLine(Data) of
{StatusCode, Rest} ->
case splitHeaders(Rest, Rn, RnRn) of
{undefined, Headers, Body} ->
{done, #recvState{stage = done, statusCode = StatusCode, headers = Headers, contentLength = undefined, body = Body}};
{0, Headers, Rest} ->
{done, #recvState{stage = done, statusCode = StatusCode, headers = Headers, contentLength = 0, body = Rest}};
{chunked, Headers, Body} ->
case IsHeadMethod orelse StatusCode == 204 orelse StatusCode == 304 orelse (StatusCode < 200 andalso StatusCode >= 100) of
true ->
{done, #recvState{stage = done, statusCode = StatusCode, headers = Headers, contentLength = 0, body = Body}};
_ ->
RecvState = #recvState{stage = body, contentLength = chunked, statusCode = StatusCode, headers = Headers},
request(RecvState, Rn, RnRn, Body, IsHeadMethod)
end;
{ContentLength, Headers, Body} ->
BodySize = erlang:size(Body),
if
BodySize == ContentLength ->
{done, #recvState{stage = done, statusCode = StatusCode, headers = Headers, contentLength = ContentLength, body = Body}};
BodySize > ContentLength ->
?wsWarn("11 contentLength get to long data why? more: ~p ~n", [BodySize - ContentLength]),
{done, #recvState{stage = done, statusCode = StatusCode, headers = Headers, contentLength = ContentLength, body = Body}};
true ->
case IsHeadMethod orelse StatusCode == 204 orelse StatusCode == 304 orelse (StatusCode < 200 andalso StatusCode >= 100) of
true ->
{done, #recvState{stage = done, statusCode = StatusCode, headers = Headers, contentLength = ContentLength, body = Body}};
_ ->
{ok, #recvState{stage = body, statusCode = StatusCode, headers = Headers, contentLength = ContentLength, body = Body}}
end
end;
not_enough_data ->
{ok, #recvState{stage = header, body = Data}}
end;
not_enough_data ->
{ok, #recvState{stage = header, body = Data}};
{error, Reason} ->
{error, Reason}
end;
request(#recvState{stage = body, contentLength = chunked, body = Body, buffer = Buffer} = RecvState, Rn, _RnRn, Data, _IsHeadMethod) ->
NewBuffer = <<Buffer/binary, Data/binary>>,
case parseChunks(NewBuffer, Rn, []) of
{ok, AddBody, _Rest} ->
LastBody = <<Body/binary, AddBody/binary>>,
{done, RecvState#recvState{stage = done, body = LastBody}};
{not_enough_data, AddBody, Rest} ->
NewBody = <<Body/binary, AddBody/binary>>,
{ok, RecvState#recvState{body = NewBody, buffer = Rest}};
{error, Reason} ->
{error, Reason}
end;
request(#recvState{stage = body, contentLength = ContentLength, body = Body} = RecvState, _Rn, _RnRn, Data, _IsHeadMethod) ->
CurData = <<Body/binary, Data/binary>>,
BodySize = erlang:size(CurData),
if
BodySize == ContentLength ->
{done, RecvState#recvState{stage = done, body = CurData}};
BodySize > ContentLength ->
?wsWarn("22 contentLength get to long data why? more: ~p ~n", [BodySize - ContentLength]),
{done, #recvState{stage = done, body = CurData}};
true ->
{ok, RecvState#recvState{body = CurData}}
end;
request(#recvState{stage = header, body = OldBody}, Rn, RnRn, Data, IsHeadMethod) ->
CurBody = <<OldBody/binary, Data/binary>>,
case parseRequestLine(CurBody, Rn) of
{StatusCode, Rest} ->
case splitHeaders(Rest, Rn, RnRn) of
{undefined, Headers, Body} ->
{done, #recvState{stage = done, statusCode = StatusCode, headers = Headers, contentLength = undefined, body = Body}};
{0, Headers, Body} ->
{done, #recvState{stage = done, statusCode = StatusCode, headers = Headers, contentLength = 0, body = Body}};
{chunked, Headers, Rest} ->
case IsHeadMethod orelse StatusCode == 204 orelse StatusCode == 304 orelse (StatusCode < 200 andalso StatusCode >= 100) of
true ->
{done, #recvState{stage = done, statusCode = StatusCode, headers = Headers, contentLength = 0, body = <<>>}};
_ ->
RecvState = #recvState{stage = body, contentLength = chunked, statusCode = StatusCode, headers = Headers},
request(RecvState, Rn, RnRn, Rest, IsHeadMethod)
end;
{ContentLength, Headers, Body} ->
BodySize = erlang:size(Body),
if
BodySize == ContentLength ->
{done, #recvState{stage = done, statusCode = StatusCode, headers = Headers, contentLength = ContentLength, body = Body}};
BodySize > ContentLength ->
?wsWarn("33 contentLength get to long data why? more: ~p ~n", [BodySize - ContentLength]),
{done, #recvState{stage = done, statusCode = StatusCode, headers = Headers, contentLength = ContentLength, body = Body}};
true ->
case IsHeadMethod orelse StatusCode == 204 orelse StatusCode == 304 orelse (StatusCode < 200 andalso StatusCode >= 100) of
true ->
{done, #recvState{stage = done, statusCode = StatusCode, headers = Headers, contentLength = ContentLength, body = Body}};
_ ->
{ok, #recvState{stage = body, statusCode = StatusCode, headers = Headers, contentLength = ContentLength, body = Body}}
end
end;
not_enough_data ->
{ok, #recvState{stage = header, body = CurBody}}
end;
not_enough_data ->
{ok, #recvState{stage = header, body = CurBody}};
{error, Reason} ->
{error, Reason}
end.
splitHeaders(Data, Rn, RnRn) ->
case binary:split(Data, RnRn) of
[Data] ->
not_enough_data;
[Headers, Body] ->
HeadersList = binary:split(Headers, Rn, [global]),
ContentLength = contentLength(HeadersList),
{ContentLength, Headers, Body}
end.
contentLength([]) ->
undefined;
contentLength([<<"Content-Length: ", Rest/binary>> | _T]) ->
binary_to_integer(Rest);
contentLength([<<"content-length: ", Rest/binary>> | _T]) ->
binary_to_integer(Rest);
contentLength([<<"Transfer-Encoding: chunked">> | _T]) ->
chunked;
contentLength([<<"transfer-encoding: chunked">> | _T]) ->
chunked;
contentLength([_ | T]) ->
contentLength(T).
parseRequestLine(Data, Rn) ->
case binary:split(Data, Rn) of
[Data] ->
not_enough_data;
[Line, Rest] ->
case binary:split(Line, <<" ">>) of
[Method, RawPath, V] ->
{Method, RawPath, V, Rest};
_ ->
{error, request_line}
end
end.
parseChunks(Data, Rn, Acc) ->
case parseChunk(Data, Rn) of
done ->
{ok, iolist_to_binary(lists:reverse(Acc)), <<>>};
{ok, Body, Rest} ->
parseChunks(Rest, Rn, [Body | Acc]);
not_enough_data ->
{not_enough_data, iolist_to_binary(lists:reverse(Acc)), Data};
{error, Reason} ->
{error, Reason}
end.
parseChunk(Data, Rn) ->
case binary:split(Data, Rn) of
[Size, Rest] ->
case parseChunkSize(Size) of
undefined ->
{error, invalid_chunk_size};
0 ->
done;
HexSize ->
parseChunkBody(Rest, HexSize)
end;
[Data] ->
not_enough_data
end.
parseChunkBody(Data, Size) ->
case Data of
<<Body:Size/binary, "\r\n", Rest/binary>> ->
{ok, Body, Rest};
_ ->
not_enough_data
end.
parseChunkSize(Bin) ->
try
binary_to_integer(Bin, 16)
catch
error:badarg ->
undefined
end.

+ 26
- 31
src/wsSrv/wsHttps.erl 파일 보기

@ -25,11 +25,6 @@
%% operating in handler mode.
-export([close_or_keepalive/2]).
-export_type([version/0]).
%% @type version(). HTTP version as a tuple, i.e. `{0, 9} | {1, 0} | {1, 1}'.
-type version() :: {0, 9} | {1, 0} | {1, 1}.
-define(CONTENT_LENGTH_HEADER, <<"content-length">>).
-define(EXPECT_HEADER, <<"expect">>).
@ -110,7 +105,7 @@ handle_request(S, PrevB, Opts, {Mod, Args} = Callback) ->
t(body_start),
{RequestBody, B2} = get_body(S, ParsedRequestHeaders, B1, Opts, Callback),
t(body_end),
Req1 = Req#req{body = RequestBody},
Req1 = Req#wsReq{body = RequestBody},
t(user_start),
Response = execute_callback(Req1),
@ -118,7 +113,7 @@ handle_request(S, PrevB, Opts, {Mod, Args} = Callback) ->
handle_response(Req1, B2, Response);
{ok, handover} ->
Req1 = Req#req{body = B1},
Req1 = Req#wsReq{body = B1},
t(user_start),
Response = Mod:handle(Req1, Args),
@ -132,7 +127,7 @@ handle_request(S, PrevB, Opts, {Mod, Args} = Callback) ->
end.
handle_response(Req, Buffer, {response, Code, UserHeaders, Body}) ->
#req{callback = {Mod, Args}} = Req,
#wsReq{callback = {Mod, Args}} = Req,
Headers = [connection(Req, UserHeaders),
content_length(UserHeaders, Body)
@ -149,7 +144,7 @@ handle_response(Req, Buffer, {response, Code, UserHeaders, Body}) ->
handle_response(Req, _Buffer, {chunk, UserHeaders, Initial}) ->
#req{callback = {Mod, Args}} = Req,
#wsReq{callback = {Mod, Args}} = Req,
ResponseHeaders = [{?TRANSFER_ENCODING_HEADER, <<"chunked">>},
connection(Req, UserHeaders)
@ -157,8 +152,8 @@ handle_response(Req, _Buffer, {chunk, UserHeaders, Initial}) ->
send_response(Req, 200, ResponseHeaders, <<"">>),
t(send_start),
Initial =:= <<"">> orelse send_chunk(Req#req.socket, Initial),
ClosingEnd = case start_chunk_loop(Req#req.socket) of
Initial =:= <<"">> orelse send_chunk(Req#wsReq.socket, Initial),
ClosingEnd = case start_chunk_loop(Req#wsReq.socket) of
{error, client_closed} -> client;
ok -> server
end,
@ -174,15 +169,15 @@ handle_response(Req, _Buffer, {chunk, UserHeaders, Initial}) ->
handle_response(Req, Buffer, {file, ResponseCode, UserHeaders,
Filename, Range}) ->
#req{callback = {Mod, Args}} = Req,
#wsReq{callback = {Mod, Args}} = Req,
ResponseHeaders = [connection(Req, UserHeaders) | UserHeaders],
case wsUtil:file_size(Filename) of
{error, FileError} ->
handle_event(Mod, file_error, [FileError], Args),
send_server_error(Req#req.socket),
wsNet:close(Req#req.socket),
send_server_error(Req#wsReq.socket),
wsNet:close(Req#wsReq.socket),
exit(normal);
Size ->
t(send_start),
@ -224,7 +219,7 @@ handle_response(Req, Buffer, {file, ResponseCode, UserHeaders,
send_response(Req, Code, Headers, UserBody) ->
ResponseHeaders = assemble_response_headers(Code, Headers),
Body = case {Req#req.method, Code} of
Body = case {Req#wsReq.method, Code} of
{'HEAD', _} -> <<>>;
{_, 304} -> <<>>;
{_, 204} -> <<>>;
@ -235,10 +230,10 @@ send_response(Req, Code, Headers, UserBody) ->
Response = [ResponseHeaders,
Body],
case wsNet:send(Req#req.socket, Response) of
case wsNet:send(Req#wsReq.socket, Response) of
ok -> ok;
{error, Closed} when Closed =:= closed orelse Closed =:= enotconn ->
#req{callback = {Mod, Args}} = Req,
#wsReq{callback = {Mod, Args}} = Req,
handle_event(Mod, client_closed, [before_response], Args),
ok
end.
@ -252,23 +247,23 @@ send_response(Req, Code, Headers, UserBody) ->
Headers :: elli:headers(),
Filename :: file:filename(),
Range :: wsUtil:range().
send_file(#req{callback = {Mod, Args}} = Req, Code, Headers, Filename, Range) ->
send_file(#wsReq{callback = {Mod, Args}} = Req, Code, Headers, Filename, Range) ->
ResponseHeaders = assemble_response_headers(Code, Headers),
case file:open(Filename, [read, raw, binary]) of
{ok, Fd} -> do_send_file(Fd, Range, Req, ResponseHeaders);
{error, FileError} ->
handle_event(Mod, file_error, [FileError], Args),
send_server_error(Req#req.socket),
wsNet:close(Req#req.socket),
send_server_error(Req#wsReq.socket),
wsNet:close(Req#wsReq.socket),
exit(normal)
end,
ok.
do_send_file(Fd, {Offset, Length}, #req{callback = {Mod, Args}} = Req, Headers) ->
try wsNet:send(Req#req.socket, Headers) of
do_send_file(Fd, {Offset, Length}, #wsReq{callback = {Mod, Args}} = Req, Headers) ->
try wsNet:send(Req#wsReq.socket, Headers) of
ok ->
case wsNet:sendfile(Fd, Req#req.socket, Offset, Length, []) of
case wsNet:sendfile(Fd, Req#wsReq.socket, Offset, Length, []) of
{ok, BytesSent} -> s(file, BytesSent), ok;
{error, Closed} when Closed =:= closed orelse
Closed =:= enotconn ->
@ -295,7 +290,7 @@ send_rescue_response(Socket, Code, Body) ->
wsNet:send(Socket, Response).
%% @doc Execute the user callback, translating failure into a proper response.
execute_callback(#req{callback = {Mod, Args}} = Req) ->
execute_callback(#wsReq{callback = {Mod, Args}} = Req) ->
try Mod:handle(Req, Args) of
%% {ok,...{file,...}}
{ok, Headers, {file, Filename}} ->
@ -593,7 +588,7 @@ do_check_max_size_x2(Socket, ContentLength, Buffer, MaxSize)
do_check_max_size_x2(_, _, _, _) -> ok.
-spec mk_req(Method, PathTuple, Headers, Headers, Body, V, Socket, Callback) -> Req when
Method :: elli:http_method(),
Method :: elli:method(),
PathTuple :: {PathType :: atom(), RawPath :: binary()},
Headers :: elli:headers(),
Body :: elli:body(),
@ -604,7 +599,7 @@ do_check_max_size_x2(_, _, _, _) -> ok.
mk_req(Method, PathTuple, Headers, ParsedHeaders, Body, V, Socket, {Mod, Args} = Callback) ->
case parse_path(PathTuple) of
{ok, {Scheme, Host, Port}, {Path, URL, URLArgs}} ->
#req{method = Method, scheme = Scheme, host = Host,
#wsReq{method = Method, scheme = Scheme, host = Host,
port = Port, path = URL, args = URLArgs,
version = V, raw_path = Path, original_headers = Headers,
body = Body, pid = self(), socket = Socket,
@ -619,7 +614,7 @@ mk_req(Method, PathTuple, Headers, ParsedHeaders, Body, V, Socket, {Mod, Args} =
mk_req(Method, Scheme, Host, Port, PathTuple, Headers, ParsedHeaders, Body, V, Socket, Callback) ->
Req = mk_req(Method, PathTuple, Headers, ParsedHeaders, Body, V, Socket, Callback),
Req#req{scheme = Scheme, host = Host, port = Port}.
Req#wsReq{scheme = Scheme, host = Host, port = Port}.
%%
%% HEADERS
@ -654,18 +649,18 @@ encode_value(V) when is_integer(V) -> integer_to_binary(V);
encode_value(V) when is_binary(V) -> V;
encode_value(V) when is_list(V) -> list_to_binary(V).
connection_token(#req{version = {1, 1}, headers = Headers}) ->
connection_token(#wsReq{version = {1, 1}, headers = Headers}) ->
case get_header(?CONNECTION_HEADER, Headers) of
<<"close">> -> <<"close">>;
<<"Close">> -> <<"close">>;
_ -> <<"Keep-Alive">>
end;
connection_token(#req{version = {1, 0}, headers = Headers}) ->
connection_token(#wsReq{version = {1, 0}, headers = Headers}) ->
case get_header(?CONNECTION_HEADER, Headers) of
<<"Keep-Alive">> -> <<"Keep-Alive">>;
_ -> <<"close">>
end;
connection_token(#req{version = {0, 9}}) ->
connection_token(#wsReq{version = {0, 9}}) ->
<<"close">>.
%% @doc Return the preferred session handling setting to close or keep the
@ -795,7 +790,7 @@ split_args(Qs) ->
%% CALLBACK HELPERS
%%
init(#req{callback = {Mod, Args}} = Req) ->
init(#wsReq{callback = {Mod, Args}} = Req) ->
?IIF(erlang:function_exported(Mod, init, 2),
case Mod:init(Req, Args) of
ignore -> {ok, standard};

+ 33
- 33
src/wsSrv/wsReq.erl 파일 보기

@ -53,25 +53,25 @@
%% @doc Return `path' split into binary parts.
path(#req{path = Path}) -> Path.
path(#wsReq{path = Path}) -> Path.
%% @doc Return the `raw_path', i.e. not split or parsed for query params.
raw_path(#req{raw_path = Path}) -> Path.
raw_path(#wsReq{raw_path = Path}) -> Path.
%% @doc Return the `headers' that have had `string:casefold/1' run on each key.
headers(#req{headers = Headers}) -> Headers.
headers(#wsReq{headers = Headers}) -> Headers.
%% @doc Return the original `headers'.
original_headers(#req{original_headers = Headers}) -> Headers.
original_headers(#wsReq{original_headers = Headers}) -> Headers.
%% @doc Return the `method'.
method(#req{method = Method}) -> Method.
method(#wsReq{method = Method}) -> Method.
%% @doc Return the `body'.
body(#req{body = Body}) -> Body.
body(#wsReq{body = Body}) -> Body.
%% @doc Return the `scheme'.
scheme(#req{scheme = Scheme}) -> Scheme.
scheme(#wsReq{scheme = Scheme}) -> Scheme.
%% @doc Return the `host'.
host(#req{host = Host}) -> Host.
host(#wsReq{host = Host}) -> Host.
%% @doc Return the `port'.
port(#req{port = Port}) -> Port.
port(#wsReq{port = Port}) -> Port.
peer(#req{socket = Socket} = _Req) ->
peer(#wsReq{socket = Socket} = _Req) ->
case wsNet:peername(Socket) of
{ok, {Address, _}} ->
list_to_binary(inet_parse:ntoa(Address));
@ -79,28 +79,28 @@ peer(#req{socket = Socket} = _Req) ->
undefined
end.
get_header(Key, #req{headers = Headers}) ->
get_header(Key, #wsReq{headers = Headers}) ->
CaseFoldedKey = string:casefold(Key),
proplists:get_value(CaseFoldedKey, Headers).
get_header(Key, #req{headers = Headers}, Default) ->
get_header(Key, #wsReq{headers = Headers}, Default) ->
CaseFoldedKey = string:casefold(Key),
proplists:get_value(CaseFoldedKey, Headers, Default).
%% @equiv get_arg(Key, Req, undefined)
get_arg(Key, #req{} = Req) ->
get_arg(Key, #wsReq{} = Req) ->
get_arg(Key, Req, undefined).
%% @equiv proplists:get_value(Key, Args, Default)
get_arg(Key, #req{args = Args}, Default) ->
get_arg(Key, #wsReq{args = Args}, Default) ->
proplists:get_value(Key, Args, Default).
%% @equiv get_arg_decoded(Key, Req, undefined)
get_arg_decoded(Key, #req{} = Req) ->
get_arg_decoded(Key, #wsReq{} = Req) ->
get_arg_decoded(Key, Req, undefined).
get_arg_decoded(Key, #req{args = Args}, Default) ->
get_arg_decoded(Key, #wsReq{args = Args}, Default) ->
case proplists:get_value(Key, Args) of
undefined -> Default;
true -> true;
@ -109,8 +109,8 @@ get_arg_decoded(Key, #req{args = Args}, Default) ->
end.
%% @doc Parse `application/x-www-form-urlencoded' body into a proplist.
body_qs(#req{body = <<>>}) -> [];
body_qs(#req{body = Body} = Req) ->
body_qs(#wsReq{body = <<>>}) -> [];
body_qs(#wsReq{body = Body} = Req) ->
case get_header(<<"Content-Type">>, Req) of
<<"application/x-www-form-urlencoded">> ->
wsHttp:split_args(Body);
@ -121,17 +121,17 @@ body_qs(#req{body = Body} = Req) ->
end.
%% @equiv post_arg(Key, Req, undefined)
post_arg(Key, #req{} = Req) ->
post_arg(Key, #wsReq{} = Req) ->
post_arg(Key, Req, undefined).
post_arg(Key, #req{} = Req, Default) ->
post_arg(Key, #wsReq{} = Req, Default) ->
proplists:get_value(Key, body_qs(Req), Default).
%% @equiv post_arg_decoded(Key, Req, undefined)
post_arg_decoded(Key, #req{} = Req) ->
post_arg_decoded(Key, #wsReq{} = Req) ->
post_arg_decoded(Key, Req, undefined).
post_arg_decoded(Key, #req{} = Req, Default) ->
post_arg_decoded(Key, #wsReq{} = Req, Default) ->
case proplists:get_value(Key, body_qs(Req)) of
undefined -> Default;
true -> true;
@ -144,9 +144,9 @@ post_arg_decoded(Key, #req{} = Req, Default) ->
%% Both keys and values in the returned proplists will be binaries or the atom
%% `true' in case no value was supplied for the query value.
-spec get_args(elli:req()) -> QueryArgs :: proplists:proplist().
get_args(#req{args = Args}) -> Args.
get_args(#wsReq{args = Args}) -> Args.
get_args_decoded(#req{args = Args}) ->
get_args_decoded(#wsReq{args = Args}) ->
lists:map(fun({K, true}) ->
{K, true};
({K, V}) ->
@ -154,10 +154,10 @@ get_args_decoded(#req{args = Args}) ->
end, Args).
post_args(#req{} = Req) ->
post_args(#wsReq{} = Req) ->
body_qs(Req).
post_args_decoded(#req{} = Req) ->
post_args_decoded(#wsReq{} = Req) ->
lists:map(fun({K, true}) ->
{K, true};
({K, V}) ->
@ -167,7 +167,7 @@ post_args_decoded(#req{} = Req) ->
%% @doc Calculate the query string associated with a given `Request'
%% as a binary.
-spec query_str(elli:req()) -> QueryStr :: binary().
query_str(#req{raw_path = Path}) ->
query_str(#wsReq{raw_path = Path}) ->
case binary:split(Path, [<<"?">>]) of
[_, Qs] -> Qs;
[_] -> <<>>
@ -178,7 +178,7 @@ query_str(#req{raw_path = Path}) ->
%% 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:req()) -> [http_range()] | parse_error.
get_range(#req{headers = Headers}) ->
get_range(#wsReq{headers = Headers}) ->
case proplists:get_value(<<"range">>, Headers) of
<<"bytes=", RangeSetBin/binary>> ->
parse_range_set(RangeSetBin);
@ -228,15 +228,15 @@ remove_whitespace(Bin) ->
%% @doc Serialize the `Req'uest record to a proplist.
%% Useful for logging.
to_proplist(#req{} = Req) ->
to_proplist(#wsReq{} = Req) ->
lists:zip(record_info(fields, req), tl(tuple_to_list(Req))).
%% @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(#req{version = {1, 1}} = Req) ->
Req#req.pid;
chunk_ref(#req{}) ->
chunk_ref(#wsReq{version = {1, 1}} = Req) ->
Req#wsReq.pid;
chunk_ref(#wsReq{}) ->
{error, not_supported}.
@ -274,7 +274,7 @@ is_ref_alive(Ref) ->
is_process_alive(Ref),
rpc:call(node(Ref), erlang, is_process_alive, [Ref])).
is_request(#req{}) -> true;
is_request(#wsReq{}) -> true;
is_request(_) -> false.
uri_decode(Bin) ->

불러오는 중...
취소
저장