Add request filter API for host blocking

Closes https://github.com/qutebrowser/qutebrowser-extensions/issues/8
This commit is contained in:
Florian Bruhin 2018-12-10 14:39:07 +01:00
parent 3d6f604739
commit 7ad7623d73
7 changed files with 115 additions and 17 deletions

View 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)

View File

@ -119,9 +119,15 @@ class HostBlocker:
return False
host = url.host()
return ((host in self._blocked_hosts or
host in self._config_blocked_hosts) and
not _is_whitelisted_url(url))
blocked = ((host in self._blocked_hosts or
host in self._config_blocked_hosts) and
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):
"""Read hosts from the given filename.

View File

@ -26,15 +26,15 @@ from PyQt5.QtWebEngineCore import (QWebEngineUrlRequestInterceptor,
from qutebrowser.config import config
from qutebrowser.browser import shared
from qutebrowser.utils import utils, log, debug
from qutebrowser.extensions import requests
class RequestInterceptor(QWebEngineUrlRequestInterceptor):
"""Handle ad blocking and custom headers."""
def __init__(self, host_blocker, args, parent=None):
def __init__(self, args, parent=None):
super().__init__(parent)
self._host_blocker = host_blocker
self._args = args
def install(self, profile):
@ -84,9 +84,10 @@ class RequestInterceptor(QWebEngineUrlRequestInterceptor):
return
# FIXME:qtwebengine only block ads for NavigationTypeOther?
if self._host_blocker.is_blocked(url, first_party):
log.webview.info("Request to {} blocked by host blocker.".format(
url.host()))
request = requests.Request(first_party_url=first_party,
request_url=url)
requests.run_filters(request)
if request.is_blocked:
info.block(True)
for header, value in shared.custom_headers(url=url):

View File

@ -60,10 +60,8 @@ def init():
_qute_scheme_handler.install(webenginesettings.private_profile)
log.init.debug("Initializing request interceptor...")
host_blocker = objreg.get('host-blocker')
args = objreg.get('args')
req_interceptor = interceptor.RequestInterceptor(
host_blocker, args=args, parent=app)
req_interceptor = interceptor.RequestInterceptor(args=args, parent=app)
req_interceptor.install(webenginesettings.default_profile)
req_interceptor.install(webenginesettings.private_profile)

View File

@ -39,6 +39,7 @@ from PyQt5.QtCore import QUrl
from qutebrowser.browser import downloads
from qutebrowser.browser.webkit import webkitelem
from qutebrowser.utils import log, objreg, message, usertypes, utils, urlutils
from qutebrowser.extensions import requests
@attr.s
@ -354,8 +355,9 @@ class _Downloader:
# qute, see the comments/discussion on
# https://github.com/qutebrowser/qutebrowser/pull/962#discussion_r40256987
# and https://github.com/qutebrowser/qutebrowser/issues/1053
host_blocker = objreg.get('host-blocker')
if host_blocker.is_blocked(url):
request = requests.Request(first_party_url=None, request_url=url)
requests.run_filters(request)
if request.is_blocked:
log.downloads.debug("Skipping {}, host-blocked".format(url))
# We still need an empty file in the output, QWebView can be pretty
# picky about displaying a file correctly when not all assets are

View File

@ -38,6 +38,7 @@ if MYPY:
from qutebrowser.utils import (message, log, usertypes, utils, objreg,
urlutils, debug)
from qutebrowser.browser import shared
from qutebrowser.extensions import requests
from qutebrowser.browser.webkit import certificateerror
from qutebrowser.browser.webkit.network import (webkitqutescheme, networkreply,
filescheme)
@ -405,10 +406,10 @@ class NetworkManager(QNetworkAccessManager):
# the webpage shutdown here.
current_url = QUrl()
host_blocker = objreg.get('host-blocker')
if host_blocker.is_blocked(req.url(), current_url):
log.webview.info("Request to {} blocked by host blocker.".format(
req.url().host()))
request = requests.Request(first_party_url=current_url,
request_url=req.url())
requests.run_filters(request)
if request.is_blocked:
return networkreply.ErrorNetworkReply(
req, HOSTBLOCK_ERROR_STRING, QNetworkReply.ContentAccessDenied,
self)

View 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)