2015-06-08 08:45:17 +00:00
|
|
|
import os
|
|
|
|
import netlib.http2
|
|
|
|
import pyparsing as pp
|
|
|
|
from . import base, generators, actions, message
|
|
|
|
|
|
|
|
"""
|
|
|
|
Normal HTTP requests:
|
|
|
|
<method>:<path>:<header>:<body>
|
|
|
|
e.g.:
|
|
|
|
GET:/
|
|
|
|
GET:/:foo=bar
|
|
|
|
POST:/:foo=bar:'content body payload'
|
|
|
|
|
|
|
|
Individual HTTP/2 frames:
|
|
|
|
h2f:<payload_length>:<type>:<flags>:<stream_id>:<payload>
|
|
|
|
e.g.:
|
|
|
|
h2f:0:PING
|
|
|
|
h2f:42:HEADERS:END_HEADERS:0x1234567:foo=bar,host=example.com
|
|
|
|
h2f:42:DATA:END_STREAM,PADDED:0x1234567:'content body payload'
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
class Method(base.OptionsOrValue):
|
|
|
|
options = [
|
|
|
|
"GET",
|
|
|
|
"HEAD",
|
|
|
|
"POST",
|
|
|
|
"PUT",
|
|
|
|
"DELETE",
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
class Path(base.Value):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class Header(base.KeyValue):
|
2015-06-16 11:52:41 +00:00
|
|
|
unique_name = None
|
2015-06-08 08:45:17 +00:00
|
|
|
preamble = "h"
|
|
|
|
|
2015-06-16 11:52:41 +00:00
|
|
|
def values(self, settings):
|
|
|
|
return (
|
|
|
|
self.key.get_generator(settings),
|
|
|
|
self.value.get_generator(settings),
|
|
|
|
)
|
|
|
|
|
2015-06-08 08:45:17 +00:00
|
|
|
|
|
|
|
class Body(base.Value):
|
|
|
|
preamble = "b"
|
|
|
|
|
|
|
|
|
|
|
|
class Times(base.Integer):
|
|
|
|
preamble = "x"
|
|
|
|
|
2015-06-16 11:52:41 +00:00
|
|
|
|
2015-06-11 14:13:22 +00:00
|
|
|
class Code(base.Integer):
|
|
|
|
pass
|
2015-06-08 08:45:17 +00:00
|
|
|
|
2015-06-16 11:52:41 +00:00
|
|
|
|
2015-06-08 08:45:17 +00:00
|
|
|
class Request(message.Message):
|
|
|
|
comps = (
|
|
|
|
Header,
|
|
|
|
Body,
|
|
|
|
Times,
|
|
|
|
)
|
2015-06-11 14:13:22 +00:00
|
|
|
logattrs = ["method", "path"]
|
|
|
|
|
|
|
|
def __init__(self, tokens):
|
2015-06-16 11:52:41 +00:00
|
|
|
super(Request, self).__init__(tokens)
|
2015-06-11 14:13:22 +00:00
|
|
|
self.rendered_values = None
|
2015-06-08 08:45:17 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def method(self):
|
|
|
|
return self.tok(Method)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def path(self):
|
|
|
|
return self.tok(Path)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def headers(self):
|
|
|
|
return self.toks(Header)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def body(self):
|
|
|
|
return self.tok(Body)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def times(self):
|
|
|
|
return self.tok(Times)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def actions(self):
|
|
|
|
return []
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def expr(klass):
|
|
|
|
parts = [i.expr() for i in klass.comps]
|
|
|
|
atom = pp.MatchFirst(parts)
|
|
|
|
resp = pp.And(
|
|
|
|
[
|
|
|
|
Method.expr(),
|
|
|
|
base.Sep,
|
|
|
|
Path.expr(),
|
|
|
|
pp.ZeroOrMore(base.Sep + atom)
|
|
|
|
]
|
|
|
|
)
|
|
|
|
resp = resp.setParseAction(klass)
|
|
|
|
return resp
|
|
|
|
|
|
|
|
def resolve(self, settings, msg=None):
|
2015-06-11 14:13:22 +00:00
|
|
|
return self
|
|
|
|
|
|
|
|
def values(self, settings):
|
|
|
|
if self.rendered_values:
|
|
|
|
return self.rendered_values
|
|
|
|
else:
|
2015-06-16 11:52:41 +00:00
|
|
|
headers = [header.values(settings) for header in self.headers]
|
2015-06-11 14:13:22 +00:00
|
|
|
|
|
|
|
body = self.body
|
|
|
|
if body:
|
|
|
|
body = body.string()
|
|
|
|
|
|
|
|
self.rendered_values = settings.protocol.create_request(
|
|
|
|
self.method.string(),
|
|
|
|
self.path.string(),
|
|
|
|
headers, # TODO: parse that into a dict?!
|
|
|
|
body)
|
|
|
|
return self.rendered_values
|
|
|
|
|
|
|
|
def spec(self):
|
|
|
|
return ":".join([i.spec() for i in self.tokens])
|
|
|
|
|
|
|
|
|
|
|
|
class Response(message.Message):
|
|
|
|
unique_name = None
|
|
|
|
comps = (
|
|
|
|
Header,
|
|
|
|
Body,
|
|
|
|
)
|
|
|
|
|
|
|
|
def __init__(self, tokens):
|
|
|
|
super(Response, self).__init__(tokens)
|
|
|
|
self.rendered_values = None
|
|
|
|
self.stream_id = 0
|
|
|
|
|
|
|
|
@property
|
|
|
|
def code(self):
|
|
|
|
return self.tok(Code)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def headers(self):
|
|
|
|
return self.toks(Header)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def body(self):
|
|
|
|
return self.tok(Body)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def actions(self):
|
|
|
|
return []
|
|
|
|
|
|
|
|
def resolve(self, settings, msg=None):
|
|
|
|
return self
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def expr(klass):
|
|
|
|
parts = [i.expr() for i in klass.comps]
|
|
|
|
atom = pp.MatchFirst(parts)
|
|
|
|
resp = pp.And(
|
|
|
|
[
|
|
|
|
Code.expr(),
|
|
|
|
pp.ZeroOrMore(base.Sep + atom)
|
|
|
|
]
|
2015-06-08 08:45:17 +00:00
|
|
|
)
|
2015-06-11 14:13:22 +00:00
|
|
|
resp = resp.setParseAction(klass)
|
|
|
|
return resp
|
2015-06-08 08:45:17 +00:00
|
|
|
|
|
|
|
def values(self, settings):
|
2015-06-11 14:13:22 +00:00
|
|
|
if self.rendered_values:
|
|
|
|
return self.rendered_values
|
|
|
|
else:
|
2015-06-16 11:52:41 +00:00
|
|
|
headers = [header.values(settings) for header in self.headers]
|
2015-06-11 14:13:22 +00:00
|
|
|
|
|
|
|
body = self.body
|
|
|
|
if body:
|
2015-06-16 09:33:10 +00:00
|
|
|
body = body.string()
|
2015-06-11 14:13:22 +00:00
|
|
|
|
|
|
|
self.rendered_values = settings.protocol.create_response(
|
|
|
|
self.code.string(),
|
|
|
|
self.stream_id,
|
|
|
|
headers, # TODO: parse that into a dict?!
|
|
|
|
body)
|
|
|
|
return self.rendered_values
|
2015-06-08 08:45:17 +00:00
|
|
|
|
|
|
|
def spec(self):
|
|
|
|
return ":".join([i.spec() for i in self.tokens])
|
|
|
|
|
2015-06-11 14:13:22 +00:00
|
|
|
def make_error_response(reason, body=None):
|
2015-06-16 09:33:10 +00:00
|
|
|
tokens = [
|
|
|
|
Code("800"),
|
|
|
|
Body(base.TokValueLiteral("pathod error: " + (body or reason))),
|
|
|
|
]
|
|
|
|
return Response(tokens)
|
2015-06-08 08:45:17 +00:00
|
|
|
|
2015-06-08 13:28:24 +00:00
|
|
|
# class Frame(message.Message):
|
2015-06-08 08:45:17 +00:00
|
|
|
# pass
|