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

Proposed by Alejandro J. Cura
Status: Merged
Approved by: dobey
Approved revision: 1219
Merged at revision: 1204
Proposed branch: lp:~alecu/ubuntuone-client/proxy-tunnel-process
Merge into: lp:ubuntuone-client
Prerequisite: lp:~alecu/ubuntuone-client/proxy-tunnel-client
Diff against target: 266 lines (+188/-1)
4 files modified
Makefile.am (+1/-0)
bin/ubuntuone-proxy-tunnel (+24/-0)
tests/proxy/test_tunnel_server.py (+132/-1)
ubuntuone/proxy/tunnel_server.py (+31/-0)
To merge this branch: bzr merge lp:~alecu/ubuntuone-client/proxy-tunnel-process
Reviewer Review Type Date Requested Status
Alejandro J. Cura (community) Abstain
Eric Casteleijn (community) Approve
Manuel de la Peña (community) Approve
Review via email: mp+96212@code.launchpad.net

Commit message

- A proxy tunnel process to be started when proxies are enabled (LP: #929207).

Description of the change

- A proxy tunnel process to be started when proxies are enabled. (LP: #929207)

To post a comment you must log in.
Revision history for this message
Manuel de la Peña (mandel) wrote :

A very stupid comments but why doing:

if len(proxies) and proxies[0].type() != QNetworkProxy.NoProxy:
    return True

When you an do:

return len(proxies) and proxies[0].type() != QNetworkProxy.NoProxy

same for:

if settings:
    return True

In IsProxyEnabledTestCase I think you can merge the tests cases, for example:

             def _assert_enabled(self, platform):
                 """Assert that the proxy is enabled."""
                 self.patch(tunnel_server.sys, "platform", platform)
                 ret = tunnel_server.is_proxy_enabled(SAMPLE_HOST, str(SAMPLE_PORT))
                 self.assertTrue(ret, "Proxy is enabled.")

             def test_platform_linux_enabled(self):
                 """Tests for the linux platform with proxies enabled."""
                 self.patch(tunnel_server.gsettings, "get_proxy_settings",
                            lambda: FAKE_SETTINGS)
                 self._assert_enabled('linux3')

             def test_platform_other_enabled(self):
                 """Tests for any other platform with proxies enabled."""
                 fake_netproxfact = FakeNetworkProxyFactoryClass(True)
                 self.patch(tunnel_server, "QNetworkProxyFactory", fake_netproxfact)
                 self._assert_enabled('windows 1.0')

Same for the disabled one.

I think it would be a good idea to use sys.exit in main specially in the case were there is no proxy.

review: Needs Fixing
Revision history for this message
Alejandro J. Cura (alecu) wrote :

> A very stupid comments but why doing:
>
> if len(proxies) and proxies[0].type() != QNetworkProxy.NoProxy:
> return True
>
> When you an do:
>
> return len(proxies) and proxies[0].type() != QNetworkProxy.NoProxy
>
> same for:
>
> if settings:
> return True
>
> In IsProxyEnabledTestCase I think you can merge the tests cases, for example:
>
> def _assert_enabled(self, platform):
> """Assert that the proxy is enabled."""
> self.patch(tunnel_server.sys, "platform", platform)
> ret = tunnel_server.is_proxy_enabled(SAMPLE_HOST,
> str(SAMPLE_PORT))
> self.assertTrue(ret, "Proxy is enabled.")
>
> def test_platform_linux_enabled(self):
> """Tests for the linux platform with proxies enabled."""
> self.patch(tunnel_server.gsettings, "get_proxy_settings",
> lambda: FAKE_SETTINGS)
> self._assert_enabled('linux3')
>
> def test_platform_other_enabled(self):
> """Tests for any other platform with proxies enabled."""
> fake_netproxfact = FakeNetworkProxyFactoryClass(True)
> self.patch(tunnel_server, "QNetworkProxyFactory",
> fake_netproxfact)
> self._assert_enabled('windows 1.0')
>
> Same for the disabled one.

Refactored all those tests as suggested.

> I think it would be a good idea to use sys.exit in main specially in the case
> were there is no proxy.

It's a normal condition for this process to exit if there's no proxy configured, so I think that it would be redundant to use sys.exit in this case.

Thanks for the review.

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

Superb!

review: Approve
Revision history for this message
Eric Casteleijn (thisfred) wrote :

Looks great, two small observations that don't need any changes on this branch:

1. if we pass too many arguments to tunnel_server.py, we'll get strange errors, but since it doesn't look like anything users will ever call themselves, it's probably fine.

2. The new Python 3 style formatting is nice, but it means we need 2.7+ for this to work (which may already be a requirement,) but also, it's reportedly *way* less efficient than the old string formatting syntax, and there are no plans to ever remove that from the language, which makes me think we should probably just stick with the old one.

review: Approve
Revision history for this message
Alejandro J. Cura (alecu) wrote :

> 2. The new Python 3 style formatting is nice, but it means we need 2.7+ for
> this to work (which may already be a requirement,) but also, it's reportedly
> *way* less efficient than the old string formatting syntax, and there are no
> plans to ever remove that from the language, which makes me think we should
> probably just stick with the old one.

This is a very good point.
Since str.format() is not being used anywhere else in u1-client I'll change this in the following branch to use the % formatting operator.

Thanks for the review!

Revision history for this message
Ubuntu One Auto Pilot (otto-pilot) wrote :

Voting does not meet specified criteria. Required: Approve >= 1, Disapprove == 0, Needs Fixing == 0, Needs Information == 0, Resubmit == 0, Pending == 0. Got: 2 Approve, 1 Pending.

Revision history for this message
Alejandro J. Cura (alecu) :
review: Abstain

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'Makefile.am'
--- Makefile.am 2012-02-24 17:57:27 +0000
+++ Makefile.am 2012-03-09 16:20:31 +0000
@@ -19,6 +19,7 @@
1919
20libexec_SCRIPTS = \20libexec_SCRIPTS = \
21 bin/ubuntuone-syncdaemon \21 bin/ubuntuone-syncdaemon \
22 bin/ubuntuone-proxy-tunnel \
22 bin/ubuntuone-login23 bin/ubuntuone-login
2324
24clientdefsdir = $(pythonpkgdir)/ubuntuone25clientdefsdir = $(pythonpkgdir)/ubuntuone
2526
=== added file 'bin/ubuntuone-proxy-tunnel'
--- bin/ubuntuone-proxy-tunnel 1970-01-01 00:00:00 +0000
+++ bin/ubuntuone-proxy-tunnel 2012-03-09 16:20:31 +0000
@@ -0,0 +1,24 @@
1#!/usr/bin/python
2#
3# Copyright 2012 Canonical Ltd.
4#
5# This program is free software: you can redistribute it and/or modify it
6# under the terms of the GNU General Public License version 3, as published
7# by the Free Software Foundation.
8#
9# This program is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranties of
11# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
12# PURPOSE. See the GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License along
15# with this program. If not, see <http://www.gnu.org/licenses/>.
16
17"""Ubuntu One tunnel for proxy support."""
18
19import sys
20
21from ubuntuone.proxy.tunnel_server import main
22
23if __name__ == "__main__":
24 main(sys.argv)
025
=== modified file 'tests/proxy/test_tunnel_server.py'
--- tests/proxy/test_tunnel_server.py 2012-03-09 14:36:34 +0000
+++ tests/proxy/test_tunnel_server.py 2012-03-09 16:20:31 +0000
@@ -15,10 +15,12 @@
15# with this program. If not, see <http://www.gnu.org/licenses/>.15# with this program. If not, see <http://www.gnu.org/licenses/>.
16"""Tests for the proxy tunnel."""16"""Tests for the proxy tunnel."""
1717
18from StringIO import StringIO
18from urlparse import urlparse19from urlparse import urlparse
1920
20from twisted.internet import defer, protocol, reactor21from twisted.internet import defer, protocol, reactor
2122from twisted.trial.unittest import TestCase
23from PyQt4.QtCore import QCoreApplication
22from ubuntuone.devtools.testcases.squid import SquidTestCase24from ubuntuone.devtools.testcases.squid import SquidTestCase
2325
24from tests.proxy import (26from tests.proxy import (
@@ -39,6 +41,14 @@
39 "GET {0.path} HTTP/1.0" + CRLF + CRLF41 "GET {0.path} HTTP/1.0" + CRLF + CRLF
40)42)
4143
44FAKE_SETTINGS = {
45 "host": "myhost",
46 "port": "8888",
47}
48
49SAMPLE_HOST = "samplehost.com"
50SAMPLE_PORT = 443
51
4252
43class DisconnectingProtocol(protocol.Protocol):53class DisconnectingProtocol(protocol.Protocol):
44 """A protocol that just disconnects."""54 """A protocol that just disconnects."""
@@ -351,3 +361,124 @@
351 """Tests for the client going thru an authenticated proxy."""361 """Tests for the client going thru an authenticated proxy."""
352362
353 get_proxy_settings = RemoteSocketTestCase.get_auth_proxy_settings363 get_proxy_settings = RemoteSocketTestCase.get_auth_proxy_settings
364
365
366class FakeNetworkProxyFactoryClass(object):
367 """A fake QNetworkProxyFactory."""
368 last_query = None
369
370 def __init__(self, enabled):
371 """Initialize this fake instance."""
372 if enabled:
373 self.proxy_type = tunnel_server.QNetworkProxy.HttpProxy
374 else:
375 self.proxy_type = tunnel_server.QNetworkProxy.NoProxy
376
377 def type(self):
378 """Return the proxy type configured."""
379 return self.proxy_type
380
381 def systemProxyForQuery(self, query):
382 """A list of proxies, but only type() will be called on the first."""
383 return [self]
384
385
386class IsProxyEnabledTestCase(TestCase):
387 """Tests for the is_proxy_enabled function."""
388
389 def _assert_proxy_state(self, platform, state, assertion):
390 """Assert the proxy is in a given state."""
391 self.patch(tunnel_server.sys, "platform", platform)
392 ret = tunnel_server.is_proxy_enabled(SAMPLE_HOST, str(SAMPLE_PORT))
393 self.assertTrue(ret == state, assertion)
394
395 def _assert_proxy_enabled(self, platform):
396 """Assert that the proxy is enabled."""
397 self._assert_proxy_state(platform, True, "Proxy is enabled.")
398
399 def _assert_proxy_disabled(self, platform):
400 """Assert that the proxy is disabled."""
401 self._assert_proxy_state(platform, False, "Proxy is disabled.")
402
403 def test_platform_linux_enabled(self):
404 """Tests for the linux platform with proxies enabled."""
405 self.patch(tunnel_server.gsettings, "get_proxy_settings",
406 lambda: FAKE_SETTINGS)
407 self._assert_proxy_enabled("linux3")
408
409 def test_platform_linux_disabled(self):
410 """Tests for the linux platform with proxies disabled."""
411 self.patch(tunnel_server.gsettings, "get_proxy_settings", lambda: {})
412 self._assert_proxy_disabled("linux3")
413
414 def test_platform_other_enabled(self):
415 """Tests for any other platform with proxies enabled."""
416 fake_netproxfact = FakeNetworkProxyFactoryClass(True)
417 self.patch(tunnel_server, "QNetworkProxyFactory", fake_netproxfact)
418 self._assert_proxy_enabled("windows 1.0")
419
420 def test_platform_other_disabled(self):
421 """Tests for any other platform with proxies disabled."""
422 fake_netproxfact = FakeNetworkProxyFactoryClass(False)
423 self.patch(tunnel_server, "QNetworkProxyFactory", fake_netproxfact)
424 self._assert_proxy_disabled("windows 1.0")
425
426
427class FakeQCoreApp(object):
428 """A fake QCoreApplication."""
429
430 fake_instance = None
431
432 def __init__(self, argv):
433 """Initialize this fake."""
434 self.executed = False
435 self.argv = argv
436 FakeQCoreApp.fake_instance = self
437
438 def exec_(self):
439 """Fake the execution of this app."""
440 self.executed = True
441
442 @staticmethod
443 def instance():
444 """But return the real instance."""
445 return QCoreApplication.instance()
446
447
448class MainFunctionTestCase(TestCase):
449 """Tests for the main function of the tunnel server."""
450
451 @defer.inlineCallbacks
452 def setUp(self):
453 """Initialize these testcases."""
454 yield super(MainFunctionTestCase, self).setUp()
455 self.called = []
456 self.proxies_enabled = False
457
458 def fake_is_proxy_enabled(*args):
459 """Store the call, return false."""
460 self.called.append(args)
461 return self.proxies_enabled
462
463 self.patch(tunnel_server, "is_proxy_enabled", fake_is_proxy_enabled)
464 self.fake_stdout = StringIO()
465 self.patch(tunnel_server.sys, "stdout", self.fake_stdout)
466 self.patch(tunnel_server, "QCoreApplication", FakeQCoreApp)
467
468 def test_checks_proxies(self):
469 """Main checks that the proxies are enabled."""
470 tunnel_server.main([])
471 self.assertEqual(len(self.called), 1)
472
473 def test_on_proxies_enabled_prints_port(self):
474 """With proxies enabled print port to stdout and start the mainloop."""
475 self.proxies_enabled = True
476 tunnel_server.main(["example.com", "443"])
477 self.assertIn("Tunnel port:", self.fake_stdout.getvalue())
478
479 def test_on_proxies_disabled_exit(self):
480 """With proxies disabled, print a message and exit gracefully."""
481 self.proxies_enabled = False
482 tunnel_server.main(["example.com", "443"])
483 self.assertIn("Proxy not enabled.", self.fake_stdout.getvalue())
484 self.assertEqual(FakeQCoreApp.fake_instance, None)
354485
=== modified file 'ubuntuone/proxy/tunnel_server.py'
--- ubuntuone/proxy/tunnel_server.py 2012-03-07 23:43:07 +0000
+++ ubuntuone/proxy/tunnel_server.py 2012-03-09 16:20:31 +0000
@@ -29,18 +29,22 @@
2929
30"""30"""
3131
32import sys
3233
33from PyQt4.QtCore import QCoreApplication, QTimer34from PyQt4.QtCore import QCoreApplication, QTimer
34from PyQt4.QtNetwork import (35from PyQt4.QtNetwork import (
35 QAbstractSocket,36 QAbstractSocket,
36 QHostAddress,37 QHostAddress,
37 QNetworkProxy,38 QNetworkProxy,
39 QNetworkProxyQuery,
40 QNetworkProxyFactory,
38 QTcpServer,41 QTcpServer,
39 QTcpSocket,42 QTcpSocket,
40)43)
41from twisted.internet import defer, interfaces44from twisted.internet import defer, interfaces
42from zope.interface import implements45from zope.interface import implements
4346
47from ubuntu_sso.utils.webclient import gsettings
44from ubuntuone.proxy.common import BaseTunnelProtocol, CRLF48from ubuntuone.proxy.common import BaseTunnelProtocol, CRLF
45from ubuntuone.proxy.logger import logger49from ubuntuone.proxy.logger import logger
4650
@@ -236,9 +240,11 @@
236 self.server = QTcpServer(QCoreApplication.instance())240 self.server = QTcpServer(QCoreApplication.instance())
237 self.server.newConnection.connect(self.new_connection)241 self.server.newConnection.connect(self.new_connection)
238 self.server.listen(QHostAddress.LocalHost, 0)242 self.server.listen(QHostAddress.LocalHost, 0)
243 logger.info("Starting tunnel server at port %d", self.port)
239244
240 def new_connection(self):245 def new_connection(self):
241 """On a new connection create a new tunnel instance."""246 """On a new connection create a new tunnel instance."""
247 logger.info("New connection made")
242 local_socket = self.server.nextPendingConnection()248 local_socket = self.server.nextPendingConnection()
243 tunnel = Tunnel(local_socket)249 tunnel = Tunnel(local_socket)
244 self.tunnels.append(tunnel)250 self.tunnels.append(tunnel)
@@ -251,3 +257,28 @@
251 def port(self):257 def port(self):
252 """The port where this server listens."""258 """The port where this server listens."""
253 return self.server.serverPort()259 return self.server.serverPort()
260
261
262def is_proxy_enabled(host, port):
263 """Check if the proxy is enabled."""
264 port = int(port)
265 if sys.platform.startswith("linux"):
266 settings = gsettings.get_proxy_settings()
267 return len(settings) > 0
268 else:
269 query = QNetworkProxyQuery(host, port)
270 proxies = QNetworkProxyFactory.systemProxyForQuery(query)
271 return len(proxies) and proxies[0].type() != QNetworkProxy.NoProxy
272
273
274def main(argv):
275 """The main function for the tunnel server."""
276 if not is_proxy_enabled(*argv[1:]):
277 sys.stdout.write("Proxy not enabled.")
278 sys.stdout.flush()
279 else:
280 app = QCoreApplication(argv)
281 tunnel_server = TunnelServer()
282 sys.stdout.write("Tunnel port: {}\n".format(tunnel_server.port))
283 sys.stdout.flush()
284 app.exec_()

Subscribers

People subscribed via source and target branches