Merge pull request #1211 from cortesi/pathod

WIP: Radical webectomy of pathod
This commit is contained in:
Aldo Cortesi
2016-06-06 08:40:20 +12:00
17 changed files with 76 additions and 654 deletions

View File

@@ -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

View File

@@ -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,

View File

@@ -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',

View File

@@ -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.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -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);

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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:

View File

@@ -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"