示例#1
0
        public HttpResponseInfo Copy()
        {
            HttpResponseInfo copy = (HttpResponseInfo)this.MemberwiseClone();

            return(copy);
        }
示例#2
0
        /// <summary>
        /// A powerful function that performs, via HttpClient, an HTTP GET request,
        /// but while adding the following helpful functions:
        /// 1) If <paramref name="settings"/> (<see cref="HttpNotModifiedProperties"/>) is not null,
        /// it allows a conditional GET request to be made.
        /// 2) Timeout allows a true timeout to be set that doesn't have to use the deeply flawed
        /// HttpClient timeout value. This is a great design flaw since their design requires the HttpClient
        /// to be reused globally.
        /// 3) Useful information is returned about the request, the HttpStatus, the basic result (Success or NotModified
        /// or Failed...), and the HttpHeaders, as well as NotModified related information that can be used on the next
        /// request to make a 304 (NotModified) request (<see cref="HttpResponseInfo"/> inherits <see cref="HttpNotModifiedProperties"/>).
        /// 4) Timing diagnostics on the response times, for both headers and content.
        /// 5) Exceptions are caught, but set on the <see cref="HttpResponseInfo.Ex"/> property.
        /// </summary>
        /// <param name="url">Url to GET.</param>
        /// <param name="settings">Not modified properties in order to make a not-modified request.</param>
        /// <param name="timeout">Timeout if any.</param>
        /// <param name="client">Client if any.</param>
        /// <param name="cancellationToken">Cancellation token.</param>
        public static async Task <HttpResponseInfo> GetAsync(
            string url,
            HttpNotModifiedProperties settings = null,
            TimeSpan?timeout  = null,
            HttpClient client = null,
            CancellationToken?cancellationToken = null)
        {
            var swTotal = Stopwatch.StartNew();

            if (url.IsNulle())
            {
                throw new ArgumentNullException(nameof(url));
            }

            var s = settings ?? new HttpNotModifiedProperties();
            HttpResponseInfo h = new HttpResponseInfo()
            {
                Date = DateTimeOffset.UtcNow
            };

            h.CopyValuesToThis(s);

            if (client == null)
            {
                client = _getHttpClient();
            }

            CancellationToken cancelToken = cancellationToken ?? new CancellationToken();
            bool condGet = s.ConditionalGet;

            TimeSpan _timeout = timeout ?? TimeSpan.Zero;

            if (_timeout <= TimeSpan.Zero)
            {
                _timeout = DefaultTimeout;
            }
            //if(timeoutSeconds > 0) // problem, HttpClient is supposed to be used globally!!
            //	client.Timeout = TimeSpan.FromSeconds((int)timeoutSeconds);

            bool closeClient = client == null;

            try {
                // --- PREP REQUEST ---
                var request = new HttpRequestMessage(HttpMethod.Get, url)
                              .SetDefaultHeaders();

                // set request IfModifiedSince header if available
                if (condGet)
                {
                    request.Headers.IfModifiedSince = s.LastModified;
                }

                // set request ETag if available
                if (condGet && s.ETag.NotNulle())
                {
                    //request.Headers.IfNoneMatch.Add(new EntityTagHeaderValue(""))
                    request.Headers.Add("If-None-Match", FixETag(s.ETag));
                }
                // -- GET RESPONSE --


                try {
                    var sw = Stopwatch.StartNew();

                    var response = await client
                                   .SendAsync(request, cancelToken)
                                   //.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancelToken)
                                   .TimeoutAfterAsync(_timeout);

                    h.HeaderResponseTime = sw.ElapsedAndReset();

                    TimeSpan remainingTime = _timeout - h.HeaderResponseTime;
                    if (remainingTime <= TimeSpan.Zero)
                    {
                        remainingTime = TimeSpan.FromMilliseconds(1);
                        // shouldn't happen, but if TimeoutAfterAsync didn't throw, make this not
                        // a negative number at least, which is invalid for the next TimeoutAfterAsync call
                    }

                    h.StatusCode = response.StatusCode;

                    h.ResultHeadersDict = response.GetHeadersDictionary();

                    h.ResultHeaders = h.ResultHeadersDict.__kvHeadersToString();

                    // --- Status code checks ---

                    if (response.StatusCode == HttpStatusCode.NotModified)
                    {
                        h.Result = HttpResponseResult.NotModified;
                        return(h);
                    }

                    bool isSuccess = response.IsSuccessStatusCode;
                    h.Result = isSuccess
                                                ? HttpResponseResult.Success
                                                : HttpResponseResult.Fail;

                    // OLD! now we DO allow content to be read even if is fail,
                    // only NotMod stops the content from being read
                    //if (!response.IsSuccessStatusCode) {
                    //	h.Result = HttpResponseResult.Fail;
                    //	return h;
                    //}

                    if (isSuccess)
                    {
                        // --- ETag ---

                        string responseEtag = FixETag(h.ResultHeadersDict.V("ETag"));                         // response.Headers.ETag; // IS NULL!! STUPID!! Come on guys!

                        if (condGet && s.ETag.NotNulle() && responseEtag == s.ETag)
                        {
                            h.Result = HttpResponseResult.NotModified;
                            return(h);
                        }
                        h.ETag = responseEtag;

                        // --- ContentLength check #1) If Content-Length header is set ---

                        int?contentLengthHeader = h.ResultHeadersDict.V("Content-Length").TrimIfNeeded().ToIntN();
                        if (condGet && s.ContentLengthNotModified(contentLengthHeader))
                        {
                            // contentLengthNotModified(eqCntLenNotMod, contentLengthHeader, s.ContentLength)) {
                            h.Result = HttpResponseResult.NotModified;
                            return(h);
                        }
                    }

                    // --- READ CONTENT ---

                    sw.Start();

                    byte[] data = await response.Content
                                  .ReadAsByteArrayAsync()
                                  .TimeoutAfterAsync(remainingTime);

                    h.ContentResponseTime = sw.ElapsedAndStop();

                    if (data == null)
                    {
                        data = new byte[0];                                   // this seems right
                    }
                    h.ContentData = data;

                    int dataLen = data.LengthN();

                    // --- ContentLength check #2) If actual data length is equal ---
                    if (isSuccess && condGet && s.ContentLengthNotModified(dataLen))
                    {
                        h.Result = HttpResponseResult.NotModified;
                        return(h);
                    }

                    h.ContentLength = dataLen;                     // methods above should have set, but no hurt

                    return(h);
                }
                catch (Exception ex) {
                    h.Result = HttpResponseResult.Fail;
                    if (ex is TimeoutException)
                    {
                        h.Result = HttpResponseResult.TimeOut;
                    }
                    else
                    {
                        h.Ex = ex;                         // don't save if just a timeout
                    }
                    return(h);
                }
            }
            finally {
                if (closeClient && client != null)
                {
                    client.Dispose();
                }
                if (h != null && swTotal != null && swTotal.IsRunning)
                {
                    h.TotalResponseTime = swTotal.Elapsed;
                }
            }
        }