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

Java 如何多次读取Jetty HttpInput(ServletInputStream)?,java,rest,jax-rs,jetty,resteasy,Java,Rest,Jax Rs,Jetty,Resteasy,目前,我正在使用RestEasy和Jetty开发一个RESTAPI。我的RESTAPI计划之一是创建一个钩子插件,利用JAX-RSContainerRequestFilter对传入请求执行任何需要的操作。Jetty中的ContainerRequestPlugin的问题是,有一次我调用了requestContext.getEntityStream()在过滤器中,即使我再次设置了实体流,我的端点类也无法再次读取请求 下面是我的过滤代码 @Provider @Priority(2000) public

目前,我正在使用
RestEasy
Jetty
开发一个RESTAPI。我的RESTAPI计划之一是创建一个钩子插件,利用JAX-RS
ContainerRequestFilter
对传入请求执行任何需要的操作。
Jetty
中的
ContainerRequestPlugin
的问题是,有一次我调用了
requestContext.getEntityStream()在过滤器中,即使我再次设置了实体流,我的端点类也无法再次读取请求

下面是我的过滤代码

@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);
    }
    }   
}
这是我的端点类

@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();
    }
}
无论何时调用此测试方法,我都可以看到在筛选器类中发送的名称,但在端点类中,名称为
NULL

后来,我发现从requestContext返回的getEntityStream是Jetty custom
ServletiInputStream
,即
org.eclipse.Jetty.server.HttpInput
。我相信无法在端点中读取请求,因为我使用ByteArrayInputStream设置了实体流

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


谢谢&您肯定已经注意到,Servlet规范不允许您两次读取请求正文内容

这是一个有意的决定,因为任何这样的特性都需要缓存或缓冲响应主体内容。这导致:

  • 针对您的Web应用的各种DoS/拒绝服务攻击
  • 当代码第二次从缓冲区读取请求并且不产生网络流量来重置空闲超时时,请求处理的空闲超时
  • 无法受益于或使用Servlet异步I/O处理
JAX-RS端点通常要求,
javax.servlet.http.HttpServletRequest
输入流由于任何原因(*)都没有被读取

您的代码没有试图限制您分配的字节数组的大小,这很容易滥用您的服务。(示例:发送42千字节的数据,将其解压为3.99 PB)

您可能会找到一种特定于JAX-RS实现的方法,例如使用Jersey内部代码来设置实体流,但这种代码很脆弱,可能需要修复代码并重新编译Jersey库的更新

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

通常,需要修改请求输入流内容的webapps是通过代理servlet来完成的,代理servlet在逐个缓冲区的基础上实时执行请求的中间人修改。Jetty有这样一个类,称为AsyncMiddleManServlet
。这本质上意味着您的客户机与代理进行对话,代理与您的端点进行对话,代理尊重网络行为和网络背压需求。(缓冲过滤器无法正确处理的内容)


(*)您可以通过使用请求中请求请求参数或请求部分(要求读取特定内容类型的正文内容)的内容来意外读取HttpServletRequest正文。

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

这是一个有意的决定,因为任何这样的特性都需要缓存或缓冲响应主体内容。这导致:

  • 针对您的Web应用的各种DoS/拒绝服务攻击
  • 当代码第二次从缓冲区读取请求并且不产生网络流量来重置空闲超时时,请求处理的空闲超时
  • 无法受益于或使用Servlet异步I/O处理
JAX-RS端点通常要求,
javax.servlet.http.HttpServletRequest
输入流由于任何原因(*)都没有被读取

您的代码没有试图限制您分配的字节数组的大小,这很容易滥用您的服务。(示例:发送42千字节的数据,将其解压为3.99 PB)

您可能会找到一种特定于JAX-RS实现的方法,例如使用Jersey内部代码来设置实体流,但这种代码很脆弱,可能需要修复代码并重新编译Jersey库的更新

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

通常,需要修改请求输入流内容的webapps是通过代理servlet来完成的,代理servlet在逐个缓冲区的基础上实时执行请求的中间人修改。Jetty有这样一个类,称为AsyncMiddleManServlet。这本质上意味着您的客户机与代理进行对话,代理与您的端点进行对话,代理尊重网络行为和网络背压需求。(缓冲过滤器无法正确处理的内容)


(*)您可以使用请求中请求请求参数或请求部分的内容(要求读取某些特定内容类型的正文内容)无意中读取HttpServletRequest正文。

您可能会找到一些帮助-您可能需要将输入流保存到某个位置,并在完成读取后重新添加,因为它们被设计为一次读取。您可能会找到一些帮助-您可能需要将输入流保存到某个位置,并在完成读取后重新添加,因为它们被设计为一次读取。哇,非常感谢先生的详细解释!,非常感谢你的意见。我刚刚意识到这是出于安全原因故意做的。实际上