我正在使用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中还有一个额外的步骤。
答案 0 :(得分:4)
我有一段时间搞清楚这一点,所以我想我将分享,以防将来有人在这篇文章中徘徊。
起初,我在httpcore-nio 4.1
,并且连接似乎不可能。最后,我做了一个反射黑客(这可能不是最好的方法)。升级到4.2.1之后,它变得相当容易。我将在下面分享这两个解决方案。如果连接客户端不是localhost,则以下代码将返回403 Forbidden
。
@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;
}
...
}
明智的一句话:不要这样做。它适用于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类: