Skip to content

Commit d904071

Browse files
author
Sergey Skripnick
committed
Fix wsgi environment building
Fixed issues: SERVER_NAME and SERVER_PORT variables may be determined in two ways: if protocol is HTTP/1.0: Value of `Host` header. If there is no such header, then address of transport may be taken (transport.get_extra_info('sockname')) if protocol is HTTP/1.1: Value of `Host` header. If there is no such header, the Bad Request should be raised, because this header is required in HTTP/1.1. REMOTE_ADDR and REMOTE_PORT should be address and port of host connected to http server. (https://www.ietf.org/rfc/rfc3875) Also, header `Authorization` should not be in environment at all.
1 parent c95bcf5 commit d904071

File tree

2 files changed

+35
-51
lines changed

2 files changed

+35
-51
lines changed

aiohttp/wsgi.py

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from urllib.parse import urlsplit
1515

1616
import aiohttp
17-
from aiohttp import server, helpers, hdrs
17+
from aiohttp import server, hdrs
1818

1919
__all__ = ('WSGIServerHttpProtocol',)
2020

@@ -63,17 +63,12 @@ def create_wsgi_environ(self, message, payload):
6363
'SERVER_PROTOCOL': 'HTTP/%s.%s' % message.version
6464
}
6565

66-
# authors should be aware that REMOTE_HOST and REMOTE_ADDR
67-
# may not qualify the remote addr:
68-
# http://www.ietf.org/rfc/rfc3875
69-
forward = self.transport.get_extra_info('addr', '127.0.0.1')
7066
script_name = self.SCRIPT_NAME
71-
server = forward
7267

7368
for hdr_name, hdr_value in message.headers.items():
74-
if hdr_name == 'HOST':
75-
server = hdr_value
76-
elif hdr_name == 'SCRIPT_NAME':
69+
if hdr_name == 'AUTHORIZATION':
70+
continue
71+
if hdr_name == 'SCRIPT_NAME':
7772
script_name = hdr_value
7873
elif hdr_name == 'CONTENT-TYPE':
7974
environ['CONTENT_TYPE'] = hdr_value
@@ -88,17 +83,16 @@ def create_wsgi_environ(self, message, payload):
8883

8984
environ[key] = hdr_value
9085

91-
remote = helpers.parse_remote_addr(forward)
86+
remote = self.transport.get_extra_info('peername')
9287
environ['REMOTE_ADDR'] = remote[0]
9388
environ['REMOTE_PORT'] = remote[1]
94-
95-
if isinstance(server, str):
96-
server = server.split(':')
97-
if len(server) == 1:
98-
server.append('80' if url_scheme == 'http' else '443')
99-
100-
environ['SERVER_NAME'] = server[0]
101-
environ['SERVER_PORT'] = str(server[1])
89+
host = message.headers.get("HOST", None)
90+
if host:
91+
host = host.split(":")
92+
else:
93+
host = self.transport.get_extra_info('sockname')
94+
environ['SERVER_NAME'] = host[0]
95+
environ['SERVER_PORT'] = str(host[1]) if len(host) > 1 else '80'
10296

10397
path_info = uri_parts.path
10498
if script_name:

tests/test_wsgi.py

Lines changed: 23 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ def setUp(self):
2222
self.writer = unittest.mock.Mock()
2323
self.writer.drain.return_value = ()
2424
self.transport = unittest.mock.Mock()
25-
self.transport.get_extra_info.return_value = '127.0.0.1'
25+
self.transport.get_extra_info.return_value = ('1.2.3.4', 8080)
2626

27-
self.headers = multidict.MultiDict()
27+
self.headers = multidict.MultiDict({"HOST": "python.org"})
2828
self.message = protocol.RawRequestMessage(
2929
'GET', '/path', (1, 0), self.headers, True, 'deflate')
3030
self.payload = aiohttp.FlowControlDataQueue(self.reader)
@@ -63,8 +63,7 @@ def test_environ(self):
6363

6464
def test_environ_headers(self):
6565
self.headers.extend(
66-
(('HOST', 'python.org'),
67-
('SCRIPT_NAME', 'script'),
66+
(('SCRIPT_NAME', 'script'),
6867
('CONTENT-TYPE', 'text/plain'),
6968
('CONTENT-LENGTH', '209'),
7069
('X_TEST', '123'),
@@ -75,10 +74,9 @@ def test_environ_headers(self):
7574
self.assertEqual(environ['HTTP_X_TEST'], '123,456')
7675
self.assertEqual(environ['SCRIPT_NAME'], 'script')
7776
self.assertEqual(environ['SERVER_NAME'], 'python.org')
78-
self.assertEqual(environ['SERVER_PORT'], '443')
77+
self.assertEqual(environ['SERVER_PORT'], '80')
7978

8079
def test_environ_host_header(self):
81-
self.headers.add('HOST', 'python.org')
8280
environ = self._make_one()
8381

8482
self.assertEqual(environ['HTTP_HOST'], 'python.org')
@@ -87,41 +85,16 @@ def test_environ_host_header(self):
8785
self.assertEqual(environ['SERVER_PROTOCOL'], 'HTTP/1.0')
8886

8987
def test_environ_host_port_header(self):
88+
headers = multidict.MultiDict({'HOST': 'python.org:443'})
9089
self.message = protocol.RawRequestMessage(
91-
'GET', '/path', (1, 1), self.headers, True, 'deflate')
92-
self.headers.add('HOST', 'python.org:443')
90+
'GET', '/path', (1, 1), headers, True, 'deflate')
9391
environ = self._make_one()
9492

9593
self.assertEqual(environ['HTTP_HOST'], 'python.org:443')
9694
self.assertEqual(environ['SERVER_NAME'], 'python.org')
9795
self.assertEqual(environ['SERVER_PORT'], '443')
9896
self.assertEqual(environ['SERVER_PROTOCOL'], 'HTTP/1.1')
9997

100-
def test_environ_forward(self):
101-
self.transport.get_extra_info.return_value = 'localhost,127.0.0.1'
102-
environ = self._make_one()
103-
104-
self.assertEqual(environ['REMOTE_ADDR'], '127.0.0.1')
105-
self.assertEqual(environ['REMOTE_PORT'], '80')
106-
107-
self.transport.get_extra_info.return_value = 'localhost,127.0.0.1:443'
108-
environ = self._make_one()
109-
110-
self.assertEqual(environ['REMOTE_ADDR'], '127.0.0.1')
111-
self.assertEqual(environ['REMOTE_PORT'], '443')
112-
113-
self.transport.get_extra_info.return_value = ('127.0.0.1', 443)
114-
environ = self._make_one()
115-
116-
self.assertEqual(environ['REMOTE_ADDR'], '127.0.0.1')
117-
self.assertEqual(environ['REMOTE_PORT'], '443')
118-
119-
self.transport.get_extra_info.return_value = '[::1]'
120-
environ = self._make_one()
121-
122-
self.assertEqual(environ['REMOTE_ADDR'], '::1')
123-
self.assertEqual(environ['REMOTE_PORT'], '80')
124-
12598
def test_wsgi_response(self):
12699
srv = self._make_srv()
127100
resp = srv.create_wsgi_response(self.message)
@@ -280,3 +253,20 @@ def test_dont_unquote_environ_path_info(self):
280253
'GET', path, (1, 0), self.headers, True, 'deflate')
281254
environ = self._make_one()
282255
self.assertEqual(environ['PATH_INFO'], path)
256+
257+
def test_not_add_authorization(self):
258+
self.headers.extend({"AUTHORIZATION": "spam",
259+
"X-CUSTOM-HEADER": "eggs"})
260+
self.message = protocol.RawRequestMessage(
261+
'GET', '/', (1, 1), self.headers, True, 'deflate')
262+
environ = self._make_one()
263+
self.assertEqual('eggs', environ['HTTP_X_CUSTOM_HEADER'])
264+
self.assertFalse('AUTHORIZATION' in environ)
265+
266+
def test_http_1_0_no_host(self):
267+
headers = multidict.MultiDict({})
268+
self.message = protocol.RawRequestMessage(
269+
'GET', '/', (1, 0), headers, True, 'deflate')
270+
environ = self._make_one()
271+
self.assertEqual('1.2.3.4', environ["SERVER_NAME"])
272+
self.assertEqual('8080', environ["SERVER_PORT"])

0 commit comments

Comments
 (0)