Question

I trying some things with the HTTP server of Sun JRE. After reading twice the documentation, I am still confused.

The com.sun.net.httpserver.Filter javadoc says the following

Asks this filter to pre/post-process the given exchange. The filter can :

  • examine or modify the request headers
  • filter the request body or the response body, by creating suitable filter streams and calling HttpExchange.setStreams(InputStream,OutputStream)
  • set attribute Objects in the exchange, which other filters or the exchange handler can access.
  • decide to either :

    • invoke the next filter in the chain, by calling Filter.Chain.doFilter(HttpExchange)
    • terminate the chain of invocation, by not calling Filter.Chain.doFilter(HttpExchange)

    if option 1. above taken, then when doFilter() returns all subsequent filters in the Chain have been called, and the response headers can be examined or modified. if option 2. above taken, then this Filter must use the HttpExchange to send back an appropriate response

What does not clear for me, what decides when the filter is pre- or post-process filter. As I assume, post-process filter is runs after the HttpHandler, so it can work with HttpExchange what is modified by HttpHandler. However, filter is called only once, so there must be a "magic" what decides the filter runs before or after the handler.

Please help me to make it clear.

Was it helpful?

Solution 2

I've wondered about this myself and I don't see how the setStreams method can be helpful for this. The only way I could do this is by wrapping HttpExchange.

This example shows how to apply gzip compression with a Filter:

https://gist.github.com/Crydust/7e4e9228cd95febccdc58f0501c1e327

OTHER TIPS

Every filter is both a pre and post filter. What I mean by that is the Request passes through it as it moves through the stack, and then the Response comes back through it on the way out to the client. The order that the filter is called depends on the order you mount it in your web.xml file.

If you were using it as a Pre filter, you would modify the InputStream and if you wanted it as a Post you would modify the OutputStream. You can even pass your own InputStream and OutputStream down through the chain.

So, say for example you had a few Filter1, Filter2 and Filter3. The InputStream would first go through Filter1, then Filter2 and finally Filter3 before being processed. The resulting OutputStream would then go back through Filter3, then Filter2 and finally Filter1 before being sent to the client. So, in that way you can modify pre and/or post processing.

Filter.doFilter() is called only once, before HttpHandler.handle() is called.

This means that you cannot modify the content of the HTTP response after your handler has run. So, it's more naturally a pre-filter.

However, in your implementation of doFilter(), you can wrap the the response's OutputStream with your own FilterOutputStream, so you can intercept the calls to HttpExchange.getResponseBody().write(...).

class MyFilter extends Filter {

    @Override
    public void doFilter(HttpExchange exch, Chain chain) throws IOException {

        exch.setStreams(null, new MyInterceptedOutputStream(exch.getResponseBody()));
        chain.doFilter(exch);
    }

    ...
}

This MyInterceptedOutputStream class will need to extend OutputStream and implement its usual methods (write() and close())

One trick: Your wrapper must ensure that it will not write anything to the original OutputStream until HttpExchange.sendResponseHeaders() has been called. This means that you have to make sure that your wrapper constructor (MyInterceptedOutputStream(OutputStream os) in the above example) doesn't not write anything, unlike GZIPOutputStream() for example!

Bonus: Example of an OutputStream wrapper that compresses the stream... and works in HttpFilter.doFilter()

import java.io.IOException;
import java.io.OutputStream;
import java.util.zip.GZIPOutputStream;

public class GZIPDelayedOutputStream extends OutputStream {

    GZIPOutputStream gzipStream;
    OutputStream originalStream;

    public GZIPDelayedOutputStream(OutputStream os) {
        super();
        originalStream = os;
    }

    private void createGzipStreamIfNecessary() throws IOException {
        if (gzipStream == null) {
            gzipStream = new GZIPOutputStream(originalStream);
        }
    }

    @Override
    public void write(int b) throws IOException {
        createGzipStreamIfNecessary();
        gzipStream.write(b);
    }

    @Override
    public void write(byte buf[]) throws IOException {
        createGzipStreamIfNecessary();
        gzipStream.write(buf);
    }

    @Override
    public void write(byte[] buf, int off, int len) throws IOException {
        createGzipStreamIfNecessary();
        gzipStream.write(buf, off, len);
    }

    @Override
    public void close() throws IOException {
        createGzipStreamIfNecessary();
        gzipStream.close();
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top