def test_read_boundary(self): src = io.BytesIO(grammar.CRLF.join(self.SIMPLE)) mstream = multipart.MultipartRecvWrapper(src, self.SIMPLE_TYPE) self.check_boundaries(mstream) # it is an error to call read_boundary during the epilogue try: mstream.read_boundary() self.fail("expected MultipartError") except multipart.MultipartError: pass # now check for non-blocking case src = io.BytesIO(grammar.CRLF.join(self.SIMPLE)) # simulate breaks in the data after LF src = MockBlockingByteReader(src, block_after=((10,))) mstream = multipart.MultipartRecvWrapper(src, self.SIMPLE_TYPE) blocks = 0 while True: result = mstream.read_boundary() if result is None: # blocked, continue blocks += 1 continue if result is False: break if not blocks: logging.warning("read_boundary expected at least one None")
def test_next_part(self): src = io.BytesIO(grammar.CRLF.join(self.SIMPLE)) mstream = multipart.MultipartRecvWrapper(src, self.SIMPLE_TYPE) # first call advances past the preamble part1 = mstream.next_part() self.assertTrue(isinstance(part1, http.RecvWrapper)) self.assertTrue(isinstance(part1.message, multipart.MessagePart)) # the process of reading from this wrapper will force the # headers to be read... # # [the boundary delimiter] is then terminated by either another # CRLF and the header fields for the next part, or by two CRLFs, # in which case there are no header fields for the next part. lines = part1.readlines() # NO header fields are actually required in body parts self.assertTrue(len(part1.message.get_headerlist()) == 0) # but we simulate the default Content-Type mtype = part1.message.get_content_type() self.assertTrue(isinstance(mtype, MediaType)) self.assertTrue(mtype.type == "text") self.assertTrue(mtype.subtype == "plain") self.assertTrue(mtype['charset'] == b"us-ascii") # The blank line is ignored as it terminates empty headers self.assertTrue(len(lines) == 2) self.assertTrue(lines[1] == b"It does NOT end with a linebreak.") # check we can't read any more data self.assertTrue(part1.read(1) == b"") part2 = mstream.next_part() lines = part2.readlines() self.assertTrue(len(part2.message.get_headerlist()) == 1) mtype = part2.message.get_content_type() self.assertTrue(isinstance(mtype, MediaType)) self.assertTrue(mtype.type == "text") self.assertTrue(mtype.subtype == "plain") self.assertTrue(mtype['charset'] == b"us-ascii") try: mstream.next_part() self.fail("Expected StopIteration for epilogue") except StopIteration: pass # check non-blocking case src = io.BytesIO(grammar.CRLF.join(self.SIMPLE)) # simulate breaks in the data after LF src = MockBlockingByteReader(src, block_after=((10,))) mstream = multipart.MultipartRecvWrapper(src, self.SIMPLE_TYPE) blocks = 0 parts = 0 while True: try: part = mstream.next_part() except StopIteration: break if part is None: # blocked, continue blocks += 1 continue parts += 1 self.assertTrue(parts == 2) if not blocks: logging.warning("next_part expected at least one None")
def test_read_nonblocking(self): src = io.BytesIO(grammar.CRLF.join(self.SIMPLE)) # simulate breaks in the data after LF src = MockBlockingByteReader(src, block_after=((10,))) mstream = multipart.MultipartRecvWrapper(src, self.SIMPLE_TYPE) # non-blocking non-empty stream line = [] blocks = 0 while True: c = mstream.read(1) if c: if c == b"\n": break else: line.append(c) continue self.assertTrue(c is None, "stream non-blocking: %s" % repr(c)) blocks += 1 # our mock blocking stream always returns None at least once self.assertTrue(blocks > 1, "non-blocking stream failed to stall") self.assertTrue( b"".join(line) == b'This is the preamble. It is to be ignored, though it\r') # readall behaviour is undefined, don't test it mstream.close()
def test_misc_headers(self): src = io.BytesIO(grammar.CRLF.join(self.MISC_HEADERS)) mstream = multipart.MultipartRecvWrapper(src, self.SIMPLE_TYPE) parts = [part for part in mstream.read_parts()] # Only Content- header fields have meaning, others may be # ignored but should be retained p = parts[0] self.assertTrue(p.message.get_header('User-Agent') == b"pyslet/1.0")
def test_empty(self): # The body must contain one or more body parts src = io.BytesIO(grammar.CRLF.join(self.EMPTY)) mstream = multipart.MultipartRecvWrapper(src, self.SIMPLE_TYPE) try: mstream.next_part() self.fail("no boundary") except multipart.MultipartError: pass # try the same thing but with no boundary at all src = io.BytesIO(grammar.CRLF.join(self.EMPTY[:-3])) mstream = multipart.MultipartRecvWrapper(src, self.SIMPLE_TYPE) try: mstream.next_part() self.fail("no boundary") except multipart.MultipartError: pass
def test_write(self): src = io.BytesIO(grammar.CRLF.join(self.SIMPLE)) mstream = multipart.MultipartRecvWrapper(src, self.SIMPLE_TYPE) self.assertFalse(mstream.writable()) try: mstream.write(b"Hello") self.fail("MultipartRecvWrapper.write") except IOError: pass mstream.close()
def test_close(self): src = io.BytesIO(grammar.CRLF.join(self.SIMPLE)) mstream = multipart.MultipartRecvWrapper(src, self.SIMPLE_TYPE) self.assertFalse(mstream.closed) mstream.close() self.assertTrue(mstream.closed) try: mstream.read(1) self.fail("MultipartRecvWrapper.read after close") except IOError: pass
def test_read_parts(self): src = io.BytesIO(grammar.CRLF.join(self.SIMPLE)) mstream = multipart.MultipartRecvWrapper(src, self.SIMPLE_TYPE) parts = [] for part in mstream.read_parts(): self.assertTrue(isinstance(part, http.RecvWrapper)) self.assertTrue(isinstance(part.message, multipart.MessagePart)) # headers should already be loaded nheaders = len(part.message.get_headerlist()) parts.append((part, part.readlines())) self.assertTrue(nheaders == len(part.message.get_headerlist())) self.assertTrue(len(parts) == 2) lines = parts[0][1] self.assertTrue(len(lines) == 2) self.assertTrue(lines[1] == b"It does NOT end with a linebreak.") lines = parts[1][1] self.assertTrue(len(lines) == 2) self.assertTrue(lines[1] == b"It DOES end with a linebreak.\r\n") # check non-blocking case src = io.BytesIO(grammar.CRLF.join(self.SIMPLE)) # simulate breaks in the data after LF src = MockBlockingByteReader(src, block_after=((10,))) mstream = multipart.MultipartRecvWrapper(src, self.SIMPLE_TYPE) blocks = 0 parts = [] for part in mstream.read_parts(): if part is None: blocks += 1 else: self.assertTrue(isinstance(part, http.RecvWrapper)) self.assertTrue(isinstance(part.message, multipart.MessagePart)) # headers should already be loaded nheaders = len(part.message.get_headerlist()) parts.append(part) self.assertTrue(nheaders == len(part.message.get_headerlist())) self.assertTrue(len(parts) == 2) if not blocks: logging.warning("read_parts expected at least one None")
def test_constructor(self): src = io.BytesIO(grammar.CRLF.join(self.SIMPLE)) # pass in a source stream and a MediaType mstream = multipart.MultipartRecvWrapper(src, self.SIMPLE_TYPE) try: mstream.fileno() self.fail("MultipartRecvWrapper.fileno") except IOError: pass # flush does nothing but is callable mstream.flush() self.assertFalse(mstream.isatty()) self.assertTrue(mstream.readable()) mstream.close()
def test_readline(self): src = io.BytesIO(grammar.CRLF.join(self.SIMPLE)) mstream = multipart.MultipartRecvWrapper(src, self.SIMPLE_TYPE) # starts by reading the preamble self.assertTrue( mstream.readline() == b"This is the preamble. It is to be ignored, though it\r\n") mstream.readline() # note the preamble ends with a line break self.assertTrue( mstream.readline() == b"explanatory note to non-MIME conformant readers.\r\n") # that's the lot self.assertTrue(mstream.readline() == b"") mstream.close()
def test_header_defaults(self): src = io.BytesIO(grammar.CRLF.join(self.SIMPLE)) mstream = multipart.MultipartRecvWrapper(src, self.SIMPLE_TYPE) parts = [part for part in mstream.read_parts()] # A body part that starts with a blank line ... is a body part # for which all default values are to be assumed. p = parts[0] # The absence of a Content-Type 'usually' indicates "text/plain; # charset=US-ASCII". If no Content-Type field is present it is # assumed to be "message/rfc822" in a "multipart/digest" and # "text/plain" otherwise. t = p.message.get_content_type() self.assertTrue(t == "text/plain; charset=us-ascii") # "Content-Transfer-Encoding: 7BIT" is assumed if the # Content-Transfer-Encoding header field is not present. self.assertTrue(p.message.get_content_transfer_encoding() == "7bit") self.assertTrue(p.message.get_content_id() is None) self.assertTrue(p.message.get_content_description() is None)
def test_read(self): src = io.BytesIO(grammar.CRLF.join(self.SIMPLE)) mstream = multipart.MultipartRecvWrapper(src, self.SIMPLE_TYPE) # blocking stream c = mstream.read(1) self.assertTrue(c == b"T") line = [] while c != b"\n": line.append(c) c = mstream.read(1) # won't return None self.assertTrue(len(c) == 1) self.assertTrue( b"".join(line) == b'This is the preamble. It is to be ignored, though it\r') data = mstream.read() self.assertTrue(data.endswith(b"readers.\r\n"), data) self.assertTrue(mstream.read(1) == b"") mstream.close()
def test_bad_boundary(self): # Boundary string comparisons must compare the boundary value # with the beginning of each candidate line. An exact match of # the entire candidate line is not required; it is sufficient # that the boundary appear in its entirety following the CRLF. # # Therefore, a boundary delimiter followed by anything other # than white space suggests a violation of: # # Boundary delimiters must not appear within the encapsulated # material src = io.BytesIO(grammar.CRLF.join(self.BAD)) mstream = multipart.MultipartRecvWrapper(src, self.SIMPLE_TYPE) # ignore the preamble self.assertTrue(mstream.read_boundary()) try: mstream.read_boundary() self.fail("boundary detected in encapsulated material") except multipart.MultipartError: pass
def test_seek(self): src = io.BytesIO(grammar.CRLF.join(self.SIMPLE)) mstream = multipart.MultipartRecvWrapper(src, self.SIMPLE_TYPE) self.assertFalse(mstream.seekable()) try: mstream.seek(0) self.fail("MultipartRecvWrapper.seek") except IOError: pass try: mstream.tell() self.fail("MultipartRecvWrapper.tell") except IOError: pass try: mstream.truncate(0) self.fail("MultipartRecvWrapper.truncate") except IOError: pass mstream.close()
def test_lws(self): # The boundary may be followed by zero or more characters of # linear whitespace. src = io.BytesIO(grammar.CRLF.join(self.LWS)) mstream = multipart.MultipartRecvWrapper(src, self.SIMPLE_TYPE) self.check_boundaries(mstream)