def test_POST_multipart_with_content_length_zero(self): """ Multipart POST requests with Content-Length >= 0 are valid and need to be handled. """ # According to: # https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.13 # Every request.POST with Content-Length >= 0 is a valid request, # this test ensures that we handle Content-Length == 0. payload = FakePayload("\r\n".join([ '--boundary', 'Content-Disposition: form-data; name="name"', '', 'value', '--boundary--' ''])) request = WSGIRequest({ 'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': 'multipart/form-data; boundary=boundary', 'CONTENT_LENGTH': 0, 'wsgi.input': payload, }) self.assertEqual(request.POST, {})
def test_POST_after_body_read_and_stream_read_multipart(self): """ POST should be populated even if body is read first, and then the stream is read second. Using multipart/form-data instead of urlencoded. """ payload = FakePayload("\r\n".join([ '--boundary', 'Content-Disposition: form-data; name="name"', '', 'value', '--boundary--' ''])) request = WSGIRequest({ 'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': 'multipart/form-data; boundary=boundary', 'CONTENT_LENGTH': len(payload), 'wsgi.input': payload, }) request.body # evaluate # Consume enough data to mess up the parsing: self.assertEqual(request.read(13), b'--boundary\r\nC') self.assertEqual(request.POST, {'name': ['value']})
def test_body_after_POST_multipart_related(self): """ Reading body after parsing multipart that isn't form-data is allowed """ # Ticket #9054 # There are cases in which the multipart data is related instead of # being a binary upload, in which case it should still be accessible # via body. payload_data = b"\r\n".join([ b'--boundary', b'Content-ID: id; name="name"', b'', b'value', b'--boundary--' b'' ]) payload = FakePayload(payload_data) request = WSGIRequest({ 'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': 'multipart/related; boundary=boundary', 'CONTENT_LENGTH': len(payload), 'wsgi.input': payload }) self.assertEqual(request.POST, {}) self.assertEqual(request.body, payload_data)
def setUp(self): """initialize a basic djagno wsgi request.""" # copied from django.test.client self.environ = { 'HTTP_COOKIE': '', 'PATH_INFO': '/', 'REMOTE_ADDR': '127.0.0.1', 'REQUEST_METHOD': 'GET', 'SCRIPT_NAME': '', 'SERVER_NAME': 'testserver', 'SERVER_PORT': '80', 'SERVER_PROTOCOL': 'HTTP/1.1', 'wsgi.version': (1, 0), 'wsgi.url_scheme': 'http', 'wsgi.input': FakePayload(b''), 'wsgi.errors': '', 'wsgi.multiprocess': True, 'wsgi.multithread': False, 'wsgi.run_once': False, } self.request = WSGIRequest(self.environ)
def test_body_after_POST_multipart_form_data(self): """ Reading body after parsing multipart/form-data is not allowed """ # Because multipart is used for large amounts of data i.e. file uploads, # we don't want the data held in memory twice, and we don't want to # silence the error by setting body = '' either. for method in HTTP_METHODS_WITH_BODY: payload = FakePayload("\r\n".join([ '--boundary', 'Content-Disposition: form-data; name="name"', '', 'value', '--boundary--' '' ])) request = WSGIRequest({ 'REQUEST_METHOD': method, 'CONTENT_TYPE': 'multipart/form-data; boundary=boundary', 'CONTENT_LENGTH': len(payload), 'wsgi.input': payload }) self.assertEqual(request.POST, {'name': ['value']}) with self.assertRaises(RawPostDataException): request.body
def put(self, url, consumer=None, token=None, callback=False, verifier=None, data={}, content_type=MULTIPART_CONTENT, **kwargs): """ Send a resource to the server using PUT. """ # If data has come from JSON remove unicode keys. data = dict([(str(k), v) for k, v in data.items()]) url = get_absolute_url(url) params = _get_args(consumer, callback=callback, verifier=verifier) params.update(data_keys(data)) req = oauth.Request(method='PUT', url=url, parameters=params) req.sign_request(self.signature_method, consumer, token) post_data = encode_multipart(BOUNDARY, data) parsed = urlparse.urlparse(url) query_string = urllib.urlencode(req, doseq=True) r = { 'CONTENT_LENGTH': len(post_data), 'CONTENT_TYPE': content_type, 'PATH_INFO': urllib.unquote(parsed[2]), 'QUERY_STRING': query_string, 'REQUEST_METHOD': 'PUT', 'wsgi.input': FakePayload(post_data), 'HTTP_HOST': 'api', 'HTTP_AUTHORIZATION': 'OAuth realm=""', } r.update(req) response = self.request(**r) return response
def _prepare_post_request(self, data, user=None): payload_content = ''' {{ "title": "{c[title]}", "summary": "{c[summary]}", "rating": {c[rating]}, "company": "{c[company]}", "reviewer": "{c[reviewer]}" }} '''.format(c=data) payload = FakePayload(payload_content) request = WSGIRequest({ 'REQUEST_METHOD': 'POST', 'REMOTE_ADDR': '127.0.0.1', 'CONTENT_TYPE': 'application/json', 'CONTENT_LENGTH': '{}'.format(len(payload)), 'wsgi.input': payload }) if user: request.user = user request._dont_enforce_csrf_checks = True return request
def test_POST_after_body_read_and_stream_read_multipart(self): """ POST should be populated even if body is read first, and then the stream is read second. Using multipart/form-data instead of urlencoded. """ payload = FakePayload("\r\n".join([ "--boundary", 'Content-Disposition: form-data; name="name"', "", "value", "--boundary--" "", ])) request = WSGIRequest({ "REQUEST_METHOD": "POST", "CONTENT_TYPE": "multipart/form-data; boundary=boundary", "CONTENT_LENGTH": len(payload), "wsgi.input": payload, }) request.body # evaluate # Consume enough data to mess up the parsing: self.assertEqual(request.read(13), b"--boundary\r\nC") self.assertEqual(request.POST, {"name": ["value"]})
def test_body_after_POST_multipart_form_data(self): """ Reading body after parsing multipart/form-data is not allowed """ # Because multipart is used for large amounts of data i.e. file uploads, # we don't want the data held in memory twice, and we don't want to # silence the error by setting body = '' either. payload = FakePayload("\r\n".join([ "--boundary", 'Content-Disposition: form-data; name="name"', "", "value", "--boundary--", ])) request = WSGIRequest({ "REQUEST_METHOD": "POST", "CONTENT_TYPE": "multipart/form-data; boundary=boundary", "CONTENT_LENGTH": len(payload), "wsgi.input": payload, }) self.assertEqual(request.POST, {"name": ["value"]}) with self.assertRaises(RawPostDataException): request.body
def test_invalid_multipart_content_length(self): data = 'data' boundary = 'boundary' parsed = urlparse('/path/') environ = self.client._base_environ(**{ 'CONTENT_TYPE': 'multipart/form-data; boundary=%s' % boundary, 'CONTENT_LENGTH': -1, 'PATH_INFO': self.client._get_path(parsed), 'QUERY_STRING': parsed[4], 'REQUEST_METHOD': 'POST', 'wsgi.input': FakePayload(data), }) request = WSGIRequest(environ) for content_type in self.serializer.content_types.keys(): if not content_type.startswith('multipart/'): continue self.assertRaises( ValueError, self.serializer.deserialize_request, request, request.META['CONTENT_TYPE'] )
def request(self, method, path, data='', content_type=None, status=OK): if not path.startswith('/'): path = '/' + path if not isinstance(data, str): data = json.dumps(data) content_type = content_type or 'application/json' parsed = urlparse(path) request = { 'REQUEST_METHOD': method, 'PATH_INFO': urllib.unquote(parsed[2]), 'QUERY_STRING': parsed[4], 'wsgi.input': FakePayload(data), 'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest', } if data: request['CONTENT_LENGTH'] = len(data) if content_type: request['CONTENT_TYPE'] = content_type response = self.client.request(**request) self.assertEqual(response.status_code, status, response.content) return (json.loads(response.content) if response['Content-Type'].startswith('application/json') else response.content)
def request_factory_patch(self, path, data=None, content_type=MULTIPART_CONTENT, **extra): """ Construct a PATCH request. """ # pylint: disable=invalid-name patch_data = self._encode_data(data or {}, content_type) parsed = urlparse(path) r = { 'CONTENT_LENGTH': len(patch_data), 'CONTENT_TYPE': content_type, 'PATH_INFO': self._get_path(parsed), 'QUERY_STRING': parsed[4], 'REQUEST_METHOD': 'PATCH', 'wsgi.input': FakePayload(patch_data), } r.update(extra) return self.request(**r)
def move(self, path, data={}, content_type=MULTIPART_CONTENT, follow=False, **extra): """ Send a resource to the server using MOVE. """ parsed = urlparse(path) r = { 'CONTENT_TYPE': 'text/html; charset=utf-8', 'PATH_INFO': self._get_path(parsed), 'QUERY_STRING': urlencode(data, doseq=True) or parsed[4], 'REQUEST_METHOD': 'MOVE', 'wsgi.input': FakePayload('') } r.update(extra) response = self.request(**r) if follow: response = self._handle_redirects(response, **extra) return response
def make_request(self, method='GET', path='/', data=None, content_type=None, **extra): if data is None: data = {} payload = None content_length = None if method not in ('DELETE', 'GET', 'HEAD', 'OPTIONS'): if content_type is None: content_type = MULTIPART_CONTENT encoded = self.client._encode_data(data, content_type) content_length = len(encoded) payload = FakePayload(encoded) data = {} if content_type is None: content_type = 'text/html; charset=utf-8' parsed = urlparse(path) environ = self.client._base_environ( **{ 'CONTENT_TYPE': content_type, 'PATH_INFO': self.client._get_path(parsed), 'QUERY_STRING': urlencode(data, doseq=True) or parsed[4], 'REQUEST_METHOD': method, }) if payload: environ['CONTENT_LENGTH'] = content_length environ['wsgi.input'] = payload environ.update(extra) return WSGIRequest(environ)
def put(self, url, consumer=None, token=None, callback=False, verifier=None, data={}, content_type=MULTIPART_CONTENT, **kwargs): """ Send a resource to the server using PUT. """ url = get_absolute_url(url) params = _get_args(consumer, callback=callback, verifier=verifier) req = oauth.Request(method='PUT', url=url, parameters=params) signature_method = oauth.SignatureMethod_HMAC_SHA1() req.sign_request(signature_method, consumer, token) post_data = encode_multipart(BOUNDARY, data) parsed = urlparse.urlparse(url) query_string = urllib.urlencode(req, doseq=True) r = { 'CONTENT_LENGTH': len(post_data), 'CONTENT_TYPE': content_type, 'PATH_INFO': urllib.unquote(parsed[2]), 'QUERY_STRING': query_string, 'REQUEST_METHOD': 'PUT', 'wsgi.input': FakePayload(post_data), 'HTTP_HOST': 'api', } r.update(req) response = self.request(**r) return response
def patch(self, path, data={}, content_type='application/json', follow=False, **extra): """ Requests a response from the server using PATH. """ post_data = self._encode_data(data, content_type) parsed = urlparse(path) r = { 'CONTENT_LENGTH': len(post_data), 'CONTENT_TYPE': content_type, 'PATH_INFO': self._get_path(parsed), 'QUERY_STRING': force_str(parsed[4]), 'REQUEST_METHOD': str('PATCH'), 'wsgi.input': FakePayload(post_data), } r.update(extra) response = self.request(**r) if follow: response = self._handle_redirects(response, **extra) return response
def _base_environ(self, **request): result = super(Client, self)._base_environ(**request) result["HTTP_USER_AGENT"] = "Django Unittest" result["HTTP_REFERER"] = "dummy" result["wsgi.input"] = FakePayload("") return result
def _base_environ(self, **request): result = super(Client, self)._base_environ(**request) result['HTTP_USER_AGENT'] = 'Django Unittest' result['HTTP_REFERER'] = 'dummy' result['wsgi.input'] = FakePayload('') return result
def test_write_after_read(self): payload = FakePayload() payload.read() msg = "Unable to write a payload after it's been read" with self.assertRaisesMessage(ValueError, msg): payload.write(b"abc")
def test_alter_old_distutils_request(): data = ('\n----------------GHSKFJDLGDS7543FJKLFHRE75642756743254\n' 'Content-Disposition: form-data; name="license"\n\n' 'BSD\n' '----------------GHSKFJDLGDS7543FJKLFHRE75642756743254\n' 'Content-Disposition: form-data; name="name"\n\nlocalshop\n' '----------------GHSKFJDLGDS7543FJKLFHRE75642756743254\n' 'Content-Disposition: form-data; name="metadata_version"\n\n' '1.0\n' '----------------GHSKFJDLGDS7543FJKLFHRE75642756743254\n' 'Content-Disposition: form-data; name="author"\n\n' 'Michael van Tellingen\n' '----------------GHSKFJDLGDS7543FJKLFHRE75642756743254\n' 'Content-Disposition: form-data; name="home_page"\n\n' 'http://github.com/mvantellingen/localshop\n' '----------------GHSKFJDLGDS7543FJKLFHRE75642756743254\n' 'Content-Disposition: form-data; name=":action"\n\n' 'submit\n' '----------------GHSKFJDLGDS7543FJKLFHRE75642756743254\n' 'Content-Disposition: form-data; name="download_url"\n\n' 'UNKNOWN\n' '----------------GHSKFJDLGDS7543FJKLFHRE75642756743254\n' 'Content-Disposition: form-data; name="summary"\n\n' 'A private pypi server including auto-mirroring of pypi.\n' '----------------GHSKFJDLGDS7543FJKLFHRE75642756743254\n' 'Content-Disposition: form-data; name="author_email"\n\n' '[email protected]\n' '----------------GHSKFJDLGDS7543FJKLFHRE75642756743254\n' 'Content-Disposition: form-data; name="version"\n\n' '0.1\n' '----------------GHSKFJDLGDS7543FJKLFHRE75642756743254\n' 'Content-Disposition: form-data; name="platform"\n\n' 'UNKNOWN\n' '----------------GHSKFJDLGDS7543FJKLFHRE75642756743254\n' 'Content-Disposition: form-data; name="classifiers"\n\n' 'Development Status :: 2 - Pre-Alpha\n' '----------------GHSKFJDLGDS7543FJKLFHRE75642756743254\n' 'Content-Disposition: form-data; name="classifiers"\n\n' 'Framework :: Django\n' '----------------GHSKFJDLGDS7543FJKLFHRE75642756743254\n' 'Content-Disposition: form-data; name="classifiers"\n\n' 'Intended Audience :: Developers\n' '----------------GHSKFJDLGDS7543FJKLFHRE75642756743254\n' 'Content-Disposition: form-data; name="classifiers"\n\n' 'Intended Audience :: System Administrators\n' '----------------GHSKFJDLGDS7543FJKLFHRE75642756743254\n' 'Content-Disposition: form-data; name="classifiers"\n\n' 'Operating System :: OS Independent\n' '----------------GHSKFJDLGDS7543FJKLFHRE75642756743254\n' 'Content-Disposition: form-data; name="classifiers"\n\n' 'Topic :: Software Development\n' '----------------GHSKFJDLGDS7543FJKLFHRE75642756743254\n' 'Content-Disposition: form-data; name="description"\n\n' 'UNKNOWN\n' '----------------GHSKFJDLGDS7543FJKLFHRE75642756743254--\n') content_type = 'multipart/form-data; boundary=--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' request = WSGIRequest({ 'REQUEST_METHOD': 'POST', 'CONTENT_LENGTH': len(data), 'CONTENT_TYPE': content_type, 'wsgi.input': FakePayload(data.encode('utf-8')), }) alter_old_distutils_request(request) expected_post = MultiValueDict({ 'name': ['localshop'], 'license': ['BSD'], 'author': ['Michael van Tellingen'], 'home_page': ['http://github.com/mvantellingen/localshop'], ':action': ['submit'], 'download_url': ['UNKNOWN'], 'summary': ['A private pypi server including auto-mirroring of pypi.'], 'author_email': ['*****@*****.**'], 'metadata_version': ['1.0'], 'version': ['0.1'], 'platform': ['UNKNOWN'], 'classifiers': [ 'Development Status :: 2 - Pre-Alpha', 'Framework :: Django', 'Intended Audience :: Developers', 'Intended Audience :: System Administrators', 'Operating System :: OS Independent', 'Topic :: Software Development' ], 'description': ['UNKNOWN'] }) expected_files = MultiValueDict() assert request.POST == expected_post assert request.FILES == expected_files