Merge lp:~alecu/ubuntuone-client/proxy-tunnel-useit into lp:ubuntuone-client
- proxy-tunnel-useit
- Merge into trunk
Status: | Merged | ||||||||
---|---|---|---|---|---|---|---|---|---|
Approved by: | Alejandro J. Cura | ||||||||
Approved revision: | 1227 | ||||||||
Merged at revision: | 1206 | ||||||||
Proposed branch: | lp:~alecu/ubuntuone-client/proxy-tunnel-useit | ||||||||
Merge into: | lp:ubuntuone-client | ||||||||
Prerequisite: | lp:~alecu/ubuntuone-client/proxy-tunnel-process | ||||||||
Diff against target: |
570 lines (+338/-30) 10 files modified
contrib/testing/testcase.py (+15/-1) tests/proxy/test_tunnel_client.py (+1/-1) tests/proxy/test_tunnel_server.py (+7/-5) tests/syncdaemon/test_action_queue.py (+51/-0) tests/syncdaemon/test_tunnel_runner.py (+114/-0) ubuntuone/proxy/common.py (+1/-0) ubuntuone/proxy/tunnel_client.py (+71/-18) ubuntuone/proxy/tunnel_server.py (+2/-3) ubuntuone/syncdaemon/action_queue.py (+6/-2) ubuntuone/syncdaemon/tunnel_runner.py (+70/-0) |
||||||||
To merge this branch: | bzr merge lp:~alecu/ubuntuone-client/proxy-tunnel-useit | ||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Natalia Bidart (community) | Approve | ||
Alejandro J. Cura (community) | Abstain | ||
Manuel de la Peña (community) | Approve | ||
Review via email: mp+96213@code.launchpad.net |
Commit message
- Tunnel storage protocol if proxy enabled in system settings (LP: #929208).
Description of the change
- Tunnel storage protocol if proxy enabled in system settings. (LP: #929208)
- 1218. By Alejandro J. Cura
-
Merged proxy-tunnel-
process into proxy-tunnel-useit. - 1219. By Alejandro J. Cura
-
move the patching of TunnelRunner to the parent class
- 1220. By Alejandro J. Cura
-
Merged proxy-tunnel-
process into proxy-tunnel-useit.
- 1221. By Alejandro J. Cura
-
merged with parent branch
- 1222. By Alejandro J. Cura
-
Merged proxy-tunnel-
process into proxy-tunnel-useit. - 1223. By Alejandro J. Cura
-
Merged proxy-tunnel-
process into proxy-tunnel-useit. - 1224. By Alejandro J. Cura
-
fixed lint issue
Manuel de la Peña (mandel) wrote : | # |
I see nothing wrong with the code!
Natalia Bidart (nataliabidart) wrote : | # |
* Could you please change this assert:
to something that reflects that self.spawned is not a boolean but a list? Perhaps using assertEqual against what should actually be in the list.
* This is in live code, TUNNEL_PORT = "Tunnel port", is it correct? does no look like a port to me :-/
* This import is not alphabetically ordered: from ubuntuone.proxy import tunnel_client. Also, if I understand correctly, that import can fail with an ImportError if the user does not have the -proxy-support debian/ubuntu package installed, since the whole ubuntuone.proxy namespace will be distributed in another binary package (as far as I know).
Alejandro J. Cura (alecu) wrote : | # |
> * Could you please change this assert:
>
> self.assertTrue
>
> to something that reflects that self.spawned is not a boolean but a list?
> Perhaps using assertEqual against what should actually be in the list.
Good point, I'll fix it.
> * This is in live code, TUNNEL_PORT = "Tunnel port", is it correct? does no
> look like a port to me :-/
The string is correct.
But I agree that the name of the constant is misleading, so I'm renaming it to something better.
> * This import is not alphabetically ordered: from ubuntuone.proxy import
> tunnel_client. Also, if I understand correctly, that import can fail with an
> ImportError if the user does not have the -proxy-support debian/ubuntu package
> installed, since the whole ubuntuone.proxy namespace will be distributed in
> another binary package (as far as I know).
The tunnel_client.py module should not be part of the different binary package, only tunnel_server.py and bin/ubuntuone-
- 1225. By Alejandro J. Cura
-
fixes requested on review
- 1226. By Alejandro J. Cura
-
The ubuntuone.proxy namespace can be provided by a different ubuntu package.
Alejandro J. Cura (alecu) wrote : | # |
I've reworked the branch with the suggestions provided in the review.
It's now pushed and ready for re-review.
Alejandro J. Cura (alecu) : | # |
Natalia Bidart (nataliabidart) wrote : | # |
Second connection attempt is failing with:
Unhandled error in Deferred:
Unhandled Error
Traceback (most recent call last):
File "/usr/lib/
self.
File "/usr/lib/
self.
File "/usr/lib/
current.result = callback(
File "/usr/lib/
return _inlineCallback
--- <exception caught here> ---
File "/usr/lib/
result = g.send(result)
File "/home/
self.connector = client.
exceptions.
Alecu and I already debugged it, so he will be working on a fix.
- 1227. By Alejandro J. Cura
-
get_client() now returns independent deferreds that can be yielded at will
Natalia Bidart (nataliabidart) wrote : | # |
Looks great!
Preview Diff
1 | === modified file 'bin/ubuntuone-proxy-tunnel' (properties changed: -x to +x) |
2 | === modified file 'contrib/testing/testcase.py' |
3 | --- contrib/testing/testcase.py 2012-02-09 21:57:00 +0000 |
4 | +++ contrib/testing/testcase.py 2012-03-12 17:46:23 +0000 |
5 | @@ -28,7 +28,7 @@ |
6 | from collections import defaultdict |
7 | from functools import wraps |
8 | |
9 | -from twisted.internet import defer |
10 | +from twisted.internet import defer, reactor |
11 | from twisted.trial.unittest import TestCase as TwistedTestCase |
12 | from ubuntuone.devtools.testcases import skipIfOS |
13 | from zope.interface import implements |
14 | @@ -267,6 +267,17 @@ |
15 | return defer.succeed(True) |
16 | |
17 | |
18 | +class FakeTunnelRunner(object): |
19 | + """A fake proxy.tunnel_client.TunnelRunner.""" |
20 | + |
21 | + def __init__(self, *args): |
22 | + """Fake a proxy tunnel.""" |
23 | + |
24 | + def get_client(self): |
25 | + """Always return the reactor.""" |
26 | + return defer.succeed(reactor) |
27 | + |
28 | + |
29 | class BaseTwistedTestCase(TwistedTestCase): |
30 | """Base TestCase with helper methods to handle temp dir. |
31 | |
32 | @@ -276,6 +287,7 @@ |
33 | makedirs(path): support read-only shares |
34 | """ |
35 | MAX_FILENAME = 32 # some platforms limit lengths of filenames |
36 | + tunnel_runner_class = FakeTunnelRunner |
37 | |
38 | def mktemp(self, name='temp'): |
39 | """ Customized mktemp that accepts an optional name argument. """ |
40 | @@ -380,6 +392,8 @@ |
41 | # Patch the user home |
42 | self.home_dir = self.mktemp('ubuntuonehacker') |
43 | self.patch(platform, "xdg_home", self.home_dir) |
44 | + self.patch(action_queue.tunnel_runner, "TunnelRunner", |
45 | + self.tunnel_runner_class) |
46 | |
47 | |
48 | class FakeMainTestCase(BaseTwistedTestCase): |
49 | |
50 | === modified file 'tests/proxy/test_tunnel_client.py' |
51 | --- tests/proxy/test_tunnel_client.py 2012-03-09 14:36:34 +0000 |
52 | +++ tests/proxy/test_tunnel_client.py 2012-03-12 17:46:23 +0000 |
53 | @@ -84,7 +84,7 @@ |
54 | |
55 | def test_sends_connect_request(self): |
56 | """Sends the expected CONNECT request.""" |
57 | - expected = tunnel_client.METHOD_LINE.format(self.host, self.port) |
58 | + expected = tunnel_client.METHOD_LINE % (self.host, self.port) |
59 | written = self.tunnel_client_proto.transport.getvalue() |
60 | first_line = written.split(CRLF)[0] |
61 | self.assertEqual(first_line + CRLF, expected) |
62 | |
63 | === modified file 'tests/proxy/test_tunnel_server.py' |
64 | --- tests/proxy/test_tunnel_server.py 2012-03-09 15:46:50 +0000 |
65 | +++ tests/proxy/test_tunnel_server.py 2012-03-12 17:46:23 +0000 |
66 | @@ -34,11 +34,11 @@ |
67 | |
68 | |
69 | FAKE_SESSION_TEMPLATE = ( |
70 | - "CONNECT {0.netloc} HTTP/1.0" + CRLF + |
71 | + "CONNECT %s HTTP/1.0" + CRLF + |
72 | "Header1: value1" + CRLF + |
73 | "Header2: value2" + CRLF + |
74 | CRLF + |
75 | - "GET {0.path} HTTP/1.0" + CRLF + CRLF |
76 | + "GET %s HTTP/1.0" + CRLF + CRLF |
77 | ) |
78 | |
79 | FAKE_SETTINGS = { |
80 | @@ -140,7 +140,8 @@ |
81 | def test_complete_connection(self): |
82 | """Test from the tunnel server down.""" |
83 | url = urlparse(self.dest_url) |
84 | - client = FakeClientFactory(FAKE_SESSION_TEMPLATE.format(url)) |
85 | + fake_session = FAKE_SESSION_TEMPLATE % (url.netloc, url.path) |
86 | + client = FakeClientFactory(fake_session) |
87 | reactor.connectTCP("0.0.0.0", self.tunnel_server.port, client) |
88 | response = yield client.response |
89 | self.assertIn(SAMPLE_CONTENT, response) |
90 | @@ -230,7 +231,7 @@ |
91 | def test_successful_connect(self): |
92 | """A successful connect thru the tunnel.""" |
93 | url = urlparse(self.dest_url) |
94 | - data = FAKE_SESSION_TEMPLATE.format(url) |
95 | + data = FAKE_SESSION_TEMPLATE % (url.netloc, url.path) |
96 | self.proto.dataReceived(data) |
97 | lines = self.transport.getvalue().split(CRLF) |
98 | self.assertEqual(lines[-1], SAMPLE_CONTENT) |
99 | @@ -474,7 +475,8 @@ |
100 | """With proxies enabled print port to stdout and start the mainloop.""" |
101 | self.proxies_enabled = True |
102 | tunnel_server.main(["example.com", "443"]) |
103 | - self.assertIn("Tunnel port:", self.fake_stdout.getvalue()) |
104 | + self.assertIn(tunnel_server.TUNNEL_PORT_LABEL + ":", |
105 | + self.fake_stdout.getvalue()) |
106 | |
107 | def test_on_proxies_disabled_exit(self): |
108 | """With proxies disabled, print a message and exit gracefully.""" |
109 | |
110 | === modified file 'tests/syncdaemon/test_action_queue.py' |
111 | --- tests/syncdaemon/test_action_queue.py 2012-02-18 16:13:04 +0000 |
112 | +++ tests/syncdaemon/test_action_queue.py 2012-03-12 17:46:23 +0000 |
113 | @@ -186,6 +186,35 @@ |
114 | return FakeRequest() |
115 | |
116 | |
117 | +class FakeTunnelClient(object): |
118 | + """A fake proxy.tunnel_client.""" |
119 | + |
120 | + def __init__(self): |
121 | + """Fake this proxy tunnel.""" |
122 | + self.tcp_connected = False |
123 | + self.ssl_connected = False |
124 | + |
125 | + def connectTCP(self, *args, **kwargs): |
126 | + """Save the connection thru TCP.""" |
127 | + self.tcp_connected = True |
128 | + |
129 | + def connectSSL(self, *args, **kwargs): |
130 | + """Save the connection thru SSL.""" |
131 | + self.ssl_connected = True |
132 | + |
133 | + |
134 | +class SavingConnectionTunnelRunner(object): |
135 | + """A fake proxy.tunnel_client.TunnelRunner.""" |
136 | + |
137 | + def __init__(self, *args): |
138 | + """Fake a proxy tunnel.""" |
139 | + self.client = FakeTunnelClient() |
140 | + |
141 | + def get_client(self): |
142 | + """Always return the reactor.""" |
143 | + return defer.succeed(self.client) |
144 | + |
145 | + |
146 | class TestingProtocol(ActionQueue.protocol): |
147 | """Protocol for testing.""" |
148 | |
149 | @@ -1295,6 +1324,28 @@ |
150 | "host 1.2.3.4", "port 4321")) |
151 | |
152 | |
153 | +class TunnelRunnerTestCase(FactoryBaseTestCase): |
154 | + """Tests for the tunnel runner.""" |
155 | + |
156 | + tunnel_runner_class = SavingConnectionTunnelRunner |
157 | + |
158 | + @defer.inlineCallbacks |
159 | + def test_make_connection_uses_tunnelrunner_non_ssl(self): |
160 | + """Check that _make_connection uses TunnelRunner.""" |
161 | + self.action_queue.use_ssl = False |
162 | + yield self.action_queue._make_connection(("127.0.0.1", 1234)) |
163 | + self.assertTrue(self.action_queue.tunnel_runner.client.tcp_connected, |
164 | + "connectTCP is called on the client.") |
165 | + |
166 | + @defer.inlineCallbacks |
167 | + def test_make_connection_uses_tunnelrunner_ssl(self): |
168 | + """Check that _make_connection uses TunnelRunner.""" |
169 | + self.action_queue.use_ssl = True |
170 | + yield self.action_queue._make_connection(("127.0.0.1", 1234)) |
171 | + self.assertTrue(self.action_queue.tunnel_runner.client.ssl_connected, |
172 | + "connectSSL is called on the client.") |
173 | + |
174 | + |
175 | class ConnectedBaseTestCase(FactoryBaseTestCase): |
176 | """Base test case generating a connected factory.""" |
177 | |
178 | |
179 | === added file 'tests/syncdaemon/test_tunnel_runner.py' |
180 | --- tests/syncdaemon/test_tunnel_runner.py 1970-01-01 00:00:00 +0000 |
181 | +++ tests/syncdaemon/test_tunnel_runner.py 2012-03-12 17:46:23 +0000 |
182 | @@ -0,0 +1,114 @@ |
183 | +# -*- coding: utf8 -*- |
184 | +# |
185 | +# Copyright 2012 Canonical Ltd. |
186 | +# |
187 | +# This program is free software: you can redistribute it and/or modify it |
188 | +# under the terms of the GNU General Public License version 3, as published |
189 | +# by the Free Software Foundation. |
190 | +# |
191 | +# This program is distributed in the hope that it will be useful, but |
192 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
193 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
194 | +# PURPOSE. See the GNU General Public License for more details. |
195 | +# |
196 | +# You should have received a copy of the GNU General Public License along |
197 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
198 | +"""Tests for the proxy tunnel runner.""" |
199 | + |
200 | +from twisted.internet import defer, error, reactor, task |
201 | +from twisted.trial.unittest import TestCase |
202 | + |
203 | +from ubuntuone.proxy import tunnel_client |
204 | +from ubuntuone.syncdaemon import tunnel_runner |
205 | + |
206 | +FAKE_HOST = "fs-1.two.ubuntu.com" |
207 | +FAKE_PORT = 443 |
208 | + |
209 | + |
210 | +class TunnelRunnerConstructorTestCase(TestCase): |
211 | + """Test the tunnel runner constructor.""" |
212 | + |
213 | + timeout = 3 |
214 | + |
215 | + def raise_import_error(self, *args): |
216 | + """Raise an import error.""" |
217 | + raise ImportError |
218 | + |
219 | + @defer.inlineCallbacks |
220 | + def test_proxy_support_not_installed(self): |
221 | + """The proxy support binary package is not installed.""" |
222 | + self.patch(tunnel_runner.TunnelRunner, "start_process", |
223 | + self.raise_import_error) |
224 | + tr = tunnel_runner.TunnelRunner(FAKE_HOST, FAKE_PORT) |
225 | + client = yield tr.get_client() |
226 | + self.assertEqual(client, reactor) |
227 | + |
228 | + |
229 | +class TunnelRunnerTestCase(TestCase): |
230 | + """Tests for the TunnelRunner.""" |
231 | + |
232 | + timeout = 3 |
233 | + |
234 | + @defer.inlineCallbacks |
235 | + def setUp(self): |
236 | + """Initialize this testcase.""" |
237 | + yield super(TunnelRunnerTestCase, self).setUp() |
238 | + self.spawned = [] |
239 | + self.patch(tunnel_client.reactor, "spawnProcess", |
240 | + lambda *args, **kwargs: self.spawned.append((args, kwargs))) |
241 | + self.process_protocol = None |
242 | + self.process_protocol_class = tunnel_client.TunnelProcessProtocol |
243 | + self.patch(tunnel_client, "TunnelProcessProtocol", |
244 | + self.storing_process_protocol_factory) |
245 | + self.tr = tunnel_runner.TunnelRunner("fs-1.one.ubuntu.com", 443) |
246 | + |
247 | + def storing_process_protocol_factory(self, *args, **kwargs): |
248 | + """Store the process protocol just created.""" |
249 | + self.process_protocol = self.process_protocol_class(*args, **kwargs) |
250 | + return self.process_protocol |
251 | + |
252 | + def test_tunnel_process_is_started(self): |
253 | + """The tunnel process is started.""" |
254 | + self.assertEqual(len(self.spawned), 1, |
255 | + "The tunnel process is started.") |
256 | + |
257 | + @defer.inlineCallbacks |
258 | + def test_tunnel_process_get_client_yielded_twice(self): |
259 | + """The get_client method can be yielded twice.""" |
260 | + self.process_protocol.processExited(error.ProcessTerminated(1)) |
261 | + client = yield self.tr.get_client() |
262 | + client = yield self.tr.get_client() |
263 | + self.assertNotEqual(client, None) |
264 | + |
265 | + @defer.inlineCallbacks |
266 | + def test_tunnel_process_exits_with_error(self): |
267 | + """The tunnel process exits with an error.""" |
268 | + self.process_protocol.processExited(error.ProcessTerminated(1)) |
269 | + client = yield self.tr.get_client() |
270 | + self.assertEqual(client, reactor) |
271 | + |
272 | + @defer.inlineCallbacks |
273 | + def test_tunnel_process_exits_gracefully(self): |
274 | + """The tunnel process exits gracefully.""" |
275 | + self.process_protocol.processExited(error.ProcessDone(0)) |
276 | + client = yield self.tr.get_client() |
277 | + self.assertEqual(client, reactor) |
278 | + |
279 | + @defer.inlineCallbacks |
280 | + def test_tunnel_process_prints_random_garbage_and_timeouts(self): |
281 | + """The tunnel process prints garbage and timeouts.""" |
282 | + clock = task.Clock() |
283 | + self.patch(tunnel_client, "reactor", clock) |
284 | + self.process_protocol.connectionMade() |
285 | + self.process_protocol.outReceived("Random garbage") |
286 | + clock.advance(self.process_protocol.timeout) |
287 | + client = yield self.tr.get_client() |
288 | + self.assertEqual(client, clock) |
289 | + |
290 | + @defer.inlineCallbacks |
291 | + def test_tunnel_process_prints_port_number(self): |
292 | + """The tunnel process prints the port number.""" |
293 | + received = "%s: %d\n" % (tunnel_client.TUNNEL_PORT_LABEL, FAKE_PORT) |
294 | + self.process_protocol.outReceived(received) |
295 | + client = yield self.tr.get_client() |
296 | + self.assertEqual(client.tunnel_port, FAKE_PORT) |
297 | |
298 | === modified file 'ubuntuone/proxy/common.py' |
299 | --- ubuntuone/proxy/common.py 2012-03-07 23:43:07 +0000 |
300 | +++ ubuntuone/proxy/common.py 2012-03-12 17:46:23 +0000 |
301 | @@ -18,6 +18,7 @@ |
302 | from twisted.protocols import basic |
303 | |
304 | CRLF = "\r\n" |
305 | +TUNNEL_PORT_LABEL = "Tunnel port" |
306 | |
307 | |
308 | class BaseTunnelProtocol(basic.LineReceiver): |
309 | |
310 | === modified file 'ubuntuone/proxy/tunnel_client.py' |
311 | --- ubuntuone/proxy/tunnel_client.py 2012-03-09 14:36:34 +0000 |
312 | +++ ubuntuone/proxy/tunnel_client.py 2012-03-12 17:46:23 +0000 |
313 | @@ -15,11 +15,15 @@ |
314 | # with this program. If not, see <http://www.gnu.org/licenses/>. |
315 | """Client for the tunnel protocol.""" |
316 | |
317 | -from twisted.internet import protocol |
318 | - |
319 | -from ubuntuone.proxy.common import BaseTunnelProtocol, CRLF |
320 | - |
321 | -METHOD_LINE = "CONNECT {0}:{1} HTTP/1.0" + CRLF |
322 | +import logging |
323 | + |
324 | +from twisted.internet import protocol, reactor |
325 | + |
326 | +from ubuntuone.proxy.common import BaseTunnelProtocol, CRLF, TUNNEL_PORT_LABEL |
327 | + |
328 | +METHOD_LINE = "CONNECT %s:%d HTTP/1.0" + CRLF |
329 | +LOCALHOST = "127.0.0.1" |
330 | +logger = logging.getLogger("ubuntuone.Proxy.TunnelClient") |
331 | |
332 | |
333 | class TunnelClientProtocol(BaseTunnelProtocol): |
334 | @@ -27,8 +31,8 @@ |
335 | |
336 | def connectionMade(self): |
337 | """The connection to the tunnel was made so send request.""" |
338 | - method_line = METHOD_LINE.format(self.factory.tunnel_host, |
339 | - self.factory.tunnel_port) |
340 | + method_line = METHOD_LINE % (self.factory.tunnel_host, |
341 | + self.factory.tunnel_port) |
342 | headers = { |
343 | "User-Agent": "Ubuntu One tunnel client" |
344 | } |
345 | @@ -82,20 +86,69 @@ |
346 | """Initialize this client.""" |
347 | self.tunnel_host = tunnel_host |
348 | self.tunnel_port = tunnel_port |
349 | - # TODO: use tcp activation to start a tunnel process if not running |
350 | |
351 | - def connectTCP(self, host, port, other_factory, *args, **kwargs): |
352 | + def connectTCP(self, host, port, factory, *args, **kwargs): |
353 | """A connectTCP going thru the tunnel.""" |
354 | - factory = TunnelClientFactory(host, port, other_factory) |
355 | - from twisted.internet import reactor |
356 | + logger.info("Connecting (TCP) to %r:%r via tunnel at %r:%r", |
357 | + host, port, self.tunnel_host, self.tunnel_port) |
358 | + tunnel_factory = TunnelClientFactory(host, port, factory) |
359 | return reactor.connectTCP(self.tunnel_host, self.tunnel_port, |
360 | - factory, *args, **kwargs) |
361 | + tunnel_factory, *args, **kwargs) |
362 | |
363 | - def connectSSL(self, host, port, other_factory, |
364 | - context_factory, *args, **kwargs): |
365 | + def connectSSL(self, host, port, factory, |
366 | + contextFactory, *args, **kwargs): |
367 | """A connectSSL going thru the tunnel.""" |
368 | - factory = TunnelClientFactory(host, port, other_factory, |
369 | - context_factory) |
370 | - from twisted.internet import reactor |
371 | + logger.info("Connecting (SSL) to %r:%r via tunnel at %r:%r", |
372 | + host, port, self.tunnel_host, self.tunnel_port) |
373 | + tunnel_factory = TunnelClientFactory(host, port, factory, |
374 | + contextFactory) |
375 | return reactor.connectTCP(self.tunnel_host, self.tunnel_port, |
376 | - factory, *args, **kwargs) |
377 | + tunnel_factory, *args, **kwargs) |
378 | + |
379 | + |
380 | +class TunnelProcessProtocol(protocol.ProcessProtocol): |
381 | + """The dialog thru stdout with the tunnel server.""" |
382 | + |
383 | + timeout = 5 |
384 | + |
385 | + def __init__(self, client_d): |
386 | + """Initialize this protocol.""" |
387 | + self.client_d = client_d |
388 | + self.timer = None |
389 | + |
390 | + def connectionMade(self): |
391 | + """The process has started, start a timer.""" |
392 | + logger.info("Tunnel process started.") |
393 | + self.timer = reactor.callLater(self.timeout, self.process_timeouted) |
394 | + |
395 | + def process_timeouted(self): |
396 | + """The process took too long to reply.""" |
397 | + if not self.client_d.called: |
398 | + logger.info("Timeout while waiting for tunnel process.") |
399 | + self.client_d.callback(reactor) |
400 | + |
401 | + def finish_timeout(self): |
402 | + """Stop the timer from firing.""" |
403 | + if self.timer and self.timer.active(): |
404 | + self.timer.cancel() |
405 | + |
406 | + def processExited(self, status): |
407 | + """The tunnel process has exited with some error code.""" |
408 | + self.finish_timeout() |
409 | + logger.info("Tunnel process exit status %r.", status) |
410 | + if not self.client_d.called: |
411 | + self.client_d.callback(reactor) |
412 | + |
413 | + def outReceived(self, data): |
414 | + """Receive the port number.""" |
415 | + if self.client_d.called: |
416 | + return |
417 | + |
418 | + for line in data.split("\n"): |
419 | + if line.startswith(TUNNEL_PORT_LABEL): |
420 | + _header, port = line.split(":", 1) |
421 | + port = int(port.strip()) |
422 | + logger.info("Tunnel process listening on port %r.", port) |
423 | + client = TunnelClient(LOCALHOST, port) |
424 | + self.client_d.callback(client) |
425 | + break |
426 | |
427 | === modified file 'ubuntuone/proxy/tunnel_server.py' |
428 | --- ubuntuone/proxy/tunnel_server.py 2012-03-09 15:35:27 +0000 |
429 | +++ ubuntuone/proxy/tunnel_server.py 2012-03-12 17:46:23 +0000 |
430 | @@ -45,7 +45,7 @@ |
431 | from zope.interface import implements |
432 | |
433 | from ubuntu_sso.utils.webclient import gsettings |
434 | -from ubuntuone.proxy.common import BaseTunnelProtocol, CRLF |
435 | +from ubuntuone.proxy.common import BaseTunnelProtocol, CRLF, TUNNEL_PORT_LABEL |
436 | from ubuntuone.proxy.logger import logger |
437 | |
438 | DEFAULT_CODE = 500 |
439 | @@ -194,7 +194,6 @@ |
440 | |
441 | def response_data_received(self, data): |
442 | """Return data coming from the other side.""" |
443 | - logger.debug("writing data to the transport: %r", data) |
444 | self.write_transport(data) |
445 | |
446 | |
447 | @@ -279,6 +278,6 @@ |
448 | else: |
449 | app = QCoreApplication(argv) |
450 | tunnel_server = TunnelServer() |
451 | - sys.stdout.write("Tunnel port: {}\n".format(tunnel_server.port)) |
452 | + sys.stdout.write("%s: %d\n" % (TUNNEL_PORT_LABEL, tunnel_server.port)) |
453 | sys.stdout.flush() |
454 | app.exec_() |
455 | |
456 | === modified file 'ubuntuone/syncdaemon/action_queue.py' |
457 | --- ubuntuone/syncdaemon/action_queue.py 2012-02-09 21:57:00 +0000 |
458 | +++ ubuntuone/syncdaemon/action_queue.py 2012-03-12 17:46:23 +0000 |
459 | @@ -57,6 +57,7 @@ |
460 | from ubuntuone.syncdaemon.logger import mklog, TRACE |
461 | from ubuntuone.syncdaemon.volume_manager import ACCESS_LEVEL_RW |
462 | from ubuntuone.syncdaemon import config, offload_queue |
463 | +from ubuntuone.syncdaemon import tunnel_runner |
464 | |
465 | logger = logging.getLogger("ubuntuone.SyncDaemon.ActionQueue") |
466 | |
467 | @@ -699,6 +700,7 @@ |
468 | self.zip_queue = ZipQueue() |
469 | self.conditions_locker = ConditionsLocker() |
470 | self.disk_queue = offload_queue.OffloadQueue() |
471 | + self.tunnel_runner = tunnel_runner.TunnelRunner(self.host, self.port) |
472 | |
473 | self.estimated_free_space = {} |
474 | event_queue.subscribe(self) |
475 | @@ -816,16 +818,18 @@ |
476 | else: |
477 | return defer.succeed((self.host, self.port)) |
478 | |
479 | + @defer.inlineCallbacks |
480 | def _make_connection(self, result): |
481 | """Do the real connect call.""" |
482 | host, port = result |
483 | ssl_context = get_ssl_context(self.disable_ssl_verify) |
484 | + client = yield self.tunnel_runner.get_client() |
485 | if self.use_ssl: |
486 | - self.connector = reactor.connectSSL(host, port, factory=self, |
487 | + self.connector = client.connectSSL(host, port, factory=self, |
488 | contextFactory=ssl_context, |
489 | timeout=self.connection_timeout) |
490 | else: |
491 | - self.connector = reactor.connectTCP(host, port, self, |
492 | + self.connector = client.connectTCP(host, port, self, |
493 | timeout=self.connection_timeout) |
494 | |
495 | def connect(self): |
496 | |
497 | === added file 'ubuntuone/syncdaemon/tunnel_runner.py' |
498 | --- ubuntuone/syncdaemon/tunnel_runner.py 1970-01-01 00:00:00 +0000 |
499 | +++ ubuntuone/syncdaemon/tunnel_runner.py 2012-03-12 17:46:23 +0000 |
500 | @@ -0,0 +1,70 @@ |
501 | +# -*- coding: utf8 -*- |
502 | +# |
503 | +# Copyright 2012 Canonical Ltd. |
504 | +# |
505 | +# This program is free software: you can redistribute it and/or modify it |
506 | +# under the terms of the GNU General Public License version 3, as published |
507 | +# by the Free Software Foundation. |
508 | +# |
509 | +# This program is distributed in the hope that it will be useful, but |
510 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
511 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
512 | +# PURPOSE. See the GNU General Public License for more details. |
513 | +# |
514 | +# You should have received a copy of the GNU General Public License along |
515 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
516 | +"""Run the tunnel process and start a client, with a reactor as a fallback.""" |
517 | + |
518 | +import logging |
519 | + |
520 | +from os import path |
521 | + |
522 | +from twisted.internet import defer, reactor |
523 | + |
524 | + |
525 | +TUNNEL_EXECUTABLE = "ubuntuone-proxy-tunnel" |
526 | +logger = logging.getLogger("ubuntuone.SyncDaemon.TunnelRunner") |
527 | + |
528 | + |
529 | +class TunnelRunner(object): |
530 | + """Run a tunnel process.""" |
531 | + |
532 | + def __init__(self, host, port): |
533 | + """Start this runner instance.""" |
534 | + self.client_d = defer.Deferred() |
535 | + try: |
536 | + self.start_process(host, port) |
537 | + except ImportError: |
538 | + logger.info("Proxy support not installed.") |
539 | + self.client_d.callback(reactor) |
540 | + |
541 | + def start_process(self, host, port): |
542 | + """Start the tunnel process.""" |
543 | + from ubuntuone.proxy.tunnel_client import TunnelProcessProtocol |
544 | + protocol = TunnelProcessProtocol(self.client_d) |
545 | + process_path = self.get_process_path() |
546 | + args = [TUNNEL_EXECUTABLE, host, str(port)] |
547 | + reactor.spawnProcess(protocol, process_path, env=None, args=args) |
548 | + |
549 | + def get_process_path(self): |
550 | + """Get the path to the tunnel process.""" |
551 | + filename = path.join(path.dirname(__file__), "..", "..", "bin", |
552 | + TUNNEL_EXECUTABLE) |
553 | + if path.isfile(filename): |
554 | + return filename |
555 | + |
556 | + from ubuntuone.clientdefs import LIBEXECDIR |
557 | + return path.join(LIBEXECDIR, TUNNEL_EXECUTABLE) |
558 | + |
559 | + def get_client(self): |
560 | + """A deferred with the reactor or a tunnel client.""" |
561 | + |
562 | + def client_selected(result, d): |
563 | + """The tunnel_client or the reactor were selected.""" |
564 | + d.callback(result) |
565 | + # make sure the result is available for next callback |
566 | + return result |
567 | + |
568 | + d = defer.Deferred() |
569 | + self.client_d.addCallback(client_selected, d) |
570 | + return d |
I get the following when I try to run the tests:
== Python Lint Notices ==
./contrib/ testing/ testcase. py:
278: undefined name 'reactor'