From 46255e6e9cfca6ee5b32d287be7d2a0eb8d73c20 Mon Sep 17 00:00:00 2001 From: Thomas Kriechbaumer Date: Mon, 22 Jun 2015 12:42:39 +0200 Subject: http2: implement more language features --- libpathod/language/http.py | 12 ++-- libpathod/language/http2.py | 140 +++++++++++++++++++++++++++++++------------- 2 files changed, 104 insertions(+), 48 deletions(-) (limited to 'libpathod/language') 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..b206c0cf 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,41 @@ 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,13 +143,13 @@ class Times(base.Integer): preamble = "x" -class Code(base.Integer): - pass - - -class Request(message.Message): +class Request(_HTTP2Message): comps = ( Header, + ShortcutContentType, + ShortcutUserAgent, + Raw, + # NestedResponse, Body, Times, ) @@ -82,22 +167,10 @@ class Request(message.Message): 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(cls): parts = [i.expr() for i in cls.comps] @@ -113,9 +186,6 @@ class Request(message.Message): 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 @@ -129,7 +199,7 @@ class Request(message.Message): self.rendered_values = settings.protocol.create_request( self.method.string(), self.path.string(), - headers, # TODO: parse that into a dict?! + headers, body) return self.rendered_values @@ -137,11 +207,14 @@ class Request(message.Message): return ":".join([i.spec() for i in self.tokens]) -class Response(message.Message): +class Response(_HTTP2Message): unique_name = None comps = ( Header, Body, + ShortcutContentType, + ShortcutLocation, + Raw, ) def __init__(self, tokens): @@ -153,21 +226,6 @@ class Response(message.Message): 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(cls): parts = [i.expr() for i in cls.comps] @@ -194,7 +252,7 @@ class Response(message.Message): self.rendered_values = settings.protocol.create_response( self.code.string(), self.stream_id, - headers, # TODO: parse that into a dict?! + headers, body) return self.rendered_values -- cgit v1.2.3 From 49707dd97f525bdce968678fa77be4b35951d008 Mon Sep 17 00:00:00 2001 From: Thomas Kriechbaumer Date: Mon, 22 Jun 2015 16:11:55 +0200 Subject: http2: implement embedded response --- libpathod/language/http2.py | 85 +++++++++++++++++++++++++-------------------- 1 file changed, 48 insertions(+), 37 deletions(-) (limited to 'libpathod/language') diff --git a/libpathod/language/http2.py b/libpathod/language/http2.py index b206c0cf..6dd93d5f 100644 --- a/libpathod/language/http2.py +++ b/libpathod/language/http2.py @@ -104,7 +104,6 @@ class Header(_HeaderMixin, base.KeyValue): preamble = "h" - class ShortcutContentType(_HeaderMixin, base.Value): preamble = "c" key = base.TokValueLiteral("content-type") @@ -143,33 +142,24 @@ class Times(base.Integer): preamble = "x" -class Request(_HTTP2Message): +class Response(_HTTP2Message): + unique_name = None comps = ( Header, + Body, ShortcutContentType, - ShortcutUserAgent, + ShortcutLocation, Raw, - # NestedResponse, - Body, - Times, ) - 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 times(self): - return self.tok(Times) + def code(self): + return self.tok(Code) @classmethod def expr(cls): @@ -177,9 +167,7 @@ class Request(_HTTP2Message): atom = pp.MatchFirst(parts) resp = pp.And( [ - Method.expr(), - base.Sep, - Path.expr(), + Code.expr(), pp.ZeroOrMore(base.Sep + atom) ] ) @@ -196,9 +184,9 @@ class Request(_HTTP2Message): if body: body = body.string() - self.rendered_values = settings.protocol.create_request( - self.method.string(), - self.path.string(), + self.rendered_values = settings.protocol.create_response( + self.code.string(), + self.stream_id, headers, body) return self.rendered_values @@ -207,24 +195,42 @@ class Request(_HTTP2Message): return ":".join([i.spec() for i in self.tokens]) -class Response(_HTTP2Message): - unique_name = None +class NestedResponse(base.NestedMessage): + preamble = "s" + nest_type = Response + + +class Request(_HTTP2Message): comps = ( Header, - Body, ShortcutContentType, - ShortcutLocation, + 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 path(self): + return self.tok(Path) + + @property + def nested_response(self): + return self.tok(NestedResponse) + + @property + def times(self): + return self.tok(Times) @classmethod def expr(cls): @@ -232,7 +238,9 @@ class Response(_HTTP2Message): atom = pp.MatchFirst(parts) resp = pp.And( [ - Code.expr(), + Method.expr(), + base.Sep, + Path.expr(), pp.ZeroOrMore(base.Sep + atom) ] ) @@ -243,15 +251,19 @@ class Response(_HTTP2Message): 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, + self.rendered_values = settings.protocol.create_request( + self.method.string(), + path, headers, body) return self.rendered_values @@ -259,7 +271,6 @@ class Response(_HTTP2Message): def spec(self): return ":".join([i.spec() for i in self.tokens]) - def make_error_response(reason, body=None): tokens = [ Code("800"), -- cgit v1.2.3