From b6bd5ddc3357d99f22f5181f5daecb46512650ba Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Sat, 11 Apr 2009 09:27:04 +0000 Subject: [PATCH] [1.0.X] 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. Merge of r10513 from trunk. git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.0.X@10514 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 314e072446..0b3f03fdb9 100644 --- a/AUTHORS +++ b/AUTHORS @@ -422,6 +422,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 b144fe2fdb..ad2b63a729 100644 --- a/django/test/client.py +++ b/django/test/client.py @@ -1,6 +1,7 @@ import urllib import sys import os +import re try: from cStringIO import StringIO except ImportError: @@ -21,7 +22,7 @@ from django.utils.itercompat import is_iterable BOUNDARY = 'BoUnDaRyStRiNg' MULTIPART_CONTENT = 'multipart/form-data; boundary=%s' % BOUNDARY - +CONTENT_TYPE_RE = re.compile('.*; charset=([\w\d-]+);?') class FakePayload(object): """ @@ -279,7 +280,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) r = { 'CONTENT_LENGTH': len(post_data), diff --git a/tests/regressiontests/test_client_regress/models.py b/tests/regressiontests/test_client_regress/models.py index fa12c5d69a..37d6ee1346 100644 --- a/tests/regressiontests/test_client_regress/models.py +++ b/tests/regressiontests/test_client_regress/models.py @@ -412,3 +412,36 @@ class SessionTests(TestCase): self.failUnless(login, 'Could not log in') self.client.logout() self.client.logout() + +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 40aaf07217..6e50b1a014 100644 --- a/tests/regressiontests/test_client_regress/urls.py +++ b/tests/regressiontests/test_client_regress/urls.py @@ -10,4 +10,5 @@ urlpatterns = patterns('', (r'^set_session/$', views.set_session_view), (r'^check_session/$', views.check_session_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 6d0972add2..1b6ceaadc0 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" @@ -47,3 +52,21 @@ def check_session_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