diff --git a/falcon/request.py b/falcon/request.py index f9a3950..8182f49 100644 --- a/falcon/request.py +++ b/falcon/request.py @@ -72,9 +72,11 @@ class Request(object): options (dict): Set of global options passed from the API handler. Attributes: - protocol (str): Either 'http' or 'https'. + protocol (str): Deprecated alias for `scheme`. May be removed in a future release. + scheme (str): Either 'http' or 'https'. method (str): HTTP method requested (e.g., 'GET', 'POST', etc.) host (str): Hostname requested by the client + port (str): Port used for the request subdomain (str): Leftmost (i.e., most specific) subdomain from the hostname. If only a single domain name is given, `subdomain` will be ``None``. @@ -545,9 +547,11 @@ class Request(object): return self.env.get('SCRIPT_NAME', '') @property - def protocol(self): + def scheme(self): return self.env['wsgi.url_scheme'] + protocol = scheme + @property def uri(self): if self._cached_uri is None: @@ -691,6 +695,25 @@ class Request(object): def remote_addr(self): return self.env.get('REMOTE_ADDR') + @property + def port(self): + try: + host_header = self.env['HTTP_HOST'] + host, port = parse_host(host_header) + except KeyError: + port = self.env['SERVER_PORT'] + + if not port: + port = '80' if self.scheme == 'http' else '443' + return port + + @property + def netloc(self): + try: + return self.env['HTTP_HOST'] + except KeyError: + return self.host + ':' + self.port + # ------------------------------------------------------------------------ # Methods # ------------------------------------------------------------------------ diff --git a/tests/test_req_vars.py b/tests/test_req_vars.py index 4e48134..ff257fc 100644 --- a/tests/test_req_vars.py +++ b/tests/test_req_vars.py @@ -668,3 +668,121 @@ class TestReqVars(testing.TestBase): except error_type as ex: self.assertEqual(ex.title, title) self.assertEqual(ex.description, description) + + def test_port_implicit_http(self): + req = Request(testing.create_environ( + protocol='HTTP/1.0', + app=self.app, + path='/hello', + query_string=self.qs, + headers=self.headers)) + + self.assertEqual(req.port, '80') + + def test_port_implicit_https(self): + req = Request(testing.create_environ( + protocol='HTTP/1.0', + scheme='https', + app=self.app, + path='/hello', + query_string=self.qs, + headers=self.headers)) + + self.assertEqual(req.port, '443') + + def test_port_explicit(self): + PORT = 9000 + req = Request(testing.create_environ( + protocol='HTTP/1.0', + port=PORT, + app=self.app, + path='/hello', + query_string=self.qs, + headers=self.headers)) + + self.assertEqual(req.port, str(PORT)) + + def test_port_from_env(self): + PORT = str(9000) + HTTP_HOST = '{0}:{1}'.format('example.org', PORT) + env = testing.create_environ( + protocol='HTTP/1.0', + port=PORT, + app=self.app, + path='/hello', + query_string=self.qs, + headers=self.headers) + env.update({'HTTP_HOST': HTTP_HOST}) + req = Request(env) + self.assertEqual(req.port, int(PORT)) + + def test_port_from_scheme_http(self): + HTTP_HOST = 'example.com' + env = testing.create_environ( + protocol='HTTP/1.0', + app=self.app, + path='/hello', + query_string=self.qs, + headers=self.headers) + env.update({'HTTP_HOST': HTTP_HOST}) + req = Request(env) + self.assertEqual(req.port, '80') + + def test_port_from_scheme_https(self): + HTTP_HOST = 'example.com' + env = testing.create_environ( + protocol='HTTP/1.0', + scheme='https', + app=self.app, + path='/hello', + query_string=self.qs, + headers=self.headers) + env.update({'HTTP_HOST': HTTP_HOST}) + req = Request(env) + self.assertEqual(req.port, '443') + + def test_scheme_https(self): + _scheme = 'https' + req = Request(testing.create_environ( + protocol='HTTP/1.0', + scheme=_scheme, + app=self.app, + path='/hello', + query_string=self.qs, + headers=self.headers)) + self.assertEqual(req.scheme, _scheme) + + def test_scheme_http(self): + _scheme = 'http' + req = Request(testing.create_environ( + protocol='HTTP/1.0', + scheme=_scheme, + app=self.app, + path='/hello', + query_string=self.qs, + headers=self.headers)) + self.assertEqual(req.scheme, _scheme) + + def test_netloc(self): + req = Request(testing.create_environ( + protocol='HTTP/1.0', + app=self.app, + path='/hello', + query_string=self.qs, + headers=self.headers)) + _netloc = '{host}:{port}'.format(host=req.host, port=req.port) + self.assertEqual(req.netloc, _netloc) + + def test_netloc_from_env(self): + PORT = str(9000) + HTTP_HOST = '{0}:{1}'.format('example.org', PORT) + env = testing.create_environ( + protocol='HTTP/1.0', + port=PORT, + app=self.app, + path='/hello', + query_string=self.qs, + headers=self.headers) + env.update({'HTTP_HOST': HTTP_HOST}) + req = Request(env) + self.assertEqual(req.netloc, HTTP_HOST)