From 9130cd63d36103055e6ec34bacca37e7532ef366 Mon Sep 17 00:00:00 2001 From: Aldo Cortesi Date: Sat, 9 Jun 2012 21:27:43 +1200 Subject: 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 | 87 +++++++++++++++++++++++++----------------------------- 1 file changed, 41 insertions(+), 46 deletions(-) diff --git a/libmproxy/proxy.py b/libmproxy/proxy.py index 2d4ee988..41f101d8 100644 --- a/libmproxy/proxy.py +++ b/libmproxy/proxy.py @@ -450,14 +450,43 @@ class ProxyHandler(SocketServer.StreamRequestHandler): self.rfile = FileLike(self.connection) self.wfile = FileLike(self.connection) + def read_contents(self, client_conn, headers, httpminor): + if "expect" in headers: + # FIXME: Should be forwarded upstream + expect = ",".join(headers['expect']) + if expect == "100-continue" and httpminor >= 1: + self.wfile.write('HTTP/1.1 100 Continue\r\n') + self.wfile.write('Proxy-agent: %s\r\n'%version.NAMEVERSION) + self.wfile.write('\r\n') + del headers['expect'] + else: + raise ProxyError(417, 'Unmet expect: %s'%expect) + if httpminor == 0: + client_conn.close = True + if "connection" in headers: + for value in ",".join(headers['connection']).split(","): + value = value.strip() + if value == "close": + client_conn.close = True + if value == "keep-alive": + client_conn.close = False + 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 - method, scheme, host, port, path, httpminor = parse_request_line(line) - if method == "CONNECT": + + 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: @@ -472,50 +501,16 @@ class ProxyHandler(SocketServer.StreamRequestHandler): 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) - if "expect" in headers: - # FIXME: Should be forwarded upstream - expect = ",".join(headers['expect']) - if expect == "100-continue" and httpminor >= 1: - self.wfile.write('HTTP/1.1 100 Continue\r\n') - self.wfile.write('Proxy-agent: %s\r\n'%version.NAMEVERSION) - self.wfile.write('\r\n') - del headers['expect'] - else: - raise ProxyError(417, 'Unmet expect: %s'%expect) - if httpminor == 0: - client_conn.close = True - if "connection" in headers: - for value in ",".join(headers['connection']).split(","): - value = value.strip() - if value == "close": - 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) + + 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() -- cgit v1.2.3