diff --git a/libpathod/language/http2.py b/libpathod/language/http2.py index c28b904ea..dec2d5fef 100644 --- a/libpathod/language/http2.py +++ b/libpathod/language/http2.py @@ -35,8 +35,15 @@ class Path(base.Value): class Header(base.KeyValue): + unique_name = None preamble = "h" + def values(self, settings): + return ( + self.key.get_generator(settings), + self.value.get_generator(settings), + ) + class Body(base.Value): preamble = "b" @@ -45,9 +52,11 @@ class Body(base.Value): class Times(base.Integer): preamble = "x" + class Code(base.Integer): pass + class Request(message.Message): comps = ( Header, @@ -57,7 +66,7 @@ class Request(message.Message): logattrs = ["method", "path"] def __init__(self, tokens): - super(Response, self).__init__(tokens) + super(Request, self).__init__(tokens) self.rendered_values = None @property @@ -106,9 +115,7 @@ class Request(message.Message): if self.rendered_values: return self.rendered_values else: - headers = self.headers - if headers: - headers = headers.values(settings) + headers = [header.values(settings) for header in self.headers] body = self.body if body: @@ -173,9 +180,7 @@ class Response(message.Message): if self.rendered_values: return self.rendered_values else: - headers = self.headers - if headers: - headers = headers.values(settings) + headers = [header.values(settings) for header in self.headers] body = self.body if body: diff --git a/libpathod/pathoc.py b/libpathod/pathoc.py index 9c0213600..c42cc82af 100644 --- a/libpathod/pathoc.py +++ b/libpathod/pathoc.py @@ -30,13 +30,8 @@ class SSLInfo: self.certchain, self.cipher, self.alp = certchain, cipher, alp def __str__(self): - if self.alp: - alp = self.alp - else: - alp = '' - parts = [ - "Application Layer Protocol: %s" % alp, + "Application Layer Protocol: %s" % self.alp, "Cipher: %s, %s bit, %s" % self.cipher, "SSL certificate chain:" ] diff --git a/libpathod/pathod.py b/libpathod/pathod.py index b6f04b923..212abbdcc 100644 --- a/libpathod/pathod.py +++ b/libpathod/pathod.py @@ -234,6 +234,7 @@ class PathodHandler(tcp.BaseHandler): method = headers[':method'] path = headers[':path'] headers = odict.ODict(headers) + httpversion = "" else: req = self.read_http_request(lg) if 'next_handle' in req: @@ -246,6 +247,7 @@ class PathodHandler(tcp.BaseHandler): path = req['path'] headers = req['headers'] body = req['body'] + httpversion = req['httpversion'] clientcert = None if self.clientcert: @@ -265,7 +267,7 @@ class PathodHandler(tcp.BaseHandler): path=path, method=method, headers=headers.lst, - # httpversion=httpversion, + httpversion=httpversion, sni=self.sni, remote_address=self.address(), clientcert=clientcert, @@ -375,7 +377,8 @@ class PathodHandler(tcp.BaseHandler): method=method, path=path, headers=headers, - body=body) + body=body, + httpversion=httpversion) def make_http_error_response(self, reason, body=None): """ diff --git a/test/test_language_http2.py b/test/test_language_http2.py index 3c751fd1d..0be42253c 100644 --- a/test/test_language_http2.py +++ b/test/test_language_http2.py @@ -10,8 +10,29 @@ import tutils def parse_request(s): return language.parse_pathoc(s, True).next() +def parse_response(s): + return language.parse_pathod(s, True).next() + +def default_settings(): + return language.Settings( + request_host = "foo.com", + protocol = netlib.http2.HTTP2Protocol(tcp.TCPClient(('localhost', 1234))) + ) + + +def test_make_error_response(): + d = cStringIO.StringIO() + s = http2.make_error_response("foo", "bar") + language.serve(s, d, default_settings()) + class TestRequest: + def test_cached_values(self): + req = parse_request("get:/") + req_id = id(req) + assert req_id == id(req.resolve(default_settings())) + assert req.values(default_settings()) == req.values(default_settings()) + def test_nonascii(self): tutils.raises("ascii", parse_request, "get:\xf0") @@ -57,16 +78,31 @@ class TestRequest: assert r[0].method.string() == "GET" assert r[1].method.string() == "GET" - def test_render(self): + def test_render_simple(self): s = cStringIO.StringIO() r = parse_request("GET:'/foo'") assert language.serve( r, s, - language.Settings( - request_host = "foo.com", - protocol = netlib.http2.HTTP2Protocol(tcp.TCPClient(('localhost', 1234))) - ) + default_settings(), + ) + + def test_render_with_headers(self): + s = cStringIO.StringIO() + r = parse_request('GET:/foo:h"foo"="bar"') + assert language.serve( + r, + s, + default_settings(), + ) + + def test_render_with_body(self): + s = cStringIO.StringIO() + r = parse_request("GET:'/foo':bfoobar") + assert language.serve( + r, + s, + default_settings(), ) def test_spec(self): @@ -74,3 +110,68 @@ class TestRequest: s = parse_request(s).spec() assert parse_request(s).spec() == s rt("get:/foo") + + +class TestResponse: + def test_cached_values(self): + res = parse_response("200") + res_id = id(res) + assert res_id == id(res.resolve(default_settings())) + assert res.values(default_settings()) == res.values(default_settings()) + + def test_nonascii(self): + tutils.raises("ascii", parse_response, "200:\xf0") + + def test_err(self): + tutils.raises(language.ParseException, parse_response, 'GET:/') + + def test_simple(self): + r = parse_response('200') + assert r.code.string() == "200" + assert len(r.headers) == 0 + + r = parse_response('200:h"foo"="bar"') + assert r.code.string() == "200" + assert len(r.headers) == 1 + assert r.headers[0].values(default_settings()) == ("foo", "bar") + assert r.body == None + + r = parse_response('200:h"foo"="bar":bfoobar:h"bla"="fasel"') + assert r.code.string() == "200" + assert len(r.headers) == 2 + assert r.headers[0].values(default_settings()) == ("foo", "bar") + assert r.headers[1].values(default_settings()) == ("bla", "fasel") + assert r.body.string() == "foobar" + + def test_render_simple(self): + s = cStringIO.StringIO() + r = parse_response('200') + assert language.serve( + r, + s, + default_settings(), + ) + + def test_render_with_headers(self): + s = cStringIO.StringIO() + r = parse_response('200:h"foo"="bar"') + assert language.serve( + r, + s, + default_settings(), + ) + + def test_render_with_body(self): + s = cStringIO.StringIO() + r = parse_response('200:bfoobar') + assert language.serve( + r, + s, + default_settings(), + ) + + def test_spec(self): + def rt(s): + s = parse_response(s).spec() + assert parse_response(s).spec() == s + rt("200:bfoobar") diff --git a/test/test_pathod.py b/test/test_pathod.py index f85ef38d0..1a3a5004d 100644 --- a/test/test_pathod.py +++ b/test/test_pathod.py @@ -1,7 +1,7 @@ import sys import cStringIO from libpathod import pathod, version -from netlib import tcp, http +from netlib import tcp, http, http2 import tutils @@ -269,3 +269,15 @@ class TestDaemonSSL(CommonTests): r, _ = self.pathoc([r"get:/p/202"]) assert r[0].status_code == 202 assert self.d.last_log()["cipher"][1] > 0 + +class TestHTTP2(tutils.DaemonTests): + force_http2 = True + ssl = True + noweb = True + noapi = True + nohang = True + + def test_http2(self): + r, _ = self.pathoc(["GET:/"], ssl=True, use_http2=True) + print(r) + assert r[0].status_code == "800" diff --git a/test/tutils.py b/test/tutils.py index 60c0765a5..2184ade56 100644 --- a/test/tutils.py +++ b/test/tutils.py @@ -73,7 +73,8 @@ class DaemonTests(object): timeout=None, connect_to=None, ssl=None, - ws_read_limit=None + ws_read_limit=None, + use_http2=False, ): """ Returns a (messages, text log) tuple. @@ -86,7 +87,8 @@ class DaemonTests(object): ssl=ssl, ws_read_limit=ws_read_limit, timeout = timeout, - fp = logfp + fp = logfp, + use_http2 = use_http2, ) c.connect(connect_to) ret = []