Add urlutils.proxy_for_url
This commit is contained in:
parent
cd8d179813
commit
b220b5438f
@ -28,17 +28,14 @@ import itertools
|
|||||||
import collections
|
import collections
|
||||||
import warnings
|
import warnings
|
||||||
import datetime
|
import datetime
|
||||||
import functools
|
|
||||||
|
|
||||||
from PyQt5.QtCore import QUrl, Qt
|
from PyQt5.QtCore import QUrl, Qt
|
||||||
from PyQt5.QtGui import QColor, QFont
|
from PyQt5.QtGui import QColor, QFont
|
||||||
from PyQt5.QtNetwork import QNetworkProxy
|
|
||||||
from PyQt5.QtWidgets import QTabWidget, QTabBar
|
from PyQt5.QtWidgets import QTabWidget, QTabBar
|
||||||
|
|
||||||
from qutebrowser.commands import cmdutils
|
from qutebrowser.commands import cmdutils
|
||||||
from qutebrowser.config import configexc
|
from qutebrowser.config import configexc
|
||||||
from qutebrowser.utils import standarddir, utils
|
from qutebrowser.utils import standarddir, utils
|
||||||
from qutebrowser.browser.webkit.network import pac
|
|
||||||
|
|
||||||
|
|
||||||
SYSTEM_PROXY = object() # Return value for Proxy type
|
SYSTEM_PROXY = object() # Return value for Proxy type
|
||||||
@ -1016,38 +1013,10 @@ class ShellCommand(BaseType):
|
|||||||
return shlex.split(value)
|
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):
|
class Proxy(BaseType):
|
||||||
|
|
||||||
"""A proxy URL or special value."""
|
"""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):
|
def __init__(self, none_ok=False):
|
||||||
super().__init__(none_ok)
|
super().__init__(none_ok)
|
||||||
self.valid_values = ValidValues(
|
self.valid_values = ValidValues(
|
||||||
@ -1055,19 +1024,18 @@ class Proxy(BaseType):
|
|||||||
('none', "Don't use any proxy"))
|
('none', "Don't use any proxy"))
|
||||||
|
|
||||||
def validate(self, value):
|
def validate(self, value):
|
||||||
|
from qutebrowser.utils import urlutils
|
||||||
self._basic_validation(value)
|
self._basic_validation(value)
|
||||||
if not value:
|
if not value:
|
||||||
return
|
return
|
||||||
elif value in self.valid_values:
|
elif value in self.valid_values:
|
||||||
return
|
return
|
||||||
url = QUrl(value)
|
url = QUrl(value)
|
||||||
if not url.isValid():
|
|
||||||
raise configexc.ValidationError(
|
try:
|
||||||
value, "invalid url, {}".format(url.errorString()))
|
self.transform(value)
|
||||||
elif url.scheme() not in self.PROXY_TYPES:
|
except (urlutils.InvalidUrlError, urlutils.InvalidProxyTypeError) as e:
|
||||||
raise configexc.ValidationError(value, "must be a proxy URL "
|
raise configexc.ValidationError(value, e)
|
||||||
"(http://... or socks://...) or "
|
|
||||||
"system/none!")
|
|
||||||
|
|
||||||
def complete(self):
|
def complete(self):
|
||||||
out = []
|
out = []
|
||||||
@ -1081,14 +1049,17 @@ class Proxy(BaseType):
|
|||||||
return out
|
return out
|
||||||
|
|
||||||
def transform(self, value):
|
def transform(self, value):
|
||||||
|
from qutebrowser.utils import urlutils
|
||||||
if not value:
|
if not value:
|
||||||
return None
|
return None
|
||||||
elif value == 'system':
|
elif value == 'system':
|
||||||
return SYSTEM_PROXY
|
return SYSTEM_PROXY
|
||||||
elif value == 'none':
|
|
||||||
return QNetworkProxy(QNetworkProxy.NoProxy)
|
if value == 'none':
|
||||||
url = QUrl(value)
|
url = QUrl('direct://')
|
||||||
return self.PROXY_TYPES[url.scheme()](url)
|
else:
|
||||||
|
url = QUrl(value)
|
||||||
|
return urlutils.proxy_from_url(url)
|
||||||
|
|
||||||
|
|
||||||
class SearchEngineName(BaseType):
|
class SearchEngineName(BaseType):
|
||||||
|
@ -27,11 +27,12 @@ import posixpath
|
|||||||
import urllib.parse
|
import urllib.parse
|
||||||
|
|
||||||
from PyQt5.QtCore import QUrl
|
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.config import config, configexc
|
||||||
from qutebrowser.utils import log, qtutils, message, utils
|
from qutebrowser.utils import log, qtutils, message, utils
|
||||||
from qutebrowser.commands import cmdexc
|
from qutebrowser.commands import cmdexc
|
||||||
|
from qutebrowser.browser.webkit.network import pac
|
||||||
|
|
||||||
|
|
||||||
# FIXME: we probably could raise some exceptions on invalid URLs
|
# 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))
|
url = QUrl('data:{};base64,{}'.format(mimetype, b64))
|
||||||
qtutils.ensure_valid(url)
|
qtutils.ensure_valid(url)
|
||||||
return 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
|
||||||
|
@ -59,16 +59,6 @@ class Font(QFont):
|
|||||||
return f
|
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:
|
class RegexEq:
|
||||||
|
|
||||||
"""A class to compare regex objects."""
|
"""A class to compare regex objects."""
|
||||||
@ -1490,10 +1480,7 @@ class TestProxy:
|
|||||||
'system',
|
'system',
|
||||||
'none',
|
'none',
|
||||||
'http://user:pass@example.com:2323/',
|
'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+http://example.com/proxy.pac',
|
||||||
'pac+https://example.com/proxy.pac',
|
|
||||||
])
|
])
|
||||||
def test_validate_valid(self, klass, val):
|
def test_validate_valid(self, klass, val):
|
||||||
klass(none_ok=True).validate(val)
|
klass(none_ok=True).validate(val)
|
||||||
@ -1519,27 +1506,18 @@ class TestProxy:
|
|||||||
@pytest.mark.parametrize('val, expected', [
|
@pytest.mark.parametrize('val, expected', [
|
||||||
('', None),
|
('', None),
|
||||||
('system', configtypes.SYSTEM_PROXY),
|
('system', configtypes.SYSTEM_PROXY),
|
||||||
('none', NetworkProxy(QNetworkProxy.NoProxy)),
|
('none', QNetworkProxy(QNetworkProxy.NoProxy)),
|
||||||
('socks://example.com/',
|
('socks://example.com/',
|
||||||
NetworkProxy(QNetworkProxy.Socks5Proxy, 'example.com')),
|
QNetworkProxy(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')),
|
|
||||||
('socks5://foo:bar@example.com:2323',
|
('socks5://foo:bar@example.com:2323',
|
||||||
NetworkProxy(QNetworkProxy.Socks5Proxy, 'example.com', 2323, 'foo',
|
QNetworkProxy(QNetworkProxy.Socks5Proxy, 'example.com', 2323,
|
||||||
'bar')),
|
'foo', 'bar')),
|
||||||
])
|
])
|
||||||
def test_transform(self, klass, val, expected):
|
def test_transform(self, klass, val, expected):
|
||||||
"""Test transform with an empty value."""
|
"""Test transform with an empty value."""
|
||||||
actual = klass().transform(val)
|
actual = klass().transform(val)
|
||||||
if isinstance(actual, QNetworkProxy):
|
if isinstance(actual, QNetworkProxy):
|
||||||
actual = NetworkProxy(actual)
|
actual = QNetworkProxy(actual)
|
||||||
assert actual == expected
|
assert actual == expected
|
||||||
|
|
||||||
|
|
||||||
|
@ -24,9 +24,11 @@ import collections
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from PyQt5.QtCore import QUrl
|
from PyQt5.QtCore import QUrl
|
||||||
|
from PyQt5.QtNetwork import QNetworkProxy
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from qutebrowser.commands import cmdexc
|
from qutebrowser.commands import cmdexc
|
||||||
|
from qutebrowser.browser.webkit.network import pac
|
||||||
from qutebrowser.utils import utils, urlutils, qtutils, usertypes
|
from qutebrowser.utils import utils, urlutils, qtutils, usertypes
|
||||||
|
|
||||||
|
|
||||||
@ -736,3 +738,43 @@ def test_file_url():
|
|||||||
def test_data_url():
|
def test_data_url():
|
||||||
url = urlutils.data_url('text/plain', b'foo')
|
url = urlutils.data_url('text/plain', b'foo')
|
||||||
assert url == QUrl('data:text/plain;base64,Zm9v')
|
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))
|
||||||
|
Loading…
Reference in New Issue
Block a user