private async Task <ResponseModel> HandleRequest(string correlation)
    {
        var requestLogger = _requestLoggerFactory.GetRequestLogger();

        // Enable rewind here to be able to read the posted body multiple times.
        _httpContextService.EnableRewind();

        // Log the request here
        requestLogger.LogRequestParameters(
            _httpContextService.Method,
            _httpContextService.DisplayUrl,
            _httpContextService.GetBody(),
            _clientDataResolver.GetClientIp(),
            _httpContextService.GetHeaders());

        _httpContextService.ClearResponse();
        _httpContextService.TryAddHeader(CorrelationHeaderKey, correlation);
        var response = await _stubRequestExecutor.ExecuteRequestAsync();

        _httpContextService.SetStatusCode(response.StatusCode);
        foreach (var(key, value) in response.Headers)
        {
            _httpContextService.AddHeader(key, value);
        }

        if (response.Body != null)
        {
            await _httpContextService.WriteAsync(response.Body);
        }

        return(response);
    }
    /// <inheritdoc />
    public string Parse(string input, IEnumerable <Match> matches)
    {
        var headers = _httpContextService.GetHeaders();

        foreach (var match in matches)
        {
            var headerName   = match.Groups[2].Value;
            var replaceValue = headers.CaseInsensitiveSearch(headerName);

            input = input.Replace(match.Value, replaceValue);
        }

        return(input);
    }
        public string Parse(string input, IEnumerable <Match> matches)
        {
            var headers = _httpContextService.GetHeaders();

            foreach (var match in matches)
            {
                if (match.Groups.Count != 3)
                {
                    continue;
                }

                var headerName = match.Groups[2].Value;
                headers.TryGetValue(headerName, out var replaceValue);

                input = input.Replace(match.Value, replaceValue);
            }

            return(input);
        }
    /// <inheritdoc />
    public ConditionCheckResultModel Validate(StubModel stub)
    {
        var result           = new ConditionCheckResultModel();
        var headerConditions = stub.Conditions?.Headers;

        if (headerConditions == null || headerConditions?.Any() != true)
        {
            return(result);
        }

        var validHeaders = 0;
        var headers      = _httpContextService.GetHeaders();

        foreach (var condition in headerConditions)
        {
            // Check whether the condition header is available in the actual headers.
            var headerValue = headers.CaseInsensitiveSearch(condition.Key);
            if (string.IsNullOrWhiteSpace(headerValue))
            {
                continue;
            }

            // Check whether the condition header value is available in the actual headers.
            var value = condition.Value ?? string.Empty;
            if (!StringHelper.IsRegexMatchOrSubstring(headerValue, value))
            {
                // If the check failed, it means the header is incorrect and the condition should fail.
                result.Log = $"Header condition '{condition.Key}: {condition.Value}' failed.";
                break;
            }

            validHeaders++;
        }

        // If the number of succeeded conditions is equal to the actual number of conditions,
        // the header condition is passed and the stub ID is passed to the result.
        result.ConditionValidation = validHeaders == headerConditions.Count
            ? ConditionValidationType.Valid
            : ConditionValidationType.Invalid;
        return(result);
    }
        public ConditionCheckResultModel Validate(string stubId, StubConditionsModel conditions)
        {
            var result = new ConditionCheckResultModel();
            var basicAuthenticationCondition = conditions?.BasicAuthentication;

            if (basicAuthenticationCondition == null ||
                (string.IsNullOrWhiteSpace(basicAuthenticationCondition.Username) &&
                 string.IsNullOrWhiteSpace(basicAuthenticationCondition.Password)))
            {
                return(result);
            }

            var headers = _httpContextService.GetHeaders();

            // Try to retrieve the Authorization header.
            if (!headers.TryGetValue("Authorization", out var authorization))
            {
                result.ConditionValidation = ConditionValidationType.Invalid;
                result.Log = "No Authorization header found in request.";
            }
            else
            {
                var expectedBase64UsernamePassword = Convert.ToBase64String(
                    Encoding.UTF8.GetBytes(
                        $"{basicAuthenticationCondition.Username}:{basicAuthenticationCondition.Password}"));
                var expectedAuthorizationHeader = $"Basic {expectedBase64UsernamePassword}";
                if (expectedAuthorizationHeader == authorization)
                {
                    result.Log = $"Basic authentication condition passed for stub '{stubId}'.";
                    result.ConditionValidation = ConditionValidationType.Valid;
                }
                else
                {
                    result.Log =
                        $"Basic authentication condition failed for stub '{stubId}'. Expected '{expectedAuthorizationHeader}' but found '{authorization}'.";
                    result.ConditionValidation = ConditionValidationType.Invalid;
                }
            }

            return(result);
        }
 /// <summary>
 /// Handles the middleware.
 /// </summary>
 public async Task Invoke(HttpContext context)
 {
     if (_httpContextService.Path.Contains("ph-api/"))
     {
         _httpContextService.AddHeader("Access-Control-Allow-Origin", "*");
         _httpContextService.AddHeader("Access-Control-Allow-Headers", "Authorization, Content-Type");
         _httpContextService.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
         _httpContextService.AddHeader("Cache-Control", "no-store, no-cache");
         _httpContextService.AddHeader("Expires", "-1");
         if (_httpContextService.GetHeaders().ContainsKey("Origin") && _httpContextService.Method.Equals("OPTIONS"))
         {
             _httpContextService.SetStatusCode(HttpStatusCode.OK);
         }
         else
         {
             await _next(context);
         }
     }
     else
     {
         await _next(context);
     }
 }
Пример #7
0
    /// <inheritdoc />
    public async Task <StubResponseWriterResultModel> WriteToResponseAsync(StubModel stub, ResponseModel response)
    {
        if (stub.Response.ReverseProxy == null || string.IsNullOrWhiteSpace(stub.Response.ReverseProxy.Url))
        {
            return(StubResponseWriterResultModel.IsNotExecuted(GetType().Name));
        }

        var proxyUrl   = stub.Response.ReverseProxy.Url;
        var appendPath = stub.Response.ReverseProxy.AppendPath == true;

        if (appendPath)
        {
            proxyUrl = proxyUrl.EnsureEndsWith("/") + _httpContextService.Path.TrimStart('/');
            if (!string.IsNullOrWhiteSpace(stub.Conditions?.Url?.Path))
            {
                // If the path condition is set, make sure the configured path is stripped from the proxy URL
                var configuredPath = stub.Conditions.Url.Path;
                var index          = proxyUrl.IndexOf(configuredPath, StringComparison.OrdinalIgnoreCase);
                if (index > -1)
                {
                    proxyUrl = proxyUrl.Remove(index, configuredPath.Length);
                }
            }
        }

        if (stub.Response.ReverseProxy.AppendQueryString == true)
        {
            proxyUrl += _httpContextService.GetQueryString();
        }

        var method          = new HttpMethod(_httpContextService.Method);
        var request         = new HttpRequestMessage(method, proxyUrl);
        var log             = $"Performing {method} request to URL {proxyUrl}";
        var originalHeaders = _httpContextService
                              .GetHeaders();
        var headers = originalHeaders
                      .Where(h => !_excludedRequestHeaderNames.Contains(h.Key, StringComparer.OrdinalIgnoreCase))
                      .ToArray();

        foreach (var header in headers)
        {
            request.Headers.Add(header.Key, header.Value);
        }

        if (method != HttpMethod.Get)
        {
            var requestBody = _httpContextService.GetBodyAsBytes();
            if (requestBody.Any())
            {
                request.Content = new ByteArrayContent(requestBody);
                var contentTypeHeader = originalHeaders.FirstOrDefault(h =>
                                                                       h.Key.Equals("content-type", StringComparison.OrdinalIgnoreCase));
                if (!string.IsNullOrWhiteSpace(contentTypeHeader.Value))
                {
                    request.Content.Headers.Add("Content-Type", contentTypeHeader.Value);
                }
            }
        }

        using var httpClient      = _httpClientFactory.CreateClient("proxy");
        using var responseMessage = await httpClient.SendAsync(request);

        var content = responseMessage.Content != null
            ? await responseMessage.Content.ReadAsByteArrayAsync()
            : Array.Empty <byte>();

        var rawResponseHeaders = responseMessage.Headers
                                 .ToDictionary(h => h.Key, h => h.Value.First());

        if (stub.Response.ReverseProxy.ReplaceRootUrl == true)
        {
            var contentAsString       = Encoding.UTF8.GetString(content);
            var rootUrlParts          = proxyUrl.Split(new[] { "/" }, StringSplitOptions.RemoveEmptyEntries);
            var rootUrl               = $"{rootUrlParts[0]}//{rootUrlParts[1]}";
            var httPlaceholderRootUrl = _httpContextService.RootUrl;
            if (appendPath && !string.IsNullOrWhiteSpace(stub.Conditions?.Url?.Path))
            {
                httPlaceholderRootUrl += stub.Conditions.Url.Path.EnsureStartsWith("/");
            }

            contentAsString = contentAsString.Replace(rootUrl, httPlaceholderRootUrl);
            content         = Encoding.UTF8.GetBytes(contentAsString);

            rawResponseHeaders = rawResponseHeaders
                                 .ToDictionary(h => h.Key, h => h.Value.Replace(rootUrl, httPlaceholderRootUrl));
        }

        response.Body = content;
        var contentHeaders = responseMessage.Content != null
            ? responseMessage.Content.Headers.ToDictionary(h => h.Key, h => h.Value.First())
            : new Dictionary <string, string>();

        var responseHeaders = rawResponseHeaders
                              .Concat(contentHeaders)
                              .Where(h => !_excludedResponseHeaderNames.Contains(h.Key, StringComparer.OrdinalIgnoreCase))
                              .ToArray();

        foreach (var header in responseHeaders)
        {
            response.Headers.AddOrReplaceCaseInsensitive(header.Key, header.Value);
        }

        response.StatusCode = (int)responseMessage.StatusCode;
        return(StubResponseWriterResultModel.IsExecuted(GetType().Name, log));
    }
Пример #8
0
        // ReSharper disable once UnusedMember.Global
        public async Task Invoke(HttpContext context)
        {
            var path = _httpContextService.Path;

            if (_segmentsToIgnore.Any(s => path.Contains(s, StringComparison.OrdinalIgnoreCase)))
            {
                await _next(context);

                return;
            }

            const string correlationHeaderKey = "X-HttPlaceholder-Correlation";
            var          correlation          = Guid.NewGuid().ToString();
            var          requestLogger        = _requestLoggerFactory.GetRequestLogger();

            requestLogger.SetCorrelationId(correlation);
            try
            {
                // Enable rewind here to be able to read the posted body multiple times.
                _httpContextService.EnableRewind();

                // Log the request here
                requestLogger.LogRequestParameters(
                    _httpContextService.Method,
                    _httpContextService.DisplayUrl,
                    _httpContextService.GetBody(),
                    _clientDataResolver.GetClientIp(),
                    _httpContextService.GetHeaders());

                _httpContextService.ClearResponse();
                _httpContextService.TryAddHeader(correlationHeaderKey, correlation);
                var response = await _stubRequestExecutor.ExecuteRequestAsync();

                _httpContextService.SetStatusCode(response.StatusCode);
                foreach (var(key, value) in response.Headers)
                {
                    _httpContextService.AddHeader(key, value);
                }

                if (response.Body != null)
                {
                    await _httpContextService.WriteAsync(response.Body);
                }
            }
            catch (RequestValidationException e)
            {
                _httpContextService.SetStatusCode((int)HttpStatusCode.InternalServerError);
                _httpContextService.TryAddHeader(correlationHeaderKey, correlation);
                _logger.LogInformation($"Request validation exception thrown: {e.Message}");
            }
            catch (Exception e)
            {
                _httpContextService.SetStatusCode((int)HttpStatusCode.InternalServerError);
                _httpContextService.TryAddHeader(correlationHeaderKey, correlation);
                _logger.LogWarning($"Unexpected exception thrown: {e}");
            }

            var loggingResult        = requestLogger.GetResult();
            var jsonLoggingResult    = JObject.FromObject(loggingResult);
            var enableRequestLogging = _settings.Storage?.EnableRequestLogging ?? false;

            if (enableRequestLogging)
            {
                _logger.LogInformation(jsonLoggingResult.ToString());
            }

            await _stubContext.AddRequestResultAsync(loggingResult);

            // We need to map the model to a DTO here, because the frontend expected that.
            await _requestNotify.NewRequestReceivedAsync(_mapper.Map <RequestResultDto>(loggingResult));
        }