Bladeren bron

ft: 代码修改

master
SisMaker 3 jaren geleden
bovenliggende
commit
7ae8b21dac
9 gewijzigde bestanden met toevoegingen van 147 en 210 verwijderingen
  1. +3
    -3
      include/eWSrv.hrl
  2. +22
    -0
      include/wsCom.hrl
  3. +0
    -1
      src/example/wsEgHer.erl
  4. +0
    -1
      src/test/elli_tests.erl
  5. +0
    -1
      src/wsSrv/elli.erl
  6. +33
    -39
      src/wsSrv/wsHttp.erl
  7. +88
    -163
      src/wsSrv/wsHttpProtocol.erl
  8. +0
    -1
      src/wsSrv/wsHttps.erl
  9. +1
    -1
      src/wsSrv/wsUtil.erl

+ 3
- 3
include/eWSrv.hrl Bestand weergeven

@ -25,16 +25,16 @@
-record(wsReq, {
method :: method(),
raw_path :: binary(),
version :: wsHttp:version(),
scheme :: undefined | binary(),
host :: undefined | binary(),
port :: undefined | 1..65535,
path :: [binary()],
args :: [{binary(), any()}],
raw_path :: binary(),
version :: wsHttp:version(),
headers :: headers(),
original_headers :: headers(),
body :: body(),
body = <<>> :: body(),
pid :: pid()
}).

+ 22
- 0
include/wsCom.hrl Bestand weergeven

@ -1,3 +1,5 @@
-include("eWSrv.hrl").
-define(wsErr(Str), error_logger:error_msg(Str)).
-define(wsErr(Format, Args), error_logger:error_msg(Format, Args)).
-define(wsWarn(Format, Args), error_logger:warning_msg(Format, Args)).
@ -5,3 +7,23 @@
-define(wsGLV(Key, List, Default), wsUtil:gLV(Key, List, Default)).
-define(IIF(Cond, Then, That), case Cond of true -> Then; _ -> That end).
-type stage() :: reqLine | header | body | done. %% http请求可能会有多个包
-record(state, {
stage = reqLine :: stage() %%
, buffer = <<>> :: binary() %%
, wsReq :: undefined | #wsReq{} %% http
, headerCnt = 0 :: pos_integer() %% header计数
, temHeader = [] :: [headers()] %% header临时数据
, contentLength :: undefined | non_neg_integer() | chunked %%
, temChunked = <<>> :: binary()
, rn :: undefined | binary:cp()
, wsMod :: callback()
, socket :: undefined | wsNet:socket()
, isSsl = false :: boolean()
, maxSize %%
, maxChunkCnt %% chunk
, maxRecvCnt %% recv最大的次数
}).

+ 0
- 1
src/example/wsEgHer.erl Bestand weergeven

@ -10,7 +10,6 @@
-export([handle/2, handle_event/3]).
-export([chunk_loop/1]).
-include("eWSrv.hrl").
-include("wsCom.hrl").
-behaviour(wsHer).

+ 0
- 1
src/test/elli_tests.erl Bestand weergeven

@ -1,6 +1,5 @@
-module(elli_tests).
-include_lib("eunit/include/eunit.hrl").
-include("eWSrv.hrl").
-include("wsCom.hrl").
-include("elli_test.hrl").

+ 0
- 1
src/wsSrv/elli.erl Bestand weergeven

@ -2,7 +2,6 @@
-behaviour(gen_server).
-include("eWSrv.hrl").
-include("wsCom.hrl").
-export([

+ 33
- 39
src/wsSrv/wsHttp.erl Bestand weergeven

@ -3,7 +3,7 @@
-behavior(gen_srv).
-include_lib("eNet/include/eNet.hrl").
-include("eWSrv.hrl").
-include("wsCom.hrl").
-export([
@ -38,16 +38,6 @@
-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, []).
@ -68,28 +58,45 @@ 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) ->
handleInfo({tcp, _Socket, Data}, State) ->
#state{stage = Stage, buffer = Buffer, socket = Socket} = State,
case wsHttpProtocol:request(Stage, <<Buffer/binary, Data/binary>>, State) of
{ok, NewState} ->
{noreply, NewState};
{done, NewState} ->
ok;
Err ->
?wsErr("recv the http data error ~p~n", [Err]),
send_bad_request(Socket),
gen_tcp:close(Socket)
end;
handleInfo({tcp_closed, _Socket}, _State) ->
ok;
handleInfo({tcp_error, Socket, Reason}, _State) ->
ok;
handleInfo({ssl, Socket, Data}, _State) ->
ok;
?wsErr("the http socket error ~p~n", [Reason]),
gen_tcp:close(Socket),
kpS;
handleInfo({ssl, Socket, Data}, State) ->
#state{stage = Stage, buffer = Buffer, socket = Socket} = State,
case wsHttpProtocol:request(Stage, <<Buffer/binary, Data/binary>>, State) of
{ok, NewState} ->
{noreply, NewState};
{done, NewState} ->
ok;
Err ->
?wsErr("recv the http data error ~p~n", [Err]),
send_bad_request(Socket),
ssl:close(Socket)
end;
handleInfo({ssl_closed, Socket}, _State) ->
ok;
handleInfo({ssl_error, Socket, Reason}, _State) ->
ok;
handleInfo({ssl, Socket, Data}, _State) ->
ok;
?wsErr("the http socket error ~p~n", [Reason]),
ssl:close(Socket),
kpS;
handleInfo({?mSockReady, Sock}, _State) ->
inet:setopts(Sock, [{packet, raw}, {active, true}]),
{ok, #state{socket = Sock}};
@ -112,19 +119,6 @@ terminate(_Reason, _State) ->
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%% @doc Accept on the socket until a client connects.
%% Handle the request, then loop if we're using keep alive or chunked transfer.
%% If {@link elli_tcp:accept/3} doesn't return a socket within a configurable
%% timeout, loop to allow code upgrades of this module.

+ 88
- 163
src/wsSrv/wsHttpProtocol.erl Bestand weergeven

@ -7,20 +7,20 @@
-compile({inline_size, 128}).
-export([
request/2
request/3
]).
-spec request(undefined | recvState(), binary()) -> {ok, recvState()} | error().
request(reqLine, Data) ->
-spec request(Stage :: stage(), Data :: binary(), State :: #state{}) -> {ok, NewState :: #state{}} | {done, NewState :: #state{}} | {error, term()}.
request(reqLine, Data, State) ->
case erlang:decode_packet(http_bin, Data, []) of
{more, _} ->
reqLine;
{ok, State#state{buffer = Data}};
{ok, {http_request, Method, RawPath, Version}, Rest} ->
case byte_size(Rest) > 0 of
true ->
request(header, Rest);
request(header, Rest, State#state{stage = header, buffer = Rest, wsReq = #wsReq{method = Method, raw_path = RawPath, version = Version}, headerCnt = 0});
_ ->
{header, Method, RawPath, Version}
{ok, State#state{stage = header, buffer = <<>>, wsReq = #wsReq{method = Method, raw_path = RawPath, version = Version}, headerCnt = 0}}
end;
{ok, {http_error, ErrStr}, _} ->
{error, ErrStr};
@ -28,178 +28,103 @@ request(reqLine, Data) ->
{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}};
end;
request(header, Data, State) ->
case erlang:decode_packet(httph_bin, Data, []) of
{more, _} ->
{ok, State#state{buffer = Data}};
{ok, {http_header, _, Key, _, Value}, Rest} ->
#state{headerCnt = HeaderCnt, temHeader = TemHeader, rn = Rn} = State,
NewTemHeader = [{Key, Value} | TemHeader],
NewHeaderCnt = HeaderCnt + 1,
case NewHeaderCnt >= 100 of
true ->
{error, too_many_headers};
_ ->
case Key of
'Content-Length' ->
request(header, Rest, State#state{buffer = Rest, headerCnt = NewHeaderCnt, temHeader = NewTemHeader, contentLength = binary_to_integer(Value)});
'Transfer-Encoding' ->
case Value of
<<"chunked">> ->
case Rn of
undefined ->
request(header, Rest, State#state{buffer = Rest, headerCnt = NewHeaderCnt, temHeader = NewTemHeader, contentLength = chunked, rn = binary:compile_pattern(<<"\r\n">>)});
_ ->
request(header, Rest, State#state{buffer = Rest, headerCnt = NewHeaderCnt, temHeader = NewTemHeader, contentLength = chunked})
end;
_ ->
{ok, #recvState{stage = body, statusCode = StatusCode, headers = Headers, contentLength = ContentLength, body = Body}}
end
end;
not_enough_data ->
{ok , #recvStaten>{stage = header, body = Data}}
{error, 'Transfer-Encoding'}
end;
_ ->
request(header, Rest, State#state{buffer = Rest, headerCnt = NewHeaderCnt, temHeader = NewTemHeader})
end
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
{ok, http_eoh, Rest} ->
#state{temHeader = TemHeader, contentLength = CLen, wsReq = WsReq} = State,
NewWsReq = WsReq#wsReq{headers = TemHeader},
case CLen of
undefined ->
{error, content_length};
0 ->
{done, State#state{buffer = Rest, wsReq = NewWsReq, temHeader = []}};
_ ->
case byte_size(Rest) > 0 of
true ->
{done, #recvState{stage = done, statusCode = StatusCode, headers = Headers, contentLength = 0, body = <<>>}};
request(body, Rest, State#state{stage = body, buffer = Rest, wsReq = NewWsReq, temHeader = []});
_ ->
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}}
{ok, State#state{stage = body, buffer = <<>>, wsReq = NewWsReq, temHeader = []}}
end
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}
{ok, {http_error, ErrStr}, _Rest} ->
{error, ErrStr};
{error, _Reason} = Ret ->
Ret
end;
request(body, Data, State) ->
#state{contentLength = CLen, wsReq = WsReq, temChunked = TemChunked, rn = Rn} = State,
case CLen of
chunked ->
case parseChunks(Data, Rn, TemChunked) of
{ok, NewTemChunked, Rest} ->
{ok, State#state{buffer = Rest, temChunked = NewTemChunked}};
{over, LastTemChunked, Rest} ->
{done, State#state{buffer = Rest, temChunked = <<>>, wsReq = WsReq#wsReq{body = LastTemChunked}}};
{error, _Reason} = Ret ->
Ret
end;
_ ->
BodySize = erlang:size(Data),
if
BodySize == CLen ->
{done, State#state{buffer = <<>>, wsReq = WsReq#wsReq{body = Data}}};
BodySize > CLen ->
<<Body:CLen/binary, Rest/binary>> = Data,
{done, State#state{buffer = Rest, wsReq = WsReq#wsReq{body = Body}}};
true ->
{ok, State#state{buffer = Data}}
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
case chunkSize(Size) of
undefined ->
{error, invalid_chunk_size};
0 ->
done;
{over, Acc, Rest};
HexSize ->
parseChunkBody(Rest, HexSize)
end;
[Data] ->
not_enough_data
case chunkBody(Rest, HexSize) of
not_enough_data ->
{ok, Acc, Data};
{ok, Body, Rest} ->
parseChunks(Rest, Rn, <<Acc/binary, Body/binary>>)
end
end
end.
parseChunkBody(Data, Size) ->
chunkBody(Data, Size) ->
case Data of
<<Body:Size/binary, "\r\n", Rest/binary>> ->
{ok, Body, Rest};
@ -207,10 +132,10 @@ parseChunkBody(Data, Size) ->
not_enough_data
end.
parseChunkSize(Bin) ->
chunkSize(Bin) ->
try
binary_to_integer(Bin, 16)
catch
error:badarg ->
undefined
end.
end.

+ 0
- 1
src/wsSrv/wsHttps.erl Bestand weergeven

@ -4,7 +4,6 @@
%% connects. It then handles requests on that connection until it's
%% closed either by the client timing out or explicitly by the user.
-module(wsHttps).
-include("eWSrv.hrl").
-include("wsCom.hrl").

+ 1
- 1
src/wsSrv/wsUtil.erl Bestand weergeven

@ -1,5 +1,5 @@
-module(wsUtil).
-include("eWSrv.hrl").
-include("wsCom.hrl").
-include_lib("kernel/include/file.hrl").

Laden…
Annuleren
Opslaan