当 RequestHandler 抛出异常时 Jetty 重试请求
Jetty retrying requests when Exception thrown by RequestHandler
我注意到 Jetty
每当 handle
抛出异常时都会重试对指定 Handler
的调用。
这是不受欢迎的行为,但我似乎无法找到阻止它的配置设置。
我使用 Jetty 有一段时间了,但最近升级了我的环境以使用 9.4.9.v20180320
。这可能是也可能不是新行为,但我从未注意到它。
下面是一个简单的用例来说明我的问题。请注意输出中有两行指示正文内容,但是在第二次尝试时令人困惑的是正文是空的。
public class JettyTest extends AbstractHandler
{
@Override
public void handle(String arg0, Request arg1, HttpServletRequest arg2, HttpServletResponse arg3)
throws IOException
{
//read request body into string
ByteArrayOutputStream bos = new ByteArrayOutputStream();
//apache commons copy
IOUtils.copy(arg1.getInputStream(), bos);
String body = new String(bos.toByteArray());
System.out.println("request body: " + body);
throw new NullPointerException();
}
public static void main(String args[])
throws Exception
{
Server server = new Server();
server.setHandler(new JettyTest());
LocalConnector localConnector = new LocalConnector(server);
server.addConnector(localConnector);
server.start();
String simpleRequest = "GET / http/1.1\r\nHost: localhost:0\r\nContent-Type: text/plain\r\nContent-Length: 2\r\n\r\nhi";
String response = localConnector.getResponse(simpleRequest);
server.join();
}
}
输出
2018-03-26 19:47:49.590:INFO:oejs.Server:main: Started @327ms
request body: hi
2018-03-26 19:47:49.660:WARN:oejs.HttpChannel:qtp1349277854-12: /
java.lang.NullPointerException
at test.JettyTest.handle(JettyTest.java:28)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)
at org.eclipse.jetty.server.Server.handle(Server.java:531)
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:352)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:260)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:281)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:102)
at org.eclipse.jetty.io.ByteArrayEndPoint.run(ByteArrayEndPoint.java:78)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:754)
at org.eclipse.jetty.util.thread.QueuedThreadPool.run(QueuedThreadPool.java:672)
at java.lang.Thread.run(Thread.java:748)
request body:
你有很多事情要做。
- 您没有处理请求(您的代码库中没有任何内容正在使用
Request.setHandled(true)
)
- 您没有错误调度处理。
- 您没有设置 ErrorHandler。
所以发生的事情是...
- 你的JettyTest.handle()被调用,异常流出。
- 请求现在处于 ERROR Dispatch 模式。
- ERROR 调度请求现在再次发送到处理程序以进行 ERROR 处理。
如果您只是对此进行更改...
package jetty.errors;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import javax.servlet.DispatcherType;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.LocalConnector;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.util.IO;
public class HandlerWithError extends AbstractHandler
{
@Override
public void handle(String s, Request request, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException
{
if (DispatcherType.REQUEST.equals(httpServletRequest.getDispatcherType()))
{
// read request body into string
ByteArrayOutputStream bos = new ByteArrayOutputStream();
// apache commons copy
IO.copy(httpServletRequest.getInputStream(), bos);
String body = new String(bos.toByteArray());
System.out.println("request body: " + body);
throw new NullPointerException();
}
else
{
System.out.println("Now in DispatchType: " + httpServletRequest.getDispatcherType());
}
}
public static void main(String args[])
throws Exception
{
Server server = new Server();
server.setHandler(new HandlerWithError());
LocalConnector localConnector = new LocalConnector(server);
server.addConnector(localConnector);
server.start();
String simpleRequest = "GET / http/1.1\r\n" +
"Host: localhost:0\r\n" +
"Connection: close\r\n" +
"Content-Type: text/plain\r\n" +
"Content-Length: 2\r\n\r\nhi";
String response = localConnector.getResponse(simpleRequest);
server.join();
}
}
你会得到结果...
2018-03-27 06:47:40.074:INFO::main: Logging initialized @429ms to org.eclipse.jetty.util.log.StdErrLog
2018-03-27 06:47:40.148:INFO:oejs.Server:main: jetty-9.4.9.v20180320; built: 2018-03-20T07:21:10-05:00; git: 1f8159b1e4a42d3f79997021ea1609f2fbac6de5; jvm 9.0.4+11
2018-03-27 06:47:40.182:INFO:oejs.AbstractConnector:main: Started LocalConnector@2f490758{HTTP/1.1,[http/1.1]}
2018-03-27 06:47:40.183:INFO:oejs.Server:main: Started @548ms
2018-03-27 06:47:40.287:WARN:oejs.HttpChannel:qtp2104545713-16: /
java.lang.NullPointerException
at jetty.errors.HandlerWithError.handle(HandlerWithError.java:29)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)
at org.eclipse.jetty.server.Server.handle(Server.java:531)
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:352)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:260)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:281)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:102)
at org.eclipse.jetty.io.ByteArrayEndPoint.run(ByteArrayEndPoint.java:78)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:754)
at org.eclipse.jetty.util.thread.QueuedThreadPool.run(QueuedThreadPool.java:672)
at java.base/java.lang.Thread.run(Thread.java:844)
request body: hi
Now in DispatchType: ERROR
了解所有这些后,您可以更改您的实现以通过以下方式利用此 ERROR 调度。
package jetty.errors;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import javax.servlet.DispatcherType;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpTester;
import org.eclipse.jetty.server.LocalConnector;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.util.IO;
public class HandlerWithError
{
public static class MyRequestHandler extends AbstractHandler
{
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
{
System.out.println("MyRequestHandler.handle() - DispatcherType: " + request.getDispatcherType());
// only work with REQUEST Dispatches
if (!DispatcherType.REQUEST.equals(request.getDispatcherType()))
{
// skip this handler
return;
}
// Set handled (by this handler), don't let other handlers operate on this request
baseRequest.setHandled(true);
// read request body into string
ByteArrayOutputStream bos = new ByteArrayOutputStream();
// apache commons copy
IO.copy(request.getInputStream(), bos);
String body = new String(bos.toByteArray());
System.out.println("request body: " + body);
throw new NullPointerException();
}
}
public static class MyErrorHandler extends AbstractHandler
{
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
{
System.out.println("MyErrorHandler.handle() - DispatcherType: " + request.getDispatcherType());
if(!DispatcherType.ERROR.equals(request.getDispatcherType()))
{
// skip this handler
return;
}
baseRequest.setHandled(true);
response.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
response.setContentType("text/plain");
response.getWriter().println("Go away, you silly NPE fool");
}
}
public static void main(String args[])
throws Exception
{
Server server = new Server();
HandlerList handlers = new HandlerList();
// Add your ERROR Dispatch handler first to handle dispatches for errors
handlers.addHandler(new MyErrorHandler());
// Add your handlers here (can be more then one)
handlers.addHandler(new MyRequestHandler());
// Always add DefaultHandler last, to ensure that something in your handler
// list is calling baseRequest.setHandled(true)
handlers.addHandler(new DefaultHandler());
server.setHandler(handlers);
LocalConnector localConnector = new LocalConnector(server);
server.addConnector(localConnector);
server.start();
try
{
String simpleRequest = "GET / http/1.1\r\n" +
"Host: localhost:0\r\n" +
"Connection: close\r\n" +
"Content-Type: text/plain\r\n" +
"Content-Length: 2\r\n\r\nhi";
HttpTester.Response response = HttpTester.parseResponse(
localConnector.getResponse(simpleRequest));
System.out.println("Response: " + response);
System.out.println(response.getContent());
}
catch (Throwable t)
{
t.printStackTrace();
}
finally
{
server.stop();
}
}
}
输出看起来像这样...
2018-03-27 07:03:25.220:INFO::main: Logging initialized @320ms to org.eclipse.jetty.util.log.StdErrLog
2018-03-27 07:03:25.288:INFO:oejs.Server:main: jetty-9.4.9.v20180320; built: 2018-03-20T07:21:10-05:00; git: 1f8159b1e4a42d3f79997021ea1609f2fbac6de5; jvm 9.0.4+11
2018-03-27 07:03:25.309:INFO:oejs.AbstractConnector:main: Started LocalConnector@4ba2ca36{HTTP/1.1,[http/1.1]}
2018-03-27 07:03:25.310:INFO:oejs.Server:main: Started @417ms
MyErrorHandler.handle() - DispatcherType: REQUEST
MyRequestHandler.handle() - DispatcherType: REQUEST
request body: hi
2018-03-27 07:03:25.399:WARN:oejs.HttpChannel:qtp525571-15: /
java.lang.NullPointerException
at jetty.errors.HandlerWithError$MyRequestHandler.handle(HandlerWithError.java:41)
at org.eclipse.jetty.server.handler.HandlerList.handle(HandlerList.java:61)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)
at org.eclipse.jetty.server.Server.handle(Server.java:531)
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:352)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:260)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:281)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:102)
at org.eclipse.jetty.io.ByteArrayEndPoint.run(ByteArrayEndPoint.java:78)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:754)
at org.eclipse.jetty.util.thread.QueuedThreadPool.run(QueuedThreadPool.java:672)
at java.base/java.lang.Thread.run(Thread.java:844)
MyErrorHandler.handle() - DispatcherType: ERROR
Response: HTTP/1.1 503 Service Unavailable
Connection: close
Content-Type: text/plain;charset=ISO-8859-1
Content-Length: 29
Server: Jetty(9.4.9.v20180320)
Go away, you silly NPE fool
2018-03-27 07:03:25.423:INFO:oejs.AbstractConnector:main: Stopped LocalConnector@4ba2ca36{HTTP/1.1,[http/1.1]}
我注意到 Jetty
每当 handle
抛出异常时都会重试对指定 Handler
的调用。
这是不受欢迎的行为,但我似乎无法找到阻止它的配置设置。
我使用 Jetty 有一段时间了,但最近升级了我的环境以使用 9.4.9.v20180320
。这可能是也可能不是新行为,但我从未注意到它。
下面是一个简单的用例来说明我的问题。请注意输出中有两行指示正文内容,但是在第二次尝试时令人困惑的是正文是空的。
public class JettyTest extends AbstractHandler
{
@Override
public void handle(String arg0, Request arg1, HttpServletRequest arg2, HttpServletResponse arg3)
throws IOException
{
//read request body into string
ByteArrayOutputStream bos = new ByteArrayOutputStream();
//apache commons copy
IOUtils.copy(arg1.getInputStream(), bos);
String body = new String(bos.toByteArray());
System.out.println("request body: " + body);
throw new NullPointerException();
}
public static void main(String args[])
throws Exception
{
Server server = new Server();
server.setHandler(new JettyTest());
LocalConnector localConnector = new LocalConnector(server);
server.addConnector(localConnector);
server.start();
String simpleRequest = "GET / http/1.1\r\nHost: localhost:0\r\nContent-Type: text/plain\r\nContent-Length: 2\r\n\r\nhi";
String response = localConnector.getResponse(simpleRequest);
server.join();
}
}
输出
2018-03-26 19:47:49.590:INFO:oejs.Server:main: Started @327ms
request body: hi
2018-03-26 19:47:49.660:WARN:oejs.HttpChannel:qtp1349277854-12: /
java.lang.NullPointerException
at test.JettyTest.handle(JettyTest.java:28)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)
at org.eclipse.jetty.server.Server.handle(Server.java:531)
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:352)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:260)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:281)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:102)
at org.eclipse.jetty.io.ByteArrayEndPoint.run(ByteArrayEndPoint.java:78)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:754)
at org.eclipse.jetty.util.thread.QueuedThreadPool.run(QueuedThreadPool.java:672)
at java.lang.Thread.run(Thread.java:748)
request body:
你有很多事情要做。
- 您没有处理请求(您的代码库中没有任何内容正在使用
Request.setHandled(true)
) - 您没有错误调度处理。
- 您没有设置 ErrorHandler。
所以发生的事情是...
- 你的JettyTest.handle()被调用,异常流出。
- 请求现在处于 ERROR Dispatch 模式。
- ERROR 调度请求现在再次发送到处理程序以进行 ERROR 处理。
如果您只是对此进行更改...
package jetty.errors;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import javax.servlet.DispatcherType;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.LocalConnector;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.util.IO;
public class HandlerWithError extends AbstractHandler
{
@Override
public void handle(String s, Request request, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException
{
if (DispatcherType.REQUEST.equals(httpServletRequest.getDispatcherType()))
{
// read request body into string
ByteArrayOutputStream bos = new ByteArrayOutputStream();
// apache commons copy
IO.copy(httpServletRequest.getInputStream(), bos);
String body = new String(bos.toByteArray());
System.out.println("request body: " + body);
throw new NullPointerException();
}
else
{
System.out.println("Now in DispatchType: " + httpServletRequest.getDispatcherType());
}
}
public static void main(String args[])
throws Exception
{
Server server = new Server();
server.setHandler(new HandlerWithError());
LocalConnector localConnector = new LocalConnector(server);
server.addConnector(localConnector);
server.start();
String simpleRequest = "GET / http/1.1\r\n" +
"Host: localhost:0\r\n" +
"Connection: close\r\n" +
"Content-Type: text/plain\r\n" +
"Content-Length: 2\r\n\r\nhi";
String response = localConnector.getResponse(simpleRequest);
server.join();
}
}
你会得到结果...
2018-03-27 06:47:40.074:INFO::main: Logging initialized @429ms to org.eclipse.jetty.util.log.StdErrLog
2018-03-27 06:47:40.148:INFO:oejs.Server:main: jetty-9.4.9.v20180320; built: 2018-03-20T07:21:10-05:00; git: 1f8159b1e4a42d3f79997021ea1609f2fbac6de5; jvm 9.0.4+11
2018-03-27 06:47:40.182:INFO:oejs.AbstractConnector:main: Started LocalConnector@2f490758{HTTP/1.1,[http/1.1]}
2018-03-27 06:47:40.183:INFO:oejs.Server:main: Started @548ms
2018-03-27 06:47:40.287:WARN:oejs.HttpChannel:qtp2104545713-16: /
java.lang.NullPointerException
at jetty.errors.HandlerWithError.handle(HandlerWithError.java:29)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)
at org.eclipse.jetty.server.Server.handle(Server.java:531)
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:352)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:260)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:281)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:102)
at org.eclipse.jetty.io.ByteArrayEndPoint.run(ByteArrayEndPoint.java:78)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:754)
at org.eclipse.jetty.util.thread.QueuedThreadPool.run(QueuedThreadPool.java:672)
at java.base/java.lang.Thread.run(Thread.java:844)
request body: hi
Now in DispatchType: ERROR
了解所有这些后,您可以更改您的实现以通过以下方式利用此 ERROR 调度。
package jetty.errors;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import javax.servlet.DispatcherType;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpTester;
import org.eclipse.jetty.server.LocalConnector;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.util.IO;
public class HandlerWithError
{
public static class MyRequestHandler extends AbstractHandler
{
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
{
System.out.println("MyRequestHandler.handle() - DispatcherType: " + request.getDispatcherType());
// only work with REQUEST Dispatches
if (!DispatcherType.REQUEST.equals(request.getDispatcherType()))
{
// skip this handler
return;
}
// Set handled (by this handler), don't let other handlers operate on this request
baseRequest.setHandled(true);
// read request body into string
ByteArrayOutputStream bos = new ByteArrayOutputStream();
// apache commons copy
IO.copy(request.getInputStream(), bos);
String body = new String(bos.toByteArray());
System.out.println("request body: " + body);
throw new NullPointerException();
}
}
public static class MyErrorHandler extends AbstractHandler
{
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
{
System.out.println("MyErrorHandler.handle() - DispatcherType: " + request.getDispatcherType());
if(!DispatcherType.ERROR.equals(request.getDispatcherType()))
{
// skip this handler
return;
}
baseRequest.setHandled(true);
response.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
response.setContentType("text/plain");
response.getWriter().println("Go away, you silly NPE fool");
}
}
public static void main(String args[])
throws Exception
{
Server server = new Server();
HandlerList handlers = new HandlerList();
// Add your ERROR Dispatch handler first to handle dispatches for errors
handlers.addHandler(new MyErrorHandler());
// Add your handlers here (can be more then one)
handlers.addHandler(new MyRequestHandler());
// Always add DefaultHandler last, to ensure that something in your handler
// list is calling baseRequest.setHandled(true)
handlers.addHandler(new DefaultHandler());
server.setHandler(handlers);
LocalConnector localConnector = new LocalConnector(server);
server.addConnector(localConnector);
server.start();
try
{
String simpleRequest = "GET / http/1.1\r\n" +
"Host: localhost:0\r\n" +
"Connection: close\r\n" +
"Content-Type: text/plain\r\n" +
"Content-Length: 2\r\n\r\nhi";
HttpTester.Response response = HttpTester.parseResponse(
localConnector.getResponse(simpleRequest));
System.out.println("Response: " + response);
System.out.println(response.getContent());
}
catch (Throwable t)
{
t.printStackTrace();
}
finally
{
server.stop();
}
}
}
输出看起来像这样...
2018-03-27 07:03:25.220:INFO::main: Logging initialized @320ms to org.eclipse.jetty.util.log.StdErrLog
2018-03-27 07:03:25.288:INFO:oejs.Server:main: jetty-9.4.9.v20180320; built: 2018-03-20T07:21:10-05:00; git: 1f8159b1e4a42d3f79997021ea1609f2fbac6de5; jvm 9.0.4+11
2018-03-27 07:03:25.309:INFO:oejs.AbstractConnector:main: Started LocalConnector@4ba2ca36{HTTP/1.1,[http/1.1]}
2018-03-27 07:03:25.310:INFO:oejs.Server:main: Started @417ms
MyErrorHandler.handle() - DispatcherType: REQUEST
MyRequestHandler.handle() - DispatcherType: REQUEST
request body: hi
2018-03-27 07:03:25.399:WARN:oejs.HttpChannel:qtp525571-15: /
java.lang.NullPointerException
at jetty.errors.HandlerWithError$MyRequestHandler.handle(HandlerWithError.java:41)
at org.eclipse.jetty.server.handler.HandlerList.handle(HandlerList.java:61)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)
at org.eclipse.jetty.server.Server.handle(Server.java:531)
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:352)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:260)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:281)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:102)
at org.eclipse.jetty.io.ByteArrayEndPoint.run(ByteArrayEndPoint.java:78)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:754)
at org.eclipse.jetty.util.thread.QueuedThreadPool.run(QueuedThreadPool.java:672)
at java.base/java.lang.Thread.run(Thread.java:844)
MyErrorHandler.handle() - DispatcherType: ERROR
Response: HTTP/1.1 503 Service Unavailable
Connection: close
Content-Type: text/plain;charset=ISO-8859-1
Content-Length: 29
Server: Jetty(9.4.9.v20180320)
Go away, you silly NPE fool
2018-03-27 07:03:25.423:INFO:oejs.AbstractConnector:main: Stopped LocalConnector@4ba2ca36{HTTP/1.1,[http/1.1]}