mirror of
https://github.com/Grasscutters/mitmproxy.git
synced 2024-11-23 08:11:00 +00:00
Factor logger out of pathoc, use it in pathod as well.
This commit is contained in:
parent
4ed5043c67
commit
a09f3e06c3
57
libpathod/log.py
Normal file
57
libpathod/log.py
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import netlib.utils
|
||||||
|
import netlib.tcp
|
||||||
|
import netlib.http
|
||||||
|
|
||||||
|
|
||||||
|
class Log:
|
||||||
|
def __init__(self, fp, hex, rfile, wfile):
|
||||||
|
self.lines = []
|
||||||
|
self.fp = fp
|
||||||
|
self.suppressed = False
|
||||||
|
self.hex = hex
|
||||||
|
self.rfile, self.wfile = rfile, wfile
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
if self.wfile:
|
||||||
|
self.wfile.start_log()
|
||||||
|
if self.rfile:
|
||||||
|
self.rfile.start_log()
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc_value, traceback):
|
||||||
|
wlog = self.wfile.get_log() if self.wfile else None
|
||||||
|
rlog = self.rfile.get_log() if self.rfile else None
|
||||||
|
if self.suppressed or not self.fp:
|
||||||
|
return
|
||||||
|
if wlog:
|
||||||
|
self("Bytes written:")
|
||||||
|
self.dump(wlog, self.hex)
|
||||||
|
if rlog:
|
||||||
|
self("Bytes read:")
|
||||||
|
self.dump(rlog, self.hex)
|
||||||
|
if exc_type == netlib.tcp.NetLibTimeout:
|
||||||
|
self("Timeout")
|
||||||
|
elif exc_type in (
|
||||||
|
netlib.tcp.NetLibDisconnect,
|
||||||
|
netlib.http.HttpErrorConnClosed
|
||||||
|
):
|
||||||
|
self("Disconnected")
|
||||||
|
elif exc_type == netlib.http.HttpError:
|
||||||
|
self("HTTP Error: %s" % exc_value.message)
|
||||||
|
self.fp.write("\n".join(self.lines))
|
||||||
|
self.fp.write("\n")
|
||||||
|
self.fp.flush()
|
||||||
|
|
||||||
|
def suppress(self):
|
||||||
|
self.suppressed = True
|
||||||
|
|
||||||
|
def dump(self, data, hexdump):
|
||||||
|
if hexdump:
|
||||||
|
for line in netlib.utils.hexdump(data):
|
||||||
|
self("\t%s %s %s" % line)
|
||||||
|
else:
|
||||||
|
for i in netlib.utils.cleanBin(data).split("\n"):
|
||||||
|
self("\t%s" % i)
|
||||||
|
|
||||||
|
def __call__(self, line):
|
||||||
|
self.lines.append(line)
|
@ -10,68 +10,16 @@ import threading
|
|||||||
import OpenSSL.crypto
|
import OpenSSL.crypto
|
||||||
|
|
||||||
from netlib import tcp, http, certutils, websockets
|
from netlib import tcp, http, certutils, websockets
|
||||||
import netlib.utils
|
|
||||||
|
|
||||||
import language.http
|
import language.http
|
||||||
import language.websockets
|
import language.websockets
|
||||||
import utils
|
from . import utils, log
|
||||||
|
|
||||||
|
|
||||||
class PathocError(Exception):
|
class PathocError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Log:
|
|
||||||
def __init__(self, fp, hex, rfile, wfile):
|
|
||||||
self.lines = []
|
|
||||||
self.fp = fp
|
|
||||||
self.suppressed = False
|
|
||||||
self.hex = hex
|
|
||||||
self.rfile, self.wfile = rfile, wfile
|
|
||||||
|
|
||||||
def __enter__(self):
|
|
||||||
if self.wfile:
|
|
||||||
self.wfile.start_log()
|
|
||||||
if self.rfile:
|
|
||||||
self.rfile.start_log()
|
|
||||||
return self
|
|
||||||
|
|
||||||
def __exit__(self, exc_type, exc_value, traceback):
|
|
||||||
wlog = self.wfile.get_log() if self.wfile else None
|
|
||||||
rlog = self.rfile.get_log() if self.rfile else None
|
|
||||||
if self.suppressed or not self.fp:
|
|
||||||
return
|
|
||||||
if wlog:
|
|
||||||
self("Bytes written:")
|
|
||||||
self.dump(wlog, self.hex)
|
|
||||||
if rlog:
|
|
||||||
self("Bytes read:")
|
|
||||||
self.dump(rlog, self.hex)
|
|
||||||
if exc_type == tcp.NetLibTimeout:
|
|
||||||
self("Timeout")
|
|
||||||
elif exc_type in (tcp.NetLibDisconnect, http.HttpErrorConnClosed):
|
|
||||||
self("Disconnected")
|
|
||||||
elif exc_type == http.HttpError:
|
|
||||||
self("HTTP Error: %s" % exc_value.message)
|
|
||||||
self.fp.write("\n".join(self.lines))
|
|
||||||
self.fp.write("\n")
|
|
||||||
self.fp.flush()
|
|
||||||
|
|
||||||
def suppress(self):
|
|
||||||
self.suppressed = True
|
|
||||||
|
|
||||||
def dump(self, data, hexdump):
|
|
||||||
if hexdump:
|
|
||||||
for line in netlib.utils.hexdump(data):
|
|
||||||
self("\t%s %s %s" % line)
|
|
||||||
else:
|
|
||||||
for i in netlib.utils.cleanBin(data).split("\n"):
|
|
||||||
self("\t%s" % i)
|
|
||||||
|
|
||||||
def __call__(self, line):
|
|
||||||
self.lines.append(line)
|
|
||||||
|
|
||||||
|
|
||||||
class SSLInfo:
|
class SSLInfo:
|
||||||
def __init__(self, certchain, cipher):
|
def __init__(self, certchain, cipher):
|
||||||
self.certchain, self.cipher = certchain, cipher
|
self.certchain, self.cipher = certchain, cipher
|
||||||
@ -149,7 +97,7 @@ class WebsocketFrameReader(threading.Thread):
|
|||||||
self.is_done = Queue.Queue()
|
self.is_done = Queue.Queue()
|
||||||
|
|
||||||
def log(self, rfile):
|
def log(self, rfile):
|
||||||
return Log(
|
return log.Log(
|
||||||
self.logfp,
|
self.logfp,
|
||||||
self.hexdump,
|
self.hexdump,
|
||||||
rfile if self.showresp else None,
|
rfile if self.showresp else None,
|
||||||
@ -242,7 +190,7 @@ class Pathoc(tcp.TCPClient):
|
|||||||
self.ws_framereader = None
|
self.ws_framereader = None
|
||||||
|
|
||||||
def log(self):
|
def log(self):
|
||||||
return Log(
|
return log.Log(
|
||||||
self.fp,
|
self.fp,
|
||||||
self.hexdump,
|
self.hexdump,
|
||||||
self.rfile if self.showresp else None,
|
self.rfile if self.showresp else None,
|
||||||
|
@ -7,7 +7,7 @@ import urllib
|
|||||||
from netlib import tcp, http, wsgi, certutils, websockets
|
from netlib import tcp, http, wsgi, certutils, websockets
|
||||||
import netlib.utils
|
import netlib.utils
|
||||||
|
|
||||||
from . import version, app, language, utils
|
from . import version, app, language, utils, log
|
||||||
import language.http
|
import language.http
|
||||||
import language.actions
|
import language.actions
|
||||||
|
|
||||||
@ -60,7 +60,8 @@ class PathodHandler(tcp.BaseHandler):
|
|||||||
wbufsize = 0
|
wbufsize = 0
|
||||||
sni = None
|
sni = None
|
||||||
|
|
||||||
def __init__(self, connection, address, server, settings):
|
def __init__(self, connection, address, server, logfp, settings):
|
||||||
|
self.logfp = logfp
|
||||||
tcp.BaseHandler.__init__(self, connection, address, server)
|
tcp.BaseHandler.__init__(self, connection, address, server)
|
||||||
self.settings = copy.copy(settings)
|
self.settings = copy.copy(settings)
|
||||||
|
|
||||||
@ -106,11 +107,9 @@ class PathodHandler(tcp.BaseHandler):
|
|||||||
again: True if request handling should continue.
|
again: True if request handling should continue.
|
||||||
log: A dictionary, or None
|
log: A dictionary, or None
|
||||||
"""
|
"""
|
||||||
if self.server.logreq:
|
lr = self.rfile if self.server.logreq else None
|
||||||
self.rfile.start_log()
|
lw = self.wfile if self.server.logresp else None
|
||||||
if self.server.logresp:
|
with log.Log(self.logfp, self.server.hexdump, lr, lw) as lg:
|
||||||
self.wfile.start_log()
|
|
||||||
|
|
||||||
line = http.get_request_line(self.rfile)
|
line = http.get_request_line(self.rfile)
|
||||||
if not line:
|
if not line:
|
||||||
# Normal termination
|
# Normal termination
|
||||||
@ -140,7 +139,7 @@ class PathodHandler(tcp.BaseHandler):
|
|||||||
)
|
)
|
||||||
except tcp.NetLibError as v:
|
except tcp.NetLibError as v:
|
||||||
s = str(v)
|
s = str(v)
|
||||||
self.info(s)
|
lg(s)
|
||||||
self.addlog(dict(type="error", msg=s))
|
self.addlog(dict(type="error", msg=s))
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
@ -150,14 +149,14 @@ class PathodHandler(tcp.BaseHandler):
|
|||||||
method, path, httpversion = m.v
|
method, path, httpversion = m.v
|
||||||
else:
|
else:
|
||||||
s = "Invalid first line: %s" % repr(line)
|
s = "Invalid first line: %s" % repr(line)
|
||||||
self.info(s)
|
lg(s)
|
||||||
self.addlog(dict(type="error", msg=s))
|
self.addlog(dict(type="error", msg=s))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
headers = http.read_headers(self.rfile)
|
headers = http.read_headers(self.rfile)
|
||||||
if headers is None:
|
if headers is None:
|
||||||
s = "Invalid headers"
|
s = "Invalid headers"
|
||||||
self.info(s)
|
lg(s)
|
||||||
self.addlog(dict(type="error", msg=s))
|
self.addlog(dict(type="error", msg=s))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -195,13 +194,13 @@ class PathodHandler(tcp.BaseHandler):
|
|||||||
)
|
)
|
||||||
except http.HttpError as s:
|
except http.HttpError as s:
|
||||||
s = str(s)
|
s = str(s)
|
||||||
self.info(s)
|
lg(s)
|
||||||
self.addlog(dict(type="error", msg=s))
|
self.addlog(dict(type="error", msg=s))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
for i in self.server.anchors:
|
for i in self.server.anchors:
|
||||||
if i[0].match(path):
|
if i[0].match(path):
|
||||||
self.info("crafting anchor: %s" % path)
|
lg("crafting anchor: %s" % path)
|
||||||
again, retlog["response"] = self.serve_crafted(i[1])
|
again, retlog["response"] = self.serve_crafted(i[1])
|
||||||
self.addlog(retlog)
|
self.addlog(retlog)
|
||||||
return again
|
return again
|
||||||
@ -214,11 +213,11 @@ class PathodHandler(tcp.BaseHandler):
|
|||||||
self.settings.websocket_key = key
|
self.settings.websocket_key = key
|
||||||
if key and not spec:
|
if key and not spec:
|
||||||
spec = "ws"
|
spec = "ws"
|
||||||
self.info("crafting spec: %s" % spec)
|
lg("crafting spec: %s" % spec)
|
||||||
try:
|
try:
|
||||||
crafted = language.parse_response(spec)
|
crafted = language.parse_response(spec)
|
||||||
except language.ParseException as v:
|
except language.ParseException as v:
|
||||||
self.info("Parse error: %s" % v.msg)
|
lg("Parse error: %s" % v.msg)
|
||||||
crafted = language.http.make_error_response(
|
crafted = language.http.make_error_response(
|
||||||
"Parse Error",
|
"Parse Error",
|
||||||
"Error parsing response spec: %s\n" % v.msg + v.marked()
|
"Error parsing response spec: %s\n" % v.msg + v.marked()
|
||||||
@ -235,7 +234,7 @@ class PathodHandler(tcp.BaseHandler):
|
|||||||
))
|
))
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
self.info("app: %s %s" % (method, path))
|
lg("app: %s %s" % (method, path))
|
||||||
req = wsgi.Request("http", method, path, headers, content)
|
req = wsgi.Request("http", method, path, headers, content)
|
||||||
flow = wsgi.Flow(self.address, req)
|
flow = wsgi.Flow(self.address, req)
|
||||||
sn = self.connection.getsockname()
|
sn = self.connection.getsockname()
|
||||||
@ -248,28 +247,15 @@ class PathodHandler(tcp.BaseHandler):
|
|||||||
a.serve(flow, self.wfile)
|
a.serve(flow, self.wfile)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _log_bytes(self, header, data, hexdump):
|
|
||||||
s = []
|
|
||||||
if hexdump:
|
|
||||||
s.append("%s (hex dump):" % header)
|
|
||||||
for line in netlib.utils.hexdump(data):
|
|
||||||
s.append("\t%s %s %s" % line)
|
|
||||||
else:
|
|
||||||
s.append("%s (unprintables escaped):" % header)
|
|
||||||
s.append(netlib.utils.cleanBin(data))
|
|
||||||
self.info("\n".join(s))
|
|
||||||
|
|
||||||
def addlog(self, log):
|
def addlog(self, log):
|
||||||
# FIXME: The bytes in the log should not be escaped. We do this at the
|
# FIXME: The bytes in the log should not be escaped. We do this at the
|
||||||
# moment because JSON encoding can't handle binary data, and I don't
|
# moment because JSON encoding can't handle binary data, and I don't
|
||||||
# want to base64 everything.
|
# want to base64 everything.
|
||||||
if self.server.logreq:
|
if self.server.logreq:
|
||||||
bytes = self.rfile.get_log().encode("string_escape")
|
bytes = self.rfile.get_log().encode("string_escape")
|
||||||
self._log_bytes("Request", bytes, self.server.hexdump)
|
|
||||||
log["request_bytes"] = bytes
|
log["request_bytes"] = bytes
|
||||||
if self.server.logresp:
|
if self.server.logresp:
|
||||||
bytes = self.wfile.get_log().encode("string_escape")
|
bytes = self.wfile.get_log().encode("string_escape")
|
||||||
self._log_bytes("Response", bytes, self.server.hexdump)
|
|
||||||
log["response_bytes"] = bytes
|
log["response_bytes"] = bytes
|
||||||
self.server.add_log(log)
|
self.server.add_log(log)
|
||||||
|
|
||||||
@ -324,6 +310,7 @@ class Pathod(tcp.TCPServer):
|
|||||||
explain=False,
|
explain=False,
|
||||||
hexdump=False,
|
hexdump=False,
|
||||||
webdebug=False,
|
webdebug=False,
|
||||||
|
logfp=sys.stdout,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
addr: (address, port) tuple. If port is 0, a free port will be
|
addr: (address, port) tuple. If port is 0, a free port will be
|
||||||
@ -350,6 +337,7 @@ class Pathod(tcp.TCPServer):
|
|||||||
self.timeout, self.logreq = timeout, logreq
|
self.timeout, self.logreq = timeout, logreq
|
||||||
self.logresp, self.hexdump = logresp, hexdump
|
self.logresp, self.hexdump = logresp, hexdump
|
||||||
self.explain = explain
|
self.explain = explain
|
||||||
|
self.logfp = logfp
|
||||||
|
|
||||||
self.app = app.make_app(noapi, webdebug)
|
self.app = app.make_app(noapi, webdebug)
|
||||||
self.app.config["pathod"] = self
|
self.app.config["pathod"] = self
|
||||||
@ -378,7 +366,13 @@ class Pathod(tcp.TCPServer):
|
|||||||
return None, req
|
return None, req
|
||||||
|
|
||||||
def handle_client_connection(self, request, client_address):
|
def handle_client_connection(self, request, client_address):
|
||||||
h = PathodHandler(request, client_address, self, self.settings)
|
h = PathodHandler(
|
||||||
|
request,
|
||||||
|
client_address,
|
||||||
|
self,
|
||||||
|
self.logfp,
|
||||||
|
self.settings
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
h.handle()
|
h.handle()
|
||||||
h.finish()
|
h.finish()
|
||||||
|
@ -83,6 +83,7 @@ class _PaThread(threading.Thread):
|
|||||||
self.server = pathod.Pathod(
|
self.server = pathod.Pathod(
|
||||||
(self.iface, 0),
|
(self.iface, 0),
|
||||||
ssl = self.ssl,
|
ssl = self.ssl,
|
||||||
|
logfp = None,
|
||||||
**self.daemonargs
|
**self.daemonargs
|
||||||
)
|
)
|
||||||
self.name = "PathodThread (%s:%s)" % (
|
self.name = "PathodThread (%s:%s)" % (
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import cStringIO
|
||||||
from libpathod import pathod, version
|
from libpathod import pathod, version
|
||||||
from netlib import tcp, http
|
from netlib import tcp, http
|
||||||
import tutils
|
import tutils
|
||||||
@ -5,7 +6,8 @@ import tutils
|
|||||||
|
|
||||||
class TestPathod(object):
|
class TestPathod(object):
|
||||||
def test_logging(self):
|
def test_logging(self):
|
||||||
p = pathod.Pathod(("127.0.0.1", 0))
|
s = cStringIO.StringIO()
|
||||||
|
p = pathod.Pathod(("127.0.0.1", 0), logfp=s)
|
||||||
assert len(p.get_log()) == 0
|
assert len(p.get_log()) == 0
|
||||||
id = p.add_log(dict(s="foo"))
|
id = p.add_log(dict(s="foo"))
|
||||||
assert p.log_by_id(id)
|
assert p.log_by_id(id)
|
||||||
|
@ -20,7 +20,7 @@ def test_parse_size():
|
|||||||
|
|
||||||
def test_parse_anchor_spec():
|
def test_parse_anchor_spec():
|
||||||
assert utils.parse_anchor_spec("foo=200") == ("foo", "200")
|
assert utils.parse_anchor_spec("foo=200") == ("foo", "200")
|
||||||
assert utils.parse_anchor_spec("foo") == None
|
assert utils.parse_anchor_spec("foo") is None
|
||||||
|
|
||||||
|
|
||||||
def test_data_path():
|
def test_data_path():
|
||||||
|
Loading…
Reference in New Issue
Block a user