Event listener(s) to intercept incoming requests and outgoing responses?

167 Views Asked by At

I'd like to log all incoming requests and outgoing responses coming into or going out of my server.

Is there an event listener that I can register that will provide a Request object before it is passed to any Handler and a Response object before it is sent out?

I already manually inserted log statements everywhere I could find, but some calls never reach my application's Handler. For example, if a user invokes HTTP GET https://example.com//quests/195 I get:

org.eclipse.jetty.http.BadMessageException: 400: Ambiguous URI empty segment
    at [email protected]/org.eclipse.jetty.server.internal.HttpConnection$HttpStreamOverHTTP1.headerComplete(HttpConnection.java:1226)
    at [email protected]/org.eclipse.jetty.server.internal.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:1012)
    at [email protected]/org.eclipse.jetty.http.HttpParser.parseFields(HttpParser.java:1252)
    at [email protected]/org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:1545)
    at [email protected]/org.eclipse.jetty.server.internal.HttpConnection.parseRequestBuffer(HttpConnection.java:626)
    at [email protected]/org.eclipse.jetty.server.internal.HttpConnection.onFillable(HttpConnection.java:460)
    at [email protected]/org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:322)
    at [email protected]/org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:100)
    at [email protected]/org.eclipse.jetty.io.SelectableChannelEndPoint$1.run(SelectableChannelEndPoint.java:53)
    at java.base/java.util.concurrent.ThreadPerTaskExecutor$TaskRunner.run(ThreadPerTaskExecutor.java:314)

In the above case, I have no where to get at a Request object so I can log the incoming request, nor the response that gets sent out automatically by Jetty. Any ideas?

1

There are 1 best solutions below

0
Joakim Erdfelt On

You have 2 choices.

Choice 1: Create a custom Handler.Wrapper with custom Request.Wrapper and Response.Wrapper

Create a normal Handler.Wrapper, and put it somewhere early in your Handler tree.

Then you can just wrap the existing request/response with your own implementation and pass it into the wrapped handler.

Eg:

public class CustomRequestResponseHandler extends Handler.Wrapper
{
    @Override
    public boolean handle(Request request, Response response, Callback callback) throws Exception
    {
        Request.Wrapper myRequest = new Request.Wrapper(request)
        {
            @Override
            public HttpURI getHttpURI()
            {
                // TODO: give a different URI? or perhaps just log the URI used?
                return super.getHttpURI();
            }
        };

        Response.Wrapper myResponse = new Response.Wrapper(request, response)
        {
            @Override
            public void write(boolean last, ByteBuffer byteBuffer, Callback callback)
            {
                // TODO: do something with the write
                super.write(last, byteBuffer, callback);
            }
        };

        return super.handle(myRequest, myResponse, callback);
    }
}

Choice 2: Create your EventsHandler to capture events

The abstract EventsHandler has a few events that you can hook into at different points during the handling that might be useful for you.

public static class CustomEventsHandler extends EventsHandler
{
    @Override
    protected void onBeforeHandling(Request request) {
    }

    @Override
    protected void onRequestRead(Request request, Content.Chunk chunk) {
    }

    @Override
    protected void onAfterHandling(Request request, boolean handled, Throwable failure) {
    }

    @Override
    protected void onResponseBegin(Request request, int status, HttpFields headers) {
    }

    @Override
    protected void onResponseWrite(Request request, boolean last, ByteBuffer content) {
    }

    @Override
    protected void onResponseWriteComplete(Request request, Throwable failure) {
    }

    @Override
    protected void onResponseTrailersComplete(Request request, HttpFields trailers) {
    }

    @Override
    protected void onComplete(Request request, Throwable failure) {
    }
}

See