feat(Request): Pass through relevant WSGI environ variables

This commit is contained in:
kurt-griffiths
2013-01-07 12:07:05 -05:00
parent d12aa8d021
commit cd1c3e2045
5 changed files with 78 additions and 9 deletions

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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
View 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'))