JSON pretty-printing.

Also rename the display modes ("pretty" instead of "indent"), and expand the
built-in documentation.
This commit is contained in:
Aldo Cortesi 2011-06-30 13:27:27 +12:00
parent 46ec8f52e7
commit 18d4c3a9e9
3 changed files with 63 additions and 26 deletions

View File

@ -224,7 +224,7 @@ class ConnectionViewHeader(WWrap):
VIEW_BODY_RAW = 0 VIEW_BODY_RAW = 0
VIEW_BODY_BINARY = 1 VIEW_BODY_BINARY = 1
VIEW_BODY_INDENT = 2 VIEW_BODY_PRETTY = 2
VIEW_FLOW_REQUEST = 0 VIEW_FLOW_REQUEST = 0
VIEW_FLOW_RESPONSE = 1 VIEW_FLOW_RESPONSE = 1
@ -418,8 +418,8 @@ class ConnectionView(WWrap):
self.state.view_body_mode = VIEW_BODY_RAW self.state.view_body_mode = VIEW_BODY_RAW
elif v == "h": elif v == "h":
self.state.view_body_mode = VIEW_BODY_BINARY self.state.view_body_mode = VIEW_BODY_BINARY
elif v == "i": elif v == "p":
self.state.view_body_mode = VIEW_BODY_INDENT self.state.view_body_mode = VIEW_BODY_PRETTY
self.master.refresh_connection(self.flow) self.master.refresh_connection(self.flow)
def keypress(self, size, key): def keypress(self, size, key):
@ -442,7 +442,7 @@ class ConnectionView(WWrap):
"View", "View",
( (
("raw", "r"), ("raw", "r"),
("indent", "i"), ("pretty", "p"),
("hex", "h"), ("hex", "h"),
), ),
self._changeview self._changeview
@ -881,8 +881,8 @@ class ConsoleMaster(flow.FlowMaster):
self.spawn_external_viewer(serr, None) self.spawn_external_viewer(serr, None)
self.refresh_connection(f) self.refresh_connection(f)
def _trailer(self, content, txt): def _trailer(self, clen, txt):
rem = len(content) - VIEW_CUTOFF rem = clen - VIEW_CUTOFF
if rem > 0: if rem > 0:
txt.append(urwid.Text("")) txt.append(urwid.Text(""))
txt.append( txt.append(
@ -893,12 +893,12 @@ class ConsoleMaster(flow.FlowMaster):
) )
) )
def _view_conn_normal(self, content, txt): def _view_conn_raw(self, content, txt):
for i in content[:VIEW_CUTOFF].splitlines(): for i in utils.cleanBin(content[:VIEW_CUTOFF]).splitlines():
txt.append( txt.append(
urwid.Text(("text", i)) urwid.Text(("text", i))
) )
self._trailer(content, txt) self._trailer(len(content), txt)
def _view_conn_binary(self, content, txt): def _view_conn_binary(self, content, txt):
for offset, hex, s in utils.hexdump(content[:VIEW_CUTOFF]): for offset, hex, s in utils.hexdump(content[:VIEW_CUTOFF]):
@ -909,14 +909,40 @@ class ConsoleMaster(flow.FlowMaster):
" ", " ",
("text", s), ("text", s),
])) ]))
self._trailer(content, txt) self._trailer(len(content), txt)
def _view_conn_pretty(self, content, txt): def _view_conn_xmlish(self, content, txt):
for i in utils.pretty_xmlish(content[:VIEW_CUTOFF]): for i in utils.pretty_xmlish(content[:VIEW_CUTOFF]):
txt.append( txt.append(
urwid.Text(("text", i)), urwid.Text(("text", i)),
) )
self._trailer(content, txt) self._trailer(len(content), txt)
def _view_conn_json(self, lines, txt):
sofar = 0
for i in lines:
sofar += len(i)
txt.append(
urwid.Text(("text", i)),
)
if sofar > VIEW_CUTOFF:
break
self._trailer(sum(len(i) for i in lines), txt)
def _find_pretty_view(self, content, hdrItems, txt):
ctype = None
for i in hdrItems:
if i[0] == "content-type":
ctype = i[1]
break
if utils.isXML(content):
return self._view_conn_xmlish(content, txt)
if ctype and "application/json" in ctype:
lines = utils.pretty_json(content)
if lines:
return self._view_conn_json(lines, txt)
return self._view_conn_raw(content, txt)
@utils.LRUCache(20) @utils.LRUCache(20)
def _cached_conn_text(self, content, hdrItems, viewmode): def _cached_conn_text(self, content, hdrItems, viewmode):
@ -934,19 +960,10 @@ class ConsoleMaster(flow.FlowMaster):
if content: if content:
if viewmode == VIEW_BODY_BINARY: if viewmode == VIEW_BODY_BINARY:
self._view_conn_binary(content, txt) self._view_conn_binary(content, txt)
elif viewmode == VIEW_BODY_INDENT: elif viewmode == VIEW_BODY_PRETTY:
if utils.isXML(content): self._find_pretty_view(content, hdrItems, txt)
self._view_conn_pretty(content, txt)
else:
if utils.isBin(content):
self._view_conn_binary(content, txt)
else:
self._view_conn_normal(content, txt)
else: else:
if utils.isBin(content): self._view_conn_raw(content, txt)
self._view_conn_binary(content, txt)
else:
self._view_conn_normal(content, txt)
return urwid.ListBox(txt) return urwid.ListBox(txt)
def _readflow(self, path): def _readflow(self, path):
@ -1206,7 +1223,10 @@ class ConsoleMaster(flow.FlowMaster):
keys = [ keys = [
("b", "save request/response body"), ("b", "save request/response body"),
("e", "edit request/response"), ("e", "edit request/response"),
("m", "change view mode (raw, indent, hex)"), ("m", "change view mode (raw, pretty, hex)"),
(None, " raw: raw data"),
(None, " pretty: pretty-print XML, HTML and JSON"),
(None, " hex: hex dump"),
("p", "previous flow"), ("p", "previous flow"),
("v", "view body in external viewer"), ("v", "view body in external viewer"),
("|", "run script"), ("|", "run script"),

View File

@ -13,6 +13,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import re, os, subprocess, datetime, textwrap, errno, sys, time, functools import re, os, subprocess, datetime, textwrap, errno, sys, time, functools
import json
CERT_SLEEP_TIME = 1 CERT_SLEEP_TIME = 1
@ -111,6 +112,14 @@ def pretty_xmlish(s):
return data return data
def pretty_json(s):
try:
p = json.loads(s)
except ValueError:
return None
return json.dumps(p, sort_keys=True, indent=4).split("\n")
def hexdump(s): def hexdump(s):
""" """
Returns a set of typles: Returns a set of typles:

View File

@ -1,4 +1,4 @@
import textwrap, cStringIO, os, time, re import textwrap, cStringIO, os, time, re, json
import libpry import libpry
from libmproxy import utils from libmproxy import utils
@ -283,6 +283,13 @@ class upretty_xmlish(libpry.AutoTree):
assert utils.pretty_xmlish(s) == ["gobbledygook"] assert utils.pretty_xmlish(s) == ["gobbledygook"]
class upretty_json(libpry.AutoTree):
def test_one(self):
s = json.dumps({"foo": 1})
assert utils.pretty_json(s)
assert not utils.pretty_json("moo")
class udummy_ca(libpry.AutoTree): class udummy_ca(libpry.AutoTree):
def test_all(self): def test_all(self):
d = self.tmpdir() d = self.tmpdir()
@ -368,6 +375,7 @@ tests = [
uHeaders(), uHeaders(),
uData(), uData(),
upretty_xmlish(), upretty_xmlish(),
upretty_json(),
udummy_ca(), udummy_ca(),
udummy_cert(), udummy_cert(),
uLRUCache(), uLRUCache(),