diff options
-rw-r--r-- | libpathod/app.py | 3 | ||||
-rw-r--r-- | libpathod/language.py | 25 | ||||
-rw-r--r-- | libpathod/pathod.py | 1 | ||||
-rw-r--r-- | libpathod/templates/docs_lang.html | 21 | ||||
-rwxr-xr-x | pathoc | 19 | ||||
-rw-r--r-- | test/test_language.py | 21 | ||||
-rw-r--r-- | test/test_pathod.py | 2 |
7 files changed, 84 insertions, 8 deletions
diff --git a/libpathod/app.py b/libpathod/app.py index 1fcfa078..78c0fc4a 100644 --- a/libpathod/app.py +++ b/libpathod/app.py @@ -1,6 +1,7 @@ import logging, pprint, cStringIO from flask import Flask, jsonify, render_template, request, abort, make_response import version, language, utils +from netlib import http_uastrings logging.basicConfig(level="DEBUG") app = Flask(__name__) @@ -61,7 +62,7 @@ def docs_pathod(): @app.route('/docs/language') def docs_language(): - return render("docs_lang.html", True, section="docs") + return render("docs_lang.html", True, section="docs", uastrings=http_uastrings.UASTRINGS) @app.route('/docs/pathoc') diff --git a/libpathod/language.py b/libpathod/language.py index 79657b37..30788569 100644 --- a/libpathod/language.py +++ b/libpathod/language.py @@ -2,7 +2,7 @@ import operator, string, random, mmap, os, time, copy import abc from email.utils import formatdate import contrib.pyparsing as pp -from netlib import http_status, tcp +from netlib import http_status, tcp, http_uastrings import utils @@ -449,6 +449,28 @@ class ShortcutLocation(_Header): return ShortcutLocation(self.value.freeze(settings)) +class ShortcutUserAgent(_Header): + def __init__(self, value): + self.specvalue = value + if isinstance(value, basestring): + value = ValueLiteral(http_uastrings.get_by_shortcut(value)[2]) + _Header.__init__(self, ValueLiteral("User-Agent"), value) + + @classmethod + def expr(klass): + e = pp.Literal("u").suppress() + u = reduce(operator.or_, [pp.Literal(i[1]) for i in http_uastrings.UASTRINGS]) + e += u | Value + return e.setParseAction(lambda x: klass(*x)) + + def spec(self): + return "u%s"%self.specvalue + + def freeze(self, settings): + return ShortcutUserAgent(self.value.freeze(settings)) + + + class Body(_Component): def __init__(self, value): self.value = value @@ -824,6 +846,7 @@ class Response(_Message): InjectAt, ShortcutContentType, ShortcutLocation, + ShortcutUserAgent, Raw, Reason ) diff --git a/libpathod/pathod.py b/libpathod/pathod.py index f327ade4..d52af15b 100644 --- a/libpathod/pathod.py +++ b/libpathod/pathod.py @@ -30,7 +30,6 @@ class PathodHandler(tcp.BaseHandler): if self.server.explain: crafted = crafted.freeze(self.server.request_settings, None) - print crafted response_log = language.serve(crafted, self.wfile, self.server.request_settings, None) log = dict( type = "crafted", diff --git a/libpathod/templates/docs_lang.html b/libpathod/templates/docs_lang.html index f01d61fb..26672bb2 100644 --- a/libpathod/templates/docs_lang.html +++ b/libpathod/templates/docs_lang.html @@ -155,6 +155,27 @@ calculate a Content-Length header if a body is set. </td> </tr> + + <tr> + <td> u<a href="#valuespec">VALUE</a> <br> uSHORTCUT </td> + + <td> + + Set a User-Agent header on this request. You can + specify either a complete <a + href="#valuespec">VALUE</a>, or a User-Agent shortcut: + + <table class="table table-condensed"> + {% for i in uastrings %} + <tr> + <td><b>{{ i[1] }}</b></td> + <td>{{ i[0] }}</td> + </tr> + {% endfor %} + </table> + + </td> + </tr> </tbody> </table> </div> @@ -1,10 +1,22 @@ #!/usr/bin/env python import argparse, sys from libpathod import pathoc, version, language -from netlib import tcp +from netlib import tcp, http_uastrings if __name__ == "__main__": - parser = argparse.ArgumentParser(description='A perverse HTTP client.') + preparser = argparse.ArgumentParser(add_help=False) + preparser.add_argument( + "--show-uas", dest="showua", action="store_true", default=False, + help="Print user agent shortcuts and exit." + ) + pa = preparser.parse_known_args()[0] + if pa.showua: + print "User agent strings:" + for i in http_uastrings.UASTRINGS: + print " ", i[1], i[0] + sys.exit(0) + + parser = argparse.ArgumentParser(description='A perverse HTTP client.', parents=[preparser]) parser.add_argument( "-i", dest="sni", type=str, default=False, help="SSL Server Name Indication" @@ -33,8 +45,9 @@ if __name__ == "__main__": 'request', type=str, nargs="+", help='Request specification' ) + group = parser.add_argument_group( - 'Controlling Output', + 'Controlling Output', """ Some of these options expand generated values for logging - if you're generating large data, use them with caution. diff --git a/test/test_language.py b/test/test_language.py index b02a89dd..d7f7b4cc 100644 --- a/test/test_language.py +++ b/test/test_language.py @@ -297,11 +297,30 @@ class TestHeaders: v3 = v2.freeze({}) assert v2.value.val == v3.value.val - def test_shortcut_content_type(self): + def test_shortcuts(self): assert language.parse_response({}, "400:c'foo'").headers[0].key.val == "Content-Type" assert language.parse_response({}, "400:l'foo'").headers[0].key.val == "Location" + assert 'Android' in language.parse_response({}, "400:ua").headers[0].value.val + assert language.parse_response({}, "400:ua").headers[0].key.val == "User-Agent" +class TestShortcutUserAgent: + def test_location_shortcut(self): + e = language.ShortcutUserAgent.expr() + v = e.parseString("ua")[0] + assert "Android" in str(v.value) + assert v.spec() == "ua" + assert v.key.val == "User-Agent" + + v = e.parseString("u'foo'")[0] + assert "foo" in str(v.value) + assert "foo" in v.spec() + + v = e.parseString("u@100'")[0] + assert len(str(v.freeze({}).value)) > 100 + v2 = v.freeze({}) + v3 = v2.freeze({}) + assert v2.value.val == v3.value.val class Test_Action: diff --git a/test/test_pathod.py b/test/test_pathod.py index 30498c3a..46b1fb1d 100644 --- a/test/test_pathod.py +++ b/test/test_pathod.py @@ -35,7 +35,7 @@ class TestNoWeb(tutils.DaemonTests): class TestTimeout(tutils.DaemonTests): - timeout = 0.01 + timeout = 0.001 def test_noweb(self): # FIXME: Add float values to spec language, reduce test timeout to # increase test performance |