发布0101
大石头 编写于 2020-01-01 23:35:09
X
using System;
using System.Net;
using NewLife.Data;
using NewLife.Net;


 //TODO請求需要Mask,如果用這裏做WS通訊會存在問題


namespace NewLife.Http
{
    /// <summary>Http客户端</summary>
    public class HttpClient : TcpSession
    {
        #region 属性
        /// <summary>是否WebSocket</summary>
        public Boolean IsWebSocket { get; set; }

        /// <summary>是否启用SSL</summary>
        public Boolean IsSSL { get; set; }

        ///// <summary>Http方法</summary>
        //public String Method { get; set; }

        ///// <summary>资源路径</summary>
        //public Uri Url { get; set; }

        /// <summary>请求</summary>
        public HttpRequest Request { get; set; } = new HttpRequest();

        /// <summary>响应</summary>
        public HttpResponse Response { get; set; }

        /// <summary>WebSocket握手</summary>
        private Boolean wsHandshake;
        #endregion

        #region 构造
        /// <summary>实例化增强TCP</summary>
        public HttpClient() : base()
        {
            Name = GetType().Name;
            Remote.Port = 80;

            //DisconnectWhenEmptyData = false;
            ProcessAsync = false;
            MatchEmpty = true;

            Opened += HttpClient_Opened;
        }
        #endregion

        #region 方法
        /// <summary>打开</summary>
        protected override Boolean OnOpen()
        {
            // 默认80端口
            if (!Active && Remote.Port == 0) Remote.Port = 80;

            if (Remote.Address.IsAny()) Remote = new NetUri(Request.Url + "");

            //Request.Method = Method;
            //Request.Url = Url;

            // 添加过滤器
            if (SendFilter == null) SendFilter = new HttpRequestFilter { Client = this };

            return base.OnOpen();
        }

        private void HttpClient_Opened(Object sender, EventArgs e)
        {
            // WebSocket此时需要发送握手
            if (IsWebSocket)
            {
                HttpHelper.MakeHandshake(Request);

                //SendAsync(new Byte[0]);
                try
                {
                    var pk = SendAsync(new Byte[0]).Result;
                    //WriteLog("Handshake# {0}", pk.Count);
                    if (Response.StatusCode != HttpStatusCode.SwitchingProtocols) throw new Exception(Response.StatusCode + "");
                    wsHandshake = true;
                }
                catch //(Exception ex)
                {
                    //WriteLog(ex.GetTrue()?.Message);
                    Close("HandshakeError");
                    throw;
                }
            }
        }
        #endregion

        #region 收发数据
        /// <summary>处理收到的数据</summary>
        /// <param name="pk"></param>
        /// <param name="remote"></param>
        internal override void ProcessReceive(Packet pk, IPEndPoint remote)
        {
            if (pk.Count == 0 && DisconnectWhenEmptyData)
            {
                Close("收到空数据");
                Dispose();

                return;
            }

            /*
             * 解析流程:
             *  首次访问或过期,创建请求对象
             *      判断头部是否完整
             *          --判断主体是否完整
             *              触发新的请求
             *          --加入缓存
             *      加入缓存
             *  触发收到数据
             */

            try
            {
                var header = Response;

                // 是否全新请求
                if (header == null || !IsWebSocket && (header.Expire < DateTime.Now || header.IsCompleted))
                {
                    var res = new HttpResponse { Expire = DateTime.Now.AddSeconds(5) };

                    // 分析头部
                    if (res.ParseHeader(pk))
                    {
                        Response = header = res;

                        // 握手响应包
                        if (IsWebSocket && pk.Count == 0) Protocol?.Match(pk, remote);

#if DEBUG
                        WriteLog(" {0} {1} {2}", (Int32)header.StatusCode, header.StatusCode, header.ContentLength);
#endif
                    }
                }

                // WebSocket
                if (IsWebSocket)
                {
                    pk = HttpHelper.ParseWS(pk);

                    OnReceive(pk, remote);
                }
                else
                {
                    if (header.ParseBody(ref pk)) OnReceive(pk, remote);
                }
            }
            catch (Exception ex)
            {
                if (!ex.IsDisposed()) OnError("OnReceive", ex);
            }
        }
        #endregion

        #region Http客户端
        class HttpRequestFilter : FilterBase
        {
            public HttpClient Client { get; set; }

            protected override Boolean OnExecute(FilterContext context)
            {
                var pk = context.Packet;

                if (Client.IsWebSocket && Client.wsHandshake)
                    pk = HttpHelper.MakeWS(pk);
                else
                    pk = Client.Request.Build(pk);

                context.Packet = pk;
#if DEBUG
                //Session.WriteLog(pk.ToStr());
#endif

                return true;
            }
        }
        #endregion
    }
}