Exemple #1
0
        private async Task <T> SendWebRequestAsync <T>(string httpMethod, string absoluteUrl, object request, CancellationToken token, bool recall = false)
        {
            if (httpMethod == null)
            {
                throw new ArgumentNullException(nameof(httpMethod));
            }

            this.PopulateRequestMetadata(request);

            var requestUri     = absoluteUrl;
            var hasQueryString = request != null && !HttpUtils.HasRequestBody(httpMethod);

            if (hasQueryString)
            {
                var queryString = QueryStringSerializer.SerializeToString(request);
                if (!string.IsNullOrEmpty(queryString))
                {
                    requestUri += "?" + queryString;
                }
            }

            var webReq = this.CreateHttpWebRequest(requestUri);

            var    timedOut = false;
            ITimer timer    = null;

            timer = PclExportClient.Instance.CreateTimer(state =>
            {
                timedOut = true;
                webReq?.Abort();
                webReq = null;
                timer?.Cancel();
                timer = null;
            }, this.Timeout.GetValueOrDefault(DefaultTimeout), this);

            Exception ResolveException(Exception ex)
            {
                if (token.IsCancellationRequested)
                {
                    return(new OperationCanceledException(token));
                }
                if (timedOut)
                {
                    return(PclExportClient.Instance.CreateTimeoutException(ex, "The request timed out"));
                }
                return(ex);
            }

            bool            returningWebResponse = false;
            HttpWebResponse webRes = null;

            T Complete(T response)
            {
                timer.Cancel();
                PclExportClient.Instance.SynchronizeCookies(this);
                ResultsFilterResponse?.Invoke(webRes, response, httpMethod, absoluteUrl, request);
                return(response);
            }

            webReq.Accept = ContentType;

            if (this.EmulateHttpViaPost)
            {
                webReq.Method = "POST";
                webReq.Headers[HttpHeaders.XHttpMethodOverride] = httpMethod;
            }
            else
            {
                webReq.Method = httpMethod;
            }

            PclExportClient.Instance.AddHeader(webReq, Headers);
            PclExport.Instance.Config(webReq, userAgent: UserAgent);

            if (this.authInfo != null && !string.IsNullOrEmpty(this.UserName))
            {
                webReq.AddAuthInfo(this.UserName, this.Password, authInfo);
            }
            else if (this.BearerToken != null)
            {
                webReq.Headers[HttpHeaders.Authorization] = "Bearer " + this.BearerToken;
            }
            else if (this.Credentials != null)
            {
                webReq.Credentials = this.Credentials;
            }
            else if (this.AlwaysSendBasicAuthHeader)
            {
                webReq.AddBasicAuth(this.UserName, this.Password);
            }

            if (!DisableAutoCompression)
            {
                PclExport.Instance.AddCompression(webReq);
            }

            ApplyWebRequestFilters(webReq);

            try
            {
                if (HttpUtils.HasRequestBody(webReq.Method))
                {
                    webReq.ContentType = ContentType;

                    if (RequestCompressionType != null)
                    {
                        webReq.Headers[HttpHeaders.ContentEncoding] = RequestCompressionType;
                    }

                    using (var requestStream = await webReq.GetRequestStreamAsync().ConfigureAwait(false))
                    {
                        token.ThrowIfCancellationRequested();
                        if (request != null)
                        {
                            StreamSerializer(null, request, requestStream);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                if (Log.IsDebugEnabled)
                {
                    Log.Debug($"Error Sending Request: {ex.Message}", ex);
                }

                throw HandleResponseError <T>(ResolveException(ex), requestUri, request);
            }

            try
            {
                webRes = (HttpWebResponse)await webReq.GetResponseAsync().ConfigureAwait(false);

                {
                    token.ThrowIfCancellationRequested();

                    ApplyWebResponseFilters(webRes);

                    returningWebResponse = typeof(T) == typeof(HttpWebResponse);
                    if (returningWebResponse)
                    {
                        return(Complete((T)(object)webRes));
                    }

                    var responseStream = webRes.ResponseStream();

                    var responseBodyLength = webRes.ContentLength;
                    var bufferRead         = new byte[BufferSize];

                    var totalRead = 0;
                    int read;
                    var ms = MemoryStreamFactory.GetStream();

                    while ((read = await responseStream.ReadAsync(bufferRead, 0, bufferRead.Length, token).ConfigureAwait(false)) != 0)
                    {
                        ms.Write(bufferRead, 0, read);
                        totalRead += read;
                        OnDownloadProgress?.Invoke(totalRead, responseBodyLength);
                    }

                    try
                    {
                        ms.Position = 0;
                        if (typeof(T) == typeof(Stream))
                        {
                            return(Complete((T)(object)ms));
                        }
                        else
                        {
                            var stream = ms;
                            try
                            {
                                if (typeof(T) == typeof(string))
                                {
                                    return(Complete((T)(object)stream.ReadToEnd()));
                                }
                                else if (typeof(T) == typeof(byte[]))
                                {
                                    return(Complete((T)(object)stream.ToArray()));
                                }
                                else
                                {
                                    return(Complete((T)this.StreamDeserializer(typeof(T), stream)));
                                }
                            }
                            finally
                            {
                                if (stream.CanRead)
                                {
                                    stream.Dispose(); // Not yet disposed, but could've been.
                                }
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        if (Log.IsDebugEnabled)
                        {
                            Log.Debug($"Error Reading Response Error: {ex.Message}", ex);
                        }

                        throw;
                    }
                    finally
                    {
                        responseStream.Close();
                    }
                }
            }
            catch (Exception ex)
            {
                var webEx     = ex as WebException;
                var firstCall = !recall;
                if (firstCall && WebRequestUtils.ShouldAuthenticate(webEx,
                                                                    (!string.IsNullOrEmpty(UserName) && !string.IsNullOrEmpty(Password)) ||
                                                                    Credentials != null ||
                                                                    BearerToken != null ||
                                                                    RefreshToken != null ||
                                                                    OnAuthenticationRequired != null))
                {
                    try
                    {
                        if (RefreshToken != null)
                        {
                            var refreshRequest = new GetAccessToken {
                                RefreshToken = RefreshToken
                            };
                            var uri = this.RefreshTokenUri ??
                                      this.BaseUri.CombineWith(refreshRequest.ToPostUrl());

                            GetAccessTokenResponse tokenResponse;
                            try
                            {
                                tokenResponse = uri.PostJsonToUrl(refreshRequest)
                                                .FromJson <GetAccessTokenResponse>();
                            }
                            catch (WebException refreshEx)
                            {
                                var webServiceEx = ServiceClientBase.ToWebServiceException(refreshEx,
                                                                                           stream => StreamDeserializer(typeof(T), stream),
                                                                                           ContentType);

                                if (webServiceEx != null)
                                {
                                    throw new RefreshTokenException(webServiceEx);
                                }

                                throw new RefreshTokenException(refreshEx.Message, refreshEx);
                            }

                            var accessToken = tokenResponse?.AccessToken;
                            if (string.IsNullOrEmpty(accessToken))
                            {
                                throw new RefreshTokenException("Could not retrieve new AccessToken from: " + uri);
                            }

                            var refreshClient = webReq = (HttpWebRequest)WebRequest.Create(requestUri);
                            if (this.CookieContainer.GetTokenCookie(BaseUri) != null)
                            {
                                this.CookieContainer.SetTokenCookie(accessToken, BaseUri);
                                refreshClient.CookieContainer.SetTokenCookie(BaseUri, accessToken);
                            }
                            else
                            {
                                refreshClient.AddBearerToken(this.BearerToken = accessToken);
                            }

                            return(await SendWebRequestAsync <T>(httpMethod, absoluteUrl, request, token, recall : true).ConfigureAwait(false));
                        }

                        OnAuthenticationRequired?.Invoke();

                        var newReq = (HttpWebRequest)WebRequest.Create(requestUri);

                        if (StoreCookies)
                        {
                            newReq.CookieContainer = CookieContainer;
                        }

                        HandleAuthException(ex, webReq);

                        return(await SendWebRequestAsync <T>(httpMethod, absoluteUrl, request, token, recall : true).ConfigureAwait(false));
                    }
                    catch (WebServiceException)
                    {
                        throw;
                    }
                    catch (Exception /*subEx*/)
                    {
                        throw HandleResponseError <T>(ResolveException(ex), requestUri, request);
                    }
                }

                if (ExceptionFilter != null && webEx?.Response != null)
                {
                    var cachedResponse = ExceptionFilter(webEx, webEx.Response, requestUri, typeof(T));
                    if (cachedResponse is T variable)
                    {
                        return(variable);
                    }
                }

                throw HandleResponseError <T>(ResolveException(ex), requestUri, request);
            }
            finally
            {
                if (!returningWebResponse)
                {
                    webRes?.Dispose();
                }
            }
        }
        private void HandleResponseError <TResponse>(Exception exception, AsyncState <TResponse> state)
        {
            var webEx = exception as WebException;

            if (PclExportClient.Instance.IsWebException(webEx))
            {
                var errorResponse = (HttpWebResponse)webEx.Response;
                Log.Error(webEx);
                if (Log.IsDebugEnabled)
                {
                    Log.Debug($"Status Code : {errorResponse.StatusCode}");
                    Log.Debug($"Status Description : {errorResponse.StatusDescription}");
                }

                var serviceEx = new WebServiceException(errorResponse.StatusDescription)
                {
                    StatusCode        = (int)errorResponse.StatusCode,
                    StatusDescription = errorResponse.StatusDescription,
                    ResponseHeaders   = errorResponse.Headers
                };

                try
                {
                    using (var stream = errorResponse.GetResponseStream())
                    {
                        var bytes = stream.ReadFully();
                        serviceEx.ResponseBody = bytes.FromUtf8Bytes();
                        var errorResponseType = WebRequestUtils.GetErrorResponseDtoType <TResponse>(state.Request);

                        if (stream.CanSeek)
                        {
                            PclExport.Instance.ResetStream(stream);
                            serviceEx.ResponseDto = this.StreamDeserializer(errorResponseType, stream);
                        }
                        else //Android
                        {
                            using (var ms = MemoryStreamFactory.GetStream(bytes))
                            {
                                serviceEx.ResponseDto = this.StreamDeserializer(errorResponseType, ms);
                            }
                        }
                        state.HandleError(serviceEx.ResponseDto, serviceEx);
                    }
                }
                catch (Exception innerEx)
                {
                    // Oh, well, we tried
                    Log.Debug($"WebException Reading Response Error: {innerEx.Message}", innerEx);
                    state.HandleError(default(TResponse), new WebServiceException(errorResponse.StatusDescription, innerEx)
                    {
                        StatusCode        = (int)errorResponse.StatusCode,
                        StatusDescription = errorResponse.StatusDescription,
                        ResponseHeaders   = errorResponse.Headers
                    });
                }
                return;
            }

            var authEx = exception as AuthenticationException;

            if (authEx != null)
            {
                var customEx = WebRequestUtils.CreateCustomException(state.Url, authEx);

                Log.Debug($"AuthenticationException: {customEx.Message}", customEx);
                state.HandleError(default(TResponse), authEx);
            }

            Log.Debug($"Exception Reading Response Error: {exception.Message}", exception);
            state.HandleError(default(TResponse), exception);

            CancelAsyncFn = null;
        }
        private void HandleResponseError <TResponse>(Exception exception, AsyncState <TResponse> state)
        {
            var webEx = exception as WebException;

            if (webEx.IsWebException())
            {
                var errorResponse = ((HttpWebResponse)webEx.Response);
                Log.Error(webEx);
                Log.DebugFormat("Status Code : {0}", errorResponse.StatusCode);
                Log.DebugFormat("Status Description : {0}", errorResponse.StatusDescription);

                var serviceEx = new WebServiceException(errorResponse.StatusDescription)
                {
                    StatusCode        = (int)errorResponse.StatusCode,
                    StatusDescription = errorResponse.StatusDescription,
                };

                try
                {
                    using (var stream = errorResponse.GetResponseStream())
                    {
                        //Uncomment to Debug exceptions:
                        //var strResponse = new StreamReader(stream).ReadToEnd();
                        //Console.WriteLine("Response: " + strResponse);
                        //stream.Position = 0;
                        serviceEx.ResponseBody = stream.ReadFully().FromUtf8Bytes();

                        PclExport.Instance.ResetStream(stream);

                        serviceEx.ResponseDto = this.StreamDeserializer(typeof(TResponse), stream);
                        state.HandleError((TResponse)serviceEx.ResponseDto, serviceEx);
                    }
                }
                catch (Exception innerEx)
                {
                    // Oh, well, we tried
                    Log.Debug(string.Format("WebException Reading Response Error: {0}", innerEx.Message), innerEx);
                    state.HandleError(default(TResponse), new WebServiceException(errorResponse.StatusDescription, innerEx)
                    {
                        StatusCode        = (int)errorResponse.StatusCode,
                        StatusDescription = errorResponse.StatusDescription,
                    });
                }
                return;
            }

            var authEx = exception as AuthenticationException;

            if (authEx != null)
            {
                var customEx = WebRequestUtils.CreateCustomException(state.Url, authEx);

                Log.Debug(string.Format("AuthenticationException: {0}", customEx.Message), customEx);
                state.HandleError(default(TResponse), authEx);
            }

            Log.Debug(string.Format("Exception Reading Response Error: {0}", exception.Message), exception);
            state.HandleError(default(TResponse), exception);

            CancelAsyncFn = null;
        }
        private void ResponseCallback <T>(IAsyncResult asyncResult)
        {
            var requestState = (AsyncState <T>)asyncResult.AsyncState;

            try
            {
                requestState.Token.ThrowIfCancellationRequested();

                var webRequest = requestState.WebRequest;

                requestState.WebResponse = (HttpWebResponse)webRequest.EndGetResponse(asyncResult);

                requestState.OnResponseInit?.Invoke(requestState.WebResponse);

                if (requestState.ResponseContentLength == default(long))
                {
                    requestState.ResponseContentLength = requestState.WebResponse.ContentLength;
                }

                ApplyWebResponseFilters(requestState.WebResponse);

                if (typeof(T) == typeof(HttpWebResponse))
                {
                    requestState.HandleSuccess((T)(object)requestState.WebResponse);
                    return;
                }

                // Read the response into a Stream object.
#if NETSTANDARD1_1 || NETSTANDARD1_6
                var responseStream = requestState.WebResponse.GetResponseStream()
                                     .Decompress(requestState.WebResponse.Headers[HttpHeaders.ContentEncoding]);
#else
                var responseStream = requestState.WebResponse.GetResponseStream();
#endif
                requestState.ResponseStream = responseStream;

                var task = responseStream.ReadAsync(requestState.BufferRead, 0, BufferSize);
                ReadCallBack(task, requestState);
            }
            catch (Exception ex)
            {
                var webEx     = ex as WebException;
                var firstCall = Interlocked.Increment(ref requestState.RequestCount) == 1;
                if (firstCall && WebRequestUtils.ShouldAuthenticate(webEx,
                                                                    (!string.IsNullOrEmpty(UserName) && !string.IsNullOrEmpty(Password)) ||
                                                                    Credentials != null ||
                                                                    BearerToken != null ||
                                                                    RefreshToken != null ||
                                                                    OnAuthenticationRequired != null))
                {
                    try
                    {
                        if (RefreshToken != null)
                        {
                            var refreshRequest = new GetAccessToken {
                                RefreshToken = RefreshToken
                            };
                            var uri = this.RefreshTokenUri ?? this.BaseUri.CombineWith(refreshRequest.ToPostUrl());

                            GetAccessTokenResponse tokenResponse;
                            try
                            {
                                tokenResponse = uri.PostJsonToUrl(refreshRequest).FromJson <GetAccessTokenResponse>();
                            }
                            catch (WebException refreshEx)
                            {
                                var webServiceEx = ServiceClientBase.ToWebServiceException(refreshEx,
                                                                                           stream => StreamDeserializer(typeof(T), stream),
                                                                                           ContentType);

                                if (webServiceEx != null)
                                {
                                    throw new RefreshTokenException(webServiceEx);
                                }

                                throw new RefreshTokenException(refreshEx.Message, refreshEx);
                            }

                            var accessToken = tokenResponse?.AccessToken;
                            if (string.IsNullOrEmpty(accessToken))
                            {
                                throw new RefreshTokenException("Could not retrieve new AccessToken from: " + uri);
                            }

                            var refreshClient = requestState.WebRequest =
                                (HttpWebRequest)WebRequest.Create(requestState.Url);
                            if (this.CookieContainer.GetTokenCookie(BaseUri) != null)
                            {
                                this.CookieContainer.SetTokenCookie(accessToken, BaseUri);
                                refreshClient.CookieContainer.SetTokenCookie(BaseUri, accessToken);
                            }
                            else
                            {
                                refreshClient.AddBearerToken(this.BearerToken = accessToken);
                            }

                            SendWebRequestAsync(
                                requestState.HttpMethod, requestState.Request,
                                requestState, requestState.WebRequest);

                            return;
                        }

                        OnAuthenticationRequired?.Invoke();

                        requestState.WebRequest = (HttpWebRequest)WebRequest.Create(requestState.Url);

                        if (StoreCookies)
                        {
                            requestState.WebRequest.CookieContainer = CookieContainer;
                        }

                        HandleAuthException(ex, requestState.WebRequest);

                        SendWebRequestAsync(
                            requestState.HttpMethod, requestState.Request,
                            requestState, requestState.WebRequest);
                    }
                    catch (WebServiceException rethrow)
                    {
                        requestState.HandleError(default(T), rethrow);
                    }
                    catch (Exception /*subEx*/)
                    {
                        HandleResponseError(ex, requestState);
                    }
                    return;
                }

                if (ExceptionFilter != null && webEx != null && webEx.Response != null)
                {
                    var cachedResponse = ExceptionFilter(webEx, webEx.Response, requestState.Url, typeof(T));
                    if (cachedResponse is T)
                    {
                        requestState.OnSuccess((T)cachedResponse);
                        return;
                    }
                }

                HandleResponseError(ex, requestState);
            }
        }
        private void ResponseCallback <T>(IAsyncResult asyncResult)
        {
            var requestState = (AsyncState <T>)asyncResult.AsyncState;

            try
            {
                var webRequest = requestState.WebRequest;

                requestState.WebResponse = (HttpWebResponse)webRequest.EndGetResponse(asyncResult);

                if (requestState.ResponseContentLength == default(long))
                {
                    requestState.ResponseContentLength = requestState.WebResponse.ContentLength;
                }

                ApplyWebResponseFilters(requestState.WebResponse);

                if (typeof(T) == typeof(HttpWebResponse))
                {
                    requestState.HandleSuccess((T)(object)requestState.WebResponse);
                    return;
                }

                // Read the response into a Stream object.
                var responseStream = requestState.WebResponse.GetResponseStream();
                requestState.ResponseStream = responseStream;

                var task = responseStream.ReadAsync(requestState.BufferRead, 0, BufferSize);
                ReadCallBack(task, requestState);
            }
            catch (Exception ex)
            {
                var firstCall = Interlocked.Increment(ref requestState.RequestCount) == 1;
                if (firstCall && WebRequestUtils.ShouldAuthenticate(ex, this.UserName, this.Password))
                {
                    try
                    {
                        requestState.WebRequest = (HttpWebRequest)WebRequest.Create(requestState.Url);

                        if (StoreCookies)
                        {
                            requestState.WebRequest.CookieContainer = CookieContainer;
                        }

                        requestState.WebRequest.AddBasicAuth(this.UserName, this.Password);

                        if (OnAuthenticationRequired != null)
                        {
                            OnAuthenticationRequired(requestState.WebRequest);
                        }

                        SendWebRequestAsync(
                            requestState.HttpMethod, requestState.Request,
                            requestState, requestState.WebRequest);
                    }
                    catch (Exception /*subEx*/)
                    {
                        HandleResponseError(ex, requestState);
                    }
                    return;
                }

                HandleResponseError(ex, requestState);
            }
        }