Version 0.2.8
This commit is contained in:
parent
f4274e1e82
commit
9a58a915c2
36 changed files with 1885 additions and 819 deletions
|
@ -3,20 +3,14 @@ Contains helpers for interacting with the console. Includes definition for the
|
|||
class that is used to run the console.
|
||||
"""
|
||||
|
||||
import StringIO
|
||||
import atexit
|
||||
import cmd2
|
||||
import os
|
||||
import re
|
||||
import readline
|
||||
import string
|
||||
import sys
|
||||
import itertools
|
||||
|
||||
from .util import PappyException
|
||||
from .colors import Styles, Colors, verb_color, scode_color, path_formatter, host_color
|
||||
from . import config
|
||||
from twisted.internet import defer
|
||||
from .colors import Colors
|
||||
|
||||
###################
|
||||
## Helper functions
|
||||
|
@ -29,229 +23,6 @@ def print_pappy_errors(func):
|
|||
print str(e)
|
||||
return catch
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def load_reqlist(line, allow_special=True, ids_only=False):
|
||||
"""
|
||||
load_reqlist(line, allow_special=True)
|
||||
A helper function for parsing a list of requests that are passed as an
|
||||
argument. If ``allow_special`` is True, then it will parse IDs such as
|
||||
``u123`` or ``s123``. Even if allow_special is false, it will still parse
|
||||
``m##`` IDs. Will print any errors with loading any of the requests and
|
||||
will return a list of all the requests which were successfully loaded.
|
||||
Returns a deferred.
|
||||
|
||||
:Returns: Twisted deferred
|
||||
"""
|
||||
from .http import Request
|
||||
# Parses a comma separated list of ids and returns a list of those requests
|
||||
# prints any errors
|
||||
ids = re.split(',\s*', line)
|
||||
reqs = []
|
||||
if not ids_only:
|
||||
for reqid in ids:
|
||||
try:
|
||||
req = yield Request.load_request(reqid, allow_special)
|
||||
reqs.append(req)
|
||||
except PappyException as e:
|
||||
print e
|
||||
defer.returnValue(reqs)
|
||||
else:
|
||||
defer.returnValue(ids)
|
||||
|
||||
def print_table(coldata, rows):
|
||||
"""
|
||||
Print a table.
|
||||
Coldata: List of dicts with info on how to print the columns.
|
||||
``name`` is the heading to give column,
|
||||
``width (optional)`` maximum width before truncating. 0 for unlimited.
|
||||
|
||||
Rows: List of tuples with the data to print
|
||||
"""
|
||||
|
||||
# Get the width of each column
|
||||
widths = []
|
||||
headers = []
|
||||
for data in coldata:
|
||||
if 'name' in data:
|
||||
headers.append(data['name'])
|
||||
else:
|
||||
headers.append('')
|
||||
empty_headers = True
|
||||
for h in headers:
|
||||
if h != '':
|
||||
empty_headers = False
|
||||
if not empty_headers:
|
||||
rows = [headers] + rows
|
||||
|
||||
for i in range(len(coldata)):
|
||||
col = coldata[i]
|
||||
if 'width' in col and col['width'] > 0:
|
||||
maxwidth = col['width']
|
||||
else:
|
||||
maxwidth = 0
|
||||
colwidth = 0
|
||||
for row in rows:
|
||||
printdata = row[i]
|
||||
if isinstance(printdata, dict):
|
||||
collen = len(str(printdata['data']))
|
||||
else:
|
||||
collen = len(str(printdata))
|
||||
if collen > colwidth:
|
||||
colwidth = collen
|
||||
if maxwidth > 0 and colwidth > maxwidth:
|
||||
widths.append(maxwidth)
|
||||
else:
|
||||
widths.append(colwidth)
|
||||
|
||||
# Print rows
|
||||
padding = 2
|
||||
is_heading = not empty_headers
|
||||
for row in rows:
|
||||
if is_heading:
|
||||
sys.stdout.write(Styles.TABLE_HEADER)
|
||||
for (col, width) in zip(row, widths):
|
||||
if isinstance(col, dict):
|
||||
printstr = str(col['data'])
|
||||
if 'color' in col:
|
||||
colors = col['color']
|
||||
formatter = None
|
||||
elif 'formatter' in col:
|
||||
colors = None
|
||||
formatter = col['formatter']
|
||||
else:
|
||||
colors = None
|
||||
formatter = None
|
||||
else:
|
||||
printstr = str(col)
|
||||
colors = None
|
||||
formatter = None
|
||||
if len(printstr) > width:
|
||||
trunc_printstr=printstr[:width]
|
||||
trunc_printstr=trunc_printstr[:-3]+'...'
|
||||
else:
|
||||
trunc_printstr=printstr
|
||||
if colors is not None:
|
||||
sys.stdout.write(colors)
|
||||
sys.stdout.write(trunc_printstr)
|
||||
sys.stdout.write(Colors.ENDC)
|
||||
elif formatter is not None:
|
||||
toprint = formatter(printstr, width)
|
||||
sys.stdout.write(toprint)
|
||||
else:
|
||||
sys.stdout.write(trunc_printstr)
|
||||
sys.stdout.write(' '*(width-len(printstr)))
|
||||
sys.stdout.write(' '*padding)
|
||||
if is_heading:
|
||||
sys.stdout.write(Colors.ENDC)
|
||||
is_heading = False
|
||||
sys.stdout.write('\n')
|
||||
sys.stdout.flush()
|
||||
|
||||
def print_requests(requests):
|
||||
"""
|
||||
Takes in a list of requests and prints a table with data on each of the
|
||||
requests. It's the same table that's used by ``ls``.
|
||||
"""
|
||||
rows = []
|
||||
for req in requests:
|
||||
rows.append(get_req_data_row(req))
|
||||
print_request_rows(rows)
|
||||
|
||||
def print_request_rows(request_rows):
|
||||
"""
|
||||
Takes in a list of request rows generated from :func:`pappyproxy.console.get_req_data_row`
|
||||
and prints a table with data on each of the
|
||||
requests. Used instead of :func:`pappyproxy.console.print_requests` if you
|
||||
can't count on storing all the requests in memory at once.
|
||||
"""
|
||||
# Print a table with info on all the requests in the list
|
||||
cols = [
|
||||
{'name':'ID'},
|
||||
{'name':'Verb'},
|
||||
{'name': 'Host'},
|
||||
{'name':'Path', 'width':40},
|
||||
{'name':'S-Code', 'width':16},
|
||||
{'name':'Req Len'},
|
||||
{'name':'Rsp Len'},
|
||||
{'name':'Time'},
|
||||
{'name':'Mngl'},
|
||||
]
|
||||
print_rows = []
|
||||
for row in request_rows:
|
||||
(reqid, verb, host, path, scode, qlen, slen, time, mngl) = row
|
||||
|
||||
verb = {'data':verb, 'color':verb_color(verb)}
|
||||
scode = {'data':scode, 'color':scode_color(scode)}
|
||||
host = {'data':host, 'color':host_color(host)}
|
||||
path = {'data':path, 'formatter':path_formatter}
|
||||
|
||||
print_rows.append((reqid, verb, host, path, scode, qlen, slen, time, mngl))
|
||||
print_table(cols, print_rows)
|
||||
|
||||
def get_req_data_row(request):
|
||||
"""
|
||||
Get the row data for a request to be printed.
|
||||
"""
|
||||
rid = request.reqid
|
||||
method = request.verb
|
||||
if 'host' in request.headers:
|
||||
host = request.headers['host']
|
||||
else:
|
||||
host = '??'
|
||||
path = request.full_path
|
||||
reqlen = len(request.body)
|
||||
rsplen = 'N/A'
|
||||
mangle_str = '--'
|
||||
|
||||
if request.unmangled:
|
||||
mangle_str = 'q'
|
||||
|
||||
if request.response:
|
||||
response_code = str(request.response.response_code) + \
|
||||
' ' + request.response.response_text
|
||||
rsplen = len(request.response.body)
|
||||
if request.response.unmangled:
|
||||
if mangle_str == '--':
|
||||
mangle_str = 's'
|
||||
else:
|
||||
mangle_str += '/s'
|
||||
else:
|
||||
response_code = ''
|
||||
|
||||
time_str = '--'
|
||||
if request.time_start and request.time_end:
|
||||
time_delt = request.time_end - request.time_start
|
||||
time_str = "%.2f" % time_delt.total_seconds()
|
||||
|
||||
return [rid, method, host, path, response_code,
|
||||
reqlen, rsplen, time_str, mangle_str]
|
||||
|
||||
def confirm(message, default='n'):
|
||||
"""
|
||||
A helper function to get confirmation from the user. It prints ``message``
|
||||
then asks the user to answer yes or no. Returns True if the user answers
|
||||
yes, otherwise returns False.
|
||||
"""
|
||||
if 'n' in default.lower():
|
||||
default = False
|
||||
else:
|
||||
default = True
|
||||
|
||||
print message
|
||||
if default:
|
||||
answer = raw_input('(Y/n) ')
|
||||
else:
|
||||
answer = raw_input('(y/N) ')
|
||||
|
||||
|
||||
if not answer:
|
||||
return default
|
||||
|
||||
if answer[0].lower() == 'y':
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
##########
|
||||
## Classes
|
||||
|
||||
|
@ -265,14 +36,16 @@ class ProxyCmd(cmd2.Cmd):
|
|||
# the \x01/\x02 are to make the prompt behave properly with the readline library
|
||||
self.prompt = 'pappy\x01' + Colors.YELLOW + '\x02> \x01' + Colors.ENDC + '\x02'
|
||||
self.debug = True
|
||||
self.session = kwargs['session']
|
||||
del kwargs['session']
|
||||
|
||||
self._cmds = {}
|
||||
self._aliases = {}
|
||||
|
||||
atexit.register(self.save_histfile)
|
||||
readline.set_history_length(config.HISTSIZE)
|
||||
readline.set_history_length(self.session.config.histsize)
|
||||
if os.path.exists('cmdhistory'):
|
||||
if config.HISTSIZE != 0:
|
||||
if self.session.config.histsize != 0:
|
||||
readline.read_history_file('cmdhistory')
|
||||
else:
|
||||
os.remove('cmdhistory')
|
||||
|
@ -338,8 +111,8 @@ class ProxyCmd(cmd2.Cmd):
|
|||
|
||||
def save_histfile(self):
|
||||
# Write the command to the history file
|
||||
if config.HISTSIZE != 0:
|
||||
readline.set_history_length(config.HISTSIZE)
|
||||
if self.session.config.histsize != 0:
|
||||
readline.set_history_length(self.session.config.histsize)
|
||||
readline.write_history_file('cmdhistory')
|
||||
|
||||
def get_names(self):
|
||||
|
@ -379,14 +152,3 @@ class ProxyCmd(cmd2.Cmd):
|
|||
for command, alias in alias_list:
|
||||
self.add_alias(command, alias)
|
||||
|
||||
# Taken from http://stackoverflow.com/questions/16571150/how-to-capture-stdout-output-from-a-python-function-call
|
||||
# then modified
|
||||
class Capturing():
|
||||
def __enter__(self):
|
||||
self._stdout = sys.stdout
|
||||
sys.stdout = self._stringio = StringIO.StringIO()
|
||||
return self
|
||||
|
||||
def __exit__(self, *args):
|
||||
self.val = self._stringio.getvalue()
|
||||
sys.stdout = self._stdout
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue