Skip to content

Commit

Permalink
use a stream rather than byte array in responder (ThreeMammals#519)
Browse files Browse the repository at this point in the history
  • Loading branch information
TomPallister authored Jul 31, 2018
1 parent eb4b996 commit b854ca6
Show file tree
Hide file tree
Showing 4 changed files with 334 additions and 335 deletions.
19 changes: 9 additions & 10 deletions src/Ocelot/Errors/Middleware/ExceptionHandlerMiddleware.cs
Original file line number Diff line number Diff line change
@@ -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>
Expand Down
146 changes: 73 additions & 73 deletions src/Ocelot/Responder/HttpContextResponder.cs
Original file line number Diff line number Diff line change
@@ -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()));
}
}
}
}
110 changes: 55 additions & 55 deletions src/Ocelot/Responder/Middleware/ResponderMiddleware.cs
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
Loading

0 comments on commit b854ca6

Please sign in to comment.