summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Bruhin <git@the-compiler.org>2016-12-22 13:51:27 +0100
committerFlorian Bruhin <git@the-compiler.org>2016-12-22 13:51:27 +0100
commitb220b5438f1f49a26ff742c8d313f062e7e68b1d (patch)
tree2d2e711b6953b230d600dee09153c58ba9d1f28c
parentcd8d179813a300d999e102d3cf7ffee96ba94bbe (diff)
downloadqutebrowser-b220b5438f1f49a26ff742c8d313f062e7e68b1d.tar.gz
qutebrowser-b220b5438f1f49a26ff742c8d313f062e7e68b1d.zip
Add urlutils.proxy_for_url
-rw-r--r--qutebrowser/config/configtypes.py55
-rw-r--r--qutebrowser/utils/urlutils.py47
-rw-r--r--tests/unit/config/test_configtypes.py32
-rw-r--r--tests/unit/utils/test_urlutils.py42
4 files changed, 106 insertions, 70 deletions
diff --git a/qutebrowser/config/configtypes.py b/qutebrowser/config/configtypes.py
index 7c5f9721c..b0399fcac 100644
--- a/qutebrowser/config/configtypes.py
+++ b/qutebrowser/config/configtypes.py
@@ -28,17 +28,14 @@ import itertools
import collections
import warnings
import datetime
-import functools
from PyQt5.QtCore import QUrl, Qt
from PyQt5.QtGui import QColor, QFont
-from PyQt5.QtNetwork import QNetworkProxy
from PyQt5.QtWidgets import QTabWidget, QTabBar
from qutebrowser.commands import cmdutils
from qutebrowser.config import configexc
from qutebrowser.utils import standarddir, utils
-from qutebrowser.browser.webkit.network import pac
SYSTEM_PROXY = object() # Return value for Proxy type
@@ -1016,38 +1013,10 @@ class ShellCommand(BaseType):
return shlex.split(value)
-def proxy_from_url(typ, url):
- """Create a QNetworkProxy from QUrl and a proxy type.
-
- Args:
- typ: QNetworkProxy::ProxyType.
- url: URL of a proxy (possibly with credentials).
-
- Return:
- New QNetworkProxy.
- """
- proxy = QNetworkProxy(typ, url.host())
- if url.port() != -1:
- proxy.setPort(url.port())
- if url.userName():
- proxy.setUser(url.userName())
- if url.password():
- proxy.setPassword(url.password())
- return proxy
-
-
class Proxy(BaseType):
"""A proxy URL or special value."""
- PROXY_TYPES = {
- 'http': functools.partial(proxy_from_url, QNetworkProxy.HttpProxy),
- 'pac+http': pac.PACFetcher,
- 'pac+https': pac.PACFetcher,
- 'socks': functools.partial(proxy_from_url, QNetworkProxy.Socks5Proxy),
- 'socks5': functools.partial(proxy_from_url, QNetworkProxy.Socks5Proxy),
- }
-
def __init__(self, none_ok=False):
super().__init__(none_ok)
self.valid_values = ValidValues(
@@ -1055,19 +1024,18 @@ class Proxy(BaseType):
('none', "Don't use any proxy"))
def validate(self, value):
+ from qutebrowser.utils import urlutils
self._basic_validation(value)
if not value:
return
elif value in self.valid_values:
return
url = QUrl(value)
- if not url.isValid():
- raise configexc.ValidationError(
- value, "invalid url, {}".format(url.errorString()))
- elif url.scheme() not in self.PROXY_TYPES:
- raise configexc.ValidationError(value, "must be a proxy URL "
- "(http://... or socks://...) or "
- "system/none!")
+
+ try:
+ self.transform(value)
+ except (urlutils.InvalidUrlError, urlutils.InvalidProxyTypeError) as e:
+ raise configexc.ValidationError(value, e)
def complete(self):
out = []
@@ -1081,14 +1049,17 @@ class Proxy(BaseType):
return out
def transform(self, value):
+ from qutebrowser.utils import urlutils
if not value:
return None
elif value == 'system':
return SYSTEM_PROXY
- elif value == 'none':
- return QNetworkProxy(QNetworkProxy.NoProxy)
- url = QUrl(value)
- return self.PROXY_TYPES[url.scheme()](url)
+
+ if value == 'none':
+ url = QUrl('direct://')
+ else:
+ url = QUrl(value)
+ return urlutils.proxy_from_url(url)
class SearchEngineName(BaseType):
diff --git a/qutebrowser/utils/urlutils.py b/qutebrowser/utils/urlutils.py
index 6a24ceb82..ae5733bc6 100644
--- a/qutebrowser/utils/urlutils.py
+++ b/qutebrowser/utils/urlutils.py
@@ -27,11 +27,12 @@ import posixpath
import urllib.parse
from PyQt5.QtCore import QUrl
-from PyQt5.QtNetwork import QHostInfo, QHostAddress
+from PyQt5.QtNetwork import QHostInfo, QHostAddress, QNetworkProxy
from qutebrowser.config import config, configexc
from qutebrowser.utils import log, qtutils, message, utils
from qutebrowser.commands import cmdexc
+from qutebrowser.browser.webkit.network import pac
# FIXME: we probably could raise some exceptions on invalid URLs
@@ -589,3 +590,47 @@ def data_url(mimetype, data):
url = QUrl('data:{};base64,{}'.format(mimetype, b64))
qtutils.ensure_valid(url)
return url
+
+
+class InvalidProxyTypeError(Exception):
+
+ """Error raised when proxy_from_url gets an unknown proxy type."""
+
+ def __init__(self, typ):
+ super().__init__("Invalid proxy type {}!".format(typ))
+
+
+def proxy_from_url(url):
+ """Create a QNetworkProxy from QUrl and a proxy type.
+
+ Args:
+ url: URL of a proxy (possibly with credentials).
+
+ Return:
+ New QNetworkProxy.
+ """
+ if not url.isValid():
+ raise InvalidUrlError(url)
+
+ scheme = url.scheme()
+ if scheme in ['pac+http', 'pac+https']:
+ return pac.PACFetcher
+
+ types = {
+ 'http': QNetworkProxy.HttpProxy,
+ 'socks': QNetworkProxy.Socks5Proxy,
+ 'socks5': QNetworkProxy.Socks5Proxy,
+ 'direct': QNetworkProxy.NoProxy,
+ }
+ if scheme not in types:
+ raise InvalidProxyTypeError(scheme)
+
+ proxy = QNetworkProxy(types[scheme], url.host())
+
+ if url.port() != -1:
+ proxy.setPort(url.port())
+ if url.userName():
+ proxy.setUser(url.userName())
+ if url.password():
+ proxy.setPassword(url.password())
+ return proxy
diff --git a/tests/unit/config/test_configtypes.py b/tests/unit/config/test_configtypes.py
index 37e763999..f2e18c459 100644
--- a/tests/unit/config/test_configtypes.py
+++ b/tests/unit/config/test_configtypes.py
@@ -59,16 +59,6 @@ class Font(QFont):
return f
-class NetworkProxy(QNetworkProxy):
-
- """A QNetworkProxy with a nicer repr()."""
-
- def __repr__(self):
- return utils.get_repr(self, type=self.type(), hostName=self.hostName(),
- port=self.port(), user=self.user(),
- password=self.password())
-
-
class RegexEq:
"""A class to compare regex objects."""
@@ -1490,10 +1480,7 @@ class TestProxy:
'system',
'none',
'http://user:pass@example.com:2323/',
- 'socks://user:pass@example.com:2323/',
- 'socks5://user:pass@example.com:2323/',
'pac+http://example.com/proxy.pac',
- 'pac+https://example.com/proxy.pac',
])
def test_validate_valid(self, klass, val):
klass(none_ok=True).validate(val)
@@ -1519,27 +1506,18 @@ class TestProxy:
@pytest.mark.parametrize('val, expected', [
('', None),
('system', configtypes.SYSTEM_PROXY),
- ('none', NetworkProxy(QNetworkProxy.NoProxy)),
+ ('none', QNetworkProxy(QNetworkProxy.NoProxy)),
('socks://example.com/',
- NetworkProxy(QNetworkProxy.Socks5Proxy, 'example.com')),
- ('socks5://example.com',
- NetworkProxy(QNetworkProxy.Socks5Proxy, 'example.com')),
- ('socks5://example.com:2342',
- NetworkProxy(QNetworkProxy.Socks5Proxy, 'example.com', 2342)),
- ('socks5://foo@example.com',
- NetworkProxy(QNetworkProxy.Socks5Proxy, 'example.com', 0, 'foo')),
- ('socks5://foo:bar@example.com',
- NetworkProxy(QNetworkProxy.Socks5Proxy, 'example.com', 0, 'foo',
- 'bar')),
+ QNetworkProxy(QNetworkProxy.Socks5Proxy, 'example.com')),
('socks5://foo:bar@example.com:2323',
- NetworkProxy(QNetworkProxy.Socks5Proxy, 'example.com', 2323, 'foo',
- 'bar')),
+ QNetworkProxy(QNetworkProxy.Socks5Proxy, 'example.com', 2323,
+ 'foo', 'bar')),
])
def test_transform(self, klass, val, expected):
"""Test transform with an empty value."""
actual = klass().transform(val)
if isinstance(actual, QNetworkProxy):
- actual = NetworkProxy(actual)
+ actual = QNetworkProxy(actual)
assert actual == expected
diff --git a/tests/unit/utils/test_urlutils.py b/tests/unit/utils/test_urlutils.py
index 984971b3c..06196f5d2 100644
--- a/tests/unit/utils/test_urlutils.py
+++ b/tests/unit/utils/test_urlutils.py
@@ -24,9 +24,11 @@ import collections
import logging
from PyQt5.QtCore import QUrl
+from PyQt5.QtNetwork import QNetworkProxy
import pytest
from qutebrowser.commands import cmdexc
+from qutebrowser.browser.webkit.network import pac
from qutebrowser.utils import utils, urlutils, qtutils, usertypes
@@ -736,3 +738,43 @@ def test_file_url():
def test_data_url():
url = urlutils.data_url('text/plain', b'foo')
assert url == QUrl('data:text/plain;base64,Zm9v')
+
+
+
+class TestProxyFromUrl:
+
+ @pytest.mark.parametrize('url, expected', [
+ ('socks://example.com/',
+ QNetworkProxy(QNetworkProxy.Socks5Proxy, 'example.com')),
+ ('socks5://example.com',
+ QNetworkProxy(QNetworkProxy.Socks5Proxy, 'example.com')),
+ ('socks5://example.com:2342',
+ QNetworkProxy(QNetworkProxy.Socks5Proxy, 'example.com', 2342)),
+ ('socks5://foo@example.com',
+ QNetworkProxy(QNetworkProxy.Socks5Proxy, 'example.com', 0, 'foo')),
+ ('socks5://foo:bar@example.com',
+ QNetworkProxy(QNetworkProxy.Socks5Proxy, 'example.com', 0, 'foo',
+ 'bar')),
+ ('socks5://foo:bar@example.com:2323',
+ QNetworkProxy(QNetworkProxy.Socks5Proxy, 'example.com', 2323,
+ 'foo', 'bar')),
+ ('direct://', QNetworkProxy(QNetworkProxy.NoProxy)),
+ ])
+ def test_proxy_from_url_valid(self, url, expected):
+ assert urlutils.proxy_from_url(QUrl(url)) == expected
+
+ @pytest.mark.parametrize('scheme', ['pac+http', 'pac+https'])
+ def test_proxy_from_url_pac(self, scheme):
+ fetcher = urlutils.proxy_from_url(QUrl('{}://foo'.format(scheme)))
+ assert fetcher is pac.PACFetcher
+
+ @pytest.mark.parametrize('url, exception', [
+ ('blah', urlutils.InvalidProxyTypeError),
+ (':', urlutils.InvalidUrlError), # invalid URL
+ # Invalid/unsupported scheme
+ ('ftp://example.com/', urlutils.InvalidProxyTypeError),
+ ('socks4://example.com/', urlutils.InvalidProxyTypeError),
+ ])
+ def test_invalid(self, url, exception):
+ with pytest.raises(exception):
+ urlutils.proxy_from_url(QUrl(url))