diff options
-rw-r--r-- | libmproxy/console/help.py | 25 | ||||
-rw-r--r-- | libmproxy/proxy.py | 97 | ||||
-rw-r--r-- | test/test_console_help.py | 25 | ||||
-rw-r--r-- | test/test_proxy.py | 52 |
4 files changed, 168 insertions, 31 deletions
diff --git a/libmproxy/console/help.py b/libmproxy/console/help.py index c75d2ae2..64428fcc 100644 --- a/libmproxy/console/help.py +++ b/libmproxy/console/help.py @@ -31,18 +31,6 @@ class HelpView(urwid.ListBox): self.helptext() ) - def keypress(self, size, key): - key = common.shortcuts(key) - if key == "q": - self.master.statusbar = self.state[0] - self.master.body = self.state[1] - self.master.header = self.state[2] - self.master.make_view() - return None - elif key == "?": - key = None - return urwid.ListBox.keypress(self, size, key) - def helptext(self): text = [] text.append(urwid.Text([("head", "Keys for this view:\n")])) @@ -174,3 +162,16 @@ class HelpView(urwid.ListBox): text.extend(common.format_keyvals(examples, key="key", val="text", indent=4)) return text + def keypress(self, size, key): + key = common.shortcuts(key) + if key == "q": + self.master.statusbar = self.state[0] + self.master.body = self.state[1] + self.master.header = self.state[2] + self.master.make_view() + return None + elif key == "?": + key = None + return urwid.ListBox.keypress(self, size, key) + + diff --git a/libmproxy/proxy.py b/libmproxy/proxy.py index 912f3f31..2c0fa57b 100644 --- a/libmproxy/proxy.py +++ b/libmproxy/proxy.py @@ -127,6 +127,61 @@ def read_http_body(rfile, connection, headers, all, limit): return content +def parse_http_protocol(s): + if not s.startswith("HTTP/"): + return None + major, minor = s.split('/')[1].split('.') + major = int(major) + minor = int(minor) + return major, minor + + +def parse_init_connect(line): + try: + method, url, protocol = string.split(line) + except ValueError: + return None + if method != 'CONNECT': + return None + try: + host, port = url.split(":") + except ValueError: + return None + port = int(port) + mm = parse_http_protocol(protocol) + if not mm: + return None + return host, port, mm[0], mm[1] + + +def parse_init_proxy(line): + try: + method, url, protocol = string.split(line) + except ValueError: + return None + parts = utils.parse_url(url) + if not parts: + return None + scheme, host, port, path = parts + mm = parse_http_protocol(protocol) + if not mm: + return None + return method, scheme, host, port, path, mm[0], mm[1] + + +def parse_init_http(line): + try: + method, url, protocol = string.split(line) + except ValueError: + return None + if not (url.startswith("/") or url == "*"): + return None + mm = parse_http_protocol(protocol) + if not mm: + return None + return method, url, mm[0], mm[1] + + #FIXME: Return full HTTP version specification from here. Allow non-HTTP #protocol specs, and make it all editable. def parse_request_line(request): @@ -146,14 +201,13 @@ def parse_request_line(request): except ValueError: raise ProxyError(400, "Can't parse request") port = int(port) - else: - if url.startswith("/") or url == "*": + elif url.startswith("/") or url == "*": scheme, port, host, path = None, None, None, url - else: - parts = utils.parse_url(url) - if not parts: - raise ProxyError(400, "Invalid url: %s"%url) - scheme, host, port, path = parts + else: + parts = utils.parse_url(url) + if not parts: + raise ProxyError(400, "Invalid url: %s"%url) + scheme, host, port, path = parts if not protocol.startswith("HTTP/"): raise ProxyError(400, "Unsupported protocol") major,minor = protocol.split('/')[1].split('.') @@ -382,6 +436,20 @@ class ProxyHandler(SocketServer.StreamRequestHandler): raise ProxyError(502, "mitmproxy: Unable to generate dummy cert.") return ret + def convert_to_ssl(self, cert): + kwargs = dict( + certfile = cert, + keyfile = self.config.certfile or self.config.cacert, + server_side = True, + ssl_version = ssl.PROTOCOL_SSLv23, + do_handshake_on_connect = True, + ) + if sys.version_info[1] > 6: + kwargs["ciphers"] = self.config.ciphers + self.connection = ssl.wrap_socket(self.connection, **kwargs) + 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 @@ -402,18 +470,8 @@ class ProxyHandler(SocketServer.StreamRequestHandler): '\r\n' ) self.wfile.flush() - kwargs = dict( - certfile = self.find_cert(host, port), - keyfile = self.config.certfile or self.config.cacert, - server_side = True, - ssl_version = ssl.PROTOCOL_SSLv23, - do_handshake_on_connect = True, - ) - if sys.version_info[1] > 6: - kwargs["ciphers"] = self.config.ciphers - self.connection = ssl.wrap_socket(self.connection, **kwargs) - self.rfile = FileLike(self.connection) - self.wfile = FileLike(self.connection) + 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" @@ -438,6 +496,7 @@ class ProxyHandler(SocketServer.StreamRequestHandler): # 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') diff --git a/test/test_console_help.py b/test/test_console_help.py new file mode 100644 index 00000000..e747635f --- /dev/null +++ b/test/test_console_help.py @@ -0,0 +1,25 @@ +import sys +import libpry +import libmproxy.console.help as help +from libmproxy import utils, flow, encoding + +class DummyMaster: + def make_view(self): + pass + + +class uHelp(libpry.AutoTree): + def test_helptext(self): + h = help.HelpView(None, "foo", None) + assert h.helptext() + + def test_keypress(self): + h = help.HelpView(DummyMaster(), "foo", [1, 2, 3]) + assert not h.keypress((0, 0), "q") + assert not h.keypress((0, 0), "?") + assert h.keypress((0, 0), "o") == "o" + + +tests = [ + uHelp() +] diff --git a/test/test_proxy.py b/test/test_proxy.py index fccef977..c67e93a0 100644 --- a/test/test_proxy.py +++ b/test/test_proxy.py @@ -135,7 +135,59 @@ class u_read_headers(libpry.AutoTree): assert headers["header"] == ['one\r\n two'] +class u_parse_http_protocol(libpry.AutoTree): + def test_simple(self): + assert proxy.parse_http_protocol("HTTP/1.1") == (1, 1) + assert proxy.parse_http_protocol("HTTP/0.0") == (0, 0) + assert not proxy.parse_http_protocol("foo/0.0") + + +class u_parse_init_connect(libpry.AutoTree): + def test_simple(self): + assert proxy.parse_init_connect("CONNECT host.com:443 HTTP/1.0") + assert not proxy.parse_init_connect("bogus") + assert not proxy.parse_init_connect("GET host.com:443 HTTP/1.0") + assert not proxy.parse_init_connect("CONNECT host.com443 HTTP/1.0") + assert not proxy.parse_init_connect("CONNECT host.com:443 foo/1.0") + + +class u_parse_init_proxy(libpry.AutoTree): + def test_simple(self): + u = "GET http://foo.com:8888/test HTTP/1.1" + m, s, h, po, pa, major, minor = proxy.parse_init_proxy(u) + assert m == "GET" + assert s == "http" + assert h == "foo.com" + assert po == 8888 + assert pa == "/test" + assert major == 1 + assert minor == 1 + + assert not proxy.parse_init_proxy("invalid") + assert not proxy.parse_init_proxy("GET invalid HTTP/1.1") + assert not proxy.parse_init_proxy("GET http://foo.com:8888/test foo/1.1") + + +class u_parse_init_http(libpry.AutoTree): + def test_simple(self): + u = "GET /test HTTP/1.1" + m, u, major, minor = proxy.parse_init_http(u) + assert m == "GET" + assert u == "/test" + assert major == 1 + assert minor == 1 + + assert not proxy.parse_init_http("invalid") + assert not proxy.parse_init_http("GET invalid HTTP/1.1") + assert not proxy.parse_init_http("GET /test foo/1.1") + + + tests = [ + u_parse_http_protocol(), + u_parse_init_connect(), + u_parse_init_proxy(), + u_parse_init_http(), uProxyError(), uFileLike(), u_parse_request_line(), |