Sshenqing-codeupdate 3.20.7
94382ad8创建于 2020年7月30日历史提交
/*----------------------------------------------------------------------------------

// Copyright 2019 Huawei Technologies Co.,Ltd.

// Licensed under the Apache License, Version 2.0 (the "License"); you may not use

// this file except in compliance with the License.  You may obtain a copy of the

// License at

//

// http://www.apache.org/licenses/LICENSE-2.0

//

// Unless required by applicable law or agreed to in writing, software distributed

// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR

// CONDITIONS OF ANY KIND, either express or implied.  See the License for the

// specific language governing permissions and limitations under the License.

//----------------------------------------------------------------------------------*/

using OBS.Internal.Log;

using OBS.Model;

using System;

using System.IO;

using System.Net;



namespace OBS.Internal

{

    internal partial class HttpClient

    {

        internal HttpObsAsyncResult BeginPerformRequest(HttpRequest request, HttpContext context, 

            AsyncCallback callback, object state)

        {

            this.PrepareRequestAndContext(request, context);

            HttpObsAsyncResult result = new HttpObsAsyncResult(callback, state);

            result.HttpRequest = request;

            result.HttpContext = context;

            result.OriginPos = (request.Content != null && request.Content.CanSeek) ? request.Content.Position : -1L;

            result.RetryCount = 0;

            result.RequestStartDateTime = DateTime.Now;

            this.BeginDoRequest(result);

            return result;

        }



        internal HttpResponse EndPerformRequest(HttpObsAsyncResult result)

        {

            HttpResponse response = null;

            HttpRequest request = result.HttpRequest;

            HttpContext context = result.HttpContext;

            int maxErrorRetry = context.ObsConfig.MaxErrorRetry;

            long originPos = result.OriginPos;

            try

            {

                response = context.ObsConfig.AsyncSocketTimeout < 0 ? result.Get() : result.Get(context.ObsConfig.AsyncSocketTimeout);

                new MergeResponseHeaderHandler(this.GetIHeaders(context));

                int statusCode = Convert.ToInt32(response.StatusCode);

                new MergeResponseHeaderHandler(this.GetIHeaders(context)).Handle(response);



                if (LoggerMgr.IsDebugEnabled)

                {

                    LoggerMgr.Debug(string.Format("Response with statusCode {0} and headers {1}", statusCode, CommonUtil.ConvertHeadersToString(response.Headers)));

                }



                if (statusCode >= 300 && statusCode < 400 && statusCode != 304)

                {

                    if (response.Headers.ContainsKey(Constants.CommonHeaders.Location))

                    {

                        string location = response.Headers[Constants.CommonHeaders.Location];

                        if (!string.IsNullOrEmpty(location))

                        {

                            if (location.IndexOf("?") < 0)

                            {

                                location += "?" + CommonUtil.ConvertParamsToString(request.Params);

                            }

                            if (LoggerMgr.IsWarnEnabled)

                            {

                                LoggerMgr.Warn(string.Format("Redirect to {0}", location));

                            }

                            context.RedirectLocation = location;

                            result.RetryCount--;

                            if (ShouldRetry(request, null, result.RetryCount, maxErrorRetry))

                            {

                                PrepareRetry(request, response, result.RetryCount, originPos, true);

                                result.Reset();

                                this.BeginDoRequest(result);

                                return this.EndPerformRequest(result);

                            }

                            else if (result.RetryCount > maxErrorRetry)

                            {

                                throw ParseObsException(response, "Exceeded 3xx redirect limit", context);

                            }

                        }

                    }

                    throw ParseObsException(response, "Try to redirect, but location is null!", context);

                }

                else if ((statusCode >= 400 && statusCode < 500) || statusCode == 304)

                {

                    ObsException exception = ParseObsException(response, "Request error", context);

                    if (Constants.RequestTimeout.Equals(exception.ErrorCode))

                    {

                        if (ShouldRetry(request, null, result.RetryCount, maxErrorRetry))

                        {

                            if (LoggerMgr.IsWarnEnabled)

                            {

                                LoggerMgr.Warn("Retrying connection that failed with RequestTimeout error");

                            }

                            PrepareRetry(request, response, result.RetryCount, originPos, true);

                            result.Reset();

                            this.BeginDoRequest(result);

                            return this.EndPerformRequest(result);

                        }

                        else if (result.RetryCount > maxErrorRetry && LoggerMgr.IsErrorEnabled)

                        {

                            LoggerMgr.Error("Exceeded maximum number of retries for RequestTimeout errors");

                        }

                    }

                    throw exception;

                }

                else if (statusCode >= 500)

                {

                    if (ShouldRetry(request, null, result.RetryCount, maxErrorRetry))

                    {

                        PrepareRetry(request, response, result.RetryCount, originPos, true);

                        result.Reset();

                        this.BeginDoRequest(result);

                        return this.EndPerformRequest(result);

                    }

                    else if (result.RetryCount > maxErrorRetry && LoggerMgr.IsErrorEnabled)

                    {

                        LoggerMgr.Error("Encountered too many 5xx errors");

                    }

                    throw ParseObsException(response, "Request error", context);

                }



                foreach (HttpResponseHandler handler in context.Handlers)

                {

                    handler.Handle(response);

                }

                return response;

            }

            catch (Exception ex)

            {

                try

                {

                    if (ex is ObsException)

                    {

                        if (LoggerMgr.IsErrorEnabled)

                        {

                            LoggerMgr.Error("Rethrowing as a ObsException error in EndPerformRequest", ex);

                        }

                        throw ex;

                    }

                    else

                    {

                        if (ShouldRetry(request, ex, result.RetryCount, maxErrorRetry))

                        {

                            PrepareRetry(request, response, result.RetryCount, originPos, true);

                            result.Reset();

                            this.BeginDoRequest(result);

                            return this.EndPerformRequest(result);

                        }

                        else if (result.RetryCount > maxErrorRetry && LoggerMgr.IsWarnEnabled)

                        {

                            LoggerMgr.Warn("Too many errors excced the max error retry count", ex);

                        }

                        if (LoggerMgr.IsErrorEnabled)

                        {

                            LoggerMgr.Error("Rethrowing as a ObsException error in PerformRequest", ex);

                        }

                        throw ParseObsException(response, ex.Message, ex, result.HttpContext);

                    }

                }

                finally

                {

                    CommonUtil.CloseIDisposable(response);

                }

            }

        }



        private void BeginDoRequest(HttpObsAsyncResult asyncResult)

        {

            HttpRequest httpRequest = asyncResult.HttpRequest;

            HttpContext context = asyncResult.HttpContext;

            if (!context.SkipAuth)

            {

                this.GetSigner(context).DoAuth(httpRequest, context, this.GetIHeaders(context));

            }



            if (!context.ObsConfig.KeepAlive && !httpRequest.Headers.ContainsKey(Constants.CommonHeaders.Connection))

            {

                httpRequest.Headers.Add(Constants.CommonHeaders.Connection, "Close");

            }



            HttpWebRequest request = HttpWebRequestFactory.BuildWebRequest(httpRequest, context);

            asyncResult.HttpWebRequest = request;

            asyncResult.HttpStartDateTime = DateTime.Now;





            if (httpRequest.Method == HttpVerb.PUT ||

                httpRequest.Method == HttpVerb.POST || httpRequest.Method == HttpVerb.DELETE)

            {

                this.BeginSetContent(asyncResult);

            }

            else

            {

                asyncResult.Continue(this.EndGetResponse);

            }

        }



        private void BeginSetContent(HttpObsAsyncResult asyncResult)

        {

            HttpWebRequest webRequest = asyncResult.HttpWebRequest;

            HttpRequest httpRequest = asyncResult.HttpRequest;



            long userSetContentLength = -1;

            if (httpRequest.Headers.ContainsKey(Constants.CommonHeaders.ContentLength))

            {

                userSetContentLength = long.Parse(httpRequest.Headers[Constants.CommonHeaders.ContentLength]);

            }



            if (userSetContentLength >= 0)

            {

                webRequest.ContentLength = userSetContentLength;

                if (webRequest.ContentLength > Constants.DefaultStreamBufferThreshold)

                {

                    webRequest.AllowWriteStreamBuffering = false;

                }

            }

            else

            {

                webRequest.SendChunked = true;

                webRequest.AllowWriteStreamBuffering = false;

            }



            webRequest.BeginGetRequestStream(this.EndGetRequestStream, asyncResult);

        }



        private void EndGetRequestStream(IAsyncResult ar)

        {

            HttpObsAsyncResult asyncResult = ar.AsyncState as HttpObsAsyncResult;

            HttpWebRequest webRequest = asyncResult.HttpWebRequest;

            ObsConfig obsConfig = asyncResult.HttpContext.ObsConfig;

            Stream data = asyncResult.HttpRequest.Content;

            if (data == null)

            {

                data = new MemoryStream();

            }

            try

            {

                using (Stream requestStream = webRequest.EndGetRequestStream(ar))

                {

                    ObsCallback callback = delegate ()

                    {

                        asyncResult.IsTimeout = false;

                    };

                    if (!webRequest.SendChunked)

                    {

                        CommonUtil.WriteTo(data, requestStream, webRequest.ContentLength, obsConfig.BufferSize, callback);

                    }

                    else

                    {

                        CommonUtil.WriteTo(data, requestStream, obsConfig.BufferSize, callback);

                    }

                }

                asyncResult.Continue(this.EndGetResponse);

            }

            catch (Exception e)

            {

                asyncResult.Abort(e);

            }

        }



        private void EndGetResponse(IAsyncResult ar)

        {

            HttpObsAsyncResult asyncResult = ar.AsyncState as HttpObsAsyncResult;

            asyncResult.IsTimeout = false;

            try

            {

                HttpResponse httpResponse = new HttpResponse(asyncResult.HttpWebRequest.EndGetResponse(ar) as HttpWebResponse);

                asyncResult.Set(httpResponse);

            }

            catch (WebException ex)

            {

                HttpWebResponse response = ex.Response as HttpWebResponse;

                if (response == null)

                {

                    asyncResult.Abort(ex);

                }

                else

                {

                    asyncResult.Set(new HttpResponse(ex, asyncResult.HttpWebRequest));

                }

            }

            catch (Exception ex)

            {

                asyncResult.Abort(ex);

            }

            finally

            {

                if (LoggerMgr.IsInfoEnabled)

                {

                    LoggerMgr.Info(string.Format("Send http request end, cost {0} ms", (DateTime.Now.Ticks - asyncResult.HttpStartDateTime.Ticks) / 10000));

                }

            }

        }





    }



    internal class HttpObsAsyncResult : ObsAsyncResult<HttpResponse>

    {

        public HttpObsAsyncResult(AsyncCallback callback, object state) : base(callback, state)

        {

            this.IsTimeout = true;

        }



        public object AdditionalState { get; set; }



        public HttpRequest HttpRequest { get; set; }



        public HttpContext HttpContext { get; set; }



        public HttpWebRequest HttpWebRequest { get; set; }



        public long OriginPos { get; set; }



        public int RetryCount { get; set; }



        public bool IsTimeout { get; set; }



        public DateTime HttpStartDateTime { get; set; }



        public DateTime RequestStartDateTime { get; set; }



        public void Reset()

        {

            this.Reset(null);

            this.RetryCount++;

        }



        public override void Reset(AsyncCallback callback)

        {

            base.Reset(callback);

            this.HttpWebRequest = null;

        }



        public override HttpResponse Get(int millisecondsTimeout)

        {

            if (!this._isCompleted)

            {

                while (!this._event.WaitOne(millisecondsTimeout))

                {

                    if (IsTimeout)

                    {

                        throw new TimeoutException("Socket timeout");

                    }

                    IsTimeout = true;

                }

            }



            if (this._exception != null)

            {

                throw this._exception;

            }

            return this._result;

        }



        public void Abort()

        {

            if (this.HttpWebRequest != null)

            {

                try

                {

                    this.HttpWebRequest.Abort();

                }

                catch (Exception ex)

                {

                    LoggerMgr.Error(ex.Message, ex);

                }

            }

        }



        public void Abort(Exception ex)

        {

            this.Abort();

            if(ex != null)

            {

                this.Set(ex);

            }

        }



        public void Continue(AsyncCallback callback)

        {

            try

            {

                this.HttpWebRequest.BeginGetResponse(callback, this);

            }

            catch (Exception ex)

            {

                this.Abort(ex);

            }

        }



        protected override void Dispose(bool disposing)

        {

            if (!_disposed)

            {

                base.Dispose(disposing);

                this.AdditionalState = null;

                CommonUtil.CloseIDisposable(this.HttpRequest);

            }

        }

    }





}