QtWebEngine: Implement custom HTTP headers

This commit is contained in:
Florian Bruhin 2016-09-07 18:29:19 +02:00
parent 5a54699863
commit 44d1056e54
8 changed files with 102 additions and 31 deletions

View File

@ -711,16 +711,12 @@ Valid values:
Default: +pass:[true]+
This setting is only available with the QtWebKit backend.
[[network-accept-language]]
=== accept-language
Value to send in the `accept-language` header.
Default: +pass:[en-US,en]+
This setting is only available with the QtWebKit backend.
[[network-referer-header]]
=== referer-header
Send the Referer header
@ -804,8 +800,6 @@ Set custom headers for qutebrowser HTTP requests.
Default: empty
This setting is only available with the QtWebKit backend.
== completion
Options related to completion and command history.

View File

@ -18,3 +18,24 @@
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
"""Various utilities shared between webpage/webview subclasses."""
from qutebrowser.config import config
def custom_headers():
"""Get the combined custom headers."""
headers = {}
dnt = b'1' if config.get('network', 'do-not-track') else b'0'
headers[b'DNT'] = dnt
headers[b'X-Do-Not-Track'] = dnt
custom_headers = config.get('network', 'custom-headers')
if custom_headers is not None:
for header, value in custom_headers.items():
headers[header.encode('ascii')] = value.encode('ascii')
accept_language = config.get('network', 'accept-language')
if accept_language is not None:
headers[b'Accept-Language'] = accept_language.encode('ascii')
return sorted(headers.items())

View File

@ -22,6 +22,7 @@
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWebEngineCore import QWebEngineUrlRequestInterceptor
from qutebrowser.browser import shared
from qutebrowser.utils import utils, log
@ -59,3 +60,6 @@ class RequestInterceptor(QWebEngineUrlRequestInterceptor):
log.webview.info("Request to {} blocked by host blocker.".format(
info.requestUrl().host()))
info.block(True)
for header, value in shared.custom_headers():
info.setHttpHeader(header, value)

View File

@ -31,6 +31,7 @@ from PyQt5.QtNetwork import (QNetworkAccessManager, QNetworkReply, QSslError,
from qutebrowser.config import config
from qutebrowser.utils import (message, log, usertypes, utils, objreg, qtutils,
urlutils, debug)
from qutebrowser.browser import shared
from qutebrowser.browser.webkit.network import qutescheme, networkreply
from qutebrowser.browser.webkit.network import filescheme
@ -449,6 +450,9 @@ class NetworkManager(QNetworkAccessManager):
if result is not None:
return result
for header, value in shared.custom_headers():
req.setRawHeader(header, value)
host_blocker = objreg.get('host-blocker')
if (op == QNetworkAccessManager.GetOperation and
host_blocker.is_blocked(req.url())):
@ -458,20 +462,6 @@ class NetworkManager(QNetworkAccessManager):
req, HOSTBLOCK_ERROR_STRING, QNetworkReply.ContentAccessDenied,
self)
if config.get('network', 'do-not-track'):
dnt = '1'.encode('ascii')
else:
dnt = '0'.encode('ascii')
req.setRawHeader('DNT'.encode('ascii'), dnt)
req.setRawHeader('X-Do-Not-Track'.encode('ascii'), dnt)
# Load custom headers
custom_headers = config.get('network', 'custom-headers')
if custom_headers is not None:
for header, value in custom_headers.items():
req.setRawHeader(header.encode('ascii'), value.encode('ascii'))
# There are some scenarios where we can't figure out current_url:
# - There's a generic NetworkManager, e.g. for downloads
# - The download was in a tab which is now closed.
@ -490,10 +480,6 @@ class NetworkManager(QNetworkAccessManager):
self.set_referer(req, current_url)
accept_language = config.get('network', 'accept-language')
if accept_language is not None:
req.setRawHeader('Accept-Language'.encode('ascii'),
accept_language.encode('ascii'))
if PYQT_VERSION < 0x050301:
# WORKAROUND (remove this when we bump the requirements to 5.3.1)
#

View File

@ -388,13 +388,11 @@ def data(readonly=False):
('network', sect.KeyValue(
('do-not-track',
SettingValue(typ.Bool(), 'true',
backends=[usertypes.Backend.QtWebKit]),
SettingValue(typ.Bool(), 'true'),
"Value to send in the `DNT` header."),
('accept-language',
SettingValue(typ.String(none_ok=True), 'en-US,en',
backends=[usertypes.Backend.QtWebKit]),
SettingValue(typ.String(none_ok=True), 'en-US,en'),
"Value to send in the `accept-language` header."),
('referer-header',
@ -437,8 +435,7 @@ def data(readonly=False):
"Whether to try to pre-fetch DNS entries to speed up browsing."),
('custom-headers',
SettingValue(typ.HeaderDict(none_ok=True), '',
backends=[usertypes.Backend.QtWebKit]),
SettingValue(typ.HeaderDict(none_ok=True), ''),
"Set custom headers for qutebrowser HTTP requests."),
readonly=readonly

View File

@ -74,6 +74,8 @@ PERFECT_FILES = [
('tests/unit/browser/test_signalfilter.py',
'qutebrowser/browser/signalfilter.py'),
('tests/unit/browser/test_shared.py',
'qutebrowser/browser/shared.py'),
# ('tests/unit/browser/test_tab.py',
# 'qutebrowser/browser/tab.py'),

View File

@ -394,12 +394,28 @@ Feature: Various utility commands.
## Custom headers
@qtwebengine_todo: Custom headers are not implemented yet
Scenario: Setting a custom header
When I set network -> custom-headers to {"X-Qute-Test": "testvalue"}
And I open headers
Then the header X-Qute-Test should be set to testvalue
Scenario: DNT header
When I set network -> do-not-track to true
And I open headers
Then the header Dnt should be set to 1
And the header X-Do-Not-Track should be set to 1
Scenario: DNT header (off)
When I set network -> do-not-track to false
And I open headers
Then the header Dnt should be set to 0
And the header X-Do-Not-Track should be set to 0
Scenario: Accept-Language header
When I set network -> accept-language to en,de
And I open headers
Then the header Accept-Language should be set to en,de
## :messages
@qtwebengine_todo: qute:log is not implemented yet

View File

@ -0,0 +1,51 @@
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
# Copyright 2016 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/>.
import pytest
from qutebrowser.browser import shared
@pytest.mark.parametrize('dnt, accept_language, custom_headers, expected', [
# DNT
(True, None, {}, {b'DNT': b'1', b'X-Do-Not-Track': b'1'}),
(False, None, {}, {b'DNT': b'0', b'X-Do-Not-Track': b'0'}),
# Accept-Language
(False, 'de, en', {}, {b'DNT': b'0', b'X-Do-Not-Track': b'0',
b'Accept-Language': b'de, en'}),
# Custom headers
(False, None, {'X-Qute': 'yes'}, {b'DNT': b'0', b'X-Do-Not-Track': b'0',
b'X-Qute': b'yes'}),
# Mixed
(False, 'de, en', {'X-Qute': 'yes'}, {b'DNT': b'0',
b'X-Do-Not-Track': b'0',
b'Accept-Language': b'de, en',
b'X-Qute': b'yes'}),
])
def test_custom_headers(config_stub, dnt, accept_language, custom_headers,
expected):
config_stub.data = {
'network': {
'do-not-track': dnt,
'accept-language': accept_language,
'custom-headers': custom_headers,
}
}
expected_items = sorted(expected.items())
assert shared.custom_headers() == expected_items