Merge lp:~alecu/ubuntu-sso-client/updated-txweb into lp:ubuntu-sso-client

Proposed by Alejandro J. Cura on 2012-03-12
Status: Merged
Approved by: Alejandro J. Cura on 2012-03-14
Approved revision: 912
Merged at revision: 911
Proposed branch: lp:~alecu/ubuntu-sso-client/updated-txweb
Merge into: lp:ubuntu-sso-client
Diff against target: 286 lines (+131/-80)
2 files modified
ubuntu_sso/utils/webclient/tests/test_webclient.py (+54/-2)
ubuntu_sso/utils/webclient/txweb.py (+77/-78)
To merge this branch: bzr merge lp:~alecu/ubuntu-sso-client/updated-txweb
Reviewer Review Type Date Requested Status
Alejandro J. Cura (community) Abstain on 2012-03-14
Eric Casteleijn (community) Approve on 2012-03-14
Diego Sarmentero (community) Approve on 2012-03-13
Review via email: mp+97119@code.launchpad.net

Commit message

- Use HTTPClientFactory to allow replacing the reactor used to connect (LP: #929207).

Description of the change

- use HTTPClientFactory to allow replacing the reactor used to connect (LP: #929207)

To post a comment you must log in.
Diego Sarmentero (diegosarmentero) wrote :

Looks good!
+1

review: Approve
Eric Casteleijn (thisfred) wrote :

Looks good

review: Approve
Alejandro J. Cura (alecu) :
review: Abstain
Ubuntu One Auto Pilot (otto-pilot) wrote :
Download full text (146.3 KiB)

The attempt to merge lp:~alecu/ubuntu-sso-client/updated-txweb into lp:ubuntu-sso-client failed. Below is the output from the failed tests.

*** Running GTK test suite for ubuntu_sso ***
ubuntu_sso.xdg_base_directory.tests.test_common
  TestBaseDirectory
    test_load_config_paths_filter ... [OK]
    test_save_config_path ... [OK]
    test_xdg_cache_home_is_utf8_bytes ... [OK]
    test_xdg_config_dirs_are_bytes ... [OK]
    test_xdg_config_home_is_utf8_bytes ... [OK]
    test_xdg_data_dirs_are_bytes ... [OK]
    test_xdg_data_home_is_utf8_bytes ... [OK]
    test_xdg_home_is_utf8_bytes ... [OK]
twisted.trial.unittest
  TestCase
    runTest ... [OK]
ubuntu_sso.main.tests.test_clients
  AbstractTestCase
    test_error ... [OK]
    test_success ... [OK]
ubuntuone.devtools.testcases.dbus
  DBusTestCase
    runTest ... [OK]
ubuntu_sso.main.tests.test_clients
  ClearCredentialsTestCase
    test_error ... [OK]
    test_success ... [OK]
  CredentialsManagementProxyTestCase
    test_error ... [OK]
    test_success ... [OK]
  FindCredentialsTestCase
    test_error ... [OK]
    test_find_credentials_sync ... [OK]
    test_find_credentials_sync_error ... [OK]
    test_success ... [OK]
  GenerateCaptchaTestCase
    test_error ... [OK]
    test_success ... [OK]
  LoginEmailPasswordTestCase
    test_error ... [OK]
    test_success ... [OK]
  LoginOnlyTestCase
    test_error ... [OK]
    test_success ... [OK]
  LoginTestCase
    test_error ... [OK]
    test_error_when_setting_credentials ... [OK]
    test_not_validated ... [OK]
    test_success ... [OK]
  RegisterTestCase
    test_error ... [OK]
    test_success ... [OK]
  RegisterUse...

912. By Alejandro J. Cura on 2012-03-14

remove extra line that was annoying the pep8 filter

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'ubuntu_sso/utils/webclient/tests/test_webclient.py'
2--- ubuntu_sso/utils/webclient/tests/test_webclient.py 2012-03-09 12:04:31 +0000
3+++ ubuntu_sso/utils/webclient/tests/test_webclient.py 2012-03-14 19:24:18 +0000
4@@ -38,7 +38,7 @@
5 )
6 from ubuntu_sso.utils import webclient
7 from ubuntu_sso.utils.ui import SSL_DETAILS_TEMPLATE
8-from ubuntu_sso.utils.webclient import gsettings
9+from ubuntu_sso.utils.webclient import gsettings, txweb
10 from ubuntu_sso.utils.webclient.common import BaseWebClient, HeaderDict, oauth
11 from ubuntu_sso.utils.webclient.tests import BaseMockWebServer
12
13@@ -241,6 +241,7 @@
14 """Test for the webclient."""
15
16 timeout = 1
17+ webclient_factory = webclient.webclient_factory
18
19 @defer.inlineCallbacks
20 def setUp(self):
21@@ -248,7 +249,7 @@
22 self.ws = MockWebServer()
23 self.addCleanup(self.ws.stop)
24 self.base_iri = self.ws.get_iri()
25- self.wc = webclient.webclient_factory()
26+ self.wc = self.webclient_factory()
27 self.addCleanup(self.wc.shutdown)
28
29 @defer.inlineCallbacks
30@@ -359,6 +360,57 @@
31 "The type of %r must be bytes" % result.content)
32
33
34+class FakeSavingReactor(object):
35+ """A fake reactor that saves connection attempts."""
36+
37+ def __init__(self):
38+ """Initialize this fake instance."""
39+ self.connections = []
40+
41+ def connectTCP(self, host, port, factory, *args):
42+ """Fake the connection."""
43+ self.connections.append((host, port, args))
44+ factory.response_headers = {}
45+ factory.deferred = defer.succeed("response content")
46+
47+ def connectSSL(self, host, port, factory, *args):
48+ """Fake the connection."""
49+ self.connections.append((host, port, args))
50+ factory.response_headers = {}
51+ factory.deferred = defer.succeed("response content")
52+
53+
54+class TxWebClientTestCase(WebClientTestCase):
55+ """Test case for txweb."""
56+
57+ webclient_factory = txweb.WebClient
58+
59+
60+class TxWebClientReactorReplaceableTestCase(TestCase):
61+ """In the txweb client the reactor is replaceable."""
62+
63+ timeout = 3
64+ FAKE_HOST = u"fake"
65+ FAKE_IRI_TEMPLATE = u"%%s://%s/fake_page" % FAKE_HOST
66+
67+ @defer.inlineCallbacks
68+ def _test_replaceable_reactor(self, iri):
69+ """The reactor can be replaced with the tunnel client."""
70+ fake_reactor = FakeSavingReactor()
71+ wc = txweb.WebClient(fake_reactor)
72+ _response = yield wc.request(iri)
73+ host, _port, _args = fake_reactor.connections[0]
74+ self.assertEqual(host, self.FAKE_HOST)
75+
76+ def test_replaceable_reactor_http(self):
77+ """Test the replaceable reactor with an http iri."""
78+ return self._test_replaceable_reactor(self.FAKE_IRI_TEMPLATE % "http")
79+
80+ def test_replaceable_reactor_https(self):
81+ """Test the replaceable reactor with an https iri."""
82+ return self._test_replaceable_reactor(self.FAKE_IRI_TEMPLATE % "https")
83+
84+
85 class TimestampCheckerTestCase(TestCase):
86 """Tests for the timestampchecker classmethod."""
87
88
89=== modified file 'ubuntu_sso/utils/webclient/txweb.py'
90--- ubuntu_sso/utils/webclient/txweb.py 2012-02-07 19:36:50 +0000
91+++ ubuntu_sso/utils/webclient/txweb.py 2012-03-14 19:24:18 +0000
92@@ -16,11 +16,9 @@
93 """A webclient backend that uses twisted.web.client."""
94
95 import base64
96-
97-from StringIO import StringIO
98-
99-from twisted.internet import defer, protocol
100-from zope.interface import implements
101+import urlparse
102+
103+from twisted.internet import defer
104
105 from ubuntu_sso.utils.webclient.common import (
106 BaseWebClient,
107@@ -31,64 +29,80 @@
108 )
109
110
111-class StringProtocol(protocol.Protocol):
112- """Hold the stuff received in a StringIO."""
113-
114- # pylint: disable=C0103
115- def __init__(self):
116- """Initialize this instance."""
117- self.deferred = defer.Deferred()
118- self.content = StringIO()
119-
120- def dataReceived(self, data):
121- """Some more blocks received."""
122- self.content.write(data)
123-
124- def connectionLost(self, reason=protocol.connectionDone):
125- """No more bytes available."""
126- self.deferred.callback(self.content.getvalue())
127-
128-
129-class StringProducer(object):
130- """Simple implementation of IBodyProducer."""
131-
132- # delay import, otherwise a default reactor gets installed
133- from twisted.web import iweb
134-
135- implements(iweb.IBodyProducer)
136-
137- def __init__(self, body):
138- """Initialize this instance with some bytes."""
139- self.body = body
140- self.length = len(body)
141-
142- # pylint: disable=C0103
143- def startProducing(self, consumer):
144- """Start producing to the given IConsumer provider."""
145- consumer.write(self.body)
146- return defer.succeed(None)
147-
148- def pauseProducing(self):
149- """In our case, do nothing."""
150-
151- def stopProducing(self):
152- """In our case, do nothing."""
153+class RawResponse(object):
154+ """A raw response from the webcall."""
155+
156+ def __init__(self, headers, content, code=200, phrase="OK"):
157+ """Initialize this response."""
158+ self.headers = headers
159+ self.content = content
160+ self.code = code
161+ self.phrase = phrase
162
163
164 class WebClient(BaseWebClient):
165 """A simple web client that does not support proxies, yet."""
166
167- # delay import, otherwise a default reactor gets installed
168- from twisted.internet import reactor
169- from twisted.web import client, http, http_headers
170-
171- # Undefined variable 'http_headers', 'client', 'reactor', 'http'
172- # pylint: disable=E0602
173+ def __init__(self, connector=None, context_factory=None, **kwargs):
174+ """Initialize this webclient."""
175+ super(WebClient, self).__init__(**kwargs)
176+
177+ if connector is None:
178+ from twisted.internet import reactor
179+ self.connector = reactor
180+ else:
181+ self.connector = connector
182+
183+ if context_factory is None:
184+ from twisted.internet import ssl
185+ self.context_factory = ssl.ClientContextFactory()
186+ else:
187+ self.context_factory = context_factory
188+
189+ @defer.inlineCallbacks
190+ def raw_request(self, method, uri, headers, postdata):
191+ """Make a raw http request."""
192+ # delay import, otherwise a default reactor gets installed
193+ from twisted.web import client, error
194+
195+ parsed_url = urlparse.urlparse(uri)
196+
197+ # pylint: disable=E1101
198+ https = parsed_url.scheme == "https"
199+ host = parsed_url.netloc.split(":")[0]
200+ # pylint: enable=E1101
201+ if parsed_url.port is None:
202+ port = 443 if https else 80
203+ else:
204+ port = parsed_url.port
205+
206+ factory = client.HTTPClientFactory(uri, method=method,
207+ postdata=postdata,
208+ headers=headers,
209+ followRedirect=False)
210+ # pylint: disable=E1103
211+ if https:
212+ self.connector.connectSSL(host, port, factory,
213+ self.context_factory)
214+ else:
215+ self.connector.connectTCP(host, port, factory)
216+ # pylint: enable=E1103
217+
218+ try:
219+ content = yield factory.deferred
220+ response = RawResponse(factory.response_headers, content)
221+ except error.Error as e:
222+ response = RawResponse(factory.response_headers, e.response,
223+ int(e.status), e.message)
224+ defer.returnValue(response)
225
226 @defer.inlineCallbacks
227 def request(self, iri, method="GET", extra_headers=None,
228 oauth_credentials=None, post_content=None):
229 """Get the page, or fail trying."""
230+ # delay import, otherwise a default reactor gets installed
231+ from twisted.web import http
232+
233 uri = self.iri_to_uri(iri)
234
235 if extra_headers:
236@@ -107,40 +121,25 @@
237 headers["Authorization"] = "Basic " + auth
238
239 try:
240- request_headers = http_headers.Headers()
241- for key, value in headers.items():
242- request_headers.addRawHeader(key, value)
243- agent = client.Agent(reactor)
244- if post_content:
245- body_producer = StringProducer(post_content)
246- else:
247- body_producer = None
248- agent_response = yield agent.request(method, uri,
249- headers=request_headers,
250- bodyProducer=body_producer)
251- raw_headers = agent_response.headers.getAllRawHeaders()
252- response_headers = HeaderDict(raw_headers)
253+ raw_response = yield self.raw_request(method, uri,
254+ headers=headers,
255+ postdata=post_content)
256+ response_headers = HeaderDict(raw_response.headers)
257 if method.lower() != "head":
258- response_content = yield self.get_agent_content(agent_response)
259+ response_content = raw_response.content
260 else:
261 response_content = ""
262- if agent_response.code == http.OK:
263+ if raw_response.code == http.OK:
264 defer.returnValue(Response(response_content, response_headers))
265- if agent_response.code == http.UNAUTHORIZED:
266- raise UnauthorizedError(agent_response.phrase,
267+ if raw_response.code == http.UNAUTHORIZED:
268+ raise UnauthorizedError(raw_response.phrase,
269 response_content)
270- raise WebClientError(agent_response.phrase, response_content)
271+ raise WebClientError(raw_response.phrase, response_content)
272 except WebClientError:
273 raise
274 except Exception as e:
275 raise WebClientError(e.message, e)
276
277- def get_agent_content(self, agent_response):
278- """Get the contents of an agent response."""
279- string_protocol = StringProtocol()
280- agent_response.deliverBody(string_protocol)
281- return string_protocol.deferred
282-
283 def force_use_proxy(self, settings):
284 """Setup this webclient to use the given proxy settings."""
285- # No proxy support in twisted.web.client
286+ # No direct proxy support in twisted.web.client

Subscribers

People subscribed via source and target branches