/// <summary> /// Creates, configures, sends, and returns a <see cref="HttpContext"/>. This completes as soon as the response is started. /// </summary> /// <returns></returns> public async Task <HttpContext> SendAsync(Action <HttpContext> configureContext, CancellationToken cancellationToken = default) { if (configureContext == null) { throw new ArgumentNullException(nameof(configureContext)); } var builder = new HttpContextBuilder(_application); builder.Configure(context => { var request = context.Request; request.Scheme = BaseAddress.Scheme; request.Host = HostString.FromUriComponent(BaseAddress); if (BaseAddress.IsDefaultPort) { request.Host = new HostString(request.Host.Host); } var pathBase = PathString.FromUriComponent(BaseAddress); if (pathBase.HasValue && pathBase.Value.EndsWith("/")) { pathBase = new PathString(pathBase.Value.Substring(0, pathBase.Value.Length - 1)); } request.PathBase = pathBase; }); builder.Configure(configureContext); return(await builder.SendAsync(cancellationToken).ConfigureAwait(false)); }
/// <summary> /// This adapts HttpRequestMessages to ASP.NET Core requests, dispatches them through the pipeline, and returns the /// associated HttpResponseMessage. /// </summary> /// <param name="request"></param> /// <param name="cancellationToken"></param> /// <returns></returns> protected override async Task <HttpResponseMessage> SendAsync( HttpRequestMessage request, CancellationToken cancellationToken) { if (request == null) { throw new ArgumentNullException(nameof(request)); } var contextBuilder = new HttpContextBuilder(_application); Stream responseBody = null; var requestContent = request.Content ?? new StreamContent(Stream.Null); var body = await requestContent.ReadAsStreamAsync(); contextBuilder.Configure(context => { var req = context.Request; req.Protocol = "HTTP/" + request.Version.ToString(fieldCount: 2); req.Method = request.Method.ToString(); req.Scheme = request.RequestUri.Scheme; req.Host = HostString.FromUriComponent(request.RequestUri); if (request.RequestUri.IsDefaultPort) { req.Host = new HostString(req.Host.Host); } req.Path = PathString.FromUriComponent(request.RequestUri); req.PathBase = PathString.Empty; if (req.Path.StartsWithSegments(_pathBase, out var remainder)) { req.Path = remainder; req.PathBase = _pathBase; } req.QueryString = QueryString.FromUriComponent(request.RequestUri); foreach (var header in request.Headers) { req.Headers.Append(header.Key, header.Value.ToArray()); } if (requestContent != null) { foreach (var header in requestContent.Headers) { req.Headers.Append(header.Key, header.Value.ToArray()); } } if (body.CanSeek) { // This body may have been consumed before, rewind it. body.Seek(0, SeekOrigin.Begin); } req.Body = body; responseBody = context.Response.Body; }); var httpContext = await contextBuilder.SendAsync(cancellationToken); var response = new HttpResponseMessage(); response.StatusCode = (HttpStatusCode)httpContext.Response.StatusCode; response.ReasonPhrase = httpContext.Features.Get <IHttpResponseFeature>().ReasonPhrase; response.RequestMessage = request; response.Content = new StreamContent(responseBody); foreach (var header in httpContext.Response.Headers) { if (!response.Headers.TryAddWithoutValidation(header.Key, (IEnumerable <string>)header.Value)) { bool success = response.Content.Headers.TryAddWithoutValidation(header.Key, (IEnumerable <string>)header.Value); Contract.Assert(success, "Bad header"); } } return(response); }