From ab700bbeedc2c35b3ce2655887d8d1c81d2a5984 Mon Sep 17 00:00:00 2001 From: Juzar Bharmal <53657281+Juzar10@users.noreply.github.com> Date: Tue, 10 Dec 2024 12:04:17 +0530 Subject: [PATCH 1/5] fix: headers for the ASCII characters. --- .../api-fetch/src/middlewares/preloading.js | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/packages/api-fetch/src/middlewares/preloading.js b/packages/api-fetch/src/middlewares/preloading.js index ac293738513bd3..32f127625748a4 100644 --- a/packages/api-fetch/src/middlewares/preloading.js +++ b/packages/api-fetch/src/middlewares/preloading.js @@ -68,13 +68,37 @@ function createPreloadingMiddleware( preloadedData ) { * @return {Promise} Promise with the response. */ function prepareResponse( responseData, parse ) { + /** + * The Header object is created and encoded to ensure ASCII characters. + */ + const headers = new Headers( + Object.fromEntries( + Object.entries( responseData.headers ).map( ( [ key, value ] ) => { + if ( key.toLowerCase() === 'link' ) { + return [ + key, + value.replace( + /<([^>]+)>/, + ( + /** @type {any} */ _, + /** @type {string} */ url + ) => `<${ encodeURI( url ) }>` + ), + ]; + } + + return [ key, String( value ) ]; + } ) + ) + ); + return Promise.resolve( parse ? responseData.body : new window.Response( JSON.stringify( responseData.body ), { status: 200, statusText: 'OK', - headers: responseData.headers, + headers, } ) ); } From ee7a74d558a8e384d50ca7c36eb8a5034d89be24 Mon Sep 17 00:00:00 2001 From: Juzar Bharmal <53657281+Juzar10@users.noreply.github.com> Date: Tue, 10 Dec 2024 12:59:53 +0530 Subject: [PATCH 2/5] fix: used current object instead of the URI one --- .../api-fetch/src/middlewares/preloading.js | 39 +++++++------------ 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/packages/api-fetch/src/middlewares/preloading.js b/packages/api-fetch/src/middlewares/preloading.js index 32f127625748a4..852b862e631c7d 100644 --- a/packages/api-fetch/src/middlewares/preloading.js +++ b/packages/api-fetch/src/middlewares/preloading.js @@ -68,29 +68,20 @@ function createPreloadingMiddleware( preloadedData ) { * @return {Promise} Promise with the response. */ function prepareResponse( responseData, parse ) { - /** - * The Header object is created and encoded to ensure ASCII characters. - */ - const headers = new Headers( - Object.fromEntries( - Object.entries( responseData.headers ).map( ( [ key, value ] ) => { - if ( key.toLowerCase() === 'link' ) { - return [ - key, - value.replace( - /<([^>]+)>/, - ( - /** @type {any} */ _, - /** @type {string} */ url - ) => `<${ encodeURI( url ) }>` - ), - ]; - } - - return [ key, String( value ) ]; - } ) - ) - ); + if ( + typeof responseData.headers === 'object' && + responseData.headers !== null + ) { + Object.entries( responseData.headers ).forEach( ( [ key, value ] ) => { + if ( key.toLowerCase() === 'link' ) { + responseData.headers[ key ] = value.replace( + /<([^>]+)>/, + ( /** @type {any} */ _, /** @type {string} */ url ) => + `<${ encodeURI( url ) }>` + ); + } + } ); + } return Promise.resolve( parse @@ -98,7 +89,7 @@ function prepareResponse( responseData, parse ) { : new window.Response( JSON.stringify( responseData.body ), { status: 200, statusText: 'OK', - headers, + headers: responseData.headers, } ) ); } From 9bf3b32cfa1e2cc937d4c8f5604befdc5e489b93 Mon Sep 17 00:00:00 2001 From: Juzar Bharmal <53657281+Juzar10@users.noreply.github.com> Date: Thu, 30 Jan 2025 20:28:12 +0530 Subject: [PATCH 3/5] feat: update the preloading to avoid unnecessary encoding. --- .../api-fetch/src/middlewares/preloading.js | 37 ++++++++++++------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/packages/api-fetch/src/middlewares/preloading.js b/packages/api-fetch/src/middlewares/preloading.js index 852b862e631c7d..b16b8ca1edc0cd 100644 --- a/packages/api-fetch/src/middlewares/preloading.js +++ b/packages/api-fetch/src/middlewares/preloading.js @@ -68,10 +68,19 @@ function createPreloadingMiddleware( preloadedData ) { * @return {Promise} Promise with the response. */ function prepareResponse( responseData, parse ) { - if ( - typeof responseData.headers === 'object' && - responseData.headers !== null - ) { + if ( parse ) { + return Promise.resolve( responseData.body ); + } + + try { + return Promise.resolve( + new window.Response( JSON.stringify( responseData.body ), { + status: 200, + statusText: 'OK', + headers: responseData.headers, + } ) + ); + } catch { Object.entries( responseData.headers ).forEach( ( [ key, value ] ) => { if ( key.toLowerCase() === 'link' ) { responseData.headers[ key ] = value.replace( @@ -81,17 +90,17 @@ function prepareResponse( responseData, parse ) { ); } } ); - } - return Promise.resolve( - parse - ? responseData.body - : new window.Response( JSON.stringify( responseData.body ), { - status: 200, - statusText: 'OK', - headers: responseData.headers, - } ) - ); + return Promise.resolve( + parse + ? responseData.body + : new window.Response( JSON.stringify( responseData.body ), { + status: 200, + statusText: 'OK', + headers: responseData.headers, + } ) + ); + } } export default createPreloadingMiddleware; From 8fa73440890f95b1b0e7d4e61cabb0f555f95be0 Mon Sep 17 00:00:00 2001 From: Juzar Bharmal <53657281+Juzar10@users.noreply.github.com> Date: Wed, 5 Feb 2025 16:38:23 +0530 Subject: [PATCH 4/5] feat: add unit-test regarding the non-ascii characters. --- .../src/middlewares/test/preloading.js | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/packages/api-fetch/src/middlewares/test/preloading.js b/packages/api-fetch/src/middlewares/test/preloading.js index 696d8a37648651..a3ffedbdef46d5 100644 --- a/packages/api-fetch/src/middlewares/test/preloading.js +++ b/packages/api-fetch/src/middlewares/test/preloading.js @@ -265,6 +265,57 @@ describe( 'Preloading Middleware', () => { expect( secondMiddleware ).toHaveBeenCalledTimes( 1 ); } ); + it( 'should not throw an error when non-ASCII headers are present', async () => { + const noResponseMock = 'undefined' === typeof window.Response; + if ( noResponseMock ) { + window.Response = class { + constructor( body, options ) { + this.body = JSON.parse( body ); + this.headers = options.headers; + + // Check for non-ASCII characters in headers + for ( const [ key, value ] of Object.entries( + this.headers || {} + ) ) { + if ( /[^\x00-\x7F]/.test( value ) ) { + throw new Error( + `Invalid non-ASCII character found in header: ${ key }` + ); + } + } + } + }; + } + + const data = { + body: 'Hello', + headers: { + Link: '; rel="alternate"; type=text/html', + }, + }; + + const preloadedData = { + 'wp/v2/example': data, + }; + + const preloadingMiddleware = + createPreloadingMiddleware( preloadedData ); + + const requestOptions = { + method: 'GET', + path: 'wp/v2/example', + parse: false, + }; + + await expect( + preloadingMiddleware( requestOptions, () => {} ) + ).resolves.not.toThrow(); + + if ( noResponseMock ) { + delete window.Response; + } + } ); + describe.each( [ [ 'GET' ], [ 'OPTIONS' ] ] )( '%s', ( method ) => { describe.each( [ [ 'all empty', {} ], From 1f80988502d2e7122f7eb998947451810ab4ae54 Mon Sep 17 00:00:00 2001 From: Juzar Bharmal <53657281+Juzar10@users.noreply.github.com> Date: Wed, 5 Feb 2025 23:23:38 +0530 Subject: [PATCH 5/5] chore: add comment for clarity --- packages/api-fetch/src/middlewares/preloading.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/api-fetch/src/middlewares/preloading.js b/packages/api-fetch/src/middlewares/preloading.js index b16b8ca1edc0cd..3eaab48dc8392c 100644 --- a/packages/api-fetch/src/middlewares/preloading.js +++ b/packages/api-fetch/src/middlewares/preloading.js @@ -81,6 +81,7 @@ function prepareResponse( responseData, parse ) { } ) ); } catch { + // See: https://github.com/WordPress/gutenberg/issues/67358#issuecomment-2621163926. Object.entries( responseData.headers ).forEach( ( [ key, value ] ) => { if ( key.toLowerCase() === 'link' ) { responseData.headers[ key ] = value.replace(