Set Content-Length header
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
5
falcon/request.py
Normal file
@@ -0,0 +1,5 @@
|
||||
class Request:
|
||||
__slots__ = ('path')
|
||||
|
||||
def __init__(self, path):
|
||||
self.path = path
|
||||
16
falcon/response.py
Normal file
16
falcon/response.py
Normal 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()]
|
||||
@@ -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',
|
||||
|
||||
@@ -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))
|
||||
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user