diff options
Diffstat (limited to 'libpathod/language')
-rw-r--r-- | libpathod/language/http.py | 12 | ||||
-rw-r--r-- | libpathod/language/http2.py | 197 |
2 files changed, 138 insertions, 71 deletions
diff --git a/libpathod/language/http.py b/libpathod/language/http.py index 3979a1ee..3c9df484 100644 --- a/libpathod/language/http.py +++ b/libpathod/language/http.py @@ -82,12 +82,10 @@ class ShortcutUserAgent(_HeaderMixin, base.OptionsOrValue): key = base.TokValueLiteral("User-Agent") def values(self, settings): + value = self.value.val if self.option_used: - value = http_uastrings.get_by_shortcut( - self.value.val.lower() - )[2] - else: - value = self.value.val + value = http_uastrings.get_by_shortcut(value.lower())[2] + return self.format_header( self.key.get_generator(settings), value @@ -143,12 +141,12 @@ class _HTTPMessage(message.Message): class Response(_HTTPMessage): unique_name = None comps = ( - Body, Header, ShortcutContentType, ShortcutLocation, Raw, Reason, + Body, actions.PauseAt, actions.DisconnectAt, @@ -256,12 +254,12 @@ class NestedResponse(base.NestedMessage): class Request(_HTTPMessage): comps = ( - Body, Header, ShortcutContentType, ShortcutUserAgent, Raw, NestedResponse, + Body, Times, actions.PauseAt, diff --git a/libpathod/language/http2.py b/libpathod/language/http2.py index 86e04056..6dd93d5f 100644 --- a/libpathod/language/http2.py +++ b/libpathod/language/http2.py @@ -1,4 +1,6 @@ import pyparsing as pp + +from netlib import http_status, http_uastrings from . import base, message """ @@ -24,6 +26,65 @@ from . import base, message h2f:42:DATA:END_STREAM,PADDED:0x1234567:'content body payload' """ +def get_header(val, headers): + """ + Header keys may be Values, so we have to "generate" them as we try the + match. + """ + for h in headers: + k = h.key.get_generator({}) + if len(k) == len(val) and k[:].lower() == val.lower(): + return h + return None + + +class _HeaderMixin(object): + unique_name = None + + def values(self, settings): + return ( + self.key.get_generator(settings), + self.value.get_generator(settings), + ) + +class _HTTP2Message(message.Message): + @property + def actions(self): + return [] # self.toks(actions._Action) + + @property + def headers(self): + headers = self.toks(_HeaderMixin) + + if not self.raw: + if not get_header("content-length", headers): + if not self.body: + length = 0 + else: + length = len(self.body.string()) + headers.append( + Header( + base.TokValueLiteral("content-length"), + base.TokValueLiteral(str(length)), + ) + ) + return headers + + @property + def raw(self): + return bool(self.tok(Raw)) + + @property + def body(self): + return self.tok(Body) + + def resolve(self, settings): + return self + + +class Code(base.Integer): + pass + class Method(base.OptionsOrValue): options = [ @@ -39,17 +100,40 @@ class Path(base.Value): pass -class Header(base.KeyValue): - unique_name = None +class Header(_HeaderMixin, base.KeyValue): preamble = "h" + +class ShortcutContentType(_HeaderMixin, base.Value): + preamble = "c" + key = base.TokValueLiteral("content-type") + + +class ShortcutLocation(_HeaderMixin, base.Value): + preamble = "l" + key = base.TokValueLiteral("location") + + +class ShortcutUserAgent(_HeaderMixin, base.OptionsOrValue): + preamble = "u" + options = [i[1] for i in http_uastrings.UASTRINGS] + key = base.TokValueLiteral("user-agent") + def values(self, settings): + value = self.value.val + if self.option_used: + value = http_uastrings.get_by_shortcut(value.lower())[2] + return ( self.key.get_generator(settings), - self.value.get_generator(settings), + value ) +class Raw(base.CaselessLiteral): + TOK = "r" + + class Body(base.Value): preamble = "b" @@ -58,45 +142,24 @@ class Times(base.Integer): preamble = "x" -class Code(base.Integer): - pass - - -class Request(message.Message): +class Response(_HTTP2Message): + unique_name = None comps = ( Header, Body, - Times, + ShortcutContentType, + ShortcutLocation, + Raw, ) - logattrs = ["method", "path"] def __init__(self, tokens): - super(Request, self).__init__(tokens) + super(Response, self).__init__(tokens) self.rendered_values = None + self.stream_id = 0 @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 [] + def code(self): + return self.tok(Code) @classmethod def expr(cls): @@ -104,18 +167,13 @@ class Request(message.Message): atom = pp.MatchFirst(parts) resp = pp.And( [ - Method.expr(), - base.Sep, - Path.expr(), + Code.expr(), pp.ZeroOrMore(base.Sep + atom) ] ) resp = resp.setParseAction(cls) return resp - def resolve(self, settings, msg=None): - return self - def values(self, settings): if self.rendered_values: return self.rendered_values @@ -126,10 +184,10 @@ class Request(message.Message): 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?! + self.rendered_values = settings.protocol.create_response( + self.code.string(), + self.stream_id, + headers, body) return self.rendered_values @@ -137,36 +195,42 @@ class Request(message.Message): return ":".join([i.spec() for i in self.tokens]) -class Response(message.Message): - unique_name = None +class NestedResponse(base.NestedMessage): + preamble = "s" + nest_type = Response + + +class Request(_HTTP2Message): comps = ( Header, + ShortcutContentType, + ShortcutUserAgent, + Raw, + NestedResponse, Body, + Times, ) + logattrs = ["method", "path"] def __init__(self, tokens): - super(Response, self).__init__(tokens) + super(Request, self).__init__(tokens) self.rendered_values = None - self.stream_id = 0 @property - def code(self): - return self.tok(Code) + def method(self): + return self.tok(Method) @property - def headers(self): - return self.toks(Header) + def path(self): + return self.tok(Path) @property - def body(self): - return self.tok(Body) + def nested_response(self): + return self.tok(NestedResponse) @property - def actions(self): - return [] - - def resolve(self, settings, msg=None): - return self + def times(self): + return self.tok(Times) @classmethod def expr(cls): @@ -174,7 +238,9 @@ class Response(message.Message): atom = pp.MatchFirst(parts) resp = pp.And( [ - Code.expr(), + Method.expr(), + base.Sep, + Path.expr(), pp.ZeroOrMore(base.Sep + atom) ] ) @@ -185,23 +251,26 @@ class Response(message.Message): if self.rendered_values: return self.rendered_values else: + path = self.path.string() + if self.nested_response: + path += self.nested_response.parsed.spec() + headers = [header.values(settings) for header in self.headers] body = self.body if body: body = body.string() - self.rendered_values = settings.protocol.create_response( - self.code.string(), - self.stream_id, - headers, # TODO: parse that into a dict?! + self.rendered_values = settings.protocol.create_request( + self.method.string(), + path, + headers, body) return self.rendered_values def spec(self): return ":".join([i.spec() for i in self.tokens]) - def make_error_response(reason, body=None): tokens = [ Code("800"), |