Reputation: 18106
I am trying to rewrite several HTTP requests with a custom URL scheme:
All requests to http://static.foo.bar
should be rewitten to static://...
and serving some reply.
Problem:
The interception and redirecting seems working, but whatever my implementation of QWebEngineUrlSchemeHandler
is returning (image or html) always replaces the full HTML page.
Expected result:
A sample image /tmp/iphone.jpg
served from SchemeHandler
is embedded in the HTML page, so the HTML shows the <h1>
title and 2 images.
Versions:
Python 3.7.4
PyQt: 5.14.1
Sample code:
import sys
import signal
from PyQt5 import QtCore
from PyQt5.QtCore import QUrl, QObject
from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEngineSettings, QWebEnginePage, QWebEngineProfile
from PyQt5.QtWebEngineCore import QWebEngineUrlRequestInterceptor, QWebEngineUrlSchemeHandler, QWebEngineUrlScheme
from PyQt5.QtWidgets import QApplication, QMainWindow
class WebEngineUrlRequestInterceptor(QWebEngineUrlRequestInterceptor):
# Everything requested from static.foo.bar goes to static://
# //static.foo.bar/1/2/4.jpeg >> static://1/2/4.jpeg
def interceptRequest(self, info):
print("interceptRequest")
print(info.requestUrl())
if 'static.foo.bar' in str(info.requestUrl()):
url = QUrl()
url.setScheme(MyWebEngineUrlScheme.scheme.decode())
url.setHost('baz.jpg')
print('Intercepting and redirecting to: %s' % url)
info.redirect(url)
class MyWebEnginePage(QWebEnginePage):
# debugging
def acceptNavigationRequest(self, url, _type, isMainFrame):
print("acceptNavigationRequest: %s" % url)
return QWebEnginePage.acceptNavigationRequest(self, url, _type, isMainFrame)
class SchemeHandler(QWebEngineUrlSchemeHandler):
def __init__(self, app):
super().__init__(app)
def requestStarted(self, request):
url = request.requestUrl()
print('SchemeHandler requestStarted: %s' % url)
# Returns a sample image
raw_html = open('/tmp/iphone.jpg', 'rb').read()
buf = QtCore.QBuffer(parent=self)
request.destroyed.connect(buf.deleteLater)
buf.open(QtCore.QIODevice.WriteOnly)
buf.write(raw_html)
buf.seek(0)
buf.close()
request.reply(b"image/jpeg", buf)
return
class MyWebEngineUrlScheme(QObject):
# Register scheme
scheme = b"static"
def __init__(self, parent=None):
super().__init__(parent)
scheme = QWebEngineUrlScheme(MyWebEngineUrlScheme.scheme)
QWebEngineUrlScheme.registerScheme(scheme)
self.m_functions = dict()
def init_handler(self, profile=None):
if profile is None:
profile = QWebEngineProfile.defaultProfile()
handler = profile.urlSchemeHandler(MyWebEngineUrlScheme.scheme)
if handler is not None:
profile.removeUrlSchemeHandler(handler)
self.handler = SchemeHandler(self)
print("registering %s to %s" % (MyWebEngineUrlScheme.scheme, self.handler))
profile.installUrlSchemeHandler(MyWebEngineUrlScheme.scheme, self.handler)
schemeApp = MyWebEngineUrlScheme()
signal.signal(signal.SIGINT, signal.SIG_DFL)
app = QApplication(sys.argv)
win = QMainWindow()
win.resize(800, 600)
html = """
<html>
<body>
<h1>test</h1>
<hr>
<p>First image</p>
<img src="http://static.foo.bar/baz.jpg" />
<hr>
<p>Second image</p>
<img src="https://store.storeimages.cdn-apple.com/4668/as-images.apple.com/is/iphone-xr-red-select-201809?wid=1200&hei=630&fmt=jpeg&qlt=95&op_usm=0.5,0.5&.v=1551226038669" />
</body>
</html>
"""
browser = QWebEngineView()
interceptor = WebEngineUrlRequestInterceptor()
profile = QWebEngineProfile()
profile.setUrlRequestInterceptor(interceptor)
page = MyWebEnginePage(profile, browser)
schemeApp.init_handler(profile)
browser.settings().setAttribute(QWebEngineSettings.PluginsEnabled, True)
browser.settings().setAttribute(QWebEngineSettings.JavascriptCanOpenWindows, False)
browser.settings().setAttribute(QWebEngineSettings.LinksIncludedInFocusChain, False)
browser.settings().setAttribute(QWebEngineSettings.LocalStorageEnabled, True)
browser.settings().setAttribute(QWebEngineSettings.JavascriptEnabled, True)
page.setHtml(html)
browser.setPage(page)
browser.show()
win.setCentralWidget(browser)
win.show()
sys.exit(app.exec_())
Upvotes: 3
Views: 2248
Reputation: 243897
If you analyze the urls requested by the page you get:
PyQt5.QtCore.QUrl('data:text/html;charset=UTF-8,%0A%3Chtml%3E%0A%3Cbody%3E%0A%3Ch1%3Etest%3C%2Fh1%3E%0A%3Chr%3E%0A%3Cp%3EFirst image%3C%2Fp%3E%0A%3Cimg src%3D%22http%3A%2F%2Fstatic.foo.bar%2Fbaz.jpg%22 %2F%3E%0A%3Chr%3E%0A%3Cp%3ESecond image%3C%2Fp%3E%0A%3Cimg src%3D%22https%3A%2F%2Fstore.storeimages.cdn-apple.com%2F4668%2Fas-images.apple.com%2Fis%2Fiphone-xr-red-select-201809%3Fwid%3D1200%26hei%3D630%26fmt%3Djpeg%26qlt%3D95%26op_usm%3D0.5%2C0.5%26.v%3D1551226038669%22 %2F%3E%0A%3C%2Fbody%3E%0A%3C%2Fhtml%3E%0A')
PyQt5.QtCore.QUrl('http://static.foo.bar/baz.jpg')
PyQt5.QtCore.QUrl('https://store.storeimages.cdn-apple.com/4668/as-images.apple.com/is/iphone-xr-red-select-201809?wid=1200&hei=630&fmt=jpeg&qlt=95&op_usm=0.5,0.5&.v=1551226038669')
Where condition if 'static.foo.bar' in str(info.requestUrl()):
is met for the first and second url((I have marked it in bold for a better visualization).
The solution is to improve the filter:
class WebEngineUrlRequestInterceptor(QWebEngineUrlRequestInterceptor):
# Everything requested from static.foo.bar goes to static://
# //static.foo.bar/1/2/4.jpeg >> static://1/2/4.jpeg
def interceptRequest(self, info):
print("interceptRequest")
print(info.requestUrl())
if info.requestUrl().host().startswith("static.foo.bar"): # or if info.requestUrl().host() == "static.foo.bar":
url = QUrl()
url.setScheme(MyWebEngineUrlScheme.scheme.decode())
url.setHost(info.requestUrl().path()[1:]) # remove "/"
print("Intercepting and redirecting to: %s" % url)
info.redirect(url)
Upvotes: 3