开发者

How do I tell the remote IP address from within NHttpRequestHandler using Apache httpcomponents?

开发者 https://www.devze.com 2023-03-12 09:55 出处:网络
I am implementing a comet-style (delayed response) http server using Apache httpcomponents. My code is very similar to the \"Basic non-blocking HTTP server\" example at http://hc.apache.org/httpcompon

I am implementing a comet-style (delayed response) http server using Apache httpcomponents. My code is very similar to the "Basic non-blocking HTTP server" example at http://hc.apache.org/httpcomponents-core-ga/examples.html

I use a DefaultServerIOEventDispatch and DefaultListeningIORe开发者_开发技巧actor to dispatch requests, just like in the example code. Inside my NHttpRequestHandler I would like to log the IP address of each request.

Inside an HttpRequestHandler you have access to an HttpRequest, an HttpResponse and an HttpContext. With an NHttpRequestHandler you also have an NHttpResponseTrigger. How do I get the remote IP address the request came from? I can't see how to do this with the objects available.

Update, here is the Scala code I ended up using:

def getIp(context: HttpContext): Option[String] = {
  val conn = context.getAttribute(ExecutionContext.HTTP_CONNECTION)

  conn match {
    case inet: HttpInetConnection =>
      inet.getRemoteAddress match {
        case sock: java.net.InetSocketAddress => // HttpComponents 4.1
          Some(sock.getAddress.getHostAddress)
        case adr: java.net.InetAddress => // HttpComponents 4.2
          Some(adr.getHostAddress)
        case unknown =>
          Some(unknown.toString)
      }
    case _ => None
  }
}

As you can see there is an extra step in HttpComponents 4.1.


I had a heck of a time figuring this out, so I figured I'll share in case someone wanders across this post in the future.

At first, I was on httpcore-nio 4.1 and getting the connection seemed impossible. Eventually, I did it with a reflections hack (that is probably by no means the best way to do it). After upgrading to 4.2.1 it was considerably easier. I'll share both solutions below. The following code will return 403 Forbidden if the connecting client is not localhost.

HttpCore NIO 4.2

@Override
public void handle(final HttpRequest request, final HttpResponse response, final HttpContext context) {
    HttpInetConnection connection = (HttpInetConnection) context.getAttribute(ExecutionContext.HTTP_CONNECTION);
    InetAddress ia = connection.getRemoteAddress();
    if("localhost".equals(ia.getHostName()) {
        response.setStatusCode(HttpStatus.SC_FORBIDDEN);
        return;
    }
    ...
}

HttpCore NIO 4.1

A word to the wise: Don't do this. It will work for 4.1, but it will break when upgrading 4.2.x. If you can, upgrade instead.

This solution works for httpcore-nio 4.1 but beware, the code is a horrible, horrible reflections hack to get a private field (iosession) out of a final variable (HttpContext).

@Override
public void handle(final HttpRequest request, final HttpResponse response, final HttpContext context) {
    try {
        Field f = context.getClass().getDeclaredField("iosession");
        boolean accessible = f.isAccessible();
        Field modifiersField = Field.class.getDeclaredField("modifiers");
        int modifiers = f.getModifiers();
        modifiersField.setAccessible(true);
        modifiersField.set(f, f.getModifiers() & ~Modifier.FINAL & ~Modifier.PRIVATE);
        f.setAccessible(true);
        IOSession io = (IOSession) f.get(context);
        f.setAccessible(accessible);
        modifiersField.set(f, modifiers);
        SocketAddress sa = io.getRemoteAddress();
        if("localhost".equals(((InetSocketAddress) sa).getHostName())) {
            response.setStatusCode(HttpStatus.SC_FORBIDDEN);
            return;
        }
    } catch (Exception e) {
        logger.error("No way! I can't believe this fantastic piece of code threw an exception!", e);
    } 
    ...
}


One can cast NHttpConnection instance to HttpInetConnection type and call HttpInetConnection#getRemoteAddress method in order to obtain the IP of the opposite endpoint. The connection object can be obtained from the HttpContext instance passed as a parameter to HttpRequestHandler. Alternatively you might want to implement EventListener interface if you want to decouple connection life cycle event logging from your protocol handling logic.

There is also a set of logging classes in the contrib package of HttpCore one can use to extend standard HttpCore classes with wire and I/O event logging capabilities:

http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/httpcore-contrib/src/main/java/org/apache/http/contrib/logging/

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号