Merge lp:~therve/txaws/safe-unicode-errors into lp:txaws

Proposed by Thomas Herve
Status: Merged
Approved by: Free Ekanayaka
Approved revision: 103
Merged at revision: 102
Proposed branch: lp:~therve/txaws/safe-unicode-errors
Merge into: lp:txaws
Diff against target: 157 lines (+84/-4)
2 files modified
txaws/server/resource.py (+4/-2)
txaws/server/tests/test_resource.py (+80/-2)
To merge this branch: bzr merge lp:~therve/txaws/safe-unicode-errors
Reviewer Review Type Date Requested Status
Free Ekanayaka (community) Approve
Review via email: mp+81818@code.launchpad.net

Description of the change

The branch covers two cases where the exception value would cause the response to be stucked on the server side.

To post a comment you must log in.
Revision history for this message
Free Ekanayaka (free.ekanayaka) wrote :

Nice fix, +1!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'txaws/server/resource.py'
2--- txaws/server/resource.py 2011-10-05 21:11:09 +0000
3+++ txaws/server/resource.py 2011-11-10 08:56:23 +0000
4@@ -3,6 +3,7 @@
5 from pytz import UTC
6
7 from twisted.python import log
8+from twisted.python.reflect import safe_str
9 from twisted.internet.defer import maybeDeferred
10 from twisted.web.resource import Resource
11 from twisted.web.server import NOT_DONE_YET
12@@ -105,14 +106,15 @@
13 if status < 400 or status >= 500:
14 log.err(failure)
15 else:
16- log.msg("status: %s message: %s" % (status, failure.value))
17+ log.msg("status: %s message: %s" % (
18+ status, safe_str(failure.value)))
19
20 bytes = failure.value.response
21 if bytes is None:
22 bytes = self.dump_error(failure.value, request)
23 else:
24 log.err(failure)
25- bytes = str(failure.value)
26+ bytes = safe_str(failure.value)
27 status = 500
28 request.setResponseCode(status)
29 request.write(bytes)
30
31=== modified file 'txaws/server/tests/test_resource.py'
32--- txaws/server/tests/test_resource.py 2011-10-05 21:11:09 +0000
33+++ txaws/server/tests/test_resource.py 2011-11-10 08:56:23 +0000
34@@ -4,6 +4,7 @@
35 from datetime import datetime
36
37 from twisted.trial.unittest import TestCase
38+from twisted.python.reflect import safe_str
39
40 from txaws.credentials import AWSCredentials
41 from txaws.service import AWSServiceEndpoint
42@@ -11,6 +12,7 @@
43 from txaws.server.method import Method
44 from txaws.server.registry import Registry
45 from txaws.server.resource import QueryAPI
46+from txaws.server.exception import APIError
47
48
49 class FakeRequest(object):
50@@ -92,7 +94,7 @@
51 return self.principal
52
53 def dump_error(self, error, request):
54- return str("%s - %s" % (error.code, error.message))
55+ return str("%s - %s" % (error.code, safe_str(error.message)))
56
57
58 class QueryAPITest(TestCase):
59@@ -273,6 +275,31 @@
60 self.api.principal = TestPrincipal(creds)
61 return self.api.handle(request).addCallback(check)
62
63+ def test_handle_500_api_error(self):
64+ """
65+ If an L{APIError} is raised with a status code superior or equal to
66+ 500, the error is logged on the server side.
67+ """
68+ creds = AWSCredentials("access", "secret")
69+ endpoint = AWSServiceEndpoint("http://uri")
70+ query = Query(action="SomeAction", creds=creds, endpoint=endpoint)
71+ query.sign()
72+ request = FakeRequest(query.params, endpoint)
73+
74+ def fail_execute(call):
75+ raise APIError(500, response="oops")
76+ self.api.execute = fail_execute
77+
78+ def check(ignored):
79+ errors = self.flushLoggedErrors()
80+ self.assertEquals(1, len(errors))
81+ self.assertTrue(request.finished)
82+ self.assertEqual("oops", request.response)
83+ self.assertEqual(500, request.code)
84+
85+ self.api.principal = TestPrincipal(creds)
86+ return self.api.handle(request).addCallback(check)
87+
88 def test_handle_with_parameter_error(self):
89 """
90 If an error occurs while parsing the parameters, L{QueryAPI.handle}
91@@ -294,6 +321,57 @@
92
93 return self.api.handle(request).addCallback(check)
94
95+ def test_handle_unicode_api_error(self):
96+ """
97+ If an L{APIError} contains a unicode message, L{QueryAPI} is able to
98+ protect itself from it.
99+ """
100+ creds = AWSCredentials("access", "secret")
101+ endpoint = AWSServiceEndpoint("http://uri")
102+ query = Query(action="SomeAction", creds=creds, endpoint=endpoint)
103+ query.sign()
104+ request = FakeRequest(query.params, endpoint)
105+
106+ def fail_execute(call):
107+ raise APIError(400, code="LangError",
108+ message=u"\N{HIRAGANA LETTER A}dvanced")
109+ self.api.execute = fail_execute
110+
111+ def check(ignored):
112+ errors = self.flushLoggedErrors()
113+ self.assertEquals(0, len(errors))
114+ self.assertTrue(request.finished)
115+ self.assertTrue(request.response.startswith("LangError"))
116+ self.assertEqual(400, request.code)
117+
118+ self.api.principal = TestPrincipal(creds)
119+ return self.api.handle(request).addCallback(check)
120+
121+ def test_handle_unicode_error(self):
122+ """
123+ If an arbitrary error raised by an API method contains a unicode
124+ message, L{QueryAPI} is able to protect itself from it.
125+ """
126+ creds = AWSCredentials("access", "secret")
127+ endpoint = AWSServiceEndpoint("http://uri")
128+ query = Query(action="SomeAction", creds=creds, endpoint=endpoint)
129+ query.sign()
130+ request = FakeRequest(query.params, endpoint)
131+
132+ def fail_execute(call):
133+ raise ValueError(u"\N{HIRAGANA LETTER A}dvanced")
134+ self.api.execute = fail_execute
135+
136+ def check(ignored):
137+ errors = self.flushLoggedErrors()
138+ self.assertEquals(1, len(errors))
139+ self.assertTrue(request.finished)
140+ self.assertIn("ValueError", request.response)
141+ self.assertEqual(500, request.code)
142+
143+ self.api.principal = TestPrincipal(creds)
144+ return self.api.handle(request).addCallback(check)
145+
146 def test_handle_with_unsupported_action(self):
147 """Only actions registered in the L{Registry} are supported."""
148 creds = AWSCredentials("access", "secret")
149@@ -311,7 +389,7 @@
150
151 return self.api.handle(request).addCallback(check)
152
153- def test_handle_non_evailable_method(self):
154+ def test_handle_non_available_method(self):
155 """Only actions registered in the L{Registry} are supported."""
156
157 class NonAvailableMethod(Method):

Subscribers

People subscribed via source and target branches

to all changes: