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