Merge lp:~alecu/ubuntuone-client/proxy-tunnel-useit into lp:ubuntuone-client

Proposed by Alejandro J. Cura on 2012-03-06
Status: Merged
Approved by: Alejandro J. Cura on 2012-03-12
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
Reviewer Review Type Date Requested Status
Natalia Bidart Approve on 2012-03-12
Alejandro J. Cura (community) Abstain on 2012-03-10
Manuel de la Peña (community) 2012-03-06 Approve on 2012-03-09
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)

To post a comment you must log in.
1218. By Alejandro J. Cura on 2012-03-06

Merged proxy-tunnel-process into proxy-tunnel-useit.

1219. By Alejandro J. Cura on 2012-03-06

move the patching of TunnelRunner to the parent class

1220. By Alejandro J. Cura on 2012-03-07

Merged proxy-tunnel-process into proxy-tunnel-useit.

Manuel de la Peña (mandel) wrote :

I get the following when I try to run the tests:

== Python Lint Notices ==

./contrib/testing/testcase.py:
    278: undefined name 'reactor'

review: Needs Fixing
1221. By Alejandro J. Cura on 2012-03-09

merged with parent branch

1222. By Alejandro J. Cura on 2012-03-09

Merged proxy-tunnel-process into proxy-tunnel-useit.

1223. By Alejandro J. Cura on 2012-03-09

Merged proxy-tunnel-process into proxy-tunnel-useit.

1224. By Alejandro J. Cura on 2012-03-09

fixed lint issue

Manuel de la Peña (mandel) wrote :

I see nothing wrong with the code!

review: Approve
Natalia Bidart (nataliabidart) wrote :

* Could you please change this assert:

        self.assertTrue(self.spawned, "The tunnel process is started.")

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).

review: Needs Fixing
Alejandro J. Cura (alecu) wrote :

> * Could you please change this assert:
>
> self.assertTrue(self.spawned, "The tunnel process is started.")
>
> 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-proxy-tunnel depend on qtnet and should be in a different package. Also tunnel_client has code to deal with the case where bin/ubuntuone-proxy-tunnel is not installed or cannot be found, and in that case SD starts just like if no proxies were configured.

1225. By Alejandro J. Cura on 2012-03-09

fixes requested on review

1226. By Alejandro J. Cura on 2012-03-10

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) :
review: Abstain
Natalia Bidart (nataliabidart) wrote :

Second connection attempt is failing with:

Unhandled error in Deferred:
Unhandled Error
Traceback (most recent call last):
  File "/usr/lib/python2.7/dist-packages/twisted/internet/defer.py", line 362, in callback
    self._startRunCallbacks(result)
  File "/usr/lib/python2.7/dist-packages/twisted/internet/defer.py", line 458, in _startRunCallbacks
    self._runCallbacks()
  File "/usr/lib/python2.7/dist-packages/twisted/internet/defer.py", line 545, in _runCallbacks
    current.result = callback(current.result, *args, **kw)
  File "/usr/lib/python2.7/dist-packages/twisted/internet/defer.py", line 1181, in unwindGenerator
    return _inlineCallbacks(None, gen, Deferred())
--- <exception caught here> ---
  File "/usr/lib/python2.7/dist-packages/twisted/internet/defer.py", line 1039, in _inlineCallbacks
    result = g.send(result)
  File "/home/nessita/canonical/client/review_proxy-tunnel-useit/ubuntuone/syncdaemon/action_queue.py", line 832, in _make_connection
    self.connector = client.connectSSL(host, port, factory=self,
exceptions.AttributeError: 'NoneType' object has no attribute 'connectSSL'

Alecu and I already debugged it, so he will be working on a fix.

review: Needs Fixing
1227. By Alejandro J. Cura on 2012-03-12

get_client() now returns independent deferreds that can be yielded at will

Natalia Bidart (nataliabidart) wrote :

Looks great!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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

Subscribers

People subscribed via source and target branches