src/eric7/WebBrowser/Network/NetworkUrlInterceptor.py

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

eric ide

mercurial