eric7/WebBrowser/Network/NetworkUrlInterceptor.py

branch
eric7
changeset 8312
800c432b34c8
parent 8228
772103b14c18
child 8318
962bce857696
equal deleted inserted replaced
8311:4e8b98454baa 8312:800c432b34c8
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2016 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a class to handle URL requests before they get processed
8 by QtWebEngine.
9 """
10
11 from PyQt5.QtCore import QMutex, QUrl
12 from PyQt5.QtWebEngineCore import (
13 QWebEngineUrlRequestInterceptor, QWebEngineUrlRequestInfo
14 )
15
16 from E5Utilities.E5MutexLocker import E5MutexLocker
17
18 from ..WebBrowserPage import WebBrowserPage
19
20 import Preferences
21
22
23 class NetworkUrlInterceptor(QWebEngineUrlRequestInterceptor):
24 """
25 Class implementing an URL request handler.
26 """
27 def __init__(self, parent=None):
28 """
29 Constructor
30
31 @param parent reference to the parent object
32 @type QObject
33 """
34 super().__init__(parent)
35
36 self.__interceptors = []
37 self.__mutex = QMutex()
38
39 self.__loadSettings()
40
41 def interceptRequest(self, info):
42 """
43 Public method handling an URL request.
44
45 @param info URL request information
46 @type QWebEngineUrlRequestInfo
47 """
48 with E5MutexLocker(self.__mutex):
49 # Do Not Track feature
50 if self.__doNotTrack:
51 info.setHttpHeader(b"DNT", b"1")
52 info.setHttpHeader(b"X-Do-Not-Track", b"1")
53
54 # Send referrer header?
55 if info.requestUrl().host() not in Preferences.getWebBrowser(
56 "SendRefererWhitelist"):
57 self.__setRefererHeader(info)
58
59 # User Agents header
60 userAgent = WebBrowserPage.userAgentForUrl(info.requestUrl())
61 info.setHttpHeader(b"User-Agent", userAgent.encode())
62
63 for interceptor in self.__interceptors:
64 interceptor.interceptRequest(info)
65
66 def installUrlInterceptor(self, interceptor):
67 """
68 Public method to install an URL interceptor.
69
70 @param interceptor URL interceptor to be installed
71 @type UrlInterceptor
72 """
73 with E5MutexLocker(self.__mutex):
74 if interceptor not in self.__interceptors:
75 self.__interceptors.append(interceptor)
76
77 def removeUrlInterceptor(self, interceptor):
78 """
79 Public method to remove an URL interceptor.
80
81 @param interceptor URL interceptor to be removed
82 @type UrlInterceptor
83 """
84 with E5MutexLocker(self.__mutex):
85 if interceptor in self.__interceptors:
86 self.__interceptors.remove(interceptor)
87
88 def __loadSettings(self):
89 """
90 Private method to load the Network Manager settings.
91 """
92 with E5MutexLocker(self.__mutex):
93 self.__doNotTrack = Preferences.getWebBrowser(
94 "DoNotTrack")
95 self.__sendReferer = Preferences.getWebBrowser(
96 "RefererSendReferer")
97 self.__refererDefaultPolicy = Preferences.getWebBrowser(
98 "RefererDefaultPolicy")
99 self.__refererTrimmingPolicy = Preferences.getWebBrowser(
100 "RefererTrimmingPolicy")
101
102 def preferencesChanged(self):
103 """
104 Public slot to handle a change of preferences.
105 """
106 self.__loadSettings()
107
108 def __setRefererHeader(self, info):
109 """
110 Private method to set the 'Referer' header depending on the configured
111 rule set.
112
113 @param info URL request information
114 @type QWebEngineUrlRequestInfo
115 @see <a href="https://wiki.mozilla.org/Security/Referrer">
116 Mozilla Referrer</a>
117 @see <a href="https://www.w3.org/TR/referrer-policy/">
118 W3C Referrer Policy</a>
119 """
120 # 1. SendReferer:
121 # 0 = never
122 # 1 = only on click (NavigationTypeLink)
123 # 2 = always (default)
124 # 2. RefererTrimmingPolicy:
125 # 0 = send full URL (no trimming) (default)
126 # 1 = send the URL without its query string
127 # 2 = only send the origin (ensure trailing /)
128 # 3. RefererDefaultPolicy:
129 # set the default referrer policy (which can be overriden by
130 # the site)
131 # 0 = no-referrer
132 # 1 = same-origin
133 # 2 = strict-origin-when-cross-origin
134 # 3 = no-referrer-when-downgrade (default)
135 # see: https://wiki.mozilla.org/Security/Referrer
136 # see: https://www.w3.org/TR/referrer-policy/
137
138 if (
139 self.__sendReferer == 0 or
140 # never send referer header
141
142 (self.__sendReferer == 1 and
143 (info.navigationType() !=
144 QWebEngineUrlRequestInfo.NavigationType.NavigationTypeLink))
145 # send referer header only on click
146 ):
147 info.setHttpHeader(b"Referer", b"")
148 else:
149 # send referer header always applying further policies
150 url = info.firstPartyUrl()
151 reqUrl = info.requestUrl()
152 if self.__refererDefaultPolicy == 0:
153 # no-referrer
154 refererUrl = b""
155 elif self.__refererDefaultPolicy == 1:
156 # same-origin
157 if self.__sameOrigin(url, reqUrl):
158 refererUrl = self.__trimmedReferer(url)
159 else:
160 refererUrl = b""
161 elif self.__refererDefaultPolicy == 2:
162 # strict-origin-when-cross-origin
163 if self.__sameOrigin(url, reqUrl):
164 refererUrl = self.__trimmedReferer(url)
165 elif url.scheme() in ("https", "wss"):
166 if self.__potentiallyTrustworthy(url):
167 refererUrl = self.__refererOrigin(url)
168 else:
169 refererUrl = b""
170 else:
171 refererUrl = self.__refererOrigin(url)
172 else:
173 # no-referrer-when-downgrade
174 if (
175 url.scheme() in ("https", "wss") and
176 not self.__potentiallyTrustworthy(url)
177 ):
178 refererUrl = b""
179 else:
180 refererUrl = self.__trimmedReferer(url)
181
182 info.setHttpHeader(b"Referer", refererUrl)
183
184 def __sameOrigin(self, url1, url2):
185 """
186 Private method to test the "same origin" policy.
187
188 @param url1 first URL for the test
189 @type QUrl
190 @param url2 second URL for the test
191 @type QUrl
192 @return flag indicating that both URLs have the same origin
193 @rtype bool
194 """
195 origin1 = url1.url(QUrl.UrlFormattingOption.RemoveUserInfo |
196 QUrl.UrlFormattingOption.RemovePath)
197 origin2 = url2.url(QUrl.UrlFormattingOption.RemoveUserInfo |
198 QUrl.UrlFormattingOption.RemovePath)
199
200 return origin1 == origin2
201
202 def __potentiallyTrustworthy(self, url):
203 """
204 Private method to check, if the given URL is potentially trustworthy.
205
206 @param url URL to be checked
207 @type QUrl
208 @return flag indicating a potentially trustworthy URL
209 @rtype bool
210 """
211 if url.scheme() == "data":
212 return False
213
214 if url.toString() in ("about:blank", "about:srcdoc"):
215 return True
216
217 origin = url.adjusted(QUrl.UrlFormattingOption.RemoveUserInfo |
218 QUrl.UrlFormattingOption.RemovePath)
219
220 if origin.isEmpty() or origin.scheme() == "":
221 return False
222 if origin.scheme() in ("https", "wss"):
223 return True
224 if origin.host().startswith("127.") or origin.host().endswith(":1"):
225 return True
226 if (
227 origin.host() == "localhost" or
228 origin.host().endswith(".localhost")
229 ):
230 return True
231 if origin.scheme() == "file":
232 return True
233 if origin.scheme() in ("qrc", "qthelp", "eric"):
234 return True
235
236 return False
237
238 def __trimmedReferer(self, url):
239 """
240 Private method to generate the trimmed referer header URL.
241
242 @param url URL to be trimmed as a referer header
243 @type QUrl
244 @return trimmed referer header URL
245 @rtype QByteArray or bytes
246 """
247 if self.__refererTrimmingPolicy == 0:
248 # send full URL (no trimming) (default)
249 refererUrl = url.toEncoded(
250 QUrl.UrlFormattingOption.RemoveUserInfo |
251 QUrl.UrlFormattingOption.RemoveFragment
252 )
253 elif self.__refererTrimmingPolicy == 1:
254 # send the URL without its query string
255 refererUrl = url.toEncoded(
256 QUrl.UrlFormattingOption.RemoveUserInfo |
257 QUrl.UrlFormattingOption.RemoveFragment |
258 QUrl.UrlFormattingOption.RemoveQuery
259 )
260 else:
261 # only send the origin (ensure trailing /)
262 refererUrl = self.__refererOrigin(url)
263
264 return refererUrl
265
266 def __refererOrigin(self, url):
267 """
268 Private method to generate an origin referer header URL.
269
270 @param url URL to generate the header from
271 @type QUrl
272 @return origin referer header URL
273 @rtype QByteArray or bytes
274 """
275 referer = url.toEncoded(
276 QUrl.UrlFormattingOption.RemoveUserInfo |
277 QUrl.UrlFormattingOption.RemovePath
278 )
279 if not referer.endsWith(b"/"):
280 referer += b"/"
281
282 return referer

eric ide

mercurial