aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libmproxy/protocol/http.py76
-rw-r--r--libmproxy/proxy/config.py14
-rw-r--r--libmproxy/proxy/primitives.py4
-rw-r--r--libmproxy/proxy/server.py18
-rw-r--r--test/test_protocol_http.py4
-rw-r--r--test/tservers.py13
6 files changed, 68 insertions, 61 deletions
diff --git a/libmproxy/protocol/http.py b/libmproxy/protocol/http.py
index 77a09e61..c8147a3c 100644
--- a/libmproxy/protocol/http.py
+++ b/libmproxy/protocol/http.py
@@ -843,6 +843,12 @@ class HttpAuthenticationError(Exception):
class HTTPHandler(ProtocolHandler, TemporaryServerChangeMixin):
+ def __init__(self, c):
+ super(HTTPHandler, self).__init__(c)
+ self.expected_form_in = c.config.http_form_in
+ self.expected_form_out = c.config.http_form_out
+ self.skip_authentication = False
+
def handle_messages(self):
while self.handle_flow():
pass
@@ -877,13 +883,15 @@ class HTTPHandler(ProtocolHandler, TemporaryServerChangeMixin):
flow = HTTPFlow(self.c.client_conn, self.c.server_conn, self.change_server)
try:
req = HTTPRequest.from_stream(self.c.client_conn.rfile,
- body_size_limit=self.c.config.body_size_limit)
+ body_size_limit=self.c.config.body_size_limit)
self.c.log("request", [req._assemble_first_line(req.form_in)])
- self.process_request(flow, req)
+ send_upstream = self.process_request(flow, req)
+ if not send_upstream:
+ return True
# Be careful NOT to assign the request to the flow before
# process_request completes. This is because the call can raise an
- # exception. If the requets object is already attached, this results
+ # exception. If the request object is already attached, this results
# in an Error object that has an attached request that has not been
# sent through to the Master.
flow.request = req
@@ -1004,44 +1012,48 @@ class HTTPHandler(ProtocolHandler, TemporaryServerChangeMixin):
Upgrade the connection to SSL after an authority (CONNECT) request has been made.
"""
self.c.log("Received CONNECT request. Upgrading to SSL...")
- self.c.mode = "transparent"
- self.c.determine_conntype()
+ self.expected_form_in = "relative"
+ self.expected_form_out = "relative"
self.c.establish_ssl(server=True, client=True)
self.c.log("Upgrade to SSL completed.")
- raise ConnectionTypeChange
def process_request(self, flow, request):
- if self.c.mode == "regular":
+
+ if not self.skip_authentication:
self.authenticate(request)
- if request.form_in == "authority" and self.c.client_conn.ssl_established:
- raise http.HttpError(502, "Must not CONNECT on already encrypted connection")
- # If we have a CONNECT request, we might need to intercept
if request.form_in == "authority":
- directly_addressed_at_mitmproxy = (self.c.mode == "regular" and not self.c.config.forward_proxy)
- if directly_addressed_at_mitmproxy:
- self.c.set_server_address((request.host, request.port), AddressPriority.FROM_PROTOCOL)
- flow.server_conn = self.c.server_conn # Update server_conn attribute on the flow
- self.c.client_conn.wfile.write(
- 'HTTP/1.1 200 Connection established\r\n' +
- ('Proxy-agent: %s\r\n' % self.c.server_version) +
- '\r\n'
- )
- self.c.client_conn.wfile.flush()
- self.ssl_upgrade() # raises ConnectionTypeChange exception
-
- if self.c.mode == "regular":
- if request.form_in == "authority": # forward mode
- self.hook_reconnect(request)
- elif request.form_in == "absolute":
- if request.scheme != "http":
- raise http.HttpError(400, "Invalid Request")
- if not self.c.config.forward_proxy:
- request.form_out = "relative"
+ if self.c.client_conn.ssl_established:
+ raise http.HttpError(400, "Must not CONNECT on already encrypted connection")
+
+ if self.expected_form_in == "absolute":
+ if not self.c.config.upstream_server:
self.c.set_server_address((request.host, request.port), AddressPriority.FROM_PROTOCOL)
flow.server_conn = self.c.server_conn # Update server_conn attribute on the flow
- else:
- raise http.HttpError(400, "Invalid request form (absolute-form or authority-form required)")
+ self.c.client_conn.send(
+ 'HTTP/1.1 200 Connection established\r\n' +
+ ('Proxy-agent: %s\r\n' % self.c.server_version) +
+ '\r\n'
+ )
+ self.ssl_upgrade()
+ self.skip_authentication = True
+ return False
+ else:
+ self.hook_reconnect(request)
+ return True
+ elif request.form_in == self.expected_form_in:
+ if request.form_in == "absolute":
+ if request.scheme != "http":
+ raise http.HttpError(400, "Invalid request scheme: %s" % request.scheme)
+
+ self.c.set_server_address((request.host, request.port), AddressPriority.FROM_PROTOCOL)
+ flow.server_conn = self.c.server_conn # Update server_conn attribute on the flow
+
+ request.form_out = self.expected_form_out
+ return True
+
+ raise http.HttpError(400, "Invalid HTTP request form (expected: %s, got: %s)" % (self.expected_form_in,
+ request.form_in))
def authenticate(self, request):
if self.c.config.authenticator:
diff --git a/libmproxy/proxy/config.py b/libmproxy/proxy/config.py
index 38c6ce89..248808e9 100644
--- a/libmproxy/proxy/config.py
+++ b/libmproxy/proxy/config.py
@@ -10,16 +10,17 @@ CONF_DIR = "~/.mitmproxy"
class ProxyConfig:
def __init__(self, confdir=CONF_DIR, clientcerts=None,
- no_upstream_cert=False, body_size_limit=None, reverse_proxy=None,
- forward_proxy=None, transparent_proxy=None, authenticator=None,
+ no_upstream_cert=False, body_size_limit=None, upstream_server=None,
+ http_form_in="absolute", http_form_out="relative", transparent_proxy=None, authenticator=None,
ciphers=None, certs=None
):
self.ciphers = ciphers
self.clientcerts = clientcerts
self.no_upstream_cert = no_upstream_cert
self.body_size_limit = body_size_limit
- self.reverse_proxy = reverse_proxy
- self.forward_proxy = forward_proxy
+ self.upstream_server = upstream_server
+ self.http_form_in = http_form_in
+ self.http_form_out = http_form_out
self.transparent_proxy = transparent_proxy
self.authenticator = authenticator
self.confdir = os.path.expanduser(confdir)
@@ -93,8 +94,9 @@ def process_proxy_options(parser, options):
clientcerts=options.clientcerts,
body_size_limit=body_size_limit,
no_upstream_cert=options.no_upstream_cert,
- reverse_proxy=rp,
- forward_proxy=fp,
+ upstream_server=(rp or fp),
+ http_form_in=("relative" if (rp or trans) else "absolute"),
+ http_form_out=("absolute" if fp else "relative"),
transparent_proxy=trans,
authenticator=authenticator,
ciphers=options.ciphers,
diff --git a/libmproxy/proxy/primitives.py b/libmproxy/proxy/primitives.py
index 8dd0e16a..75ad5482 100644
--- a/libmproxy/proxy/primitives.py
+++ b/libmproxy/proxy/primitives.py
@@ -23,12 +23,10 @@ class AddressPriority(object):
Enum that signifies the priority of the given address when choosing the destination host.
Higher is better (None < i)
"""
- FORCE = 5
- """forward mode"""
MANUALLY_CHANGED = 4
"""user changed the target address in the ui"""
FROM_SETTINGS = 3
- """reverse proxy mode"""
+ """upstream proxy from arguments (reverse proxy or forward proxy)"""
FROM_CONNECTION = 2
"""derived from transparent resolver"""
FROM_PROTOCOL = 1
diff --git a/libmproxy/proxy/server.py b/libmproxy/proxy/server.py
index 37ec7758..5aaabf87 100644
--- a/libmproxy/proxy/server.py
+++ b/libmproxy/proxy/server.py
@@ -59,12 +59,6 @@ class ConnectionHandler:
self.conntype = None
self.sni = None
- self.mode = "regular"
- if self.config.reverse_proxy:
- self.mode = "reverse"
- if self.config.transparent_proxy:
- self.mode = "transparent"
-
def handle(self):
self.log("clientconnect")
self.channel.ask("clientconnect", self)
@@ -76,11 +70,8 @@ class ConnectionHandler:
# Can we already identify the target server and connect to it?
server_address = None
address_priority = None
- if self.config.forward_proxy:
- server_address = self.config.forward_proxy[1:]
- address_priority = AddressPriority.FORCE
- elif self.config.reverse_proxy:
- server_address = self.config.reverse_proxy[1:]
+ if self.config.upstream_server:
+ server_address = self.config.upstream_server[1:]
address_priority = AddressPriority.FROM_SETTINGS
elif self.config.transparent_proxy:
server_address = self.config.transparent_proxy["resolver"].original_addr(
@@ -125,8 +116,8 @@ class ConnectionHandler:
if self.config.transparent_proxy:
client_ssl = server_ssl = (self.server_conn.address.port in self.config.transparent_proxy["sslports"])
- elif self.config.reverse_proxy:
- client_ssl = server_ssl = (self.config.reverse_proxy[0] == "https")
+ elif self.config.upstream_server:
+ client_ssl = server_ssl = (self.config.upstream_server[0] == "https")
# TODO: Make protocol generic (as with transparent proxies)
# TODO: Add SSL-terminating capatbility (SSL -> mitmproxy -> plain and vice versa)
if client_ssl or server_ssl:
@@ -152,7 +143,6 @@ class ConnectionHandler:
"""
Sets a new server address with the given priority.
Does not re-establish either connection or SSL handshake.
- @type priority: libmproxy.proxy.primitives.AddressPriority
"""
address = tcp.Address.wrap(address)
diff --git a/test/test_protocol_http.py b/test/test_protocol_http.py
index 6ff0cb65..290d82a1 100644
--- a/test/test_protocol_http.py
+++ b/test/test_protocol_http.py
@@ -90,7 +90,7 @@ class TestInvalidRequests(tservers.HTTPProxTest):
def test_double_connect(self):
p = self.pathoc()
r = p.request("connect:'%s:%s'" % ("127.0.0.1", self.server2.port))
- assert r.status_code == 502
+ assert r.status_code == 400
assert "Must not CONNECT on already encrypted connection" in r.content
def test_relative_request(self):
@@ -98,7 +98,7 @@ class TestInvalidRequests(tservers.HTTPProxTest):
p.connect()
r = p.request("get:/p/200")
assert r.status_code == 400
- assert "Invalid request form" in r.content
+ assert "Invalid HTTP request form" in r.content
class TestProxyChaining(tservers.HTTPChainProxyTest):
diff --git a/test/tservers.py b/test/tservers.py
index bfafc8cd..addc7011 100644
--- a/test/tservers.py
+++ b/test/tservers.py
@@ -197,6 +197,8 @@ class TransparentProxTest(ProxTestBase):
resolver = cls.resolver(cls.server.port),
sslports = ports
)
+ d["http_form_in"] = "relative"
+ d["http_form_out"] = "relative"
return d
def pathod(self, spec, sni=None):
@@ -225,11 +227,13 @@ class ReverseProxTest(ProxTestBase):
@classmethod
def get_proxy_config(cls):
d = ProxTestBase.get_proxy_config()
- d["reverse_proxy"] = (
+ d["upstream_server"] = (
"https" if cls.ssl else "http",
"127.0.0.1",
cls.server.port
)
+ d["http_form_in"] = "relative"
+ d["http_form_out"] = "relative"
return d
def pathoc(self, sni=None):
@@ -258,18 +262,19 @@ class ChainProxTest(ProxTestBase):
Chain n instances of mitmproxy in a row - because we can.
"""
n = 2
- chain_config = [lambda: ProxyConfig(
- )] * n
+ chain_config = [lambda: ProxyConfig()] * n
@classmethod
def setupAll(cls):
super(ChainProxTest, cls).setupAll()
cls.chain = []
for i in range(cls.n):
config = cls.chain_config[i]()
- config.forward_proxy = ("http", "127.0.0.1",
+ config.upstream_server = ("http", "127.0.0.1",
cls.proxy.port if i == 0 else
cls.chain[-1].port
)
+ config.http_form_in = "absolute"
+ config.http_form_out = "absolute"
tmaster = cls.masterclass(config)
tmaster.start_app(APP_HOST, APP_PORT, cls.externalapp)
cls.chain.append(ProxyThread(tmaster))