How to determine remote IP address from JAX-RS resource?

Michael Spector picture Michael Spector · Jul 3, 2012 · Viewed 7.1k times · Source

I have the following JAX-RS resource running in Grizzly HTTP server:

@Path("/board")
public class BoardResource {

    @POST
    @Consumes("application/x-www-form-urlencoded")
    public void login(@FormParam("email") String email, @FormParam("password") String password,
            @HeaderParam("user-agent") String userAgent) {
        // how to determine remote IP address here?
    }
}

How can I determine remote IP address inside login() handler?

Thanks, Michael

Answer

Michael Spector picture Michael Spector · Aug 22, 2013

Since right now this is not possible to access remote IP information from Grizzly context, I've implemented this dirty hack:

First, I've added new dependency injection binder to my application, which will bind java.net.SocketAddress instance to one containing request origin IP:

package test;

import java.lang.reflect.Field;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import javax.inject.Inject;
import javax.inject.Provider;
import org.glassfish.grizzly.http.server.Request;
import org.glassfish.hk2.api.Factory;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.internal.PropertiesDelegate;
import org.glassfish.jersey.process.internal.RequestScoped;
import org.glassfish.jersey.server.ContainerRequest;

public class RemoteAddrBinder extends AbstractBinder {

    private static class RemoteAddrProviderFactory implements Factory<SocketAddress> {
        @Inject
        private Provider<ContainerRequest> request;

        @Override
        public SocketAddress provide() {
            ContainerRequest containerRequest = request.get();
            PropertiesDelegate delegate = containerRequest.getPropertiesDelegate();
            try {
                Field requestField = delegate.getClass().getDeclaredField("request");
                requestField.setAccessible(true);
                Request grizzlyRequest = (Request) requestField.get(delegate);
                return new InetSocketAddress(grizzlyRequest.getRemoteAddr(), grizzlyRequest.getRemotePort());
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }

        @Override
        public void dispose(SocketAddress instance) {
        }
    }

    @Override
    protected void configure() {        bindFactory(RemoteAddrProviderFactory.class).to(SocketAddress.class).in(RequestScoped.class);
    }
}

Then, I've registered this binder in the application:

application.register(new RemoteAddrBinder());

Now, SocketAddress can be injected into any JAX-RS method:

@GET
@Path("test")
public Response test(@Context SocketAddress remoteAddr) {
    return Response.ok("Your IP is: " + ((InetSocketAddress) remoteAddr).getAddress().getHostAddress()).build();
}

Hope this helps someone :)