Merge lp:~facundo/magicicada-client/kill-qt4 into lp:magicicada-client
- kill-qt4
- Merge into trunk
Proposed by
Facundo Batista
Status: | Merged |
---|---|
Approved by: | Natalia Bidart |
Approved revision: | 1439 |
Merged at revision: | 1439 |
Proposed branch: | lp:~facundo/magicicada-client/kill-qt4 |
Merge into: | lp:magicicada-client |
Diff against target: |
3154 lines (+27/-2840) 24 files modified
bin/ubuntuone-proxy-tunnel (+0/-36) contrib/check-reactor-import (+0/-76) contrib/testing/testcase.py (+2/-16) dependencies.txt (+0/-2) run-tests (+2/-9) run-tests.bat (+0/-2) setup.py (+1/-1) ubuntuone/proxy/__init__.py (+0/-27) ubuntuone/proxy/common.py (+0/-73) ubuntuone/proxy/logger.py (+0/-48) ubuntuone/proxy/tests/__init__.py (+0/-156) ubuntuone/proxy/tests/ssl/dummy.cert (+0/-19) ubuntuone/proxy/tests/ssl/dummy.key (+0/-16) ubuntuone/proxy/tests/test_tunnel_client.py (+0/-225) ubuntuone/proxy/tests/test_tunnel_server.py (+0/-743) ubuntuone/proxy/tunnel_client.py (+0/-202) ubuntuone/proxy/tunnel_server.py (+0/-405) ubuntuone/syncdaemon/action_queue.py (+3/-11) ubuntuone/syncdaemon/tests/test_action_queue.py (+17/-57) ubuntuone/syncdaemon/tests/test_tunnel_runner.py (+0/-185) ubuntuone/syncdaemon/tunnel_runner.py (+0/-86) ubuntuone/syncdaemon/utils.py (+2/-9) ubuntuone/utils/gsettings.py (+0/-114) ubuntuone/utils/tests/test_gsettings.py (+0/-322) |
To merge this branch: | bzr merge lp:~facundo/magicicada-client/kill-qt4 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Natalia Bidart | Approve | ||
Review via email: mp+341428@code.launchpad.net |
Commit message
Removed everything PyQt related, including the HTTP(S) Proxy.
Description of the change
Removed everything PyQt related, including the HTTP(S) Proxy.
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === removed file 'bin/ubuntuone-proxy-tunnel' |
2 | --- bin/ubuntuone-proxy-tunnel 2015-09-29 21:05:26 +0000 |
3 | +++ bin/ubuntuone-proxy-tunnel 1970-01-01 00:00:00 +0000 |
4 | @@ -1,36 +0,0 @@ |
5 | -#!/usr/bin/python |
6 | -# |
7 | -# Copyright 2012 Canonical Ltd. |
8 | -# |
9 | -# This program is free software: you can redistribute it and/or modify it |
10 | -# under the terms of the GNU General Public License version 3, as published |
11 | -# by the Free Software Foundation. |
12 | -# |
13 | -# This program is distributed in the hope that it will be useful, but |
14 | -# WITHOUT ANY WARRANTY; without even the implied warranties of |
15 | -# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
16 | -# PURPOSE. See the GNU General Public License for more details. |
17 | -# |
18 | -# You should have received a copy of the GNU General Public License along |
19 | -# with this program. If not, see <http://www.gnu.org/licenses/>. |
20 | -# |
21 | -# In addition, as a special exception, the copyright holders give |
22 | -# permission to link the code of portions of this program with the |
23 | -# OpenSSL library under certain conditions as described in each |
24 | -# individual source file, and distribute linked combinations |
25 | -# including the two. |
26 | -# You must obey the GNU General Public License in all respects |
27 | -# for all of the code used other than OpenSSL. If you modify |
28 | -# file(s) with this exception, you may extend this exception to your |
29 | -# version of the file(s), but you are not obligated to do so. If you |
30 | -# do not wish to do so, delete this exception statement from your |
31 | -# version. If you delete this exception statement from all source |
32 | -# files in the program, then also delete it here. |
33 | -"""Tunnel for proxy support.""" |
34 | - |
35 | -import sys |
36 | - |
37 | -from ubuntuone.proxy.tunnel_server import main |
38 | - |
39 | -if __name__ == "__main__": |
40 | - main(sys.argv) |
41 | |
42 | === removed file 'contrib/check-reactor-import' |
43 | --- contrib/check-reactor-import 2016-05-28 23:32:02 +0000 |
44 | +++ contrib/check-reactor-import 1970-01-01 00:00:00 +0000 |
45 | @@ -1,76 +0,0 @@ |
46 | -#! /usr/bin/python |
47 | -# |
48 | -# Copyright (C) 2012 Canonical Ltd. |
49 | -# |
50 | -# This program is free software: you can redistribute it and/or modify it |
51 | -# under the terms of the GNU General Public License version 3, as published |
52 | -# by the Free Software Foundation. |
53 | -# |
54 | -# This program is distributed in the hope that it will be useful, but |
55 | -# WITHOUT ANY WARRANTY; without even the implied warranties of |
56 | -# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
57 | -# PURPOSE. See the GNU General Public License for more details. |
58 | -# |
59 | -# You should have received a copy of the GNU General Public License along |
60 | -# with this program. If not, see <http://www.gnu.org/licenses/>. |
61 | -# |
62 | -# In addition, as a special exception, the copyright holders give |
63 | -# permission to link the code of portions of this program with the |
64 | -# OpenSSL library under certain conditions as described in each |
65 | -# individual source file, and distribute linked combinations |
66 | -# including the two. |
67 | -# You must obey the GNU General Public License in all respects |
68 | -# for all of the code used other than OpenSSL. If you modify |
69 | -# file(s) with this exception, you may extend this exception to your |
70 | -# version of the file(s), but you are not obligated to do so. If you |
71 | -# do not wish to do so, delete this exception statement from your |
72 | -# version. If you delete this exception statement from all source |
73 | -# files in the program, then also delete it here. |
74 | -"""A script that checks for unintended imports of twisted.internet.reactor.""" |
75 | - |
76 | -# NOTE: the goal of this script is to avoid a bug that affects |
77 | -# ubuntuone-control-panel on windows and darwin. Those platforms use |
78 | -# the qt4reactor, and will break if the default reactor is installed |
79 | -# first. This can happen if a module used by control-panel imports reactor. |
80 | -# Only sub-modules that are not used by ubuntuone-control-panel can safely |
81 | -# import reactor at module-level. |
82 | - |
83 | -from __future__ import (unicode_literals, print_function) |
84 | - |
85 | -import __builtin__ |
86 | - |
87 | -import os |
88 | -import sys |
89 | -import traceback |
90 | - |
91 | -sys.path.append(os.path.abspath(os.getcwd())) |
92 | - |
93 | - |
94 | -def fake_import(*args, **kwargs): |
95 | - """A wrapper for __import__ that dies when importing reactor.""" |
96 | - imp_name_base = args[0] |
97 | - |
98 | - if len(args) == 4 and args[3] is not None: |
99 | - imp_names = ["{0}.{1}".format(imp_name_base, sm) |
100 | - for sm in args[3]] |
101 | - else: |
102 | - imp_names = [imp_name_base] |
103 | - |
104 | - for imp_name in imp_names: |
105 | - if 'twisted.internet.reactor' == imp_name: |
106 | - print("ERROR: should not import reactor here:") |
107 | - traceback.print_stack() |
108 | - sys.exit(1) |
109 | - |
110 | - r = real_import(*args, **kwargs) |
111 | - return r |
112 | - |
113 | - |
114 | -if __name__ == '__main__': |
115 | - |
116 | - real_import = __builtin__.__import__ |
117 | - __builtin__.__import__ = fake_import |
118 | - |
119 | - subs = ["", ".tools", ".logger"] |
120 | - for module in ["ubuntuone.platform" + p for p in subs]: |
121 | - m = __import__(module) |
122 | |
123 | === modified file 'contrib/testing/testcase.py' |
124 | --- contrib/testing/testcase.py 2016-09-17 01:06:23 +0000 |
125 | +++ contrib/testing/testcase.py 2018-03-14 21:14:07 +0000 |
126 | @@ -1,7 +1,7 @@ |
127 | # -*- coding: utf-8 -*- |
128 | # |
129 | # Copyright 2009-2015 Canonical Ltd. |
130 | -# Copyright 2015-2016 Chicharreros (https://launchpad.net/~chicharreros) |
131 | +# Copyright 2015-2018 Chicharreros (https://launchpad.net/~chicharreros) |
132 | # |
133 | # This program is free software: you can redistribute it and/or modify it |
134 | # under the terms of the GNU General Public License version 3, as published |
135 | @@ -41,7 +41,7 @@ |
136 | from collections import defaultdict |
137 | from functools import wraps |
138 | |
139 | -from twisted.internet import defer, reactor |
140 | +from twisted.internet import defer |
141 | from twisted.trial.unittest import TestCase as TwistedTestCase |
142 | from ubuntuone.devtools.testcases import skipIfOS |
143 | from zope.interface import implements |
144 | @@ -312,17 +312,6 @@ |
145 | return defer.succeed(True) |
146 | |
147 | |
148 | -class FakeTunnelRunner(object): |
149 | - """A fake proxy.tunnel_client.TunnelRunner.""" |
150 | - |
151 | - def __init__(self, *args): |
152 | - """Fake a proxy tunnel.""" |
153 | - |
154 | - def get_client(self): |
155 | - """Always return the reactor.""" |
156 | - return defer.succeed(reactor) |
157 | - |
158 | - |
159 | class BaseTwistedTestCase(TwistedTestCase): |
160 | """Base TestCase with helper methods to handle temp dir. |
161 | |
162 | @@ -332,7 +321,6 @@ |
163 | makedirs(path): support read-only shares |
164 | """ |
165 | MAX_FILENAME = 32 # some platforms limit lengths of filenames |
166 | - tunnel_runner_class = FakeTunnelRunner |
167 | |
168 | def mktemp(self, name='temp'): |
169 | """ Customized mktemp that accepts an optional name argument. """ |
170 | @@ -439,8 +427,6 @@ |
171 | self.log = logging.getLogger("ubuntuone.SyncDaemon.TEST") |
172 | self.log.info("starting test %s.%s", self.__class__.__name__, |
173 | self._testMethodName) |
174 | - self.patch(action_queue.tunnel_runner, "TunnelRunner", |
175 | - self.tunnel_runner_class) |
176 | |
177 | |
178 | class FakeMainTestCase(BaseTwistedTestCase): |
179 | |
180 | === modified file 'dependencies.txt' |
181 | --- dependencies.txt 2017-01-07 18:51:07 +0000 |
182 | +++ dependencies.txt 2018-03-14 21:14:07 +0000 |
183 | @@ -6,6 +6,4 @@ |
184 | python-gi |
185 | python-protobuf |
186 | python-pyinotify |
187 | -python-qt4-dbus |
188 | -python-qt4reactor |
189 | python-twisted |
190 | |
191 | === modified file 'run-tests' |
192 | --- run-tests 2016-05-29 20:16:26 +0000 |
193 | +++ run-tests 2018-03-14 21:14:07 +0000 |
194 | @@ -1,6 +1,7 @@ |
195 | #! /bin/bash |
196 | # |
197 | # Copyright 2012-2013 Canonical Ltd. |
198 | +# Copyright 2015-2018 Chicharreros (https://launchpad.net/~chicharreros) |
199 | # |
200 | # This program is free software: you can redistribute it and/or modify it |
201 | # under the terms of the GNU General Public License version 3, as published |
202 | @@ -27,8 +28,6 @@ |
203 | # version. If you delete this exception statement from all source |
204 | # files in the program, then also delete it here. |
205 | |
206 | -PROXY_TESTS_PATH="ubuntuone/proxy/tests" |
207 | - |
208 | # Allow alternative python executable via environment variable. This is |
209 | # useful for virtualenv testing. |
210 | PYTHON=${PYTHON:-'python'} |
211 | @@ -47,22 +46,16 @@ |
212 | if [ "$SYSNAME" == "Darwin" ]; then |
213 | IGNORE_FILES="test_linux.py,test_windows.py" |
214 | IGNORE_PATHS="ubuntuone/platform/tests/linux" |
215 | - REACTOR=qt4 |
216 | else |
217 | # Linux |
218 | IGNORE_FILES="test_darwin.py,test_fsevents_daemon.py,test_windows.py" |
219 | IGNORE_PATHS="ubuntuone/platform/tests/windows" |
220 | - REACTOR=gi |
221 | fi |
222 | |
223 | echo "*** Running test suite for ""$MODULE"" ***" |
224 | export SSL_CERTIFICATES_DIR=/etc/ssl/certs |
225 | $PYTHON ./setup.py build |
226 | -u1trial --reactor=$REACTOR -i "$IGNORE_FILES" -p "$IGNORE_PATHS,$PROXY_TESTS_PATH" "$MODULE" |
227 | -echo "*** Running tests for ubuntuone-client-proxy ***" |
228 | -u1trial --reactor=qt4 -i "$IGNORE_FILES" -p "$IGNORE_PATHS" "$PROXY_TESTS_PATH" |
229 | +u1trial -i "$IGNORE_FILES" -p "$IGNORE_PATHS" "$MODULE" |
230 | $PYTHON ./setup.py clean |
231 | rm -rf _trial_temp |
232 | rm -rf build |
233 | - |
234 | -$PYTHON contrib/check-reactor-import |
235 | |
236 | === modified file 'run-tests.bat' |
237 | --- run-tests.bat 2013-06-10 19:27:21 +0000 |
238 | +++ run-tests.bat 2018-03-14 21:14:07 +0000 |
239 | @@ -87,8 +87,6 @@ |
240 | ECHO Performing style checks... |
241 | "%LINTPATH%" |
242 | |
243 | -"%PYTHONEXEPATH%" contrib\check-reactor-import |
244 | - |
245 | :: if pep8 is not present, move to the end |
246 | IF EXIST "%PEP8PATH%" ( |
247 | "%PEP8PATH%" --repeat ubuntuone |
248 | |
249 | === modified file 'setup.py' |
250 | --- setup.py 2016-08-06 20:02:14 +0000 |
251 | +++ setup.py 2018-03-14 21:14:07 +0000 |
252 | @@ -1,6 +1,7 @@ |
253 | #!/usr/bin/python |
254 | # |
255 | # Copyright 2013 Canonical Ltd. |
256 | +# Copyright 2015-2018 Chicharreros (https://launchpad.net/~chicharreros) |
257 | # |
258 | # This program is free software: you can redistribute it and/or modify it |
259 | # under the terms of the GNU General Public License version 3, as published |
260 | @@ -203,7 +204,6 @@ |
261 | ] |
262 | |
263 | libexec_scripts = [ |
264 | - 'bin/ubuntuone-proxy-tunnel', |
265 | 'bin/ubuntuone-syncdaemon', |
266 | ] |
267 | |
268 | |
269 | === removed directory 'ubuntuone/proxy' |
270 | === removed file 'ubuntuone/proxy/__init__.py' |
271 | --- ubuntuone/proxy/__init__.py 2016-05-29 00:50:05 +0000 |
272 | +++ ubuntuone/proxy/__init__.py 1970-01-01 00:00:00 +0000 |
273 | @@ -1,27 +0,0 @@ |
274 | -# Copyright 2012 Canonical Ltd. |
275 | -# |
276 | -# This program is free software: you can redistribute it and/or modify it |
277 | -# under the terms of the GNU General Public License version 3, as published |
278 | -# by the Free Software Foundation. |
279 | -# |
280 | -# This program is distributed in the hope that it will be useful, but |
281 | -# WITHOUT ANY WARRANTY; without even the implied warranties of |
282 | -# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
283 | -# PURPOSE. See the GNU General Public License for more details. |
284 | -# |
285 | -# You should have received a copy of the GNU General Public License along |
286 | -# with this program. If not, see <http://www.gnu.org/licenses/>. |
287 | -# |
288 | -# In addition, as a special exception, the copyright holders give |
289 | -# permission to link the code of portions of this program with the |
290 | -# OpenSSL library under certain conditions as described in each |
291 | -# individual source file, and distribute linked combinations |
292 | -# including the two. |
293 | -# You must obey the GNU General Public License in all respects |
294 | -# for all of the code used other than OpenSSL. If you modify |
295 | -# file(s) with this exception, you may extend this exception to your |
296 | -# version of the file(s), but you are not obligated to do so. If you |
297 | -# do not wish to do so, delete this exception statement from your |
298 | -# version. If you delete this exception statement from all source |
299 | -# files in the program, then also delete it here. |
300 | -"""Proxy support.""" |
301 | |
302 | === removed file 'ubuntuone/proxy/common.py' |
303 | --- ubuntuone/proxy/common.py 2012-04-09 20:08:42 +0000 |
304 | +++ ubuntuone/proxy/common.py 1970-01-01 00:00:00 +0000 |
305 | @@ -1,73 +0,0 @@ |
306 | -# -*- coding: utf-8 -*- |
307 | -# |
308 | -# Copyright 2012 Canonical Ltd. |
309 | -# |
310 | -# This program is free software: you can redistribute it and/or modify it |
311 | -# under the terms of the GNU General Public License version 3, as published |
312 | -# by the Free Software Foundation. |
313 | -# |
314 | -# This program is distributed in the hope that it will be useful, but |
315 | -# WITHOUT ANY WARRANTY; without even the implied warranties of |
316 | -# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
317 | -# PURPOSE. See the GNU General Public License for more details. |
318 | -# |
319 | -# You should have received a copy of the GNU General Public License along |
320 | -# with this program. If not, see <http://www.gnu.org/licenses/>. |
321 | -# |
322 | -# In addition, as a special exception, the copyright holders give |
323 | -# permission to link the code of portions of this program with the |
324 | -# OpenSSL library under certain conditions as described in each |
325 | -# individual source file, and distribute linked combinations |
326 | -# including the two. |
327 | -# You must obey the GNU General Public License in all respects |
328 | -# for all of the code used other than OpenSSL. If you modify |
329 | -# file(s) with this exception, you may extend this exception to your |
330 | -# version of the file(s), but you are not obligated to do so. If you |
331 | -# do not wish to do so, delete this exception statement from your |
332 | -# version. If you delete this exception statement from all source |
333 | -# files in the program, then also delete it here. |
334 | -"""Common classes to the tunnel client and server.""" |
335 | - |
336 | -from twisted.protocols import basic |
337 | - |
338 | -CRLF = "\r\n" |
339 | -TUNNEL_PORT_LABEL = "Tunnel port" |
340 | -TUNNEL_COOKIE_LABEL = "Tunnel cookie" |
341 | -TUNNEL_COOKIE_HEADER = "Proxy-Tunnel-Cookie" |
342 | - |
343 | - |
344 | -class BaseTunnelProtocol(basic.LineReceiver): |
345 | - """CONNECT base protocol for tunnelling connections.""" |
346 | - |
347 | - delimiter = CRLF |
348 | - |
349 | - def __init__(self): |
350 | - """Initialize this protocol.""" |
351 | - self._first_line = True |
352 | - self.received_headers = [] |
353 | - |
354 | - def header_line(self, line): |
355 | - """Handle each header line received.""" |
356 | - key, value = line.split(":", 1) |
357 | - value = value.strip() |
358 | - self.received_headers.append((key, value)) |
359 | - |
360 | - def lineReceived(self, line): |
361 | - """Process a line in the header.""" |
362 | - if self._first_line: |
363 | - self._first_line = False |
364 | - self.handle_first_line(line) |
365 | - else: |
366 | - if line: |
367 | - self.header_line(line) |
368 | - else: |
369 | - self.setRawMode() |
370 | - self.headers_done() |
371 | - |
372 | - def remote_disconnected(self): |
373 | - """The remote end closed the connection.""" |
374 | - self.transport.loseConnection() |
375 | - |
376 | - def format_headers(self, headers): |
377 | - """Format some headers as a few response lines.""" |
378 | - return "".join("%s: %s" % item + CRLF for item in headers.items()) |
379 | |
380 | === removed file 'ubuntuone/proxy/logger.py' |
381 | --- ubuntuone/proxy/logger.py 2012-06-21 18:58:50 +0000 |
382 | +++ ubuntuone/proxy/logger.py 1970-01-01 00:00:00 +0000 |
383 | @@ -1,48 +0,0 @@ |
384 | -# ubuntuone.syncdaemon.logger - logging utilities |
385 | -# |
386 | -# Copyright 2009-2012 Canonical Ltd. |
387 | -# |
388 | -# This program is free software: you can redistribute it and/or modify it |
389 | -# under the terms of the GNU General Public License version 3, as published |
390 | -# by the Free Software Foundation. |
391 | -# |
392 | -# This program is distributed in the hope that it will be useful, but |
393 | -# WITHOUT ANY WARRANTY; without even the implied warranties of |
394 | -# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
395 | -# PURPOSE. See the GNU General Public License for more details. |
396 | -# |
397 | -# You should have received a copy of the GNU General Public License along |
398 | -# with this program. If not, see <http://www.gnu.org/licenses/>. |
399 | -# |
400 | -# In addition, as a special exception, the copyright holders give |
401 | -# permission to link the code of portions of this program with the |
402 | -# OpenSSL library under certain conditions as described in each |
403 | -# individual source file, and distribute linked combinations |
404 | -# including the two. |
405 | -# You must obey the GNU General Public License in all respects |
406 | -# for all of the code used other than OpenSSL. If you modify |
407 | -# file(s) with this exception, you may extend this exception to your |
408 | -# version of the file(s), but you are not obligated to do so. If you |
409 | -# do not wish to do so, delete this exception statement from your |
410 | -# version. If you delete this exception statement from all source |
411 | -# files in the program, then also delete it here. |
412 | -"""SyncDaemon logging utilities and config.""" |
413 | - |
414 | -import logging |
415 | -import os |
416 | - |
417 | -from ubuntuone.logger import ( |
418 | - basic_formatter, |
419 | - CustomRotatingFileHandler, |
420 | -) |
421 | - |
422 | -from ubuntuone.platform.logger import ubuntuone_log_dir |
423 | - |
424 | - |
425 | -LOGFILENAME = os.path.join(ubuntuone_log_dir, 'proxy.log') |
426 | -logger = logging.getLogger("ubuntuone.proxy") |
427 | -logger.setLevel(logging.DEBUG) |
428 | -handler = CustomRotatingFileHandler(filename=LOGFILENAME) |
429 | -handler.setFormatter(basic_formatter) |
430 | -handler.setLevel(logging.DEBUG) |
431 | -logger.addHandler(handler) |
432 | |
433 | === removed directory 'ubuntuone/proxy/tests' |
434 | === removed file 'ubuntuone/proxy/tests/__init__.py' |
435 | --- ubuntuone/proxy/tests/__init__.py 2016-06-01 18:28:19 +0000 |
436 | +++ ubuntuone/proxy/tests/__init__.py 1970-01-01 00:00:00 +0000 |
437 | @@ -1,156 +0,0 @@ |
438 | -# Copyright 2012 Canonical Ltd. |
439 | -# |
440 | -# This program is free software: you can redistribute it and/or modify it |
441 | -# under the terms of the GNU General Public License version 3, as published |
442 | -# by the Free Software Foundation. |
443 | -# |
444 | -# This program is distributed in the hope that it will be useful, but |
445 | -# WITHOUT ANY WARRANTY; without even the implied warranties of |
446 | -# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
447 | -# PURPOSE. See the GNU General Public License for more details. |
448 | -# |
449 | -# You should have received a copy of the GNU General Public License along |
450 | -# with this program. If not, see <http://www.gnu.org/licenses/>. |
451 | -# |
452 | -# In addition, as a special exception, the copyright holders give |
453 | -# permission to link the code of portions of this program with the |
454 | -# OpenSSL library under certain conditions as described in each |
455 | -# individual source file, and distribute linked combinations |
456 | -# including the two. |
457 | -# You must obey the GNU General Public License in all respects |
458 | -# for all of the code used other than OpenSSL. If you modify |
459 | -# file(s) with this exception, you may extend this exception to your |
460 | -# version of the file(s), but you are not obligated to do so. If you |
461 | -# do not wish to do so, delete this exception statement from your |
462 | -# version. If you delete this exception statement from all source |
463 | -# files in the program, then also delete it here. |
464 | -"""Tests for the Magicicada proxy support.""" |
465 | - |
466 | -from os import path |
467 | -from StringIO import StringIO |
468 | - |
469 | -from twisted.application import internet, service |
470 | -from twisted.internet import defer, ssl |
471 | -from twisted.web import http, resource, server |
472 | - |
473 | -SAMPLE_CONTENT = "hello world!" |
474 | -SIMPLERESOURCE = "simpleresource" |
475 | -DUMMY_KEY_FILENAME = "dummy.key" |
476 | -DUMMY_CERT_FILENAME = "dummy.cert" |
477 | -FAKE_COOKIE = "fa:ke:co:ok:ie" |
478 | - |
479 | - |
480 | -class SaveHTTPChannel(http.HTTPChannel): |
481 | - """A save protocol to be used in tests.""" |
482 | - |
483 | - protocolInstance = None |
484 | - |
485 | - def connectionMade(self): |
486 | - """Keep track of the given protocol.""" |
487 | - SaveHTTPChannel.protocolInstance = self |
488 | - http.HTTPChannel.connectionMade(self) |
489 | - |
490 | - |
491 | -class SaveSite(server.Site): |
492 | - """A site that let us know when it's closed.""" |
493 | - |
494 | - protocol = SaveHTTPChannel |
495 | - |
496 | - def __init__(self, *args, **kwargs): |
497 | - """Create a new instance.""" |
498 | - server.Site.__init__(self, *args, **kwargs) |
499 | - self.timeOut = None |
500 | - |
501 | - |
502 | -class BaseMockWebServer(object): |
503 | - """A mock webserver for testing""" |
504 | - |
505 | - def __init__(self): |
506 | - """Start up this instance.""" |
507 | - self.root = self.get_root_resource() |
508 | - self.site = SaveSite(self.root) |
509 | - application = service.Application('web') |
510 | - self.service_collection = service.IServiceCollection(application) |
511 | - self.tcpserver = internet.TCPServer(0, self.site) |
512 | - self.tcpserver.setServiceParent(self.service_collection) |
513 | - self.sslserver = internet.SSLServer(0, self.site, self.get_context()) |
514 | - self.sslserver.setServiceParent(self.service_collection) |
515 | - self.service_collection.startService() |
516 | - |
517 | - def get_dummy_path(self, filename): |
518 | - """Path pointing at the dummy certificate files.""" |
519 | - base_path = path.dirname(__file__) |
520 | - return path.join(base_path, "ssl", filename) |
521 | - |
522 | - def get_context(self): |
523 | - """Return an ssl context.""" |
524 | - key_path = self.get_dummy_path(DUMMY_KEY_FILENAME) |
525 | - cert_path = self.get_dummy_path(DUMMY_CERT_FILENAME) |
526 | - return ssl.DefaultOpenSSLContextFactory(key_path, cert_path) |
527 | - |
528 | - def get_root_resource(self): |
529 | - """Get the root resource with all the children.""" |
530 | - raise NotImplementedError |
531 | - |
532 | - def get_iri(self): |
533 | - """Build the iri for this mock server.""" |
534 | - port_num = self.tcpserver._port.getHost().port |
535 | - return u"http://0.0.0.0:%d/" % port_num |
536 | - |
537 | - def get_ssl_iri(self): |
538 | - """Build the iri for the ssl mock server.""" |
539 | - port_num = self.sslserver._port.getHost().port |
540 | - return u"https://0.0.0.0:%d/" % port_num |
541 | - |
542 | - def stop(self): |
543 | - """Shut it down.""" |
544 | - if self.site.protocol.protocolInstance: |
545 | - self.site.protocol.protocolInstance.timeoutConnection() |
546 | - return self.service_collection.stopService() |
547 | - |
548 | - |
549 | -class SimpleResource(resource.Resource): |
550 | - """A simple web resource.""" |
551 | - |
552 | - def __init__(self): |
553 | - """Initialize this mock resource.""" |
554 | - resource.Resource.__init__(self) |
555 | - self.rendered = defer.Deferred() |
556 | - |
557 | - def render_GET(self, request): |
558 | - """Make a bit of html out of the resource's content.""" |
559 | - if not self.rendered.called: |
560 | - self.rendered.callback(None) |
561 | - return SAMPLE_CONTENT |
562 | - |
563 | - |
564 | -class MockWebServer(BaseMockWebServer): |
565 | - """A mock webserver.""" |
566 | - |
567 | - def __init__(self): |
568 | - """Initialize this mock server.""" |
569 | - self.simple_resource = SimpleResource() |
570 | - super(MockWebServer, self).__init__() |
571 | - |
572 | - def get_root_resource(self): |
573 | - """Get the root resource with all the children.""" |
574 | - root = resource.Resource() |
575 | - root.putChild(SIMPLERESOURCE, self.simple_resource) |
576 | - return root |
577 | - |
578 | - |
579 | -class FakeTransport(StringIO): |
580 | - """A fake transport that stores everything written to it.""" |
581 | - |
582 | - connected = True |
583 | - disconnecting = False |
584 | - cookie = None |
585 | - |
586 | - def loseConnection(self): |
587 | - """Mark the connection as lost.""" |
588 | - self.connected = False |
589 | - self.disconnecting = True |
590 | - |
591 | - def getPeer(self): |
592 | - """Return the peer IAddress.""" |
593 | - return None |
594 | |
595 | === removed directory 'ubuntuone/proxy/tests/ssl' |
596 | === removed file 'ubuntuone/proxy/tests/ssl/dummy.cert' |
597 | --- ubuntuone/proxy/tests/ssl/dummy.cert 2012-02-23 23:58:33 +0000 |
598 | +++ ubuntuone/proxy/tests/ssl/dummy.cert 1970-01-01 00:00:00 +0000 |
599 | @@ -1,19 +0,0 @@ |
600 | ------BEGIN CERTIFICATE----- |
601 | -MIIDEDCCAnmgAwIBAgIJAM/bIJ77awBCMA0GCSqGSIb3DQEBBQUAMIGgMQswCQYD |
602 | -VQQGEwJBUjETMBEGA1UECAwKRmFrZSBTdGF0ZTESMBAGA1UEBwwJRmFrZSBDaXR5 |
603 | -MRUwEwYDVQQKDAxGYWtlIENvbXBhbnkxFjAUBgNVBAsMDUZha2UgRGl2aXNpb24x |
604 | -EjAQBgNVBAMMCUZha2UgTmFtZTElMCMGCSqGSIb3DQEJARYWZmFrZUBlbWFpbC5h |
605 | -ZGRyZXNzLm5vdDAeFw0xMjAyMjIxOTI0MjBaFw0yMjAyMjMxOTI0MjBaMIGgMQsw |
606 | -CQYDVQQGEwJBUjETMBEGA1UECAwKRmFrZSBTdGF0ZTESMBAGA1UEBwwJRmFrZSBD |
607 | -aXR5MRUwEwYDVQQKDAxGYWtlIENvbXBhbnkxFjAUBgNVBAsMDUZha2UgRGl2aXNp |
608 | -b24xEjAQBgNVBAMMCUZha2UgTmFtZTElMCMGCSqGSIb3DQEJARYWZmFrZUBlbWFp |
609 | -bC5hZGRyZXNzLm5vdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0hbliGty |
610 | -HwfZixU609UHBQdbfO+oObrPIrIawWX5FxD6KhX4ei23idmpyYEcXLK4ivNlT4dW |
611 | -27bvhtpf6/FBbu9e1YdwcdDNoXajr9Ia4NZJyANgo9b5UIsnyTc45NlnpZgRg5zc |
612 | -Oz7Vwwr4qf6r1ljK/I2mAO7rlpH5Ak9J+RkCAwEAAaNQME4wHQYDVR0OBBYEFLwr |
613 | -ps/JLNcfpSuuylMnkvImVvkgMB8GA1UdIwQYMBaAFLwrps/JLNcfpSuuylMnkvIm |
614 | -VvkgMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAWYDBAr0MgpnBxIne |
615 | -WRz8MX0/c7IqrEuZCYMSGnU7PoX3GdNk1Lkif1ufELKSoG8jY16CDgEl26GPxA1k |
616 | -Tho7MWSLikLbuQYJs2saF9by0Y/Mrau0auxEnpHZ7pkybeKFnrIqiNKvTVMnjo5T |
617 | -FMET5qEOKKvp9IOnezCYX1nYXyY= |
618 | ------END CERTIFICATE----- |
619 | |
620 | === removed file 'ubuntuone/proxy/tests/ssl/dummy.key' |
621 | --- ubuntuone/proxy/tests/ssl/dummy.key 2012-02-23 23:58:33 +0000 |
622 | +++ ubuntuone/proxy/tests/ssl/dummy.key 1970-01-01 00:00:00 +0000 |
623 | @@ -1,16 +0,0 @@ |
624 | ------BEGIN PRIVATE KEY----- |
625 | -MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBANIW5Yhrch8H2YsV |
626 | -OtPVBwUHW3zvqDm6zyKyGsFl+RcQ+ioV+Hott4nZqcmBHFyyuIrzZU+HVtu274ba |
627 | -X+vxQW7vXtWHcHHQzaF2o6/SGuDWScgDYKPW+VCLJ8k3OOTZZ6WYEYOc3Ds+1cMK |
628 | -+Kn+q9ZYyvyNpgDu65aR+QJPSfkZAgMBAAECgYBSxFh7TTExjmsjAyMg700LqyFc |
629 | -8CHLVJBkL9ygkqb2cmbMC8nPgJFNSqY8T5Q35OUVQNyJ31zVxJVLAF9H2c0Xy48K |
630 | -IkbS/hntyqlJYK1yfTbTHkDiweToE3Lm+55Do1TX04AyvBrwA1O/jNGi4xIlUEAy |
631 | -1Bs8MrJ1E/j/XDn9/QJBAOuhPTgG3F7bKuBrQzv98CvC5o2Txf3vLY8nL8V24b3l |
632 | -XgqzkDLhUxReBmmkGxZfKAju3+gXFvGGpbP7V8zShg8CQQDkQGs7kArFq/KR/GCh |
633 | -CAmJaDWy4LJkSqzDHoJbTrS7YuqN6X6mW1xPRnWpYSxae38fJsCpG3Vq8Mv1Zl32 |
634 | -VPZXAkEAsAeE9JYri7GwFngLgoXzJr4z/xCmmU5VetyLk7l8a6Eu4E/FKj2rE0wq |
635 | -/kDa+5ubDRFntLuLKGSu5gafUST1gQJABhdmBTfp4a6eEaFPntyNDJq4XCa8/Ao2 |
636 | -JBrrVa57Ckkwg0sI8z2a8A6sUzHhsiR7lwQ8vgaakpkMiGcL+Of5jwJAA/qX3PW+ |
637 | -9JXbjWxpgh7FHnZJNRZ8xSe47REGA7qS/nIlV9iRuf/9M+k3A5VqitfFxrjPwSyI |
638 | -rvKTYkk13dL4hg== |
639 | ------END PRIVATE KEY----- |
640 | |
641 | === removed file 'ubuntuone/proxy/tests/test_tunnel_client.py' |
642 | --- ubuntuone/proxy/tests/test_tunnel_client.py 2016-06-01 18:28:19 +0000 |
643 | +++ ubuntuone/proxy/tests/test_tunnel_client.py 1970-01-01 00:00:00 +0000 |
644 | @@ -1,225 +0,0 @@ |
645 | -# -*- coding: utf-8 -*- |
646 | -# |
647 | -# Copyright 2012 Canonical Ltd. |
648 | -# |
649 | -# This program is free software: you can redistribute it and/or modify it |
650 | -# under the terms of the GNU General Public License version 3, as published |
651 | -# by the Free Software Foundation. |
652 | -# |
653 | -# This program is distributed in the hope that it will be useful, but |
654 | -# WITHOUT ANY WARRANTY; without even the implied warranties of |
655 | -# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
656 | -# PURPOSE. See the GNU General Public License for more details. |
657 | -# |
658 | -# You should have received a copy of the GNU General Public License along |
659 | -# with this program. If not, see <http://www.gnu.org/licenses/>. |
660 | -# |
661 | -# In addition, as a special exception, the copyright holders give |
662 | -# permission to link the code of portions of this program with the |
663 | -# OpenSSL library under certain conditions as described in each |
664 | -# individual source file, and distribute linked combinations |
665 | -# including the two. |
666 | -# You must obey the GNU General Public License in all respects |
667 | -# for all of the code used other than OpenSSL. If you modify |
668 | -# file(s) with this exception, you may extend this exception to your |
669 | -# version of the file(s), but you are not obligated to do so. If you |
670 | -# do not wish to do so, delete this exception statement from your |
671 | -# version. If you delete this exception statement from all source |
672 | -# files in the program, then also delete it here. |
673 | -"""Tests for the proxy tunnel.""" |
674 | - |
675 | -from twisted.internet import defer, protocol, ssl |
676 | -from twisted.trial.unittest import TestCase |
677 | -from twisted.web import client |
678 | - |
679 | -from ubuntuone.devtools.testcases.squid import SquidTestCase |
680 | - |
681 | -from ubuntuone.proxy.tests import ( |
682 | - FakeTransport, |
683 | - FAKE_COOKIE, |
684 | - MockWebServer, |
685 | - SAMPLE_CONTENT, |
686 | - SIMPLERESOURCE, |
687 | -) |
688 | -from ubuntuone.proxy import tunnel_client |
689 | -from ubuntuone.proxy.tunnel_client import CRLF, TunnelClient |
690 | -from ubuntuone.proxy.tunnel_server import TunnelServer |
691 | - |
692 | - |
693 | -FAKE_HEADER = ( |
694 | - "HTTP/1.0 200 Connected!" + CRLF + |
695 | - "Header1: value1" + CRLF + |
696 | - "Header2: value2" + CRLF + |
697 | - CRLF |
698 | -) |
699 | - |
700 | - |
701 | -class SavingProtocol(protocol.Protocol): |
702 | - """A protocol that saves all that it receives.""" |
703 | - |
704 | - def __init__(self): |
705 | - """Initialize this protocol.""" |
706 | - self.saved_data = None |
707 | - |
708 | - def connectionMade(self): |
709 | - """The connection was made, start saving.""" |
710 | - self.saved_data = [] |
711 | - |
712 | - def dataReceived(self, data): |
713 | - """Save the data received.""" |
714 | - self.saved_data.append(data) |
715 | - |
716 | - @property |
717 | - def content(self): |
718 | - """All the content so far.""" |
719 | - return "".join(self.saved_data) |
720 | - |
721 | - |
722 | -class TunnelClientProtocolTestCase(TestCase): |
723 | - """Tests for the client side tunnel protocol.""" |
724 | - |
725 | - timeout = 3 |
726 | - |
727 | - @defer.inlineCallbacks |
728 | - def setUp(self): |
729 | - """Initialize this testcase.""" |
730 | - yield super(TunnelClientProtocolTestCase, self).setUp() |
731 | - self.host, self.port = "9.9.9.9", 8765 |
732 | - fake_addr = object() |
733 | - self.cookie = FAKE_COOKIE |
734 | - self.other_proto = SavingProtocol() |
735 | - other_factory = protocol.ClientFactory() |
736 | - other_factory.buildProtocol = lambda _addr: self.other_proto |
737 | - tunnel_client_factory = tunnel_client.TunnelClientFactory( |
738 | - self.host, self.port, other_factory, self.cookie) |
739 | - tunnel_client_proto = tunnel_client_factory.buildProtocol(fake_addr) |
740 | - tunnel_client_proto.transport = FakeTransport() |
741 | - tunnel_client_proto.connectionMade() |
742 | - self.tunnel_client_proto = tunnel_client_proto |
743 | - |
744 | - def test_sends_connect_request(self): |
745 | - """Sends the expected CONNECT request.""" |
746 | - expected = tunnel_client.METHOD_LINE % (self.host, self.port) |
747 | - written = self.tunnel_client_proto.transport.getvalue() |
748 | - first_line = written.split(CRLF)[0] |
749 | - self.assertEqual(first_line + CRLF, expected) |
750 | - self.assertTrue(written.endswith(CRLF * 2), |
751 | - "Ends with a double CRLF") |
752 | - |
753 | - def test_sends_cookie_header(self): |
754 | - """Sends the expected cookie header.""" |
755 | - expected = "%s: %s" % (tunnel_client.TUNNEL_COOKIE_HEADER, self.cookie) |
756 | - written = self.tunnel_client_proto.transport.getvalue() |
757 | - headers = written.split(CRLF)[1:] |
758 | - self.assertIn(expected, headers) |
759 | - |
760 | - def test_handles_successful_connection(self): |
761 | - """A successful connection is handled.""" |
762 | - self.tunnel_client_proto.dataReceived(FAKE_HEADER) |
763 | - self.assertEqual(self.tunnel_client_proto.status_code, "200") |
764 | - |
765 | - def test_protocol_is_switched(self): |
766 | - """The protocol is switched after the headers are received.""" |
767 | - expected = (SAMPLE_CONTENT + CRLF) * 2 |
768 | - self.tunnel_client_proto.dataReceived(FAKE_HEADER + SAMPLE_CONTENT) |
769 | - self.other_proto.dataReceived(CRLF + SAMPLE_CONTENT + CRLF) |
770 | - self.assertEqual(self.other_proto.content, expected) |
771 | - |
772 | - |
773 | -class FakeOtherFactory(object): |
774 | - """A fake factory.""" |
775 | - |
776 | - def __init__(self): |
777 | - """Initialize this fake.""" |
778 | - self.started_called = None |
779 | - self.failed_called = None |
780 | - self.lost_called = None |
781 | - |
782 | - def startedConnecting(self, *args): |
783 | - """Store the call.""" |
784 | - self.started_called = args |
785 | - |
786 | - def clientConnectionFailed(self, *args): |
787 | - """Store the call.""" |
788 | - self.failed_called = args |
789 | - |
790 | - def clientConnectionLost(self, *args): |
791 | - """Store the call.""" |
792 | - self.lost_called = args |
793 | - |
794 | - |
795 | -class TunnelClientFactoryTestCase(TestCase): |
796 | - """Tests for the TunnelClientFactory.""" |
797 | - |
798 | - def test_forwards_started(self): |
799 | - """The factory forwards the startedConnecting call.""" |
800 | - fake_other_factory = FakeOtherFactory() |
801 | - tcf = tunnel_client.TunnelClientFactory(None, None, fake_other_factory, |
802 | - FAKE_COOKIE) |
803 | - fake_connector = object() |
804 | - tcf.startedConnecting(fake_connector) |
805 | - self.assertEqual(fake_other_factory.started_called, (fake_connector,)) |
806 | - |
807 | - def test_forwards_failed(self): |
808 | - """The factory forwards the clientConnectionFailed call.""" |
809 | - fake_reason = object() |
810 | - fake_other_factory = FakeOtherFactory() |
811 | - tcf = tunnel_client.TunnelClientFactory(None, None, fake_other_factory, |
812 | - FAKE_COOKIE) |
813 | - fake_connector = object() |
814 | - tcf.clientConnectionFailed(fake_connector, fake_reason) |
815 | - self.assertEqual(fake_other_factory.failed_called, |
816 | - (fake_connector, fake_reason)) |
817 | - |
818 | - def test_forwards_lost(self): |
819 | - """The factory forwards the clientConnectionLost call.""" |
820 | - fake_reason = object() |
821 | - fake_other_factory = FakeOtherFactory() |
822 | - tcf = tunnel_client.TunnelClientFactory(None, None, fake_other_factory, |
823 | - FAKE_COOKIE) |
824 | - fake_connector = object() |
825 | - tcf.clientConnectionLost(fake_connector, fake_reason) |
826 | - self.assertEqual(fake_other_factory.lost_called, |
827 | - (fake_connector, fake_reason)) |
828 | - |
829 | - |
830 | -class TunnelClientTestCase(SquidTestCase): |
831 | - """Test the client for the tunnel.""" |
832 | - |
833 | - timeout = 3 |
834 | - |
835 | - @defer.inlineCallbacks |
836 | - def setUp(self): |
837 | - """Initialize this testcase.""" |
838 | - yield super(TunnelClientTestCase, self).setUp() |
839 | - self.ws = MockWebServer() |
840 | - self.addCleanup(self.ws.stop) |
841 | - self.dest_url = self.ws.get_iri().encode("utf-8") + SIMPLERESOURCE |
842 | - self.dest_ssl_url = ( |
843 | - self.ws.get_ssl_iri().encode("utf-8") + SIMPLERESOURCE) |
844 | - self.cookie = FAKE_COOKIE |
845 | - self.tunnel_server = TunnelServer(self.cookie) |
846 | - self.addCleanup(self.tunnel_server.shutdown) |
847 | - |
848 | - @defer.inlineCallbacks |
849 | - def test_connects_right(self): |
850 | - """Uses the CONNECT method on the tunnel.""" |
851 | - tunnel_client = TunnelClient("0.0.0.0", self.tunnel_server.port, |
852 | - self.cookie) |
853 | - factory = client.HTTPClientFactory(self.dest_url) |
854 | - scheme, host, port, path = client._parse(self.dest_url) |
855 | - tunnel_client.connectTCP(host, port, factory) |
856 | - result = yield factory.deferred |
857 | - self.assertEqual(result, SAMPLE_CONTENT) |
858 | - |
859 | - @defer.inlineCallbacks |
860 | - def test_starts_tls_connection(self): |
861 | - """TLS is started after connecting; control passed to the client.""" |
862 | - tunnel_client = TunnelClient( |
863 | - "0.0.0.0", self.tunnel_server.port, self.cookie) |
864 | - factory = client.HTTPClientFactory(self.dest_ssl_url) |
865 | - scheme, host, port, path = client._parse(self.dest_ssl_url) |
866 | - context_factory = ssl.ClientContextFactory() |
867 | - tunnel_client.connectSSL(host, port, factory, context_factory) |
868 | - result = yield factory.deferred |
869 | - self.assertEqual(result, SAMPLE_CONTENT) |
870 | |
871 | === removed file 'ubuntuone/proxy/tests/test_tunnel_server.py' |
872 | --- ubuntuone/proxy/tests/test_tunnel_server.py 2016-06-01 18:28:19 +0000 |
873 | +++ ubuntuone/proxy/tests/test_tunnel_server.py 1970-01-01 00:00:00 +0000 |
874 | @@ -1,743 +0,0 @@ |
875 | -# -*- coding: utf-8 -*- |
876 | -# |
877 | -# Copyright 2012-2013 Canonical Ltd. |
878 | -# |
879 | -# This program is free software: you can redistribute it and/or modify it |
880 | -# under the terms of the GNU General Public License version 3, as published |
881 | -# by the Free Software Foundation. |
882 | -# |
883 | -# This program is distributed in the hope that it will be useful, but |
884 | -# WITHOUT ANY WARRANTY; without even the implied warranties of |
885 | -# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
886 | -# PURPOSE. See the GNU General Public License for more details. |
887 | -# |
888 | -# You should have received a copy of the GNU General Public License along |
889 | -# with this program. If not, see <http://www.gnu.org/licenses/>. |
890 | -# |
891 | -# In addition, as a special exception, the copyright holders give |
892 | -# permission to link the code of portions of this program with the |
893 | -# OpenSSL library under certain conditions as described in each |
894 | -# individual source file, and distribute linked combinations |
895 | -# including the two. |
896 | -# You must obey the GNU General Public License in all respects |
897 | -# for all of the code used other than OpenSSL. If you modify |
898 | -# file(s) with this exception, you may extend this exception to your |
899 | -# version of the file(s), but you are not obligated to do so. If you |
900 | -# do not wish to do so, delete this exception statement from your |
901 | -# version. If you delete this exception statement from all source |
902 | -# files in the program, then also delete it here. |
903 | -"""Tests for the proxy tunnel.""" |
904 | - |
905 | -from StringIO import StringIO |
906 | -from urlparse import urlparse |
907 | - |
908 | -from twisted.internet import defer, protocol, reactor |
909 | -from twisted.trial.unittest import TestCase |
910 | -from PyQt4.QtCore import QCoreApplication |
911 | -from PyQt4.QtNetwork import QAuthenticator |
912 | -from ubuntuone.devtools.testcases import skipIfOS |
913 | -from ubuntuone.devtools.testcases.squid import SquidTestCase |
914 | - |
915 | -from ubuntuone.proxy.tests import ( |
916 | - FakeTransport, |
917 | - FAKE_COOKIE, |
918 | - MockWebServer, |
919 | - SAMPLE_CONTENT, |
920 | - SIMPLERESOURCE, |
921 | -) |
922 | -from ubuntuone.proxy import tunnel_server |
923 | -from ubuntuone.proxy.tunnel_server import CRLF |
924 | - |
925 | - |
926 | -FAKE_SESSION_TEMPLATE = ( |
927 | - "CONNECT %s HTTP/1.0" + CRLF + |
928 | - "Header1: value1" + CRLF + |
929 | - "Header2: value2" + CRLF + |
930 | - tunnel_server.TUNNEL_COOKIE_HEADER + ": %s" + CRLF + |
931 | - CRLF + |
932 | - "GET %s HTTP/1.0" + CRLF + CRLF |
933 | -) |
934 | - |
935 | -FAKE_SETTINGS = { |
936 | - "http": { |
937 | - "host": "myhost", |
938 | - "port": 8888, |
939 | - } |
940 | -} |
941 | - |
942 | -FAKE_AUTH_SETTINGS = { |
943 | - "http": { |
944 | - "host": "myhost", |
945 | - "port": 8888, |
946 | - "username": "fake_user", |
947 | - "password": "fake_password", |
948 | - } |
949 | -} |
950 | - |
951 | -SAMPLE_HOST = "samplehost.com" |
952 | -SAMPLE_PORT = 443 |
953 | - |
954 | -FAKE_CREDS = { |
955 | - "username": "rhea", |
956 | - "password": "caracolcaracola", |
957 | -} |
958 | - |
959 | - |
960 | -class DisconnectingProtocol(protocol.Protocol): |
961 | - """A protocol that just disconnects.""" |
962 | - |
963 | - def connectionMade(self): |
964 | - """Upon connecting: just disconnect.""" |
965 | - self.transport.loseConnection() |
966 | - |
967 | - |
968 | -class DisconnectingClientFactory(protocol.ClientFactory): |
969 | - """A factory that fires a deferred on connection.""" |
970 | - |
971 | - def __init__(self): |
972 | - """Initialize this instance.""" |
973 | - self.connected = defer.Deferred() |
974 | - |
975 | - def buildProtocol(self, addr): |
976 | - """The connection was made.""" |
977 | - proto = DisconnectingProtocol() |
978 | - if not self.connected.called: |
979 | - self.connected.callback(proto) |
980 | - return proto |
981 | - |
982 | - |
983 | -class FakeProtocol(protocol.Protocol): |
984 | - """A protocol that forwards some data.""" |
985 | - |
986 | - def __init__(self, factory, data): |
987 | - """Initialize this fake.""" |
988 | - self.factory = factory |
989 | - self.data = data |
990 | - self.received_data = [] |
991 | - |
992 | - def connectionMade(self): |
993 | - """Upon connection: send the stored data.""" |
994 | - self.transport.write(self.data) |
995 | - |
996 | - def dataReceived(self, data): |
997 | - """Some data was received.""" |
998 | - self.received_data.append(data) |
999 | - |
1000 | - def connectionLost(self, reason): |
1001 | - """The connection was lost, return the response.""" |
1002 | - response = "".join(self.received_data) |
1003 | - if not self.factory.response.called: |
1004 | - self.factory.response.callback(response) |
1005 | - |
1006 | - |
1007 | -class FakeClientFactory(protocol.ClientFactory): |
1008 | - """A factory that forwards some data to the protocol.""" |
1009 | - |
1010 | - def __init__(self, data): |
1011 | - """Initialize this fake.""" |
1012 | - self.data = data |
1013 | - self.response = defer.Deferred() |
1014 | - |
1015 | - def buildProtocol(self, addr): |
1016 | - """The connection was made.""" |
1017 | - return FakeProtocol(self, self.data) |
1018 | - |
1019 | - |
1020 | -class TunnelIntegrationTestCase(SquidTestCase): |
1021 | - """Basic tunnel integration tests.""" |
1022 | - |
1023 | - timeout = 3 |
1024 | - |
1025 | - @defer.inlineCallbacks |
1026 | - def setUp(self): |
1027 | - """Initialize this testcase.""" |
1028 | - yield super(TunnelIntegrationTestCase, self).setUp() |
1029 | - self.ws = MockWebServer() |
1030 | - self.addCleanup(self.ws.stop) |
1031 | - self.dest_url = self.ws.get_iri().encode("utf-8") + SIMPLERESOURCE |
1032 | - self.cookie = FAKE_COOKIE |
1033 | - self.tunnel_server = tunnel_server.TunnelServer(self.cookie) |
1034 | - self.addCleanup(self.tunnel_server.shutdown) |
1035 | - |
1036 | - def test_init(self): |
1037 | - """The tunnel is started.""" |
1038 | - self.assertNotEqual(self.tunnel_server.port, 0) |
1039 | - |
1040 | - @defer.inlineCallbacks |
1041 | - def test_accepts_connections(self): |
1042 | - """The tunnel accepts incoming connections.""" |
1043 | - ncf = DisconnectingClientFactory() |
1044 | - reactor.connectTCP("0.0.0.0", self.tunnel_server.port, ncf) |
1045 | - yield ncf.connected |
1046 | - |
1047 | - @defer.inlineCallbacks |
1048 | - def test_complete_connection(self): |
1049 | - """Test from the tunnel server down.""" |
1050 | - url = urlparse(self.dest_url) |
1051 | - fake_session = FAKE_SESSION_TEMPLATE % ( |
1052 | - url.netloc, self.cookie, url.path) |
1053 | - client = FakeClientFactory(fake_session) |
1054 | - reactor.connectTCP("0.0.0.0", self.tunnel_server.port, client) |
1055 | - response = yield client.response |
1056 | - self.assertIn(SAMPLE_CONTENT, response) |
1057 | - |
1058 | - |
1059 | -class FakeClient(object): |
1060 | - """A fake destination client.""" |
1061 | - |
1062 | - protocol = None |
1063 | - connection_result = defer.succeed(True) |
1064 | - credentials = None |
1065 | - check_credentials = False |
1066 | - proxy_domain = None |
1067 | - |
1068 | - def connect(self, hostport): |
1069 | - """Establish a connection with the other end.""" |
1070 | - if (self.check_credentials and |
1071 | - self.protocol.proxy_credentials != FAKE_CREDS): |
1072 | - self.proxy_domain = "fake domain" |
1073 | - return defer.fail(tunnel_server.ProxyAuthenticationError()) |
1074 | - return self.connection_result |
1075 | - |
1076 | - def write(self, data): |
1077 | - """Write some data to the other end.""" |
1078 | - if data == 'GET /simpleresource HTTP/1.0\r\n\r\n': |
1079 | - self.protocol.transport.write(SAMPLE_CONTENT) |
1080 | - |
1081 | - def stop(self): |
1082 | - """Stop this fake client.""" |
1083 | - |
1084 | - def close(self): |
1085 | - """Reset this client.""" |
1086 | - |
1087 | - |
1088 | -class ServerTunnelProtocolTestCase(SquidTestCase): |
1089 | - """Tests for the ServerTunnelProtocol.""" |
1090 | - |
1091 | - @defer.inlineCallbacks |
1092 | - def setUp(self): |
1093 | - """Initialize this test instance.""" |
1094 | - yield super(ServerTunnelProtocolTestCase, self).setUp() |
1095 | - self.ws = MockWebServer() |
1096 | - self.addCleanup(self.ws.stop) |
1097 | - self.dest_url = self.ws.get_iri().encode("utf-8") + SIMPLERESOURCE |
1098 | - self.transport = FakeTransport() |
1099 | - self.transport.cookie = FAKE_COOKIE |
1100 | - self.fake_client = FakeClient() |
1101 | - self.proto = tunnel_server.ServerTunnelProtocol( |
1102 | - lambda _: self.fake_client) |
1103 | - self.fake_client.protocol = self.proto |
1104 | - self.proto.transport = self.transport |
1105 | - self.cookie_line = "%s: %s" % (tunnel_server.TUNNEL_COOKIE_HEADER, |
1106 | - FAKE_COOKIE) |
1107 | - |
1108 | - def test_broken_request(self): |
1109 | - """Broken request.""" |
1110 | - self.proto.dataReceived("Broken request." + CRLF) |
1111 | - self.assertTrue(self.transport.getvalue().startswith("HTTP/1.0 400 "), |
1112 | - "A broken request must fail.") |
1113 | - |
1114 | - def test_wrong_method(self): |
1115 | - """Wrong method.""" |
1116 | - self.proto.dataReceived("GET http://slashdot.org HTTP/1.0" + CRLF) |
1117 | - self.assertTrue(self.transport.getvalue().startswith("HTTP/1.0 405 "), |
1118 | - "Using a wrong method fails.") |
1119 | - |
1120 | - def test_invalid_http_version(self): |
1121 | - """Invalid HTTP version.""" |
1122 | - self.proto.dataReceived("CONNECT 127.0.0.1:9999 HTTP/1.1" + CRLF) |
1123 | - self.assertTrue(self.transport.getvalue().startswith("HTTP/1.0 505 "), |
1124 | - "Invalid http version is not allowed.") |
1125 | - |
1126 | - def test_connection_is_established(self): |
1127 | - """The response code is sent.""" |
1128 | - expected = "HTTP/1.0 200 Proxy connection established" + CRLF |
1129 | - self.proto.dataReceived("CONNECT 127.0.0.1:9999 HTTP/1.0" + CRLF + |
1130 | - self.cookie_line + CRLF * 2) |
1131 | - self.assertTrue(self.transport.getvalue().startswith(expected), |
1132 | - "First line must be the response status") |
1133 | - |
1134 | - def test_connection_fails(self): |
1135 | - """The connection to the other end fails, and it's handled.""" |
1136 | - error = tunnel_server.ConnectionError() |
1137 | - self.patch(self.fake_client, "connection_result", defer.fail(error)) |
1138 | - expected = "HTTP/1.0 500 Connection error" + CRLF |
1139 | - self.proto.dataReceived("CONNECT 127.0.0.1:9999 HTTP/1.0" + CRLF + |
1140 | - self.cookie_line + CRLF * 2) |
1141 | - self.assertTrue(self.transport.getvalue().startswith(expected), |
1142 | - "The connection should fail at this point.") |
1143 | - |
1144 | - def test_headers_stored(self): |
1145 | - """The request headers are stored.""" |
1146 | - expected = [ |
1147 | - ("Header1", "value1"), |
1148 | - ("Header2", "value2"), |
1149 | - ] |
1150 | - self.proto.dataReceived("CONNECT 127.0.0.1:9999 HTTP/1.0" + CRLF + |
1151 | - "Header1: value1" + CRLF + |
1152 | - "Header2: value2" + CRLF + CRLF) |
1153 | - self.assertEqual(self.proto.received_headers, expected) |
1154 | - |
1155 | - def test_cookie_header_present(self): |
1156 | - """The cookie header must be present.""" |
1157 | - self.proto.received_headers = [ |
1158 | - (tunnel_server.TUNNEL_COOKIE_HEADER, FAKE_COOKIE), |
1159 | - ] |
1160 | - self.proto.verify_cookie() |
1161 | - |
1162 | - def test_cookie_header_absent(self): |
1163 | - """The tunnel should refuse connections without the cookie.""" |
1164 | - self.proto.received_headers = [] |
1165 | - exception = self.assertRaises(tunnel_server.ConnectionError, |
1166 | - self.proto.verify_cookie) |
1167 | - self.assertEqual(exception.code, 418) |
1168 | - |
1169 | - def test_successful_connect(self): |
1170 | - """A successful connect thru the tunnel.""" |
1171 | - url = urlparse(self.dest_url) |
1172 | - data = FAKE_SESSION_TEMPLATE % (url.netloc, self.transport.cookie, |
1173 | - url.path) |
1174 | - self.proto.dataReceived(data) |
1175 | - lines = self.transport.getvalue().split(CRLF) |
1176 | - self.assertEqual(lines[-1], SAMPLE_CONTENT) |
1177 | - |
1178 | - def test_header_split(self): |
1179 | - """Test a header with many colons.""" |
1180 | - self.proto.header_line("key: host:port") |
1181 | - self.assertIn("key", dict(self.proto.received_headers)) |
1182 | - |
1183 | - @defer.inlineCallbacks |
1184 | - def test_keyring_credentials_are_retried(self): |
1185 | - """Wrong credentials are retried with values from keyring.""" |
1186 | - self.fake_client.check_credentials = True |
1187 | - self.patch(self.proto, "verify_cookie", lambda: None) |
1188 | - self.patch(self.proto, "error_response", |
1189 | - lambda code, desc: self.fail(desc)) |
1190 | - self.proto.proxy_domain = "xxx" |
1191 | - self.patch(tunnel_server.Keyring, "get_credentials", |
1192 | - lambda _, domain: defer.succeed(FAKE_CREDS)) |
1193 | - yield self.proto.headers_done() |
1194 | - |
1195 | - def test_creds_are_not_logged(self): |
1196 | - """The proxy credentials are not logged.""" |
1197 | - log = [] |
1198 | - self.patch(tunnel_server.logger, "info", |
1199 | - lambda text, *args: log.append(text % args)) |
1200 | - proxy = tunnel_server.build_proxy(FAKE_AUTH_SETTINGS) |
1201 | - authenticator = QAuthenticator() |
1202 | - username = FAKE_AUTH_SETTINGS["http"]["username"] |
1203 | - password = FAKE_AUTH_SETTINGS["http"]["password"] |
1204 | - self.proto.proxy_credentials = { |
1205 | - "username": username, |
1206 | - "password": password, |
1207 | - } |
1208 | - self.proto.proxy_domain = proxy.hostName() |
1209 | - |
1210 | - self.proto.proxy_auth_required(proxy, authenticator) |
1211 | - |
1212 | - for line in log: |
1213 | - self.assertNotIn(username, line) |
1214 | - self.assertNotIn(password, line) |
1215 | - |
1216 | - |
1217 | -class FakeServerTunnelProtocol(object): |
1218 | - """A fake ServerTunnelProtocol.""" |
1219 | - |
1220 | - def __init__(self): |
1221 | - """Initialize this fake tunnel.""" |
1222 | - self.response_received = defer.Deferred() |
1223 | - self.proxy_credentials = None |
1224 | - |
1225 | - def response_data_received(self, data): |
1226 | - """Fire the response deferred.""" |
1227 | - if not self.response_received.called: |
1228 | - self.response_received.callback(data) |
1229 | - |
1230 | - def remote_disconnected(self): |
1231 | - """The remote server disconnected.""" |
1232 | - |
1233 | - def proxy_auth_required(self, proxy, authenticator): |
1234 | - """Proxy credentials are needed.""" |
1235 | - if self.proxy_credentials: |
1236 | - authenticator.setUser(self.proxy_credentials["username"]) |
1237 | - authenticator.setPassword(self.proxy_credentials["password"]) |
1238 | - |
1239 | - |
1240 | -class BuildProxyTestCase(TestCase): |
1241 | - """Tests for the build_proxy function.""" |
1242 | - |
1243 | - def test_socks_is_preferred(self): |
1244 | - """Socks overrides all protocols.""" |
1245 | - settings = { |
1246 | - "http": {"host": "httphost", "port": 3128}, |
1247 | - "https": {"host": "httpshost", "port": 3129}, |
1248 | - "socks": {"host": "sockshost", "port": 1080}, |
1249 | - } |
1250 | - proxy = tunnel_server.build_proxy(settings) |
1251 | - self.assertEqual(proxy.type(), proxy.Socks5Proxy) |
1252 | - self.assertEqual(proxy.hostName(), "sockshost") |
1253 | - self.assertEqual(proxy.port(), 1080) |
1254 | - |
1255 | - def test_https_beats_http(self): |
1256 | - """HTTPS wins over HTTP, since all of SD traffic is https.""" |
1257 | - settings = { |
1258 | - "http": {"host": "httphost", "port": 3128}, |
1259 | - "https": {"host": "httpshost", "port": 3129}, |
1260 | - } |
1261 | - proxy = tunnel_server.build_proxy(settings) |
1262 | - self.assertEqual(proxy.type(), proxy.HttpProxy) |
1263 | - self.assertEqual(proxy.hostName(), "httpshost") |
1264 | - self.assertEqual(proxy.port(), 3129) |
1265 | - |
1266 | - def test_http_if_no_other_choice(self): |
1267 | - """Finally, we use the host configured for HTTP.""" |
1268 | - settings = { |
1269 | - "http": {"host": "httphost", "port": 3128}, |
1270 | - } |
1271 | - proxy = tunnel_server.build_proxy(settings) |
1272 | - self.assertEqual(proxy.type(), proxy.HttpProxy) |
1273 | - self.assertEqual(proxy.hostName(), "httphost") |
1274 | - self.assertEqual(proxy.port(), 3128) |
1275 | - |
1276 | - def test_use_noproxy_as_fallback(self): |
1277 | - """If nothing useful, revert to no proxy.""" |
1278 | - settings = {} |
1279 | - proxy = tunnel_server.build_proxy(settings) |
1280 | - self.assertEqual(proxy.type(), proxy.DefaultProxy) |
1281 | - |
1282 | - |
1283 | -class RemoteSocketTestCase(SquidTestCase): |
1284 | - """Tests for the client that connects to the other side.""" |
1285 | - |
1286 | - timeout = 3 |
1287 | - |
1288 | - def get_proxy_settings(self): |
1289 | - return {} |
1290 | - |
1291 | - @defer.inlineCallbacks |
1292 | - def setUp(self): |
1293 | - """Initialize this testcase.""" |
1294 | - yield super(RemoteSocketTestCase, self).setUp() |
1295 | - self.ws = MockWebServer() |
1296 | - self.addCleanup(self.ws.stop) |
1297 | - self.dest_url = self.ws.get_iri().encode("utf-8") + SIMPLERESOURCE |
1298 | - |
1299 | - self.addCleanup(tunnel_server.QNetworkProxy.setApplicationProxy, |
1300 | - tunnel_server.QNetworkProxy.applicationProxy()) |
1301 | - settings = {"http": self.get_proxy_settings()} |
1302 | - proxy = tunnel_server.build_proxy(settings) |
1303 | - tunnel_server.QNetworkProxy.setApplicationProxy(proxy) |
1304 | - |
1305 | - def test_invalid_port(self): |
1306 | - """A request with an invalid port fails with a 400.""" |
1307 | - protocol = tunnel_server.ServerTunnelProtocol( |
1308 | - tunnel_server.RemoteSocket) |
1309 | - protocol.transport = FakeTransport() |
1310 | - protocol.dataReceived("CONNECT 127.0.0.1:wrong_port HTTP/1.0" + |
1311 | - CRLF * 2) |
1312 | - |
1313 | - status_line = protocol.transport.getvalue() |
1314 | - self.assertTrue(status_line.startswith("HTTP/1.0 400 "), |
1315 | - "The port must be an integer.") |
1316 | - |
1317 | - @defer.inlineCallbacks |
1318 | - def test_connection_is_finished_when_stopping(self): |
1319 | - """The client disconnects when requested.""" |
1320 | - fake_protocol = FakeServerTunnelProtocol() |
1321 | - client = tunnel_server.RemoteSocket(fake_protocol) |
1322 | - url = urlparse(self.dest_url) |
1323 | - yield client.connect(url.netloc) |
1324 | - yield client.stop() |
1325 | - |
1326 | - @defer.inlineCallbacks |
1327 | - def test_stop_but_never_connected(self): |
1328 | - """Stop but it was never connected.""" |
1329 | - fake_protocol = FakeServerTunnelProtocol() |
1330 | - client = tunnel_server.RemoteSocket(fake_protocol) |
1331 | - yield client.stop() |
1332 | - |
1333 | - @defer.inlineCallbacks |
1334 | - def test_client_write(self): |
1335 | - """Data written to the client is sent to the other side.""" |
1336 | - fake_protocol = FakeServerTunnelProtocol() |
1337 | - client = tunnel_server.RemoteSocket(fake_protocol) |
1338 | - self.addCleanup(client.stop) |
1339 | - url = urlparse(self.dest_url) |
1340 | - yield client.connect(url.netloc) |
1341 | - client.write("GET /simpleresource HTTP/1.0" + CRLF * 2) |
1342 | - yield self.ws.simple_resource.rendered |
1343 | - |
1344 | - @defer.inlineCallbacks |
1345 | - def test_client_read(self): |
1346 | - """Data received by the client is written into the transport.""" |
1347 | - fake_protocol = FakeServerTunnelProtocol() |
1348 | - client = tunnel_server.RemoteSocket(fake_protocol) |
1349 | - self.addCleanup(client.stop) |
1350 | - url = urlparse(self.dest_url) |
1351 | - yield client.connect(url.netloc) |
1352 | - client.write("GET /simpleresource HTTP/1.0" + CRLF * 2) |
1353 | - yield self.ws.simple_resource.rendered |
1354 | - data = yield fake_protocol.response_received |
1355 | - _headers, content = str(data).split(CRLF * 2, 1) |
1356 | - self.assertEqual(content, SAMPLE_CONTENT) |
1357 | - |
1358 | - |
1359 | -class AnonProxyRemoteSocketTestCase(RemoteSocketTestCase): |
1360 | - """Tests for the client going thru an anonymous proxy.""" |
1361 | - |
1362 | - get_proxy_settings = RemoteSocketTestCase.get_nonauth_proxy_settings |
1363 | - |
1364 | - def parse_headers(self, raw_headers): |
1365 | - """Parse the headers.""" |
1366 | - lines = raw_headers.split(CRLF) |
1367 | - header_lines = lines[1:] |
1368 | - headers_pairs = (l.split(":", 1) for l in header_lines) |
1369 | - return dict((k.lower(), v.strip()) for k, v in headers_pairs) |
1370 | - |
1371 | - @defer.inlineCallbacks |
1372 | - def test_verify_client_uses_proxy(self): |
1373 | - """Verify that the client uses the proxy.""" |
1374 | - fake_protocol = FakeServerTunnelProtocol() |
1375 | - client = tunnel_server.RemoteSocket(fake_protocol) |
1376 | - self.addCleanup(client.stop) |
1377 | - url = urlparse(self.dest_url) |
1378 | - yield client.connect(url.netloc) |
1379 | - client.write("GET /simpleresource HTTP/1.0" + CRLF * 2) |
1380 | - yield self.ws.simple_resource.rendered |
1381 | - data = yield fake_protocol.response_received |
1382 | - raw_headers, _content = str(data).split(CRLF * 2, 1) |
1383 | - self.parse_headers(raw_headers) |
1384 | - |
1385 | - |
1386 | -@skipIfOS('linux2', 'LP: #1111880 - ncsa_auth crashing for auth proxy tests.') |
1387 | -class AuthenticatedProxyRemoteSocketTestCase(AnonProxyRemoteSocketTestCase): |
1388 | - """Tests for the client going thru an authenticated proxy.""" |
1389 | - |
1390 | - get_proxy_settings = RemoteSocketTestCase.get_auth_proxy_settings |
1391 | - |
1392 | - @defer.inlineCallbacks |
1393 | - def test_proxy_authentication_error(self): |
1394 | - """The proxy credentials were wrong on purpose.""" |
1395 | - settings = {"http": self.get_proxy_settings()} |
1396 | - settings["http"]["password"] = "wrong password!!!" |
1397 | - proxy = tunnel_server.build_proxy(settings) |
1398 | - tunnel_server.QNetworkProxy.setApplicationProxy(proxy) |
1399 | - fake_protocol = FakeServerTunnelProtocol() |
1400 | - client = tunnel_server.RemoteSocket(fake_protocol) |
1401 | - self.addCleanup(client.stop) |
1402 | - url = urlparse(self.dest_url) |
1403 | - yield self.assertFailure(client.connect(url.netloc), |
1404 | - tunnel_server.ProxyAuthenticationError) |
1405 | - |
1406 | - @defer.inlineCallbacks |
1407 | - def test_proxy_nobody_listens(self): |
1408 | - """The proxy settings point to a proxy that's unreachable.""" |
1409 | - settings = dict(http={ |
1410 | - "host": "127.0.0.1", |
1411 | - "port": 83, # unused port according to /etc/services |
1412 | - }) |
1413 | - proxy = tunnel_server.build_proxy(settings) |
1414 | - tunnel_server.QNetworkProxy.setApplicationProxy(proxy) |
1415 | - fake_protocol = FakeServerTunnelProtocol() |
1416 | - client = tunnel_server.RemoteSocket(fake_protocol) |
1417 | - self.addCleanup(client.stop) |
1418 | - url = urlparse(self.dest_url) |
1419 | - yield self.assertFailure(client.connect(url.netloc), |
1420 | - tunnel_server.ConnectionError) |
1421 | - |
1422 | - def test_use_credentials(self): |
1423 | - """The credentials are used if present.""" |
1424 | - fake_protocol = FakeServerTunnelProtocol() |
1425 | - client = tunnel_server.RemoteSocket(fake_protocol) |
1426 | - proxy = tunnel_server.build_proxy(FAKE_SETTINGS) |
1427 | - authenticator = QAuthenticator() |
1428 | - |
1429 | - client.proxyAuthenticationRequired.emit(proxy, authenticator) |
1430 | - self.assertEqual(proxy.user(), "") |
1431 | - self.assertEqual(proxy.password(), "") |
1432 | - fake_protocol.proxy_credentials = FAKE_CREDS |
1433 | - |
1434 | - client.proxyAuthenticationRequired.emit(proxy, authenticator) |
1435 | - self.assertEqual(authenticator.user(), FAKE_CREDS["username"]) |
1436 | - self.assertEqual(authenticator.password(), FAKE_CREDS["password"]) |
1437 | - |
1438 | - |
1439 | -class FakeNetworkProxyFactoryClass(object): |
1440 | - """A fake QNetworkProxyFactory.""" |
1441 | - last_query = None |
1442 | - use_system = False |
1443 | - |
1444 | - def __init__(self, enabled): |
1445 | - """Initialize this fake instance.""" |
1446 | - if enabled: |
1447 | - self.proxy_type = tunnel_server.QNetworkProxy.HttpProxy |
1448 | - else: |
1449 | - self.proxy_type = tunnel_server.QNetworkProxy.NoProxy |
1450 | - |
1451 | - def type(self): |
1452 | - """Return the proxy type configured.""" |
1453 | - return self.proxy_type |
1454 | - |
1455 | - @classmethod |
1456 | - def setUseSystemConfiguration(cls, new_value): |
1457 | - """Save the system configuration requested.""" |
1458 | - cls.use_system = new_value |
1459 | - |
1460 | - @classmethod |
1461 | - def useSystemConfiguration(cls): |
1462 | - """Is the system configured for proxies?""" |
1463 | - return cls.use_system |
1464 | - |
1465 | - def systemProxyForQuery(self, query): |
1466 | - """A list of proxies, but only type() will be called on the first.""" |
1467 | - return [self] |
1468 | - |
1469 | - |
1470 | -class CheckProxyEnabledTestCase(TestCase): |
1471 | - """Tests for the check_proxy_enabled function.""" |
1472 | - |
1473 | - @defer.inlineCallbacks |
1474 | - def setUp(self): |
1475 | - """Initialize this testcase.""" |
1476 | - yield super(CheckProxyEnabledTestCase, self).setUp() |
1477 | - self.app_proxy = [] |
1478 | - |
1479 | - def _assert_proxy_state(self, platform, state, assertion): |
1480 | - """Assert the proxy is in a given state.""" |
1481 | - self.patch(tunnel_server.QNetworkProxy, "setApplicationProxy", |
1482 | - lambda proxy: self.app_proxy.append(proxy)) |
1483 | - self.patch(tunnel_server.sys, "platform", platform) |
1484 | - ret = tunnel_server.check_proxy_enabled(SAMPLE_HOST, str(SAMPLE_PORT)) |
1485 | - self.assertTrue(ret == state, assertion) |
1486 | - |
1487 | - def _assert_proxy_enabled(self, platform): |
1488 | - """Assert that the proxy is enabled.""" |
1489 | - self._assert_proxy_state(platform, True, "Proxy is enabled.") |
1490 | - |
1491 | - def _assert_proxy_disabled(self, platform): |
1492 | - """Assert that the proxy is disabled.""" |
1493 | - self._assert_proxy_state(platform, False, "Proxy is disabled.") |
1494 | - |
1495 | - def test_platform_linux_enabled(self): |
1496 | - """Tests for the linux platform with proxies enabled.""" |
1497 | - self.patch(tunnel_server.gsettings, "get_proxy_settings", |
1498 | - lambda: FAKE_SETTINGS) |
1499 | - self._assert_proxy_enabled("linux3") |
1500 | - self.assertEqual(len(self.app_proxy), 1) |
1501 | - |
1502 | - def test_platform_linux_disabled(self): |
1503 | - """Tests for the linux platform with proxies disabled.""" |
1504 | - self.patch(tunnel_server.gsettings, "get_proxy_settings", lambda: {}) |
1505 | - self._assert_proxy_disabled("linux3") |
1506 | - self.assertEqual(len(self.app_proxy), 0) |
1507 | - |
1508 | - def test_platform_other_enabled(self): |
1509 | - """Tests for any other platform with proxies enabled.""" |
1510 | - fake_netproxfact = FakeNetworkProxyFactoryClass(True) |
1511 | - self.patch(tunnel_server, "QNetworkProxyFactory", fake_netproxfact) |
1512 | - self._assert_proxy_enabled("windows 1.0") |
1513 | - self.assertEqual(len(self.app_proxy), 0) |
1514 | - self.assertTrue(fake_netproxfact.useSystemConfiguration()) |
1515 | - |
1516 | - def test_platform_other_disabled(self): |
1517 | - """Tests for any other platform with proxies disabled.""" |
1518 | - fake_netproxfact = FakeNetworkProxyFactoryClass(False) |
1519 | - self.patch(tunnel_server, "QNetworkProxyFactory", fake_netproxfact) |
1520 | - self._assert_proxy_disabled("windows 1.0") |
1521 | - self.assertEqual(len(self.app_proxy), 0) |
1522 | - self.assertTrue(fake_netproxfact.useSystemConfiguration()) |
1523 | - |
1524 | - |
1525 | -class FakeQCoreApp(object): |
1526 | - """A fake QCoreApplication.""" |
1527 | - |
1528 | - fake_instance = None |
1529 | - |
1530 | - def __init__(self, argv): |
1531 | - """Initialize this fake.""" |
1532 | - self.executed = False |
1533 | - self.argv = argv |
1534 | - FakeQCoreApp.fake_instance = self |
1535 | - |
1536 | - def exec_(self): |
1537 | - """Fake the execution of this app.""" |
1538 | - self.executed = True |
1539 | - |
1540 | - @staticmethod |
1541 | - def instance(): |
1542 | - """But return the real instance.""" |
1543 | - return QCoreApplication.instance() |
1544 | - |
1545 | - |
1546 | -class MainFunctionTestCase(TestCase): |
1547 | - """Tests for the main function of the tunnel server.""" |
1548 | - |
1549 | - @defer.inlineCallbacks |
1550 | - def setUp(self): |
1551 | - """Initialize these testcases.""" |
1552 | - yield super(MainFunctionTestCase, self).setUp() |
1553 | - self.called = [] |
1554 | - self.proxies_enabled = False |
1555 | - |
1556 | - def fake_is_proxy_enabled(*args): |
1557 | - """Store the call, return false.""" |
1558 | - self.called.append(args) |
1559 | - return self.proxies_enabled |
1560 | - |
1561 | - self.patch(tunnel_server, "check_proxy_enabled", fake_is_proxy_enabled) |
1562 | - self.fake_stdout = StringIO() |
1563 | - self.patch(tunnel_server.sys, "stdout", self.fake_stdout) |
1564 | - self.patch(tunnel_server, "QCoreApplication", FakeQCoreApp) |
1565 | - |
1566 | - def test_checks_proxies(self): |
1567 | - """Main checks that the proxies are enabled.""" |
1568 | - tunnel_server.main([]) |
1569 | - self.assertEqual(len(self.called), 1) |
1570 | - |
1571 | - def test_on_proxies_enabled_prints_port_and_cookie(self): |
1572 | - """With proxies enabled print port to stdout and start the mainloop.""" |
1573 | - self.patch(tunnel_server.uuid, "uuid4", lambda: FAKE_COOKIE) |
1574 | - self.proxies_enabled = True |
1575 | - port = 443 |
1576 | - tunnel_server.main(["example.com", str(port)]) |
1577 | - stdout = self.fake_stdout.getvalue() |
1578 | - |
1579 | - self.assertIn(tunnel_server.TUNNEL_PORT_LABEL + ": ", stdout) |
1580 | - cookie_line = tunnel_server.TUNNEL_COOKIE_LABEL + ": " + FAKE_COOKIE |
1581 | - self.assertIn(cookie_line, stdout) |
1582 | - |
1583 | - def test_on_proxies_disabled_exit(self): |
1584 | - """With proxies disabled, print a message and exit gracefully.""" |
1585 | - self.proxies_enabled = False |
1586 | - tunnel_server.main(["example.com", "443"]) |
1587 | - self.assertIn("Proxy not enabled.", self.fake_stdout.getvalue()) |
1588 | - self.assertEqual(FakeQCoreApp.fake_instance, None) |
1589 | - |
1590 | - def test_qtdbus_installed_on_linux(self): |
1591 | - """The QtDbus mainloop is installed.""" |
1592 | - self.patch(tunnel_server.sys, "platform", "linux123") |
1593 | - installed = [] |
1594 | - self.patch( |
1595 | - tunnel_server, "install_qt_dbus", lambda: installed.append(None)) |
1596 | - self.proxies_enabled = True |
1597 | - tunnel_server.main(["example.com", "443"]) |
1598 | - self.assertEqual(len(installed), 1) |
1599 | - |
1600 | - def test_qtdbus_not_installed_on_windows(self): |
1601 | - """The QtDbus mainloop is installed.""" |
1602 | - self.patch(tunnel_server.sys, "platform", "win98") |
1603 | - installed = [] |
1604 | - self.patch( |
1605 | - tunnel_server, "install_qt_dbus", lambda: installed.append(None)) |
1606 | - self.proxies_enabled = True |
1607 | - tunnel_server.main(["example.com", "443"]) |
1608 | - self.assertEqual(len(installed), 0) |
1609 | - |
1610 | - def test_fix_turkish_locale_called(self): |
1611 | - """The fix_turkish_locale function is called, always.""" |
1612 | - called = [] |
1613 | - self.patch( |
1614 | - tunnel_server, "fix_turkish_locale", |
1615 | - lambda *args, **kwargs: called.append((args, kwargs))) |
1616 | - tunnel_server.main(["localhost", "443"]) |
1617 | - self.assertEqual(called, [((), {})]) |
1618 | |
1619 | === removed file 'ubuntuone/proxy/tunnel_client.py' |
1620 | --- ubuntuone/proxy/tunnel_client.py 2016-05-29 00:50:05 +0000 |
1621 | +++ ubuntuone/proxy/tunnel_client.py 1970-01-01 00:00:00 +0000 |
1622 | @@ -1,202 +0,0 @@ |
1623 | -# -*- coding: utf-8 -*- |
1624 | -# |
1625 | -# Copyright 2012 Canonical Ltd. |
1626 | -# |
1627 | -# This program is free software: you can redistribute it and/or modify it |
1628 | -# under the terms of the GNU General Public License version 3, as published |
1629 | -# by the Free Software Foundation. |
1630 | -# |
1631 | -# This program is distributed in the hope that it will be useful, but |
1632 | -# WITHOUT ANY WARRANTY; without even the implied warranties of |
1633 | -# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1634 | -# PURPOSE. See the GNU General Public License for more details. |
1635 | -# |
1636 | -# You should have received a copy of the GNU General Public License along |
1637 | -# with this program. If not, see <http://www.gnu.org/licenses/>. |
1638 | -# |
1639 | -# In addition, as a special exception, the copyright holders give |
1640 | -# permission to link the code of portions of this program with the |
1641 | -# OpenSSL library under certain conditions as described in each |
1642 | -# individual source file, and distribute linked combinations |
1643 | -# including the two. |
1644 | -# You must obey the GNU General Public License in all respects |
1645 | -# for all of the code used other than OpenSSL. If you modify |
1646 | -# file(s) with this exception, you may extend this exception to your |
1647 | -# version of the file(s), but you are not obligated to do so. If you |
1648 | -# do not wish to do so, delete this exception statement from your |
1649 | -# version. If you delete this exception statement from all source |
1650 | -# files in the program, then also delete it here. |
1651 | -"""Client for the tunnel protocol.""" |
1652 | - |
1653 | -import logging |
1654 | - |
1655 | -from twisted.internet import protocol, reactor |
1656 | - |
1657 | -from ubuntuone.clientdefs import NAME |
1658 | -from ubuntuone.proxy.common import ( |
1659 | - BaseTunnelProtocol, |
1660 | - CRLF, |
1661 | - TUNNEL_COOKIE_LABEL, |
1662 | - TUNNEL_COOKIE_HEADER, |
1663 | - TUNNEL_PORT_LABEL, |
1664 | -) |
1665 | - |
1666 | -METHOD_LINE = "CONNECT %s:%d HTTP/1.0" + CRLF |
1667 | -LOCALHOST = "127.0.0.1" |
1668 | - |
1669 | -logger = logging.getLogger("ubuntuone.SyncDaemon.TunnelClient") |
1670 | - |
1671 | - |
1672 | -class TunnelClientProtocol(BaseTunnelProtocol): |
1673 | - """Client protocol for the handshake part of the tunnel.""" |
1674 | - |
1675 | - def connectionMade(self): |
1676 | - """The connection to the tunnel was made so send request.""" |
1677 | - method_line = METHOD_LINE % (self.factory.tunnel_host, |
1678 | - self.factory.tunnel_port) |
1679 | - headers = { |
1680 | - "User-Agent": "%s tunnel client" % NAME, |
1681 | - TUNNEL_COOKIE_HEADER: self.factory.cookie, |
1682 | - } |
1683 | - self.transport.write(method_line + |
1684 | - self.format_headers(headers) + |
1685 | - CRLF) |
1686 | - |
1687 | - def handle_first_line(self, line): |
1688 | - """The first line received is the status line.""" |
1689 | - try: |
1690 | - proto_version, self.status_code, description = line.split(" ", 2) |
1691 | - except ValueError: |
1692 | - self.transport.loseConnection() |
1693 | - |
1694 | - def headers_done(self): |
1695 | - """All the headers have arrived. Time to switch protocols.""" |
1696 | - remaining_data = self.clearLineBuffer() |
1697 | - if self.status_code != "200": |
1698 | - self.transport.loseConnection() |
1699 | - return |
1700 | - addr = self.transport.getPeer() |
1701 | - other_protocol = self.factory.other_factory.buildProtocol(addr) |
1702 | - self.transport.protocol = other_protocol |
1703 | - other_protocol.transport = self.transport |
1704 | - self.transport = None |
1705 | - if self.factory.context_factory: |
1706 | - other_protocol.transport.startTLS(self.factory.context_factory) |
1707 | - other_protocol.connectionMade() |
1708 | - if remaining_data: |
1709 | - other_protocol.dataReceived(remaining_data) |
1710 | - |
1711 | - |
1712 | -class TunnelClientFactory(protocol.ClientFactory): |
1713 | - """A factory for Tunnel Client Protocols.""" |
1714 | - |
1715 | - protocol = TunnelClientProtocol |
1716 | - |
1717 | - def __init__(self, tunnel_host, tunnel_port, other_factory, cookie, |
1718 | - context_factory=None): |
1719 | - """Initialize this factory.""" |
1720 | - self.tunnel_host = tunnel_host |
1721 | - self.tunnel_port = tunnel_port |
1722 | - self.other_factory = other_factory |
1723 | - self.context_factory = context_factory |
1724 | - self.cookie = cookie |
1725 | - |
1726 | - def startedConnecting(self, connector): |
1727 | - """Forward this call to the other factory.""" |
1728 | - self.other_factory.startedConnecting(connector) |
1729 | - |
1730 | - def clientConnectionFailed(self, connector, reason): |
1731 | - """Forward this call to the other factory.""" |
1732 | - self.other_factory.clientConnectionFailed(connector, reason) |
1733 | - |
1734 | - def clientConnectionLost(self, connector, reason): |
1735 | - """Forward this call to the other factory.""" |
1736 | - self.other_factory.clientConnectionLost(connector, reason) |
1737 | - |
1738 | - |
1739 | -class TunnelClient(object): |
1740 | - """A client for the proxy tunnel.""" |
1741 | - |
1742 | - def __init__(self, tunnel_host, tunnel_port, cookie): |
1743 | - """Initialize this client.""" |
1744 | - self.tunnel_host = tunnel_host |
1745 | - self.tunnel_port = tunnel_port |
1746 | - self.cookie = cookie |
1747 | - |
1748 | - def connectTCP(self, host, port, factory, *args, **kwargs): |
1749 | - """A connectTCP going thru the tunnel.""" |
1750 | - logger.info("Connecting (TCP) to %r:%r via tunnel at %r:%r", |
1751 | - host, port, self.tunnel_host, self.tunnel_port) |
1752 | - tunnel_factory = TunnelClientFactory(host, port, factory, self.cookie) |
1753 | - return reactor.connectTCP(self.tunnel_host, self.tunnel_port, |
1754 | - tunnel_factory, *args, **kwargs) |
1755 | - |
1756 | - def connectSSL(self, host, port, factory, |
1757 | - contextFactory, *args, **kwargs): |
1758 | - """A connectSSL going thru the tunnel.""" |
1759 | - logger.info("Connecting (SSL) to %r:%r via tunnel at %r:%r", |
1760 | - host, port, self.tunnel_host, self.tunnel_port) |
1761 | - tunnel_factory = TunnelClientFactory( |
1762 | - host, port, factory, self.cookie, contextFactory) |
1763 | - return reactor.connectTCP(self.tunnel_host, self.tunnel_port, |
1764 | - tunnel_factory, *args, **kwargs) |
1765 | - |
1766 | - |
1767 | -class TunnelProcessProtocol(protocol.ProcessProtocol): |
1768 | - """The dialog thru stdout with the tunnel server.""" |
1769 | - |
1770 | - timeout = 30 |
1771 | - |
1772 | - def __init__(self, client_d): |
1773 | - """Initialize this protocol.""" |
1774 | - self.client_d = client_d |
1775 | - self.timer = None |
1776 | - self.port = None |
1777 | - self.cookie = None |
1778 | - |
1779 | - def connectionMade(self): |
1780 | - """The process has started, start a timer.""" |
1781 | - logger.info("Tunnel process started.") |
1782 | - self.timer = reactor.callLater(self.timeout, self.process_timeouted) |
1783 | - |
1784 | - def process_timeouted(self): |
1785 | - """The process took too long to reply.""" |
1786 | - if not self.client_d.called: |
1787 | - logger.info("Timeout while waiting for tunnel process.") |
1788 | - self.client_d.callback(reactor) |
1789 | - |
1790 | - def finish_timeout(self): |
1791 | - """Stop the timer from firing.""" |
1792 | - if self.timer and self.timer.active(): |
1793 | - logger.debug("canceling timer before connection timeout") |
1794 | - self.timer.cancel() |
1795 | - |
1796 | - def processExited(self, status): |
1797 | - """The tunnel process has exited with some error code.""" |
1798 | - self.finish_timeout() |
1799 | - logger.info("Tunnel process exit status %r.", status) |
1800 | - if not self.client_d.called: |
1801 | - logger.debug("Tunnel process exited before TunnelClient created. " |
1802 | - "Falling back to reactor") |
1803 | - self.client_d.callback(reactor) |
1804 | - |
1805 | - def outReceived(self, data): |
1806 | - """Receive the port number.""" |
1807 | - if self.client_d.called: |
1808 | - return |
1809 | - |
1810 | - for line in data.split("\n"): |
1811 | - if line.startswith(TUNNEL_PORT_LABEL): |
1812 | - _header, port = line.split(":", 1) |
1813 | - self.port = int(port.strip()) |
1814 | - if line.startswith(TUNNEL_COOKIE_LABEL): |
1815 | - _header, cookie = line.split(":", 1) |
1816 | - self.cookie = cookie.strip() |
1817 | - |
1818 | - if self.port and self.cookie: |
1819 | - logger.info("Tunnel process listening on port %r.", self.port) |
1820 | - client = TunnelClient(LOCALHOST, self.port, self.cookie) |
1821 | - self.client_d.callback(client) |
1822 | - |
1823 | - def errReceived(self, data): |
1824 | - logger.debug("Got stderr from tunnel process: %r", data) |
1825 | |
1826 | === removed file 'ubuntuone/proxy/tunnel_server.py' |
1827 | --- ubuntuone/proxy/tunnel_server.py 2017-01-07 18:51:07 +0000 |
1828 | +++ ubuntuone/proxy/tunnel_server.py 1970-01-01 00:00:00 +0000 |
1829 | @@ -1,405 +0,0 @@ |
1830 | -# -*- coding: utf-8 -*- |
1831 | -# |
1832 | -# Copyright 2012-2013 Canonical Ltd. |
1833 | -# Copyright 2015-2017 Chicharreros (https://launchpad.net/~chicharreros) |
1834 | -# |
1835 | -# This program is free software: you can redistribute it and/or modify it |
1836 | -# under the terms of the GNU General Public License version 3, as published |
1837 | -# by the Free Software Foundation. |
1838 | -# |
1839 | -# This program is distributed in the hope that it will be useful, but |
1840 | -# WITHOUT ANY WARRANTY; without even the implied warranties of |
1841 | -# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1842 | -# PURPOSE. See the GNU General Public License for more details. |
1843 | -# |
1844 | -# You should have received a copy of the GNU General Public License along |
1845 | -# with this program. If not, see <http://www.gnu.org/licenses/>. |
1846 | -# |
1847 | -# In addition, as a special exception, the copyright holders give |
1848 | -# permission to link the code of portions of this program with the |
1849 | -# OpenSSL library under certain conditions as described in each |
1850 | -# individual source file, and distribute linked combinations |
1851 | -# including the two. |
1852 | -# You must obey the GNU General Public License in all respects |
1853 | -# for all of the code used other than OpenSSL. If you modify |
1854 | -# file(s) with this exception, you may extend this exception to your |
1855 | -# version of the file(s), but you are not obligated to do so. If you |
1856 | -# do not wish to do so, delete this exception statement from your |
1857 | -# version. If you delete this exception statement from all source |
1858 | -# files in the program, then also delete it here. |
1859 | -"""A tunnel through proxies. |
1860 | - |
1861 | -The layers in a tunneled proxied connection: |
1862 | - |
1863 | -↓ tunnelclient - initiates tcp to tunnelserver, request outward connection |
1864 | -↕ client protocol - started after the tunneclient gets connected |
1865 | ----process boundary--- |
1866 | -↕ tunnelserver - creates a tunnel instance per incoming connection |
1867 | -↕ tunnel - hold a qtcpsocket to tunnelclient, and srvtunnelproto to the remote |
1868 | -↕ servertunnelprotocol - gets CONNECT from tunnelclient, creates a remotesocket |
1869 | -↕ remotesocket - connects to the destination server via a proxy |
1870 | -↕ proxy server - goes thru firewalls |
1871 | -↑ server - dialogues with the client protocol |
1872 | - |
1873 | -""" |
1874 | - |
1875 | -import sys |
1876 | -import uuid |
1877 | - |
1878 | -from PyQt4.QtCore import QCoreApplication, QTimer |
1879 | -from PyQt4.QtNetwork import ( |
1880 | - QAbstractSocket, |
1881 | - QHostAddress, |
1882 | - QNetworkProxy, |
1883 | - QNetworkProxyQuery, |
1884 | - QNetworkProxyFactory, |
1885 | - QTcpServer, |
1886 | - QTcpSocket, |
1887 | -) |
1888 | -from twisted.internet import defer, interfaces |
1889 | -from zope.interface import implements |
1890 | - |
1891 | -from ubuntuone.clientdefs import NAME |
1892 | -from ubuntuone.keyring import Keyring |
1893 | -from ubuntuone.utils import gsettings |
1894 | -from ubuntuone.proxy.common import ( |
1895 | - BaseTunnelProtocol, |
1896 | - CRLF, |
1897 | - TUNNEL_COOKIE_HEADER, |
1898 | - TUNNEL_COOKIE_LABEL, |
1899 | - TUNNEL_PORT_LABEL, |
1900 | -) |
1901 | -from ubuntuone.proxy.logger import logger |
1902 | -try: |
1903 | - from ubuntuone.utils.locale import fix_turkish_locale |
1904 | -except ImportError: |
1905 | - def fix_turkish_locale(): |
1906 | - return None |
1907 | - |
1908 | - |
1909 | -DEFAULT_CODE = 500 |
1910 | -DEFAULT_DESCRIPTION = "Connection error" |
1911 | - |
1912 | - |
1913 | -class ConnectionError(Exception): |
1914 | - """The client failed connecting to the destination.""" |
1915 | - |
1916 | - def __init__(self, code=DEFAULT_CODE, description=DEFAULT_DESCRIPTION): |
1917 | - self.code = code |
1918 | - self.description = description |
1919 | - |
1920 | - |
1921 | -class ProxyAuthenticationError(ConnectionError): |
1922 | - """Credentials mismatch going thru a proxy.""" |
1923 | - |
1924 | - |
1925 | -def build_proxy(settings_groups): |
1926 | - """Create a QNetworkProxy from these settings.""" |
1927 | - proxy_groups = [ |
1928 | - ("socks", QNetworkProxy.Socks5Proxy), |
1929 | - ("https", QNetworkProxy.HttpProxy), |
1930 | - ("http", QNetworkProxy.HttpProxy), |
1931 | - ] |
1932 | - for group, proxy_type in proxy_groups: |
1933 | - if group not in settings_groups: |
1934 | - continue |
1935 | - settings = settings_groups[group] |
1936 | - if "host" in settings and "port" in settings: |
1937 | - return QNetworkProxy(proxy_type, |
1938 | - hostName=settings.get("host", ""), |
1939 | - port=settings.get("port", 0), |
1940 | - user=settings.get("username", ""), |
1941 | - password=settings.get("password", "")) |
1942 | - logger.error("No proxy correctly configured.") |
1943 | - return QNetworkProxy(QNetworkProxy.DefaultProxy) |
1944 | - |
1945 | - |
1946 | -class RemoteSocket(QTcpSocket): |
1947 | - """A dumb connection through a proxy to a destination hostport.""" |
1948 | - |
1949 | - def __init__(self, tunnel_protocol): |
1950 | - """Initialize this object.""" |
1951 | - super(RemoteSocket, self).__init__() |
1952 | - self.protocol = tunnel_protocol |
1953 | - self.connected_d = defer.Deferred() |
1954 | - self.connected.connect(self.handle_connected) |
1955 | - self.proxyAuthenticationRequired.connect(self.handle_auth_required) |
1956 | - self.buffered_data = [] |
1957 | - |
1958 | - def handle_connected(self): |
1959 | - """When connected, send all pending data.""" |
1960 | - self.disconnected.connect(self.handle_disconnected) |
1961 | - self.connected_d.callback(None) |
1962 | - for d in self.buffered_data: |
1963 | - logger.debug("writing remote: %d bytes", len(d)) |
1964 | - super(RemoteSocket, self).write(d) |
1965 | - self.buffered_data = [] |
1966 | - |
1967 | - def handle_disconnected(self): |
1968 | - """Do something with disconnections.""" |
1969 | - logger.debug("Remote socket disconnected") |
1970 | - self.protocol.remote_disconnected() |
1971 | - |
1972 | - def write(self, data): |
1973 | - """Write data to the remote end, buffering if not connected.""" |
1974 | - if self.state() == QAbstractSocket.ConnectedState: |
1975 | - logger.debug("writing remote: %d bytes", len(data)) |
1976 | - super(RemoteSocket, self).write(data) |
1977 | - else: |
1978 | - self.buffered_data.append(data) |
1979 | - |
1980 | - def connect(self, hostport): |
1981 | - """Try to establish the connection to the remote end.""" |
1982 | - host, port = hostport.split(":") |
1983 | - |
1984 | - try: |
1985 | - port = int(port) |
1986 | - except ValueError: |
1987 | - raise ConnectionError(400, "Destination port must be an integer.") |
1988 | - |
1989 | - self.readyRead.connect(self.handle_ready_read) |
1990 | - self.error.connect(self.handle_error) |
1991 | - self.connectToHost(host, port) |
1992 | - |
1993 | - return self.connected_d |
1994 | - |
1995 | - def handle_auth_required(self, proxy, authenticator): |
1996 | - """Handle the proxyAuthenticationRequired signal.""" |
1997 | - self.protocol.proxy_auth_required(proxy, authenticator) |
1998 | - |
1999 | - def handle_error(self, socket_error): |
2000 | - """Some error happened while connecting.""" |
2001 | - error_description = "%s (%d)" % (self.errorString(), socket_error) |
2002 | - logger.error("connection error: %s", error_description) |
2003 | - if self.connected_d.called: |
2004 | - return |
2005 | - |
2006 | - if socket_error == self.ProxyAuthenticationRequiredError: |
2007 | - error = ProxyAuthenticationError(407, error_description) |
2008 | - else: |
2009 | - error = ConnectionError(500, error_description) |
2010 | - |
2011 | - self.connected_d.errback(error) |
2012 | - |
2013 | - def handle_ready_read(self): |
2014 | - """Forward data from the remote end to the parent protocol.""" |
2015 | - data = self.readAll() |
2016 | - self.protocol.response_data_received(data) |
2017 | - |
2018 | - @defer.inlineCallbacks |
2019 | - def stop(self): |
2020 | - """Finish and cleanup.""" |
2021 | - self.disconnectFromHost() |
2022 | - while self.state() != self.UnconnectedState: |
2023 | - d = defer.Deferred() |
2024 | - QTimer.singleShot(100, lambda: d.callback(None)) |
2025 | - yield d |
2026 | - |
2027 | - |
2028 | -class ServerTunnelProtocol(BaseTunnelProtocol): |
2029 | - """CONNECT sever protocol for tunnelling connections.""" |
2030 | - |
2031 | - def __init__(self, client_class): |
2032 | - """Initialize this protocol.""" |
2033 | - BaseTunnelProtocol.__init__(self) |
2034 | - self.hostport = "" |
2035 | - self.client = None |
2036 | - self.client_class = client_class |
2037 | - self.proxy_credentials = None |
2038 | - self.proxy_domain = None |
2039 | - |
2040 | - def error_response(self, code, description): |
2041 | - """Write a response with an error, and disconnect.""" |
2042 | - self.write_transport("HTTP/1.0 %d %s" % (code, description) + CRLF * 2) |
2043 | - self.transport.loseConnection() |
2044 | - if self.client: |
2045 | - self.client.stop() |
2046 | - self.clearLineBuffer() |
2047 | - |
2048 | - def write_transport(self, data): |
2049 | - """Write a response in the transport.""" |
2050 | - self.transport.write(data) |
2051 | - |
2052 | - def proxy_auth_required(self, proxy, authenticator): |
2053 | - """Proxy authentication is required.""" |
2054 | - logger.info("auth_required %r, %r", |
2055 | - proxy.hostName(), self.proxy_domain) |
2056 | - if self.proxy_credentials and proxy.hostName() == self.proxy_domain: |
2057 | - logger.info("Credentials added to authenticator.") |
2058 | - authenticator.setUser(self.proxy_credentials["username"]) |
2059 | - authenticator.setPassword(self.proxy_credentials["password"]) |
2060 | - else: |
2061 | - logger.info("Credentials needed, but none available.") |
2062 | - self.proxy_domain = proxy.hostName() |
2063 | - |
2064 | - def handle_first_line(self, line): |
2065 | - """Special handling for the first line received.""" |
2066 | - try: |
2067 | - method, hostport, proto_version = line.split(" ", 2) |
2068 | - if proto_version != "HTTP/1.0": |
2069 | - self.error_response(505, "HTTP Version Not Supported") |
2070 | - return |
2071 | - if method != "CONNECT": |
2072 | - self.error_response(405, "Only the CONNECT method is allowed") |
2073 | - return |
2074 | - self.hostport = hostport |
2075 | - except ValueError: |
2076 | - self.error_response(400, "Bad request") |
2077 | - |
2078 | - def verify_cookie(self): |
2079 | - """Fail if the cookie is wrong or missing.""" |
2080 | - cookie_received = dict(self.received_headers).get(TUNNEL_COOKIE_HEADER) |
2081 | - if cookie_received != self.transport.cookie: |
2082 | - raise ConnectionError(418, "Please see RFC 2324") |
2083 | - |
2084 | - @defer.inlineCallbacks |
2085 | - def headers_done(self): |
2086 | - """An empty line was received, start connecting and switch mode.""" |
2087 | - try: |
2088 | - self.verify_cookie() |
2089 | - try: |
2090 | - logger.info("Connecting once") |
2091 | - self.client = self.client_class(self) |
2092 | - yield self.client.connect(self.hostport) |
2093 | - except ProxyAuthenticationError: |
2094 | - if not self.proxy_domain: |
2095 | - logger.info("No proxy domain defined") |
2096 | - raise |
2097 | - |
2098 | - credentials = yield Keyring().get_credentials( |
2099 | - str(self.proxy_domain)) |
2100 | - if "username" in credentials: |
2101 | - self.proxy_credentials = credentials |
2102 | - logger.info("Connecting again with keyring credentials") |
2103 | - self.client = self.client_class(self) |
2104 | - yield self.client.connect(self.hostport) |
2105 | - logger.info("Connected with keyring credentials") |
2106 | - |
2107 | - response_headers = { |
2108 | - "Server": "%s proxy tunnel" % NAME, |
2109 | - } |
2110 | - self.write_transport("HTTP/1.0 200 Proxy connection established" + |
2111 | - CRLF + self.format_headers(response_headers) + |
2112 | - CRLF) |
2113 | - except ConnectionError as e: |
2114 | - logger.exception("Connection error") |
2115 | - self.error_response(e.code, e.description) |
2116 | - except Exception: |
2117 | - logger.exception("Unhandled problem while connecting") |
2118 | - |
2119 | - def rawDataReceived(self, data): |
2120 | - """Tunnel all raw data straight to the other side.""" |
2121 | - self.client.write(data) |
2122 | - |
2123 | - def response_data_received(self, data): |
2124 | - """Return data coming from the other side.""" |
2125 | - self.write_transport(data) |
2126 | - |
2127 | - |
2128 | -class Tunnel(object): |
2129 | - """An instance of a running tunnel.""" |
2130 | - |
2131 | - implements(interfaces.ITransport) |
2132 | - |
2133 | - def __init__(self, local_socket, cookie): |
2134 | - """Initialize this Tunnel instance.""" |
2135 | - self.cookie = cookie |
2136 | - self.disconnecting = False |
2137 | - self.local_socket = local_socket |
2138 | - self.protocol = ServerTunnelProtocol(RemoteSocket) |
2139 | - self.protocol.transport = self |
2140 | - local_socket.readyRead.connect(self.server_ready_read) |
2141 | - local_socket.disconnected.connect(self.local_disconnected) |
2142 | - |
2143 | - def server_ready_read(self): |
2144 | - """Data available on the local end. Move it forward.""" |
2145 | - data = bytes(self.local_socket.readAll()) |
2146 | - self.protocol.dataReceived(data) |
2147 | - |
2148 | - def write(self, data): |
2149 | - """Data available on the remote end. Bring it back.""" |
2150 | - logger.debug("writing local: %d bytes", len(data)) |
2151 | - self.local_socket.write(data) |
2152 | - |
2153 | - def loseConnection(self): |
2154 | - """The remote end disconnected.""" |
2155 | - logger.debug("disconnecting local end.") |
2156 | - self.local_socket.close() |
2157 | - |
2158 | - def local_disconnected(self): |
2159 | - """The local end disconnected.""" |
2160 | - logger.debug("The local socket got disconnected.") |
2161 | - # TODO: handle this case in an upcoming branch |
2162 | - |
2163 | - |
2164 | -class TunnelServer(object): |
2165 | - """A server for tunnel instances.""" |
2166 | - |
2167 | - def __init__(self, cookie): |
2168 | - """Initialize this tunnel instance.""" |
2169 | - self.tunnels = [] |
2170 | - self.cookie = cookie |
2171 | - self.server = QTcpServer(QCoreApplication.instance()) |
2172 | - self.server.newConnection.connect(self.new_connection) |
2173 | - self.server.listen(QHostAddress.LocalHost, 0) |
2174 | - logger.info("Starting tunnel server at port %d", self.port) |
2175 | - |
2176 | - def new_connection(self): |
2177 | - """On a new connection create a new tunnel instance.""" |
2178 | - logger.info("New connection made") |
2179 | - local_socket = self.server.nextPendingConnection() |
2180 | - tunnel = Tunnel(local_socket, self.cookie) |
2181 | - self.tunnels.append(tunnel) |
2182 | - |
2183 | - def shutdown(self): |
2184 | - """Terminate every connection.""" |
2185 | - # TODO: handle this gracefully in an upcoming branch |
2186 | - |
2187 | - @property |
2188 | - def port(self): |
2189 | - """The port where this server listens.""" |
2190 | - return self.server.serverPort() |
2191 | - |
2192 | - |
2193 | -def check_proxy_enabled(host, port): |
2194 | - """Check if the proxy is enabled.""" |
2195 | - port = int(port) |
2196 | - if sys.platform.startswith("linux"): |
2197 | - settings = gsettings.get_proxy_settings() |
2198 | - enabled = len(settings) > 0 |
2199 | - if enabled: |
2200 | - proxy = build_proxy(settings) |
2201 | - QNetworkProxy.setApplicationProxy(proxy) |
2202 | - else: |
2203 | - logger.info("Proxy is disabled.") |
2204 | - return enabled |
2205 | - else: |
2206 | - QNetworkProxyFactory.setUseSystemConfiguration(True) |
2207 | - query = QNetworkProxyQuery(host, port) |
2208 | - proxies = QNetworkProxyFactory.systemProxyForQuery(query) |
2209 | - return len(proxies) and proxies[0].type() != QNetworkProxy.NoProxy |
2210 | - |
2211 | - |
2212 | -def install_qt_dbus(): |
2213 | - """Import and install the qt+dbus integration.""" |
2214 | - from dbus.mainloop.qt import DBusQtMainLoop |
2215 | - DBusQtMainLoop(set_as_default=True) |
2216 | - |
2217 | - |
2218 | -def main(argv): |
2219 | - """The main function for the tunnel server.""" |
2220 | - fix_turkish_locale() |
2221 | - if not check_proxy_enabled(*argv[1:]): |
2222 | - sys.stdout.write("Proxy not enabled.") |
2223 | - sys.stdout.flush() |
2224 | - else: |
2225 | - if sys.platform.startswith("linux"): |
2226 | - install_qt_dbus() |
2227 | - |
2228 | - app = QCoreApplication(argv) |
2229 | - cookie = str(uuid.uuid4()) |
2230 | - tunnel_server = TunnelServer(cookie) |
2231 | - sys.stdout.write("%s: %d\n" % (TUNNEL_PORT_LABEL, tunnel_server.port) + |
2232 | - "%s: %s\n" % (TUNNEL_COOKIE_LABEL, cookie)) |
2233 | - sys.stdout.flush() |
2234 | - app.exec_() |
2235 | |
2236 | === modified file 'ubuntuone/syncdaemon/action_queue.py' |
2237 | --- ubuntuone/syncdaemon/action_queue.py 2017-02-10 01:15:07 +0000 |
2238 | +++ ubuntuone/syncdaemon/action_queue.py 2018-03-14 21:14:07 +0000 |
2239 | @@ -1,7 +1,7 @@ |
2240 | # -*- coding: utf-8 -*- |
2241 | # |
2242 | # Copyright 2009-2015 Canonical Ltd. |
2243 | -# Copyright 2015-2017 Chicharreros (https://launchpad.net/~chicharreros) |
2244 | +# Copyright 2015-2018 Chicharreros (https://launchpad.net/~chicharreros) |
2245 | # |
2246 | # This program is free software: you can redistribute it and/or modify it |
2247 | # under the terms of the GNU General Public License version 3, as published |
2248 | @@ -59,7 +59,6 @@ |
2249 | from ubuntuone.syncdaemon.interfaces import IActionQueue, IMarker |
2250 | from ubuntuone.syncdaemon.logger import mklog, TRACE |
2251 | from ubuntuone.syncdaemon import config, offload_queue |
2252 | -from ubuntuone.syncdaemon import tunnel_runner |
2253 | |
2254 | logger = logging.getLogger("ubuntuone.SyncDaemon.ActionQueue") |
2255 | |
2256 | @@ -856,27 +855,20 @@ |
2257 | self.event_queue.push('SV_VOLUME_NEW_GENERATION', |
2258 | volume_id=volume_id, generation=generation) |
2259 | |
2260 | - def _get_tunnel_runner(self, host, port): |
2261 | - """Build the tunnel runner.""" |
2262 | - return tunnel_runner.TunnelRunner(host, port) |
2263 | - |
2264 | - @defer.inlineCallbacks |
2265 | def _make_connection(self): |
2266 | """Do the real connect call.""" |
2267 | connection_info = self.connection_info.next() |
2268 | logger.info("Attempting connection to %s", connection_info) |
2269 | host = connection_info['host'] |
2270 | port = connection_info['port'] |
2271 | - tunnelrunner = self._get_tunnel_runner(host, port) |
2272 | - client = yield tunnelrunner.get_client() |
2273 | if connection_info['use_ssl']: |
2274 | ssl_context = get_ssl_context( |
2275 | connection_info['disable_ssl_verify'], host) |
2276 | - self.connector = client.connectSSL( |
2277 | + self.connector = reactor.connectSSL( |
2278 | host, port, factory=self, contextFactory=ssl_context, |
2279 | timeout=self.connection_timeout) |
2280 | else: |
2281 | - self.connector = client.connectTCP( |
2282 | + self.connector = reactor.connectTCP( |
2283 | host, port, self, timeout=self.connection_timeout) |
2284 | |
2285 | def connect(self): |
2286 | |
2287 | === modified file 'ubuntuone/syncdaemon/tests/test_action_queue.py' |
2288 | --- ubuntuone/syncdaemon/tests/test_action_queue.py 2018-03-08 19:39:13 +0000 |
2289 | +++ ubuntuone/syncdaemon/tests/test_action_queue.py 2018-03-14 21:14:07 +0000 |
2290 | @@ -1,7 +1,7 @@ |
2291 | # -*- coding: utf-8 -*- |
2292 | # |
2293 | # Copyright 2009-2015 Canonical Ltd. |
2294 | -# Copyright 2016-2017 Chicharreros (https://launchpad.net/~chicharreros) |
2295 | +# Copyright 2015-2018 Chicharreros (https://launchpad.net/~chicharreros) |
2296 | # |
2297 | # This program is free software: you can redistribute it and/or modify it |
2298 | # under the terms of the GNU General Public License version 3, as published |
2299 | @@ -1384,9 +1384,13 @@ |
2300 | self.assertTrue(self.handler.check_info("Connection started", |
2301 | "host 1.2.3.4", "port 4321")) |
2302 | |
2303 | - @defer.inlineCallbacks |
2304 | def test_connection_info_rotation(self): |
2305 | """It tries to connect to different servers.""" |
2306 | + # store how connectTCP is called |
2307 | + called = [] |
2308 | + self.patch( |
2309 | + reactor, 'connectTCP', |
2310 | + lambda host, port, *a, **k: called.append((host, port))) |
2311 | |
2312 | multiple_conn = [ |
2313 | {'host': 'host1', 'port': 'port1', 'use_ssl': False}, |
2314 | @@ -1394,61 +1398,14 @@ |
2315 | ] |
2316 | self.action_queue.connection_info = itertools.cycle(multiple_conn) |
2317 | |
2318 | - self.tunnel_runner = None |
2319 | - |
2320 | - def mitm(*args): |
2321 | - tunnel_runner = SavingConnectionTunnelRunner(*args) |
2322 | - self.tunnel_runner = tunnel_runner |
2323 | - return tunnel_runner |
2324 | - |
2325 | - self.action_queue._get_tunnel_runner = mitm |
2326 | - |
2327 | - yield self.action_queue._make_connection() |
2328 | - self.assertEqual(self.tunnel_runner.host, 'host1') |
2329 | - self.assertEqual(self.tunnel_runner.port, 'port1') |
2330 | - |
2331 | - yield self.action_queue._make_connection() |
2332 | - self.assertEqual(self.tunnel_runner.host, 'host2') |
2333 | - self.assertEqual(self.tunnel_runner.port, 'port2') |
2334 | - |
2335 | - yield self.action_queue._make_connection() |
2336 | - self.assertEqual(self.tunnel_runner.host, 'host1') |
2337 | - self.assertEqual(self.tunnel_runner.port, 'port1') |
2338 | - |
2339 | - |
2340 | -class TunnelRunnerTestCase(FactoryBaseTestCase): |
2341 | - """Tests for the tunnel runner.""" |
2342 | - |
2343 | - tunnel_runner_class = SavingConnectionTunnelRunner |
2344 | - |
2345 | - def setUp(self): |
2346 | - result = super(TunnelRunnerTestCase, self).setUp() |
2347 | - self.tunnel_runner = None |
2348 | - orig_get_tunnel_runner = self.action_queue._get_tunnel_runner |
2349 | - |
2350 | - def mitm(*args): |
2351 | - tunnel_runner = orig_get_tunnel_runner(*args) |
2352 | - self.tunnel_runner = tunnel_runner |
2353 | - return tunnel_runner |
2354 | - |
2355 | - self.action_queue._get_tunnel_runner = mitm |
2356 | - return result |
2357 | - |
2358 | - @defer.inlineCallbacks |
2359 | - def test_make_connection_uses_tunnelrunner_non_ssl(self): |
2360 | - """Check that _make_connection uses TunnelRunner.""" |
2361 | - self._patch_connection_info(use_ssl=False) |
2362 | - yield self.action_queue._make_connection() |
2363 | - self.assertTrue(self.tunnel_runner.client.tcp_connected, |
2364 | - "connectTCP is called on the client.") |
2365 | - |
2366 | - @defer.inlineCallbacks |
2367 | - def test_make_connection_uses_tunnelrunner_ssl(self): |
2368 | - """Check that _make_connection uses TunnelRunner.""" |
2369 | - self._patch_connection_info(use_ssl=True, disable_ssl_verify=False) |
2370 | - yield self.action_queue._make_connection() |
2371 | - self.assertTrue(self.tunnel_runner.client.ssl_connected, |
2372 | - "connectSSL is called on the client.") |
2373 | + self.action_queue._make_connection() |
2374 | + self.assertEqual(called[-1], ('host1', 'port1')) |
2375 | + |
2376 | + self.action_queue._make_connection() |
2377 | + self.assertEqual(called[-1], ('host2', 'port2')) |
2378 | + |
2379 | + self.action_queue._make_connection() |
2380 | + self.assertEqual(called[-1], ('host1', 'port1')) |
2381 | |
2382 | |
2383 | class ContextRequestedWithHost(FactoryBaseTestCase): |
2384 | @@ -1459,6 +1416,9 @@ |
2385 | @defer.inlineCallbacks |
2386 | def test_context_request_passes_host(self): |
2387 | """The context is requested passing the host.""" |
2388 | + # avoid a real connection |
2389 | + self.patch(reactor, 'connectSSL', lambda *a, **k: None) |
2390 | + |
2391 | fake_host = "fake_host" |
2392 | fake_disable_ssl_verify = False |
2393 | |
2394 | |
2395 | === removed file 'ubuntuone/syncdaemon/tests/test_tunnel_runner.py' |
2396 | --- ubuntuone/syncdaemon/tests/test_tunnel_runner.py 2016-06-04 21:14:35 +0000 |
2397 | +++ ubuntuone/syncdaemon/tests/test_tunnel_runner.py 1970-01-01 00:00:00 +0000 |
2398 | @@ -1,185 +0,0 @@ |
2399 | -# -*- coding: utf-8 -*- |
2400 | -# |
2401 | -# Copyright 2012 Canonical Ltd. |
2402 | -# |
2403 | -# This program is free software: you can redistribute it and/or modify it |
2404 | -# under the terms of the GNU General Public License version 3, as published |
2405 | -# by the Free Software Foundation. |
2406 | -# |
2407 | -# This program is distributed in the hope that it will be useful, but |
2408 | -# WITHOUT ANY WARRANTY; without even the implied warranties of |
2409 | -# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
2410 | -# PURPOSE. See the GNU General Public License for more details. |
2411 | -# |
2412 | -# You should have received a copy of the GNU General Public License along |
2413 | -# with this program. If not, see <http://www.gnu.org/licenses/>. |
2414 | -# |
2415 | -# In addition, as a special exception, the copyright holders give |
2416 | -# permission to link the code of portions of this program with the |
2417 | -# OpenSSL library under certain conditions as described in each |
2418 | -# individual source file, and distribute linked combinations |
2419 | -# including the two. |
2420 | -# You must obey the GNU General Public License in all respects |
2421 | -# for all of the code used other than OpenSSL. If you modify |
2422 | -# file(s) with this exception, you may extend this exception to your |
2423 | -# version of the file(s), but you are not obligated to do so. If you |
2424 | -# do not wish to do so, delete this exception statement from your |
2425 | -# version. If you delete this exception statement from all source |
2426 | -# files in the program, then also delete it here. |
2427 | -"""Tests for the proxy tunnel runner.""" |
2428 | - |
2429 | -from twisted.internet import defer, error, reactor, task |
2430 | -from twisted.trial.unittest import TestCase |
2431 | - |
2432 | -from ubuntuone.proxy.tests import FAKE_COOKIE |
2433 | -from ubuntuone.proxy import tunnel_client |
2434 | -from ubuntuone.syncdaemon import tunnel_runner |
2435 | - |
2436 | -FAKE_HOST = "fs-1.two.ubuntu.com" |
2437 | -FAKE_PORT = 443 |
2438 | - |
2439 | - |
2440 | -class TunnelRunnerConstructorTestCase(TestCase): |
2441 | - """Test the tunnel runner constructor.""" |
2442 | - |
2443 | - timeout = 3 |
2444 | - |
2445 | - def raise_import_error(self, *args): |
2446 | - """Raise an import error.""" |
2447 | - raise ImportError |
2448 | - |
2449 | - @defer.inlineCallbacks |
2450 | - def test_proxy_support_not_installed(self): |
2451 | - """The proxy support binary package is not installed.""" |
2452 | - self.patch(tunnel_runner.TunnelRunner, "start_process", |
2453 | - self.raise_import_error) |
2454 | - tr = tunnel_runner.TunnelRunner(FAKE_HOST, FAKE_PORT) |
2455 | - client = yield tr.get_client() |
2456 | - self.assertEqual(client, reactor) |
2457 | - |
2458 | - @defer.inlineCallbacks |
2459 | - def test_executable_not_found(self): |
2460 | - """The executable is not found anywhere.""" |
2461 | - self.patch(tunnel_runner, "get_tunnel_bin_cmd", |
2462 | - lambda *args, **kwargs: ["this_does_not_exist"]) |
2463 | - tr = tunnel_runner.TunnelRunner(FAKE_HOST, FAKE_PORT) |
2464 | - client = yield tr.get_client() |
2465 | - self.assertEqual(client, reactor) |
2466 | - |
2467 | - |
2468 | -class FakeProcessTransport(object): |
2469 | - """A fake ProcessTransport.""" |
2470 | - |
2471 | - pid = 0 |
2472 | - |
2473 | - def __init__(self): |
2474 | - """Initialize this fake.""" |
2475 | - self._signals_sent = [] |
2476 | - |
2477 | - def signalProcess(self, signalID): |
2478 | - """Send a signal to the process.""" |
2479 | - self._signals_sent.append(signalID) |
2480 | - |
2481 | - |
2482 | -class TunnelRunnerTestCase(TestCase): |
2483 | - """Tests for the TunnelRunner.""" |
2484 | - |
2485 | - timeout = 3 |
2486 | - |
2487 | - @defer.inlineCallbacks |
2488 | - def setUp(self): |
2489 | - """Initialize this testcase.""" |
2490 | - yield super(TunnelRunnerTestCase, self).setUp() |
2491 | - self.spawned = [] |
2492 | - self.triggers = [] |
2493 | - self.fake_process_transport = FakeProcessTransport() |
2494 | - |
2495 | - def fake_spawn_process(*args, **kwargs): |
2496 | - """A fake spawnProcess.""" |
2497 | - self.spawned.append((args, kwargs)) |
2498 | - return self.fake_process_transport |
2499 | - |
2500 | - self.patch(tunnel_client.reactor, "spawnProcess", fake_spawn_process) |
2501 | - |
2502 | - def fake_add_system_event_trigger(*args, **kwargs): |
2503 | - """A fake addSystemEventTrigger.""" |
2504 | - self.triggers.append((args, kwargs)) |
2505 | - |
2506 | - self.patch(tunnel_client.reactor, "addSystemEventTrigger", |
2507 | - fake_add_system_event_trigger) |
2508 | - self.process_protocol = None |
2509 | - self.process_protocol_class = tunnel_client.TunnelProcessProtocol |
2510 | - self.patch(tunnel_client, "TunnelProcessProtocol", |
2511 | - self.storing_process_protocol_factory) |
2512 | - self.tr = tunnel_runner.TunnelRunner("fs-1.one.ubuntu.com", 443) |
2513 | - |
2514 | - def storing_process_protocol_factory(self, *args, **kwargs): |
2515 | - """Store the process protocol just created.""" |
2516 | - self.process_protocol = self.process_protocol_class(*args, **kwargs) |
2517 | - return self.process_protocol |
2518 | - |
2519 | - def test_tunnel_process_is_started(self): |
2520 | - """The tunnel process is started.""" |
2521 | - self.assertEqual( |
2522 | - len(self.spawned), 1, "The tunnel process is started.") |
2523 | - |
2524 | - def test_system_event_finished(self): |
2525 | - """An event is added to stop the process with the reactor.""" |
2526 | - expected = [(("before", "shutdown", self.tr.stop), {})] |
2527 | - self.assertEqual(self.triggers, expected) |
2528 | - |
2529 | - def test_stop_process(self): |
2530 | - """The process is stopped if still running.""" |
2531 | - self.tr.process_transport.pid = 1234 |
2532 | - self.tr.stop() |
2533 | - self.assertEqual(self.fake_process_transport._signals_sent, ["KILL"]) |
2534 | - |
2535 | - def test_not_stopped_if_already_finished(self): |
2536 | - """Do not stop the tunnel process if it's already finished.""" |
2537 | - self.tr.process_transport.pid = None |
2538 | - self.tr.stop() |
2539 | - self.assertEqual(self.fake_process_transport._signals_sent, []) |
2540 | - |
2541 | - @defer.inlineCallbacks |
2542 | - def test_tunnel_process_get_client_yielded_twice(self): |
2543 | - """The get_client method can be yielded twice.""" |
2544 | - self.process_protocol.processExited(error.ProcessTerminated(1)) |
2545 | - client = yield self.tr.get_client() |
2546 | - client = yield self.tr.get_client() |
2547 | - self.assertNotEqual(client, None) |
2548 | - |
2549 | - @defer.inlineCallbacks |
2550 | - def test_tunnel_process_exits_with_error(self): |
2551 | - """The tunnel process exits with an error.""" |
2552 | - self.process_protocol.processExited(error.ProcessTerminated(1)) |
2553 | - client = yield self.tr.get_client() |
2554 | - self.assertEqual(client, reactor) |
2555 | - |
2556 | - @defer.inlineCallbacks |
2557 | - def test_tunnel_process_exits_gracefully(self): |
2558 | - """The tunnel process exits gracefully.""" |
2559 | - self.process_protocol.processExited(error.ProcessDone(0)) |
2560 | - client = yield self.tr.get_client() |
2561 | - self.assertEqual(client, reactor) |
2562 | - |
2563 | - @defer.inlineCallbacks |
2564 | - def test_tunnel_process_prints_random_garbage_and_timeouts(self): |
2565 | - """The tunnel process prints garbage and timeouts.""" |
2566 | - clock = task.Clock() |
2567 | - self.patch(tunnel_client, "reactor", clock) |
2568 | - self.process_protocol.connectionMade() |
2569 | - self.process_protocol.outReceived("Random garbage") |
2570 | - clock.advance(self.process_protocol.timeout) |
2571 | - client = yield self.tr.get_client() |
2572 | - self.assertEqual(client, clock) |
2573 | - |
2574 | - @defer.inlineCallbacks |
2575 | - def test_tunnel_process_prints_port_number_and_cookie(self): |
2576 | - """The tunnel process prints the port number.""" |
2577 | - received = "%s: %d\n%s: %s\n" % ( |
2578 | - tunnel_client.TUNNEL_PORT_LABEL, FAKE_PORT, |
2579 | - tunnel_client.TUNNEL_COOKIE_LABEL, FAKE_COOKIE) |
2580 | - self.process_protocol.outReceived(received) |
2581 | - client = yield self.tr.get_client() |
2582 | - self.assertEqual(client.tunnel_port, FAKE_PORT) |
2583 | - self.assertEqual(client.cookie, FAKE_COOKIE) |
2584 | |
2585 | === removed file 'ubuntuone/syncdaemon/tunnel_runner.py' |
2586 | --- ubuntuone/syncdaemon/tunnel_runner.py 2012-11-28 08:08:10 +0000 |
2587 | +++ ubuntuone/syncdaemon/tunnel_runner.py 1970-01-01 00:00:00 +0000 |
2588 | @@ -1,86 +0,0 @@ |
2589 | -# -*- coding: utf-8 -*- |
2590 | -# |
2591 | -# Copyright 2012 Canonical Ltd. |
2592 | -# |
2593 | -# This program is free software: you can redistribute it and/or modify it |
2594 | -# under the terms of the GNU General Public License version 3, as published |
2595 | -# by the Free Software Foundation. |
2596 | -# |
2597 | -# This program is distributed in the hope that it will be useful, but |
2598 | -# WITHOUT ANY WARRANTY; without even the implied warranties of |
2599 | -# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
2600 | -# PURPOSE. See the GNU General Public License for more details. |
2601 | -# |
2602 | -# You should have received a copy of the GNU General Public License along |
2603 | -# with this program. If not, see <http://www.gnu.org/licenses/>. |
2604 | -# |
2605 | -# In addition, as a special exception, the copyright holders give |
2606 | -# permission to link the code of portions of this program with the |
2607 | -# OpenSSL library under certain conditions as described in each |
2608 | -# individual source file, and distribute linked combinations |
2609 | -# including the two. |
2610 | -# You must obey the GNU General Public License in all respects |
2611 | -# for all of the code used other than OpenSSL. If you modify |
2612 | -# file(s) with this exception, you may extend this exception to your |
2613 | -# version of the file(s), but you are not obligated to do so. If you |
2614 | -# do not wish to do so, delete this exception statement from your |
2615 | -# version. If you delete this exception statement from all source |
2616 | -# files in the program, then also delete it here. |
2617 | -"""Run the tunnel process and start a client, with a reactor as a fallback.""" |
2618 | - |
2619 | -import logging |
2620 | - |
2621 | -from twisted.internet import defer, reactor |
2622 | - |
2623 | -from ubuntuone.clientdefs import LIBEXECDIR |
2624 | -from ubuntuone.syncdaemon.utils import get_tunnel_bin_cmd |
2625 | - |
2626 | -logger = logging.getLogger("ubuntuone.SyncDaemon.TunnelRunner") |
2627 | - |
2628 | - |
2629 | -class TunnelRunner(object): |
2630 | - """Run a tunnel process.""" |
2631 | - |
2632 | - def __init__(self, host, port): |
2633 | - """Start this runner instance.""" |
2634 | - self.client_d = defer.Deferred() |
2635 | - self.process_transport = None |
2636 | - try: |
2637 | - self.start_process(host, port) |
2638 | - except ImportError: |
2639 | - logger.info("Proxy support not installed.") |
2640 | - self.client_d.callback(reactor) |
2641 | - except Exception: |
2642 | - logger.exception("Error while starting tunnel process:") |
2643 | - self.client_d.callback(reactor) |
2644 | - |
2645 | - def start_process(self, host, port): |
2646 | - """Start the tunnel process.""" |
2647 | - from ubuntuone.proxy.tunnel_client import TunnelProcessProtocol |
2648 | - protocol = TunnelProcessProtocol(self.client_d) |
2649 | - tunnel_cmd = get_tunnel_bin_cmd(extra_fallbacks=[LIBEXECDIR]) |
2650 | - |
2651 | - args = tunnel_cmd + [host, str(port)] |
2652 | - |
2653 | - self.process_transport = reactor.spawnProcess(protocol, args[0], |
2654 | - env=None, args=args) |
2655 | - reactor.addSystemEventTrigger("before", "shutdown", self.stop) |
2656 | - |
2657 | - def stop(self): |
2658 | - """Stop the tunnel process if still running.""" |
2659 | - logger.info("Stopping process %r", self.process_transport.pid) |
2660 | - if self.process_transport.pid is not None: |
2661 | - self.process_transport.signalProcess("KILL") |
2662 | - |
2663 | - def get_client(self): |
2664 | - """A deferred with the reactor or a tunnel client.""" |
2665 | - |
2666 | - def client_selected(result, d): |
2667 | - """The tunnel_client or the reactor were selected.""" |
2668 | - d.callback(result) |
2669 | - # make sure the result is available for next callback |
2670 | - return result |
2671 | - |
2672 | - d = defer.Deferred() |
2673 | - self.client_d.addCallback(client_selected, d) |
2674 | - return d |
2675 | |
2676 | === modified file 'ubuntuone/syncdaemon/utils.py' |
2677 | --- ubuntuone/syncdaemon/utils.py 2015-09-20 00:03:47 +0000 |
2678 | +++ ubuntuone/syncdaemon/utils.py 2018-03-14 21:14:07 +0000 |
2679 | @@ -1,6 +1,7 @@ |
2680 | # -*- coding: utf-8 -*- |
2681 | # |
2682 | # Copyright 2012 Canonical Ltd. |
2683 | +# Copyright 2015-2018 Chicharreros (https://launchpad.net/~chicharreros) |
2684 | # |
2685 | # This program is free software: you can redistribute it and/or modify it |
2686 | # under the terms of the GNU General Public License version 3, as published |
2687 | @@ -42,10 +43,8 @@ |
2688 | |
2689 | |
2690 | SYNCDAEMON_EXECUTABLE = 'ubuntuone-syncdaemon' |
2691 | -TUNNEL_EXECUTABLE = 'ubuntuone-proxy-tunnel' |
2692 | |
2693 | -DARWIN_APP_NAMES = {SYNCDAEMON_EXECUTABLE: 'UbuntuOne Syncdaemon.app', |
2694 | - TUNNEL_EXECUTABLE: 'UbuntuOne Proxy Tunnel.app'} |
2695 | +DARWIN_APP_NAMES = {SYNCDAEMON_EXECUTABLE: 'UbuntuOne Syncdaemon.app'} |
2696 | |
2697 | |
2698 | def _get_bin_cmd(exe_name, extra_fallbacks=[]): |
2699 | @@ -73,9 +72,3 @@ |
2700 | def get_sd_bin_cmd(): |
2701 | """Get cmd + args to launch syncdaemon executable.""" |
2702 | return _get_bin_cmd(SYNCDAEMON_EXECUTABLE) |
2703 | - |
2704 | - |
2705 | -def get_tunnel_bin_cmd(extra_fallbacks): |
2706 | - """Get cmd + args to launch proxy tunnel.""" |
2707 | - return _get_bin_cmd(TUNNEL_EXECUTABLE, |
2708 | - extra_fallbacks=extra_fallbacks) |
2709 | |
2710 | === removed file 'ubuntuone/utils/gsettings.py' |
2711 | --- ubuntuone/utils/gsettings.py 2017-01-07 18:51:07 +0000 |
2712 | +++ ubuntuone/utils/gsettings.py 1970-01-01 00:00:00 +0000 |
2713 | @@ -1,114 +0,0 @@ |
2714 | -# -*- coding: utf-8 -*- |
2715 | -# |
2716 | -# Copyright 2011-2012 Canonical Ltd. |
2717 | -# Copyright 2015-2016 Chicharreros (https://launchpad.net/~chicharreros) |
2718 | -# |
2719 | -# This program is free software: you can redistribute it and/or modify it |
2720 | -# under the terms of the GNU General Public License version 3, as published |
2721 | -# by the Free Software Foundation. |
2722 | -# |
2723 | -# This program is distributed in the hope that it will be useful, but |
2724 | -# WITHOUT ANY WARRANTY; without even the implied warranties of |
2725 | -# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
2726 | -# PURPOSE. See the GNU General Public License for more details. |
2727 | -# |
2728 | -# You should have received a copy of the GNU General Public License along |
2729 | -# with this program. If not, see <http://www.gnu.org/licenses/>. |
2730 | -# |
2731 | -# In addition, as a special exception, the copyright holders give |
2732 | -# permission to link the code of portions of this program with the |
2733 | -# OpenSSL library under certain conditions as described in each |
2734 | -# individual source file, and distribute linked combinations |
2735 | -# including the two. |
2736 | -# You must obey the GNU General Public License in all respects |
2737 | -# for all of the code used other than OpenSSL. If you modify |
2738 | -# file(s) with this exception, you may extend this exception to your |
2739 | -# version of the file(s), but you are not obligated to do so. If you |
2740 | -# do not wish to do so, delete this exception statement from your |
2741 | -# version. If you delete this exception statement from all source |
2742 | -# files in the program, then also delete it here. |
2743 | - |
2744 | -"""Retrieve the proxy configuration from Gnome.""" |
2745 | - |
2746 | -import logging |
2747 | -import subprocess |
2748 | - |
2749 | - |
2750 | -logger = logging.getLogger(__name__) |
2751 | -GSETTINGS_CMDLINE = "gsettings list-recursively org.gnome.system.proxy" |
2752 | -CANNOT_PARSE_WARNING = "Cannot parse gsettings value: %r" |
2753 | - |
2754 | - |
2755 | -def parse_proxy_host(hostname): |
2756 | - """Parse the host to get username and password.""" |
2757 | - username = None |
2758 | - password = None |
2759 | - if "@" in hostname: |
2760 | - username, hostname = hostname.rsplit("@", 1) |
2761 | - if ":" in username: |
2762 | - username, password = username.split(":", 1) |
2763 | - return hostname, username, password |
2764 | - |
2765 | - |
2766 | -def parse_manual_proxy_settings(scheme, gsettings): |
2767 | - """Parse the settings for a given scheme.""" |
2768 | - host, username, pwd = parse_proxy_host(gsettings[scheme + ".host"]) |
2769 | - # if the user did not set a proxy for a type (http/https/ftp) we should |
2770 | - # return None to ensure that it is not used |
2771 | - if host == '': |
2772 | - return None |
2773 | - |
2774 | - settings = { |
2775 | - "host": host, |
2776 | - "port": gsettings[scheme + ".port"], |
2777 | - } |
2778 | - if scheme == "http" and gsettings["http.use-authentication"]: |
2779 | - username = gsettings["http.authentication-user"] |
2780 | - pwd = gsettings["http.authentication-password"] |
2781 | - if username is not None and pwd is not None: |
2782 | - settings.update({ |
2783 | - "username": username, |
2784 | - "password": pwd, |
2785 | - }) |
2786 | - return settings |
2787 | - |
2788 | - |
2789 | -def get_proxy_settings(): |
2790 | - """Parse the proxy settings as returned by the gsettings executable.""" |
2791 | - output = subprocess.check_output(GSETTINGS_CMDLINE.split()) |
2792 | - gsettings = {} |
2793 | - base_len = len("org.gnome.system.proxy.") |
2794 | - |
2795 | - for line in output.split("\n"): |
2796 | - try: |
2797 | - path, key, value = line.split(" ", 2) |
2798 | - except ValueError: |
2799 | - continue |
2800 | - if value.startswith("'"): |
2801 | - parsed_value = value[1:-1] |
2802 | - elif value.startswith(('[', '@')): |
2803 | - parsed_value = value |
2804 | - elif value in ('true', 'false'): |
2805 | - parsed_value = (value == 'true') |
2806 | - elif value.isdigit(): |
2807 | - parsed_value = int(value) |
2808 | - else: |
2809 | - logger.warning(CANNOT_PARSE_WARNING, value) |
2810 | - parsed_value = value |
2811 | - relative_key = (path + "." + key)[base_len:] |
2812 | - gsettings[relative_key] = parsed_value |
2813 | - mode = gsettings["mode"] |
2814 | - if mode == "none": |
2815 | - settings = {} |
2816 | - elif mode == "manual": |
2817 | - settings = {} |
2818 | - for scheme in ["http", "https"]: |
2819 | - scheme_settings = parse_manual_proxy_settings(scheme, gsettings) |
2820 | - if scheme_settings is not None: |
2821 | - settings[scheme] = scheme_settings |
2822 | - else: |
2823 | - # If mode is automatic the PAC javascript should be interpreted |
2824 | - # on each request. That is out of scope so it's ignored for now |
2825 | - settings = {} |
2826 | - |
2827 | - return settings |
2828 | |
2829 | === removed file 'ubuntuone/utils/tests/test_gsettings.py' |
2830 | --- ubuntuone/utils/tests/test_gsettings.py 2017-01-07 18:51:07 +0000 |
2831 | +++ ubuntuone/utils/tests/test_gsettings.py 1970-01-01 00:00:00 +0000 |
2832 | @@ -1,322 +0,0 @@ |
2833 | -# -*- coding: utf-8 -*- |
2834 | -# |
2835 | -# Copyright 2011-2012 Canonical Ltd. |
2836 | -# Copyright 2015-2017 Chicharreros (https://launchpad.net/~chicharreros) |
2837 | -# |
2838 | -# This program is free software: you can redistribute it and/or modify it |
2839 | -# under the terms of the GNU General Public License version 3, as published |
2840 | -# by the Free Software Foundation. |
2841 | -# |
2842 | -# This program is distributed in the hope that it will be useful, but |
2843 | -# WITHOUT ANY WARRANTY; without even the implied warranties of |
2844 | -# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
2845 | -# PURPOSE. See the GNU General Public License for more details. |
2846 | -# |
2847 | -# You should have received a copy of the GNU General Public License along |
2848 | -# with this program. If not, see <http://www.gnu.org/licenses/>. |
2849 | -# |
2850 | -# In addition, as a special exception, the copyright holders give |
2851 | -# permission to link the code of portions of this program with the |
2852 | -# OpenSSL library under certain conditions as described in each |
2853 | -# individual source file, and distribute linked combinations |
2854 | -# including the two. |
2855 | -# You must obey the GNU General Public License in all respects |
2856 | -# for all of the code used other than OpenSSL. If you modify |
2857 | -# file(s) with this exception, you may extend this exception to your |
2858 | -# version of the file(s), but you are not obligated to do so. If you |
2859 | -# do not wish to do so, delete this exception statement from your |
2860 | -# version. If you delete this exception statement from all source |
2861 | -# files in the program, then also delete it here. |
2862 | - |
2863 | -"""Test the gsettings parser.""" |
2864 | - |
2865 | -import logging |
2866 | - |
2867 | -from twisted.trial.unittest import TestCase |
2868 | -from ubuntuone.devtools.handlers import MementoHandler |
2869 | - |
2870 | -from ubuntuone.utils import gsettings |
2871 | - |
2872 | -TEMPLATE_GSETTINGS_OUTPUT = """\ |
2873 | -org.gnome.system.proxy autoconfig-url '{autoconfig_url}' |
2874 | -org.gnome.system.proxy ignore-hosts {ignore_hosts:s} |
2875 | -org.gnome.system.proxy mode '{mode}' |
2876 | -org.gnome.system.proxy.ftp host '{ftp_host}' |
2877 | -org.gnome.system.proxy.ftp port {ftp_port} |
2878 | -org.gnome.system.proxy.http authentication-password '{auth_password}' |
2879 | -org.gnome.system.proxy.http authentication-user '{auth_user}' |
2880 | -org.gnome.system.proxy.http host '{http_host}' |
2881 | -org.gnome.system.proxy.http port {http_port} |
2882 | -org.gnome.system.proxy.http use-authentication {http_use_auth} |
2883 | -org.gnome.system.proxy.https host '{https_host}' |
2884 | -org.gnome.system.proxy.https port {https_port} |
2885 | -org.gnome.system.proxy.socks host '{socks_host}' |
2886 | -org.gnome.system.proxy.socks port {socks_port} |
2887 | -""" |
2888 | - |
2889 | -BASE_GSETTINGS_VALUES = { |
2890 | - "autoconfig_url": "", |
2891 | - "ignore_hosts": ["localhost", "127.0.0.0/8"], |
2892 | - "mode": "none", |
2893 | - "ftp_host": "", |
2894 | - "ftp_port": 0, |
2895 | - "auth_password": "", |
2896 | - "auth_user": "", |
2897 | - "http_host": "", |
2898 | - "http_port": 0, |
2899 | - "http_use_auth": "false", |
2900 | - "https_host": "", |
2901 | - "https_port": 0, |
2902 | - "socks_host": "", |
2903 | - "socks_port": 0, |
2904 | -} |
2905 | - |
2906 | - |
2907 | -class ProxySettingsTestCase(TestCase): |
2908 | - """Test the getting of the proxy settings.""" |
2909 | - |
2910 | - def test_gsettings_cmdline_correct(self): |
2911 | - """The command line used to get the proxy settings is the right one.""" |
2912 | - expected = "gsettings list-recursively org.gnome.system.proxy".split() |
2913 | - called = [] |
2914 | - |
2915 | - def append_output(args): |
2916 | - """Append the output and return some settings.""" |
2917 | - called.append(args) |
2918 | - return TEMPLATE_GSETTINGS_OUTPUT.format(**BASE_GSETTINGS_VALUES) |
2919 | - |
2920 | - self.patch(gsettings.subprocess, "check_output", append_output) |
2921 | - gsettings.get_proxy_settings() |
2922 | - self.assertEqual(called[0], expected) |
2923 | - |
2924 | - def test_gsettings_parser_none(self): |
2925 | - """Test a parser of gsettings.""" |
2926 | - expected = {} |
2927 | - fake_output = TEMPLATE_GSETTINGS_OUTPUT.format(**BASE_GSETTINGS_VALUES) |
2928 | - self.patch(gsettings.subprocess, "check_output", |
2929 | - lambda _: fake_output) |
2930 | - ps = gsettings.get_proxy_settings() |
2931 | - self.assertEqual(ps, expected) |
2932 | - |
2933 | - def _assert_parser_anonymous(self, scheme): |
2934 | - """Assert the parsing of anonymous settings.""" |
2935 | - template_values = dict(BASE_GSETTINGS_VALUES) |
2936 | - expected_host = "expected_host" |
2937 | - expected_port = 54321 |
2938 | - expected = { |
2939 | - "host": expected_host, |
2940 | - "port": expected_port, |
2941 | - } |
2942 | - template_values.update({ |
2943 | - "mode": "manual", |
2944 | - scheme + "_host": expected_host, |
2945 | - scheme + "_port": expected_port, |
2946 | - }) |
2947 | - fake_output = TEMPLATE_GSETTINGS_OUTPUT.format(**template_values) |
2948 | - self.patch(gsettings.subprocess, "check_output", |
2949 | - lambda _: fake_output) |
2950 | - ps = gsettings.get_proxy_settings() |
2951 | - self.assertEqual(ps[scheme], expected) |
2952 | - |
2953 | - def test_gsettings_parser_http_anonymous(self): |
2954 | - """Test a parser of gsettings.""" |
2955 | - self._assert_parser_anonymous('http') |
2956 | - |
2957 | - def test_gsettings_parser_https_anonymus(self): |
2958 | - """Test a parser of gsettings.""" |
2959 | - self._assert_parser_anonymous('https') |
2960 | - |
2961 | - def test_gsettings_empty_ignore_hosts(self): |
2962 | - """Missing values in the ignore hosts.""" |
2963 | - troublesome_value = "@as []" |
2964 | - template_values = dict(BASE_GSETTINGS_VALUES) |
2965 | - template_values["ignore_hosts"] = troublesome_value |
2966 | - fake_output = TEMPLATE_GSETTINGS_OUTPUT.format(**template_values) |
2967 | - self.patch(gsettings.subprocess, "check_output", |
2968 | - lambda _: fake_output) |
2969 | - ps = gsettings.get_proxy_settings() |
2970 | - self.assertEqual(ps, {}) |
2971 | - |
2972 | - def test_gsettings_cannot_parse(self): |
2973 | - """Some weird setting that cannot be parsed is logged with warning.""" |
2974 | - memento = MementoHandler() |
2975 | - memento.setLevel(logging.DEBUG) |
2976 | - gsettings.logger.addHandler(memento) |
2977 | - self.addCleanup(gsettings.logger.removeHandler, memento) |
2978 | - |
2979 | - troublesome_value = "#bang" |
2980 | - template_values = dict(BASE_GSETTINGS_VALUES) |
2981 | - template_values["ignore_hosts"] = troublesome_value |
2982 | - fake_output = TEMPLATE_GSETTINGS_OUTPUT.format(**template_values) |
2983 | - self.patch(gsettings.subprocess, "check_output", |
2984 | - lambda _: fake_output) |
2985 | - ps = gsettings.get_proxy_settings() |
2986 | - self.assertTrue(memento.check_warning(gsettings.CANNOT_PARSE_WARNING % |
2987 | - troublesome_value)) |
2988 | - self.assertEqual(ps, {}) |
2989 | - |
2990 | - def test_gsettings_parser_http_authenticated(self): |
2991 | - """Test a parser of gsettings.""" |
2992 | - template_values = dict(BASE_GSETTINGS_VALUES) |
2993 | - expected_host = "expected_host" |
2994 | - expected_port = 54321 |
2995 | - expected_user = "carlitos" |
2996 | - expected_password = "very secret password" |
2997 | - expected = { |
2998 | - "host": expected_host, |
2999 | - "port": expected_port, |
3000 | - "username": expected_user, |
3001 | - "password": expected_password, |
3002 | - } |
3003 | - template_values.update({ |
3004 | - "mode": "manual", |
3005 | - "http_host": expected_host, |
3006 | - "http_port": expected_port, |
3007 | - "auth_user": expected_user, |
3008 | - "auth_password": expected_password, |
3009 | - "http_use_auth": "true", |
3010 | - }) |
3011 | - fake_output = TEMPLATE_GSETTINGS_OUTPUT.format(**template_values) |
3012 | - self.patch(gsettings.subprocess, "check_output", |
3013 | - lambda _: fake_output) |
3014 | - ps = gsettings.get_proxy_settings() |
3015 | - self.assertEqual(ps["http"], expected) |
3016 | - |
3017 | - def _assert_parser_authenticated_url(self, scheme): |
3018 | - """Test a parser of gsettings with creds in the url.""" |
3019 | - template_values = dict(BASE_GSETTINGS_VALUES) |
3020 | - expected_host = "expected_host" |
3021 | - expected_port = 54321 |
3022 | - expected_user = "carlitos" |
3023 | - expected_password = "very secret password" |
3024 | - composed_url = '%s:%s@%s' % (expected_user, expected_password, |
3025 | - expected_host) |
3026 | - expected = { |
3027 | - "host": expected_host, |
3028 | - "port": expected_port, |
3029 | - "username": expected_user, |
3030 | - "password": expected_password, |
3031 | - } |
3032 | - template_values.update({ |
3033 | - "mode": "manual", |
3034 | - scheme + "_host": composed_url, |
3035 | - scheme + "_port": expected_port, |
3036 | - "http_use_auth": "false", |
3037 | - }) |
3038 | - fake_output = TEMPLATE_GSETTINGS_OUTPUT.format(**template_values) |
3039 | - self.patch(gsettings.subprocess, "check_output", |
3040 | - lambda _: fake_output) |
3041 | - ps = gsettings.get_proxy_settings() |
3042 | - self.assertEqual(ps[scheme], expected) |
3043 | - |
3044 | - def test_gsettings_parser_http_authenticated_url(self): |
3045 | - """Test a parser of gsettings with creds in the url.""" |
3046 | - self._assert_parser_authenticated_url('http') |
3047 | - |
3048 | - def test_gsettings_parser_https_authenticated_url(self): |
3049 | - """Test a parser of gsettings with creds in the url.""" |
3050 | - self._assert_parser_authenticated_url('https') |
3051 | - |
3052 | - def test_gsettings_auth_over_url(self): |
3053 | - """Test that the settings are more important that the url.""" |
3054 | - template_values = dict(BASE_GSETTINGS_VALUES) |
3055 | - expected_host = "expected_host" |
3056 | - expected_port = 54321 |
3057 | - expected_user = "carlitos" |
3058 | - expected_password = "very secret password" |
3059 | - composed_url = '%s:%s@%s' % ('user', 'random', |
3060 | - expected_host) |
3061 | - http_expected = { |
3062 | - "host": expected_host, |
3063 | - "port": expected_port, |
3064 | - "username": expected_user, |
3065 | - "password": expected_password, |
3066 | - } |
3067 | - template_values.update({ |
3068 | - "mode": "manual", |
3069 | - "http_host": composed_url, |
3070 | - "http_port": expected_port, |
3071 | - "auth_user": expected_user, |
3072 | - "auth_password": expected_password, |
3073 | - "http_use_auth": "true", |
3074 | - }) |
3075 | - fake_output = TEMPLATE_GSETTINGS_OUTPUT.format(**template_values) |
3076 | - self.patch(gsettings.subprocess, "check_output", |
3077 | - lambda _: fake_output) |
3078 | - ps = gsettings.get_proxy_settings() |
3079 | - self.assertEqual(ps["http"], http_expected) |
3080 | - |
3081 | - def _assert_parser_empty_url(self, scheme): |
3082 | - """Assert the parsing of an empty url.""" |
3083 | - template_values = dict(BASE_GSETTINGS_VALUES) |
3084 | - template_values.update({ |
3085 | - "mode": "manual", |
3086 | - scheme + "_host": '', |
3087 | - scheme + "_port": 0, |
3088 | - "http_use_auth": "false", |
3089 | - }) |
3090 | - fake_output = TEMPLATE_GSETTINGS_OUTPUT.format(**template_values) |
3091 | - self.patch(gsettings.subprocess, "check_output", |
3092 | - lambda _: fake_output) |
3093 | - ps = gsettings.get_proxy_settings() |
3094 | - self.assertNotIn(scheme, ps) |
3095 | - |
3096 | - def test_gsettings_parser_empty_http_url(self): |
3097 | - """Test when there is no http proxy set.""" |
3098 | - self._assert_parser_empty_url('http') |
3099 | - |
3100 | - def test_gsettings_parser_empty_https_url(self): |
3101 | - """Test when there is no https proxy set.""" |
3102 | - self._assert_parser_empty_url('https') |
3103 | - |
3104 | - |
3105 | -class ParseProxyHostTestCase(TestCase): |
3106 | - """Test the parsing of the domain.""" |
3107 | - |
3108 | - def test_onlyhost(self): |
3109 | - """Parse a host with no username or password.""" |
3110 | - sample = "hostname" |
3111 | - hostname, username, password = gsettings.parse_proxy_host(sample) |
3112 | - self.assertEqual(username, None) |
3113 | - self.assertEqual(password, None) |
3114 | - self.assertEqual(hostname, "hostname") |
3115 | - |
3116 | - def test_user_and_host(self): |
3117 | - """Parse host just with the username.""" |
3118 | - sample = "username@hostname" |
3119 | - hostname, username, password = gsettings.parse_proxy_host(sample) |
3120 | - self.assertEqual(username, "username") |
3121 | - self.assertEqual(password, None) |
3122 | - self.assertEqual(hostname, "hostname") |
3123 | - |
3124 | - def test_user_pass_and_host(self): |
3125 | - """Test parsing a host with a username and password.""" |
3126 | - sample = "username:password@hostname" |
3127 | - hostname, username, password = gsettings.parse_proxy_host(sample) |
3128 | - self.assertEqual(username, "username") |
3129 | - self.assertEqual(password, "password") |
3130 | - self.assertEqual(hostname, "hostname") |
3131 | - |
3132 | - def test_username_with_at(self): |
3133 | - """Test parsing the host with a username with @.""" |
3134 | - sample = "username@company.com:password@hostname" |
3135 | - hostname, username, password = gsettings.parse_proxy_host(sample) |
3136 | - self.assertEqual(username, "username@company.com") |
3137 | - self.assertEqual(password, "password") |
3138 | - self.assertEqual(hostname, "hostname") |
3139 | - |
3140 | - def test_username_with_at_nopass(self): |
3141 | - """Test parsing the host without a password.""" |
3142 | - sample = "username@company.com@hostname" |
3143 | - hostname, username, password = gsettings.parse_proxy_host(sample) |
3144 | - self.assertEqual(username, "username@company.com") |
3145 | - self.assertEqual(password, None) |
3146 | - self.assertEqual(hostname, "hostname") |
3147 | - |
3148 | - def test_user_pass_with_colon_and_host(self): |
3149 | - """Test parsing the host with a password that contains :.""" |
3150 | - sample = "username:pass:word@hostname" |
3151 | - hostname, username, password = gsettings.parse_proxy_host(sample) |
3152 | - self.assertEqual(username, "username") |
3153 | - self.assertEqual(password, "pass:word") |
3154 | - self.assertEqual(hostname, "hostname") |
Thanks!