Fix PEP 333 compliance for the write callable.

This commit is contained in:
Paul J. Davis 2010-03-23 14:50:16 -04:00
parent e37bfa3c4e
commit 014d711bd7
4 changed files with 60 additions and 52 deletions

View File

@ -16,6 +16,7 @@ from urllib import unquote
from gunicorn import __version__
from gunicorn.http.parser import Parser
from gunicorn.http.response import Response
from gunicorn.http.tee import TeeInput
from gunicorn.util import CHUNK_SIZE
@ -43,7 +44,7 @@ class Request(object):
def __init__(self, socket, client_address, server_address, conf):
self.debug = conf['debug']
self.conf = conf
self._sock = socket
self.socket = socket
self.client_address = client_address
self.server_address = server_address
@ -51,8 +52,8 @@ class Request(object):
self.response_headers = []
self._version = 11
self.parser = Parser.parse_request()
self.start_response_called = False
self.log = logging.getLogger(__name__)
self.response = None
self.response_chunked = False
self.headers_sent = False
@ -60,12 +61,12 @@ class Request(object):
environ = {}
headers = []
buf = StringIO()
data = self._sock.recv(CHUNK_SIZE)
data = self.socket.recv(CHUNK_SIZE)
buf.write(data)
buf2 = self.parser.filter_headers(headers, buf)
if not buf2:
while True:
data = self._sock.recv(CHUNK_SIZE)
data = self.socket.recv(CHUNK_SIZE)
if not data:
break
buf.write(data)
@ -77,14 +78,14 @@ class Request(object):
self.log.debug("Headers:\n%s" % headers)
if self.parser.headers_dict.get('Expect','').lower() == "100-continue":
self._sock.send("HTTP/1.1 100 Continue\r\n\r\n")
self.socket.send("HTTP/1.1 100 Continue\r\n\r\n")
if not self.parser.content_len and not self.parser.is_chunked:
wsgi_input = TeeInput(self._sock, self.parser, StringIO(),
wsgi_input = TeeInput(self.socket, self.parser, StringIO(),
self.conf)
content_length = "0"
else:
wsgi_input = TeeInput(self._sock, self.parser, buf2, self.conf)
wsgi_input = TeeInput(self.socket, self.parser, buf2, self.conf)
content_length = str(wsgi_input.len)
# This value should evaluate true if an equivalent application
@ -154,23 +155,15 @@ class Request(object):
environ[key] = value
return environ
def start_response(self, status, response_headers, exc_info=None):
def start_response(self, status, headers, exc_info=None):
if exc_info:
try:
if self.headers_sent:
if self.response and self.response.sent_headers:
raise exc_info[0], exc_info[1], exc_info[2]
finally:
exc_info = None
elif self.start_response_called:
elif self.response is not None:
raise AssertionError("Response headers already set!")
self.response_status = status
for name, value in response_headers:
if name.lower() == "transfer-encoding":
if value.lower() == "chunked":
self.response_chunked = True
if not isinstance(value, basestring):
value = str(value)
self.response_headers.append((name.title(), value.strip()))
self.start_response_called = True
self.response = Response(self, status, headers)
return self.response.write

View File

@ -3,43 +3,46 @@
# This file is part of gunicorn released under the MIT license.
# See the NOTICE for more information.
from gunicorn.util import http_date, write, write_chunk
from gunicorn.util import close, http_date, write, write_chunk, is_hoppish
class Response(object):
def __init__(self, sock, response, req):
def __init__(self, req, status, headers):
self.req = req
self._sock = sock
self.data = response
self.headers = req.response_headers or []
self.status = req.response_status
self.SERVER_VERSION = req.SERVER_VERSION
self.chunked = req.response_chunked
self.version = req.SERVER_VERSION
self.status = status
self.chunked = False
self.headers = []
self.headers_sent = False
def default_headers(self):
return [
"HTTP/1.1 %s\r\n" % self.status,
"Server: %s\r\n" % self.SERVER_VERSION,
for name, value in headers:
assert isinstance(name, basestring), "%r is not a string" % name
assert isinstance(value, basestring), "%r is not a string" % value
assert not is_hoppish(name), "%s is a hop-by-hop header." % name
if name.lower().strip() == "transfer-encoding":
if value.lower().strip() == "chunked":
self.chunked = True
self.headers.append((name.strip(), value.strip()))
def send_headers(self):
if self.headers_sent:
return
tosend = [
"HTP/1.1 %s\r\n" % self.status,
"Server: %s\r\n" % self.version,
"Date: %s\r\n" % http_date(),
"Connection: close\r\n"
]
tosend.extend(["%s: %s\r\n" % (n, v) for n, v in self.headers])
write(self.req.socket, "%s\r\n" % "".join(tosend))
self.headers_sent = True
def send(self):
# send headers
resp_head = self.default_headers()
resp_head.extend(["%s: %s\r\n" % (n, v) for n, v in self.headers])
write(self._sock, "%s\r\n" % "".join(resp_head))
self.req.headers_sent = True
def write(self, arg):
self.send_headers()
assert isinstance(arg, basestring), "%r is not a string." % arg
write(self.req.socket, arg, self.chunked)
last_chunk = None
for chunk in self.data:
last_chunk = chunk
write(self._sock, chunk, self.chunked)
if self.chunked and last_chunk != "":
# send last chunk
write_chunk(self._sock, "")
if hasattr(self.data, "close"):
self.data.close()
def close(self):
if self.chunked:
write_chunk(self.socket, "")
close(self.req.socket)

View File

@ -29,6 +29,11 @@ weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
monthname = [None,
'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
hop_headers = set("""
connection keep-alive proxy-authenticate proxy-authorization
te trailers transfer-encoding upgrade
""".split())
try:
from setproctitle import setproctitle
@ -202,3 +207,6 @@ def to_bytestring(s):
return s.encode('utf-8')
else:
return s
def is_hoppish(header):
return header.lower().strip() in hop_headers

View File

@ -158,7 +158,12 @@ class Worker(object):
if not environ or not req.parser.status_line:
return
response = self.app(environ, req.start_response)
respiter = self.app(environ, req.start_response)
for item in respiter:
req.response.write(item)
req.response.close()
if hasattr(respiter, "close"):
respiter.close()
except Exception, e:
# Only send back traceback in HTTP in debug mode.
if not self.debug:
@ -166,7 +171,6 @@ class Worker(object):
util.write_error(client, traceback.format_exc())
return
http.Response(client, response, req).send()
except socket.error, e:
if e[0] != errno.EPIPE:
self.log.exception("Error processing request.")