Python: get remote IP from HTTP request using the requests module
Introduction:#
This is just a quick tip on how to get the remote IP address from an HTTP request using the requests module.
Variables:#
Python 3.4.5
requests 2.18.1
urllib3 1.21.1
To get the IP, we need to use the stream parameter to keep the connection open:
r = requests.get("http://example.com", stream=True)
At this point, only the response headers have been downloaded and the connection remains open until we access Response.content.
The requests module documentation
warns that:
If you set stream to True when making a request, Requests cannot release the connection back to the pool unless you consume all the data or call Response.close. This can lead to inefficiency with connections. If you find yourself partially reading request bodies (or not reading them at all) while using stream=True, you should make the request within a with statement to ensure it’s always closed
We can see this in action using netstat. In this case, the Apache web server waits for the other side to shut down its half of the connection (FIN_WAIT2):
# netstat -entp
Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address Foreign Address State User Inode PID/Program name
...
tcp6 0 0 192.168.1.10:80 192.168.1.254:38610 FIN_WAIT2 0 0 -
With a context manager , the socket is closed at both ends:
>>> with requests.get("http://example.com/", stream=True) as r:
... r.status_code
...
404
# netstat -entp
Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address Foreign Address State User Inode PID/Program name
...
tcp6 0 0 192.168.1.10:80 192.168.1.254:44124 TIME_WAIT 0 0 -
Back to the main topic, requests.raw._original_response is an http.client.HTTPResponse
instance. Its .fp is the socket file, a buffer wrapping a SocketIO object with the actual socket in the _sock attribute. So, the full path to the socket is requests.raw._original_response.fp.raw._sock, which exposes .getpeername():
>>> with requests.get("http://example.com/", stream=True) as r:
... r.raw._original_response.fp.raw._sock.getpeername()[0]
...
'192.168.1.10'
Note: this relies on private internal attributes and may break in newer versions.