Add request filter API for host blocking
Closes https://github.com/qutebrowser/qutebrowser-extensions/issues/8
This commit is contained in:
parent
3d6f604739
commit
7ad7623d73
37
qutebrowser/api/requests.py
Normal file
37
qutebrowser/api/requests.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||||
|
|
||||||
|
# Copyright 2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||||
|
#
|
||||||
|
# This file is part of qutebrowser.
|
||||||
|
#
|
||||||
|
# qutebrowser is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# qutebrowser is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
"""APIs related to intercepting/blocking requests."""
|
||||||
|
|
||||||
|
import typing
|
||||||
|
|
||||||
|
import attr
|
||||||
|
from PyQt5.QtCore import QUrl
|
||||||
|
|
||||||
|
from qutebrowser.extensions import requests
|
||||||
|
# pylint: disable=unused-import
|
||||||
|
from qutebrowser.extensions.requests import Request
|
||||||
|
|
||||||
|
|
||||||
|
def register_filter(reqfilter: requests.RequestFilterType) -> None:
|
||||||
|
"""Register a request filter.
|
||||||
|
|
||||||
|
Whenever a request happens, the filter gets called with a Request object.
|
||||||
|
"""
|
||||||
|
requests.register_filter(reqfilter)
|
@ -119,9 +119,15 @@ class HostBlocker:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
host = url.host()
|
host = url.host()
|
||||||
return ((host in self._blocked_hosts or
|
blocked = ((host in self._blocked_hosts or
|
||||||
host in self._config_blocked_hosts) and
|
host in self._config_blocked_hosts) and
|
||||||
not _is_whitelisted_url(url))
|
not _is_whitelisted_url(url))
|
||||||
|
|
||||||
|
if blocked:
|
||||||
|
logger.info("Request to {} blocked by host blocker."
|
||||||
|
.format(url.host()))
|
||||||
|
|
||||||
|
return blocked
|
||||||
|
|
||||||
def _read_hosts_file(self, filename, target):
|
def _read_hosts_file(self, filename, target):
|
||||||
"""Read hosts from the given filename.
|
"""Read hosts from the given filename.
|
||||||
|
@ -26,15 +26,15 @@ from PyQt5.QtWebEngineCore import (QWebEngineUrlRequestInterceptor,
|
|||||||
from qutebrowser.config import config
|
from qutebrowser.config import config
|
||||||
from qutebrowser.browser import shared
|
from qutebrowser.browser import shared
|
||||||
from qutebrowser.utils import utils, log, debug
|
from qutebrowser.utils import utils, log, debug
|
||||||
|
from qutebrowser.extensions import requests
|
||||||
|
|
||||||
|
|
||||||
class RequestInterceptor(QWebEngineUrlRequestInterceptor):
|
class RequestInterceptor(QWebEngineUrlRequestInterceptor):
|
||||||
|
|
||||||
"""Handle ad blocking and custom headers."""
|
"""Handle ad blocking and custom headers."""
|
||||||
|
|
||||||
def __init__(self, host_blocker, args, parent=None):
|
def __init__(self, args, parent=None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self._host_blocker = host_blocker
|
|
||||||
self._args = args
|
self._args = args
|
||||||
|
|
||||||
def install(self, profile):
|
def install(self, profile):
|
||||||
@ -84,9 +84,10 @@ class RequestInterceptor(QWebEngineUrlRequestInterceptor):
|
|||||||
return
|
return
|
||||||
|
|
||||||
# FIXME:qtwebengine only block ads for NavigationTypeOther?
|
# FIXME:qtwebengine only block ads for NavigationTypeOther?
|
||||||
if self._host_blocker.is_blocked(url, first_party):
|
request = requests.Request(first_party_url=first_party,
|
||||||
log.webview.info("Request to {} blocked by host blocker.".format(
|
request_url=url)
|
||||||
url.host()))
|
requests.run_filters(request)
|
||||||
|
if request.is_blocked:
|
||||||
info.block(True)
|
info.block(True)
|
||||||
|
|
||||||
for header, value in shared.custom_headers(url=url):
|
for header, value in shared.custom_headers(url=url):
|
||||||
|
@ -60,10 +60,8 @@ def init():
|
|||||||
_qute_scheme_handler.install(webenginesettings.private_profile)
|
_qute_scheme_handler.install(webenginesettings.private_profile)
|
||||||
|
|
||||||
log.init.debug("Initializing request interceptor...")
|
log.init.debug("Initializing request interceptor...")
|
||||||
host_blocker = objreg.get('host-blocker')
|
|
||||||
args = objreg.get('args')
|
args = objreg.get('args')
|
||||||
req_interceptor = interceptor.RequestInterceptor(
|
req_interceptor = interceptor.RequestInterceptor(args=args, parent=app)
|
||||||
host_blocker, args=args, parent=app)
|
|
||||||
req_interceptor.install(webenginesettings.default_profile)
|
req_interceptor.install(webenginesettings.default_profile)
|
||||||
req_interceptor.install(webenginesettings.private_profile)
|
req_interceptor.install(webenginesettings.private_profile)
|
||||||
|
|
||||||
|
@ -39,6 +39,7 @@ from PyQt5.QtCore import QUrl
|
|||||||
from qutebrowser.browser import downloads
|
from qutebrowser.browser import downloads
|
||||||
from qutebrowser.browser.webkit import webkitelem
|
from qutebrowser.browser.webkit import webkitelem
|
||||||
from qutebrowser.utils import log, objreg, message, usertypes, utils, urlutils
|
from qutebrowser.utils import log, objreg, message, usertypes, utils, urlutils
|
||||||
|
from qutebrowser.extensions import requests
|
||||||
|
|
||||||
|
|
||||||
@attr.s
|
@attr.s
|
||||||
@ -354,8 +355,9 @@ class _Downloader:
|
|||||||
# qute, see the comments/discussion on
|
# qute, see the comments/discussion on
|
||||||
# https://github.com/qutebrowser/qutebrowser/pull/962#discussion_r40256987
|
# https://github.com/qutebrowser/qutebrowser/pull/962#discussion_r40256987
|
||||||
# and https://github.com/qutebrowser/qutebrowser/issues/1053
|
# and https://github.com/qutebrowser/qutebrowser/issues/1053
|
||||||
host_blocker = objreg.get('host-blocker')
|
request = requests.Request(first_party_url=None, request_url=url)
|
||||||
if host_blocker.is_blocked(url):
|
requests.run_filters(request)
|
||||||
|
if request.is_blocked:
|
||||||
log.downloads.debug("Skipping {}, host-blocked".format(url))
|
log.downloads.debug("Skipping {}, host-blocked".format(url))
|
||||||
# We still need an empty file in the output, QWebView can be pretty
|
# We still need an empty file in the output, QWebView can be pretty
|
||||||
# picky about displaying a file correctly when not all assets are
|
# picky about displaying a file correctly when not all assets are
|
||||||
|
@ -38,6 +38,7 @@ if MYPY:
|
|||||||
from qutebrowser.utils import (message, log, usertypes, utils, objreg,
|
from qutebrowser.utils import (message, log, usertypes, utils, objreg,
|
||||||
urlutils, debug)
|
urlutils, debug)
|
||||||
from qutebrowser.browser import shared
|
from qutebrowser.browser import shared
|
||||||
|
from qutebrowser.extensions import requests
|
||||||
from qutebrowser.browser.webkit import certificateerror
|
from qutebrowser.browser.webkit import certificateerror
|
||||||
from qutebrowser.browser.webkit.network import (webkitqutescheme, networkreply,
|
from qutebrowser.browser.webkit.network import (webkitqutescheme, networkreply,
|
||||||
filescheme)
|
filescheme)
|
||||||
@ -405,10 +406,10 @@ class NetworkManager(QNetworkAccessManager):
|
|||||||
# the webpage shutdown here.
|
# the webpage shutdown here.
|
||||||
current_url = QUrl()
|
current_url = QUrl()
|
||||||
|
|
||||||
host_blocker = objreg.get('host-blocker')
|
request = requests.Request(first_party_url=current_url,
|
||||||
if host_blocker.is_blocked(req.url(), current_url):
|
request_url=req.url())
|
||||||
log.webview.info("Request to {} blocked by host blocker.".format(
|
requests.run_filters(request)
|
||||||
req.url().host()))
|
if request.is_blocked:
|
||||||
return networkreply.ErrorNetworkReply(
|
return networkreply.ErrorNetworkReply(
|
||||||
req, HOSTBLOCK_ERROR_STRING, QNetworkReply.ContentAccessDenied,
|
req, HOSTBLOCK_ERROR_STRING, QNetworkReply.ContentAccessDenied,
|
||||||
self)
|
self)
|
||||||
|
53
qutebrowser/extensions/requests.py
Normal file
53
qutebrowser/extensions/requests.py
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||||
|
|
||||||
|
# Copyright 2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||||
|
#
|
||||||
|
# This file is part of qutebrowser.
|
||||||
|
#
|
||||||
|
# qutebrowser is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# qutebrowser is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
"""Infrastructure for filtering requests."""
|
||||||
|
|
||||||
|
import typing
|
||||||
|
|
||||||
|
import attr
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s
|
||||||
|
class Request:
|
||||||
|
|
||||||
|
"""A request which can be blocked."""
|
||||||
|
|
||||||
|
first_party_url = attr.ib() # type: QUrl
|
||||||
|
request_url = attr.ib() # type: QUrl
|
||||||
|
is_blocked = attr.ib(False) # type: bool
|
||||||
|
|
||||||
|
def block(self):
|
||||||
|
"""Block this request."""
|
||||||
|
self.is_blocked = True
|
||||||
|
|
||||||
|
|
||||||
|
RequestFilterType = typing.Callable[[Request], None]
|
||||||
|
|
||||||
|
|
||||||
|
_request_filters = [] # type: typing.List[RequestFilterType]
|
||||||
|
|
||||||
|
|
||||||
|
def register_filter(reqfilter: RequestFilterType) -> None:
|
||||||
|
_request_filters.append(reqfilter)
|
||||||
|
|
||||||
|
|
||||||
|
def run_filters(info):
|
||||||
|
for reqfilter in _request_filters:
|
||||||
|
reqfilter(info)
|
Loading…
Reference in New Issue
Block a user