LogicAppsSampleTestFramework/TestFramework/MockHttpHost.cs (120 lines of code) (raw):
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
namespace TestFramework
{
using System;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Threading;
using System.Web.Http;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.ResponseCompression;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;
/// <summary>
/// The mock HTTP host.
/// </summary>
public class MockHttpHost : IDisposable
{
/// <summary>
/// The web host.
/// </summary>
public IWebHost Host { get; set; }
/// <summary>
/// The request handler.
/// </summary>
public Func<HttpRequestMessage, HttpResponseMessage> RequestHandler { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="MockHttpHost"/> class.
/// </summary>
public MockHttpHost(string url = null)
{
this.Host = WebHost
.CreateDefaultBuilder()
.UseSetting(key: WebHostDefaults.SuppressStatusMessagesKey, value: "true")
.ConfigureLogging(config => config.ClearProviders())
.ConfigureServices(services =>
{
services.AddSingleton<MockHttpHost>(this);
})
.UseStartup<Startup>()
.UseUrls(url ?? TestEnvironment.FlowV2MockTestHostUri)
.Build();
this.Host.Start();
}
/// <summary>
/// Disposes the resources.
/// </summary>
public void Dispose()
{
this.Host.StopAsync().Wait();
}
private class Startup
{
/// <summary>
/// Gets or sets the request pipeline manager.
/// </summary>
private MockHttpHost Host { get; set; }
public Startup(MockHttpHost host)
{
this.Host = host;
}
/// <summary>
/// Configure the services.
/// </summary>
/// <param name="services">The services.</param>
public void ConfigureServices(IServiceCollection services)
{
services
.Configure<IISServerOptions>(options =>
{
options.AllowSynchronousIO = true;
})
.AddResponseCompression(options =>
{
options.EnableForHttps = true;
options.Providers.Add<GzipCompressionProvider>();
})
.AddMvc(options =>
{
options.EnableEndpointRouting = true;
})
.SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
}
/// <summary>
/// Configures the application.
/// </summary>
/// <param name="app">The application.</param>
public void Configure(IApplicationBuilder app)
{
app.UseResponseCompression();
app.Use(async (context, next) =>
{
var syncIOFeature = context.Features.Get<IHttpBodyControlFeature>();
if (syncIOFeature != null)
{
syncIOFeature.AllowSynchronousIO = true;
}
using (var request = GetHttpRequestMessage(context))
using (var responseMessage = this.Host.RequestHandler(request))
{
var response = context.Response;
response.StatusCode = (int)responseMessage.StatusCode;
var responseHeaders = responseMessage.Headers;
// Ignore the Transfer-Encoding header if it is just "chunked".
// We let the host decide about whether the response should be chunked or not.
if (responseHeaders.TransferEncodingChunked == true &&
responseHeaders.TransferEncoding.Count == 1)
{
responseHeaders.TransferEncoding.Clear();
}
foreach (var header in responseHeaders)
{
response.Headers.Append(header.Key, header.Value.ToArray());
}
if (responseMessage.Content != null)
{
var contentHeaders = responseMessage.Content.Headers;
// Copy the response content headers only after ensuring they are complete.
// We ask for Content-Length first because HttpContent lazily computes this
// and only afterwards writes the value into the content headers.
var unused = contentHeaders.ContentLength;
foreach (var header in contentHeaders)
{
response.Headers.Append(header.Key, header.Value.ToArray());
}
await responseMessage.Content.CopyToAsync(response.Body).ConfigureAwait(false);
}
}
});
}
}
/// <summary>
/// Gets the http request message.
/// </summary>
/// <param name="httpContext">The http context.</param>
public static HttpRequestMessage GetHttpRequestMessage(HttpContext httpContext)
{
var feature = httpContext.Features.Get<HttpRequestMessageFeature>();
if (feature == null)
{
feature = new HttpRequestMessageFeature(httpContext);
httpContext.Features.Set(feature);
}
return feature.HttpRequestMessage;
}
}
}