如何使用Apache httpcomponents从NHttpRequestHandler中告知远程IP地址?

时间:2011-06-13 20:41:23

标签: java apache-httpcomponents

我正在使用Apache httpcomponents实现一个彗星式(延迟响应)http服务器。我的代码与http://hc.apache.org/httpcomponents-core-ga/examples.html

中的“基本非阻塞HTTP服务器”示例非常相似

我使用DefaultServerIOEventDispatch和DefaultListeningIOReactor来分派请求,就像在示例代码中一样。在我的NHttpRequestHandler里面,我想记录每个请求的IP地址。

在HttpRequestHandler中,您可以访问HttpRequest,HttpResponse和HttpContext。使用NHttpRequestHandler,您还有一个NHttpResponseTrigger。如何获取请求来自的远程IP地址?我无法看到如何使用可用的对象。

更新,这是我最终使用的Scala代码:

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
  }
}

正如您所看到的,HttpComponents 4.1中还有一个额外的步骤。

2 个答案:

答案 0 :(得分:4)

我有一段时间搞清楚这一点,所以我想我将分享,以防将来有人在这篇文章中徘徊。

起初,我在httpcore-nio 4.1,并且连接似乎不可能。最后,我做了一个反射黑客(这可能不是最好的方法)。升级到4.2.1之后,它变得相当容易。我将在下面分享这两个解决方案。如果连接客户端不是localhost,则以下代码将返回403 Forbidden

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

明智的一句话:不要这样做。它适用于4.1,但在升级4.2.x时会中断。如果可以,请改为升级。

此解决方案适用于httpcore-nio 4.1,但要注意,代码是一个可怕的,可怕的反射黑客,从最终变量(iosession)中获取私有字段(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);
    } 
    ...
}

答案 1 :(得分:1)

可以将NHttpConnection实例转换为HttpInetConnection类型并调用HttpInetConnection#getRemoteAddress方法以获取相对端点的IP。可以从作为HttpContext的参数传递的HttpRequestHandler实例获取连接对象。或者,如果要将连接生命周期事件日志记录与协议处理逻辑分离,则可能需要实现EventListener接口。

在HttpCore的contrib包中还有一组日志记录类可以用来扩展具有有线和I / O事件记录功能的标准HttpCore类:

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