From 31631c533cba4ac397c5aadf66671278a7b05632 Mon Sep 17 00:00:00 2001 From: AICells <1713699517@qq.com> Date: Sun, 29 Dec 2019 01:46:41 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/agHttpCli.hrl | 19 ++- src/httpCli/agAgencyUtils.erl | 48 ++----- src/httpCli/agHttpCli.erl | 4 +- src/httpCli/agHttpProtocol.erl | 228 ++++++++++++++++++++------------- src/httpCli/agTcpAgencyIns.erl | 28 ++-- 5 files changed, 174 insertions(+), 153 deletions(-) diff --git a/include/agHttpCli.hrl b/include/agHttpCli.hrl index 0a31cf0..8010c9f 100644 --- a/include/agHttpCli.hrl +++ b/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{}. diff --git a/src/httpCli/agAgencyUtils.erl b/src/httpCli/agAgencyUtils.erl index 4629134..f8d5806 100644 --- a/src/httpCli/agAgencyUtils.erl +++ b/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 = <>, - 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. - diff --git a/src/httpCli/agHttpCli.erl b/src/httpCli/agHttpCli.erl index 8548b3d..6ca36e7 100644 --- a/src/httpCli/agHttpCli.erl +++ b/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 diff --git a/src/httpCli/agHttpProtocol.erl b/src/httpCli/agHttpProtocol.erl index e558637..bdd5e6a 100644 --- a/src/httpCli/agHttpProtocol.erl +++ b/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(), binary: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 = <>, + case parseChunks(NewBuffer, Rn, []) of + {ok, AddBody, _Rest} -> + LastBody = <>, + {done, RecvState#recvState{stage = done, body = LastBody}}; + {not_enough_data, AddBody, Rest} -> + NewBody = <>, + {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 -> - <> = 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 = <>, + 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 = <>, + 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) -> <> -> {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}. diff --git a/src/httpCli/agTcpAgencyIns.erl b/src/httpCli/agTcpAgencyIns.erl index fef4666..0b2c8fe 100644 --- a/src/httpCli/agTcpAgencyIns.erl +++ b/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};