Add urlutils.proxy_for_url
This commit is contained in:
parent
cd8d179813
commit
b220b5438f
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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))
|
||||
|
Loading…
Reference in New Issue
Block a user