public IInteraction TransformClientRequestForRealService(IInteraction clientRequest) { var builder = new MarkdownInteraction.Builder() .From(clientRequest) .RequestHeaders(clientRequest.RequestHeaders //Remove unwanted headers from client request .Where(h => !_requestHeaderExcludePatterns.Any(pattern => pattern.IsMatch($"{h.Item1}: {h.Item2}"))) //Fix host header .Select(h => { (string name, string value) = h; if (name.ToLower() == "host") { return(name, _realServiceHost); } else { return(h); } }) ); return(builder.Build()); }
public async Task <ServiceResponse> GetServiceResponseForRequest(Uri host, IInteraction interaction, bool lowerCaseHeaders) { var response = await _service.InvokeServiceEndpoint( interaction.Method, null, null, new Uri($"{_redirectHost.GetLeftPart(UriPartial.Authority)}{interaction.Path}"), interaction.RequestHeaders); var builder = new MarkdownInteraction.Builder() .From(interaction) .ResponseHeaders(response.Headers); if (response.Body != null && response.ContentType != null) { builder.ResponseBody(response.Body.ToString(), response.ContentType); } else { builder.RemoveResponseBody(); } var interactionToRecord = builder.Build(); _allInteractions[interactionToRecord.Number] = interactionToRecord; return(response); }
//private static readonly private AspNetCoreServirtiumServer(IHostBuilder hostBuilder, IInteractionMonitor monitor, IInteractionTransforms interactionTransforms, int?port) { _interactionMonitor = monitor; _host = hostBuilder.ConfigureWebHostDefaults(webBuilder => { if (port != null) { //If a port is specified, override urls with specified port, listening on all available hosts, for HTTP. webBuilder.UseUrls($"http://*:{port}"); } webBuilder.Configure(app => { app.Run(async ctx => { var targetHost = new Uri($"{ctx.Request.Scheme}{Uri.SchemeDelimiter}{ctx.Request.Host}"); var requestInteraction = new MarkdownInteraction.Builder() .Number(_interactionCounter.Bump()) .Method(new System.Net.Http.HttpMethod(ctx.Request.Method)) .Path($"{ctx.Request.Path}{ctx.Request.QueryString}") //Remap headers from a dictionary of string lists to a list of (string, string) tuples .RequestHeaders ( ctx.Request.Headers .SelectMany(kvp => kvp.Value.Select(val => (kvp.Key, val))) .ToArray() ) .Build(); var serviceRequestInteraction = interactionTransforms.TransformClientRequestForRealService(requestInteraction); var responseFromService = await monitor.GetServiceResponseForRequest( targetHost, serviceRequestInteraction, false); var clientResponse = interactionTransforms.TransformRealServiceResponseForClient(responseFromService); //Always remove the 'Transfer-Encoding: chunked' header if present. //If it's present in the response.Headers collection ast this point, Kestrel expects you to add chunk notation to the body yourself //However if you just send it with no content-length, Kestrel will add the chunked header and chunk the body for you. clientResponse = clientResponse .WithRevisedHeaders( clientResponse.Headers .Where((h) => !(h.Name.ToLower() == "transfer-encoding" && h.Value.ToLower() == "chunked"))) .WithReadjustedContentLength(); ctx.Response.OnCompleted(() => { Console.WriteLine($"{requestInteraction.Method} Request to {targetHost}{requestInteraction.Path} returned to client with code {ctx.Response.StatusCode}"); return(Task.CompletedTask); }); //Transfer adjusted headers to the response going out to the client foreach ((string headerName, string headerValue) in clientResponse.Headers) { if (ctx.Response.Headers.TryGetValue(headerName, out var headerInResponse)) { ctx.Response.Headers[headerName] = new StringValues(headerInResponse.Append(headerValue).ToArray()); } else { ctx.Response.Headers[headerName] = new StringValues(headerValue); } } if (clientResponse.Body != null) { await ctx.Response.WriteAsync(clientResponse.Body.ToString()); } await ctx.Response.CompleteAsync(); }); }); }).Build(); }