diff --git a/docs/features/errorcodes.rst b/docs/features/errorcodes.rst
new file mode 100644
index 00000000..fe058790
--- /dev/null
+++ b/docs/features/errorcodes.rst
@@ -0,0 +1,13 @@
+Http Error Status Codes
+=======================
+
+Ocelot will return HTTP status error codes based on internal logic in certain siturations:
+- 401 if the authentication middleware runs and the user is not authenticated.
+- 403 if the authorisation middleware runs and the user is unauthenticated, claim value not authroised, scope not authorised, user doesnt have required claim or cannot find claim.
+- 503 if the downstream request times out.
+- 499 if the request is cancelled by the client.
+- 404 if unable to find a downstream route.
+- 502 if unable to connect to downstream service.
+- 500 if unable to complete the HTTP request downstream and the exception is not OperationCanceledException or HttpRequestException.
+- 404 if Ocelot is unable to map an internal error code to a HTTP status code.
+
diff --git a/docs/index.rst b/docs/index.rst
index 7952c91a..1acbd712 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -42,6 +42,7 @@ Thanks for taking a look at the Ocelot documentation. Please use the left hand n
features/loadbalancer
features/delegatinghandlers
features/raft
+ features/errorcodes
.. toctree::
:maxdepth: 2
diff --git a/src/Ocelot/Errors/OcelotErrorCode.cs b/src/Ocelot/Errors/OcelotErrorCode.cs
index d8b6f333..6ca67e7d 100644
--- a/src/Ocelot/Errors/OcelotErrorCode.cs
+++ b/src/Ocelot/Errors/OcelotErrorCode.cs
@@ -39,6 +39,7 @@
CannotAddPlaceholderError = 34,
CannotRemovePlaceholderError = 35,
QuotaExceededError = 36,
- RequestCanceled = 37,
+ RequestCanceled = 37,
+ ConnectionToDownstreamServiceError = 38,
}
}
diff --git a/src/Ocelot/Requester/ConnectionToDownstreamServiceError.cs b/src/Ocelot/Requester/ConnectionToDownstreamServiceError.cs
new file mode 100644
index 00000000..6c03863c
--- /dev/null
+++ b/src/Ocelot/Requester/ConnectionToDownstreamServiceError.cs
@@ -0,0 +1,13 @@
+using Ocelot.Errors;
+using System;
+
+namespace Ocelot.Requester
+{
+ public class ConnectionToDownstreamServiceError : Error
+ {
+ public ConnectionToDownstreamServiceError(Exception exception)
+ : base($"Error connecting to downstream service, exception: {exception}", OcelotErrorCode.ConnectionToDownstreamServiceError)
+ {
+ }
+ }
+}
diff --git a/src/Ocelot/Requester/HttpExeptionToErrorMapper.cs b/src/Ocelot/Requester/HttpExeptionToErrorMapper.cs
index f2fbad2d..a86fcf37 100644
--- a/src/Ocelot/Requester/HttpExeptionToErrorMapper.cs
+++ b/src/Ocelot/Requester/HttpExeptionToErrorMapper.cs
@@ -4,6 +4,7 @@ namespace Ocelot.Requester
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
+ using System.Net.Http;
public class HttpExeptionToErrorMapper : IExceptionToErrorMapper
{
@@ -28,6 +29,11 @@ namespace Ocelot.Requester
return new RequestCanceledError(exception.Message);
}
+ if (type == typeof(HttpRequestException))
+ {
+ return new ConnectionToDownstreamServiceError(exception);
+ }
+
return new UnableToCompleteRequestError(exception);
}
}
diff --git a/src/Ocelot/Responder/ErrorsToHttpStatusCodeMapper.cs b/src/Ocelot/Responder/ErrorsToHttpStatusCodeMapper.cs
index c50daa27..6b0ee6cc 100644
--- a/src/Ocelot/Responder/ErrorsToHttpStatusCodeMapper.cs
+++ b/src/Ocelot/Responder/ErrorsToHttpStatusCodeMapper.cs
@@ -38,6 +38,11 @@ namespace Ocelot.Responder
if (errors.Any(e => e.Code == OcelotErrorCode.UnableToFindDownstreamRouteError))
{
return 404;
+ }
+
+ if (errors.Any(e => e.Code == OcelotErrorCode.ConnectionToDownstreamServiceError))
+ {
+ return 502;
}
if (errors.Any(e => e.Code == OcelotErrorCode.UnableToCompleteRequestError))
diff --git a/src/Ocelot/Responder/IErrorsToHttpStatusCodeMapper.cs b/src/Ocelot/Responder/IErrorsToHttpStatusCodeMapper.cs
index 26da864f..be1b2f66 100644
--- a/src/Ocelot/Responder/IErrorsToHttpStatusCodeMapper.cs
+++ b/src/Ocelot/Responder/IErrorsToHttpStatusCodeMapper.cs
@@ -1,8 +1,9 @@
-using Ocelot.Errors;
-using System.Collections.Generic;
+namespace Ocelot.Responder
+{
+ using System.Net;
+ using Ocelot.Errors;
+ using System.Collections.Generic;
-namespace Ocelot.Responder
-{
///
/// Map a list OceoltErrors to a single appropriate HTTP status code
///
diff --git a/test/Ocelot.AcceptanceTests/HttpTests.cs b/test/Ocelot.AcceptanceTests/HttpTests.cs
index 57947d07..da59d000 100644
--- a/test/Ocelot.AcceptanceTests/HttpTests.cs
+++ b/test/Ocelot.AcceptanceTests/HttpTests.cs
@@ -141,7 +141,7 @@ namespace Ocelot.AcceptanceTests
}
[Fact]
- public void should_return_response_500_when_using_http_one_to_talk_to_server_running_http_two()
+ public void should_return_response_502_when_using_http_one_to_talk_to_server_running_http_two()
{
var port = RandomPortFinder.GetRandomPort();
@@ -177,7 +177,7 @@ namespace Ocelot.AcceptanceTests
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/", httpContent))
- .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.InternalServerError))
+ .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.BadGateway))
.BDDfy();
}
diff --git a/test/Ocelot.AcceptanceTests/ReturnsErrorTests.cs b/test/Ocelot.AcceptanceTests/ReturnsErrorTests.cs
index 1ebc1113..323c9e14 100644
--- a/test/Ocelot.AcceptanceTests/ReturnsErrorTests.cs
+++ b/test/Ocelot.AcceptanceTests/ReturnsErrorTests.cs
@@ -16,6 +16,38 @@
{
_serviceHandler = new ServiceHandler();
_steps = new Steps();
+ }
+
+ [Fact]
+ public void should_return_bad_gateway_error_if_downstream_service_doesnt_respond()
+ {
+ var configuration = new FileConfiguration
+ {
+ ReRoutes = new List
+ {
+ new FileReRoute
+ {
+ DownstreamPathTemplate = "/",
+ UpstreamPathTemplate = "/",
+ UpstreamHttpMethod = new List { "Get" },
+ DownstreamHostAndPorts = new List
+ {
+ new FileHostAndPort
+ {
+ Host = "localhost",
+ Port = 53877,
+ },
+ },
+ DownstreamScheme = "http",
+ },
+ },
+ };
+
+ this.Given(x => _steps.GivenThereIsAConfiguration(configuration))
+ .And(x => _steps.GivenOcelotIsRunning())
+ .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
+ .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.BadGateway))
+ .BDDfy();
}
[Fact]
diff --git a/test/Ocelot.AcceptanceTests/SslTests.cs b/test/Ocelot.AcceptanceTests/SslTests.cs
index 58c9e6a1..ee1f8141 100644
--- a/test/Ocelot.AcceptanceTests/SslTests.cs
+++ b/test/Ocelot.AcceptanceTests/SslTests.cs
@@ -89,7 +89,7 @@ namespace Ocelot.AcceptanceTests
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
- .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.InternalServerError))
+ .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.BadGateway))
.BDDfy();
}
diff --git a/test/Ocelot.UnitTests/Requester/HttpExeptionToErrorMapperTests.cs b/test/Ocelot.UnitTests/Requester/HttpExeptionToErrorMapperTests.cs
index 14413aad..a05ccec0 100644
--- a/test/Ocelot.UnitTests/Requester/HttpExeptionToErrorMapperTests.cs
+++ b/test/Ocelot.UnitTests/Requester/HttpExeptionToErrorMapperTests.cs
@@ -7,6 +7,7 @@
using Shouldly;
using System;
using System.Collections.Generic;
+ using System.Net.Http;
using System.Threading.Tasks;
using Xunit;
@@ -38,6 +39,14 @@
error.ShouldBeOfType();
}
+ [Fact]
+ public void should_return_ConnectionToDownstreamServiceError()
+ {
+ var error = _mapper.Map(new HttpRequestException());
+
+ error.ShouldBeOfType();
+ }
+
[Fact]
public void should_return_request_canceled_for_subtype()
{
diff --git a/test/Ocelot.UnitTests/Responder/ErrorsToHttpStatusCodeMapperTests.cs b/test/Ocelot.UnitTests/Responder/ErrorsToHttpStatusCodeMapperTests.cs
index 8b550a05..d662f86f 100644
--- a/test/Ocelot.UnitTests/Responder/ErrorsToHttpStatusCodeMapperTests.cs
+++ b/test/Ocelot.UnitTests/Responder/ErrorsToHttpStatusCodeMapperTests.cs
@@ -50,6 +50,13 @@ namespace Ocelot.UnitTests.Responder
public void should_return_internal_server_error(OcelotErrorCode errorCode)
{
ShouldMapErrorToStatusCode(errorCode, HttpStatusCode.InternalServerError);
+ }
+
+ [Theory]
+ [InlineData(OcelotErrorCode.ConnectionToDownstreamServiceError)]
+ public void should_return_bad_gateway_error(OcelotErrorCode errorCode)
+ {
+ ShouldMapErrorToStatusCode(errorCode, HttpStatusCode.BadGateway);
}
[Theory]
@@ -125,7 +132,7 @@ namespace Ocelot.UnitTests.Responder
// If this test fails then it's because the number of error codes has changed.
// You should make the appropriate changes to the test cases here to ensure
// they cover all the error codes, and then modify this assertion.
- Enum.GetNames(typeof(OcelotErrorCode)).Length.ShouldBe(38, "Looks like the number of error codes has changed. Do you need to modify ErrorsToHttpStatusCodeMapper?");
+ Enum.GetNames(typeof(OcelotErrorCode)).Length.ShouldBe(39, "Looks like the number of error codes has changed. Do you need to modify ErrorsToHttpStatusCodeMapper?");
}
private void ShouldMapErrorToStatusCode(OcelotErrorCode errorCode, HttpStatusCode expectedHttpStatusCode)