improve content-length handling. Chunked encoding is only used when not

content-length is provided and http version >= 1.1. In other case send
until we content is empty.

Also HTTP don't expect we can send more than content-length if header is
set, so discard any content if we have already send the expected
lenghth. (spotted by @GrahamDumpleton)

reviewed and OK by @davisp. More readable function by @davisp
This commit is contained in:
benoitc 2011-01-09 13:54:24 +01:00
parent cd726f6d8c
commit 39f5d313f7

View File

@ -117,6 +117,9 @@ class Response(object):
self.should_close = req.should_close() self.should_close = req.should_close()
self.headers = [] self.headers = []
self.headers_sent = False self.headers_sent = False
self.clength = -1
#self.te = None
self.sent = 0
def force_close(self): def force_close(self):
self.should_close = True self.should_close = True
@ -133,17 +136,19 @@ class Response(object):
self.status = status self.status = status
self.process_headers(headers) self.process_headers(headers)
self.chunked = self.is_chunked()
return self.write return self.write
def process_headers(self, headers): def process_headers(self, headers):
for name, value in headers: for name, value in headers:
assert isinstance(name, basestring), "%r is not a string" % name assert isinstance(name, basestring), "%r is not a string" % name
if util.is_hoppish(name): lname = name.lower().strip()
lname = name.lower().strip() if lname == "content-length":
if lname == "transfer-encoding": self.clength = int(value)
if value.lower().strip() == "chunked": elif util.is_hoppish(name):
self.chunked = True #if lname == "transfer-encoding":
elif lname == "connection": # self.te = value.lower().strip()
if lname == "connection":
# handle websocket # handle websocket
if value.lower().strip() != "upgrade": if value.lower().strip() != "upgrade":
continue continue
@ -152,13 +157,26 @@ class Response(object):
continue continue
self.headers.append((name.strip(), str(value).strip())) self.headers.append((name.strip(), str(value).strip()))
def is_chunked(self):
# maybe we should do this test if users expect this header
# to force chunked encoding
#if self.te == "chunked" and self.req.version > (1, 0):
# return True
if self.clength:
return False
elif self.req.version <= (1,0):
return False
return True
def default_headers(self): def default_headers(self):
connection = "keep-alive" connection = "keep-alive"
if self.should_close: if self.should_close:
connection = "close" connection = "close"
return [ return [
"HTTP/1.1 %s\r\n" % self.status, "HTTP/%s.%s %s\r\n" % (self.req.version[0],
self.req.version[1], self.status),
"Server: %s\r\n" % self.version, "Server: %s\r\n" % self.version,
"Date: %s\r\n" % util.http_date(), "Date: %s\r\n" % util.http_date(),
"Connection: %s\r\n" % connection "Connection: %s\r\n" % connection
@ -175,6 +193,19 @@ class Response(object):
def write(self, arg): def write(self, arg):
self.send_headers() self.send_headers()
assert isinstance(arg, basestring), "%r is not a string." % arg assert isinstance(arg, basestring), "%r is not a string." % arg
arglen = len(arg)
tosend = arglen
if self.clength is not None:
if self.sent >= self.clength:
# Never write more than self.clength bytes
return
tosend = min(self.clength - self.sent, tosend)
if tosend < arglen:
arg = arg[:tosend]
self.sent += tosend
util.write(self.sock, arg, self.chunked) util.write(self.sock, arg, self.chunked)
def close(self): def close(self):