* code: numeric HTTP status code, e.g. 200 or 404
+ * reason: human-readable reason phrase describing the status code
+
* headers: httputil.HTTPHeaders object
* buffer: cStringIO object for response body
- * body: respose body as string (created on demand from self.buffer)
+ * body: response body as string (created on demand from self.buffer)
* error: Exception object, if any
plus 'queue', which is the delay (if any) introduced by waiting for
a slot under AsyncHTTPClient's max_clients setting.
"""
- def __init__(self, request, code, headers=None, buffer=None,
+ def __init__(self, request, code, reason=None, headers=None, buffer=None,
effective_url=None, error=None, request_time=None,
time_info=None):
self.request = request
self.code = code
+ self.reason = reason
if headers is not None:
self.headers = headers
else:
def _on_headers(self, data):
data = native_str(data.decode("latin1"))
first_line, _, header_data = data.partition("\n")
- match = re.match("HTTP/1.[01] ([0-9]+)", first_line)
+ match = re.match("HTTP/1.[01] ([0-9]+) ([^\r]*)", first_line)
assert match
self.code = int(match.group(1))
+ self.reason = match.group(2)
self.headers = HTTPHeaders.parse(header_data)
if "Content-Length" in self.headers:
else:
buffer = BytesIO(data) # TODO: don't require one big string?
response = HTTPResponse(original_request,
- self.code, headers=self.headers,
+ self.code, reason=self.reason,
+ headers=self.headers,
request_time=time.time() - self.start_time,
buffer=buffer,
effective_url=self.request.url)
raise
+class StatusHandler(RequestHandler):
+ def get(self):
+ reason = self.request.arguments.get('reason', [])
+ self.set_status(int(self.get_argument('code')), reason=reason[0] if reason else None)
+
+
# This test is shared with wsgi_test.py
class WSGISafeWebTest(AsyncHTTPTestCase, LogTrapTestCase):
COOKIE_SECRET = "WebTest.COOKIE_SECRET"
url("/multi_header", MultiHeaderHandler),
url("/redirect", RedirectHandler),
url("/header_injection", HeaderInjectionHandler),
+ url("/status", StatusHandler),
]
return urls
response = self.fetch("/header_injection")
self.assertEqual(response.body, b("ok"))
+ def test_status(self):
+ response = self.fetch("/status?code=304")
+ self.assertEqual(response.code, 304)
+ self.assertEqual(response.reason, "Not Modified")
+ response = self.fetch("/status?code=304&reason=Foo")
+ self.assertEqual(response.code, 304)
+ self.assertEqual(response.reason, "Foo")
+ response = self.fetch("/status?code=682&reason=Bar")
+ self.assertEqual(response.code, 682)
+ self.assertEqual(response.reason, "Bar")
+ response = self.fetch("/status?code=682")
+ self.assertEqual(response.code, 500)
+
class NonWSGIWebTests(AsyncHTTPTestCase, LogTrapTestCase):
def get_app(self):
self.set_header("Connection", "Keep-Alive")
self._write_buffer = []
self._status_code = 200
+ self._reason = None
def set_default_headers(self):
"""Override this to set HTTP headers at the beginning of the request.
"""
pass
- def set_status(self, status_code):
- """Sets the status code for our response."""
- assert status_code in httplib.responses
+ def set_status(self, status_code, reason=None):
+ """Sets the status code for our response.
+
+ :arg int status_code: Response status code. If `reason` is ``None``,
+ it must be present in `httplib.responses`.
+ :arg string reason: Human-readable reason phrase describing the status
+ code. If ``None``, it will be filled in from `httplib.responses`.
+ """
+ assert reason is not None or status_code in httplib.responses
self._status_code = status_code
+ self._reason = reason
def get_status(self):
"""Returns the status code for our response."""
self._handle_request_exception(e)
def _generate_headers(self):
+ reason = self._reason
+ if reason is None:
+ reason = httplib.responses[self._status_code]
lines = [utf8(self.request.version + " " +
str(self._status_code) +
- " " + httplib.responses[self._status_code])]
+ " " + reason)]
lines.extend([(utf8(n) + b(": ") + utf8(v)) for n, v in
itertools.chain(self._headers.iteritems(), self._list_headers)])
if hasattr(self, "_new_cookie"):
def __call__(self, environ, start_response):
handler = web.Application.__call__(self, HTTPRequest(environ))
assert handler._finished
- status = str(handler._status_code) + " " + \
- httplib.responses[handler._status_code]
+ reason = handler._reason
+ if reason is None:
+ reason = httplib.responses[handler._status_code]
+ status = str(handler._status_code) + " " + reason
headers = handler._headers.items() + handler._list_headers
if hasattr(handler, "_new_cookie"):
for cookie in handler._new_cookie.values():