3

我正在使用 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 中有一个额外的步骤。

4

2 回答 2

5

我花了很长时间才弄清楚这一点,所以我想我会分享,以防将来有人在这篇文章中徘徊。

起初,我一直在工作httpcore-nio 4.1,而且似乎不可能建立连接。最终,我使用了反射技巧(这可能绝不是最好的方法)。升级到 4.2.1 后,它变得容易多了。我将在下面分享这两种解决方案。403 Forbidden如果连接的客户端不是 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

对智者的一句话:不要这样做。它适用于 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);
    } 
    ...
}
于 2012-07-15T21:13:40.873 回答
1

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

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

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

于 2011-06-15T11:43:05.247 回答