繁体   English   中英

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

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

目前我正在使用RestEasyJetty开发 REST API 。 我对这个 REST API 的计划之一是使用 JAX-RS ContainerRequestFilter创建一个挂钩插件来处理传入请求所需的任何事情。 JettyContainerRequestPlugin的问题是,一旦我调用requestContext.getEntityStream(); 在过滤器中,即使我再次设置了实体 Stream,我的端点 Class 也无法再次读取请求。

以下是我的过滤器代码

@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);
    }
    }   
}

然后这是我的端点 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();
    }
}

每当我调用此测试方法时,我都可以看到在过滤器 class 但在端点 class 中发送的名称是NULL

后来我发现从 requestContext 返回的 getEntityStream 是 Jetty 自定义ServletInputStream ,即org.eclipse.jetty.server.HttpInput 我相信无法在 EndPoint 中读取请求,因为我使用 ByteArrayInputStream 设置了实体 Stream。

所以我的问题是,有没有办法使用通用 InputStream 实现来构建/转换 Jetty HttpInput? 或者有没有其他方法可以解决这种情况? 我可以在哪里多次阅读 Jetty HttpInput?

感谢和问候

毫无疑问,您已经注意到,Servlet 规范不允许您两次读取请求正文内容。

这是一个有意的决定,因为任何此类功能都需要缓存或缓冲响应正文内容。 这导致:

  • 针对您的 webapp 的各种 DoS / 拒绝服务攻击。
  • 当您的代码第二次从缓冲区读取请求并且不产生网络流量来重置空闲超时时,请求处理的空闲超时。
  • 无法受益于或使用 Servlet 异步 I/O 处理。

JAX-RS 端点通常要求javax.servlet.http.HttpServletRequest输入 stream 由于任何原因 (*) 根本没有被读取。

您的代码没有尝试限制您分配的字节 arrays 的大小,使用Zip Bomb很容易滥用您的服务。 (示例:发送 42 KB 的数据,解压为 3.99 PB)

您可能会找到一种特定于 JAX-RS 实现的方式,例如使用 Jersey 内部代码来设置实体 stream,但这种代码将很脆弱,并且可能导致需要修复您的代码并通过更新 Z3CDA26E0C8AEDBF6662ADB42 重新编译。

如果您 go 自定义路由,请特别注意不要在您的代码中引入明显的漏洞,限制您的请求大小,限制您可以缓冲的内容等。

通常,需要修改请求输入 stream 内容的 web 应用程序通过代理 servlets 执行此操作,该代理在逐个缓冲区的基础上实时执行请求的中间人修改。 Jetty 有这样一个 class,方便地称为AsyncMiddleManServlet 这实质上意味着您的客户端与与您的端点对话的代理对话,该代理尊重网络行为和网络背压需求。 (缓冲过滤器无法正确处理的东西)

(*) 您可能会通过使用请求中要求请求参数或请求部分的内容意外读取 HttpServletRequest 正文(这需要为某些特定的 Content-Types 读取正文内容)

暂无
暂无

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

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