def test_encode_multipart_data_list_params(self): params_in = [("one", ["ABC", "XYZ"]), ("one", "UVW")] body, headers = encode_multipart_data(params_in, []) params_out, files_out = self.parse_headers_and_body_with_django( headers, body) self.assertEqual({"one": ["ABC", "XYZ", "UVW"]}, params_out) self.assertSetEqual(set(), set(files_out))
def test_encode_multipart_data_multiple_params(self): # Sequences of parameters and files can be passed to # encode_multipart_data() so that multiple parameters/files with the # same name can be provided. params_in = [ ("one", "ABC"), ("one", "XYZ"), ("two", "DEF"), ("two", "UVW"), ] files_in = [ ("f-one", BytesIO(urandom(32))), ("f-two", BytesIO(urandom(32))), ] body, headers = encode_multipart_data(params_in, files_in) self.assertEqual("%s" % len(body), headers["Content-Length"]) self.assertThat(headers["Content-Type"], StartsWith("multipart/form-data; boundary=")) # Round-trip through Django's multipart code. params_out, files_out = (parse_headers_and_body_with_django( headers, body)) params_out_expected = MultiValueDict() for name, value in params_in: params_out_expected.appendlist(name, value) self.assertEqual(params_out_expected, params_out, ahem_django_ahem) self.assertSetEqual({"f-one", "f-two"}, set(files_out)) files_expected = {name: buf.getvalue() for name, buf in files_in} files_observed = {name: buf.read() for name, buf in files_out.items()} self.assertEqual(files_expected, files_observed, ahem_django_ahem)
def test_encode_multipart_data_multiple_params(self): # Sequences of parameters and files can be passed to # encode_multipart_data() so that multiple parameters/files with the # same name can be provided. params_in = [ ("one", "ABC"), ("one", "XYZ"), ("two", "DEF"), ("two", "UVW"), ] files_in = [ ("f-one", BytesIO(urandom(32))), ("f-two", BytesIO(urandom(32))), ] body, headers = encode_multipart_data(params_in, files_in) self.assertEqual("%s" % len(body), headers["Content-Length"]) self.assertThat( headers["Content-Type"], StartsWith("multipart/form-data; boundary=")) # Round-trip through Django's multipart code. params_out, files_out = ( parse_headers_and_body_with_django(headers, body)) params_out_expected = MultiValueDict() for name, value in params_in: params_out_expected.appendlist(name, value) self.assertEqual( params_out_expected, params_out, ahem_django_ahem) self.assertSetEqual({"f-one", "f-two"}, set(files_out)) files_expected = {name: buf.getvalue() for name, buf in files_in} files_observed = {name: buf.read() for name, buf in files_out.items()} self.assertEqual( files_expected, files_observed, ahem_django_ahem)
def test__get_response_restores_files_across_requests(self): handler = views.WebApplicationHandler(3) file_content = sample_binary_data file_name = 'content' recorder = [] def get_response_read_content_files(self, request): # Simple get_response method which returns the 'file_name' file # from the request in the response. content = request.FILES[file_name].read() # Record calls. recorder.append(content) response = HttpResponse(content=content, content_type=b"text/plain; charset=utf-8") handler._WebApplicationHandler__retry.add(response) return response self.patch(WSGIHandler, "get_response", get_response_read_content_files) body, headers = encode_multipart_data( [], [[file_name, io.BytesIO(file_content)]]) env = { 'REQUEST_METHOD': 'POST', 'wsgi.input': wsgi._InputStream(io.BytesIO(body.encode("utf-8"))), 'CONTENT_TYPE': headers['Content-Type'], 'CONTENT_LENGTH': headers['Content-Length'], 'HTTP_MIME_VERSION': headers['MIME-Version'], } request = make_request(env) response = handler.get_response(request) self.assertEqual(file_content, response.content) self.assertEqual(recorder, [file_content] * 3)
def test_encode_multipart_data_multiple_params(self): # Sequences of parameters and files passed to # encode_multipart_data() permit use of the same name for # multiple parameters and/or files. See `make_payloads` to # understand how it processes different types of parameter # values. params_in = [ ("one", "ABC"), ("one", "XYZ"), ("two", ["DEF", "UVW"]), ] files_in = [ ("f-one", BytesIO(b"f1")), ("f-two", open(self.make_file(contents=b"f2"), "rb")), ("f-three", lambda: open(self.make_file(contents=b"f3"), "rb")), ] body, headers = encode_multipart_data(params_in, files_in) self.assertEqual("%s" % len(body), headers["Content-Length"]) self.assertThat(headers["Content-Type"], StartsWith("multipart/form-data; boundary=")) # Round-trip through Django's multipart code. params_out, files_out = (parse_headers_and_body_with_django( headers, body)) params_out_expected = MultiValueDict() params_out_expected.appendlist("one", "ABC") params_out_expected.appendlist("one", "XYZ") params_out_expected.appendlist("two", "DEF") params_out_expected.appendlist("two", "UVW") self.assertEqual(params_out_expected, params_out, ahem_django_ahem) files_expected = {"f-one": b"f1", "f-two": b"f2", "f-three": b"f3"} files_observed = {name: buf.read() for name, buf in files_out.items()} self.assertEqual(files_expected, files_observed, ahem_django_ahem)
def prepare_payload(op, method, uri, data): """Return the URI (modified perhaps) and body and headers. - For GET requests, encode parameters in the query string. - Otherwise always encode parameters in the request body. - Except op; this can always go in the query string. :param method: The HTTP method. :param uri: The URI of the action. :param data: A dict or iterable of name=value pairs to pack into the body or headers, depending on the type of request. """ if method == "GET": query = data if op is None else chain([("op", op)], data) body, headers = None, {} else: query = [] if op is None else [("op", op)] if data: body, headers = encode_multipart_data(data) else: body, headers = None, {} uri = urlparse(uri)._replace(query=urlencode(query)).geturl() return uri, body, headers
def prepare_payload(op, method, uri, data): """Return the URI (modified perhaps) and body and headers. - For GET requests, encode parameters in the query string. - Otherwise always encode parameters in the request body. - Except op; this can always go in the query string. :param method: The HTTP method. :param uri: The URI of the action. :param data: A dict or iterable of name=value pairs to pack into the body or headers, depending on the type of request. """ if method == "GET": query = data if op is None else chain([("op", op)], data) body, headers = None, {} else: query = [] if op is None else [("op", op)] if data: body, headers = encode_multipart_data(data) else: body, headers = None, {} uri = urlparse(uri)._replace(query=urlencode(query)).geturl() return uri, body, headers
def test_encode_multipart_data_produces_str(self): data = {factory.make_string(): factory.make_string().encode("ascii")} files = { factory.make_string(): (BytesIO(factory.make_string().encode("ascii"))) } body, headers = encode_multipart_data(data, files) self.assertIsInstance(body, str)
def test_encode_multipart_data_list_params(self): params_in = [ ("one", ["ABC", "XYZ"]), ("one", "UVW"), ] body, headers = encode_multipart_data(params_in, []) params_out, files_out = ( parse_headers_and_body_with_django(headers, body)) self.assertEqual({'one': ['ABC', 'XYZ', 'UVW']}, params_out) self.assertSetEqual(set(), set(files_out))
def test_encode_multipart_data_produces_bytes(self): data = { factory.make_string(): factory.make_string().encode('ascii'), } files = { factory.make_string(): ( BytesIO(factory.make_string().encode('ascii'))), } body, headers = encode_multipart_data(data, files) self.assertIsInstance(body, bytes)
def test_encode_multipart_data_produces_bytes(self): data = { factory.getRandomString(): (factory.getRandomString().encode('ascii')), } files = { factory.getRandomString(): (BytesIO(factory.getRandomString().encode('ascii'))), } body, headers = encode_multipart_data(data, files) self.assertIsInstance(body, bytes)
def test_encode_multipart_data(self): # The encode_multipart_data() function should take a list of # parameters and files and encode them into a MIME # multipart/form-data suitable for posting to the MAAS server. params = {"op": "add", "foo": "bar\u1234"} random_data = urandom(32) files = {"baz": BytesIO(random_data)} body, headers = encode_multipart_data(params, files) self.assertEqual("%s" % len(body), headers["Content-Length"]) self.assertThat(headers["Content-Type"], StartsWith("multipart/form-data; boundary=")) # Round-trip through Django's multipart code. post, files = parse_headers_and_body_with_django(headers, body) self.assertEqual({name: [value] for name, value in params.items()}, post, ahem_django_ahem) self.assertSetEqual({"baz"}, set(files)) self.assertEqual(random_data, files["baz"].read(), ahem_django_ahem)
def test_encode_multipart_data(self): # The encode_multipart_data() function should take a list of # parameters and files and encode them into a MIME # multipart/form-data suitable for posting to the MAAS server. params = {"op": "add", "foo": "bar\u1234"} random_data = urandom(32) files = {"baz": BytesIO(random_data)} body, headers = encode_multipart_data(params, files) self.assertEqual("%s" % len(body), headers["Content-Length"]) self.assertThat( headers["Content-Type"], StartsWith("multipart/form-data; boundary=")) # Round-trip through Django's multipart code. post, files = parse_headers_and_body_with_django(headers, body) self.assertEqual( {name: [value] for name, value in params.items()}, post, ahem_django_ahem) self.assertSetEqual({"baz"}, set(files)) self.assertEqual( random_data, files["baz"].read(), ahem_django_ahem)
def test_encode_multipart_data_multiple_params(self): # Sequences of parameters and files passed to # encode_multipart_data() permit use of the same name for # multiple parameters and/or files. See `make_payloads` to # understand how it processes different types of parameter # values. params_in = [ ("one", "ABC"), ("one", "XYZ"), ("two", ["DEF", "UVW"]), ] files_in = [ ("f-one", BytesIO(b"f1")), ("f-two", open(self.make_file(contents=b"f2"), "rb")), ("f-three", lambda: open(self.make_file(contents=b"f3"), "rb")), ] body, headers = encode_multipart_data(params_in, files_in) self.assertEqual("%s" % len(body), headers["Content-Length"]) self.assertThat( headers["Content-Type"], StartsWith("multipart/form-data; boundary=")) # Round-trip through Django's multipart code. params_out, files_out = ( parse_headers_and_body_with_django(headers, body)) params_out_expected = MultiValueDict() params_out_expected.appendlist("one", "ABC") params_out_expected.appendlist("one", "XYZ") params_out_expected.appendlist("two", "DEF") params_out_expected.appendlist("two", "UVW") self.assertEqual( params_out_expected, params_out, ahem_django_ahem) files_expected = {"f-one": b"f1", "f-two": b"f2", "f-three": b"f3"} files_observed = {name: buf.read() for name, buf in files_out.items()} self.assertEqual( files_expected, files_observed, ahem_django_ahem)
def _formulate_change(self, path, params, as_json=False): """Return URL, headers, and body for a non-GET request. This is similar to _formulate_get, except parameters are encoded as a multipart form body. :param path: Path to the object to issue a GET on. :param params: A dict of parameter values. :param as_json: Encode params as application/json instead of multipart/form-data. Only use this if you know the API already supports JSON requests. :return: A tuple: URL, headers, and body for the request. """ url = self._make_url(path) if "op" in params: params = dict(params) op = params.pop("op") url += "?" + urlencode([("op", op)]) if as_json: body, headers = encode_json_data(params) else: body, headers = encode_multipart_data(params, {}) self.auth.sign_request(url, headers) return url, headers, body
def _formulate_change(self, path, params, as_json=False): """Return URL, headers, and body for a non-GET request. This is similar to _formulate_get, except parameters are encoded as a multipart form body. :param path: Path to the object to issue a GET on. :param params: A dict of parameter values. :param as_json: Encode params as application/json instead of multipart/form-data. Only use this if you know the API already supports JSON requests. :return: A tuple: URL, headers, and body for the request. """ url = self._make_url(path) if 'op' in params: params = dict(params) op = params.pop('op') url += '?' + urlencode([('op', op)]) if as_json: body, headers = encode_json_data(params) else: body, headers = encode_multipart_data(params, {}) self.auth.sign_request(url, headers) return url, headers, body
def test_encode_multipart_data_closes_with_closing_boundary_line(self): data = {b'foo': factory.getRandomString().encode('ascii')} files = {b'bar': BytesIO(factory.getRandomString().encode('ascii'))} body, headers = encode_multipart_data(data, files) self.assertThat(body, EndsWith(b'--'))
def test_encode_multipart_data_closes_with_closing_boundary_line(self): data = {b'foo': factory.make_string().encode('ascii')} files = {b'bar': BytesIO(factory.make_string().encode('ascii'))} body, headers = encode_multipart_data(data, files) self.assertThat(body, EndsWith(b'--'))
def test_encode_multipart_data_closes_with_closing_boundary_line(self): data = {"foo": factory.make_string().encode("ascii")} files = {"bar": BytesIO(factory.make_string().encode("ascii"))} body, headers = encode_multipart_data(data, files) self.assertThat(body, EndsWith("--"))