-
Notifications
You must be signed in to change notification settings - Fork 15
Customize handling of http 502 #12
Changes from all commits
c7b71d4
0895f96
f793f47
08295fc
bef40d7
05fcfca
48c70b8
5cb5124
0790a6f
a3978a6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| package org.littleshoot.proxy; | ||
|
|
||
| import io.netty.handler.codec.http.FullHttpResponse; | ||
| import io.netty.handler.codec.http.HttpRequest; | ||
| import io.netty.handler.codec.http.HttpResponseStatus; | ||
| import io.netty.handler.codec.http.HttpVersion; | ||
| import org.littleshoot.proxy.impl.ProxyUtils; | ||
|
|
||
| public class BadGatewayFailureHttpResponseComposer implements FailureHttpResponseComposer { | ||
|
|
||
| /** | ||
| * Tells the client that something went wrong trying to proxy its request. If the Bad Gateway is a response to | ||
| * an HTTP HEAD request, the response will contain no body, but the Content-Length header will be set to the | ||
| * value it would have been if this 502 Bad Gateway were in response to a GET. | ||
| * | ||
| * @param httpRequest the HttpRequest that is resulting in the Bad Gateway response | ||
| * @param cause raised exception | ||
| * @return true if the connection will be kept open, or false if it will be disconnected | ||
| */ | ||
| @Override | ||
| public FullHttpResponse compose(HttpRequest httpRequest, Throwable cause) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we should unit test for this component. |
||
| String body = provideCustomMessage(httpRequest, cause); | ||
|
|
||
| FullHttpResponse response = ProxyUtils.createFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_GATEWAY, body); | ||
|
|
||
| if (ProxyUtils.isHEAD(httpRequest)) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not tested.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| // don't allow any body content in response to a HEAD request | ||
| response.content().clear(); | ||
| } | ||
| return response; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the following part will be common between various implementations FullHttpResponse response = ProxyUtils.createFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_GATEWAY, body);
if (ProxyUtils.isHEAD(httpRequest)) {
// don't allow any body content in response to a HEAD request
response.content().clear();
}
return response;so we should abstract it away There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. diff --git a/src/main/java/org/littleshoot/proxy/BadGatewayFailureHttpResponseComposer.java b/src/main/java/org/littleshoot/proxy/BadGatewayFailureHttpResponseComposer.java
index 26bcd78..b7e8abd 100644
--- a/src/main/java/org/littleshoot/proxy/BadGatewayFailureHttpResponseComposer.java
+++ b/src/main/java/org/littleshoot/proxy/BadGatewayFailureHttpResponseComposer.java
@@ -20,7 +20,7 @@ public final class BadGatewayFailureHttpResponseComposer implements ServerConnec
*/
@Override
public FullHttpResponse compose(HttpRequest httpRequest, Throwable cause) {
- String body = "Bad Gateway: " + httpRequest.getUri();
+ String body = provideCustomMessage(httpRequest, cause);
FullHttpResponse response = ProxyUtils.createFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_GATEWAY, body);
@@ -31,6 +31,10 @@ public final class BadGatewayFailureHttpResponseComposer implements ServerConnec
return response;
}
+ protected String provideCustomMessage(HttpRequest httpRequest, Throwable cause) {
+ return "Bad Gateway: " + httpRequest.getUri();
+ }
+
public FullHttpResponse compose(HttpRequest httpRequest) {
return this.compose(httpRequest, null);
}There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this way you custom implementation would be a specialization of the default impl provided by BadGatewayFailureHttpResponseComposer. |
||
| } | ||
|
|
||
| /** | ||
| * The method can be overridden to provide a custom message along with 502 code | ||
| * @param httpRequest initial request | ||
| * @param cause an exception thrown on a failure | ||
| * @return custom message | ||
| */ | ||
| protected String provideCustomMessage(HttpRequest httpRequest, Throwable cause) { | ||
| return "Bad Gateway: " + httpRequest.getUri(); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| package org.littleshoot.proxy; | ||
|
|
||
| import io.netty.handler.codec.http.FullHttpResponse; | ||
| import io.netty.handler.codec.http.HttpRequest; | ||
|
|
||
| /** | ||
| * Interface for objects that can provide a custom http response on a specific failure. | ||
| */ | ||
| public interface FailureHttpResponseComposer { | ||
|
|
||
| /** | ||
| * Creates an {@link FullHttpResponse} based on initial request and failure cause | ||
| * @param httpRequest initial request | ||
| * @param cause an exception thrown during a failure | ||
| * @return failure http response | ||
| */ | ||
| FullHttpResponse compose(HttpRequest httpRequest, Throwable cause); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| package org.littleshoot.proxy; | ||
|
|
||
| import io.netty.handler.codec.http.FullHttpResponse; | ||
| import io.netty.handler.codec.http.HttpMethod; | ||
| import io.netty.handler.codec.http.HttpRequest; | ||
| import org.junit.Test; | ||
|
|
||
| import java.io.IOException; | ||
|
|
||
| import static org.junit.Assert.*; | ||
| import static org.mockito.Mockito.*; | ||
|
|
||
| public class BadGatewayFailureHttpResponseComposerTest { | ||
|
|
||
| private static final String REQUEST_URI = "https://localhost/hi"; | ||
|
|
||
| @Test | ||
| public void testDefault() throws IOException { | ||
| FailureHttpResponseComposer badGatewayResponseComposer = new BadGatewayFailureHttpResponseComposer(); | ||
|
|
||
| HttpRequest initialRequest = mock(HttpRequest.class); | ||
| when(initialRequest.getUri()).thenReturn(REQUEST_URI); | ||
|
|
||
| FullHttpResponse response = badGatewayResponseComposer.compose(initialRequest, new RuntimeException()); | ||
|
|
||
| assertEquals(502, response.getStatus().code()); | ||
| assertEquals("Bad Gateway", response.getStatus().reasonPhrase()); | ||
| assertEquals("Bad Gateway: " + REQUEST_URI, new String(response.content().array())); | ||
| } | ||
|
|
||
| @Test | ||
| public void testCustomMessage() throws IOException { | ||
| FailureHttpResponseComposer badGatewayResponseComposer = new BadGatewayFailureHttpResponseComposer() { | ||
| @Override | ||
| protected String provideCustomMessage(HttpRequest httpRequest, Throwable cause) { | ||
| return "Invalid certificate: " + httpRequest.getUri(); | ||
| } | ||
| }; | ||
|
|
||
| HttpRequest initialRequest = mock(HttpRequest.class); | ||
| when(initialRequest.getUri()).thenReturn(REQUEST_URI); | ||
|
|
||
| FullHttpResponse response = badGatewayResponseComposer.compose(initialRequest, new RuntimeException()); | ||
|
|
||
| assertEquals(502, response.getStatus().code()); | ||
| assertEquals("Bad Gateway", response.getStatus().reasonPhrase()); | ||
| assertEquals("Invalid certificate: " + REQUEST_URI, new String(response.content().array())); | ||
| } | ||
|
|
||
| @Test | ||
| public void testClearedContent() throws IOException { | ||
| FailureHttpResponseComposer badGatewayResponseComposer = new BadGatewayFailureHttpResponseComposer(); | ||
|
|
||
| HttpRequest initialRequest = mock(HttpRequest.class); | ||
| when(initialRequest.getUri()).thenReturn(REQUEST_URI); | ||
|
|
||
| FullHttpResponse response = badGatewayResponseComposer.compose(initialRequest, new RuntimeException()); | ||
|
|
||
| assertEquals(502, response.getStatus().code()); | ||
|
|
||
| assertEquals(0, response.content().readerIndex()); | ||
| assertNotEquals(0, response.content().writerIndex()); | ||
|
|
||
| when(initialRequest.getMethod()).thenReturn(HttpMethod.HEAD); | ||
|
|
||
| response = badGatewayResponseComposer.compose(initialRequest, new RuntimeException()); | ||
|
|
||
| assertEquals(502, response.getStatus().code()); | ||
|
|
||
| assertEquals(0, response.content().readerIndex()); | ||
| assertEquals(0, response.content().writerIndex()); | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| package org.littleshoot.proxy.impl; | ||
|
|
||
| import io.netty.handler.codec.http.FullHttpResponse; | ||
| import io.netty.handler.codec.http.HttpRequest; | ||
| import org.junit.Test; | ||
| import org.littleshoot.proxy.BadGatewayFailureHttpResponseComposer; | ||
| import org.littleshoot.proxy.FailureHttpResponseComposer; | ||
|
|
||
| import static org.junit.Assert.assertTrue; | ||
|
|
||
| public class DefaultHttpProxyServerTest { | ||
|
|
||
| @Test | ||
| public void testDefaultUnrecoverableFailureHttpResponseComposer() { | ||
| DefaultHttpProxyServer httpProxyServer = (DefaultHttpProxyServer) DefaultHttpProxyServer.bootstrap().start(); | ||
| assertTrue(httpProxyServer.getUnrecoverableFailureHttpResponseComposer() instanceof BadGatewayFailureHttpResponseComposer); | ||
| httpProxyServer.stop(); | ||
| } | ||
|
|
||
| @Test | ||
| public void testCustomUnrecoverableFailureHttpResponseComposer() { | ||
|
|
||
| class CustomUnrecoverableFailureHttpResponseComposer implements FailureHttpResponseComposer { | ||
| @Override | ||
| public FullHttpResponse compose(HttpRequest httpRequest, Throwable cause) { | ||
| return null; | ||
| } | ||
| } | ||
|
|
||
| DefaultHttpProxyServer httpProxyServer = (DefaultHttpProxyServer) DefaultHttpProxyServer | ||
| .bootstrap() | ||
| .withUnrecoverableFailureHttpResponseComposer(new CustomUnrecoverableFailureHttpResponseComposer()) | ||
| .start(); | ||
| assertTrue(httpProxyServer.getUnrecoverableFailureHttpResponseComposer() instanceof CustomUnrecoverableFailureHttpResponseComposer); | ||
| httpProxyServer.stop(); | ||
| } | ||
|
|
||
| } |

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code in this class is copied from
writeBadRequestmethod ofClientToProxyConnectionclass