diff --git a/sanic/request.py b/sanic/request.py index d8674c48..27ff011e 100644 --- a/sanic/request.py +++ b/sanic/request.py @@ -45,7 +45,7 @@ class Request(dict): __slots__ = ( 'app', 'headers', 'version', 'method', '_cookies', 'transport', 'body', 'parsed_json', 'parsed_args', 'parsed_form', 'parsed_files', - '_ip', '_parsed_url', 'uri_template', 'stream' + '_ip', '_parsed_url', 'uri_template', 'stream', '_remote_addr' ) def __init__(self, url_bytes, headers, version, method, transport): @@ -142,7 +142,7 @@ class Request(dict): @property def cookies(self): if self._cookies is None: - cookie = self.headers.get('Cookie') or self.headers.get('cookie') + cookie = self.headers.get('Cookie') if cookie is not None: cookies = SimpleCookie() cookies.load(cookie) @@ -159,6 +159,25 @@ class Request(dict): (None, None)) return self._ip + @property + def remote_addr(self): + """Attempt to return the original client ip based on X-Forwarded-For. + + :return: original client ip. + """ + if not hasattr(self, '_remote_addr'): + forwarded_for = self.headers.get('X-Forwarded-For', '').split(',') + remote_addrs = [ + addr for addr in [ + addr.strip() for addr in forwarded_for + ] if addr + ] + if len(remote_addrs) > 0: + self._remote_addr = remote_addrs[0] + else: + self._remote_addr = '' + return self._remote_addr + @property def scheme(self): if self.app.websocket_enabled \ diff --git a/tests/test_requests.py b/tests/test_requests.py index 997f32cb..f0696c7f 100644 --- a/tests/test_requests.py +++ b/tests/test_requests.py @@ -211,6 +211,32 @@ def test_content_type(): assert response.text == 'application/json' +def test_remote_addr(): + app = Sanic('test_content_type') + + @app.route('/') + async def handler(request): + return text(request.remote_addr) + + headers = { + 'X-Forwarded-For': '127.0.0.1, 127.0.1.2' + } + request, response = app.test_client.get('/', headers=headers) + assert request.remote_addr == '127.0.0.1' + assert response.text == '127.0.0.1' + + request, response = app.test_client.get('/') + assert request.remote_addr == '' + assert response.text == '' + + headers = { + 'X-Forwarded-For': '127.0.0.1, , ,,127.0.1.2' + } + request, response = app.test_client.get('/', headers=headers) + assert request.remote_addr == '127.0.0.1' + assert response.text == '127.0.0.1' + + def test_match_info(): app = Sanic('test_match_info') @@ -259,6 +285,7 @@ def test_post_form_urlencoded(): assert request.form.get('test') == 'OK' + @pytest.mark.parametrize( 'payload', [ '------sanic\r\n' \