简体   繁体   中英

How to read Jetty HttpInput (ServletInputStream) many times?

Currently im developing a REST API using RestEasy and Jetty . 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 . The thing with ContainerRequestPlugin in Jetty here is that once I called 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.

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

@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 .

Later then I figured out that the getEntityStream returned from requestContext is Jetty custom ServletInputStream that is org.eclipse.jetty.server.HttpInput . I believe the request cannot be read in EndPoint since I set the Entity Stream using ByteArrayInputStream.

So my question will be, is there any way to build/convert Jetty HttpInput using generic InputStream implementation? or is there any other way to work around this case? where I can read Jetty HttpInput many times?

Thanks & Regards

As you have no doubt noticed, the Servlet spec does not allow you to read the Request body contents twice.

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.
  • 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.

JAX-RS endpoints typically require that the javax.servlet.http.HttpServletRequest input stream has not been read, at all, for any reason (*).

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 . (example: sending 42 kilobytes of data that unpacks to 3.99 petabytes)

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.

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.

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. Jetty has such a class, called conveniently 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)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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