mirror of
https://github.com/Grasscutters/mitmproxy.git
synced 2025-02-07 10:40:09 +00:00
improve error handling
This commit is contained in:
parent
1a41c15c03
commit
1e4e332ef9
@ -22,7 +22,7 @@ def get_line(fp):
|
|||||||
if line == "\r\n" or line == "\n": # Possible leftover from previous message
|
if line == "\r\n" or line == "\n": # Possible leftover from previous message
|
||||||
line = fp.readline()
|
line = fp.readline()
|
||||||
if line == "":
|
if line == "":
|
||||||
raise tcp.NetLibDisconnect
|
raise tcp.NetLibDisconnect()
|
||||||
return line
|
return line
|
||||||
|
|
||||||
|
|
||||||
@ -927,7 +927,7 @@ class HTTPHandler(ProtocolHandler):
|
|||||||
body_size_limit=self.c.config.body_size_limit, include_body=include_body)
|
body_size_limit=self.c.config.body_size_limit, include_body=include_body)
|
||||||
return res
|
return res
|
||||||
except (tcp.NetLibDisconnect, http.HttpErrorConnClosed), v:
|
except (tcp.NetLibDisconnect, http.HttpErrorConnClosed), v:
|
||||||
self.c.log("error in server communication: %s" % str(v), level="debug")
|
self.c.log("error in server communication: %s" % repr(v), level="debug")
|
||||||
if i < 1:
|
if i < 1:
|
||||||
# In any case, we try to reconnect at least once.
|
# In any case, we try to reconnect at least once.
|
||||||
# This is necessary because it might be possible that we already initiated an upstream connection
|
# This is necessary because it might be possible that we already initiated an upstream connection
|
||||||
@ -1064,23 +1064,23 @@ class HTTPHandler(ProtocolHandler):
|
|||||||
def handle_error(self, error, flow=None):
|
def handle_error(self, error, flow=None):
|
||||||
|
|
||||||
message = repr(error)
|
message = repr(error)
|
||||||
code = getattr(error, "code", 502)
|
|
||||||
headers = getattr(error, "headers", None)
|
|
||||||
|
|
||||||
if "tlsv1 alert unknown ca" in message:
|
if "tlsv1 alert unknown ca" in message:
|
||||||
message = message + " \nThe client does not trust the proxy's certificate."
|
message += " The client does not trust the proxy's certificate."
|
||||||
|
|
||||||
self.c.log("error: %s" % message, level="info")
|
if isinstance(error, tcp.NetLibDisconnect):
|
||||||
|
self.c.log("TCP connection closed unexpectedly.", "debug", [repr(error)])
|
||||||
|
else:
|
||||||
|
self.c.log("error: %s" % message, level="info")
|
||||||
|
|
||||||
if flow:
|
if flow:
|
||||||
flow.error = Error(message)
|
# TODO: no flows without request or with both request and response at the moment.
|
||||||
# FIXME: no flows without request or with both request and response at the moment.
|
|
||||||
if flow.request and not flow.response:
|
if flow.request and not flow.response:
|
||||||
|
flow.error = Error(message)
|
||||||
self.c.channel.ask("error", flow.error)
|
self.c.channel.ask("error", flow.error)
|
||||||
else:
|
|
||||||
pass
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
code = getattr(error, "code", 502)
|
||||||
|
headers = getattr(error, "headers", None)
|
||||||
self.send_error(code, message, headers)
|
self.send_error(code, message, headers)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
@ -16,51 +16,55 @@ class TCPHandler(ProtocolHandler):
|
|||||||
|
|
||||||
server = "%s:%s" % self.c.server_conn.address()[:2]
|
server = "%s:%s" % self.c.server_conn.address()[:2]
|
||||||
buf = memoryview(bytearray(self.chunk_size))
|
buf = memoryview(bytearray(self.chunk_size))
|
||||||
|
|
||||||
conns = [self.c.client_conn.rfile, self.c.server_conn.rfile]
|
conns = [self.c.client_conn.rfile, self.c.server_conn.rfile]
|
||||||
while True:
|
|
||||||
r, _, _ = select.select(conns, [], [], 10)
|
|
||||||
for rfile in r:
|
|
||||||
if self.c.client_conn.rfile == rfile:
|
|
||||||
src, dst = self.c.client_conn, self.c.server_conn
|
|
||||||
direction = "-> tcp ->"
|
|
||||||
src_str, dst_str = "client", server
|
|
||||||
else:
|
|
||||||
dst, src = self.c.client_conn, self.c.server_conn
|
|
||||||
direction = "<- tcp <-"
|
|
||||||
dst_str, src_str = "client", server
|
|
||||||
|
|
||||||
closed = False
|
try:
|
||||||
if src.ssl_established:
|
while True:
|
||||||
# Unfortunately, pyOpenSSL lacks a recv_into function.
|
r, _, _ = select.select(conns, [], [], 10)
|
||||||
contents = src.rfile.read(1) # We need to read a single byte before .pending() becomes usable
|
for rfile in r:
|
||||||
contents += src.rfile.read(src.connection.pending())
|
if self.c.client_conn.rfile == rfile:
|
||||||
if not contents:
|
src, dst = self.c.client_conn, self.c.server_conn
|
||||||
closed = True
|
direction = "-> tcp ->"
|
||||||
else:
|
src_str, dst_str = "client", server
|
||||||
size = src.connection.recv_into(buf)
|
|
||||||
if not size:
|
|
||||||
closed = True
|
|
||||||
|
|
||||||
if closed:
|
|
||||||
conns.remove(src.rfile)
|
|
||||||
# Shutdown connection to the other peer
|
|
||||||
if dst.ssl_established:
|
|
||||||
dst.connection.shutdown()
|
|
||||||
else:
|
else:
|
||||||
dst.connection.shutdown(socket.SHUT_WR)
|
dst, src = self.c.client_conn, self.c.server_conn
|
||||||
|
direction = "<- tcp <-"
|
||||||
|
dst_str, src_str = "client", server
|
||||||
|
|
||||||
if len(conns) == 0:
|
closed = False
|
||||||
return
|
if src.ssl_established:
|
||||||
continue
|
# Unfortunately, pyOpenSSL lacks a recv_into function.
|
||||||
|
contents = src.rfile.read(1) # We need to read a single byte before .pending() becomes usable
|
||||||
|
contents += src.rfile.read(src.connection.pending())
|
||||||
|
if not contents:
|
||||||
|
closed = True
|
||||||
|
else:
|
||||||
|
size = src.connection.recv_into(buf)
|
||||||
|
if not size:
|
||||||
|
closed = True
|
||||||
|
|
||||||
if src.ssl_established or dst.ssl_established:
|
if closed:
|
||||||
# if one of the peers is over SSL, we need to send bytes/strings
|
conns.remove(src.rfile)
|
||||||
if not src.ssl_established: # only ssl to dst, i.e. we revc'd into buf but need bytes/string now.
|
# Shutdown connection to the other peer
|
||||||
contents = buf[:size].tobytes()
|
if dst.ssl_established:
|
||||||
self.c.log("%s %s\r\n%s" % (direction, dst_str, cleanBin(contents)), "debug")
|
dst.connection.shutdown()
|
||||||
dst.connection.send(contents)
|
else:
|
||||||
else:
|
dst.connection.shutdown(socket.SHUT_WR)
|
||||||
# socket.socket.send supports raw bytearrays/memoryviews
|
|
||||||
self.c.log("%s %s\r\n%s" % (direction, dst_str, cleanBin(buf.tobytes())), "debug")
|
if len(conns) == 0:
|
||||||
dst.connection.send(buf[:size])
|
return
|
||||||
|
continue
|
||||||
|
|
||||||
|
if src.ssl_established or dst.ssl_established:
|
||||||
|
# if one of the peers is over SSL, we need to send bytes/strings
|
||||||
|
if not src.ssl_established: # only ssl to dst, i.e. we revc'd into buf but need bytes/string now.
|
||||||
|
contents = buf[:size].tobytes()
|
||||||
|
# self.c.log("%s %s\r\n%s" % (direction, dst_str, cleanBin(contents)), "debug")
|
||||||
|
dst.connection.send(contents)
|
||||||
|
else:
|
||||||
|
# socket.socket.send supports raw bytearrays/memoryviews
|
||||||
|
# self.c.log("%s %s\r\n%s" % (direction, dst_str, cleanBin(buf.tobytes())), "debug")
|
||||||
|
dst.connection.send(buf[:size])
|
||||||
|
except socket.error as e:
|
||||||
|
self.c.log("TCP connection closed unexpectedly.", "debug", [repr(e)])
|
||||||
|
return
|
@ -3,7 +3,6 @@ import copy
|
|||||||
import os
|
import os
|
||||||
from netlib import tcp, certutils
|
from netlib import tcp, certutils
|
||||||
from .. import stateobject, utils
|
from .. import stateobject, utils
|
||||||
from .primitives import ProxyError
|
|
||||||
|
|
||||||
|
|
||||||
class ClientConnection(tcp.BaseHandler, stateobject.SimpleStateObject):
|
class ClientConnection(tcp.BaseHandler, stateobject.SimpleStateObject):
|
||||||
@ -156,11 +155,8 @@ class ServerConnection(tcp.TCPClient, stateobject.SimpleStateObject):
|
|||||||
path = os.path.join(clientcerts, self.address.host.encode("idna")) + ".pem"
|
path = os.path.join(clientcerts, self.address.host.encode("idna")) + ".pem"
|
||||||
if os.path.exists(path):
|
if os.path.exists(path):
|
||||||
clientcert = path
|
clientcert = path
|
||||||
try:
|
self.convert_to_ssl(cert=clientcert, sni=sni)
|
||||||
self.convert_to_ssl(cert=clientcert, sni=sni)
|
self.timestamp_ssl_setup = utils.timestamp()
|
||||||
self.timestamp_ssl_setup = utils.timestamp()
|
|
||||||
except tcp.NetLibError, v:
|
|
||||||
raise ProxyError(400, repr(v))
|
|
||||||
|
|
||||||
def finish(self):
|
def finish(self):
|
||||||
tcp.TCPClient.finish(self)
|
tcp.TCPClient.finish(self)
|
||||||
|
@ -95,7 +95,7 @@ class ConnectionHandler:
|
|||||||
# Delegate handling to the protocol handler
|
# Delegate handling to the protocol handler
|
||||||
protocol_handler(self.conntype)(self).handle_messages()
|
protocol_handler(self.conntype)(self).handle_messages()
|
||||||
|
|
||||||
except (ProxyError, tcp.NetLibError), e:
|
except ProxyError as e:
|
||||||
protocol_handler(self.conntype)(self).handle_error(e)
|
protocol_handler(self.conntype)(self).handle_error(e)
|
||||||
except Exception:
|
except Exception:
|
||||||
import traceback, sys
|
import traceback, sys
|
||||||
@ -190,18 +190,24 @@ class ConnectionHandler:
|
|||||||
raise ProxyError(502, "No server connection.")
|
raise ProxyError(502, "No server connection.")
|
||||||
if self.server_conn.ssl_established:
|
if self.server_conn.ssl_established:
|
||||||
raise ProxyError(502, "SSL to Server already established.")
|
raise ProxyError(502, "SSL to Server already established.")
|
||||||
self.server_conn.establish_ssl(self.config.clientcerts, self.sni)
|
try:
|
||||||
|
self.server_conn.establish_ssl(self.config.clientcerts, self.sni)
|
||||||
|
except tcp.NetLibError as v:
|
||||||
|
raise ProxyError(502, repr(v))
|
||||||
if client:
|
if client:
|
||||||
if self.client_conn.ssl_established:
|
if self.client_conn.ssl_established:
|
||||||
raise ProxyError(502, "SSL to Client already established.")
|
raise ProxyError(502, "SSL to Client already established.")
|
||||||
cert, key = self.find_cert()
|
cert, key = self.find_cert()
|
||||||
self.client_conn.convert_to_ssl(
|
try:
|
||||||
cert, key,
|
self.client_conn.convert_to_ssl(
|
||||||
handle_sni=self.handle_sni,
|
cert, key,
|
||||||
cipher_list=self.config.ciphers,
|
handle_sni=self.handle_sni,
|
||||||
dhparams=self.config.certstore.dhparams,
|
cipher_list=self.config.ciphers,
|
||||||
ca_file=self.config.ca_file
|
dhparams=self.config.certstore.dhparams,
|
||||||
)
|
ca_file=self.config.ca_file
|
||||||
|
)
|
||||||
|
except tcp.NetLibError as v:
|
||||||
|
raise ProxyError(400, repr(v))
|
||||||
|
|
||||||
def server_reconnect(self):
|
def server_reconnect(self):
|
||||||
address = self.server_conn.address
|
address = self.server_conn.address
|
||||||
|
@ -179,7 +179,7 @@ class TestHTTPConnectSSLError(tservers.HTTPProxTest):
|
|||||||
p = self.pathoc_raw()
|
p = self.pathoc_raw()
|
||||||
dst = ("localhost", self.proxy.port)
|
dst = ("localhost", self.proxy.port)
|
||||||
p.connect(connect_to=dst)
|
p.connect(connect_to=dst)
|
||||||
tutils.raises("400 - Bad Request", p.http_connect, dst)
|
tutils.raises("502 - Bad Gateway", p.http_connect, dst)
|
||||||
|
|
||||||
|
|
||||||
class TestHTTPS(tservers.HTTPProxTest, CommonMixin):
|
class TestHTTPS(tservers.HTTPProxTest, CommonMixin):
|
||||||
@ -244,7 +244,7 @@ class TestTransparentSSL(tservers.TransparentProxTest, CommonMixin):
|
|||||||
p = pathoc.Pathoc(("localhost", self.proxy.port))
|
p = pathoc.Pathoc(("localhost", self.proxy.port))
|
||||||
p.connect()
|
p.connect()
|
||||||
r = p.request("get:/")
|
r = p.request("get:/")
|
||||||
assert r.status_code == 502
|
assert r.status_code == 400
|
||||||
|
|
||||||
|
|
||||||
class TestProxy(tservers.HTTPProxTest):
|
class TestProxy(tservers.HTTPProxTest):
|
||||||
|
Loading…
Reference in New Issue
Block a user