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
=== modified file 'bin/ubuntuone-proxy-tunnel' (properties changed: -x to +x)
=== modified file 'contrib/testing/testcase.py'
--- contrib/testing/testcase.py 2012-02-09 21:57:00 +0000
+++ contrib/testing/testcase.py 2012-03-12 17:46:23 +0000
@@ -28,7 +28,7 @@
28from collections import defaultdict28from collections import defaultdict
29from functools import wraps29from functools import wraps
3030
31from twisted.internet import defer31from twisted.internet import defer, reactor
32from twisted.trial.unittest import TestCase as TwistedTestCase32from twisted.trial.unittest import TestCase as TwistedTestCase
33from ubuntuone.devtools.testcases import skipIfOS33from ubuntuone.devtools.testcases import skipIfOS
34from zope.interface import implements34from zope.interface import implements
@@ -267,6 +267,17 @@
267 return defer.succeed(True)267 return defer.succeed(True)
268268
269269
270class FakeTunnelRunner(object):
271 """A fake proxy.tunnel_client.TunnelRunner."""
272
273 def __init__(self, *args):
274 """Fake a proxy tunnel."""
275
276 def get_client(self):
277 """Always return the reactor."""
278 return defer.succeed(reactor)
279
280
270class BaseTwistedTestCase(TwistedTestCase):281class BaseTwistedTestCase(TwistedTestCase):
271 """Base TestCase with helper methods to handle temp dir.282 """Base TestCase with helper methods to handle temp dir.
272283
@@ -276,6 +287,7 @@
276 makedirs(path): support read-only shares287 makedirs(path): support read-only shares
277 """288 """
278 MAX_FILENAME = 32 # some platforms limit lengths of filenames289 MAX_FILENAME = 32 # some platforms limit lengths of filenames
290 tunnel_runner_class = FakeTunnelRunner
279291
280 def mktemp(self, name='temp'):292 def mktemp(self, name='temp'):
281 """ Customized mktemp that accepts an optional name argument. """293 """ Customized mktemp that accepts an optional name argument. """
@@ -380,6 +392,8 @@
380 # Patch the user home392 # Patch the user home
381 self.home_dir = self.mktemp('ubuntuonehacker')393 self.home_dir = self.mktemp('ubuntuonehacker')
382 self.patch(platform, "xdg_home", self.home_dir)394 self.patch(platform, "xdg_home", self.home_dir)
395 self.patch(action_queue.tunnel_runner, "TunnelRunner",
396 self.tunnel_runner_class)
383397
384398
385class FakeMainTestCase(BaseTwistedTestCase):399class FakeMainTestCase(BaseTwistedTestCase):
386400
=== modified file 'tests/proxy/test_tunnel_client.py'
--- tests/proxy/test_tunnel_client.py 2012-03-09 14:36:34 +0000
+++ tests/proxy/test_tunnel_client.py 2012-03-12 17:46:23 +0000
@@ -84,7 +84,7 @@
8484
85 def test_sends_connect_request(self):85 def test_sends_connect_request(self):
86 """Sends the expected CONNECT request."""86 """Sends the expected CONNECT request."""
87 expected = tunnel_client.METHOD_LINE.format(self.host, self.port)87 expected = tunnel_client.METHOD_LINE % (self.host, self.port)
88 written = self.tunnel_client_proto.transport.getvalue()88 written = self.tunnel_client_proto.transport.getvalue()
89 first_line = written.split(CRLF)[0]89 first_line = written.split(CRLF)[0]
90 self.assertEqual(first_line + CRLF, expected)90 self.assertEqual(first_line + CRLF, expected)
9191
=== modified file 'tests/proxy/test_tunnel_server.py'
--- tests/proxy/test_tunnel_server.py 2012-03-09 15:46:50 +0000
+++ tests/proxy/test_tunnel_server.py 2012-03-12 17:46:23 +0000
@@ -34,11 +34,11 @@
3434
3535
36FAKE_SESSION_TEMPLATE = (36FAKE_SESSION_TEMPLATE = (
37 "CONNECT {0.netloc} HTTP/1.0" + CRLF +37 "CONNECT %s HTTP/1.0" + CRLF +
38 "Header1: value1" + CRLF +38 "Header1: value1" + CRLF +
39 "Header2: value2" + CRLF +39 "Header2: value2" + CRLF +
40 CRLF +40 CRLF +
41 "GET {0.path} HTTP/1.0" + CRLF + CRLF41 "GET %s HTTP/1.0" + CRLF + CRLF
42)42)
4343
44FAKE_SETTINGS = {44FAKE_SETTINGS = {
@@ -140,7 +140,8 @@
140 def test_complete_connection(self):140 def test_complete_connection(self):
141 """Test from the tunnel server down."""141 """Test from the tunnel server down."""
142 url = urlparse(self.dest_url)142 url = urlparse(self.dest_url)
143 client = FakeClientFactory(FAKE_SESSION_TEMPLATE.format(url))143 fake_session = FAKE_SESSION_TEMPLATE % (url.netloc, url.path)
144 client = FakeClientFactory(fake_session)
144 reactor.connectTCP("0.0.0.0", self.tunnel_server.port, client)145 reactor.connectTCP("0.0.0.0", self.tunnel_server.port, client)
145 response = yield client.response146 response = yield client.response
146 self.assertIn(SAMPLE_CONTENT, response)147 self.assertIn(SAMPLE_CONTENT, response)
@@ -230,7 +231,7 @@
230 def test_successful_connect(self):231 def test_successful_connect(self):
231 """A successful connect thru the tunnel."""232 """A successful connect thru the tunnel."""
232 url = urlparse(self.dest_url)233 url = urlparse(self.dest_url)
233 data = FAKE_SESSION_TEMPLATE.format(url)234 data = FAKE_SESSION_TEMPLATE % (url.netloc, url.path)
234 self.proto.dataReceived(data)235 self.proto.dataReceived(data)
235 lines = self.transport.getvalue().split(CRLF)236 lines = self.transport.getvalue().split(CRLF)
236 self.assertEqual(lines[-1], SAMPLE_CONTENT)237 self.assertEqual(lines[-1], SAMPLE_CONTENT)
@@ -474,7 +475,8 @@
474 """With proxies enabled print port to stdout and start the mainloop."""475 """With proxies enabled print port to stdout and start the mainloop."""
475 self.proxies_enabled = True476 self.proxies_enabled = True
476 tunnel_server.main(["example.com", "443"])477 tunnel_server.main(["example.com", "443"])
477 self.assertIn("Tunnel port:", self.fake_stdout.getvalue())478 self.assertIn(tunnel_server.TUNNEL_PORT_LABEL + ":",
479 self.fake_stdout.getvalue())
478480
479 def test_on_proxies_disabled_exit(self):481 def test_on_proxies_disabled_exit(self):
480 """With proxies disabled, print a message and exit gracefully."""482 """With proxies disabled, print a message and exit gracefully."""
481483
=== modified file 'tests/syncdaemon/test_action_queue.py'
--- tests/syncdaemon/test_action_queue.py 2012-02-18 16:13:04 +0000
+++ tests/syncdaemon/test_action_queue.py 2012-03-12 17:46:23 +0000
@@ -186,6 +186,35 @@
186 return FakeRequest()186 return FakeRequest()
187187
188188
189class FakeTunnelClient(object):
190 """A fake proxy.tunnel_client."""
191
192 def __init__(self):
193 """Fake this proxy tunnel."""
194 self.tcp_connected = False
195 self.ssl_connected = False
196
197 def connectTCP(self, *args, **kwargs):
198 """Save the connection thru TCP."""
199 self.tcp_connected = True
200
201 def connectSSL(self, *args, **kwargs):
202 """Save the connection thru SSL."""
203 self.ssl_connected = True
204
205
206class SavingConnectionTunnelRunner(object):
207 """A fake proxy.tunnel_client.TunnelRunner."""
208
209 def __init__(self, *args):
210 """Fake a proxy tunnel."""
211 self.client = FakeTunnelClient()
212
213 def get_client(self):
214 """Always return the reactor."""
215 return defer.succeed(self.client)
216
217
189class TestingProtocol(ActionQueue.protocol):218class TestingProtocol(ActionQueue.protocol):
190 """Protocol for testing."""219 """Protocol for testing."""
191220
@@ -1295,6 +1324,28 @@
1295 "host 1.2.3.4", "port 4321"))1324 "host 1.2.3.4", "port 4321"))
12961325
12971326
1327class TunnelRunnerTestCase(FactoryBaseTestCase):
1328 """Tests for the tunnel runner."""
1329
1330 tunnel_runner_class = SavingConnectionTunnelRunner
1331
1332 @defer.inlineCallbacks
1333 def test_make_connection_uses_tunnelrunner_non_ssl(self):
1334 """Check that _make_connection uses TunnelRunner."""
1335 self.action_queue.use_ssl = False
1336 yield self.action_queue._make_connection(("127.0.0.1", 1234))
1337 self.assertTrue(self.action_queue.tunnel_runner.client.tcp_connected,
1338 "connectTCP is called on the client.")
1339
1340 @defer.inlineCallbacks
1341 def test_make_connection_uses_tunnelrunner_ssl(self):
1342 """Check that _make_connection uses TunnelRunner."""
1343 self.action_queue.use_ssl = True
1344 yield self.action_queue._make_connection(("127.0.0.1", 1234))
1345 self.assertTrue(self.action_queue.tunnel_runner.client.ssl_connected,
1346 "connectSSL is called on the client.")
1347
1348
1298class ConnectedBaseTestCase(FactoryBaseTestCase):1349class ConnectedBaseTestCase(FactoryBaseTestCase):
1299 """Base test case generating a connected factory."""1350 """Base test case generating a connected factory."""
13001351
13011352
=== added file 'tests/syncdaemon/test_tunnel_runner.py'
--- tests/syncdaemon/test_tunnel_runner.py 1970-01-01 00:00:00 +0000
+++ tests/syncdaemon/test_tunnel_runner.py 2012-03-12 17:46:23 +0000
@@ -0,0 +1,114 @@
1# -*- coding: utf8 -*-
2#
3# Copyright 2012 Canonical Ltd.
4#
5# This program is free software: you can redistribute it and/or modify it
6# under the terms of the GNU General Public License version 3, as published
7# by the Free Software Foundation.
8#
9# This program is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranties of
11# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
12# PURPOSE. See the GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License along
15# with this program. If not, see <http://www.gnu.org/licenses/>.
16"""Tests for the proxy tunnel runner."""
17
18from twisted.internet import defer, error, reactor, task
19from twisted.trial.unittest import TestCase
20
21from ubuntuone.proxy import tunnel_client
22from ubuntuone.syncdaemon import tunnel_runner
23
24FAKE_HOST = "fs-1.two.ubuntu.com"
25FAKE_PORT = 443
26
27
28class TunnelRunnerConstructorTestCase(TestCase):
29 """Test the tunnel runner constructor."""
30
31 timeout = 3
32
33 def raise_import_error(self, *args):
34 """Raise an import error."""
35 raise ImportError
36
37 @defer.inlineCallbacks
38 def test_proxy_support_not_installed(self):
39 """The proxy support binary package is not installed."""
40 self.patch(tunnel_runner.TunnelRunner, "start_process",
41 self.raise_import_error)
42 tr = tunnel_runner.TunnelRunner(FAKE_HOST, FAKE_PORT)
43 client = yield tr.get_client()
44 self.assertEqual(client, reactor)
45
46
47class TunnelRunnerTestCase(TestCase):
48 """Tests for the TunnelRunner."""
49
50 timeout = 3
51
52 @defer.inlineCallbacks
53 def setUp(self):
54 """Initialize this testcase."""
55 yield super(TunnelRunnerTestCase, self).setUp()
56 self.spawned = []
57 self.patch(tunnel_client.reactor, "spawnProcess",
58 lambda *args, **kwargs: self.spawned.append((args, kwargs)))
59 self.process_protocol = None
60 self.process_protocol_class = tunnel_client.TunnelProcessProtocol
61 self.patch(tunnel_client, "TunnelProcessProtocol",
62 self.storing_process_protocol_factory)
63 self.tr = tunnel_runner.TunnelRunner("fs-1.one.ubuntu.com", 443)
64
65 def storing_process_protocol_factory(self, *args, **kwargs):
66 """Store the process protocol just created."""
67 self.process_protocol = self.process_protocol_class(*args, **kwargs)
68 return self.process_protocol
69
70 def test_tunnel_process_is_started(self):
71 """The tunnel process is started."""
72 self.assertEqual(len(self.spawned), 1,
73 "The tunnel process is started.")
74
75 @defer.inlineCallbacks
76 def test_tunnel_process_get_client_yielded_twice(self):
77 """The get_client method can be yielded twice."""
78 self.process_protocol.processExited(error.ProcessTerminated(1))
79 client = yield self.tr.get_client()
80 client = yield self.tr.get_client()
81 self.assertNotEqual(client, None)
82
83 @defer.inlineCallbacks
84 def test_tunnel_process_exits_with_error(self):
85 """The tunnel process exits with an error."""
86 self.process_protocol.processExited(error.ProcessTerminated(1))
87 client = yield self.tr.get_client()
88 self.assertEqual(client, reactor)
89
90 @defer.inlineCallbacks
91 def test_tunnel_process_exits_gracefully(self):
92 """The tunnel process exits gracefully."""
93 self.process_protocol.processExited(error.ProcessDone(0))
94 client = yield self.tr.get_client()
95 self.assertEqual(client, reactor)
96
97 @defer.inlineCallbacks
98 def test_tunnel_process_prints_random_garbage_and_timeouts(self):
99 """The tunnel process prints garbage and timeouts."""
100 clock = task.Clock()
101 self.patch(tunnel_client, "reactor", clock)
102 self.process_protocol.connectionMade()
103 self.process_protocol.outReceived("Random garbage")
104 clock.advance(self.process_protocol.timeout)
105 client = yield self.tr.get_client()
106 self.assertEqual(client, clock)
107
108 @defer.inlineCallbacks
109 def test_tunnel_process_prints_port_number(self):
110 """The tunnel process prints the port number."""
111 received = "%s: %d\n" % (tunnel_client.TUNNEL_PORT_LABEL, FAKE_PORT)
112 self.process_protocol.outReceived(received)
113 client = yield self.tr.get_client()
114 self.assertEqual(client.tunnel_port, FAKE_PORT)
0115
=== modified file 'ubuntuone/proxy/common.py'
--- ubuntuone/proxy/common.py 2012-03-07 23:43:07 +0000
+++ ubuntuone/proxy/common.py 2012-03-12 17:46:23 +0000
@@ -18,6 +18,7 @@
18from twisted.protocols import basic18from twisted.protocols import basic
1919
20CRLF = "\r\n"20CRLF = "\r\n"
21TUNNEL_PORT_LABEL = "Tunnel port"
2122
2223
23class BaseTunnelProtocol(basic.LineReceiver):24class BaseTunnelProtocol(basic.LineReceiver):
2425
=== modified file 'ubuntuone/proxy/tunnel_client.py'
--- ubuntuone/proxy/tunnel_client.py 2012-03-09 14:36:34 +0000
+++ ubuntuone/proxy/tunnel_client.py 2012-03-12 17:46:23 +0000
@@ -15,11 +15,15 @@
15# with this program. If not, see <http://www.gnu.org/licenses/>.15# with this program. If not, see <http://www.gnu.org/licenses/>.
16"""Client for the tunnel protocol."""16"""Client for the tunnel protocol."""
1717
18from twisted.internet import protocol18import logging
1919
20from ubuntuone.proxy.common import BaseTunnelProtocol, CRLF20from twisted.internet import protocol, reactor
2121
22METHOD_LINE = "CONNECT {0}:{1} HTTP/1.0" + CRLF22from ubuntuone.proxy.common import BaseTunnelProtocol, CRLF, TUNNEL_PORT_LABEL
23
24METHOD_LINE = "CONNECT %s:%d HTTP/1.0" + CRLF
25LOCALHOST = "127.0.0.1"
26logger = logging.getLogger("ubuntuone.Proxy.TunnelClient")
2327
2428
25class TunnelClientProtocol(BaseTunnelProtocol):29class TunnelClientProtocol(BaseTunnelProtocol):
@@ -27,8 +31,8 @@
2731
28 def connectionMade(self):32 def connectionMade(self):
29 """The connection to the tunnel was made so send request."""33 """The connection to the tunnel was made so send request."""
30 method_line = METHOD_LINE.format(self.factory.tunnel_host,34 method_line = METHOD_LINE % (self.factory.tunnel_host,
31 self.factory.tunnel_port)35 self.factory.tunnel_port)
32 headers = {36 headers = {
33 "User-Agent": "Ubuntu One tunnel client"37 "User-Agent": "Ubuntu One tunnel client"
34 }38 }
@@ -82,20 +86,69 @@
82 """Initialize this client."""86 """Initialize this client."""
83 self.tunnel_host = tunnel_host87 self.tunnel_host = tunnel_host
84 self.tunnel_port = tunnel_port88 self.tunnel_port = tunnel_port
85 # TODO: use tcp activation to start a tunnel process if not running
8689
87 def connectTCP(self, host, port, other_factory, *args, **kwargs):90 def connectTCP(self, host, port, factory, *args, **kwargs):
88 """A connectTCP going thru the tunnel."""91 """A connectTCP going thru the tunnel."""
89 factory = TunnelClientFactory(host, port, other_factory)92 logger.info("Connecting (TCP) to %r:%r via tunnel at %r:%r",
90 from twisted.internet import reactor93 host, port, self.tunnel_host, self.tunnel_port)
94 tunnel_factory = TunnelClientFactory(host, port, factory)
91 return reactor.connectTCP(self.tunnel_host, self.tunnel_port,95 return reactor.connectTCP(self.tunnel_host, self.tunnel_port,
92 factory, *args, **kwargs)96 tunnel_factory, *args, **kwargs)
9397
94 def connectSSL(self, host, port, other_factory,98 def connectSSL(self, host, port, factory,
95 context_factory, *args, **kwargs):99 contextFactory, *args, **kwargs):
96 """A connectSSL going thru the tunnel."""100 """A connectSSL going thru the tunnel."""
97 factory = TunnelClientFactory(host, port, other_factory,101 logger.info("Connecting (SSL) to %r:%r via tunnel at %r:%r",
98 context_factory)102 host, port, self.tunnel_host, self.tunnel_port)
99 from twisted.internet import reactor103 tunnel_factory = TunnelClientFactory(host, port, factory,
104 contextFactory)
100 return reactor.connectTCP(self.tunnel_host, self.tunnel_port,105 return reactor.connectTCP(self.tunnel_host, self.tunnel_port,
101 factory, *args, **kwargs)106 tunnel_factory, *args, **kwargs)
107
108
109class TunnelProcessProtocol(protocol.ProcessProtocol):
110 """The dialog thru stdout with the tunnel server."""
111
112 timeout = 5
113
114 def __init__(self, client_d):
115 """Initialize this protocol."""
116 self.client_d = client_d
117 self.timer = None
118
119 def connectionMade(self):
120 """The process has started, start a timer."""
121 logger.info("Tunnel process started.")
122 self.timer = reactor.callLater(self.timeout, self.process_timeouted)
123
124 def process_timeouted(self):
125 """The process took too long to reply."""
126 if not self.client_d.called:
127 logger.info("Timeout while waiting for tunnel process.")
128 self.client_d.callback(reactor)
129
130 def finish_timeout(self):
131 """Stop the timer from firing."""
132 if self.timer and self.timer.active():
133 self.timer.cancel()
134
135 def processExited(self, status):
136 """The tunnel process has exited with some error code."""
137 self.finish_timeout()
138 logger.info("Tunnel process exit status %r.", status)
139 if not self.client_d.called:
140 self.client_d.callback(reactor)
141
142 def outReceived(self, data):
143 """Receive the port number."""
144 if self.client_d.called:
145 return
146
147 for line in data.split("\n"):
148 if line.startswith(TUNNEL_PORT_LABEL):
149 _header, port = line.split(":", 1)
150 port = int(port.strip())
151 logger.info("Tunnel process listening on port %r.", port)
152 client = TunnelClient(LOCALHOST, port)
153 self.client_d.callback(client)
154 break
102155
=== modified file 'ubuntuone/proxy/tunnel_server.py'
--- ubuntuone/proxy/tunnel_server.py 2012-03-09 15:35:27 +0000
+++ ubuntuone/proxy/tunnel_server.py 2012-03-12 17:46:23 +0000
@@ -45,7 +45,7 @@
45from zope.interface import implements45from zope.interface import implements
4646
47from ubuntu_sso.utils.webclient import gsettings47from ubuntu_sso.utils.webclient import gsettings
48from ubuntuone.proxy.common import BaseTunnelProtocol, CRLF48from ubuntuone.proxy.common import BaseTunnelProtocol, CRLF, TUNNEL_PORT_LABEL
49from ubuntuone.proxy.logger import logger49from ubuntuone.proxy.logger import logger
5050
51DEFAULT_CODE = 50051DEFAULT_CODE = 500
@@ -194,7 +194,6 @@
194194
195 def response_data_received(self, data):195 def response_data_received(self, data):
196 """Return data coming from the other side."""196 """Return data coming from the other side."""
197 logger.debug("writing data to the transport: %r", data)
198 self.write_transport(data)197 self.write_transport(data)
199198
200199
@@ -279,6 +278,6 @@
279 else:278 else:
280 app = QCoreApplication(argv)279 app = QCoreApplication(argv)
281 tunnel_server = TunnelServer()280 tunnel_server = TunnelServer()
282 sys.stdout.write("Tunnel port: {}\n".format(tunnel_server.port))281 sys.stdout.write("%s: %d\n" % (TUNNEL_PORT_LABEL, tunnel_server.port))
283 sys.stdout.flush()282 sys.stdout.flush()
284 app.exec_()283 app.exec_()
285284
=== modified file 'ubuntuone/syncdaemon/action_queue.py'
--- ubuntuone/syncdaemon/action_queue.py 2012-02-09 21:57:00 +0000
+++ ubuntuone/syncdaemon/action_queue.py 2012-03-12 17:46:23 +0000
@@ -57,6 +57,7 @@
57from ubuntuone.syncdaemon.logger import mklog, TRACE57from ubuntuone.syncdaemon.logger import mklog, TRACE
58from ubuntuone.syncdaemon.volume_manager import ACCESS_LEVEL_RW58from ubuntuone.syncdaemon.volume_manager import ACCESS_LEVEL_RW
59from ubuntuone.syncdaemon import config, offload_queue59from ubuntuone.syncdaemon import config, offload_queue
60from ubuntuone.syncdaemon import tunnel_runner
6061
61logger = logging.getLogger("ubuntuone.SyncDaemon.ActionQueue")62logger = logging.getLogger("ubuntuone.SyncDaemon.ActionQueue")
6263
@@ -699,6 +700,7 @@
699 self.zip_queue = ZipQueue()700 self.zip_queue = ZipQueue()
700 self.conditions_locker = ConditionsLocker()701 self.conditions_locker = ConditionsLocker()
701 self.disk_queue = offload_queue.OffloadQueue()702 self.disk_queue = offload_queue.OffloadQueue()
703 self.tunnel_runner = tunnel_runner.TunnelRunner(self.host, self.port)
702704
703 self.estimated_free_space = {}705 self.estimated_free_space = {}
704 event_queue.subscribe(self)706 event_queue.subscribe(self)
@@ -816,16 +818,18 @@
816 else:818 else:
817 return defer.succeed((self.host, self.port))819 return defer.succeed((self.host, self.port))
818820
821 @defer.inlineCallbacks
819 def _make_connection(self, result):822 def _make_connection(self, result):
820 """Do the real connect call."""823 """Do the real connect call."""
821 host, port = result824 host, port = result
822 ssl_context = get_ssl_context(self.disable_ssl_verify)825 ssl_context = get_ssl_context(self.disable_ssl_verify)
826 client = yield self.tunnel_runner.get_client()
823 if self.use_ssl:827 if self.use_ssl:
824 self.connector = reactor.connectSSL(host, port, factory=self,828 self.connector = client.connectSSL(host, port, factory=self,
825 contextFactory=ssl_context,829 contextFactory=ssl_context,
826 timeout=self.connection_timeout)830 timeout=self.connection_timeout)
827 else:831 else:
828 self.connector = reactor.connectTCP(host, port, self,832 self.connector = client.connectTCP(host, port, self,
829 timeout=self.connection_timeout)833 timeout=self.connection_timeout)
830834
831 def connect(self):835 def connect(self):
832836
=== added file 'ubuntuone/syncdaemon/tunnel_runner.py'
--- ubuntuone/syncdaemon/tunnel_runner.py 1970-01-01 00:00:00 +0000
+++ ubuntuone/syncdaemon/tunnel_runner.py 2012-03-12 17:46:23 +0000
@@ -0,0 +1,70 @@
1# -*- coding: utf8 -*-
2#
3# Copyright 2012 Canonical Ltd.
4#
5# This program is free software: you can redistribute it and/or modify it
6# under the terms of the GNU General Public License version 3, as published
7# by the Free Software Foundation.
8#
9# This program is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranties of
11# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
12# PURPOSE. See the GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License along
15# with this program. If not, see <http://www.gnu.org/licenses/>.
16"""Run the tunnel process and start a client, with a reactor as a fallback."""
17
18import logging
19
20from os import path
21
22from twisted.internet import defer, reactor
23
24
25TUNNEL_EXECUTABLE = "ubuntuone-proxy-tunnel"
26logger = logging.getLogger("ubuntuone.SyncDaemon.TunnelRunner")
27
28
29class TunnelRunner(object):
30 """Run a tunnel process."""
31
32 def __init__(self, host, port):
33 """Start this runner instance."""
34 self.client_d = defer.Deferred()
35 try:
36 self.start_process(host, port)
37 except ImportError:
38 logger.info("Proxy support not installed.")
39 self.client_d.callback(reactor)
40
41 def start_process(self, host, port):
42 """Start the tunnel process."""
43 from ubuntuone.proxy.tunnel_client import TunnelProcessProtocol
44 protocol = TunnelProcessProtocol(self.client_d)
45 process_path = self.get_process_path()
46 args = [TUNNEL_EXECUTABLE, host, str(port)]
47 reactor.spawnProcess(protocol, process_path, env=None, args=args)
48
49 def get_process_path(self):
50 """Get the path to the tunnel process."""
51 filename = path.join(path.dirname(__file__), "..", "..", "bin",
52 TUNNEL_EXECUTABLE)
53 if path.isfile(filename):
54 return filename
55
56 from ubuntuone.clientdefs import LIBEXECDIR
57 return path.join(LIBEXECDIR, TUNNEL_EXECUTABLE)
58
59 def get_client(self):
60 """A deferred with the reactor or a tunnel client."""
61
62 def client_selected(result, d):
63 """The tunnel_client or the reactor were selected."""
64 d.callback(result)
65 # make sure the result is available for next callback
66 return result
67
68 d = defer.Deferred()
69 self.client_d.addCallback(client_selected, d)
70 return d

Subscribers

People subscribed via source and target branches