简体   繁体   English

如何多次读取 Jetty HttpInput (ServletInputStream)?

[英]How to read Jetty HttpInput (ServletInputStream) many times?

Currently im developing a REST API using RestEasy and Jetty .目前我正在使用RestEasyJetty开发 REST API 。 One of my plan with this REST API is to create a hook plugin to do anything needed with the incoming request utilizing JAX-RS ContainerRequestFilter .我对这个 REST API 的计划之一是使用 JAX-RS ContainerRequestFilter创建一个挂钩插件来处理传入请求所需的任何事情。 The thing with ContainerRequestPlugin in Jetty here is that once I called requestContext.getEntityStream(); JettyContainerRequestPlugin的问题是,一旦我调用requestContext.getEntityStream(); in the Filter then the request wont be able to be read again by my EndPoint Class even if I have set the Entity Stream again.在过滤器中,即使我再次设置了实体 Stream,我的端点 Class 也无法再次读取请求。

Following are my Filter code以下是我的过滤器代码

@Provider
@Priority(2000)
public class DummyRequestFilter implements ContainerRequestFilter{
    static Logger log = Logger.getLogger(DummyRequestFilter .class.getName());
    
    @Context
    private HttpServletRequest servletRequest;
    
    @Override
    public void filter(ContainerRequestContext requestContext) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
    String requestBody = "";
    
    try {           
        IOUtils.copy(requestContext.getEntityStream(), baos);
        
        InputStream is1 = new ByteArrayInputStream(baos.toByteArray());
        InputStream is2 = new ByteArrayInputStream(baos.toByteArray());
        
        requestBody = IOUtils.toString(is1);
        
        log.info(requestBody);
        
        requestContext.setEntityStream(is2);
                
    }catch (Exception e) {
        log.log(Level.SEVERE,"Exception Occurred",e);
    }
    }   
}

Then here is my endpoint class然后这是我的端点 class

@Path("/")
public class DummyService {
    
    Logger log = Logger.getLogger(DummyService .class.getName());
    
    @GET
    @Path("test")
    @Produces(MediaType.APPLICATION_JSON)
    public Response test(@FormParam("name") String name) {
        log.info("Name = "+name);

        return Response.status(200).build();
    }
}

Whenever I called this test method I can see the name sent in Filter class but in the Endpoint class name is NULL .每当我调用此测试方法时,我都可以看到在过滤器 class 但在端点 class 中发送的名称是NULL

Later then I figured out that the getEntityStream returned from requestContext is Jetty custom ServletInputStream that is org.eclipse.jetty.server.HttpInput .后来我发现从 requestContext 返回的 getEntityStream 是 Jetty 自定义ServletInputStream ,即org.eclipse.jetty.server.HttpInput I believe the request cannot be read in EndPoint since I set the Entity Stream using ByteArrayInputStream.我相信无法在 EndPoint 中读取请求,因为我使用 ByteArrayInputStream 设置了实体 Stream。

So my question will be, is there any way to build/convert Jetty HttpInput using generic InputStream implementation?所以我的问题是,有没有办法使用通用 InputStream 实现来构建/转换 Jetty HttpInput? or is there any other way to work around this case?或者有没有其他方法可以解决这种情况? where I can read Jetty HttpInput many times?我可以在哪里多次阅读 Jetty HttpInput?

Thanks & Regards感谢和问候

As you have no doubt noticed, the Servlet spec does not allow you to read the Request body contents twice.毫无疑问,您已经注意到,Servlet 规范不允许您两次读取请求正文内容。

This is an intentional decision as any such feature would require caching or buffering the response body content.这是一个有意的决定,因为任何此类功能都需要缓存或缓冲响应正文内容。 Which leads to:这导致:

  • Various DoS / Denial of Service attacks against your webapp.针对您的 webapp 的各种 DoS / 拒绝服务攻击。
  • Idle Timeouts on request processing when your code reads the request the second time from the buffer and produces no network traffic to reset the idle timeout.当您的代码第二次从缓冲区读取请求并且不产生网络流量来重置空闲超时时,请求处理的空闲超时。
  • The inability to benefit from or use Servlet Async I/O processing.无法受益于或使用 Servlet 异步 I/O 处理。

JAX-RS endpoints typically require that the javax.servlet.http.HttpServletRequest input stream has not been read, at all, for any reason (*). JAX-RS 端点通常要求javax.servlet.http.HttpServletRequest输入 stream 由于任何原因 (*) 根本没有被读取。

Your code makes no attempt to limit the size of the byte arrays you allocate, it would be easy to abuse your service with a Zip Bomb .您的代码没有尝试限制您分配的字节 arrays 的大小,使用Zip Bomb很容易滥用您的服务。 (example: sending 42 kilobytes of data that unpacks to 3.99 petabytes) (示例:发送 42 KB 的数据,解压为 3.99 PB)

You may find a JAX-RS implementation specific way, such as using Jersey internal code to set the entity stream, but that kind of code will be fragile and likely result in the need to fix your code and recompile with updates to your Jersey library.您可能会找到一种特定于 JAX-RS 实现的方式,例如使用 Jersey 内部代码来设置实体 stream,但这种代码将很脆弱,并且可能导致需要修复您的代码并通过更新 Z3CDA26E0C8AEDBF6662ADB42 重新编译。

If you go the custom route, please be take extra care to not introduce obvious vulnerabilities in your code, limit your request size, limit what you can buffer, etc.如果您 go 自定义路由,请特别注意不要在您的代码中引入明显的漏洞,限制您的请求大小,限制您可以缓冲的内容等。

Typically webapps that need to modify request input stream content do it via proxy servlets that perform middle-man modification of the request in real-time, on a buffer by buffer basis.通常,需要修改请求输入 stream 内容的 web 应用程序通过代理 servlets 执行此操作,该代理在逐个缓冲区的基础上实时执行请求的中间人修改。 Jetty has such a class, called conveniently AsyncMiddleManServlet . Jetty 有这样一个 class,方便地称为AsyncMiddleManServlet This essentially means your client talks to the proxy which talks to your endpoint, which honors network behaviors and network backpressure needs.这实质上意味着您的客户端与与您的端点对话的代理对话,该代理尊重网络行为和网络背压需求。 (something a buffering filter wouldn't be able to handle properly) (缓冲过滤器无法正确处理的东西)

(*) You can accidentally read the HttpServletRequest body by using things from the request that ask for the request parameters or the request parts (which require that the body content be read for certain specific Content-Types) (*) 您可能会通过使用请求中要求请求参数或请求部分的内容意外读取 HttpServletRequest 正文(这需要为某些特定的 Content-Types 读取正文内容)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM