From 8dc73e8425f0672bb7e4734902879de53c6f7125 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Sat, 11 Apr 2009 09:20:10 +0000 Subject: [PATCH] Fixed #10571 -- Ensured that unicode POST data is correctly encoded by the test client. Thanks to Rick Wagner for his help identifying and fixing this problem. git-svn-id: http://code.djangoproject.com/svn/django/trunk@10513 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- AUTHORS | 1 + django/test/client.py | 11 +++++-- .../test_client_regress/models.py | 33 +++++++++++++++++++ .../test_client_regress/urls.py | 1 + .../test_client_regress/views.py | 23 +++++++++++++ 5 files changed, 67 insertions(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index 1bfb3ad975..94bd91e906 100644 --- a/AUTHORS +++ b/AUTHORS @@ -436,6 +436,7 @@ answer newbie questions, and generally made Django that much better: Vlado Milton Waddams Chris Wagner + Rick Wagner wam-djangobug@wamber.net Wang Chun Filip Wasilewski diff --git a/django/test/client.py b/django/test/client.py index 47232ef146..348bfa7d26 100644 --- a/django/test/client.py +++ b/django/test/client.py @@ -2,6 +2,7 @@ import urllib from urlparse import urlparse, urlunparse, urlsplit import sys import os +import re try: from cStringIO import StringIO except ImportError: @@ -25,7 +26,7 @@ from django.test.utils import ContextList BOUNDARY = 'BoUnDaRyStRiNg' MULTIPART_CONTENT = 'multipart/form-data; boundary=%s' % BOUNDARY - +CONTENT_TYPE_RE = re.compile('.*; charset=([\w\d-]+);?') class FakePayload(object): """ @@ -290,7 +291,13 @@ class Client(object): if content_type is MULTIPART_CONTENT: post_data = encode_multipart(BOUNDARY, data) else: - post_data = data + # Encode the content so that the byte representation is correct. + match = CONTENT_TYPE_RE.match(content_type) + if match: + charset = match.group(1) + else: + charset = settings.DEFAULT_CHARSET + post_data = smart_str(data, encoding=charset) parsed = urlparse(path) r = { diff --git a/tests/regressiontests/test_client_regress/models.py b/tests/regressiontests/test_client_regress/models.py index fe55ffff92..be2cb8fa37 100644 --- a/tests/regressiontests/test_client_regress/models.py +++ b/tests/regressiontests/test_client_regress/models.py @@ -624,3 +624,36 @@ class QueryStringTests(TestCase): self.assertEqual(response.context['post-bar'], 'bang') self.assertEqual(response.context['request-foo'], 'whiz') self.assertEqual(response.context['request-bar'], 'bang') + +class UnicodePayloadTests(TestCase): + def test_simple_unicode_payload(self): + "A simple ASCII-only unicode JSON document can be POSTed" + # Regression test for #10571 + json = u'{"english": "mountain pass"}' + response = self.client.post("/test_client_regress/parse_unicode_json/", json, + content_type="application/json") + self.assertEqual(response.content, json) + + def test_unicode_payload_utf8(self): + "A non-ASCII unicode data encoded as UTF-8 can be POSTed" + # Regression test for #10571 + json = u'{"dog": "собака"}' + response = self.client.post("/test_client_regress/parse_unicode_json/", json, + content_type="application/json; charset=utf-8") + self.assertEqual(response.content, json.encode('utf-8')) + + def test_unicode_payload_utf16(self): + "A non-ASCII unicode data encoded as UTF-16 can be POSTed" + # Regression test for #10571 + json = u'{"dog": "собака"}' + response = self.client.post("/test_client_regress/parse_unicode_json/", json, + content_type="application/json; charset=utf-16") + self.assertEqual(response.content, json.encode('utf-16')) + + def test_unicode_payload_non_utf(self): + "A non-ASCII unicode data as a non-UTF based encoding can be POSTed" + #Regression test for #10571 + json = u'{"dog": "собака"}' + response = self.client.post("/test_client_regress/parse_unicode_json/", json, + content_type="application/json; charset=koi8-r") + self.assertEqual(response.content, json.encode('koi8-r')) diff --git a/tests/regressiontests/test_client_regress/urls.py b/tests/regressiontests/test_client_regress/urls.py index c5872bf899..99eb7b70be 100644 --- a/tests/regressiontests/test_client_regress/urls.py +++ b/tests/regressiontests/test_client_regress/urls.py @@ -23,4 +23,5 @@ urlpatterns = patterns('', (r'^check_session/$', views.check_session_view), (r'^request_methods/$', views.request_methods_view), (r'^check_unicode/$', views.return_unicode), + (r'^parse_unicode_json/$', views.return_json_file), ) diff --git a/tests/regressiontests/test_client_regress/views.py b/tests/regressiontests/test_client_regress/views.py index 75b8d887e8..5ba7cc30a9 100644 --- a/tests/regressiontests/test_client_regress/views.py +++ b/tests/regressiontests/test_client_regress/views.py @@ -1,7 +1,12 @@ +from django.conf import settings from django.contrib.auth.decorators import login_required from django.http import HttpResponse, HttpResponseRedirect from django.core.exceptions import SuspiciousOperation from django.shortcuts import render_to_response +from django.utils import simplejson +from django.utils.encoding import smart_str +from django.core.serializers.json import DjangoJSONEncoder +from django.test.client import CONTENT_TYPE_RE def no_template_view(request): "A simple view that expects a GET request, and returns a rendered template" @@ -63,3 +68,21 @@ def request_methods_view(request): def return_unicode(request): return render_to_response('unicode.html') + +def return_json_file(request): + "A view that parses and returns a JSON string as a file." + match = CONTENT_TYPE_RE.match(request.META['CONTENT_TYPE']) + if match: + charset = match.group(1) + else: + charset = settings.DEFAULT_CHARSET + + # This just checks that the uploaded data is JSON + obj_dict = simplejson.loads(request.raw_post_data.decode(charset)) + obj_json = simplejson.dumps(obj_dict, encoding=charset, + cls=DjangoJSONEncoder, + ensure_ascii=False) + response = HttpResponse(smart_str(obj_json, encoding=charset), status=200, + mimetype='application/json; charset=' + charset) + response['Content-Disposition'] = 'attachment; filename=testfile.json' + return response