def test_tolerant_magic_dict_get_first(self): magic_dict = TolerantMagicDict() test_list = ["b", "c", "d"] for item in test_list: magic_dict.add(random.choice(["a", "A"]), item) self.assertEqual(magic_dict.get_first("A"), test_list[0])
def test_tolerant_magic_dict_delitem_method(self): magic_dict = TolerantMagicDict() magic_dict.add("a", "b") magic_dict.add("A", "c") magic_dict.add("a", "d") del magic_dict["a"] self.assertNotIn("a", magic_dict) self.assertNotIn("A", magic_dict)
def _parse_origin_path(self): parsed_url = urllib.parse.urlparse(self.origin_path) self._path = parsed_url.path link_args = TolerantMagicDict() for (query_name, query_value) in urllib.parse.parse_qsl( parsed_url.query): link_args.add(query_name, query_value) self._link_args = link_args
def parse(content_type: str, data: bytes) -> "HTTPMultipartBody": """ Parse HTTP v1 Multipart Body. It will raise an Error during the parse period if parse failed. """ body_args = HTTPMultipartBody() if not content_type.lower().startswith("multipart/form-data"): raise ProtocolError("Unknown content-type.") for field in content_type.split(";"): # Search Boundary if field.find("boundary=") == -1: continue boundary = ensure_bytes(field.split("=")[1]) if boundary.startswith(b'"') and boundary.endswith(b'"'): boundary = boundary[1:-1] break else: raise ProtocolError("Cannot Find Boundary.") full_boundary = b"--" + boundary body_content = data.split(full_boundary + b"--")[0] full_boundary += _CRLF_BYTES_MARK splitted_body_content = body_content.split(full_boundary) for part in splitted_body_content: if not part: continue initial, content = part.split(_CRLF_BYTES_MARK * 2) headers = HTTPHeaders.parse(initial) disposition = headers.get_first("content-disposition") disposition_list = [] disposition_dict = TolerantMagicDict() for field in disposition.split(";"): # Split Disposition field = field.strip() # Remove Useless Spaces. if field.find("=") == -1: # This is not a key-value pair. disposition_list.append(field) continue key, value = field.split("=") if value.startswith('"') and value.endswith('"'): value = value[1:-1] disposition_dict.add(key.strip().lower(), value.strip()) if disposition_list[0] != "form-data": raise ProtocolError("Cannot Parse Body.") # Mixed form-data will be supported later. content = content[:-2] # Drop CRLF Mark if "filename" in disposition_dict.keys(): body_args.files.add( disposition_dict.get_first("name", ""), HTTPMultipartFileField( fieldname=disposition_dict.get_first("name", ""), filename=disposition_dict.get_first("filename", ""), content=content, content_type=headers.get_first( "content-type", "application/octet-stream"), headers=headers, encoding=headers.get_first("content-transfer-encoding", "binary"))) else: try: content = content.decode() except UnicodeDecodeError: pass body_args.add(disposition_dict.get_first("name", ""), content) return body_args
def __init__(self, *args, **kwargs): self.files = TolerantMagicDict() TolerantMagicDict.__init__(self, *args, **kwargs)
class HTTPMultipartBody(TolerantMagicDict): """ HTTPBody class, based on TolerantMagicDict. It has not only all the features from TolerantMagicDict, but also can parse and make HTTP Body. """ def __init__(self, *args, **kwargs): self.files = TolerantMagicDict() TolerantMagicDict.__init__(self, *args, **kwargs) @staticmethod def parse(content_type: str, data: bytes) -> "HTTPMultipartBody": """ Parse HTTP v1 Multipart Body. It will raise an Error during the parse period if parse failed. """ body_args = HTTPMultipartBody() if not content_type.lower().startswith("multipart/form-data"): raise ProtocolError("Unknown content-type.") for field in content_type.split(";"): # Search Boundary if field.find("boundary=") == -1: continue boundary = ensure_bytes(field.split("=")[1]) if boundary.startswith(b'"') and boundary.endswith(b'"'): boundary = boundary[1:-1] break else: raise ProtocolError("Cannot Find Boundary.") full_boundary = b"--" + boundary body_content = data.split(full_boundary + b"--")[0] full_boundary += _CRLF_BYTES_MARK splitted_body_content = body_content.split(full_boundary) for part in splitted_body_content: if not part: continue initial, content = part.split(_CRLF_BYTES_MARK * 2) headers = HTTPHeaders.parse(initial) disposition = headers.get_first("content-disposition") disposition_list = [] disposition_dict = TolerantMagicDict() for field in disposition.split(";"): # Split Disposition field = field.strip() # Remove Useless Spaces. if field.find("=") == -1: # This is not a key-value pair. disposition_list.append(field) continue key, value = field.split("=") if value.startswith('"') and value.endswith('"'): value = value[1:-1] disposition_dict.add(key.strip().lower(), value.strip()) if disposition_list[0] != "form-data": raise ProtocolError("Cannot Parse Body.") # Mixed form-data will be supported later. content = content[:-2] # Drop CRLF Mark if "filename" in disposition_dict.keys(): body_args.files.add( disposition_dict.get_first("name", ""), HTTPMultipartFileField( fieldname=disposition_dict.get_first("name", ""), filename=disposition_dict.get_first("filename", ""), content=content, content_type=headers.get_first( "content-type", "application/octet-stream"), headers=headers, encoding=headers.get_first("content-transfer-encoding", "binary"))) else: try: content = content.decode() except UnicodeDecodeError: pass body_args.add(disposition_dict.get_first("name", ""), content) return body_args def assemble(self) -> Tuple[bytes, str]: """ Generate HTTP v1 Body to bytes. It will return the body in bytes and the content-type in str. """ body = b"" boundary = "----------FutureFinityFormBoundary" boundary += ensure_str(security.get_random_str(8)).lower() content_type = "multipart/form-data; boundary=" + boundary full_boundary = b"--" + ensure_bytes(boundary) for field_name, field_value in self.items(): body += full_boundary + _CRLF_BYTES_MARK if isinstance(field_value, str): body += b"Content-Disposition: form-data; " body += ensure_bytes("name=\"%s\"\r\n" % field_name) body += _CRLF_BYTES_MARK body += ensure_bytes(field_value) body += _CRLF_BYTES_MARK else: raise ProtocolError("Unknown Field Type") for file_field in self.files.values(): body += full_boundary + _CRLF_BYTES_MARK body += file_field.assemble() body += full_boundary + b"--" + _CRLF_BYTES_MARK return body, content_type def __str__(self) -> str: # Multipart Body is not printable. return object.__str__(self) def __repr__(self) -> str: # Multipart Body is not printable. return object.__repr__(self) def copy(self) -> "HTTPMultipartBody": raise ProtocolError("HTTPMultipartBody is not copyable.") __copy__ = copy
def test_tolerant_magic_dict_copy(self): magic_dict = TolerantMagicDict() magic_dict.add("a", "b") magic_dict.add("C", "D") copied_magic_dict = magic_dict.copy() self.assertEqual(magic_dict, copied_magic_dict)
def test_tolerant_magic_dict_str_method(self): magic_dict = TolerantMagicDict() magic_dict.add("A", "b") self.assertEqual(str(magic_dict), "TolerantMagicDict([('a', 'b')])")
def test_tolerant_magic_dict_getitem_method(self): magic_dict = TolerantMagicDict() magic_dict.add("a", "b") magic_dict.add("a", "c") magic_dict.add("a", "d") self.assertEqual(magic_dict["A"], "b")
def test_tolerant_magic_dict_setitem_method(self): magic_dict = TolerantMagicDict() magic_dict["A"] = "b" self.assertListEqual(magic_dict.get_list("a"), ["b"])
def test_tolerant_magic_dict_add(self): magic_dict = TolerantMagicDict() self.assertNotIn("a", magic_dict) magic_dict.add("A", "b") self.assertIn("a", magic_dict) self.assertEqual("b", magic_dict["a"])
def __init__(self, files: typing.List=[HTTPMultipartFileField], *args, **kwargs): self.files = TolerantMagicDict() TolerantMagicDict.__init__(self, *args, **kwargs)