AICells пре 5 година
родитељ
комит
31631c533c
5 измењених фајлова са 174 додато и 153 уклоњено
  1. +8
    -11
      include/agHttpCli.hrl
  2. +9
    -39
      src/httpCli/agAgencyUtils.erl
  3. +2
    -2
      src/httpCli/agHttpCli.erl
  4. +140
    -88
      src/httpCli/agHttpProtocol.erl
  5. +15
    -13
      src/httpCli/agTcpAgencyIns.erl

+ 8
- 11
include/agHttpCli.hrl Прегледај датотеку

@ -42,22 +42,20 @@
timestamp :: erlang:timestamp()
}).
-record(requestRet1, {
state :: body | done,
body :: undefined | binary(),
-record(requestRet, {
statusCode :: undefined | 100..505,
contentLength :: undefined | non_neg_integer() | chunked,
headers :: undefined | [binary()],
reason :: undefined | binary(),
statusCode :: undefined | 100..505
body :: undefined | binary()
}).
-record(recvState, {
stage = header :: header | body | done, %% tcp可能会有多个包
contentLength :: undefined | non_neg_integer() | chunked,
statusCode :: undefined | 100..505,
reason :: undefined | binary(),
headers :: undefined | [binary()],
contentLength :: undefined | non_neg_integer() | chunked,
stage = header :: header | body | done, %% tcp可能会有多个包
body :: undefined | binary()
buffer = <<>> :: binary(),
body = <<>> :: binary()
}).
-record(httpParam, {
@ -74,7 +72,6 @@
}).
-record(cliState, {
binPatterns :: tuple(),
requestsIn = 1 :: non_neg_integer(),
requestsOut = 0 :: non_neg_integer(),
status = leisure :: waiting | leisure,
@ -82,7 +79,6 @@
backlogSize = 0 :: integer(),
curInfo = undefined :: tuple(),
recvState :: recvState() | undefined
}).
-record(poolOpts, {
@ -102,6 +98,7 @@
-type binPatterns() :: #binPatterns {}.
-type miAgHttpCliRet() :: #miAgHttpCliRet{}.
-type request() :: #request{}.
-type requestRet() :: #requestRet{}.
-type recvState() :: #recvState{}.
-type httpParam() :: #httpParam{}.
-type cliState() :: #cliState{}.

+ 9
- 39
src/httpCli/agAgencyUtils.erl Прегледај датотеку

@ -11,13 +11,12 @@
, delQueue/1
, clearQueue/0
, cancelTimer/1
, agencyReply/2
, agencyReply/4
, agencyReplyAll/1
, agencyResponse/2
, initReconnectState/1
, resetReconnectState/1
, updateReconnectState/1
, handleData/2
]).
getQueue(RequestsIn) ->
@ -32,10 +31,14 @@ delQueue(RequestsIn) ->
clearQueue() ->
erlang:erase().
-spec agencyResponse(recvState(), term()) -> ok.
agencyResponse(Reply, {PidForm, RequestId, TimerRef}) ->
agencyReply(PidForm, RequestId, TimerRef, Reply);
agencyResponse(RequestRet, undefined) ->
-spec agencyReply(term(), term()) -> ok.
agencyReply({undefined, _RequestId, TimerRef}, _Reply) ->
agAgencyUtils:cancelTimer(TimerRef);
agencyReply({PidForm, RequestId, TimerRef}, Reply) ->
agAgencyUtils:cancelTimer(TimerRef),
catch PidForm ! #miAgHttpCliRet{requestId = RequestId, reply = Reply},
ok;
agencyReply(undefined, RequestRet) ->
?WARN(not_curInfo ,"not find curInfo ret is:~p~n ",[RequestRet]),
ok.
@ -98,36 +101,3 @@ minCur(A, B) when B >= A ->
minCur(_, B) ->
B.
-spec handleData(recvState() | undefined, binary(), binPatterns()) -> {ok, term(), cliState()} | {error, atom(), cliState()}.
handleData(undeined, BinPatterns, Data) ->
case responses(NewData, BinPatterns, TemResponseRet) of
{ok, ResponseRet, NewTemResponseRet, Rest} ->
io:format("IMY************************handleData ~p~n",[Rest]),
{ok, ResponseRet, CliState#cliState{buffer = Rest, recvState = NewTemResponseRet}};
{error, Reason} ->
{error, Reason, CliState}
end;
handleData(RecvState, BinPatterns, Data) ->
NewData = <<Buffer/binary, Data/binary>>,
case responses(NewData, BinPatterns, TemResponseRet) of
{ok, ResponseRet, NewTemResponseRet, Rest} ->
io:format("IMY************************handleData ~p~n",[Rest]),
{ok, ResponseRet, CliState#cliState{buffer = Rest, recvState = NewTemResponseRet}};
{error, Reason} ->
{error, Reason, CliState}
end.
responses(<<>>, _BinPatterns, TemResponseRet) ->
{ok, waiting_data, TemResponseRet, <<>>};
responses(Data, BinPatterns, TemResponseRet) ->
case agHttpProtocol:response(Data, TemResponseRet, BinPatterns) of
{ok, #recvState{stage = done} = NewTemResponseRet, Rest} ->
{ok, NewTemResponseRet, undefined, Rest};
{ok, NewTemResponseRet, Rest} ->
{ok, waiting_data, NewTemResponseRet, Rest};
{error, not_enough_data} ->
{ok, waiting_data, TemResponseRet, Data};
{error, _Reason} = Err ->
Err
end.

+ 2
- 2
src/httpCli/agHttpCli.erl Прегледај датотеку

@ -103,7 +103,7 @@ callAgency(PoolName, Request) ->
callAgency(PoolName, Request, Timeout) ->
case castAgency(PoolName, Request, self(), Timeout) of
{ok, RequestId} ->
% io:format("IMY************************ todo receiveResponse ~p ~n", [RequestId]),
%io:format("IMY************************ todo receiveResponse ~p ~n", [RequestId]),
receiveResponse(RequestId);
{error, Reason} ->
{error, Reason}
@ -135,7 +135,7 @@ castAgency(PoolName, {Method, Path, Headers, Body}, Pid, Timeout) ->
receiveResponse(RequestId) ->
receive
#miAgHttpCliRet{requestId = RequestId, reply = Reply} ->
%io:format("IMY************************ miAgHttpCliRet ~p ~p ~n", [ok, erlang:get(cnt)]),
%io:format("IMY************************ miAgHttpCliRet ~p ~p ~n", [erlang:get(cnt), Reply]),
Reply
after 5000 ->
timeout

+ 140
- 88
src/httpCli/agHttpProtocol.erl Прегледај датотеку

@ -8,7 +8,7 @@
headers/1
, request/5
, response/1
, response/3
, response/4
, binPatterns/0
]).
@ -39,58 +39,105 @@ request(Method, Host, Path, Headers, Body) ->
-spec response(binary()) -> {ok, recvState(), binary()} | error().
response(Data) ->
response(Data, undefined, binPatterns()).
response(undefined, binary:compile_pattern(<<"\r\n">>), binary:compile_pattern(<<"\r\n\r\n">>), Data).
-spec response(binary(), undefined | recvState(), binPatterns()) -> {ok, recvState(), binary()} | error().
response(Data, undefined, BinPatterns) ->
case parseStatusLine(Data, BinPatterns) of
-spec response(undefined | recvState(), binary:cp(), binaryn><span class="p">:cp(), binary()) -> {ok, recvState()} | error().
response(undefined, Rn, RnRn, Data) ->
case parseStatusLine(Data, Rn) of
{StatusCode, Reason, Rest} ->
case splitHeaders(Rest, BinPatterns) of
{undefined, Headers, Rest2} ->
{ok, #recvState{stage = done, statusCode = StatusCode, reason = Reason, headers = Headers, contentLength = undefined}, Rest2};
{0, Headers, Rest2} ->
{ok, #recvState{stage = done, statusCode = StatusCode, reason = Reason, headers = Headers, contentLength = 0}, Rest2};
{ContentLength, Headers, Rest2} ->
response(Rest2, #recvState{stage = body, statusCode = StatusCode, reason = Reason, headers = Headers, contentLength = ContentLength}, BinPatterns);
{error, Reason2} ->
{error, Reason2}
case splitHeaders(Rest, Rn, RnRn) of
{undefined, Headers, Body} ->
{done, #recvState{stage = done, statusCode = StatusCode, reason = Reason, headers = Headers, contentLength = undefined, body = Body}};
{0, Headers, Rest} ->
{done, #recvState{stage = done, statusCode = StatusCode, reason = Reason, headers = Headers, contentLength = 0, body = Rest}};
{chunked, Headers, Body} ->
RecvState = #recvState{stage = body, contentLength = chunked, statusCode = StatusCode, reason = Reason, headers = Headers},
response(RecvState, Rn, RnRn, Body);
{ContentLength, Headers, Body} ->
BodySize = erlang:size(Body),
if
BodySize == ContentLength ->
{done, #recvState{stage = done, statusCode = StatusCode, reason = Reason, headers = Headers, contentLength = ContentLength, body = Body}};
BodySize > ContentLength ->
?WARN(agTcpAgencyIns, "11 contentLength get to long data why? more: ~p ~n",[BodySize - ContentLength]),
{done, #recvState{stage = done, statusCode = StatusCode, reason = Reason, headers = Headers, contentLength = ContentLength, body = Body}};
true ->
{ok, #recvState{stage = body, statusCode = StatusCode, reason = Reason, headers = Headers, contentLength = ContentLength, body = Body}}
end;
not_enough_data ->
%% headers都不足
{ok, #recvState{stage = header, body = Data}}
end;
not_enough_data ->
%% headers都不足
{ok, #recvState{stage = header, body = Data}};
{error, Reason} ->
{error, Reason}
end;
response(Data, #recvState{stage = body, contentLength = chunked} = Response, BinPatterns) ->
case parseChunks(Data, BinPatterns, []) of
{ok, Body, Rest} ->
{ok, Response#recvState{stage = done, body = Body}, Rest};
response(#recvState{stage = body, contentLength = chunked, body = Body, buffer = Buffer} = RecvState, Rn, _RnRn, Data) ->
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;
response(Data, #recvState{stage = body, contentLength = ContentLength} = Response, _BinPatterns) when size(Data) >= ContentLength ->
<<Body:ContentLength/binary, Rest/binary>> = Data,
{ok, Response#recvState{stage = done, body = Body}, Rest};
response(Data, #recvState{stage = body} = Response, _BinPatterns) ->
{ok, Response, Data}.
response(#recvState{stage = body, contentLength = ContentLength, body = Body} = RecvState, _Rn, _RnRn, Data) ->
CurData = <<Body/binary, Data/binary>>,
BodySize = erlang:size(Body),
if
BodySize == ContentLength ->
{done, RecvState#recvState{stage = done, body = CurData}};
BodySize > ContentLength ->
?WARN(agTcpAgencyIns, "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;
response(#recvState{stage = header, body = Body}, Rn, RnRn, Data) ->
CurData = <<Body/binary, Data/binary>>,
case parseStatusLine(CurData, Rn) of
{StatusCode, Reason, Rest} ->
case splitHeaders(Rest, Rn, RnRn) of
{undefined, Headers, Body} ->
{done, #recvState{stage = done, statusCode = StatusCode, reason = Reason, headers = Headers, contentLength = undefined, body = Body}};
{0, Headers, Body} ->
{done, #recvState{stage = done, statusCode = StatusCode, reason = Reason, headers = Headers, contentLength = 0, body = Body}};
{chunked, Headers, Rest} ->
RecvState = #recvState{stage = body, contentLength = chunked, statusCode = StatusCode, reason = Reason, headers = Headers},
response(RecvState, Rn, RnRn, Rest);
{ContentLength, Headers, Body} ->
case size(Body) >= ContentLength of
true ->
{done, #recvState{stage = done, statusCode = StatusCode, reason = Reason, headers = Headers, contentLength = ContentLength, body = Body}};
_ ->
{ok, #recvState{stage = body, statusCode = StatusCode, reason = Reason, headers = Headers, contentLength = ContentLength, body = Body}}
end;
not_enough_data ->
%% headers都不足
{ok, #recvState{stage = header, body = CurData}}
end;
not_enough_data ->
{ok, #recvState{stage = header, body = CurData}};
{error, Reason} ->
{error, Reason}
end.
spellHeaders(Headers) ->
[[Key, <<": ">>, Value, <<"\r\n">>] || {Key, Value} <- Headers].
splitHeaders(Data, #binPatterns{rn = Rn, rnrn = Rnrn}) ->
case binary:split(Data, Rnrn) of
splitHeaders(Data, Rn, RnRn) ->
case binary:split(Data, RnRn) of
[Data] ->
{error, not_enough_data};
[Headers, Rest] ->
Headers2 = binarySplitGlobal(Headers, Rn),
ContentLength = contentLength(Headers2),
{ContentLength, Headers2, Rest}
end.
binarySplitGlobal(Bin, Pattern) ->
case binary:split(Bin, Pattern) of
[Split, Rest] ->
[Split | binarySplitGlobal(Rest, Pattern)];
Rest ->
Rest
not_enough_data;
[Headers, Body] ->
HeadersList = binary:split(Headers, Rn, [global]),
ContentLength = contentLength(HeadersList),
{ContentLength, HeadersList, Body}
end.
contentLength([]) ->
@ -106,27 +153,71 @@ contentLength([<<"transfer-encoding: chunked">> | _T]) ->
contentLength([_ | T]) ->
contentLength(T).
parseChunks(Data, BinPatterns, Acc) ->
case parseChunk(Data, BinPatterns) of
{ok, <<>>, Rest} ->
{ok, iolist_to_binary(lists:reverse(Acc)), Rest};
parseStatusLine(Data, Rn) ->
case binary:split(Data, Rn) of
[Data] ->
not_enough_data;
[Line, Rest] ->
case parseStatusReason(Line) of
{ok, StatusCode, Reason} ->
{StatusCode, Reason, Rest};
{error, Reason} ->
{error, Reason}
end
end.
parseStatusReason(<<"HTTP/1.1 200 OK">>) ->
{ok, 200, <<"OK">>};
parseStatusReason(<<"HTTP/1.1 204 No Content">>) ->
{ok, 204, <<"No Content">>};
parseStatusReason(<<"HTTP/1.1 301 Moved Permanently">>) ->
{ok, 301, <<"Moved Permanently">>};
parseStatusReason(<<"HTTP/1.1 302 Found">>) ->
{ok, 302, <<"Found">>};
parseStatusReason(<<"HTTP/1.1 403 Forbidden">>) ->
{ok, 403, <<"Forbidden">>};
parseStatusReason(<<"HTTP/1.1 404 Not Found">>) ->
{ok, 404, <<"Not Found">>};
parseStatusReason(<<"HTTP/1.1 500 Internal Server Error">>) ->
{ok, 500, <<"Internal Server Error">>};
parseStatusReason(<<"HTTP/1.1 502 Bad Gateway">>) ->
{ok, 502, <<"Bad Gateway">>};
parseStatusReason(<<"HTTP/1.1 ", N1, N2, N3, " ", Reason/bits>>)
when $0 =< N1, N1 =< $9,
$0 =< N2, N2 =< $9,
$0 =< N3, N3 =< $9 ->
StatusCode = (N1 - $0) * 100 + (N2 - $0) * 10 + (N3 - $0),
{ok, StatusCode, Reason};
parseStatusReason(<<"HTTP/1.0 ", _/binary>>) ->
{error, unsupported_feature};
parseStatusReason(_) ->
{error, bad_request}.
parseChunks(Data, Rn, Acc) ->
case parseChunk(Data, Rn) of
done ->
{ok, iolist_to_binary(lists:reverse(Acc)), <<>>};
{ok, Body, Rest} ->
parseChunks(Rest, BinPatterns, [Body | Acc]);
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, #binPatterns{rn = Rn}) ->
parseChunk(Data, Rn) ->
case binary:split(Data, Rn) of
[Size, Rest] ->
case parseChunkSize(Size) of
undefined ->
{error, invalid_chunk_size};
Size2 ->
parseChunkBody(Rest, Size2)
0 ->
done;
HexSize ->
parseChunkBody(Rest, HexSize)
end;
[Data] ->
{error, not_enough_data}
not_enough_data
end.
parseChunkBody(Data, Size) ->
@ -134,7 +225,7 @@ parseChunkBody(Data, Size) ->
<<Body:Size/binary, "\r\n", Rest/binary>> ->
{ok, Body, Rest};
_ ->
{error, not_enough_data}
not_enough_data
end.
parseChunkSize(Bin) ->
@ -161,42 +252,3 @@ parseHeaders([Header | T], Acc) ->
parseHeaders(T, [{Key, Value} | Acc])
end.
parseStatusLine(Data, #binPatterns{rn = Rn}) ->
case binary:split(Data, Rn) of
[Data] ->
{error, not_enough_data};
[Line, Rest] ->
case parseStatusReason(Line) of
{ok, StatusCode, Reason} ->
{StatusCode, Reason, Rest};
{error, Reason} ->
{error, Reason}
end
end.
parseStatusReason(<<"HTTP/1.1 200 OK">>) ->
{ok, 200, <<"OK">>};
parseStatusReason(<<"HTTP/1.1 204 No Content">>) ->
{ok, 204, <<"No Content">>};
parseStatusReason(<<"HTTP/1.1 301 Moved Permanently">>) ->
{ok, 301, <<"Moved Permanently">>};
parseStatusReason(<<"HTTP/1.1 302 Found">>) ->
{ok, 302, <<"Found">>};
parseStatusReason(<<"HTTP/1.1 403 Forbidden">>) ->
{ok, 403, <<"Forbidden">>};
parseStatusReason(<<"HTTP/1.1 404 Not Found">>) ->
{ok, 404, <<"Not Found">>};
parseStatusReason(<<"HTTP/1.1 500 Internal Server Error">>) ->
{ok, 500, <<"Internal Server Error">>};
parseStatusReason(<<"HTTP/1.1 502 Bad Gateway">>) ->
{ok, 502, <<"Bad Gateway">>};
parseStatusReason(<<"HTTP/1.1 ", N1, N2, N3, " ", Reason/bits>>)
when $0 =< N1, N1 =< $9,
$0 =< N2, N2 =< $9,
$0 =< N3, N3 =< $9 ->
StatusCode = (N1 - $0) * 100 + (N2 - $0) * 10 + (N3 - $0),
{ok, StatusCode, Reason};
parseStatusReason(<<"HTTP/1.0 ", _/binary>>) ->
{error, unsupported_feature};
parseStatusReason(_) ->
{error, bad_request}.

+ 15
- 13
src/httpCli/agTcpAgencyIns.erl Прегледај датотеку

@ -16,6 +16,8 @@
serverName :: serverName(),
userPassWord :: binary(),
host :: binary(),
rn :: binary:cp(),
rnrn :: binary:cp(),
reconnectState :: undefined | reconnectState(),
socket :: undefined | inet:socket(),
timerRef :: undefined | reference()
@ -28,7 +30,7 @@ init({PoolName, AgencyName, AgencyOpts}) ->
BacklogSize = ?GET_FROM_LIST(backlogSize, AgencyOpts, ?DEFAULT_BACKLOG_SIZE),
ReconnectState = agAgencyUtils:initReconnectState(AgencyOpts),
self() ! ?miDoNetConnect,
{ok, #srvState{poolName = PoolName, serverName = AgencyName, reconnectState = ReconnectState}, #cliState{backlogSize = BacklogSize}}.
{ok, #srvState{poolName = PoolName, serverName = AgencyName, rn = binary:compile_pattern(<<"\r\n">>), rnrn = binary:compile_pattern(<<"\r\n\r\n">>), reconnectState = ReconnectState}, #cliState{backlogSize = BacklogSize}}.
-spec handleMsg(term(), srvState(), cliState()) -> {ok, term(), term()}.
handleMsg({miRequest, FromPid, _Method, _Path, _Headers, _Body, RequestId, _OverTime},
@ -70,23 +72,23 @@ handleMsg({miRequest, FromPid, Method, Path, Headers, Body, RequestId, OverTime}
end
end;
handleMsg({tcp, Socket, Data},
#srvState{serverName = ServerName, socket = Socket} = SrvState,
#cliState{binPatterns = BinPatterns, backlogNum = BacklogNum, curInfo = CurInfo, requestsOut = RequestsOut, recvState = RecvState} = CliState) ->
try agAgencyUtils:handleData(Data, BinPatterns, RecvState) of
{ok, waiting_data, NewClientState} ->
{ok, SrvState, NewClientState};
{ok, RequestRet, NewClientState} ->
agAgencyUtils:agencyResponse(RequestRet, CurInfo),
#srvState{serverName = ServerName, rn = Rn, rnrn = RnRn, socket = Socket} = SrvState,
#cliState{backlogNum = BacklogNum, curInfo = CurInfo, requestsOut = RequestsOut, recvState = RecvState} = CliState) ->
try agHttpProtocol:response(RecvState, Rn, RnRn, Data) of
{done, #recvState{statusCode = StatusCode, contentLength = ContentLength, body = Body}} ->
agAgencyUtils:agencyReply(CurInfo, #requestRet{statusCode = StatusCode, contentLength = ContentLength, body = Body}),
case agAgencyUtils:getQueue(RequestsOut + 1) of
undefined ->
{ok, SrvState, NewClientState#cliState{backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined}};
{ok, SrvState, CliState#cliState{backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined}};
MiRequest ->
dealQueueRequest(MiRequest, SrvState, NewClientState#cliState{backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined})
dealQueueRequest(MiRequest, SrvState, CliState#cliState{backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined})
end;
{error, Reason, NewClientState} ->
{ok, NewRecvState} ->
{ok, SrvState, CliState#cliState{recvState = NewRecvState}};
{error, Reason} ->
?WARN(ServerName, "handle tcp data error: ~p~n", [Reason]),
gen_tcp:close(Socket),
dealClose(SrvState, NewClientState, {error, tcp_data_error})
dealClose(SrvState, CliState, {error, tcp_data_error})
catch
E:R:S ->
?WARN(ServerName, "handle tcp data crash: ~p:~p~n~p~n", [E, R, S]),
@ -123,7 +125,7 @@ handleMsg(?miDoNetConnect,
{ok, Socket} ->
NewReconnectState = agAgencyUtils:resetReconnectState(ReconnectState),
%% buff之类状态数据
NewCliState = CliState#cliState{binPatterns = agHttpProtocol:binPatterns(), buffer = <<>>, status = leisure, recvState = undefined, curInfo = undefined},
NewCliState = CliState#cliState{status = leisure, recvState = undefined, curInfo = undefined},
case agAgencyUtils:getQueue(RequestsOut + 1) of
undefined ->
{ok, SrvState#srvState{userPassWord = UserPassword, host = Host, reconnectState = NewReconnectState, socket = Socket}, NewCliState};

Loading…
Откажи
Сачувај