RequestHandler抛出Exception时Jetty重试请求

时间:2018-03-26 23:50:59

标签: java servlets jetty embedded-jetty

每当Jetty抛出异常时,我都会注意Handler正在重试对指定handle的调用。

这是不受欢迎的行为,但我似乎找不到配置设置来阻止它。

我一直在使用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$1.run(ByteArrayEndPoint.java:78)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:754)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:672)
    at java.lang.Thread.run(Thread.java:748)

request body: 

1 个答案:

答案 0 :(得分:1)

你有很多事情要发生。

  1. 您没有处理请求(代码库中没有任何内容使用Request.setHandled(true)
  2. 您没有ERROR派遣处理。
  3. 您没有设置ErrorHandler。
  4. 所以会发生什么......

    1. 调用JettyTest.handle(),异常流出。
    2. 请求现在处于ERROR Dispatch模式。
    3. 现在,ERROR调度请求再次发送到处理程序以进行错误处理。
    4. 如果您只是对此进行更改......

      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$1.run(ByteArrayEndPoint.java:78)
          at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:754)
          at org.eclipse.jetty.util.thread.QueuedThreadPool$2.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$1.run(ByteArrayEndPoint.java:78)
          at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:754)
          at org.eclipse.jetty.util.thread.QueuedThreadPool$2.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]}