Set Content-Length header

This commit is contained in:
Kurt Griffiths
2013-01-02 11:22:33 -05:00
parent 54ca5f3fd6
commit 03bf72fbb5
8 changed files with 101 additions and 23 deletions

View File

@@ -1,3 +1,6 @@
from falcon.request import Request
from falcon.response import Response
from falcon.default_request_handlers import *
from falcon.status_codes import *
@@ -5,25 +8,25 @@ from falcon.status_codes import *
# TODO: log exceptions, trace execution, etc.
class Api:
"""WSGI application implementing a Falcon web API"""
"""Provides routing and such for building a web service application"""
def __init__(self):
self.routes = {}
def __call__(self, environ, start_response):
"""WSGI protocol handler"""
# PERF: Use literal constructor for dicts
# PERF: Don't use multi-assignment
ctx = {}
req = {}
resp = {}
# TODO: What other things does req need?
req['path'] = path = environ['PATH_INFO']
# TODO
# ctx.update(global_ctx_for_route)
path = environ['PATH_INFO']
req = Request(path)
resp = Response()
# PERF: Use try...except blocks when the key usually exists
try:
# TODO: Figure out a way to use codegen to make a state machine,
@@ -34,19 +37,36 @@ class Api:
try:
handler(ctx, req, resp)
except:
except Exception as ex:
# TODO
pass
resp.set_header('Content-Type', 'text/plain')
# Consider refactoring into functions, but be careful since that can
# affect performance...
body = resp.body
content_length = 0
try:
start_response(resp['status'], [('Content-Type', 'text/plain')])
except:
if body is not None:
content_length = len(body)
except Exception as ex:
#TODO
pass
resp.set_header('Content-Length', content_length)
headers = resp._wsgi_headers()
try:
start_response(resp.status, headers)
except Exception as ex:
# TODO
pass
# PERF: Can't predict ratio of empty body to nonempty, so use
# "in" which is a good all-around performer.
return [resp['body']] if 'body' in resp else []
return [body] if body is not None else []
def add_route(self, uri_template, handler):
self.routes[uri_template] = handler

View File

@@ -1,4 +1,4 @@
from status_codes import *
def path_not_found_handler(ctx, req, resp):
resp['status'] = HTTP_404
resp.status = HTTP_404

5
falcon/request.py Normal file
View File

@@ -0,0 +1,5 @@
class Request:
__slots__ = ('path')
def __init__(self, path):
self.path = path

16
falcon/response.py Normal file
View File

@@ -0,0 +1,16 @@
class Response:
__slots__ = ('status', '_headers', 'body', 'stream')
def __init__(self):
self.status = None
self._headers = {}
self.body = None
self.stream = None
def set_header(self, name, value):
self._headers[name] = str(value)
#TODO: Add some helper functions and test them
def _wsgi_headers(self):
return [t for t in self._headers.items()]

View File

@@ -1,4 +1,6 @@
import inspect
import random
import testtools
import falcon
@@ -32,6 +34,26 @@ class TestSuite(testtools.TestCase):
def _simulate_request(self, path):
self.api(create_environ(path), self.srmock)
class RandChars:
_chars = 'abcdefghijklqmnopqrstuvwxyz0123456789 \n\t!@#$%^&*()-_=+`~<>,.?/'
def __init__(self, min, max):
self.target = random.randint(min, max)
self.counter = 0
def __iter__(self):
return self
def next(self):
if self.counter < self.target:
self.counter += 1
return self._chars[random.randint(0, len(self._chars)-1)]
else:
raise StopIteration
def rand_string(min, max):
return ''.join([c for c in RandChars(min, max)])
def create_environ(path='/', query_string=''):
return {
'SERVER_SOFTWARE': 'WSGIServer/0.1 Python/2.7.3',

View File

@@ -1,5 +1,7 @@
import testtools
from testtools.matchers import Equals, MatchesRegex
import random
from testtools.matchers import Equals, MatchesRegex, Contains
import falcon
import test.helpers as helpers
@@ -7,10 +9,15 @@ import test.helpers as helpers
# TODO: Framework adds keep-alive and either chunked or content-length
# TODO: Test setting various headers, and seeing that Falcon doesn't override custom ones, but will set them if not present (or not?)
# TODO: Test correct content length is set
# TODO: Test calling set_header with bogus arguments
# TODO: The order in which header fields with differing field names are received is not significant. However, it is "good practice" to send general-header fields first, followed by request-header or response- header fields, and ending with the entity-header fields.
# TODO: Helper functions for getting and setting common headers
# TODO: Any default headers, such as content-type?
# TODO: if status is 1xx, 204, or 404 ignore body, don't set content-length
class RequestHandler:
sample_status = "200 OK"
sample_body = "Hello World!"
sample_body = helpers.rand_string(0, 128 * 1024)
def __init__(self):
self.called = False
@@ -20,8 +27,8 @@ class RequestHandler:
self.ctx, self.req, self.resp = ctx, req, resp
resp['status'] = falcon.HTTP_200
resp['body'] = self.sample_body
resp.status = falcon.HTTP_200
resp.body = self.sample_body
class TestHeaders(helpers.TestSuite):
@@ -31,3 +38,10 @@ class TestHeaders(helpers.TestSuite):
def test_auto_headers(self):
self._simulate_request(self.test_route)
headers = self.srmock.headers
content_length = ('Content-Length', str(len(self.on_hello.sample_body)))
self.assertThat(headers, Contains(content_length))

View File

@@ -16,8 +16,8 @@ class HelloRequestHandler:
self.ctx, self.req, self.resp = ctx, req, resp
resp['status'] = falcon.HTTP_200
resp['body'] = self.sample_body
resp.status = falcon.HTTP_200
resp.body = self.sample_body
class TestHelloWorld(helpers.TestSuite):
@@ -38,8 +38,6 @@ class TestHelloWorld(helpers.TestSuite):
self._simulate_request(self.test_route)
resp = self.on_hello.resp
self.assertTrue('status' in resp)
self.assertThat(resp['status'], Equals(self.on_hello.sample_status))
self.assertThat(resp.status, Equals(self.on_hello.sample_status))
self.assertTrue('body' in resp)
self.assertThat(resp['body'], Equals(self.on_hello.sample_body))
self.assertThat(resp.body, Equals(self.on_hello.sample_body))

View File

@@ -1,5 +1,7 @@
* Test sending/receiving various status codes
* Ensure req dict has things the app needs, including path, user agent, various headers.
* Test default http status code (200 OK?)
* Test compiling routes, throwing on invalid routes (such as missing initial forward slash or non-ascii)
* Test setting the body to a stream, rather than a string (and content-length set to chunked?)
* Test custom error handlers - customizing error document at least
@@ -14,4 +16,5 @@
* Test passing a shared dict to each mock call (e.g., db connections, config)...and that it is passed to the request handler correctly
* Test pre/post filters
* Test error handling with standard response (for all error classes?)
* Test passing bogus data to create_api and/or add_route