-
-
Notifications
You must be signed in to change notification settings - Fork 213
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: [#1502] Allow fetch to be intercepted and modified #1662
Merged
capricorn86
merged 8 commits into
capricorn86:master
from
OlaviSau:feature-1502-allow-fetch-to-be-intercepted
Jan 7, 2025
+649
−8
Merged
Changes from 4 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
a9ec9d8
feat: [1502] Allow fetch to be intercepted and modified
OlaviSau 050bab7
feat: [1502] Add the possibility to intercept sync requests
OlaviSau fdda4ed
fix: [1502] Correct the types for sync response hooks
OlaviSau c5f8b26
feat: [1502] Allow fetch response to be intercepted
OlaviSau 3717627
fix: [1502] Move async task manager to be ended after the intercept
OlaviSau 700ff3e
Merge branch 'master' into feature-1502-allow-fetch-to-be-intercepted
OlaviSau 56c8c97
fix: [1502] The interceptor not always being there
OlaviSau 6e4b68d
Merge branch 'feature-1502-allow-fetch-to-be-intercepted' of github.c…
OlaviSau File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,6 +26,7 @@ import FetchResponseHeaderUtility from './utilities/FetchResponseHeaderUtility.j | |
import FetchHTTPSCertificate from './certificate/FetchHTTPSCertificate.js'; | ||
import { Buffer } from 'buffer'; | ||
import FetchBodyUtility from './utilities/FetchBodyUtility.js'; | ||
import IFetchInterceptor from './types/IFetchInterceptor.js'; | ||
|
||
const LAST_CHUNK = Buffer.from('0\r\n\r\n'); | ||
|
||
|
@@ -39,7 +40,7 @@ const LAST_CHUNK = Buffer.from('0\r\n\r\n'); | |
*/ | ||
export default class Fetch { | ||
private reject: (reason: Error) => void | null = null; | ||
private resolve: (value: Response | Promise<Response>) => void | null = null; | ||
private resolve: (value: Response | Promise<Response>) => Promise<void> = null; | ||
private listeners = { | ||
onSignalAbort: this.onSignalAbort.bind(this) | ||
}; | ||
|
@@ -50,6 +51,7 @@ export default class Fetch { | |
private nodeResponse: IncomingMessage | null = null; | ||
private response: Response | null = null; | ||
private responseHeaders: Headers | null = null; | ||
private interceptor?: IFetchInterceptor; | ||
private request: Request; | ||
private redirectCount = 0; | ||
private disableCache: boolean; | ||
|
@@ -99,6 +101,7 @@ export default class Fetch { | |
options.disableSameOriginPolicy ?? | ||
this.#browserFrame.page.context.browser.settings.fetch.disableSameOriginPolicy ?? | ||
false; | ||
this.interceptor = this.#browserFrame.page.context.browser.settings.fetch.interceptor; | ||
} | ||
|
||
/** | ||
|
@@ -108,6 +111,15 @@ export default class Fetch { | |
*/ | ||
public async send(): Promise<Response> { | ||
FetchRequestReferrerUtility.prepareRequest(new URL(this.#window.location.href), this.request); | ||
const beforeRequestResponse = this.interceptor?.beforeAsyncRequest | ||
? await this.interceptor.beforeAsyncRequest({ | ||
request: this.request, | ||
window: this.#window | ||
}) | ||
: undefined; | ||
if (beforeRequestResponse instanceof Response) { | ||
return beforeRequestResponse; | ||
} | ||
FetchRequestValidationUtility.validateSchema(this.request); | ||
|
||
if (this.request.signal.aborted) { | ||
|
@@ -122,7 +134,14 @@ export default class Fetch { | |
this.response = new this.#window.Response(result.buffer, { | ||
headers: { 'Content-Type': result.type } | ||
}); | ||
return this.response; | ||
const interceptedResponse = this.interceptor.afterAsyncResponse | ||
? await this.interceptor.afterAsyncResponse({ | ||
window: this.#window, | ||
response: this.response, | ||
request: this.request | ||
}) | ||
: undefined; | ||
return interceptedResponse instanceof Response ? interceptedResponse : this.response; | ||
} | ||
|
||
// Security check for "https" to "http" requests. | ||
|
@@ -365,9 +384,9 @@ export default class Fetch { | |
throw new this.#window.Error('Fetch already sent.'); | ||
} | ||
|
||
this.resolve = (response: Response | Promise<Response>): void => { | ||
this.resolve = async (response: Response | Promise<Response>): Promise<void> => { | ||
// We can end up here when closing down the browser frame and there is an ongoing request. | ||
// Therefore we need to check if browserFrame.page.context is still available. | ||
// Therefore, we need to check if browserFrame.page.context is still available. | ||
if ( | ||
!this.disableCache && | ||
response instanceof Response && | ||
|
@@ -383,7 +402,14 @@ export default class Fetch { | |
}); | ||
} | ||
this.#browserFrame[PropertySymbol.asyncTaskManager].endTask(taskID); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We need to end the task for the "asyncTaskManager" after the intercepted response has been handled There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed it :) |
||
resolve(response); | ||
const interceptedResponse = this.interceptor.afterAsyncResponse | ||
? await this.interceptor.afterAsyncResponse({ | ||
window: this.#window, | ||
response: await response, | ||
request: this.request | ||
}) | ||
: undefined; | ||
resolve(interceptedResponse instanceof Response ? interceptedResponse : response); | ||
}; | ||
this.reject = (error: Error): void => { | ||
this.#browserFrame[PropertySymbol.asyncTaskManager].endTask(taskID); | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import Request from '../Request.js'; | ||
import BrowserWindow from '../../window/BrowserWindow.js'; | ||
import Response from '../Response.js'; | ||
import ISyncResponse from './ISyncResponse.js'; | ||
|
||
export default interface IFetchInterceptor { | ||
/** | ||
* Hook dispatched before making an async request. | ||
* It can be used for modifying the request, providing a response without making a request or for logging. | ||
* | ||
* @param context Contains the request and the window from where the request was made. | ||
* | ||
* @returns Promise that can resolve to a response to be used instead of sending out the response. | ||
*/ | ||
beforeAsyncRequest?: (context: { | ||
request: Request; | ||
window: BrowserWindow; | ||
}) => Promise<Response | void>; | ||
|
||
/** | ||
* Hook dispatched before making an sync request. | ||
* It can be used for modifying the request, providing a response without making a request or for logging. | ||
* | ||
* @param context Contains the request and the window from where the request was made. | ||
* | ||
* @returns Promise that can resolve to a response to be used instead of sending out the response. | ||
*/ | ||
beforeSyncRequest?: (context: { | ||
request: Request; | ||
window: BrowserWindow; | ||
}) => ISyncResponse | void; | ||
|
||
/** | ||
* Hook dispatched after receiving an async response. | ||
* It can be used for modifying or replacing the response and logging. | ||
* | ||
* @param context Contains the request, response and the window from where the request was made. | ||
* | ||
* @returns Promise that can resolve to a response to be used instead of sending out the response. | ||
*/ | ||
afterAsyncResponse?: (context: { | ||
request: Request; | ||
response: Response; | ||
window: BrowserWindow; | ||
}) => Promise<Response | void>; | ||
|
||
/** | ||
* Hook dispatched after receiving a sync response. | ||
* It can be used for modifying or replacing the response and logging | ||
* | ||
* @param context Contains the request, response and the window from where the request was made. | ||
* | ||
* @returns Promise that can resolve to a response to be used instead of sending out the response. | ||
*/ | ||
afterSyncResponse?: (context: { | ||
request: Request; | ||
response: ISyncResponse; | ||
window: BrowserWindow; | ||
}) => ISyncResponse | void; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this cause any issues? @capricorn86
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is only used internally, so it should be fine