From 90385a7941e2e3bf673adcc16372c2f4a1da2dae Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Mon, 16 Aug 2021 09:36:28 -0700 Subject: [PATCH] browser(firefox): failure response interception (#8194) --- browser_patches/firefox-beta/BUILD_NUMBER | 4 +- .../firefox-beta/juggler/NetworkObserver.js | 60 ++++++++++++------- .../firefox-beta/juggler/protocol/Protocol.js | 1 + browser_patches/firefox/BUILD_NUMBER | 4 +- .../firefox/juggler/NetworkObserver.js | 60 ++++++++++++------- .../firefox/juggler/protocol/Protocol.js | 1 + src/server/firefox/ffNetworkManager.ts | 2 + 7 files changed, 82 insertions(+), 50 deletions(-) diff --git a/browser_patches/firefox-beta/BUILD_NUMBER b/browser_patches/firefox-beta/BUILD_NUMBER index 127898f99d..34b1c99202 100644 --- a/browser_patches/firefox-beta/BUILD_NUMBER +++ b/browser_patches/firefox-beta/BUILD_NUMBER @@ -1,2 +1,2 @@ -1278 -Changed: lushnikov@chromium.org Fri 13 Aug 2021 08:38:35 AM PDT +1279 +Changed: yurys@chromium.org Fri 13 Aug 2021 07:00:48 PM PDT diff --git a/browser_patches/firefox-beta/juggler/NetworkObserver.js b/browser_patches/firefox-beta/juggler/NetworkObserver.js index c3d2ac423b..951e15770a 100644 --- a/browser_patches/firefox-beta/juggler/NetworkObserver.js +++ b/browser_patches/firefox-beta/juggler/NetworkObserver.js @@ -78,7 +78,7 @@ class PageNetwork { const intercepted = this._interceptedRequests.get(requestId); if (!intercepted) throw new Error(`Cannot find request "${requestId}"`); - return { response: await new ResponseInterceptor(intercepted).interceptResponse(url, method, headers, postData) }; + return await new ResponseInterceptor(intercepted).interceptResponse(url, method, headers, postData); } this._takeIntercepted(requestId).resume(url, method, headers, postData); return {}; @@ -152,37 +152,47 @@ class ResponseInterceptor { // channel gets redirected we report the redirect on the original(paused) // request. networkObserver._channelToRequest.set(newChannel, this._originalRequest); - const body = await new Promise((resolve, reject) => { - NetUtil.asyncFetch(newChannel, (stream, status) => { - networkObserver._responseInterceptionChannels.delete(newChannel); - networkObserver._channelToRequest.delete(newChannel); - if (!Components.isSuccessCode(status)) { - reject(status); - return; - } - try { - resolve(NetUtil.readInputStreamToString(stream, stream.available())); - } catch (e) { - if (e.result == Cr.NS_BASE_STREAM_CLOSED) { - // The stream was empty. - resolve(''); - } else { - reject(e); + const pageNetwork = this._originalRequest._pageNetwork; + let body; + try { + body = await new Promise((resolve, reject) => { + NetUtil.asyncFetch(newChannel, (stream, status) => { + networkObserver._responseInterceptionChannels.delete(newChannel); + networkObserver._channelToRequest.delete(newChannel); + if (!Components.isSuccessCode(status)) { + reject(status); + return; } - } finally { - stream.close(); - } + try { + resolve(NetUtil.readInputStreamToString(stream, stream.available())); + } catch (e) { + if (e.result == Cr.NS_BASE_STREAM_CLOSED) { + // The stream was empty. + resolve(''); + } else { + reject(e.result); + } + } finally { + stream.close(); + } + }); }); - }); + } catch (error) { + if (typeof error !== 'number') + dump(`ERROR: enexpected error type: ${error}\n`); + if (pageNetwork) + pageNetwork._interceptedRequests.delete(this._originalRequest.requestId); + this._originalRequest._failWithErrorCode(error); + return { error: helper.getNetworkErrorStatusText(error) }; + } const finalRequest = this._finalRequest; const responseChannel = finalRequest === this._originalRequest ? this._responseChannel : finalRequest.httpChannel; - const pageNetwork = this._originalRequest._pageNetwork; if (pageNetwork) pageNetwork._responseStorage.addResponseBody(finalRequest, responseChannel, body); const response = responseHead(responseChannel); this._interceptedResponse = Object.assign({ body }, response); - return response; + return { response }; } isInterceptedRequest(request) { @@ -332,6 +342,10 @@ class NetworkRequest { // Public interception API. abort(errorCode) { const error = errorMap[errorCode] || Cr.NS_ERROR_FAILURE; + this._failWithErrorCode(error); + } + + _failWithErrorCode(error) { this._interceptedChannel.cancelInterception(error); this._interceptedChannel = undefined; } diff --git a/browser_patches/firefox-beta/juggler/protocol/Protocol.js b/browser_patches/firefox-beta/juggler/protocol/Protocol.js index 910678e955..9ebf15b013 100644 --- a/browser_patches/firefox-beta/juggler/protocol/Protocol.js +++ b/browser_patches/firefox-beta/juggler/protocol/Protocol.js @@ -536,6 +536,7 @@ const Network = { }, returns: { response: t.Optional(networkTypes.InterceptedResponse), + error: t.Optional(t.String), }, }, 'fulfillInterceptedRequest': { diff --git a/browser_patches/firefox/BUILD_NUMBER b/browser_patches/firefox/BUILD_NUMBER index a766aa883d..499b198bfe 100644 --- a/browser_patches/firefox/BUILD_NUMBER +++ b/browser_patches/firefox/BUILD_NUMBER @@ -1,2 +1,2 @@ -1283 -Changed: dgozman@gmail.com Thu Aug 12 16:33:32 PDT 2021 +1284 +Changed: yurys@chromium.org Fri 13 Aug 2021 07:00:24 PM PDT diff --git a/browser_patches/firefox/juggler/NetworkObserver.js b/browser_patches/firefox/juggler/NetworkObserver.js index c3d2ac423b..951e15770a 100644 --- a/browser_patches/firefox/juggler/NetworkObserver.js +++ b/browser_patches/firefox/juggler/NetworkObserver.js @@ -78,7 +78,7 @@ class PageNetwork { const intercepted = this._interceptedRequests.get(requestId); if (!intercepted) throw new Error(`Cannot find request "${requestId}"`); - return { response: await new ResponseInterceptor(intercepted).interceptResponse(url, method, headers, postData) }; + return await new ResponseInterceptor(intercepted).interceptResponse(url, method, headers, postData); } this._takeIntercepted(requestId).resume(url, method, headers, postData); return {}; @@ -152,37 +152,47 @@ class ResponseInterceptor { // channel gets redirected we report the redirect on the original(paused) // request. networkObserver._channelToRequest.set(newChannel, this._originalRequest); - const body = await new Promise((resolve, reject) => { - NetUtil.asyncFetch(newChannel, (stream, status) => { - networkObserver._responseInterceptionChannels.delete(newChannel); - networkObserver._channelToRequest.delete(newChannel); - if (!Components.isSuccessCode(status)) { - reject(status); - return; - } - try { - resolve(NetUtil.readInputStreamToString(stream, stream.available())); - } catch (e) { - if (e.result == Cr.NS_BASE_STREAM_CLOSED) { - // The stream was empty. - resolve(''); - } else { - reject(e); + const pageNetwork = this._originalRequest._pageNetwork; + let body; + try { + body = await new Promise((resolve, reject) => { + NetUtil.asyncFetch(newChannel, (stream, status) => { + networkObserver._responseInterceptionChannels.delete(newChannel); + networkObserver._channelToRequest.delete(newChannel); + if (!Components.isSuccessCode(status)) { + reject(status); + return; } - } finally { - stream.close(); - } + try { + resolve(NetUtil.readInputStreamToString(stream, stream.available())); + } catch (e) { + if (e.result == Cr.NS_BASE_STREAM_CLOSED) { + // The stream was empty. + resolve(''); + } else { + reject(e.result); + } + } finally { + stream.close(); + } + }); }); - }); + } catch (error) { + if (typeof error !== 'number') + dump(`ERROR: enexpected error type: ${error}\n`); + if (pageNetwork) + pageNetwork._interceptedRequests.delete(this._originalRequest.requestId); + this._originalRequest._failWithErrorCode(error); + return { error: helper.getNetworkErrorStatusText(error) }; + } const finalRequest = this._finalRequest; const responseChannel = finalRequest === this._originalRequest ? this._responseChannel : finalRequest.httpChannel; - const pageNetwork = this._originalRequest._pageNetwork; if (pageNetwork) pageNetwork._responseStorage.addResponseBody(finalRequest, responseChannel, body); const response = responseHead(responseChannel); this._interceptedResponse = Object.assign({ body }, response); - return response; + return { response }; } isInterceptedRequest(request) { @@ -332,6 +342,10 @@ class NetworkRequest { // Public interception API. abort(errorCode) { const error = errorMap[errorCode] || Cr.NS_ERROR_FAILURE; + this._failWithErrorCode(error); + } + + _failWithErrorCode(error) { this._interceptedChannel.cancelInterception(error); this._interceptedChannel = undefined; } diff --git a/browser_patches/firefox/juggler/protocol/Protocol.js b/browser_patches/firefox/juggler/protocol/Protocol.js index 910678e955..9ebf15b013 100644 --- a/browser_patches/firefox/juggler/protocol/Protocol.js +++ b/browser_patches/firefox/juggler/protocol/Protocol.js @@ -536,6 +536,7 @@ const Network = { }, returns: { response: t.Optional(networkTypes.InterceptedResponse), + error: t.Optional(t.String), }, }, 'fulfillInterceptedRequest': { diff --git a/src/server/firefox/ffNetworkManager.ts b/src/server/firefox/ffNetworkManager.ts index eddc7e7788..2ef90ec310 100644 --- a/src/server/firefox/ffNetworkManager.ts +++ b/src/server/firefox/ffNetworkManager.ts @@ -227,6 +227,8 @@ class FFRouteImpl implements network.RouteDelegate { }) as any; if (!overrides.interceptResponse) return null; + if (result.error) + throw new Error(`Request failed: ${result.error}`); return new InterceptedResponse(request, result.response.status, result.response.statusText, result.response.headers); }