mirror of
https://github.com/zhigang1992/mitmproxy.git
synced 2026-04-23 20:00:09 +08:00
Merge pull request #1211 from cortesi/pathod
WIP: Radical webectomy of pathod
This commit is contained in:
180
pathod/app.py
180
pathod/app.py
@@ -1,180 +0,0 @@
|
||||
import logging
|
||||
import pprint
|
||||
import io
|
||||
import copy
|
||||
from flask import Flask, jsonify, render_template, request, abort, make_response
|
||||
from . import version, language
|
||||
from netlib.http import user_agents
|
||||
from netlib import strutils
|
||||
|
||||
logging.basicConfig(level="DEBUG")
|
||||
EXAMPLE_HOST = "example.com"
|
||||
EXAMPLE_WEBSOCKET_KEY = "examplekey"
|
||||
|
||||
# pylint: disable=unused-variable
|
||||
|
||||
|
||||
def make_app(noapi, debug):
|
||||
app = Flask(__name__)
|
||||
app.debug = debug
|
||||
|
||||
if not noapi:
|
||||
@app.route('/api/info')
|
||||
def api_info():
|
||||
return jsonify(
|
||||
version=version.IVERSION
|
||||
)
|
||||
|
||||
@app.route('/api/log')
|
||||
def api_log():
|
||||
return jsonify(
|
||||
log=app.config["pathod"].get_log()
|
||||
)
|
||||
|
||||
@app.route('/api/clear_log')
|
||||
def api_clear_log():
|
||||
app.config["pathod"].clear_log()
|
||||
return "OK"
|
||||
|
||||
def render(s, cacheable, **kwargs):
|
||||
kwargs["noapi"] = app.config["pathod"].noapi
|
||||
kwargs["nocraft"] = app.config["pathod"].nocraft
|
||||
kwargs["craftanchor"] = app.config["pathod"].craftanchor
|
||||
resp = make_response(render_template(s, **kwargs), 200)
|
||||
if cacheable:
|
||||
resp.headers["Cache-control"] = "public, max-age=4320"
|
||||
return resp
|
||||
|
||||
@app.route('/')
|
||||
@app.route('/index.html')
|
||||
def index():
|
||||
return render(
|
||||
"index.html",
|
||||
True,
|
||||
section="main",
|
||||
version=version.VERSION
|
||||
)
|
||||
|
||||
@app.route('/download')
|
||||
@app.route('/download.html')
|
||||
def download():
|
||||
return render(
|
||||
"download.html", True, section="download", version=version.VERSION
|
||||
)
|
||||
|
||||
@app.route('/about')
|
||||
@app.route('/about.html')
|
||||
def about():
|
||||
return render("about.html", True, section="about")
|
||||
|
||||
@app.route('/docs/pathod')
|
||||
def docs_pathod():
|
||||
return render(
|
||||
"docs_pathod.html", True, section="docs", subsection="pathod"
|
||||
)
|
||||
|
||||
@app.route('/docs/language')
|
||||
def docs_language():
|
||||
return render(
|
||||
"docs_lang.html", True,
|
||||
section="docs", uastrings=user_agents.UASTRINGS,
|
||||
subsection="lang"
|
||||
)
|
||||
|
||||
@app.route('/docs/pathoc')
|
||||
def docs_pathoc():
|
||||
return render(
|
||||
"docs_pathoc.html", True, section="docs", subsection="pathoc"
|
||||
)
|
||||
|
||||
@app.route('/docs/lib_pathod')
|
||||
def docs_lib_pathod():
|
||||
return render(
|
||||
"docs_lib_pathod.html", True, section="docs", subsection="pathod"
|
||||
)
|
||||
|
||||
@app.route('/docs/test')
|
||||
def docs_test():
|
||||
return render(
|
||||
"docs_test.html", True, section="docs", subsection="test"
|
||||
)
|
||||
|
||||
@app.route('/log')
|
||||
def log():
|
||||
if app.config["pathod"].noapi:
|
||||
abort(404)
|
||||
return render(
|
||||
"log.html",
|
||||
False,
|
||||
section="log",
|
||||
log=app.config["pathod"].get_log()
|
||||
)
|
||||
|
||||
@app.route('/log/<int:lid>')
|
||||
def onelog(lid):
|
||||
item = app.config["pathod"].log_by_id(int(lid))
|
||||
if not item:
|
||||
abort(404)
|
||||
l = pprint.pformat(item)
|
||||
return render("onelog.html", False, section="log", alog=l, lid=lid)
|
||||
|
||||
def _preview(is_request):
|
||||
if is_request:
|
||||
template = "request_preview.html"
|
||||
else:
|
||||
template = "response_preview.html"
|
||||
|
||||
spec = request.args["spec"]
|
||||
|
||||
args = dict(
|
||||
spec=spec,
|
||||
section="main",
|
||||
syntaxerror=None,
|
||||
error=None,
|
||||
)
|
||||
if not spec.strip():
|
||||
args["error"] = "Can't parse an empty spec."
|
||||
return render(template, False, **args)
|
||||
|
||||
try:
|
||||
if is_request:
|
||||
r = language.parse_pathoc(spec).next()
|
||||
else:
|
||||
r = language.parse_pathod(spec).next()
|
||||
except language.ParseException as v:
|
||||
args["syntaxerror"] = str(v)
|
||||
args["marked"] = v.marked()
|
||||
return render(template, False, **args)
|
||||
|
||||
s = io.BytesIO()
|
||||
|
||||
settings = copy.copy(app.config["pathod"].settings)
|
||||
settings.request_host = EXAMPLE_HOST
|
||||
settings.websocket_key = EXAMPLE_WEBSOCKET_KEY
|
||||
|
||||
safe = r.preview_safe()
|
||||
err, safe = app.config["pathod"].check_policy(
|
||||
safe,
|
||||
settings
|
||||
)
|
||||
if err:
|
||||
args["error"] = err
|
||||
return render(template, False, **args)
|
||||
if is_request:
|
||||
settings.request_host = EXAMPLE_HOST
|
||||
language.serve(safe, s, settings)
|
||||
else:
|
||||
settings.websocket_key = EXAMPLE_WEBSOCKET_KEY
|
||||
language.serve(safe, s, settings)
|
||||
|
||||
args["output"] = strutils.bytes_to_escaped_str(s.getvalue())
|
||||
return render(template, False, **args)
|
||||
|
||||
@app.route('/response_preview')
|
||||
def response_preview():
|
||||
return _preview(False)
|
||||
|
||||
@app.route('/request_preview')
|
||||
def request_preview():
|
||||
return _preview(True)
|
||||
return app
|
||||
@@ -10,7 +10,7 @@ from netlib import tcp, certutils, websockets
|
||||
from netlib.exceptions import HttpException, HttpReadDisconnect, TcpTimeout, TcpDisconnect, \
|
||||
TlsException
|
||||
|
||||
from . import version, app, language, utils, log, protocols
|
||||
from . import version, language, utils, log, protocols
|
||||
|
||||
|
||||
DEFAULT_CERT_DOMAIN = "pathod.net"
|
||||
@@ -136,7 +136,6 @@ class PathodHandler(tcp.BaseHandler):
|
||||
path = req.path
|
||||
http_version = req.http_version
|
||||
headers = req.headers
|
||||
body = req.content
|
||||
|
||||
clientcert = None
|
||||
if self.clientcert:
|
||||
@@ -203,24 +202,27 @@ class PathodHandler(tcp.BaseHandler):
|
||||
self.server.craftanchor
|
||||
)])
|
||||
|
||||
if anchor_gen:
|
||||
spec = anchor_gen.next()
|
||||
if not anchor_gen:
|
||||
anchor_gen = iter([self.make_http_error_response(
|
||||
"Not found",
|
||||
"No valid craft request found"
|
||||
)])
|
||||
|
||||
if self.use_http2 and isinstance(spec, language.http2.Response):
|
||||
spec.stream_id = req.stream_id
|
||||
spec = anchor_gen.next()
|
||||
|
||||
lg("crafting spec: %s" % spec)
|
||||
nexthandler, retlog["response"] = self.http_serve_crafted(
|
||||
spec,
|
||||
lg
|
||||
)
|
||||
if nexthandler and websocket_key:
|
||||
self.protocol = protocols.websockets.WebsocketsProtocol(self)
|
||||
return self.protocol.handle_websocket, retlog
|
||||
else:
|
||||
return nexthandler, retlog
|
||||
if self.use_http2 and isinstance(spec, language.http2.Response):
|
||||
spec.stream_id = req.stream_id
|
||||
|
||||
lg("crafting spec: %s" % spec)
|
||||
nexthandler, retlog["response"] = self.http_serve_crafted(
|
||||
spec,
|
||||
lg
|
||||
)
|
||||
if nexthandler and websocket_key:
|
||||
self.protocol = protocols.websockets.WebsocketsProtocol(self)
|
||||
return self.protocol.handle_websocket, retlog
|
||||
else:
|
||||
return self.protocol.handle_http_app(method, path, headers, body, lg)
|
||||
return nexthandler, retlog
|
||||
|
||||
def make_http_error_response(self, reason, body=None):
|
||||
resp = self.protocol.make_error_response(reason, body)
|
||||
@@ -302,9 +304,7 @@ class Pathod(tcp.TCPServer):
|
||||
staticdir=None,
|
||||
anchors=(),
|
||||
sizelimit=None,
|
||||
noweb=False,
|
||||
nocraft=False,
|
||||
noapi=False,
|
||||
nohang=False,
|
||||
timeout=None,
|
||||
logreq=False,
|
||||
@@ -326,7 +326,6 @@ class Pathod(tcp.TCPServer):
|
||||
None.
|
||||
sizelimit: Limit size of served data.
|
||||
nocraft: Disable response crafting.
|
||||
noapi: Disable the API.
|
||||
nohang: Disable pauses.
|
||||
"""
|
||||
tcp.TCPServer.__init__(self, addr)
|
||||
@@ -335,16 +334,14 @@ class Pathod(tcp.TCPServer):
|
||||
self.staticdir = staticdir
|
||||
self.craftanchor = craftanchor
|
||||
self.sizelimit = sizelimit
|
||||
self.noweb, self.nocraft = noweb, nocraft
|
||||
self.noapi, self.nohang = noapi, nohang
|
||||
self.nocraft = nocraft
|
||||
self.nohang = nohang
|
||||
self.timeout, self.logreq = timeout, logreq
|
||||
self.logresp, self.hexdump = logresp, hexdump
|
||||
self.http2_framedump = http2_framedump
|
||||
self.explain = explain
|
||||
self.logfp = logfp
|
||||
|
||||
self.app = app.make_app(noapi, webdebug)
|
||||
self.app.config["pathod"] = self
|
||||
self.log = []
|
||||
self.logid = 0
|
||||
self.anchors = anchors
|
||||
@@ -404,14 +401,13 @@ class Pathod(tcp.TCPServer):
|
||||
return
|
||||
|
||||
def add_log(self, d):
|
||||
if not self.noapi:
|
||||
with self.loglock:
|
||||
d["id"] = self.logid
|
||||
self.log.insert(0, d)
|
||||
if len(self.log) > self.LOGBUF:
|
||||
self.log.pop()
|
||||
self.logid += 1
|
||||
return d["id"]
|
||||
with self.loglock:
|
||||
d["id"] = self.logid
|
||||
self.log.insert(0, d)
|
||||
if len(self.log) > self.LOGBUF:
|
||||
self.log.pop()
|
||||
self.logid += 1
|
||||
return d["id"]
|
||||
|
||||
def clear_log(self):
|
||||
with self.loglock:
|
||||
@@ -469,9 +465,7 @@ def main(args): # pragma: no cover
|
||||
staticdir=args.staticdir,
|
||||
anchors=args.anchors,
|
||||
sizelimit=args.sizelimit,
|
||||
noweb=args.noweb,
|
||||
nocraft=args.nocraft,
|
||||
noapi=args.noapi,
|
||||
nohang=args.nohang,
|
||||
timeout=args.timeout,
|
||||
logreq=args.logreq,
|
||||
|
||||
@@ -74,18 +74,10 @@ def args_pathod(argv, stdout_=sys.stdout, stderr_=sys.stderr):
|
||||
default=None,
|
||||
type=str,
|
||||
help='Size limit of served responses. Understands size suffixes, i.e. 100k.')
|
||||
parser.add_argument(
|
||||
"--noapi", dest='noapi', default=False, action="store_true",
|
||||
help='Disable API.'
|
||||
)
|
||||
parser.add_argument(
|
||||
"--nohang", dest='nohang', default=False, action="store_true",
|
||||
help='Disable pauses during crafted response generation.'
|
||||
)
|
||||
parser.add_argument(
|
||||
"--noweb", dest='noweb', default=False, action="store_true",
|
||||
help='Disable both web interface and API.'
|
||||
)
|
||||
parser.add_argument(
|
||||
"--nocraft",
|
||||
dest='nocraft',
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
from netlib import wsgi
|
||||
from netlib.exceptions import TlsException
|
||||
from netlib.http import http1
|
||||
from .. import version, language
|
||||
@@ -11,30 +10,6 @@ class HTTPProtocol(object):
|
||||
def make_error_response(self, reason, body):
|
||||
return language.http.make_error_response(reason, body)
|
||||
|
||||
def handle_http_app(self, method, path, headers, body, lg):
|
||||
"""
|
||||
Handle a request to the built-in app.
|
||||
"""
|
||||
if self.pathod_handler.server.noweb:
|
||||
crafted = self.pathod_handler.make_http_error_response("Access Denied")
|
||||
language.serve(crafted, self.pathod_handler.wfile, self.pathod_handler.settings)
|
||||
return None, dict(
|
||||
type="error",
|
||||
msg="Access denied: web interface disabled"
|
||||
)
|
||||
lg("app: %s %s" % (method, path))
|
||||
req = wsgi.Request("http", method, path, b"HTTP/1.1", headers, body)
|
||||
flow = wsgi.Flow(self.pathod_handler.address, req)
|
||||
sn = self.pathod_handler.connection.getsockname()
|
||||
a = wsgi.WSGIAdaptor(
|
||||
self.pathod_handler.server.app,
|
||||
sn[0],
|
||||
self.pathod_handler.server.address.port,
|
||||
version.NAMEVERSION
|
||||
)
|
||||
a.serve(flow, self.pathod_handler.wfile)
|
||||
return self.pathod_handler.handle_http_request, None
|
||||
|
||||
def handle_http_connect(self, connect, lg):
|
||||
"""
|
||||
Handle a CONNECT request.
|
||||
|
||||
9
pathod/static/bootstrap.min.css
vendored
9
pathod/static/bootstrap.min.css
vendored
File diff suppressed because one or more lines are too long
6
pathod/static/bootstrap.min.js
vendored
6
pathod/static/bootstrap.min.js
vendored
File diff suppressed because one or more lines are too long
4
pathod/static/jquery-1.7.2.min.js
vendored
4
pathod/static/jquery-1.7.2.min.js
vendored
File diff suppressed because one or more lines are too long
9
pathod/static/jquery.localscroll-min.js
vendored
9
pathod/static/jquery.localscroll-min.js
vendored
@@ -1,9 +0,0 @@
|
||||
/**
|
||||
* jQuery.LocalScroll - Animated scrolling navigation, using anchors.
|
||||
* Copyright (c) 2007-2009 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
|
||||
* Dual licensed under MIT and GPL.
|
||||
* Date: 3/11/2009
|
||||
* @author Ariel Flesler
|
||||
* @version 1.2.7
|
||||
**/
|
||||
;(function($){var l=location.href.replace(/#.*/,'');var g=$.localScroll=function(a){$('body').localScroll(a)};g.defaults={duration:1e3,axis:'y',event:'click',stop:true,target:window,reset:true};g.hash=function(a){if(location.hash){a=$.extend({},g.defaults,a);a.hash=false;if(a.reset){var e=a.duration;delete a.duration;$(a.target).scrollTo(0,a);a.duration=e}i(0,location,a)}};$.fn.localScroll=function(b){b=$.extend({},g.defaults,b);return b.lazy?this.bind(b.event,function(a){var e=$([a.target,a.target.parentNode]).filter(d)[0];if(e)i(a,e,b)}):this.find('a,area').filter(d).bind(b.event,function(a){i(a,this,b)}).end().end();function d(){return!!this.href&&!!this.hash&&this.href.replace(this.hash,'')==l&&(!b.filter||$(this).is(b.filter))}};function i(a,e,b){var d=e.hash.slice(1),f=document.getElementById(d)||document.getElementsByName(d)[0];if(!f)return;if(a)a.preventDefault();var h=$(b.target);if(b.lock&&h.is(':animated')||b.onBefore&&b.onBefore.call(b,a,f,h)===false)return;if(b.stop)h.stop(true);if(b.hash){var j=f.id==d?'id':'name',k=$('<a> </a>').attr(j,d).css({position:'absolute',top:$(window).scrollTop(),left:$(window).scrollLeft()});f[j]='';$('body').prepend(k);location=e.hash;k.remove();f[j]=d}h.scrollTo(f,b).trigger('notify.serialScroll',[f])}})(jQuery);
|
||||
11
pathod/static/jquery.scrollTo-min.js
vendored
11
pathod/static/jquery.scrollTo-min.js
vendored
@@ -1,11 +0,0 @@
|
||||
/**
|
||||
* jQuery.ScrollTo - Easy element scrolling using jQuery.
|
||||
* Copyright (c) 2007-2009 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
|
||||
* Dual licensed under MIT and GPL.
|
||||
* Date: 3/9/2009
|
||||
* @author Ariel Flesler
|
||||
* @version 1.4.1
|
||||
*
|
||||
* http://flesler.blogspot.com/2007/10/jqueryscrollto.html
|
||||
*/
|
||||
;(function($){var m=$.scrollTo=function(b,h,f){$(window).scrollTo(b,h,f)};m.defaults={axis:'xy',duration:parseFloat($.fn.jquery)>=1.3?0:1};m.window=function(b){return $(window).scrollable()};$.fn.scrollable=function(){return this.map(function(){var b=this,h=!b.nodeName||$.inArray(b.nodeName.toLowerCase(),['iframe','#document','html','body'])!=-1;if(!h)return b;var f=(b.contentWindow||b).document||b.ownerDocument||b;return $.browser.safari||f.compatMode=='BackCompat'?f.body:f.documentElement})};$.fn.scrollTo=function(l,j,a){if(typeof j=='object'){a=j;j=0}if(typeof a=='function')a={onAfter:a};if(l=='max')l=9e9;a=$.extend({},m.defaults,a);j=j||a.speed||a.duration;a.queue=a.queue&&a.axis.length>1;if(a.queue)j/=2;a.offset=n(a.offset);a.over=n(a.over);return this.scrollable().each(function(){var k=this,o=$(k),d=l,p,g={},q=o.is('html,body');switch(typeof d){case'number':case'string':if(/^([+-]=)?\d+(\.\d+)?(px)?$/.test(d)){d=n(d);break}d=$(d,this);case'object':if(d.is||d.style)p=(d=$(d)).offset()}$.each(a.axis.split(''),function(b,h){var f=h=='x'?'Left':'Top',i=f.toLowerCase(),c='scroll'+f,r=k[c],s=h=='x'?'Width':'Height';if(p){g[c]=p[i]+(q?0:r-o.offset()[i]);if(a.margin){g[c]-=parseInt(d.css('margin'+f))||0;g[c]-=parseInt(d.css('border'+f+'Width'))||0}g[c]+=a.offset[i]||0;if(a.over[i])g[c]+=d[s.toLowerCase()]()*a.over[i]}else g[c]=d[i];if(/^\d+$/.test(g[c]))g[c]=g[c]<=0?0:Math.min(g[c],u(s));if(!b&&a.queue){if(r!=g[c])t(a.onAfterFirst);delete g[c]}});t(a.onAfter);function t(b){o.animate(g,j,a.easing,b&&function(){b.call(this,l,a)})};function u(b){var h='scroll'+b;if(!q)return k[h];var f='client'+b,i=k.ownerDocument.documentElement,c=k.ownerDocument.body;return Math.max(i[h],c[h])-Math.min(i[f],c[f])}}).end()};function n(b){return typeof b=='object'?b:{top:b,left:b}}})(jQuery);
|
||||
@@ -1,56 +0,0 @@
|
||||
|
||||
|
||||
.fronttable {
|
||||
}
|
||||
|
||||
.bigtitle {
|
||||
font-weight: bold;
|
||||
font-size: 50px;
|
||||
line-height: 55px;
|
||||
text-align: center;
|
||||
display: table;
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.bigtitle>div {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
section {
|
||||
margin-top: 50px;
|
||||
}
|
||||
|
||||
.example {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.terminal {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
background: #000;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.innerlink {
|
||||
text-decoration: none;
|
||||
border-bottom:1px dotted;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.masthead {
|
||||
padding: 50px 0 60px;
|
||||
text-align: center;
|
||||
|
||||
}
|
||||
|
||||
.header {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
margin-top: 10px;
|
||||
|
||||
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 376 B |
@@ -1,120 +0,0 @@
|
||||
.highlight { background: #f8f8f8; }
|
||||
.highlight .c { color: #408080; font-style: italic } /* Comment */
|
||||
.highlight .err { border: 1px solid #FF0000 } /* Error */
|
||||
.highlight .k { color: #008000; font-weight: bold } /* Keyword */
|
||||
.highlight .o { color: #666666 } /* Operator */
|
||||
.highlight .cm { color: #408080; font-style: italic } /* Comment.Multiline */
|
||||
.highlight .cp { color: #BC7A00 } /* Comment.Preproc */
|
||||
.highlight .c1 { color: #408080; font-style: italic } /* Comment.Single */
|
||||
.highlight .cs { color: #408080; font-style: italic } /* Comment.Special */
|
||||
.highlight .gd { color: #A00000 } /* Generic.Deleted */
|
||||
.highlight .ge { font-style: italic } /* Generic.Emph */
|
||||
.highlight .gr { color: #FF0000 } /* Generic.Error */
|
||||
.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
|
||||
.highlight .gi { color: #00A000 } /* Generic.Inserted */
|
||||
.highlight .go { color: #808080 } /* Generic.Output */
|
||||
.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
|
||||
.highlight .gs { font-weight: bold } /* Generic.Strong */
|
||||
.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
|
||||
.highlight .gt { color: #0040D0 } /* Generic.Traceback */
|
||||
.highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
|
||||
.highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
|
||||
.highlight .kp { color: #008000 } /* Keyword.Pseudo */
|
||||
.highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
|
||||
.highlight .kt { color: #B00040 } /* Keyword.Type */
|
||||
.highlight .m { color: #666666 } /* Literal.Number */
|
||||
.highlight .s { color: #BA2121 } /* Literal.String */
|
||||
.highlight .na { color: #7D9029 } /* Name.Attribute */
|
||||
.highlight .nb { color: #008000 } /* Name.Builtin */
|
||||
.highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */
|
||||
.highlight .no { color: #880000 } /* Name.Constant */
|
||||
.highlight .nd { color: #AA22FF } /* Name.Decorator */
|
||||
.highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */
|
||||
.highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
|
||||
.highlight .nf { color: #0000FF } /* Name.Function */
|
||||
.highlight .nl { color: #A0A000 } /* Name.Label */
|
||||
.highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
|
||||
.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */
|
||||
.highlight .nv { color: #19177C } /* Name.Variable */
|
||||
.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
|
||||
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
|
||||
.highlight .mf { color: #666666 } /* Literal.Number.Float */
|
||||
.highlight .mh { color: #666666 } /* Literal.Number.Hex */
|
||||
.highlight .mi { color: #666666 } /* Literal.Number.Integer */
|
||||
.highlight .mo { color: #666666 } /* Literal.Number.Oct */
|
||||
.highlight .sb { color: #BA2121 } /* Literal.String.Backtick */
|
||||
.highlight .sc { color: #BA2121 } /* Literal.String.Char */
|
||||
.highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
|
||||
.highlight .s2 { color: #BA2121 } /* Literal.String.Double */
|
||||
.highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
|
||||
.highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */
|
||||
.highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
|
||||
.highlight .sx { color: #008000 } /* Literal.String.Other */
|
||||
.highlight .sr { color: #BB6688 } /* Literal.String.Regex */
|
||||
.highlight .s1 { color: #BA2121 } /* Literal.String.Single */
|
||||
.highlight .ss { color: #19177C } /* Literal.String.Symbol */
|
||||
.highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */
|
||||
.highlight .vc { color: #19177C } /* Name.Variable.Class */
|
||||
.highlight .vg { color: #19177C } /* Name.Variable.Global */
|
||||
.highlight .vi { color: #19177C } /* Name.Variable.Instance */
|
||||
.highlight .il { color: #666666 } /* Literal.Number.Integer.Long */
|
||||
.grokdoc { background: #f8f8f8; }
|
||||
.grokdoc .c { color: #408080; font-style: italic } /* Comment */
|
||||
.grokdoc .err { border: 1px solid #FF0000 } /* Error */
|
||||
.grokdoc .k { color: #008000; font-weight: bold } /* Keyword */
|
||||
.grokdoc .o { color: #666666 } /* Operator */
|
||||
.grokdoc .cm { color: #408080; font-style: italic } /* Comment.Multiline */
|
||||
.grokdoc .cp { color: #BC7A00 } /* Comment.Preproc */
|
||||
.grokdoc .c1 { color: #408080; font-style: italic } /* Comment.Single */
|
||||
.grokdoc .cs { color: #408080; font-style: italic } /* Comment.Special */
|
||||
.grokdoc .gd { color: #A00000 } /* Generic.Deleted */
|
||||
.grokdoc .ge { font-style: italic } /* Generic.Emph */
|
||||
.grokdoc .gr { color: #FF0000 } /* Generic.Error */
|
||||
.grokdoc .gh { color: #000080; font-weight: bold } /* Generic.Heading */
|
||||
.grokdoc .gi { color: #00A000 } /* Generic.Inserted */
|
||||
.grokdoc .go { color: #808080 } /* Generic.Output */
|
||||
.grokdoc .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
|
||||
.grokdoc .gs { font-weight: bold } /* Generic.Strong */
|
||||
.grokdoc .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
|
||||
.grokdoc .gt { color: #0040D0 } /* Generic.Traceback */
|
||||
.grokdoc .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
|
||||
.grokdoc .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
|
||||
.grokdoc .kp { color: #008000 } /* Keyword.Pseudo */
|
||||
.grokdoc .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
|
||||
.grokdoc .kt { color: #B00040 } /* Keyword.Type */
|
||||
.grokdoc .m { color: #666666 } /* Literal.Number */
|
||||
.grokdoc .s { color: #BA2121 } /* Literal.String */
|
||||
.grokdoc .na { color: #7D9029 } /* Name.Attribute */
|
||||
.grokdoc .nb { color: #008000 } /* Name.Builtin */
|
||||
.grokdoc .nc { color: #0000FF; font-weight: bold } /* Name.Class */
|
||||
.grokdoc .no { color: #880000 } /* Name.Constant */
|
||||
.grokdoc .nd { color: #AA22FF } /* Name.Decorator */
|
||||
.grokdoc .ni { color: #999999; font-weight: bold } /* Name.Entity */
|
||||
.grokdoc .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
|
||||
.grokdoc .nf { color: #0000FF } /* Name.Function */
|
||||
.grokdoc .nl { color: #A0A000 } /* Name.Label */
|
||||
.grokdoc .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
|
||||
.grokdoc .nt { color: #008000; font-weight: bold } /* Name.Tag */
|
||||
.grokdoc .nv { color: #19177C } /* Name.Variable */
|
||||
.grokdoc .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
|
||||
.grokdoc .w { color: #bbbbbb } /* Text.Whitespace */
|
||||
.grokdoc .mf { color: #666666 } /* Literal.Number.Float */
|
||||
.grokdoc .mh { color: #666666 } /* Literal.Number.Hex */
|
||||
.grokdoc .mi { color: #666666 } /* Literal.Number.Integer */
|
||||
.grokdoc .mo { color: #666666 } /* Literal.Number.Oct */
|
||||
.grokdoc .sb { color: #BA2121 } /* Literal.String.Backtick */
|
||||
.grokdoc .sc { color: #BA2121 } /* Literal.String.Char */
|
||||
.grokdoc .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
|
||||
.grokdoc .s2 { color: #BA2121 } /* Literal.String.Double */
|
||||
.grokdoc .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
|
||||
.grokdoc .sh { color: #BA2121 } /* Literal.String.Heredoc */
|
||||
.grokdoc .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
|
||||
.grokdoc .sx { color: #008000 } /* Literal.String.Other */
|
||||
.grokdoc .sr { color: #BB6688 } /* Literal.String.Regex */
|
||||
.grokdoc .s1 { color: #BA2121 } /* Literal.String.Single */
|
||||
.grokdoc .ss { color: #19177C } /* Literal.String.Symbol */
|
||||
.grokdoc .bp { color: #008000 } /* Name.Builtin.Pseudo */
|
||||
.grokdoc .vc { color: #19177C } /* Name.Variable.Class */
|
||||
.grokdoc .vg { color: #19177C } /* Name.Variable.Global */
|
||||
.grokdoc .vi { color: #19177C } /* Name.Variable.Instance */
|
||||
.grokdoc .il { color: #666666 } /* Literal.Number.Integer.Long */
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 106 KiB |
@@ -1,82 +0,0 @@
|
||||
import tutils
|
||||
|
||||
|
||||
class TestApp(tutils.DaemonTests):
|
||||
SSL = False
|
||||
|
||||
def test_index(self):
|
||||
r = self.getpath("/")
|
||||
assert r.status_code == 200
|
||||
assert r.content
|
||||
|
||||
def test_about(self):
|
||||
r = self.getpath("/about")
|
||||
assert r.status_code == 200
|
||||
|
||||
def test_download(self):
|
||||
r = self.getpath("/download")
|
||||
assert r.status_code == 200
|
||||
|
||||
def test_docs(self):
|
||||
assert self.getpath("/docs/pathod").status_code == 200
|
||||
assert self.getpath("/docs/pathoc").status_code == 200
|
||||
assert self.getpath("/docs/language").status_code == 200
|
||||
assert self.getpath("/docs/pathod").status_code == 200
|
||||
assert self.getpath("/docs/test").status_code == 200
|
||||
|
||||
def test_log(self):
|
||||
assert self.getpath("/log").status_code == 200
|
||||
assert self.get("200:da").status_code == 200
|
||||
id = self.d.expect_log(1)[0]["id"]
|
||||
assert self.getpath("/log").status_code == 200
|
||||
assert self.getpath("/log/%s" % id).status_code == 200
|
||||
assert self.getpath("/log/9999999").status_code == 404
|
||||
|
||||
def test_response_preview(self):
|
||||
r = self.getpath("/response_preview", params=dict(spec="200"))
|
||||
assert r.status_code == 200
|
||||
assert 'Response' in r.content
|
||||
|
||||
r = self.getpath("/response_preview", params=dict(spec="foo"))
|
||||
assert r.status_code == 200
|
||||
assert 'Error' in r.content
|
||||
|
||||
r = self.getpath("/response_preview", params=dict(spec="200:b@100m"))
|
||||
assert r.status_code == 200
|
||||
assert "too large" in r.content
|
||||
|
||||
r = self.getpath("/response_preview", params=dict(spec="200:b@5k"))
|
||||
assert r.status_code == 200
|
||||
assert 'Response' in r.content
|
||||
|
||||
r = self.getpath(
|
||||
"/response_preview",
|
||||
params=dict(
|
||||
spec="200:b<nonexistent"))
|
||||
assert r.status_code == 200
|
||||
assert 'File access denied' in r.content
|
||||
|
||||
r = self.getpath("/response_preview", params=dict(spec="200:b<file"))
|
||||
assert r.status_code == 200
|
||||
assert 'testfile' in r.content
|
||||
|
||||
def test_request_preview(self):
|
||||
r = self.getpath("/request_preview", params=dict(spec="get:/"))
|
||||
assert r.status_code == 200
|
||||
assert 'Request' in r.content
|
||||
|
||||
r = self.getpath("/request_preview", params=dict(spec="foo"))
|
||||
assert r.status_code == 200
|
||||
assert 'Error' in r.content
|
||||
|
||||
r = self.getpath("/request_preview", params=dict(spec="get:/:b@100m"))
|
||||
assert r.status_code == 200
|
||||
assert "too large" in r.content
|
||||
|
||||
r = self.getpath("/request_preview", params=dict(spec="get:/:b@5k"))
|
||||
assert r.status_code == 200
|
||||
assert 'Request' in r.content
|
||||
|
||||
r = self.getpath("/request_preview", params=dict(spec=""))
|
||||
assert r.status_code == 200
|
||||
assert 'empty spec' in r.content
|
||||
@@ -1,7 +1,4 @@
|
||||
import json
|
||||
from six.moves import cStringIO as StringIO
|
||||
import re
|
||||
import pytest
|
||||
from mock import Mock
|
||||
|
||||
from netlib import http
|
||||
@@ -9,10 +6,9 @@ from netlib import tcp
|
||||
from netlib.exceptions import NetlibException
|
||||
from netlib.http import http1, http2
|
||||
|
||||
from pathod import pathoc, test, version, pathod, language
|
||||
from pathod import pathoc, language
|
||||
from netlib.tutils import raises
|
||||
import tutils
|
||||
from test.mitmproxy.tutils import skip_windows
|
||||
|
||||
|
||||
def test_response():
|
||||
@@ -20,37 +16,7 @@ def test_response():
|
||||
assert repr(r)
|
||||
|
||||
|
||||
class _TestDaemon:
|
||||
ssloptions = pathod.SSLOptions()
|
||||
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
cls.d = test.Daemon(
|
||||
ssl=cls.ssl,
|
||||
ssloptions=cls.ssloptions,
|
||||
staticdir=tutils.test_data.path("data"),
|
||||
anchors=[
|
||||
(re.compile("/anchor/.*"), "202")
|
||||
]
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def teardown_class(cls):
|
||||
cls.d.shutdown()
|
||||
|
||||
def setUp(self):
|
||||
self.d.clear_log()
|
||||
|
||||
def test_info(self):
|
||||
c = pathoc.Pathoc(
|
||||
("127.0.0.1", self.d.port),
|
||||
ssl=self.ssl,
|
||||
fp=None
|
||||
)
|
||||
c.connect()
|
||||
resp = c.request("get:/api/info")
|
||||
assert tuple(json.loads(resp.content)["version"]) == version.IVERSION
|
||||
|
||||
class PathocTestDaemon(tutils.DaemonTests):
|
||||
def tval(
|
||||
self,
|
||||
requests,
|
||||
@@ -77,23 +43,23 @@ class _TestDaemon:
|
||||
showsummary=showsummary,
|
||||
fp=s
|
||||
)
|
||||
c.connect(showssl=showssl, fp=s)
|
||||
if timeout:
|
||||
c.settimeout(timeout)
|
||||
for i in requests:
|
||||
r = language.parse_pathoc(i).next()
|
||||
if explain:
|
||||
r = r.freeze(language.Settings())
|
||||
try:
|
||||
c.request(r)
|
||||
except NetlibException:
|
||||
pass
|
||||
with c.connect(showssl=showssl, fp=s):
|
||||
if timeout:
|
||||
c.settimeout(timeout)
|
||||
for i in requests:
|
||||
r = language.parse_pathoc(i).next()
|
||||
if explain:
|
||||
r = r.freeze(language.Settings())
|
||||
try:
|
||||
c.request(r)
|
||||
except NetlibException:
|
||||
pass
|
||||
return s.getvalue()
|
||||
|
||||
|
||||
class TestDaemonSSL(_TestDaemon):
|
||||
class TestDaemonSSL(PathocTestDaemon):
|
||||
ssl = True
|
||||
ssloptions = pathod.SSLOptions(
|
||||
ssloptions = dict(
|
||||
request_client_cert=True,
|
||||
sans=["test1.com", "test2.com"],
|
||||
alpn_select=b'h2',
|
||||
@@ -106,11 +72,10 @@ class TestDaemonSSL(_TestDaemon):
|
||||
sni="foobar.com",
|
||||
fp=None
|
||||
)
|
||||
c.connect()
|
||||
c.request("get:/p/200")
|
||||
r = c.request("get:/api/log")
|
||||
d = json.loads(r.content)
|
||||
assert d["log"][0]["request"]["sni"] == "foobar.com"
|
||||
with c.connect():
|
||||
c.request("get:/p/200")
|
||||
log = self.d.log()
|
||||
assert log[0]["request"]["sni"] == "foobar.com"
|
||||
|
||||
def test_showssl(self):
|
||||
assert "certificate chain" in self.tval(["get:/p/200"], showssl=True)
|
||||
@@ -122,11 +87,11 @@ class TestDaemonSSL(_TestDaemon):
|
||||
clientcert=tutils.test_data.path("data/clientcert/client.pem"),
|
||||
fp=None
|
||||
)
|
||||
c.connect()
|
||||
c.request("get:/p/200")
|
||||
r = c.request("get:/api/log")
|
||||
d = json.loads(r.content)
|
||||
assert d["log"][0]["request"]["clientcert"]["keyinfo"]
|
||||
with c.connect():
|
||||
c.request("get:/p/200")
|
||||
|
||||
log = self.d.log()
|
||||
assert log[0]["request"]["clientcert"]["keyinfo"]
|
||||
|
||||
def test_http2_without_ssl(self):
|
||||
fp = StringIO()
|
||||
@@ -139,7 +104,7 @@ class TestDaemonSSL(_TestDaemon):
|
||||
tutils.raises(NotImplementedError, c.connect)
|
||||
|
||||
|
||||
class TestDaemon(_TestDaemon):
|
||||
class TestDaemon(PathocTestDaemon):
|
||||
ssl = False
|
||||
|
||||
def test_ssl_error(self):
|
||||
@@ -163,7 +128,7 @@ class TestDaemon(_TestDaemon):
|
||||
201])
|
||||
assert "202" in self.tval(["get:'/p/202:b@1'"], ignorecodes=[200, 201])
|
||||
|
||||
def test_timeout(self):
|
||||
def _test_timeout(self):
|
||||
assert "Timeout" in self.tval(["get:'/p/200:p0,100'"], timeout=0.01)
|
||||
assert "HTTP" in self.tval(
|
||||
["get:'/p/200:p5,100'"],
|
||||
@@ -178,8 +143,8 @@ class TestDaemon(_TestDaemon):
|
||||
)
|
||||
|
||||
def test_showresp(self):
|
||||
reqs = ["get:/api/info:p0,0", "get:/api/info:p0,0"]
|
||||
assert self.tval(reqs).count("200") == 2
|
||||
reqs = ["get:/p/200:da", "get:/p/200:da"]
|
||||
assert self.tval(reqs).count("200 OK") == 2
|
||||
assert self.tval(reqs, showresp=True).count("HTTP/1.1 200 OK") == 2
|
||||
assert self.tval(
|
||||
reqs, showresp=True, hexdump=True
|
||||
@@ -195,8 +160,8 @@ class TestDaemon(_TestDaemon):
|
||||
assert "b@100" not in self.tval(reqs, explain=True)
|
||||
|
||||
def test_showreq(self):
|
||||
reqs = ["get:/api/info:p0,0", "get:/api/info:p0,0"]
|
||||
assert self.tval(reqs, showreq=True).count("GET /api") == 2
|
||||
reqs = ["get:/p/200:da", "get:/p/200:da"]
|
||||
assert self.tval(reqs, showreq=True).count("GET /p/200") == 2
|
||||
assert self.tval(
|
||||
reqs, showreq=True, hexdump=True
|
||||
).count("0000000000") == 2
|
||||
@@ -206,23 +171,20 @@ class TestDaemon(_TestDaemon):
|
||||
|
||||
def test_websocket_shutdown(self):
|
||||
c = pathoc.Pathoc(("127.0.0.1", self.d.port), fp=None)
|
||||
c.connect()
|
||||
c.request("ws:/")
|
||||
c.stop()
|
||||
with c.connect():
|
||||
c.request("ws:/")
|
||||
|
||||
@skip_windows
|
||||
@pytest.mark.skip(reason="race condition")
|
||||
def test_wait_finish(self):
|
||||
c = pathoc.Pathoc(
|
||||
("127.0.0.1", self.d.port),
|
||||
fp=None,
|
||||
ws_read_limit=1
|
||||
)
|
||||
c.connect()
|
||||
c.request("ws:/")
|
||||
c.request("wf:f'wf:x100'")
|
||||
[i for i in c.wait(timeout=0, finish=False)]
|
||||
[i for i in c.wait(timeout=0)]
|
||||
with c.connect():
|
||||
c.request("ws:/")
|
||||
c.request("wf:f'wf:x100'")
|
||||
[i for i in c.wait(timeout=0, finish=False)]
|
||||
[i for i in c.wait(timeout=0)]
|
||||
|
||||
def test_connect_fail(self):
|
||||
to = ("foobar", 80)
|
||||
@@ -264,8 +226,9 @@ class TestDaemon(_TestDaemon):
|
||||
c.socks_connect(("example.com", 0xDEAD))
|
||||
|
||||
|
||||
class TestDaemonHTTP2(_TestDaemon):
|
||||
class TestDaemonHTTP2(PathocTestDaemon):
|
||||
ssl = True
|
||||
explain = False
|
||||
|
||||
if tcp.HAS_ALPN:
|
||||
|
||||
@@ -295,10 +258,9 @@ class TestDaemonHTTP2(_TestDaemon):
|
||||
tmp_convert_to_ssl = c.convert_to_ssl
|
||||
c.convert_to_ssl = Mock()
|
||||
c.convert_to_ssl.side_effect = tmp_convert_to_ssl
|
||||
c.connect()
|
||||
|
||||
_, kwargs = c.convert_to_ssl.call_args
|
||||
assert set(kwargs['alpn_protos']) == set([b'http/1.1', b'h2'])
|
||||
with c.connect():
|
||||
_, kwargs = c.convert_to_ssl.call_args
|
||||
assert set(kwargs['alpn_protos']) == set([b'http/1.1', b'h2'])
|
||||
|
||||
def test_request(self):
|
||||
c = pathoc.Pathoc(
|
||||
@@ -307,6 +269,6 @@ class TestDaemonHTTP2(_TestDaemon):
|
||||
ssl=True,
|
||||
use_http2=True,
|
||||
)
|
||||
c.connect()
|
||||
resp = c.request("get:/p/200")
|
||||
with c.connect():
|
||||
resp = c.request("get:/p/200")
|
||||
assert resp.status_code == 200
|
||||
|
||||
@@ -23,18 +23,10 @@ class TestPathod(object):
|
||||
assert len(p.get_log()) <= p.LOGBUF
|
||||
|
||||
|
||||
class TestNoWeb(tutils.DaemonTests):
|
||||
noweb = True
|
||||
|
||||
def test_noweb(self):
|
||||
assert self.get("200:da").status_code == 200
|
||||
assert self.getpath("/").status_code == 800
|
||||
|
||||
|
||||
class TestTimeout(tutils.DaemonTests):
|
||||
timeout = 0.01
|
||||
|
||||
def test_noweb(self):
|
||||
def test_timeout(self):
|
||||
# FIXME: Add float values to spec language, reduce test timeout to
|
||||
# increase test performance
|
||||
# This is a bodge - we have some platform difference that causes
|
||||
@@ -43,16 +35,6 @@ class TestTimeout(tutils.DaemonTests):
|
||||
assert self.d.last_log()["type"] == "timeout"
|
||||
|
||||
|
||||
class TestNoApi(tutils.DaemonTests):
|
||||
noapi = True
|
||||
|
||||
def test_noapi(self):
|
||||
assert self.getpath("/log").status_code == 404
|
||||
r = self.getpath("/")
|
||||
assert r.status_code == 200
|
||||
assert "Log" not in r.content
|
||||
|
||||
|
||||
class TestNotAfterConnect(tutils.DaemonTests):
|
||||
ssl = False
|
||||
ssloptions = dict(
|
||||
@@ -271,8 +253,6 @@ class TestDaemonSSL(CommonTests):
|
||||
|
||||
class TestHTTP2(tutils.DaemonTests):
|
||||
ssl = True
|
||||
noweb = True
|
||||
noapi = True
|
||||
nohang = True
|
||||
|
||||
if tcp.HAS_ALPN:
|
||||
|
||||
@@ -24,14 +24,13 @@ def treader(bytes):
|
||||
|
||||
|
||||
class DaemonTests(object):
|
||||
noweb = False
|
||||
noapi = False
|
||||
nohang = False
|
||||
ssl = False
|
||||
timeout = None
|
||||
hexdump = False
|
||||
ssloptions = None
|
||||
nocraft = False
|
||||
explain = True
|
||||
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
@@ -47,15 +46,13 @@ class DaemonTests(object):
|
||||
ssl=cls.ssl,
|
||||
ssloptions=so,
|
||||
sizelimit=1 * 1024 * 1024,
|
||||
noweb=cls.noweb,
|
||||
noapi=cls.noapi,
|
||||
nohang=cls.nohang,
|
||||
timeout=cls.timeout,
|
||||
hexdump=cls.hexdump,
|
||||
nocraft=cls.nocraft,
|
||||
logreq=True,
|
||||
logresp=True,
|
||||
explain=True
|
||||
explain=cls.explain
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@@ -65,8 +62,7 @@ class DaemonTests(object):
|
||||
|
||||
def teardown(self):
|
||||
self.d.wait_for_silence()
|
||||
if not (self.noweb or self.noapi):
|
||||
self.d.clear_log()
|
||||
self.d.clear_log()
|
||||
|
||||
def _getpath(self, path, params=None):
|
||||
scheme = "https" if self.ssl else "http"
|
||||
|
||||
Reference in New Issue
Block a user