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

Proposed by Alejandro J. Cura
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
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)

To post a comment you must log in.
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.

Revision history for this message
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

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

Revision history for this message
Manuel de la Peña (mandel) wrote :

I see nothing wrong with the code!

review: Approve
Revision history for this message
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
Revision history for this message
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

fixes requested on review

1226. By Alejandro J. Cura

The ubuntuone.proxy namespace can be provided by a different ubuntu package.

Revision history for this message
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.

Revision history for this message
Alejandro J. Cura (alecu) :
review: Abstain
Revision history for this message
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

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

Revision history for this message
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