QtWebEngine перехватывает запрос с пользовательской схемой URL, обслуживающей ответ - PullRequest
1 голос
/ 08 марта 2020

Я пытаюсь переписать несколько HTTP-запросов с помощью пользовательской схемы URL:
Все запросы к http://static.foo.bar должны быть переадресованы на static://... и обработать некоторый ответ.

Проблема:
Кажется, что перехват и перенаправление работают, но независимо от того, что возвращает моя реализация QWebEngineUrlSchemeHandler (изображение или html), всегда заменяет полную страницу HTML.

Ожидаемый результат:
Образец изображения /tmp/iphone.jpg, отправленный с SchemeHandler, встроен в страницу HTML, поэтому HTML показывает заголовок <h1> и 2 изображения.

Версии:
Python 3.7.4
PyQt: 5.14.1

Пример кода:

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_())

1 Ответ

1 голос
/ 08 марта 2020

Если вы проанализируете URL-адреса, запрошенные на странице, вы получите:

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%2F<b>static.foo.bar</b>%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://<b>static.foo.bar</b>/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')

Где выполняется условие if 'static.foo.bar' in str(info.requestUrl()): для первого и второго URL-адреса (я выделил его жирным шрифтом для лучшей визуализации) .

Решением является улучшение фильтра:

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