Source code for multipart_stream_parser
from collections import namedtuple
__version__ = "0.1.0"
[docs]class MultipartType(namedtuple('MultipartType',
('subtype', 'boundary', 'parameters'))):
"""TODO"""
pass
[docs]class Multipart(namedtuple('Multipart', ('headers', 'body'))):
"""TODO"""
pass
[docs]class InvalidContentType(Exception):
"""TODO"""
pass
[docs]def is_multipart(content_type):
"""TODO"""
return content_type.startswith('multipart/')
[docs]def multipart_type(content_type, prepend_boundary_dashes=None):
"""TODO
"""
if not is_multipart(content_type):
raise InvalidContentType(
'Not a multipart type: "{}"'.format(content_type))
st_start = content_type.find('/')+1
st_end = content_type.find(';')
subtype = content_type[st_start:st_end]
boundary = None
parameters = {}
for param in content_type[st_end+1:].strip().split():
k, v = param.split('=', 1)
if k == 'boundary':
boundary = v
else:
parameters[k] = v
if boundary is None:
raise InvalidContentType(
'boundary parameter is missing: "{}"'.format(content_type))
if prepend_boundary_dashes or \
(prepend_boundary_dashes is None and
(not boundary.startswith('--') and
len(boundary) > 2 and boundary[2] != '-')):
boundary = '--' + boundary
return MultipartType(subtype=subtype, boundary=boundary,
parameters=parameters)
def _find_pattern(pattern, buf, iterator, start=0):
"""Find pattern in buf, appending new data from iterator to buf if
necessary
"""
while len(buf) <= start + len(pattern):
buf.extend(next(iterator))
while True:
pos = buf.find(pattern, start)
if pos >= 0:
assert pos >= start
return pos
else:
start = len(buf) - len(pattern) - 1
buf.extend(next(iterator))
def _read_headers(buf, iterator):
"""Read all headers, yield them, and remove them from buf"""
start = 0
while True:
pos = _find_pattern(b'\r\n', buf, iterator, start)
if pos == start:
buf = buf[pos+2:]
return
else:
k, v = buf[start:pos].decode('ascii').split(': ', maxsplit=1)
yield k.title(), v
start = pos + 2
[docs]def parse_parts(iterator, boundary):
"""TODO"""
boundary = boundary.encode('ascii') + b'\r\n'
buf = bytearray()
pos = _find_pattern(boundary, buf, iterator)
buf = buf[pos+len(boundary):]
boundary = b'\r\n' + boundary
while True:
while len(buf) < 2:
buf.extend(next(iterator))
if buf[0:1] == b'--':
return
headers = dict(_read_headers(buf, iterator))
# TODO: if content-lenght specified, preload data and check
# for boundary there
pos = _find_pattern(boundary, buf, iterator)
newbuf = buf[pos+len(boundary):]
yield Multipart(headers=headers, body=buf[:pos])
buf = newbuf