Example #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);

            if (webReq != null && Proxy != null)
            {
                webReq.Proxy = Proxy;
            }

            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;
                    }

                    if (HttpLog != null)
                    {
                        webReq.AppendHttpRequestHeaders(HttpLog, new Uri(BaseUri));
                    }

                    using var requestStream = await webReq.GetRequestStreamAsync().ConfigAwait();

                    token.ThrowIfCancellationRequested();

                    if (request != null)
                    {
                        StreamSerializer(null, request, requestStream);
                    }
                }
                else
                {
                    if (HttpLog != null)
                    {
                        webReq.AppendHttpRequestHeaders(HttpLog, new Uri(BaseUri));
                    }
                }
                HttpLog?.AppendLine();
            }
            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().ConfigAwait();

                {
                    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).ConfigAwait()) != 0)
                    {
                        await ms.WriteAsync(bufferRead, 0, read, token).ConfigAwait();

                        totalRead += read;
                        OnDownloadProgress?.Invoke(totalRead, responseBodyLength);
                    }

                    try
                    {
                        ms.Position = 0;

                        if (HttpLog != null)
                        {
                            webRes.AppendHttpResponseHeaders(HttpLog);
                            if (webRes.ContentLength != 0 && webRes.StatusCode != HttpStatusCode.NoContent)
                            {
                                var isBinary = typeof(T) == typeof(Stream) || typeof(T) == typeof(byte[]) || ContentType.IsBinary();
                                if (isBinary)
                                {
                                    HttpLog.Append("(base64) ");
                                    HttpLog.AppendLine(Convert.ToBase64String(ms.ReadFully()));
                                }
                                else
                                {
                                    HttpLog.AppendLine(ms.ReadToEnd());
                                }
                                HttpLog.AppendLine().AppendLine();
                                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)await stream.ReadToEndAsync().ConfigAwait()));
                                }
                                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
                    {
                        if (HttpLog != null)
                        {
                            HttpLogFilter?.Invoke(HttpLog);
                        }

                        responseStream.Close();
                    }
                }
            }
            catch (Exception ex)
            {
                var webEx                 = ex as WebException;
                var firstCall             = !recall;
                var hasRefreshTokenCookie = this.CookieContainer.GetRefreshTokenCookie(BaseUri) != null;
                var hasRefreshToken       = RefreshToken != null || hasRefreshTokenCookie;

                if (firstCall && WebRequestUtils.ShouldAuthenticate(webEx,
                                                                    (!string.IsNullOrEmpty(UserName) && !string.IsNullOrEmpty(Password)) ||
                                                                    Credentials != null ||
                                                                    BearerToken != null ||
                                                                    hasRefreshToken ||
                                                                    OnAuthenticationRequired != null))
                {
                    try
                    {
                        if (hasRefreshToken)
                        {
                            var refreshRequest = new GetAccessToken {
                                RefreshToken = hasRefreshTokenCookie ? null : RefreshToken,
                            };
                            var uri = this.RefreshTokenUri ?? this.BaseUri.CombineWith(refreshRequest.ToPostUrl());

                            this.BearerToken = null;
                            this.CookieContainer?.DeleteCookie(new Uri(BaseUri), "ss-tok");

                            GetAccessTokenResponse tokenResponse;
                            try
                            {
                                tokenResponse = (await uri.PostJsonToUrlAsync(refreshRequest, requestFilter: req => {
                                    if (hasRefreshTokenCookie)
                                    {
                                        req.CookieContainer = CookieContainer;
                                    }
                                }, token: token).ConfigAwait()).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;
                            var refreshClient = webReq = (HttpWebRequest)WebRequest.Create(requestUri);
                            var tokenCookie   = this.CookieContainer.GetTokenCookie(BaseUri);

                            if (!string.IsNullOrEmpty(accessToken))
                            {
                                refreshClient.AddBearerToken(this.BearerToken = accessToken);
                            }
                            else if (tokenCookie != null)
                            {
                                refreshClient.CookieContainer = CookieContainer;
                                refreshClient.CookieContainer.SetTokenCookie(BaseUri, tokenCookie);
                            }
                            else
                            {
                                throw new RefreshTokenException("Could not retrieve new AccessToken from: " + uri);
                            }

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

                        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).ConfigAwait());
                    }
                    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();
                }
            }
        }
Example #2
0
        public static string ToUrl(this object requestDto, string httpMethod, Func<Type, string> fallback)
        {
            httpMethod = httpMethod.ToUpper();
            var urlFilter = requestDto as IUrlFilter;

            var requestType = requestDto.GetType();
            var requestRoutes = routesCache.GetOrAdd(requestType, GetRoutesForType);
            if (requestRoutes.Count == 0)
            {
                if (fallback == null)
                    throw new InvalidOperationException($"There are no rest routes mapped for '{requestType}' type. "
                        + "(Note: The automatic route selection only works with [Route] attributes on the request DTO and "
                        + "not with routes registered in the IAppHost!)");

                var predefinedRoute = fallback(requestType);
                if (httpMethod is "GET" or "DELETE" or "OPTIONS" or "HEAD")
                {
                    var queryProperties = RestRoute.GetQueryProperties(requestDto.GetType());
                    if (queryProperties.Count > 0)
                        predefinedRoute += "?" + RestRoute.GetQueryString(requestDto, queryProperties);
                }

                return urlFilter == null ? predefinedRoute : urlFilter.ToUrl(predefinedRoute);
            }

            var routesApplied = requestRoutes.Select(route => route.Apply(requestDto, httpMethod)).ToList();
            var matchingRoutes = routesApplied.Where(x => x.Matches).ToList();
            if (matchingRoutes.Count == 0)
            {
                var errors = Join(Empty, routesApplied.Select(x =>
                    $"\r\n\t{x.Route.Path}:\t{x.FailReason}").ToArray());
                var errMsg = $"None of the given rest routes matches '{requestType.GetOperationName()}' request:{errors}";

                throw new InvalidOperationException(errMsg);
            }

            RouteResolutionResult matchingRoute;
            if (matchingRoutes.Count > 1)
            {
                matchingRoute = FindMostSpecificRoute(matchingRoutes);
                if (matchingRoute == null)
                {
                    var errors = Join(Empty, matchingRoutes.Select(x => "\r\n\t" + x.Route.Path).ToArray());
                    var errMsg = $"Ambiguous matching routes found for '{requestType.Name}' request:{errors}";
                    throw new InvalidOperationException(errMsg);
                }
            }
            else
            {
                matchingRoute = matchingRoutes[0];
            }

            var url = matchingRoute.Uri;
            if (!HttpUtils.HasRequestBody(httpMethod))
            {
                var queryParams = matchingRoute.Route.FormatQueryParameters(requestDto);
                if (!IsNullOrEmpty(queryParams))
                {
                    url += "?" + queryParams;
                }
            }

            return urlFilter == null ? url : urlFilter.ToUrl(url);
        }
Example #3
0
        public List <PostmanRequest> GetRequests(Postman request, string parentId, IEnumerable <Operation> operations)
        {
            var ret     = new List <PostmanRequest>();
            var feature = HostContext.GetPlugin <PostmanFeature>();

            var headers = feature.Headers ?? ("Accept: " + MimeTypes.Json);

            if (Response is IHttpResponse httpRes)
            {
                if (request.ssopt != null ||
                    request.sspid != null ||
                    request.ssid != null)
                {
                    if (feature.EnableSessionExport != true)
                    {
                        throw new ArgumentException("PostmanFeature.EnableSessionExport is not enabled");
                    }
                }

                if (request.ssopt != null)
                {
                    Request.AddSessionOptions(request.ssopt);
                }
                if (request.sspid != null)
                {
                    httpRes.Cookies.AddPermanentCookie(SessionFeature.PermanentSessionId, request.sspid);
                }
                if (request.ssid != null)
                {
                    httpRes.Cookies.AddSessionCookie(SessionFeature.SessionId, request.ssid,
                                                     (HostContext.Config.UseSecureCookies && Request.IsSecureConnection));
                }
            }

            foreach (var op in operations)
            {
                if (!HostContext.Metadata.IsVisible(base.Request, op))
                {
                    continue;
                }

                var allVerbs = op.Actions.Concat(
                    op.Routes.SelectMany(x => x.Verbs))
                               .SelectMany(x => x == ActionContext.AnyAction
                        ? feature.DefaultVerbsForAny
                        : new List <string> {
                    x
                })
                               .ToSet();

                var propertyTypes = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase);
                op.RequestType.GetSerializableFields()
                .Each(x => propertyTypes[x.Name] = x.FieldType.AsFriendlyName(feature));
                op.RequestType.GetSerializableProperties()
                .Each(x => propertyTypes[x.Name] = x.PropertyType.AsFriendlyName(feature));

                foreach (var route in op.Routes)
                {
                    var routeVerbs = route.Verbs.Contains(ActionContext.AnyAction)
                        ? feature.DefaultVerbsForAny.ToArray()
                        : route.Verbs;

                    var restRoute = route.ToRestRoute();

                    foreach (var verb in routeVerbs)
                    {
                        allVerbs.Remove(verb); //exclude handled verbs

                        var routeData = restRoute.QueryStringVariables
                                        .Map(x => new PostmanData
                        {
                            key   = x,
                            value = "",
                            type  = "text",
                        })
                                        .ApplyPropertyTypes(propertyTypes);

                        ret.Add(new PostmanRequest
                        {
                            collectionId  = parentId,
                            id            = SessionExtensions.CreateRandomSessionId(),
                            method        = verb,
                            url           = Request.GetBaseUrl().CombineWith(restRoute.Path.ToPostmanPathVariables()),
                            name          = GetName(feature, request, op.RequestType, restRoute.Path),
                            description   = op.RequestType.GetDescription(),
                            pathVariables = !HttpUtils.HasRequestBody(verb)
                                ? restRoute.Variables.Concat(routeData.Select(x => x.key))
                                            .ApplyPropertyTypes(propertyTypes)
                                : null,
                            data = HttpUtils.HasRequestBody(verb)
                                ? routeData
                                : null,
                            dataMode = "params",
                            headers  = headers,
                            version  = 2,
                            time     = DateTime.UtcNow.ToUnixTimeMs(),
                        });
                    }
                }

                var emptyRequest = op.RequestType.CreateInstance();
                var virtualPath  = emptyRequest.ToReplyUrlOnly();

                var requestParams = propertyTypes
                                    .Map(x => new PostmanData
                {
                    key   = x.Key,
                    value = x.Value,
                    type  = "text",
                });

                ret.AddRange(allVerbs.Select(verb =>
                                             new PostmanRequest
                {
                    collectionId  = parentId,
                    id            = SessionExtensions.CreateRandomSessionId(),
                    method        = verb,
                    url           = Request.GetBaseUrl().CombineWith(virtualPath),
                    pathVariables = !HttpUtils.HasRequestBody(verb)
                            ? requestParams.Select(x => x.key)
                                    .ApplyPropertyTypes(propertyTypes)
                            : null,
                    name        = GetName(feature, request, op.RequestType, virtualPath),
                    description = op.RequestType.GetDescription(),
                    data        = HttpUtils.HasRequestBody(verb)
                            ? requestParams
                            : null,
                    dataMode = "params",
                    headers  = headers,
                    version  = 2,
                    time     = DateTime.UtcNow.ToUnixTimeMs(),
                }));
            }

            return(ret);
        }
Example #4
0
        public Task <TResponse> SendAsync <TResponse>(string httpMethod, string absoluteUrl, object request, CancellationToken token = default(CancellationToken))
        {
            var client = GetHttpClient();

            if (!HttpUtils.HasRequestBody(httpMethod) && request != null)
            {
                var queryString = QueryStringSerializer.SerializeToString(request);
                if (!string.IsNullOrEmpty(queryString))
                {
                    absoluteUrl += "?" + queryString;
                }
            }

            var response = ResultsFilter?.Invoke(typeof(TResponse), httpMethod, absoluteUrl, request);

            if (response is TResponse)
            {
                var tcs = new TaskCompletionSource <TResponse>();
                tcs.SetResult((TResponse)response);
                return(tcs.Task);
            }

            if (token == default(CancellationToken))
            {
                if (CancelTokenSource == null)
                {
                    CancelTokenSource = new CancellationTokenSource();
                }
                token = CancelTokenSource.Token;
            }

            var httpReq       = CreateRequest(httpMethod, absoluteUrl, request);
            var sendAsyncTask = client.SendAsync(httpReq, token);

            if (typeof(TResponse) == typeof(HttpResponseMessage))
            {
                return((Task <TResponse>)(object) sendAsyncTask);
            }

            return(sendAsyncTask
                   .ContinueWith(responseTask =>
            {
                var httpRes = responseTask.Result;

                if (httpRes.StatusCode == HttpStatusCode.Unauthorized)
                {
                    if (RefreshToken != null)
                    {
                        var refreshDto = new GetAccessToken {
                            RefreshToken = RefreshToken
                        };
                        var uri = this.RefreshTokenUri ?? this.BaseUri.CombineWith(refreshDto.ToPostUrl());
                        return this.PostAsync <GetAccessTokenResponse>(uri, refreshDto)
                        .ContinueWith(t =>
                        {
                            if (t.IsFaulted)
                            {
                                var refreshEx = t.Exception.UnwrapIfSingleException() as WebServiceException;
                                if (refreshEx != null)
                                {
                                    throw new RefreshTokenException(refreshEx);
                                }
                                throw t.Exception;
                            }

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

                            var refreshRequest = CreateRequest(httpMethod, absoluteUrl, request);
                            if (this.GetTokenCookie() != null)
                            {
                                this.SetTokenCookie(accessToken);
                            }
                            else
                            {
                                refreshRequest.AddBearerToken(this.BearerToken = accessToken);
                            }

                            return client.SendAsync(refreshRequest, token).ContinueWith(refreshTask =>
                                                                                        ConvertToResponse <TResponse>(refreshTask.Result,
                                                                                                                      httpMethod, absoluteUrl, refreshRequest, token),
                                                                                        token).Unwrap();
                        }, token).Unwrap();
                    }
                    if (UserName != null && Password != null && client.DefaultRequestHeaders.Authorization == null)
                    {
                        AddBasicAuth(client);
                        httpReq = CreateRequest(httpMethod, absoluteUrl, request);
                        sendAsyncTask = client.SendAsync(httpReq, token);
                        return sendAsyncTask.ContinueWith(t =>
                                                          ConvertToResponse <TResponse>(t.Result, httpMethod, absoluteUrl, request, token),
                                                          token).Unwrap();
                    }
                }

                return ConvertToResponse <TResponse>(httpRes, httpMethod, absoluteUrl, request, token);
            }, token).Unwrap());
        }