feat(Request): Pass through relevant WSGI environ variables
This commit is contained in:
2
NOTES.md
2
NOTES.md
@@ -1,5 +1,7 @@
|
||||
* Keep-Alive is intentially disabled for HTTP/1.0 clients to mitigate problems with proxies. See also http://tools.ietf.org/html/rfc2616#section-19.6.2
|
||||
* resp.set_header assumes both params are strings. App may crash otherwise. Falcon trusts the caller. You *are* testing all your code paths, aren't you?
|
||||
* If the WSGI server passes an empty path, Falcon will force it to '/', so you don't have to test for the empty string in your app.
|
||||
* If you are hosting multiple apps with a single WSGI server, the SCRIPT_NAME variable can read from req.app
|
||||
* If you have several headers to set, consider using set_headers to avoid function call overhead
|
||||
* Don't set content-length. It will only be overridden.
|
||||
* The order in which header fields are sent in the response is undefined. Headers are not grouped according to the recommendation in [RFC 2616](http://tools.ietf.org/html/rfc2616#section-4.2) in order to generate responses as quickly as possible.
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
class Request:
|
||||
__slots__ = ('path', 'headers')
|
||||
__slots__ = ('path', 'headers', 'app', 'query_string', 'method')
|
||||
|
||||
def __init__(self, env):
|
||||
self.path = env['PATH_INFO']
|
||||
self.method = env['REQUEST_METHOD']
|
||||
self.path = env['PATH_INFO'] or '/'
|
||||
self.headers = headers = {}
|
||||
|
||||
# Extract HTTP headers
|
||||
@@ -19,6 +20,15 @@ class Request:
|
||||
|
||||
headers['HOST'] = host
|
||||
|
||||
self.app = env['SCRIPT_NAME']
|
||||
self.query_string = env['QUERY_STRING']
|
||||
|
||||
if 'CONTENT_TYPE' in env:
|
||||
headers['CONTENT_TYPE'] = env['CONTENT_TYPE']
|
||||
|
||||
if 'CONTENT_LENGTH' in env:
|
||||
headers['CONTENT_LENGTH'] = env['CONTENT_LENGTH']
|
||||
|
||||
def get_header(self, name, default=None):
|
||||
"""Return a header value as a string
|
||||
|
||||
@@ -29,9 +39,12 @@ class Request:
|
||||
|
||||
headers = self.headers
|
||||
|
||||
# Optimize for the header existing in most cases
|
||||
# Use try..except to optimize for the header existing in most cases
|
||||
try:
|
||||
return headers[name.upper()]
|
||||
# Don't take the time to cache beforehand, using HTTP naming.
|
||||
# Will be faster, assuming that most headers are looked up only
|
||||
# once, and not all headers will be requested.
|
||||
return headers[name.upper().replace('-', '_')]
|
||||
except KeyError as e:
|
||||
return default
|
||||
|
||||
|
||||
@@ -57,12 +57,12 @@ def rand_string(min, max):
|
||||
return ''.join([c for c in RandChars(min, max)])
|
||||
|
||||
def create_environ(path='/', query_string='', protocol='HTTP/1.1', port='80',
|
||||
headers=None):
|
||||
headers=None, script=''):
|
||||
|
||||
env = {
|
||||
'SERVER_PROTOCOL': protocol,
|
||||
'SERVER_SOFTWARE': 'gunicorn/0.17.0',
|
||||
'SCRIPT_NAME': '',
|
||||
'SCRIPT_NAME': script,
|
||||
'REQUEST_METHOD': 'GET',
|
||||
'PATH_INFO': path,
|
||||
'QUERY_STRING': query_string,
|
||||
@@ -81,6 +81,13 @@ def create_environ(path='/', query_string='', protocol='HTTP/1.1', port='80',
|
||||
|
||||
if headers is not None:
|
||||
for name, value in headers.iteritems():
|
||||
env['HTTP_' + name.upper()] = value.strip()
|
||||
name = name.upper().replace('-', '_')
|
||||
|
||||
if name == 'CONTENT_TYPE':
|
||||
env[name] = value.strip()
|
||||
elif name == 'CONTENT_LENGTH':
|
||||
env[name] = value.strip()
|
||||
else:
|
||||
env['HTTP_' + name.upper()] = value.strip()
|
||||
|
||||
return env
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import testtools
|
||||
import random
|
||||
|
||||
from testtools.matchers import Equals, MatchesRegex, Contains, Not
|
||||
|
||||
import falcon
|
||||
|
||||
49
test/test_req_vars.py
Normal file
49
test/test_req_vars.py
Normal file
@@ -0,0 +1,49 @@
|
||||
import testtools
|
||||
from testtools.matchers import Equals, MatchesRegex, Contains, Not
|
||||
|
||||
import falcon
|
||||
from falcon.request import Request
|
||||
import test.helpers as helpers
|
||||
|
||||
class TestReqVars(helpers.TestSuite):
|
||||
|
||||
def prepare(self):
|
||||
qs = '?marker=deadbeef&limit=10'
|
||||
headers = {
|
||||
'Content-Type': 'text/plain',
|
||||
'Content-Length': '4829'
|
||||
}
|
||||
|
||||
self.req = Request(helpers.create_environ(script='/test',
|
||||
path='/hello',
|
||||
query_string=qs,
|
||||
headers=headers))
|
||||
|
||||
def test_reconstruct_url(self):
|
||||
req = self.req
|
||||
|
||||
host = req.get_header('host')
|
||||
app = req.app
|
||||
path = req.path
|
||||
query_string = req.query_string
|
||||
|
||||
expected_url = 'http://falconer/test/hello?marker=deadbeef&limit=10'
|
||||
actual_url = ''.join(['http://', host, app, path, query_string])
|
||||
self.assertThat(actual_url, Equals(expected_url))
|
||||
|
||||
def test_empty_path(self):
|
||||
self.req = Request(helpers.create_environ(path=''))
|
||||
self.assertThat(self.req.path, Equals('/'))
|
||||
|
||||
def test_content_type(self):
|
||||
self.assertThat(self.req.get_header('content-type'),
|
||||
Equals('text/plain'))
|
||||
|
||||
def test_content_length(self):
|
||||
self.assertThat(self.req.get_header('content-length'),
|
||||
Equals('4829'))
|
||||
|
||||
def test_http_request_method(self):
|
||||
self.assertThat(self.req.method, Equals('GET'))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user