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 | 28 | from collections import defaultdict | 28 | from collections import defaultdict |
7 | 29 | from functools import wraps | 29 | from functools import wraps |
8 | 30 | 30 | ||
10 | 31 | from twisted.internet import defer | 31 | from twisted.internet import defer, reactor |
11 | 32 | from twisted.trial.unittest import TestCase as TwistedTestCase | 32 | from twisted.trial.unittest import TestCase as TwistedTestCase |
12 | 33 | from ubuntuone.devtools.testcases import skipIfOS | 33 | from ubuntuone.devtools.testcases import skipIfOS |
13 | 34 | from zope.interface import implements | 34 | from zope.interface import implements |
14 | @@ -267,6 +267,17 @@ | |||
15 | 267 | return defer.succeed(True) | 267 | return defer.succeed(True) |
16 | 268 | 268 | ||
17 | 269 | 269 | ||
18 | 270 | class FakeTunnelRunner(object): | ||
19 | 271 | """A fake proxy.tunnel_client.TunnelRunner.""" | ||
20 | 272 | |||
21 | 273 | def __init__(self, *args): | ||
22 | 274 | """Fake a proxy tunnel.""" | ||
23 | 275 | |||
24 | 276 | def get_client(self): | ||
25 | 277 | """Always return the reactor.""" | ||
26 | 278 | return defer.succeed(reactor) | ||
27 | 279 | |||
28 | 280 | |||
29 | 270 | class BaseTwistedTestCase(TwistedTestCase): | 281 | class BaseTwistedTestCase(TwistedTestCase): |
30 | 271 | """Base TestCase with helper methods to handle temp dir. | 282 | """Base TestCase with helper methods to handle temp dir. |
31 | 272 | 283 | ||
32 | @@ -276,6 +287,7 @@ | |||
33 | 276 | makedirs(path): support read-only shares | 287 | makedirs(path): support read-only shares |
34 | 277 | """ | 288 | """ |
35 | 278 | MAX_FILENAME = 32 # some platforms limit lengths of filenames | 289 | MAX_FILENAME = 32 # some platforms limit lengths of filenames |
36 | 290 | tunnel_runner_class = FakeTunnelRunner | ||
37 | 279 | 291 | ||
38 | 280 | def mktemp(self, name='temp'): | 292 | def mktemp(self, name='temp'): |
39 | 281 | """ Customized mktemp that accepts an optional name argument. """ | 293 | """ Customized mktemp that accepts an optional name argument. """ |
40 | @@ -380,6 +392,8 @@ | |||
41 | 380 | # Patch the user home | 392 | # Patch the user home |
42 | 381 | self.home_dir = self.mktemp('ubuntuonehacker') | 393 | self.home_dir = self.mktemp('ubuntuonehacker') |
43 | 382 | self.patch(platform, "xdg_home", self.home_dir) | 394 | self.patch(platform, "xdg_home", self.home_dir) |
44 | 395 | self.patch(action_queue.tunnel_runner, "TunnelRunner", | ||
45 | 396 | self.tunnel_runner_class) | ||
46 | 383 | 397 | ||
47 | 384 | 398 | ||
48 | 385 | class FakeMainTestCase(BaseTwistedTestCase): | 399 | class FakeMainTestCase(BaseTwistedTestCase): |
49 | 386 | 400 | ||
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 | 84 | 84 | ||
55 | 85 | def test_sends_connect_request(self): | 85 | def test_sends_connect_request(self): |
56 | 86 | """Sends the expected CONNECT request.""" | 86 | """Sends the expected CONNECT request.""" |
58 | 87 | expected = tunnel_client.METHOD_LINE.format(self.host, self.port) | 87 | expected = tunnel_client.METHOD_LINE % (self.host, self.port) |
59 | 88 | written = self.tunnel_client_proto.transport.getvalue() | 88 | written = self.tunnel_client_proto.transport.getvalue() |
60 | 89 | first_line = written.split(CRLF)[0] | 89 | first_line = written.split(CRLF)[0] |
61 | 90 | self.assertEqual(first_line + CRLF, expected) | 90 | self.assertEqual(first_line + CRLF, expected) |
62 | 91 | 91 | ||
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 | 34 | 34 | ||
68 | 35 | 35 | ||
69 | 36 | FAKE_SESSION_TEMPLATE = ( | 36 | FAKE_SESSION_TEMPLATE = ( |
71 | 37 | "CONNECT {0.netloc} HTTP/1.0" + CRLF + | 37 | "CONNECT %s HTTP/1.0" + CRLF + |
72 | 38 | "Header1: value1" + CRLF + | 38 | "Header1: value1" + CRLF + |
73 | 39 | "Header2: value2" + CRLF + | 39 | "Header2: value2" + CRLF + |
74 | 40 | CRLF + | 40 | CRLF + |
76 | 41 | "GET {0.path} HTTP/1.0" + CRLF + CRLF | 41 | "GET %s HTTP/1.0" + CRLF + CRLF |
77 | 42 | ) | 42 | ) |
78 | 43 | 43 | ||
79 | 44 | FAKE_SETTINGS = { | 44 | FAKE_SETTINGS = { |
80 | @@ -140,7 +140,8 @@ | |||
81 | 140 | def test_complete_connection(self): | 140 | def test_complete_connection(self): |
82 | 141 | """Test from the tunnel server down.""" | 141 | """Test from the tunnel server down.""" |
83 | 142 | url = urlparse(self.dest_url) | 142 | url = urlparse(self.dest_url) |
85 | 143 | client = FakeClientFactory(FAKE_SESSION_TEMPLATE.format(url)) | 143 | fake_session = FAKE_SESSION_TEMPLATE % (url.netloc, url.path) |
86 | 144 | client = FakeClientFactory(fake_session) | ||
87 | 144 | reactor.connectTCP("0.0.0.0", self.tunnel_server.port, client) | 145 | reactor.connectTCP("0.0.0.0", self.tunnel_server.port, client) |
88 | 145 | response = yield client.response | 146 | response = yield client.response |
89 | 146 | self.assertIn(SAMPLE_CONTENT, response) | 147 | self.assertIn(SAMPLE_CONTENT, response) |
90 | @@ -230,7 +231,7 @@ | |||
91 | 230 | def test_successful_connect(self): | 231 | def test_successful_connect(self): |
92 | 231 | """A successful connect thru the tunnel.""" | 232 | """A successful connect thru the tunnel.""" |
93 | 232 | url = urlparse(self.dest_url) | 233 | url = urlparse(self.dest_url) |
95 | 233 | data = FAKE_SESSION_TEMPLATE.format(url) | 234 | data = FAKE_SESSION_TEMPLATE % (url.netloc, url.path) |
96 | 234 | self.proto.dataReceived(data) | 235 | self.proto.dataReceived(data) |
97 | 235 | lines = self.transport.getvalue().split(CRLF) | 236 | lines = self.transport.getvalue().split(CRLF) |
98 | 236 | self.assertEqual(lines[-1], SAMPLE_CONTENT) | 237 | self.assertEqual(lines[-1], SAMPLE_CONTENT) |
99 | @@ -474,7 +475,8 @@ | |||
100 | 474 | """With proxies enabled print port to stdout and start the mainloop.""" | 475 | """With proxies enabled print port to stdout and start the mainloop.""" |
101 | 475 | self.proxies_enabled = True | 476 | self.proxies_enabled = True |
102 | 476 | tunnel_server.main(["example.com", "443"]) | 477 | tunnel_server.main(["example.com", "443"]) |
104 | 477 | self.assertIn("Tunnel port:", self.fake_stdout.getvalue()) | 478 | self.assertIn(tunnel_server.TUNNEL_PORT_LABEL + ":", |
105 | 479 | self.fake_stdout.getvalue()) | ||
106 | 478 | 480 | ||
107 | 479 | def test_on_proxies_disabled_exit(self): | 481 | def test_on_proxies_disabled_exit(self): |
108 | 480 | """With proxies disabled, print a message and exit gracefully.""" | 482 | """With proxies disabled, print a message and exit gracefully.""" |
109 | 481 | 483 | ||
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 | 186 | return FakeRequest() | 186 | return FakeRequest() |
115 | 187 | 187 | ||
116 | 188 | 188 | ||
117 | 189 | class FakeTunnelClient(object): | ||
118 | 190 | """A fake proxy.tunnel_client.""" | ||
119 | 191 | |||
120 | 192 | def __init__(self): | ||
121 | 193 | """Fake this proxy tunnel.""" | ||
122 | 194 | self.tcp_connected = False | ||
123 | 195 | self.ssl_connected = False | ||
124 | 196 | |||
125 | 197 | def connectTCP(self, *args, **kwargs): | ||
126 | 198 | """Save the connection thru TCP.""" | ||
127 | 199 | self.tcp_connected = True | ||
128 | 200 | |||
129 | 201 | def connectSSL(self, *args, **kwargs): | ||
130 | 202 | """Save the connection thru SSL.""" | ||
131 | 203 | self.ssl_connected = True | ||
132 | 204 | |||
133 | 205 | |||
134 | 206 | class SavingConnectionTunnelRunner(object): | ||
135 | 207 | """A fake proxy.tunnel_client.TunnelRunner.""" | ||
136 | 208 | |||
137 | 209 | def __init__(self, *args): | ||
138 | 210 | """Fake a proxy tunnel.""" | ||
139 | 211 | self.client = FakeTunnelClient() | ||
140 | 212 | |||
141 | 213 | def get_client(self): | ||
142 | 214 | """Always return the reactor.""" | ||
143 | 215 | return defer.succeed(self.client) | ||
144 | 216 | |||
145 | 217 | |||
146 | 189 | class TestingProtocol(ActionQueue.protocol): | 218 | class TestingProtocol(ActionQueue.protocol): |
147 | 190 | """Protocol for testing.""" | 219 | """Protocol for testing.""" |
148 | 191 | 220 | ||
149 | @@ -1295,6 +1324,28 @@ | |||
150 | 1295 | "host 1.2.3.4", "port 4321")) | 1324 | "host 1.2.3.4", "port 4321")) |
151 | 1296 | 1325 | ||
152 | 1297 | 1326 | ||
153 | 1327 | class TunnelRunnerTestCase(FactoryBaseTestCase): | ||
154 | 1328 | """Tests for the tunnel runner.""" | ||
155 | 1329 | |||
156 | 1330 | tunnel_runner_class = SavingConnectionTunnelRunner | ||
157 | 1331 | |||
158 | 1332 | @defer.inlineCallbacks | ||
159 | 1333 | def test_make_connection_uses_tunnelrunner_non_ssl(self): | ||
160 | 1334 | """Check that _make_connection uses TunnelRunner.""" | ||
161 | 1335 | self.action_queue.use_ssl = False | ||
162 | 1336 | yield self.action_queue._make_connection(("127.0.0.1", 1234)) | ||
163 | 1337 | self.assertTrue(self.action_queue.tunnel_runner.client.tcp_connected, | ||
164 | 1338 | "connectTCP is called on the client.") | ||
165 | 1339 | |||
166 | 1340 | @defer.inlineCallbacks | ||
167 | 1341 | def test_make_connection_uses_tunnelrunner_ssl(self): | ||
168 | 1342 | """Check that _make_connection uses TunnelRunner.""" | ||
169 | 1343 | self.action_queue.use_ssl = True | ||
170 | 1344 | yield self.action_queue._make_connection(("127.0.0.1", 1234)) | ||
171 | 1345 | self.assertTrue(self.action_queue.tunnel_runner.client.ssl_connected, | ||
172 | 1346 | "connectSSL is called on the client.") | ||
173 | 1347 | |||
174 | 1348 | |||
175 | 1298 | class ConnectedBaseTestCase(FactoryBaseTestCase): | 1349 | class ConnectedBaseTestCase(FactoryBaseTestCase): |
176 | 1299 | """Base test case generating a connected factory.""" | 1350 | """Base test case generating a connected factory.""" |
177 | 1300 | 1351 | ||
178 | 1301 | 1352 | ||
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 | 1 | # -*- coding: utf8 -*- | ||
184 | 2 | # | ||
185 | 3 | # Copyright 2012 Canonical Ltd. | ||
186 | 4 | # | ||
187 | 5 | # This program is free software: you can redistribute it and/or modify it | ||
188 | 6 | # under the terms of the GNU General Public License version 3, as published | ||
189 | 7 | # by the Free Software Foundation. | ||
190 | 8 | # | ||
191 | 9 | # This program is distributed in the hope that it will be useful, but | ||
192 | 10 | # WITHOUT ANY WARRANTY; without even the implied warranties of | ||
193 | 11 | # MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
194 | 12 | # PURPOSE. See the GNU General Public License for more details. | ||
195 | 13 | # | ||
196 | 14 | # You should have received a copy of the GNU General Public License along | ||
197 | 15 | # with this program. If not, see <http://www.gnu.org/licenses/>. | ||
198 | 16 | """Tests for the proxy tunnel runner.""" | ||
199 | 17 | |||
200 | 18 | from twisted.internet import defer, error, reactor, task | ||
201 | 19 | from twisted.trial.unittest import TestCase | ||
202 | 20 | |||
203 | 21 | from ubuntuone.proxy import tunnel_client | ||
204 | 22 | from ubuntuone.syncdaemon import tunnel_runner | ||
205 | 23 | |||
206 | 24 | FAKE_HOST = "fs-1.two.ubuntu.com" | ||
207 | 25 | FAKE_PORT = 443 | ||
208 | 26 | |||
209 | 27 | |||
210 | 28 | class TunnelRunnerConstructorTestCase(TestCase): | ||
211 | 29 | """Test the tunnel runner constructor.""" | ||
212 | 30 | |||
213 | 31 | timeout = 3 | ||
214 | 32 | |||
215 | 33 | def raise_import_error(self, *args): | ||
216 | 34 | """Raise an import error.""" | ||
217 | 35 | raise ImportError | ||
218 | 36 | |||
219 | 37 | @defer.inlineCallbacks | ||
220 | 38 | def test_proxy_support_not_installed(self): | ||
221 | 39 | """The proxy support binary package is not installed.""" | ||
222 | 40 | self.patch(tunnel_runner.TunnelRunner, "start_process", | ||
223 | 41 | self.raise_import_error) | ||
224 | 42 | tr = tunnel_runner.TunnelRunner(FAKE_HOST, FAKE_PORT) | ||
225 | 43 | client = yield tr.get_client() | ||
226 | 44 | self.assertEqual(client, reactor) | ||
227 | 45 | |||
228 | 46 | |||
229 | 47 | class TunnelRunnerTestCase(TestCase): | ||
230 | 48 | """Tests for the TunnelRunner.""" | ||
231 | 49 | |||
232 | 50 | timeout = 3 | ||
233 | 51 | |||
234 | 52 | @defer.inlineCallbacks | ||
235 | 53 | def setUp(self): | ||
236 | 54 | """Initialize this testcase.""" | ||
237 | 55 | yield super(TunnelRunnerTestCase, self).setUp() | ||
238 | 56 | self.spawned = [] | ||
239 | 57 | self.patch(tunnel_client.reactor, "spawnProcess", | ||
240 | 58 | lambda *args, **kwargs: self.spawned.append((args, kwargs))) | ||
241 | 59 | self.process_protocol = None | ||
242 | 60 | self.process_protocol_class = tunnel_client.TunnelProcessProtocol | ||
243 | 61 | self.patch(tunnel_client, "TunnelProcessProtocol", | ||
244 | 62 | self.storing_process_protocol_factory) | ||
245 | 63 | self.tr = tunnel_runner.TunnelRunner("fs-1.one.ubuntu.com", 443) | ||
246 | 64 | |||
247 | 65 | def storing_process_protocol_factory(self, *args, **kwargs): | ||
248 | 66 | """Store the process protocol just created.""" | ||
249 | 67 | self.process_protocol = self.process_protocol_class(*args, **kwargs) | ||
250 | 68 | return self.process_protocol | ||
251 | 69 | |||
252 | 70 | def test_tunnel_process_is_started(self): | ||
253 | 71 | """The tunnel process is started.""" | ||
254 | 72 | self.assertEqual(len(self.spawned), 1, | ||
255 | 73 | "The tunnel process is started.") | ||
256 | 74 | |||
257 | 75 | @defer.inlineCallbacks | ||
258 | 76 | def test_tunnel_process_get_client_yielded_twice(self): | ||
259 | 77 | """The get_client method can be yielded twice.""" | ||
260 | 78 | self.process_protocol.processExited(error.ProcessTerminated(1)) | ||
261 | 79 | client = yield self.tr.get_client() | ||
262 | 80 | client = yield self.tr.get_client() | ||
263 | 81 | self.assertNotEqual(client, None) | ||
264 | 82 | |||
265 | 83 | @defer.inlineCallbacks | ||
266 | 84 | def test_tunnel_process_exits_with_error(self): | ||
267 | 85 | """The tunnel process exits with an error.""" | ||
268 | 86 | self.process_protocol.processExited(error.ProcessTerminated(1)) | ||
269 | 87 | client = yield self.tr.get_client() | ||
270 | 88 | self.assertEqual(client, reactor) | ||
271 | 89 | |||
272 | 90 | @defer.inlineCallbacks | ||
273 | 91 | def test_tunnel_process_exits_gracefully(self): | ||
274 | 92 | """The tunnel process exits gracefully.""" | ||
275 | 93 | self.process_protocol.processExited(error.ProcessDone(0)) | ||
276 | 94 | client = yield self.tr.get_client() | ||
277 | 95 | self.assertEqual(client, reactor) | ||
278 | 96 | |||
279 | 97 | @defer.inlineCallbacks | ||
280 | 98 | def test_tunnel_process_prints_random_garbage_and_timeouts(self): | ||
281 | 99 | """The tunnel process prints garbage and timeouts.""" | ||
282 | 100 | clock = task.Clock() | ||
283 | 101 | self.patch(tunnel_client, "reactor", clock) | ||
284 | 102 | self.process_protocol.connectionMade() | ||
285 | 103 | self.process_protocol.outReceived("Random garbage") | ||
286 | 104 | clock.advance(self.process_protocol.timeout) | ||
287 | 105 | client = yield self.tr.get_client() | ||
288 | 106 | self.assertEqual(client, clock) | ||
289 | 107 | |||
290 | 108 | @defer.inlineCallbacks | ||
291 | 109 | def test_tunnel_process_prints_port_number(self): | ||
292 | 110 | """The tunnel process prints the port number.""" | ||
293 | 111 | received = "%s: %d\n" % (tunnel_client.TUNNEL_PORT_LABEL, FAKE_PORT) | ||
294 | 112 | self.process_protocol.outReceived(received) | ||
295 | 113 | client = yield self.tr.get_client() | ||
296 | 114 | self.assertEqual(client.tunnel_port, FAKE_PORT) | ||
297 | 0 | 115 | ||
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 | 18 | from twisted.protocols import basic | 18 | from twisted.protocols import basic |
303 | 19 | 19 | ||
304 | 20 | CRLF = "\r\n" | 20 | CRLF = "\r\n" |
305 | 21 | TUNNEL_PORT_LABEL = "Tunnel port" | ||
306 | 21 | 22 | ||
307 | 22 | 23 | ||
308 | 23 | class BaseTunnelProtocol(basic.LineReceiver): | 24 | class BaseTunnelProtocol(basic.LineReceiver): |
309 | 24 | 25 | ||
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 | 15 | # with this program. If not, see <http://www.gnu.org/licenses/>. | 15 | # with this program. If not, see <http://www.gnu.org/licenses/>. |
315 | 16 | """Client for the tunnel protocol.""" | 16 | """Client for the tunnel protocol.""" |
316 | 17 | 17 | ||
322 | 18 | from twisted.internet import protocol | 18 | import logging |
323 | 19 | 19 | ||
324 | 20 | from ubuntuone.proxy.common import BaseTunnelProtocol, CRLF | 20 | from twisted.internet import protocol, reactor |
325 | 21 | 21 | ||
326 | 22 | METHOD_LINE = "CONNECT {0}:{1} HTTP/1.0" + CRLF | 22 | from ubuntuone.proxy.common import BaseTunnelProtocol, CRLF, TUNNEL_PORT_LABEL |
327 | 23 | |||
328 | 24 | METHOD_LINE = "CONNECT %s:%d HTTP/1.0" + CRLF | ||
329 | 25 | LOCALHOST = "127.0.0.1" | ||
330 | 26 | logger = logging.getLogger("ubuntuone.Proxy.TunnelClient") | ||
331 | 23 | 27 | ||
332 | 24 | 28 | ||
333 | 25 | class TunnelClientProtocol(BaseTunnelProtocol): | 29 | class TunnelClientProtocol(BaseTunnelProtocol): |
334 | @@ -27,8 +31,8 @@ | |||
335 | 27 | 31 | ||
336 | 28 | def connectionMade(self): | 32 | def connectionMade(self): |
337 | 29 | """The connection to the tunnel was made so send request.""" | 33 | """The connection to the tunnel was made so send request.""" |
340 | 30 | method_line = METHOD_LINE.format(self.factory.tunnel_host, | 34 | method_line = METHOD_LINE % (self.factory.tunnel_host, |
341 | 31 | self.factory.tunnel_port) | 35 | self.factory.tunnel_port) |
342 | 32 | headers = { | 36 | headers = { |
343 | 33 | "User-Agent": "Ubuntu One tunnel client" | 37 | "User-Agent": "Ubuntu One tunnel client" |
344 | 34 | } | 38 | } |
345 | @@ -82,20 +86,69 @@ | |||
346 | 82 | """Initialize this client.""" | 86 | """Initialize this client.""" |
347 | 83 | self.tunnel_host = tunnel_host | 87 | self.tunnel_host = tunnel_host |
348 | 84 | self.tunnel_port = tunnel_port | 88 | self.tunnel_port = tunnel_port |
349 | 85 | # TODO: use tcp activation to start a tunnel process if not running | ||
350 | 86 | 89 | ||
352 | 87 | def connectTCP(self, host, port, other_factory, *args, **kwargs): | 90 | def connectTCP(self, host, port, factory, *args, **kwargs): |
353 | 88 | """A connectTCP going thru the tunnel.""" | 91 | """A connectTCP going thru the tunnel.""" |
356 | 89 | factory = TunnelClientFactory(host, port, other_factory) | 92 | logger.info("Connecting (TCP) to %r:%r via tunnel at %r:%r", |
357 | 90 | from twisted.internet import reactor | 93 | host, port, self.tunnel_host, self.tunnel_port) |
358 | 94 | tunnel_factory = TunnelClientFactory(host, port, factory) | ||
359 | 91 | return reactor.connectTCP(self.tunnel_host, self.tunnel_port, | 95 | return reactor.connectTCP(self.tunnel_host, self.tunnel_port, |
361 | 92 | factory, *args, **kwargs) | 96 | tunnel_factory, *args, **kwargs) |
362 | 93 | 97 | ||
365 | 94 | def connectSSL(self, host, port, other_factory, | 98 | def connectSSL(self, host, port, factory, |
366 | 95 | context_factory, *args, **kwargs): | 99 | contextFactory, *args, **kwargs): |
367 | 96 | """A connectSSL going thru the tunnel.""" | 100 | """A connectSSL going thru the tunnel.""" |
371 | 97 | factory = TunnelClientFactory(host, port, other_factory, | 101 | logger.info("Connecting (SSL) to %r:%r via tunnel at %r:%r", |
372 | 98 | context_factory) | 102 | host, port, self.tunnel_host, self.tunnel_port) |
373 | 99 | from twisted.internet import reactor | 103 | tunnel_factory = TunnelClientFactory(host, port, factory, |
374 | 104 | contextFactory) | ||
375 | 100 | return reactor.connectTCP(self.tunnel_host, self.tunnel_port, | 105 | return reactor.connectTCP(self.tunnel_host, self.tunnel_port, |
377 | 101 | factory, *args, **kwargs) | 106 | tunnel_factory, *args, **kwargs) |
378 | 107 | |||
379 | 108 | |||
380 | 109 | class TunnelProcessProtocol(protocol.ProcessProtocol): | ||
381 | 110 | """The dialog thru stdout with the tunnel server.""" | ||
382 | 111 | |||
383 | 112 | timeout = 5 | ||
384 | 113 | |||
385 | 114 | def __init__(self, client_d): | ||
386 | 115 | """Initialize this protocol.""" | ||
387 | 116 | self.client_d = client_d | ||
388 | 117 | self.timer = None | ||
389 | 118 | |||
390 | 119 | def connectionMade(self): | ||
391 | 120 | """The process has started, start a timer.""" | ||
392 | 121 | logger.info("Tunnel process started.") | ||
393 | 122 | self.timer = reactor.callLater(self.timeout, self.process_timeouted) | ||
394 | 123 | |||
395 | 124 | def process_timeouted(self): | ||
396 | 125 | """The process took too long to reply.""" | ||
397 | 126 | if not self.client_d.called: | ||
398 | 127 | logger.info("Timeout while waiting for tunnel process.") | ||
399 | 128 | self.client_d.callback(reactor) | ||
400 | 129 | |||
401 | 130 | def finish_timeout(self): | ||
402 | 131 | """Stop the timer from firing.""" | ||
403 | 132 | if self.timer and self.timer.active(): | ||
404 | 133 | self.timer.cancel() | ||
405 | 134 | |||
406 | 135 | def processExited(self, status): | ||
407 | 136 | """The tunnel process has exited with some error code.""" | ||
408 | 137 | self.finish_timeout() | ||
409 | 138 | logger.info("Tunnel process exit status %r.", status) | ||
410 | 139 | if not self.client_d.called: | ||
411 | 140 | self.client_d.callback(reactor) | ||
412 | 141 | |||
413 | 142 | def outReceived(self, data): | ||
414 | 143 | """Receive the port number.""" | ||
415 | 144 | if self.client_d.called: | ||
416 | 145 | return | ||
417 | 146 | |||
418 | 147 | for line in data.split("\n"): | ||
419 | 148 | if line.startswith(TUNNEL_PORT_LABEL): | ||
420 | 149 | _header, port = line.split(":", 1) | ||
421 | 150 | port = int(port.strip()) | ||
422 | 151 | logger.info("Tunnel process listening on port %r.", port) | ||
423 | 152 | client = TunnelClient(LOCALHOST, port) | ||
424 | 153 | self.client_d.callback(client) | ||
425 | 154 | break | ||
426 | 102 | 155 | ||
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 | 45 | from zope.interface import implements | 45 | from zope.interface import implements |
432 | 46 | 46 | ||
433 | 47 | from ubuntu_sso.utils.webclient import gsettings | 47 | from ubuntu_sso.utils.webclient import gsettings |
435 | 48 | from ubuntuone.proxy.common import BaseTunnelProtocol, CRLF | 48 | from ubuntuone.proxy.common import BaseTunnelProtocol, CRLF, TUNNEL_PORT_LABEL |
436 | 49 | from ubuntuone.proxy.logger import logger | 49 | from ubuntuone.proxy.logger import logger |
437 | 50 | 50 | ||
438 | 51 | DEFAULT_CODE = 500 | 51 | DEFAULT_CODE = 500 |
439 | @@ -194,7 +194,6 @@ | |||
440 | 194 | 194 | ||
441 | 195 | def response_data_received(self, data): | 195 | def response_data_received(self, data): |
442 | 196 | """Return data coming from the other side.""" | 196 | """Return data coming from the other side.""" |
443 | 197 | logger.debug("writing data to the transport: %r", data) | ||
444 | 198 | self.write_transport(data) | 197 | self.write_transport(data) |
445 | 199 | 198 | ||
446 | 200 | 199 | ||
447 | @@ -279,6 +278,6 @@ | |||
448 | 279 | else: | 278 | else: |
449 | 280 | app = QCoreApplication(argv) | 279 | app = QCoreApplication(argv) |
450 | 281 | tunnel_server = TunnelServer() | 280 | tunnel_server = TunnelServer() |
452 | 282 | sys.stdout.write("Tunnel port: {}\n".format(tunnel_server.port)) | 281 | sys.stdout.write("%s: %d\n" % (TUNNEL_PORT_LABEL, tunnel_server.port)) |
453 | 283 | sys.stdout.flush() | 282 | sys.stdout.flush() |
454 | 284 | app.exec_() | 283 | app.exec_() |
455 | 285 | 284 | ||
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 | 57 | from ubuntuone.syncdaemon.logger import mklog, TRACE | 57 | from ubuntuone.syncdaemon.logger import mklog, TRACE |
461 | 58 | from ubuntuone.syncdaemon.volume_manager import ACCESS_LEVEL_RW | 58 | from ubuntuone.syncdaemon.volume_manager import ACCESS_LEVEL_RW |
462 | 59 | from ubuntuone.syncdaemon import config, offload_queue | 59 | from ubuntuone.syncdaemon import config, offload_queue |
463 | 60 | from ubuntuone.syncdaemon import tunnel_runner | ||
464 | 60 | 61 | ||
465 | 61 | logger = logging.getLogger("ubuntuone.SyncDaemon.ActionQueue") | 62 | logger = logging.getLogger("ubuntuone.SyncDaemon.ActionQueue") |
466 | 62 | 63 | ||
467 | @@ -699,6 +700,7 @@ | |||
468 | 699 | self.zip_queue = ZipQueue() | 700 | self.zip_queue = ZipQueue() |
469 | 700 | self.conditions_locker = ConditionsLocker() | 701 | self.conditions_locker = ConditionsLocker() |
470 | 701 | self.disk_queue = offload_queue.OffloadQueue() | 702 | self.disk_queue = offload_queue.OffloadQueue() |
471 | 703 | self.tunnel_runner = tunnel_runner.TunnelRunner(self.host, self.port) | ||
472 | 702 | 704 | ||
473 | 703 | self.estimated_free_space = {} | 705 | self.estimated_free_space = {} |
474 | 704 | event_queue.subscribe(self) | 706 | event_queue.subscribe(self) |
475 | @@ -816,16 +818,18 @@ | |||
476 | 816 | else: | 818 | else: |
477 | 817 | return defer.succeed((self.host, self.port)) | 819 | return defer.succeed((self.host, self.port)) |
478 | 818 | 820 | ||
479 | 821 | @defer.inlineCallbacks | ||
480 | 819 | def _make_connection(self, result): | 822 | def _make_connection(self, result): |
481 | 820 | """Do the real connect call.""" | 823 | """Do the real connect call.""" |
482 | 821 | host, port = result | 824 | host, port = result |
483 | 822 | ssl_context = get_ssl_context(self.disable_ssl_verify) | 825 | ssl_context = get_ssl_context(self.disable_ssl_verify) |
484 | 826 | client = yield self.tunnel_runner.get_client() | ||
485 | 823 | if self.use_ssl: | 827 | if self.use_ssl: |
487 | 824 | self.connector = reactor.connectSSL(host, port, factory=self, | 828 | self.connector = client.connectSSL(host, port, factory=self, |
488 | 825 | contextFactory=ssl_context, | 829 | contextFactory=ssl_context, |
489 | 826 | timeout=self.connection_timeout) | 830 | timeout=self.connection_timeout) |
490 | 827 | else: | 831 | else: |
492 | 828 | self.connector = reactor.connectTCP(host, port, self, | 832 | self.connector = client.connectTCP(host, port, self, |
493 | 829 | timeout=self.connection_timeout) | 833 | timeout=self.connection_timeout) |
494 | 830 | 834 | ||
495 | 831 | def connect(self): | 835 | def connect(self): |
496 | 832 | 836 | ||
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 | 1 | # -*- coding: utf8 -*- | ||
502 | 2 | # | ||
503 | 3 | # Copyright 2012 Canonical Ltd. | ||
504 | 4 | # | ||
505 | 5 | # This program is free software: you can redistribute it and/or modify it | ||
506 | 6 | # under the terms of the GNU General Public License version 3, as published | ||
507 | 7 | # by the Free Software Foundation. | ||
508 | 8 | # | ||
509 | 9 | # This program is distributed in the hope that it will be useful, but | ||
510 | 10 | # WITHOUT ANY WARRANTY; without even the implied warranties of | ||
511 | 11 | # MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
512 | 12 | # PURPOSE. See the GNU General Public License for more details. | ||
513 | 13 | # | ||
514 | 14 | # You should have received a copy of the GNU General Public License along | ||
515 | 15 | # with this program. If not, see <http://www.gnu.org/licenses/>. | ||
516 | 16 | """Run the tunnel process and start a client, with a reactor as a fallback.""" | ||
517 | 17 | |||
518 | 18 | import logging | ||
519 | 19 | |||
520 | 20 | from os import path | ||
521 | 21 | |||
522 | 22 | from twisted.internet import defer, reactor | ||
523 | 23 | |||
524 | 24 | |||
525 | 25 | TUNNEL_EXECUTABLE = "ubuntuone-proxy-tunnel" | ||
526 | 26 | logger = logging.getLogger("ubuntuone.SyncDaemon.TunnelRunner") | ||
527 | 27 | |||
528 | 28 | |||
529 | 29 | class TunnelRunner(object): | ||
530 | 30 | """Run a tunnel process.""" | ||
531 | 31 | |||
532 | 32 | def __init__(self, host, port): | ||
533 | 33 | """Start this runner instance.""" | ||
534 | 34 | self.client_d = defer.Deferred() | ||
535 | 35 | try: | ||
536 | 36 | self.start_process(host, port) | ||
537 | 37 | except ImportError: | ||
538 | 38 | logger.info("Proxy support not installed.") | ||
539 | 39 | self.client_d.callback(reactor) | ||
540 | 40 | |||
541 | 41 | def start_process(self, host, port): | ||
542 | 42 | """Start the tunnel process.""" | ||
543 | 43 | from ubuntuone.proxy.tunnel_client import TunnelProcessProtocol | ||
544 | 44 | protocol = TunnelProcessProtocol(self.client_d) | ||
545 | 45 | process_path = self.get_process_path() | ||
546 | 46 | args = [TUNNEL_EXECUTABLE, host, str(port)] | ||
547 | 47 | reactor.spawnProcess(protocol, process_path, env=None, args=args) | ||
548 | 48 | |||
549 | 49 | def get_process_path(self): | ||
550 | 50 | """Get the path to the tunnel process.""" | ||
551 | 51 | filename = path.join(path.dirname(__file__), "..", "..", "bin", | ||
552 | 52 | TUNNEL_EXECUTABLE) | ||
553 | 53 | if path.isfile(filename): | ||
554 | 54 | return filename | ||
555 | 55 | |||
556 | 56 | from ubuntuone.clientdefs import LIBEXECDIR | ||
557 | 57 | return path.join(LIBEXECDIR, TUNNEL_EXECUTABLE) | ||
558 | 58 | |||
559 | 59 | def get_client(self): | ||
560 | 60 | """A deferred with the reactor or a tunnel client.""" | ||
561 | 61 | |||
562 | 62 | def client_selected(result, d): | ||
563 | 63 | """The tunnel_client or the reactor were selected.""" | ||
564 | 64 | d.callback(result) | ||
565 | 65 | # make sure the result is available for next callback | ||
566 | 66 | return result | ||
567 | 67 | |||
568 | 68 | d = defer.Deferred() | ||
569 | 69 | self.client_d.addCallback(client_selected, d) | ||
570 | 70 | return d |
I get the following when I try to run the tests:
== Python Lint Notices ==
./contrib/ testing/ testcase. py:
278: undefined name 'reactor'