gunicorn/gunicorn/http/message.py
2010-06-03 16:11:16 -04:00

188 lines
5.5 KiB
Python

import os
import re
import urlparse
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
from body import ChunkedReader, LengthReader, EOFReader, Body
from errors import *
class Message(object):
def __init__(self, unreader):
self.unreader = unreader
self.version = None
self.headers = []
self.trailers = []
self.body = None
self.hdrre = re.compile("[\x00-\x1F\x7F()<>@,;:\[\]={} \t\\\\\"]")
unused = self.parse(self.unreader)
self.unreader.unread(unused)
self.set_body_reader()
def parse(self):
raise NotImplementedError()
def parse_headers(self, data):
headers = []
# Split lines on \r\n keeping the \r\n on each line
lines = []
while len(data):
pos = data.find("\r\n")
if pos < 0:
lines.append(data)
data = ""
else:
lines.append(data[:pos+2])
data = data[pos+2:]
# Parse headers into key/value pairs paying attention
# to continuation lines.
while len(lines):
# Parse initial header name : value pair.
curr = lines.pop(0)
if curr.find(":") < 0:
raise InvalidHeader(curr.strip())
name, value = curr.split(":", 1)
name = name.rstrip(" \t").upper()
if self.hdrre.search(name):
raise InvalidHeaderName(name)
name, value = name.strip(), [value.lstrip()]
# Consume value continuation lines
while len(lines) and lines[0].startswith((" ", "\t")):
value.append(lines.pop(0))
value = ''.join(value).rstrip()
headers.append((name, value))
return headers
def set_body_reader(self):
chunked = False
clength = None
for (name, value) in self.headers:
if name.upper() == "CONTENT-LENGTH":
try:
clength = int(value)
except ValueError:
clenth = None
elif name.upper() == "TRANSFER-ENCODING":
chunked = value.lower() == "chunked"
if chunked:
self.body = Body(ChunkedReader(self, self.unreader))
elif clength is not None:
self.body = Body(LengthReader(self.unreader, clength))
else:
self.body = Body(EOFReader(self.unreader))
def should_close(self):
for (h, v) in self.headers:
if h.lower() == "connection":
if v.lower().strip() == "close":
return True
elif v.lower().strip() == "keep-alive":
return False
return self.version <= (1, 0)
class Request(Message):
def __init__(self, unreader):
self.methre = re.compile("[A-Z0-9$-_.]{3,20}")
self.versre = re.compile("HTTP/(\d+).(\d+)")
self.method = None
self.uri = None
self.scheme = None
self.host = None
self.port = 80
self.path = None
self.query = None
self.fragment = None
super(Request, self).__init__(unreader)
def get_data(self, unreader, buf, stop=False):
data = unreader.read()
if not data:
if stop:
raise StopIteration()
raise NoMoreData(buf.getvalue())
buf.write(data)
def parse(self, unreader):
buf = StringIO()
self.get_data(unreader, buf, stop=True)
# Request line
idx = buf.getvalue().find("\r\n")
while idx < 0:
self.get_data(unreader, buf)
idx = buf.getvalue().find("\r\n")
self.parse_request_line(buf.getvalue()[:idx])
rest = buf.getvalue()[idx+2:] # Skip \r\n
buf.truncate(0)
buf.write(rest)
# Headers
idx = buf.getvalue().find("\r\n\r\n")
done = buf.getvalue()[:2] == "\r\n"
while idx < 0 and not done:
self.get_data(unreader, buf)
idx = buf.getvalue().find("\r\n\r\n")
done = buf.getvalue()[:2] == "\r\n"
if done:
self.unreader.unread(buf.getvalue()[2:])
return ""
self.headers = self.parse_headers(buf.getvalue()[:idx])
ret = buf.getvalue()[idx+4:]
buf.truncate(0)
return ret
def parse_request_line(self, line):
bits = line.split(None, 2)
if len(bits) != 3:
raise InvalidRequestLine(line)
# Method
if not self.methre.match(bits[0]):
raise InvalidRequestMethod(bits[0])
self.method = bits[0].upper()
# URI
self.uri = bits[1]
parts = urlparse.urlparse(bits[1])
self.scheme = parts.scheme or None
self.host = parts.netloc or None
if parts.port is None:
self.port = 80
else:
self.host = self.host.rsplit(":", 1)[0]
self.port = parts.port
self.path = parts.path or None
self.query = parts.query or None
self.fragment = parts.fragment or None
# Version
match = self.versre.match(bits[2])
if match is None:
raise InvalidHTTPVersion(bits[2])
self.version = (int(match.group(1)), int(match.group(2)))
def set_body_reader(self):
super(Request, self).set_body_reader()
if isinstance(self.body.reader, EOFReader):
self.body = Body(LengthReader(self.unreader, 0))