From 9130cd63d36103055e6ec34bacca37e7532ef366 Mon Sep 17 00:00:00 2001 From: Aldo Cortesi Date: Sat, 9 Jun 2012 21:27:43 +1200 Subject: [PATCH] Significant cleanup of proxy internals. Dispense with the loose parsing of client requests that we had before. We now have service modes ("proxy" and "reverse proxy" for now), and we only accept requests that are appropriate for the mode we're in. --- libmproxy/proxy.py | 89 ++++++++++++++++++++++------------------------ 1 file changed, 42 insertions(+), 47 deletions(-) diff --git a/libmproxy/proxy.py b/libmproxy/proxy.py index 2d4ee9880..41f101d87 100644 --- a/libmproxy/proxy.py +++ b/libmproxy/proxy.py @@ -450,51 +450,7 @@ class ProxyHandler(SocketServer.StreamRequestHandler): self.rfile = FileLike(self.connection) self.wfile = FileLike(self.connection) - def read_request(self, client_conn): - line = self.rfile.readline() - if line == "\r\n" or line == "\n": # Possible leftover from previous message - line = self.rfile.readline() - if line == "": - return None - method, scheme, host, port, path, httpminor = parse_request_line(line) - if method == "CONNECT": - # FIXME: Discard additional headers sent to the proxy. Should I expose - # these to users? - while 1: - d = self.rfile.readline() - if d == '\r\n' or d == '\n': - break - self.wfile.write( - 'HTTP/1.1 200 Connection established\r\n' + - ('Proxy-agent: %s\r\n'%version.NAMEVERSION) + - '\r\n' - ) - self.wfile.flush() - certfile = self.find_cert(host, port) - self.convert_to_ssl(certfile) - method, scheme, host, port, path, httpminor = parse_request_line(self.rfile.readline()) - if scheme is None: - scheme = "https" - headers = read_headers(self.rfile) - if host is None and "host" in headers: - netloc = headers["host"][0] - if ':' in netloc: - host, port = string.split(netloc, ':') - port = int(port) - else: - host = netloc - if scheme == "https": - port = 443 - else: - port = 80 - port = int(port) - if host is None: - if self.config.reverse_proxy: - scheme, host, port = self.config.reverse_proxy - else: - # FIXME: We only specify the first part of the invalid request in this error. - # We should gather up everything read from the socket, and specify it all. - raise ProxyError(400, 'Invalid request: %s'%line) + def read_contents(self, client_conn, headers, httpminor): if "expect" in headers: # FIXME: Should be forwarded upstream expect = ",".join(headers['expect']) @@ -514,8 +470,47 @@ class ProxyHandler(SocketServer.StreamRequestHandler): client_conn.close = True if value == "keep-alive": client_conn.close = False - content = read_http_body(self.rfile, client_conn, headers, False, self.config.body_size_limit) - return flow.Request(client_conn, host, port, scheme, method, path, headers, content) + return read_http_body(self.rfile, client_conn, headers, False, self.config.body_size_limit) + + def read_request(self, client_conn): + line = self.rfile.readline() + if line == "\r\n" or line == "\n": # Possible leftover from previous message + line = self.rfile.readline() + if line == "": + return None + + if self.config.reverse_proxy: + scheme, host, port = self.config.reverse_proxy + method, path, httpmajor, httpminor = parse_init_http(line) + headers = read_headers(self.rfile) + content = self.read_contents(client_conn, headers, httpminor) + return flow.Request(client_conn, host, port, "http", method, path, headers, content) + elif line.startswith("CONNECT"): + host, port, httpmajor, httpminor = parse_init_connect(line) + # FIXME: Discard additional headers sent to the proxy. Should I expose + # these to users? + while 1: + d = self.rfile.readline() + if d == '\r\n' or d == '\n': + break + self.wfile.write( + 'HTTP/1.1 200 Connection established\r\n' + + ('Proxy-agent: %s\r\n'%version.NAMEVERSION) + + '\r\n' + ) + self.wfile.flush() + certfile = self.find_cert(host, port) + self.convert_to_ssl(certfile) + + method, path, httpmajor, httpminor = parse_init_http(self.rfile.readline(line)) + headers = read_headers(self.rfile) + content = self.read_contents(client_conn, headers, httpminor) + return flow.Request(client_conn, host, port, "https", method, path, headers, content) + else: + method, scheme, host, port, path, httpmajor, httpminor = parse_init_proxy(line) + headers = read_headers(self.rfile) + content = self.read_contents(client_conn, headers, httpminor) + return flow.Request(client_conn, host, port, scheme, method, path, headers, content) def send_response(self, response): d = response._assemble()