Make Message classes more self-contained.

This commit is contained in:
Aldo Cortesi 2012-10-30 15:22:53 +13:00
parent b2deb470de
commit a09584b9e6
2 changed files with 51 additions and 45 deletions

View File

@ -85,6 +85,7 @@ def serve(msg, fp, settings, request_host=None):
Calling this function may modify the object. Calling this function may modify the object.
""" """
msg = msg.resolve(settings, request_host)
started = time.time() started = time.time()
hdrs = msg.headervals(settings, request_host) hdrs = msg.headervals(settings, request_host)
@ -96,7 +97,11 @@ def serve(msg, fp, settings, request_host=None):
if msg.body: if msg.body:
vals.append(msg.body.value.get_generator(settings)) vals.append(msg.body.value.get_generator(settings))
vals.reverse() vals.reverse()
actions = msg.ready_actions(settings, request_host)
actions = msg.actions[:]
actions.sort()
actions.reverse()
actions = [i.intermediate(settings) for i in actions]
disconnect = write_values(fp, vals, actions[:]) disconnect = write_values(fp, vals, actions[:])
duration = time.time() - started duration = time.time() - started
@ -105,18 +110,7 @@ def serve(msg, fp, settings, request_host=None):
started = started, started = started,
duration = duration, duration = duration,
) )
for i in msg.logattrs: ret.update(msg.log(settings))
v = getattr(msg, i)
# Careful not to log any VALUE specs without sanitizing them first. We truncate at 1k.
if hasattr(v, "values"):
v = [x[:TRUNCATE] for x in v.values(settings)]
v = "".join(v).encode("string_escape")
elif hasattr(v, "__len__"):
v = v[:TRUNCATE]
v = v.encode("string_escape")
ret[i] = v
ret["spec"] = msg.spec()
ret.update(msg.logflags)
return ret return ret
@ -228,7 +222,7 @@ class _Token(object):
""" """
return None return None
def resolve(self, msg): # pragma: no cover def resolve(self, msg, settings, request_host): # pragma: no cover
""" """
Resolves this token to ready it for transmission. This means that Resolves this token to ready it for transmission. This means that
the calculated offsets of actions are fixed. the calculated offsets of actions are fixed.
@ -559,7 +553,7 @@ class _Action(_Token):
def __init__(self, offset): def __init__(self, offset):
self.offset = offset self.offset = offset
def resolve_offset(self, msg, settings, request_host): def resolve(self, msg, settings, request_host):
""" """
Resolves offset specifications to a numeric offset. Returns a copy Resolves offset specifications to a numeric offset. Returns a copy
of the action object. of the action object.
@ -713,11 +707,11 @@ class _Message(object):
l += len(i.value.get_generator(settings)) l += len(i.value.get_generator(settings))
return l return l
def headervals(self, settings, request_host): def resolve(self, settings, request_host):
hdrs = self.headers[:] tokens = self.tokens[:]
if not self.raw: if not self.raw:
if self.body and not utils.get_header("Content-Length", self.headers): if self.body and not utils.get_header("Content-Length", self.headers):
hdrs.append( tokens.append(
Header( Header(
ValueLiteral("Content-Length"), ValueLiteral("Content-Length"),
ValueLiteral(str(len(self.body.value.get_generator(settings)))), ValueLiteral(str(len(self.body.value.get_generator(settings)))),
@ -725,7 +719,7 @@ class _Message(object):
) )
if request_host: if request_host:
if not utils.get_header("Host", self.headers): if not utils.get_header("Host", self.headers):
hdrs.append( tokens.append(
Header( Header(
ValueLiteral("Host"), ValueLiteral("Host"),
ValueLiteral(request_host) ValueLiteral(request_host)
@ -733,23 +727,21 @@ class _Message(object):
) )
else: else:
if not utils.get_header("Date", self.headers): if not utils.get_header("Date", self.headers):
hdrs.append( tokens.append(
Header( Header(
ValueLiteral("Date"), ValueLiteral("Date"),
ValueLiteral(formatdate(timeval=None, localtime=False, usegmt=True)) ValueLiteral(formatdate(timeval=None, localtime=False, usegmt=True))
) )
) )
intermediate = self.__class__(tokens)
return self.__class__([i.resolve(intermediate, settings, request_host) for i in tokens])
def headervals(self, settings, request_host):
values = [] values = []
for h in hdrs: for h in self.headers:
values.extend(h.values(settings)) values.extend(h.values(settings))
return values return values
def ready_actions(self, settings, request_host):
actions = [i.resolve_offset(self, settings, request_host) for i in self.actions]
actions.sort()
actions.reverse()
return [i.intermediate(settings) for i in actions]
@abc.abstractmethod @abc.abstractmethod
def preamble(self, settings): # pragma: no cover def preamble(self, settings): # pragma: no cover
pass pass
@ -758,6 +750,24 @@ class _Message(object):
def expr(klass): # pragma: no cover def expr(klass): # pragma: no cover
pass pass
def log(self, settings):
"""
A dictionary that should be logged if this message is served.
"""
ret = {}
for i in self.logattrs:
v = getattr(self, i)
# Careful not to log any VALUE specs without sanitizing them first. We truncate at 1k.
if hasattr(v, "values"):
v = [x[:TRUNCATE] for x in v.values(settings)]
v = "".join(v).encode("string_escape")
elif hasattr(v, "__len__"):
v = v[:TRUNCATE]
v = v.encode("string_escape")
ret[i] = v
ret["spec"] = self.spec()
return ret
Sep = pp.Optional(pp.Literal(":")).suppress() Sep = pp.Optional(pp.Literal(":")).suppress()
@ -774,7 +784,6 @@ class Response(_Message):
Reason Reason
) )
logattrs = ["code", "reason", "version", "body"] logattrs = ["code", "reason", "version", "body"]
logflags = dict()
@property @property
def code(self): def code(self):
return self._get_token(Code) return self._get_token(Code)
@ -820,7 +829,6 @@ class Request(_Message):
Raw Raw
) )
logattrs = ["method", "path", "body"] logattrs = ["method", "path", "body"]
logflags = dict()
@property @property
def method(self): def method(self):
return self._get_token(Method) return self._get_token(Method)
@ -855,16 +863,14 @@ class Request(_Message):
return ":".join([i.spec() for i in self.tokens]) return ":".join([i.spec() for i in self.tokens])
class PathodErrorResponse(Response): def PathodErrorResponse(reason, body=None):
logflags = dict(internal=True)
def __init__(self, reason, body=None):
tokens = [ tokens = [
Code("800"), Code("800"),
Header(ValueLiteral("Content-Type"), ValueLiteral("text/plain")), Header(ValueLiteral("Content-Type"), ValueLiteral("text/plain")),
Reason(ValueLiteral(reason)), Reason(ValueLiteral(reason)),
Body(ValueLiteral("pathod error: " + (body or reason))), Body(ValueLiteral("pathod error: " + (body or reason))),
] ]
Response.__init__(self, tokens) return Response(tokens)
FILESTART = "+" FILESTART = "+"

View File

@ -257,10 +257,10 @@ class Test_Action:
l.sort() l.sort()
assert l[0].offset == 0 assert l[0].offset == 0
def test_resolve_offset(self): def test_resolve(self):
r = language.parse_request({}, 'GET:"/foo"') r = language.parse_request({}, 'GET:"/foo"')
e = language.DisconnectAt("r") e = language.DisconnectAt("r")
ret = e.resolve_offset(r, {}, None) ret = e.resolve(r, {}, None)
assert isinstance(ret.offset, int) assert isinstance(ret.offset, int)
def test_repr(self): def test_repr(self):
@ -584,9 +584,9 @@ class TestResponse:
s = cStringIO.StringIO() s = cStringIO.StringIO()
language.serve(x, s, {}) language.serve(x, s, {})
assert x.length({}, None) == len(s.getvalue()) assert x.length({}, None) == len(s.getvalue())
testlen(language.parse_response({}, "400:m'msg'")) testlen(language.parse_response({}, "400:m'msg':r"))
testlen(language.parse_response({}, "400:m'msg':h'foo'='bar'")) testlen(language.parse_response({}, "400:m'msg':h'foo'='bar':r"))
testlen(language.parse_response({}, "400:m'msg':h'foo'='bar':b@100b")) testlen(language.parse_response({}, "400:m'msg':h'foo'='bar':b@100b:r"))
def test_maximum_length(self): def test_maximum_length(self):
def testlen(x): def testlen(x):