http2: implement Headers for request & response
improve test coverage fix super ctor call fix legacy httpversion simpliy SSLInfo without ALPN
This commit is contained in:
parent
ec68aa303e
commit
408b4ffef0
|
@ -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:
|
||||
|
|
|
@ -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 = '<no protocol negotiated>'
|
||||
|
||||
parts = [
|
||||
"Application Layer Protocol: %s" % alp,
|
||||
"Application Layer Protocol: %s" % self.alp,
|
||||
"Cipher: %s, %s bit, %s" % self.cipher,
|
||||
"SSL certificate chain:"
|
||||
]
|
||||
|
|
|
@ -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):
|
||||
"""
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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 = []
|
||||
|
|
Loading…
Reference in New Issue