def parse_request_line(self, line): bits = line.split(None, 2) if len(bits) != 3: raise ParseError("Invalid request line.") # Method if not self.methre.match(bits[0]): raise ParseError("Invalid request line. Bad method.") self.method = bits[0].upper() # URI self.uri = bits[1] parts = uri.parse(bits[1]) self.scheme = parts["scheme"] or None self.userinfo = parts["userinfo"] or None self.host = parts["host"] or None if not parts["port"]: if self.scheme == b("http"): self.port = b("80") elif self.scheme == b("https"): self.port = b("443") else: self.port = None 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 ParseError("Invalid HTTP version.") self.version = (int(match.group(1)), int(match.group(2)))
def should_close(self): for (h, v) in self.headers: if h.lower() == b("connection"): if v.lower().strip() == b("close"): return True elif v.lower().strip() == b("keep-alive"): return False return self.version <= (1, 0)
def app(self, environ): status = 200 body = pprint.pformat(environ).encode('latin-1') headers = [ (b("Content-Type"), b("text/plain")), (b("Content-Length"), b(str(len(body)))) ] return status, headers, [body]
def respond(self, status, headers, body): front = [b("HTTP/1.1 %d %s" % (status, STATUS_CODES[status]))] for name, value in headers: front.append(name + b(": ") + value) front.extend([b(""), b("")]) self.started = True self.socket.send(b("\r\n").join(front)) for data in body: self.socket.send(data)
def __init__(self, node, n=0, **params): super(Rep, self).__init__(**params) self.node = node if n == 0: self.n = b("*") elif n == 1: self.n = b("+") else: raise ValueError(n)
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(b("\r\n")) if pos < 0: lines.append(data) data = b("") 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(b(":")) < 0: raise ParseError("Invalid header. No colon separator found.") name, value = curr.split(b(":"), 1) name = name.rstrip(b(" \t")).upper() if self.hdrre.search(name): raise ParseError("Invalid header. Invalid bytes.") name, value = name.strip(), [value.lstrip()] # Consume value continuation lines while len(lines) and lines[0].startswith((b(" "), b("\t"))): value.append(lines.pop(0)) value = b("").join(value).rstrip() headers.append((name, value)) return headers
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(b("\r\n")) if pos < 0: lines.append(data) data = b("") 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(b(":")) < 0: raise ParseError("Invalid header. No colon separator found.") name, value = curr.split(b(":"), 1) name = name.rstrip(b(" \t")).upper() if self.hdrre.search(name): raise ParseError("Invalid header. Invalid bytes.") name, value = name.strip(), [value.lstrip()] # Consume value continuation lines while len(lines) and lines[0].startswith((b(" "), b("\t"))): value.append(lines.pop(0)) value = b("").join(value).rstrip() headers.append((name, value)) return headers
def check_environ(environ): # Keys covered in specific validaotors: # http.body, wsgi.errors, wsgi.upgrade, wsgi.upgraded assert_(envrion["wsgi.version"] == (2, 0), "Invalid wsgi version.") for key in environ.keys(): assert_(type(key) is STRING_TYPE, "Invalid environ key type.") basic_key_types = { BOOL_TYPE: """ wsgi.multithread wsg.multiprocess """.split(), INT_TYPE: """ conn.server_port conn.remote_port """.split(), BYTES_TYPE: """ http.method http.uri.raw http.uri.path http.uri.query_string wsgi.url_scheme wsgi.script_name conn.server_name conn.remote_ip """.split() } for tp, names in basic_key_types: for key in names: assert_(type(environ[key]) is tp, "Invalid value type.") if len(environ["wsgi.script_name"]): assert_(environ["wsgi.script_name"][0] == b("/"), "wsgi.script_name doesn't start with '/'")
def handle(self, app): try: response = app(self.environ) if response is None: return False (status, headers, body) = response self.respond(status, headers, body) except: if self.started: raise tb = traceback.format_exc().encode("ascii", "replace") headers = [ (b("Content-Type"), b("text/plain")), (b("Content-Length"), b(str(len(tb)))) ] self.respond(500, headers, [tb]) return True
def parse(self, unreader): buf = BufferIO() self._get_data(unreader, buf, stop=True) # Request line idx = buf.getvalue().find(b("\r\n")) while idx < 0: self._get_data(unreader, buf) idx = buf.getvalue().find(b("\r\n")) self.parse_request_line(buf.getvalue()[:idx]) rest = buf.getvalue()[idx+2:] # Skip \r\n buf.truncate(0) buf.seek(0) buf.write(rest) # Headers idx = buf.getvalue().find(b("\r\n\r\n")) done = buf.getvalue()[:2] == b("\r\n") while idx < 0 and not done: self._get_data(unreader, buf) idx = buf.getvalue().find(b("\r\n\r\n")) done = buf.getvalue()[:2] == b("\r\n") if done: self.unreader.unread(buf.getvalue()[2:]) return b("") self.headers = self.parse_headers(buf.getvalue()[:idx]) ret = buf.getvalue()[idx+4:] buf.truncate(0) buf.seek(0) return ret
def parse(self, unreader): buf = BufferIO() self._get_data(unreader, buf, stop=True) # Request line idx = buf.getvalue().find(b("\r\n")) while idx < 0: self._get_data(unreader, buf) idx = buf.getvalue().find(b("\r\n")) self.parse_request_line(buf.getvalue()[:idx]) rest = buf.getvalue()[idx + 2:] # Skip \r\n buf.truncate(0) buf.seek(0) buf.write(rest) # Headers idx = buf.getvalue().find(b("\r\n\r\n")) done = buf.getvalue()[:2] == b("\r\n") while idx < 0 and not done: self._get_data(unreader, buf) idx = buf.getvalue().find(b("\r\n\r\n")) done = buf.getvalue()[:2] == b("\r\n") if done: self.unreader.unread(buf.getvalue()[2:]) return b("") self.headers = self.parse_headers(buf.getvalue()[:idx]) ret = buf.getvalue()[idx + 4:] buf.truncate(0) buf.seek(0) return ret
def check_status(status): assert_(type(status) == type(b("")), "Status must be a bytes object.") status_code = status.split(None, 1)[0] assert_(len(status) == 3, "Status code must be three digits.") assert_(int(status) >= 100, "Status code must be >= 100") assert_(int(parts[0]) > 100, "Invalid status respones.") assert_(len(parts) == 2, "No status message provided.") assert_(len(status.split(None, 1)) == 2, "The status should include a status message.")
def set_body_reader(self): chunked = False clength = 0 for (name, value) in self.headers: if name.lower() == b("content-length"): try: clength = int(value) except ValueError: clength = 0 elif name.lower() == b("transfer-encoding"): chunked = value.lower() == b("chunked") elif name.lower() == b("sec-websocket-key1"): clength = 8 if chunked: self.body = Body(ChunkedReader(self.unreader, self)) else: self.body = Body(LengthReader(self.unreader, clength))
def parse(value): ret = { "scheme": None, "userinfo": None, "host": None, "port": None, "path": None, "query": None, "fragment": None } if value == b("*"): ret["path"] = b("*") return ret for pat in patterns: match = pat.match(value) if match: ret.update(match.groupdict()) return ret raise ValueError(b("Invalid HTTP URI: ") + value)
def render(self): ret = [b("(") + self.name] for i, n in enumerate(self.nodes): ret.append(b("(?:") + n.render() + b(")")) if i + 1 < len(self.nodes): ret.append(b("|")) ret.append(b(")")) return b("").join(ret)
def render(self): ret = [b("(") + self.name] for i, n in enumerate(self.nodes): ret.append(b("(?:") + n.render() + b(")")) if i+1 < len(self.nodes): ret.append(b("|")) ret.append(b(")")) return b("").join(ret)
def __init__(self, unreader): self.unreader = unreader self.methre = re.compile(b("[A-Z0-9$-_.]{3,20}")) self.versre = re.compile(b("HTTP/(\d+).(\d+)")) self.hdrre = re.compile(b("[\x00-\x1F\x7F()<>@,;:\[\]={} \t\\\\\"]")) self.method = None self.uri = None self.scheme = None self.userinfo = None self.host = None self.port = b("80") self.path = None self.query = None self.fragment = None self.version = None self.headers = [] self.trailers = [] self.body = None unused = self.parse(self.unreader) self.unreader.unread(unused) self.set_body_reader()
def render(self): ret = [b("(") + self.name] for n in self.nodes: ret.append(n.render()) ret.append(b(")")) return b("").join(ret)
def render(self): return b("[") + self.bytes + b("]")
def __init__(self, bytes): self.bytes = b(bytes)
def __init__(self, name=None): if name is None: self.name = b("?:") else: self.name = b("?P<") + b(name) + b(">")
def render(self): return b("(") + self.name + self.node.render() + b(")?")
def __init__(self, byte): self.byte = b(byte)
def render(self): rep = b("(?:") + self.node.render() + b(")") + self.n return b("(") + self.name + rep + b(")")
def compile(self): return re.compile(b("^") + self.render() + b("$"))
import re from wsgiref2.util import b BOOL_TYPE = type(True) INT_TYPE = type(1) BYTES_TYPE = type(b("")) STRING_TYPE = type("") HDR_NAME_RE = re.compile(b("[\x00-\x1F\x7F()<>@,;:\[\]={} \t\\\\\"]")) HDR_VALUE_RE = re.compile(b("\n[^ \t]")) def assert_(cond, *args): if not cond: raise AssertionError(*args) def check_environ(environ): # Keys covered in specific validaotors: # http.body, wsgi.errors, wsgi.upgrade, wsgi.upgraded assert_(envrion["wsgi.version"] == (2, 0), "Invalid wsgi version.") for key in environ.keys(): assert_(type(key) is STRING_TYPE, "Invalid environ key type.") basic_key_types = { BOOL_TYPE: """ wsgi.multithread wsg.multiprocess
def app(self, environ): status = 200 body = pprint.pformat(environ).encode('latin-1') headers = [(b("Content-Type"), b("text/plain")), (b("Content-Length"), b(str(len(body))))] return status, headers, [body]