mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-06-19 06:38:14 +08:00
use a stream rather than byte array in responder (#519)
This commit is contained in:
@ -1,16 +1,15 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Configuration.Repository;
|
||||
using Ocelot.Infrastructure.Extensions;
|
||||
using Ocelot.Infrastructure.RequestData;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
|
||||
namespace Ocelot.Errors.Middleware
|
||||
{
|
||||
using Configuration;
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Configuration.Repository;
|
||||
using Ocelot.Infrastructure.Extensions;
|
||||
using Ocelot.Infrastructure.RequestData;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
|
||||
/// <summary>
|
||||
/// Catches all unhandled exceptions thrown by middleware, logs and returns a 500
|
||||
/// </summary>
|
||||
|
@ -1,74 +1,74 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Ocelot.Headers;
|
||||
using Ocelot.Middleware;
|
||||
|
||||
namespace Ocelot.Responder
|
||||
{
|
||||
/// <summary>
|
||||
/// Cannot unit test things in this class due to methods not being implemented
|
||||
/// on .net concretes used for testing
|
||||
/// </summary>
|
||||
public class HttpContextResponder : IHttpResponder
|
||||
{
|
||||
private readonly IRemoveOutputHeaders _removeOutputHeaders;
|
||||
|
||||
public HttpContextResponder(IRemoveOutputHeaders removeOutputHeaders)
|
||||
{
|
||||
_removeOutputHeaders = removeOutputHeaders;
|
||||
}
|
||||
|
||||
public async Task SetResponseOnHttpContext(HttpContext context, DownstreamResponse response)
|
||||
{
|
||||
_removeOutputHeaders.Remove(response.Headers);
|
||||
|
||||
foreach (var httpResponseHeader in response.Headers)
|
||||
{
|
||||
AddHeaderIfDoesntExist(context, httpResponseHeader);
|
||||
}
|
||||
|
||||
foreach (var httpResponseHeader in response.Content.Headers)
|
||||
{
|
||||
AddHeaderIfDoesntExist(context, new Header(httpResponseHeader.Key, httpResponseHeader.Value));
|
||||
}
|
||||
|
||||
var content = await response.Content.ReadAsByteArrayAsync();
|
||||
|
||||
AddHeaderIfDoesntExist(context, new Header("Content-Length", new []{ content.Length.ToString() }) );
|
||||
|
||||
context.Response.OnStarting(state =>
|
||||
{
|
||||
var httpContext = (HttpContext)state;
|
||||
|
||||
httpContext.Response.StatusCode = (int)response.StatusCode;
|
||||
|
||||
return Task.CompletedTask;
|
||||
}, context);
|
||||
|
||||
using (Stream stream = new MemoryStream(content))
|
||||
{
|
||||
if (response.StatusCode != HttpStatusCode.NotModified && context.Response.ContentLength != 0)
|
||||
{
|
||||
await stream.CopyToAsync(context.Response.Body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetErrorResponseOnContext(HttpContext context, int statusCode)
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Ocelot.Headers;
|
||||
using Ocelot.Middleware;
|
||||
|
||||
namespace Ocelot.Responder
|
||||
{
|
||||
/// <summary>
|
||||
/// Cannot unit test things in this class due to methods not being implemented
|
||||
/// on .net concretes used for testing
|
||||
/// </summary>
|
||||
public class HttpContextResponder : IHttpResponder
|
||||
{
|
||||
private readonly IRemoveOutputHeaders _removeOutputHeaders;
|
||||
|
||||
public HttpContextResponder(IRemoveOutputHeaders removeOutputHeaders)
|
||||
{
|
||||
context.Response.StatusCode = statusCode;
|
||||
}
|
||||
|
||||
private static void AddHeaderIfDoesntExist(HttpContext context, Header httpResponseHeader)
|
||||
{
|
||||
if (!context.Response.Headers.ContainsKey(httpResponseHeader.Key))
|
||||
{
|
||||
context.Response.Headers.Add(httpResponseHeader.Key, new StringValues(httpResponseHeader.Values.ToArray()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_removeOutputHeaders = removeOutputHeaders;
|
||||
}
|
||||
|
||||
public async Task SetResponseOnHttpContext(HttpContext context, DownstreamResponse response)
|
||||
{
|
||||
_removeOutputHeaders.Remove(response.Headers);
|
||||
|
||||
foreach (var httpResponseHeader in response.Headers)
|
||||
{
|
||||
AddHeaderIfDoesntExist(context, httpResponseHeader);
|
||||
}
|
||||
|
||||
foreach (var httpResponseHeader in response.Content.Headers)
|
||||
{
|
||||
AddHeaderIfDoesntExist(context, new Header(httpResponseHeader.Key, httpResponseHeader.Value));
|
||||
}
|
||||
|
||||
var content = await response.Content.ReadAsStreamAsync();
|
||||
|
||||
AddHeaderIfDoesntExist(context, new Header("Content-Length", new []{ content.Length.ToString() }) );
|
||||
|
||||
context.Response.OnStarting(state =>
|
||||
{
|
||||
var httpContext = (HttpContext)state;
|
||||
|
||||
httpContext.Response.StatusCode = (int)response.StatusCode;
|
||||
|
||||
return Task.CompletedTask;
|
||||
}, context);
|
||||
|
||||
using(content)
|
||||
{
|
||||
if (response.StatusCode != HttpStatusCode.NotModified && context.Response.ContentLength != 0)
|
||||
{
|
||||
await content.CopyToAsync(context.Response.Body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetErrorResponseOnContext(HttpContext context, int statusCode)
|
||||
{
|
||||
context.Response.StatusCode = statusCode;
|
||||
}
|
||||
|
||||
private static void AddHeaderIfDoesntExist(HttpContext context, Header httpResponseHeader)
|
||||
{
|
||||
if (!context.Response.Headers.ContainsKey(httpResponseHeader.Key))
|
||||
{
|
||||
context.Response.Headers.Add(httpResponseHeader.Key, new StringValues(httpResponseHeader.Values.ToArray()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,55 +1,55 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.Errors;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Infrastructure.Extensions;
|
||||
|
||||
namespace Ocelot.Responder.Middleware
|
||||
{
|
||||
/// <summary>
|
||||
/// Completes and returns the request and request body, if any pipeline errors occured then sets the appropriate HTTP status code instead.
|
||||
/// </summary>
|
||||
public class ResponderMiddleware : OcelotMiddleware
|
||||
{
|
||||
private readonly OcelotRequestDelegate _next;
|
||||
private readonly IHttpResponder _responder;
|
||||
private readonly IErrorsToHttpStatusCodeMapper _codeMapper;
|
||||
|
||||
public ResponderMiddleware(OcelotRequestDelegate next,
|
||||
IHttpResponder responder,
|
||||
IOcelotLoggerFactory loggerFactory,
|
||||
IErrorsToHttpStatusCodeMapper codeMapper
|
||||
)
|
||||
:base(loggerFactory.CreateLogger<ResponderMiddleware>())
|
||||
{
|
||||
_next = next;
|
||||
_responder = responder;
|
||||
_codeMapper = codeMapper;
|
||||
}
|
||||
|
||||
public async Task Invoke(DownstreamContext context)
|
||||
{
|
||||
await _next.Invoke(context);
|
||||
|
||||
if (context.IsError)
|
||||
{
|
||||
Logger.LogWarning($"{context.Errors.ToErrorString()} errors found in {MiddlewareName}. Setting error response for request path:{context.HttpContext.Request.Path}, request method: {context.HttpContext.Request.Method}");
|
||||
|
||||
SetErrorResponse(context.HttpContext, context.Errors);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogDebug("no pipeline errors, setting and returning completed response");
|
||||
await _responder.SetResponseOnHttpContext(context.HttpContext, context.DownstreamResponse);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetErrorResponse(HttpContext context, List<Error> errors)
|
||||
{
|
||||
var statusCode = _codeMapper.Map(errors);
|
||||
_responder.SetErrorResponseOnContext(context, statusCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.Errors;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Infrastructure.Extensions;
|
||||
|
||||
namespace Ocelot.Responder.Middleware
|
||||
{
|
||||
/// <summary>
|
||||
/// Completes and returns the request and request body, if any pipeline errors occured then sets the appropriate HTTP status code instead.
|
||||
/// </summary>
|
||||
public class ResponderMiddleware : OcelotMiddleware
|
||||
{
|
||||
private readonly OcelotRequestDelegate _next;
|
||||
private readonly IHttpResponder _responder;
|
||||
private readonly IErrorsToHttpStatusCodeMapper _codeMapper;
|
||||
|
||||
public ResponderMiddleware(OcelotRequestDelegate next,
|
||||
IHttpResponder responder,
|
||||
IOcelotLoggerFactory loggerFactory,
|
||||
IErrorsToHttpStatusCodeMapper codeMapper
|
||||
)
|
||||
:base(loggerFactory.CreateLogger<ResponderMiddleware>())
|
||||
{
|
||||
_next = next;
|
||||
_responder = responder;
|
||||
_codeMapper = codeMapper;
|
||||
}
|
||||
|
||||
public async Task Invoke(DownstreamContext context)
|
||||
{
|
||||
await _next.Invoke(context);
|
||||
|
||||
if (context.IsError)
|
||||
{
|
||||
Logger.LogWarning($"{context.Errors.ToErrorString()} errors found in {MiddlewareName}. Setting error response for request path:{context.HttpContext.Request.Path}, request method: {context.HttpContext.Request.Method}");
|
||||
|
||||
SetErrorResponse(context.HttpContext, context.Errors);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogDebug("no pipeline errors, setting and returning completed response");
|
||||
await _responder.SetResponseOnHttpContext(context.HttpContext, context.DownstreamResponse);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetErrorResponse(HttpContext context, List<Error> errors)
|
||||
{
|
||||
var statusCode = _codeMapper.Map(errors);
|
||||
_responder.SetErrorResponseOnContext(context, statusCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user