Version 0.2.5
This commit is contained in:
parent
0b6a63ddbb
commit
1acaeccf5e
14 changed files with 119 additions and 11 deletions
15
README.md
15
README.md
|
@ -223,10 +223,18 @@ The following commands can be used to view requests and responses
|
||||||
| `viq <id(s)>` | view_request_info, viq | View additional information about requests. Includes the target port, if SSL was used, applied tags, and other information. |
|
| `viq <id(s)>` | view_request_info, viq | View additional information about requests. Includes the target port, if SSL was used, applied tags, and other information. |
|
||||||
| `vfq <id(s)>` | view_full_request, vfq | [V]iew [F]ull Re[Q]uest, prints the full request including headers and data. |
|
| `vfq <id(s)>` | view_full_request, vfq | [V]iew [F]ull Re[Q]uest, prints the full request including headers and data. |
|
||||||
| `vbq <id(s)>` | view_request_bytes, vbq | [V]iew [B]ytes of Re[Q]uest, prints the full request including headers and data without coloring or additional newlines. Use this if you want to write a request to a file. |
|
| `vbq <id(s)>` | view_request_bytes, vbq | [V]iew [B]ytes of Re[Q]uest, prints the full request including headers and data without coloring or additional newlines. Use this if you want to write a request to a file. |
|
||||||
|
| `ppq <id(s)> [format]` | pretty_print_request, ppq | Pretty print a request. If a format is given, it will try and print the body of the request with that format. Otherwise it will make a guess based off of the Content-Type header. |
|
||||||
| `vhq <id(s)>` | view_request_headers, vhq | [V]iew [H]eaders of a Re[Q]uest. Prints just the headers of a request. |
|
| `vhq <id(s)>` | view_request_headers, vhq | [V]iew [H]eaders of a Re[Q]uest. Prints just the headers of a request. |
|
||||||
| `vfs <id(s)>` | view_full_response, vfs |[V]iew [F]ull Re[S]ponse, prints the full response associated with a request including headers and data. |
|
| `vfs <id(s)>` | view_full_response, vfs |[V]iew [F]ull Re[S]ponse, prints the full response associated with a request including headers and data. |
|
||||||
| `vbs <id(s)>` | view_response_bytes, vbs | [V]iew [B]ytes of Re[S]ponse, prints the full response including headers and data without coloring or additional newlines. Use this if you want to write a response to a file. |
|
|
||||||
| `vhs <id(s)>` | view_response_headers, vhs | [V]iew [H]eaders of a Re[S]ponse. Prints just the headers of a response associated with a request. |
|
| `vhs <id(s)>` | view_response_headers, vhs | [V]iew [H]eaders of a Re[S]ponse. Prints just the headers of a response associated with a request. |
|
||||||
|
| `vbs <id(s)>` | view_response_bytes, vbs | [V]iew [B]ytes of Re[S]ponse, prints the full response including headers and data without coloring or additional newlines. Use this if you want to write a response to a file. |
|
||||||
|
| `pps <id(s)> [format]` | pretty_print_response, pps | Pretty print a response. If a format is given, it will try and print the body of the response with that format. Otherwise it will make a guess based off of the Content-Type header. |
|
||||||
|
|
||||||
|
Available formats for `ppq` and `pps` commands:
|
||||||
|
|
||||||
|
| Format | Description |
|
||||||
|
|:-------|:------------|
|
||||||
|
| `json` | Print as JSON |
|
||||||
|
|
||||||
The table shown by `ls` will have the following columns:
|
The table shown by `ls` will have the following columns:
|
||||||
|
|
||||||
|
@ -944,6 +952,11 @@ Changelog
|
||||||
---------
|
---------
|
||||||
The boring part of the readme
|
The boring part of the readme
|
||||||
|
|
||||||
|
* 0.2.5
|
||||||
|
* Requests sent with repeater now are given `repeater` tag
|
||||||
|
* Add ppq and pps commands
|
||||||
|
* Look at the pretty prompt
|
||||||
|
* Bugfixes
|
||||||
* 0.2.4
|
* 0.2.4
|
||||||
* Add command history saving between sessions
|
* Add command history saving between sessions
|
||||||
* Add html encoder/decoder
|
* Add html encoder/decoder
|
||||||
|
|
|
@ -59,9 +59,9 @@ author = u'Rob Glew'
|
||||||
# built documents.
|
# built documents.
|
||||||
#
|
#
|
||||||
# The short X.Y version.
|
# The short X.Y version.
|
||||||
version = u'0.2.4'
|
version = u'0.2.5'
|
||||||
# The full version, including alpha/beta/rc tags.
|
# The full version, including alpha/beta/rc tags.
|
||||||
release = u'0.2.4'
|
release = u'0.2.5'
|
||||||
|
|
||||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||||
# for a list of supported languages.
|
# for a list of supported languages.
|
||||||
|
|
|
@ -99,6 +99,8 @@ class CommServer(LineReceiver):
|
||||||
def action_submit_request(self, data):
|
def action_submit_request(self, data):
|
||||||
message = base64.b64decode(data['full_message'])
|
message = base64.b64decode(data['full_message'])
|
||||||
req = yield Request.submit_new(data['host'], data['port'], data['is_ssl'], message)
|
req = yield Request.submit_new(data['host'], data['port'], data['is_ssl'], message)
|
||||||
|
if 'tags' in data:
|
||||||
|
req.tags = data['tags']
|
||||||
yield req.async_deep_save()
|
yield req.async_deep_save()
|
||||||
|
|
||||||
retdata = {}
|
retdata = {}
|
||||||
|
|
|
@ -262,7 +262,7 @@ class ProxyCmd(cmd2.Cmd):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.prompt = 'pappy> '
|
self.prompt = 'pappy' + Colors.YELLOW + '> ' + Colors.ENDC
|
||||||
self.debug = True
|
self.debug = True
|
||||||
|
|
||||||
self._cmds = {}
|
self._cmds = {}
|
||||||
|
|
|
@ -803,7 +803,17 @@ class HTTPMessage(object):
|
||||||
stripped = True
|
stripped = True
|
||||||
elif key.lower() == 'content-length':
|
elif key.lower() == 'content-length':
|
||||||
# We use our own content length
|
# We use our own content length
|
||||||
self._data_obj = LengthData(int(val))
|
if self._data_obj and self._data_obj.complete:
|
||||||
|
# We're regenerating or something so we want to base this header
|
||||||
|
# off our existing body
|
||||||
|
val = self._data_obj.body
|
||||||
|
self._data_obj = LengthData(len(val))
|
||||||
|
if len(val) > 0:
|
||||||
|
self._data_obj.add_data(val)
|
||||||
|
self._encoding_type = ENCODE_NONE
|
||||||
|
self.complete = True
|
||||||
|
else:
|
||||||
|
self._data_obj = LengthData(int(val))
|
||||||
|
|
||||||
return (not stripped)
|
return (not stripped)
|
||||||
|
|
||||||
|
@ -1972,8 +1982,10 @@ class Request(HTTPMessage):
|
||||||
"""
|
"""
|
||||||
from .proxy import ProxyClientFactory, get_next_connection_id, ClientTLSContext
|
from .proxy import ProxyClientFactory, get_next_connection_id, ClientTLSContext
|
||||||
|
|
||||||
new_obj = Request(full_request)
|
new_req = Request(full_request)
|
||||||
factory = ProxyClientFactory(new_obj, save_all=False)
|
new_req.is_ssl = is_ssl
|
||||||
|
new_req.port = port
|
||||||
|
factory = ProxyClientFactory(new_req, save_all=False)
|
||||||
factory.connection_id = get_next_connection_id()
|
factory.connection_id = get_next_connection_id()
|
||||||
if is_ssl:
|
if is_ssl:
|
||||||
reactor.connectSSL(host, port, factory, ClientTLSContext())
|
reactor.connectSSL(host, port, factory, ClientTLSContext())
|
||||||
|
|
|
@ -109,7 +109,7 @@ def active_intercepting_macros():
|
||||||
Returns a list of the active intercepting macro objects. Modifying
|
Returns a list of the active intercepting macro objects. Modifying
|
||||||
this list will not affect which macros are active.
|
this list will not affect which macros are active.
|
||||||
"""
|
"""
|
||||||
return pappyproxy.pappy.server_factory.intercepting_macros[:]
|
return [v for k, v in pappyproxy.pappy.server_factory.intercepting_macros.iteritems() ]
|
||||||
|
|
||||||
def in_memory_reqs():
|
def in_memory_reqs():
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -54,6 +54,8 @@ def _code_helper(line, func, copy=True):
|
||||||
args = shlex.split(line)
|
args = shlex.split(line)
|
||||||
if not args:
|
if not args:
|
||||||
s = clipboard.paste()
|
s = clipboard.paste()
|
||||||
|
print 'Will decode:'
|
||||||
|
print printable_data(s)
|
||||||
s = func(s)
|
s = func(s)
|
||||||
if copy:
|
if copy:
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -124,7 +124,7 @@ def list_int_macros(line):
|
||||||
running = []
|
running = []
|
||||||
not_running = []
|
not_running = []
|
||||||
for macro in loaded_int_macros:
|
for macro in loaded_int_macros:
|
||||||
if macro.name in active_intercepting_macros():
|
if macro.name in [m.name for m in active_intercepting_macros()]:
|
||||||
running.append(macro)
|
running.append(macro)
|
||||||
else:
|
else:
|
||||||
not_running.append(macro)
|
not_running.append(macro)
|
||||||
|
|
|
@ -212,7 +212,11 @@ def intercept(line):
|
||||||
editor = 'vi'
|
editor = 'vi'
|
||||||
if 'EDITOR' in os.environ:
|
if 'EDITOR' in os.environ:
|
||||||
editor = os.environ['EDITOR']
|
editor = os.environ['EDITOR']
|
||||||
subprocess.call([editor, to_edit])
|
additional_args = []
|
||||||
|
if editor == 'vim':
|
||||||
|
# prevent adding additional newline
|
||||||
|
additional_args.append('-b')
|
||||||
|
subprocess.call([editor, to_edit] + additional_args)
|
||||||
stdscr.clear()
|
stdscr.clear()
|
||||||
deferred.callback(None)
|
deferred.callback(None)
|
||||||
finally:
|
finally:
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
import crochet
|
import crochet
|
||||||
import datetime
|
import datetime
|
||||||
|
import json
|
||||||
import pappyproxy
|
import pappyproxy
|
||||||
|
import pygments
|
||||||
|
import pprint
|
||||||
import shlex
|
import shlex
|
||||||
|
|
||||||
from pappyproxy.console import load_reqlist, print_table, print_request_rows, get_req_data_row
|
from pappyproxy.console import load_reqlist, print_table, print_request_rows, get_req_data_row
|
||||||
|
@ -9,6 +12,8 @@ from pappyproxy.http import Request
|
||||||
from twisted.internet import defer
|
from twisted.internet import defer
|
||||||
from pappyproxy.plugin import main_context_ids
|
from pappyproxy.plugin import main_context_ids
|
||||||
from pappyproxy.colors import Colors, Styles, verb_color, scode_color, path_formatter, host_color
|
from pappyproxy.colors import Colors, Styles, verb_color, scode_color, path_formatter, host_color
|
||||||
|
from pygments.formatters import TerminalFormatter
|
||||||
|
from pygments.lexers.data import JsonLexer
|
||||||
|
|
||||||
###################
|
###################
|
||||||
## Helper functions
|
## Helper functions
|
||||||
|
@ -91,6 +96,17 @@ def print_tree(tree):
|
||||||
# Prints a tree. Takes in a sorted list of path tuples
|
# Prints a tree. Takes in a sorted list of path tuples
|
||||||
_print_tree_helper(tree, 0, [])
|
_print_tree_helper(tree, 0, [])
|
||||||
|
|
||||||
|
def pretty_print_body(fmt, body):
|
||||||
|
if fmt.lower() == 'json':
|
||||||
|
try:
|
||||||
|
d = json.loads(body.strip())
|
||||||
|
except:
|
||||||
|
raise PappyException('Body could not be parsed as JSON')
|
||||||
|
s = json.dumps(d, indent=4, sort_keys=True)
|
||||||
|
print pygments.highlight(s, JsonLexer(), TerminalFormatter())
|
||||||
|
else:
|
||||||
|
raise PappyException('%s is not a valid format' % fmt)
|
||||||
|
|
||||||
def _get_tree_prefix(depth, print_bars, last):
|
def _get_tree_prefix(depth, print_bars, last):
|
||||||
if depth == 0:
|
if depth == 0:
|
||||||
return u''
|
return u''
|
||||||
|
@ -248,6 +264,22 @@ def view_request_bytes(line):
|
||||||
print '-'*30
|
print '-'*30
|
||||||
print ''
|
print ''
|
||||||
|
|
||||||
|
@crochet.wait_for(timeout=None)
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def pretty_print_request(line):
|
||||||
|
"""
|
||||||
|
Print the body of the request pretty printed.
|
||||||
|
Usage: pretty_print_request <format> <reqid(s)>
|
||||||
|
"""
|
||||||
|
args = shlex.split(line)
|
||||||
|
if len(args) < 2:
|
||||||
|
raise PappyException("Usage: pretty_print_request <format> <reqid(s)>")
|
||||||
|
reqids = args[1]
|
||||||
|
|
||||||
|
reqs = yield load_reqlist(reqids)
|
||||||
|
for req in reqs:
|
||||||
|
pretty_print_body(args[0], req.body)
|
||||||
|
|
||||||
@crochet.wait_for(timeout=None)
|
@crochet.wait_for(timeout=None)
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def view_response_headers(line):
|
def view_response_headers(line):
|
||||||
|
@ -297,6 +329,25 @@ def view_response_bytes(line):
|
||||||
else:
|
else:
|
||||||
print "Request %s does not have a response" % req.reqid
|
print "Request %s does not have a response" % req.reqid
|
||||||
|
|
||||||
|
@crochet.wait_for(timeout=None)
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def pretty_print_response(line):
|
||||||
|
"""
|
||||||
|
Print the body of the request pretty printed.
|
||||||
|
Usage: pretty_print_request <format> <reqid(s)>
|
||||||
|
"""
|
||||||
|
args = shlex.split(line)
|
||||||
|
if len(args) < 2:
|
||||||
|
raise PappyException("Usage: pretty_print_request <format> <reqid(s)>")
|
||||||
|
reqids = args[1]
|
||||||
|
|
||||||
|
reqs = yield load_reqlist(reqids)
|
||||||
|
for req in reqs:
|
||||||
|
if req.response:
|
||||||
|
pretty_print_body(args[0], req.response.body)
|
||||||
|
else:
|
||||||
|
print 'No response associated with request %s' % req.reqid
|
||||||
|
|
||||||
@crochet.wait_for(timeout=None)
|
@crochet.wait_for(timeout=None)
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def dump_response(line):
|
def dump_response(line):
|
||||||
|
@ -345,9 +396,11 @@ def load_cmds(cmd):
|
||||||
'view_request_headers': (view_request_headers, None),
|
'view_request_headers': (view_request_headers, None),
|
||||||
'view_full_request': (view_full_request, None),
|
'view_full_request': (view_full_request, None),
|
||||||
'view_request_bytes': (view_request_bytes, None),
|
'view_request_bytes': (view_request_bytes, None),
|
||||||
|
'pretty_print_request': (pretty_print_request, None),
|
||||||
'view_response_headers': (view_response_headers, None),
|
'view_response_headers': (view_response_headers, None),
|
||||||
'view_full_response': (view_full_response, None),
|
'view_full_response': (view_full_response, None),
|
||||||
'view_response_bytes': (view_response_bytes, None),
|
'view_response_bytes': (view_response_bytes, None),
|
||||||
|
'pretty_print_response': (pretty_print_response, None),
|
||||||
'site_map': (site_map, None),
|
'site_map': (site_map, None),
|
||||||
'dump_response': (dump_response, None),
|
'dump_response': (dump_response, None),
|
||||||
})
|
})
|
||||||
|
@ -357,9 +410,11 @@ def load_cmds(cmd):
|
||||||
('view_request_headers', 'vhq'),
|
('view_request_headers', 'vhq'),
|
||||||
('view_full_request', 'vfq'),
|
('view_full_request', 'vfq'),
|
||||||
('view_request_bytes', 'vbq'),
|
('view_request_bytes', 'vbq'),
|
||||||
|
('pretty_print_request', 'ppq'),
|
||||||
('view_response_headers', 'vhs'),
|
('view_response_headers', 'vhs'),
|
||||||
('view_full_response', 'vfs'),
|
('view_full_response', 'vfs'),
|
||||||
('view_response_bytes', 'vbs'),
|
('view_response_bytes', 'vbs'),
|
||||||
|
('pretty_print_response', 'pps'),
|
||||||
('site_map', 'sm'),
|
('site_map', 'sm'),
|
||||||
#('dump_response', 'dr'),
|
#('dump_response', 'dr'),
|
||||||
])
|
])
|
||||||
|
|
|
@ -119,6 +119,7 @@ def submit_current_buffer():
|
||||||
full_request = '\n'.join(curbuf)
|
full_request = '\n'.join(curbuf)
|
||||||
commdata = {'action': 'submit',
|
commdata = {'action': 'submit',
|
||||||
'full_message': base64.b64encode(full_request),
|
'full_message': base64.b64encode(full_request),
|
||||||
|
'tags': ['repeater'],
|
||||||
'port': int(vim.eval("s:repport")),
|
'port': int(vim.eval("s:repport")),
|
||||||
'host': vim.eval("s:rephost")}
|
'host': vim.eval("s:rephost")}
|
||||||
if vim.eval("s:repisssl") == '1':
|
if vim.eval("s:repisssl") == '1':
|
||||||
|
|
|
@ -5,6 +5,7 @@ SHORT_NAME = '{{short_name}}'
|
||||||
runargs = []
|
runargs = []
|
||||||
|
|
||||||
def init(args):
|
def init(args):
|
||||||
|
global runargs
|
||||||
runargs = args
|
runargs = args
|
||||||
|
|
||||||
def mangle_request(request):
|
def mangle_request(request):
|
||||||
|
|
|
@ -846,6 +846,24 @@ def test_request_url_blankpath():
|
||||||
assert r.full_path == '/?foo=bar'
|
assert r.full_path == '/?foo=bar'
|
||||||
assert r.url == 'https://www.google.com?foo=bar'
|
assert r.url == 'https://www.google.com?foo=bar'
|
||||||
|
|
||||||
|
def test_request_modify_header2():
|
||||||
|
r = http.Request(('POST /some/path HTTP/1.1\r\n'
|
||||||
|
'Host: test.host.thing\r\n'
|
||||||
|
'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:43.0) Gecko/20100101 Firefox/43.0\r\n'
|
||||||
|
'Content-Length: 282\r\n'
|
||||||
|
'Connection: keep-alive\r\n'
|
||||||
|
'\r\n'
|
||||||
|
'a|b|c|d'))
|
||||||
|
r2 = r.copy()
|
||||||
|
r2.headers['User-Agent'] = 'Moziller/6.9'
|
||||||
|
assert r2.full_message == ('POST /some/path HTTP/1.1\r\n'
|
||||||
|
'Host: test.host.thing\r\n'
|
||||||
|
'User-Agent: Moziller/6.9\r\n'
|
||||||
|
'Content-Length: 7\r\n'
|
||||||
|
'Connection: keep-alive\r\n'
|
||||||
|
'\r\n'
|
||||||
|
'a|b|c|d')
|
||||||
|
|
||||||
|
|
||||||
####################
|
####################
|
||||||
## Response tests
|
## Response tests
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -3,7 +3,7 @@
|
||||||
import pkgutil
|
import pkgutil
|
||||||
from setuptools import setup, find_packages
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
VERSION = '0.2.4'
|
VERSION = '0.2.5'
|
||||||
|
|
||||||
setup(name='pappyproxy',
|
setup(name='pappyproxy',
|
||||||
version=VERSION,
|
version=VERSION,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue