@@ -8,6 +8,8 @@ import HTTP, { IncomingMessage } from 'http';
88import HTTPS from 'https' ;
99import Zlib from 'zlib' ;
1010import URL from '../url/URL.js' ;
11+ import FS from 'fs' ;
12+ import Path from 'path' ;
1113import { Socket } from 'net' ;
1214import Stream from 'stream' ;
1315import DataURIParser from './data-uri/DataURIParser.js' ;
@@ -27,6 +29,7 @@ import FetchHTTPSCertificate from './certificate/FetchHTTPSCertificate.js';
2729import { Buffer } from 'buffer' ;
2830import FetchBodyUtility from './utilities/FetchBodyUtility.js' ;
2931import IFetchInterceptor from './types/IFetchInterceptor.js' ;
32+ import VirtualServerUtility from './utilities/VirtualServerUtility.js' ;
3033
3134const LAST_CHUNK = Buffer . from ( '0\r\n\r\n' ) ;
3235
@@ -51,7 +54,7 @@ export default class Fetch {
5154 private nodeResponse : IncomingMessage | null = null ;
5255 private response : Response | null = null ;
5356 private responseHeaders : Headers | null = null ;
54- private interceptor ? : IFetchInterceptor ;
57+ private interceptor : IFetchInterceptor | null ;
5558 private request : Request ;
5659 private redirectCount = 0 ;
5760 private disableCache : boolean ;
@@ -111,17 +114,27 @@ export default class Fetch {
111114 */
112115 public async send ( ) : Promise < Response > {
113116 FetchRequestReferrerUtility . prepareRequest ( new URL ( this . #window. location . href ) , this . request ) ;
114- const beforeRequestResponse = this . interceptor ?. beforeAsyncRequest
115- ? await this . interceptor . beforeAsyncRequest ( {
116- request : this . request ,
117- window : this . #window
118- } )
119- : undefined ;
120- if ( beforeRequestResponse instanceof Response ) {
121- return beforeRequestResponse ;
117+
118+ if ( this . interceptor ?. beforeAsyncRequest ) {
119+ const taskID = this . #browserFrame[ PropertySymbol . asyncTaskManager ] . startTask ( ) ;
120+ const response = await this . interceptor . beforeAsyncRequest ( {
121+ request : this . request ,
122+ window : this . #window
123+ } ) ;
124+ this . #browserFrame[ PropertySymbol . asyncTaskManager ] . endTask ( taskID ) ;
125+ if ( response instanceof Response ) {
126+ return response ;
127+ }
122128 }
129+
123130 FetchRequestValidationUtility . validateSchema ( this . request ) ;
124131
132+ const virtualServerResponse = await this . getVirtualServerResponse ( ) ;
133+
134+ if ( virtualServerResponse ) {
135+ return virtualServerResponse ;
136+ }
137+
125138 if ( this . request . signal . aborted ) {
126139 throw new this . #window. DOMException (
127140 'The operation was aborted.' ,
@@ -171,7 +184,7 @@ export default class Fetch {
171184 const compliesWithCrossOriginPolicy = await this . compliesWithCrossOriginPolicy ( ) ;
172185
173186 if ( ! compliesWithCrossOriginPolicy ) {
174- this . #window . console . warn (
187+ this . #browserFrame ?. page ? .console . warn (
175188 `Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at "${ this . request . url } ".`
176189 ) ;
177190 throw new this . #window. DOMException (
@@ -270,6 +283,62 @@ export default class Fetch {
270283 return response ;
271284 }
272285
286+ /**
287+ * Returns virtual server response.
288+ *
289+ * @returns Response.
290+ */
291+ private async getVirtualServerResponse ( ) : Promise < Response | null > {
292+ const filePath = VirtualServerUtility . getFilepath ( this . #window, this . request . url ) ;
293+
294+ if ( ! filePath ) {
295+ return null ;
296+ }
297+
298+ if ( this . request . method !== 'GET' ) {
299+ this . #browserFrame?. page ?. console . error (
300+ `${ this . request . method } ${ this . request . url } 404 (Not Found)`
301+ ) ;
302+ return VirtualServerUtility . getNotFoundResponse ( this . #window) ;
303+ }
304+
305+ const taskID = this . #browserFrame[ PropertySymbol . asyncTaskManager ] . startTask ( ) ;
306+ let buffer : Buffer ;
307+
308+ try {
309+ const stat = await FS . promises . stat ( filePath ) ;
310+ buffer = await FS . promises . readFile (
311+ stat . isDirectory ( ) ? Path . join ( filePath , 'index.html' ) : filePath
312+ ) ;
313+ } catch ( error ) {
314+ this . #browserFrame?. page ?. console . error (
315+ `${ this . request . method } ${ this . request . url } 404 (Not Found)`
316+ ) ;
317+
318+ this . #browserFrame[ PropertySymbol . asyncTaskManager ] . endTask ( taskID ) ;
319+
320+ return VirtualServerUtility . getNotFoundResponse ( this . #window) ;
321+ }
322+
323+ this . #browserFrame[ PropertySymbol . asyncTaskManager ] . endTask ( taskID ) ;
324+
325+ const body = new this . #window. ReadableStream ( {
326+ start ( controller ) {
327+ setTimeout ( ( ) => {
328+ controller . enqueue ( buffer ) ;
329+ controller . close ( ) ;
330+ } ) ;
331+ }
332+ } ) ;
333+
334+ const response = new this . #window. Response ( body ) ;
335+
336+ response [ PropertySymbol . buffer ] = buffer ;
337+ ( < string > response . url ) = this . request . url ;
338+
339+ return response ;
340+ }
341+
273342 /**
274343 * Checks if the request complies with the Cross-Origin policy.
275344 *
@@ -410,7 +479,17 @@ export default class Fetch {
410479 } )
411480 : undefined ;
412481 this . #browserFrame[ PropertySymbol . asyncTaskManager ] . endTask ( taskID ) ;
413- resolve ( interceptedResponse instanceof Response ? interceptedResponse : response ) ;
482+ const returnResponse =
483+ interceptedResponse instanceof Response ? interceptedResponse : response ;
484+
485+ // The browser outputs errors to the console when the response is not ok.
486+ if ( returnResponse instanceof Response && ! returnResponse . ok ) {
487+ this . #browserFrame?. page ?. console . error (
488+ `${ this . request . method } ${ this . request . url } ${ returnResponse . status } (${ returnResponse . statusText } )`
489+ ) ;
490+ }
491+
492+ resolve ( returnResponse ) ;
414493 } ;
415494 this . reject = ( error : Error ) : void => {
416495 this . #browserFrame[ PropertySymbol . asyncTaskManager ] . endTask ( taskID ) ;
@@ -517,7 +596,7 @@ export default class Fetch {
517596 */
518597 private onError ( error : Error ) : void {
519598 this . finalizeRequest ( ) ;
520- this . #window . console . error ( error ) ;
599+ this . #browserFrame ?. page ? .console . error ( error ) ;
521600 this . reject (
522601 new this . #window. DOMException (
523602 `Failed to execute "fetch()" on "Window" with URL "${ this . request . url } ": ${ error . message } ` ,
0 commit comments