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/
精彩评论