Search code examples
pythonwsgieventlet

Obtaining Client IP address from a WSGI app using Eventlet


I'm currently writing a basic dispatch model server based on the Python Eventlet library (http://eventlet.net/doc/). Having looked at the WSGI docs on Eventlet (http://eventlet.net/doc/modules/wsgi.html), I can see that the eventlet.wsgi.server function logs the x-forwarded-for header in addition to the client IP address.

However, the way to obtain this is to attach a file-like object (the default which is sys.stderr) and then have the server pipe that to that object.

I would like to be able to obtain the client IP from within the application itself (i.e. the function that has start_response and environ as parameters). Indeed, an environ key would be perfect for this. Is there a way to obtain the IP address simply (i.e. through the environ dictionary or similar), without having to resort to redirecting the log object somehow?


Solution

  • What you want is in the wsgi environ, specifically environ['REMOTE_ADDR'].

    However, if there is a proxy involved, then REMOTE_ADDR will be the address of the proxy, and the client address will be included (most likely) in HTTP_X_FORWARDED_FOR.

    Here's a function that should do what you want, for most cases (all credit to Sævar):

    def get_client_address(environ):
        try:
            return environ['HTTP_X_FORWARDED_FOR'].split(',')[-1].strip()
        except KeyError:
            return environ['REMOTE_ADDR']
    

    You can easily see what is included in the wsgi environ by writing a simple wsgi app and pointing a browser at it, for example:

    from eventlet import wsgi
    import eventlet
    
    from pprint import pformat
    
    def show_env(env, start_response):
        start_response('200 OK', [('Content-Type', 'text/plain')])
        return ['%s\r\n' % pformat(env)]
    
    wsgi.server(eventlet.listen(('', 8090)), show_env)
    

    And combining the two ...

    from eventlet import wsgi
    import eventlet
    
    from pprint import pformat
    
    def get_client_address(environ):
        try:
            return environ['HTTP_X_FORWARDED_FOR'].split(',')[-1].strip()
        except KeyError:
            return environ['REMOTE_ADDR']
    
    def show_env(env, start_response):
        start_response('200 OK', [('Content-Type', 'text/plain')])
        return ['%s\r\n\r\nClient Address: %s\r\n' % (pformat(env), get_client_address(env))]
    
    wsgi.server(eventlet.listen(('', 8090)), show_env)