From ec4539f6670f876a7e257b91fe43f9e6b712d70b Mon Sep 17 00:00:00 2001 From: Jonathan Barrow Date: Sun, 8 Jan 2023 08:41:00 -0500 Subject: [PATCH 1/6] Fixed support for X-Cookie, X-Referer, X-Origin, and X-Set-Cookie headers --- src/main.js | 248 +++++++++++++++++++++++++++------------------------- 1 file changed, 131 insertions(+), 117 deletions(-) diff --git a/src/main.js b/src/main.js index 303042d..46ff221 100644 --- a/src/main.js +++ b/src/main.js @@ -1,140 +1,154 @@ const corsHeaders = { - 'Access-Control-Allow-Origin': '*', - 'Access-Control-Allow-Methods': 'GET,HEAD,POST,OPTIONS', - 'Access-Control-Max-Age': '86400', + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Methods': 'GET,HEAD,POST,OPTIONS', + 'Access-Control-Max-Age': '86400', }; -async function handleRequest(request, destinationUrl, iteration = 0) { - console.log( - `PROXYING ${destinationUrl}${ - iteration ? ' ON ITERATION ' + iteration : '' - }`, - ); +async function handleRequest(oRequest, destination, iteration = 0) { + console.log(`PROXYING ${destination}${iteration ? ' ON ITERATION ' + iteration : ''}`); - // Rewrite request to point to API url. This also makes the request mutable - // so we can add the correct Origin header to make the API server think - // that this request isn't cross-site. - request = new Request(destinationUrl, request); - request.headers.set('Origin', new URL(destinationUrl).origin); + // Create a new mutable request object for the destination + const request = new Request(destination, oRequest); + request.headers.set('Origin', new URL(destination).origin); - // Set PHPSESSID cookie - if (request.headers.get('PHPSESSID')) { - request.headers.set( - 'Cookie', - `PHPSESSID=${request.headers.get('PHPSESSID')};`, - ); - } + // TODO - Make cookie handling better. PHPSESSID overwrites all other cookie related headers - // Set User Agent - request.headers.set( - 'User-Agent', - ' Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:93.0) Gecko/20100101 Firefox/93.0', - ); + // Add custom X headers from client + // These headers are usually forbidden to be set by fetch + if (oRequest.headers.has('X-Cookie')) { + request.headers.set('Cookie', oRequest.headers.get('X-Cookie')); + request.headers.delete('X-Cookie'); + } - let response = await fetch(request); + if (request.headers.has('X-Referer')) { + request.headers.set('Referer', request.headers.get('X-Referer')); + request.headers.delete('X-Referer'); + } - if ( - (response.status === 302 || response.status === 301) && - response.headers.get('location') - ) { - if (iteration > 5) { - event.respondWith( - new Response('418 Too many redirects', { - status: 418, - }), - ); - } + if (request.headers.has('X-Origin')) { + request.headers.set('Origin', request.headers.get('X-Origin')); + request.headers.delete('X-Origin'); + } - return await handleRequest( - request, - response.headers.get('location'), - iteration + 1, - ); - } + // Set PHPSESSID cookie + if (request.headers.get('PHPSESSID')) { + request.headers.set('Cookie', `PHPSESSID=${request.headers.get('PHPSESSID')}`); + } - // Recreate the response so we can modify the headers - response = new Response(response.body, response); + // Set User Agent, if not exists + const useragent = request.headers.get('User-Agent'); + if (!useragent) { + request.headers.set('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:93.0) Gecko/20100101 Firefox/93.0'); + } - // Set CORS headers - response.headers.set('Access-Control-Allow-Origin', '*'); - response.headers.set('Access-Control-Expose-Headers', '*'); + // Fetch the new resource + const oResponse = await fetch(request); - // Get and set PHPSESSID cookie - const cookies = response.headers.get('Set-Cookie'); - if (cookies && cookies.includes('PHPSESSID') && cookies.includes(';')) { - let phpsessid = cookies.slice(cookies.search('PHPSESSID') + 10); - phpsessid = phpsessid.slice(0, phpsessid.search(';')); - response.headers.set('PHPSESSID', phpsessid); - } + // If the server returned a redirect, follow it + if ( + (oResponse.status === 302 || oResponse.status === 301) && + oResponse.headers.get('location') + ) { + // Server tried to redirect too many times + if (iteration > 5) { + return event.respondWith( + new Response('418 Too many redirects', { + status: 418 + }), + ); + } - // Append to/Add Vary header so browser will cache response correctly - response.headers.append('Vary', 'Origin'); + // Handle and return the request for the redirected destination + return await handleRequest(request, oResponse.headers.get('location'), iteration + 1); + } - return response; + // Create mutable response using the original response as init + const response = new Response(oResponse.body, oResponse); + + // Set CORS headers + response.headers.set('Access-Control-Allow-Origin', '*'); + response.headers.set('Access-Control-Expose-Headers', '*'); + + const cookiesToSet = response.headers.get('Set-Cookie'); + + // Transfer Set-Cookie to X-Set-Cookie + // Normally the Set-Cookie header is not accessible to fetch clients + if (cookiesToSet) { + response.headers.set('X-Set-Cookie', response.headers.get('Set-Cookie')); + } + + // Set PHPSESSID cookie + if (cookiesToSet && cookiesToSet.includes('PHPSESSID') && cookiesToSet.includes(';')) { + let phpsessid = cookies.slice(cookies.search('PHPSESSID') + 10); + phpsessid = phpsessid.slice(0, phpsessid.search(';')); + + response.headers.set('PHPSESSID', phpsessid); + } + + // Append to/Add Vary header so browser will cache response correctly + response.headers.append('Vary', 'Origin'); + + return response; } function handleOptions(request) { - // Make sure the necessary headers are present - // for this to be a valid pre-flight request - let headers = request.headers; + // Make sure the necessary headers are present + // for this to be a valid pre-flight request + const headers = request.headers; + let response = new Response(null, { + headers: { + Allow: 'GET, HEAD, POST, OPTIONS' + } + }); - if ( - headers.get('Origin') !== null && - headers.get('Access-Control-Request-Method') !== null && - headers.get('Access-Control-Request-Headers') !== null - ) { - return new Response(null, { - headers: { - ...corsHeaders, - // Allow all future content Request headers to go back to browser - // such as Authorization (Bearer) or X-Client-Name-Version - 'Access-Control-Allow-Headers': request.headers.get( - 'Access-Control-Request-Headers', - ), - }, - }); - } else { - // Handle standard OPTIONS request - return new Response(null, { - headers: { - Allow: 'GET, HEAD, POST, OPTIONS', - }, - }); - } + if ( + headers.get('Origin') !== null && + headers.get('Access-Control-Request-Method') !== null && + headers.get('Access-Control-Request-Headers') !== null + ) { + response = new Response(null, { + headers: { + ...corsHeaders, + // Allow all future content Request headers to go back to browser + // such as Authorization (Bearer) or X-Client-Name-Version + 'Access-Control-Allow-Headers': request.headers.get('Access-Control-Request-Headers') + } + }); + } + + return response; } addEventListener('fetch', (event) => { - const request = event.request; - const url = new URL(request.url); - const destinationUrl = url.searchParams.get('destination'); + const request = event.request; + const url = new URL(request.url); + const destination = url.searchParams.get('destination'); - console.log(`HTTP ${request.method} - ${request.url}`); + console.log(`HTTP ${request.method} - ${request.url}`); - if (request.method === 'OPTIONS') { - // Handle CORS preflight requests - event.respondWith(handleOptions(request)); - } else if (!destinationUrl) { - event.respondWith( - new Response('200 OK', { - status: 200, - headers: { - Allow: 'GET, HEAD, POST, OPTIONS', - 'Access-Control-Allow-Origin': '*', - }, - }), - ); - } else if ( - request.method === 'GET' || - request.method === 'HEAD' || - request.method === 'POST' - ) { - // Handle request - event.respondWith(handleRequest(request, destinationUrl)); - } else { - event.respondWith( - new Response('404 Not Found', { - status: 404, - }), - ); - } -}); + let response = new Response('404 Not Found', { + status: 404 + }); + + if (request.method === 'OPTIONS') { + // Handle CORS preflight requests + response = handleOptions(request); + } else if (!destination) { + response = new Response('200 OK', { + status: 200, + headers: { + Allow: 'GET, HEAD, POST, OPTIONS', + 'Access-Control-Allow-Origin': '*', + } + }); + } else if ( + request.method === 'GET' || + request.method === 'HEAD' || + request.method === 'POST' + ) { + // Handle request + response = handleRequest(request, destination); + } + + event.respondWith(response); +}); \ No newline at end of file From 0cdaeb71618b45d1615130fa5ba4e7d44e5b9384 Mon Sep 17 00:00:00 2001 From: Jelle van Snik Date: Sun, 8 Jan 2023 14:59:18 +0100 Subject: [PATCH 2/6] update linting action --- .github/workflows/linting_testing.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linting_testing.yml b/.github/workflows/linting_testing.yml index 79ff925..916f563 100644 --- a/.github/workflows/linting_testing.yml +++ b/.github/workflows/linting_testing.yml @@ -5,7 +5,7 @@ on: branches: - master - dev - pull_request: + pull_request_target: types: [opened, reopened, synchronize] jobs: From 459ad63d045c164b31d43e25725a71d147d58e62 Mon Sep 17 00:00:00 2001 From: Jelle van Snik Date: Sun, 8 Jan 2023 15:01:40 +0100 Subject: [PATCH 3/6] update linting --- .vscode/settings.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 633acae..e4df0f2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,5 @@ { "editor.formatOnSave": true, - "editor.defaultFormatter": "dbaeumer.vscode-eslint" + "editor.defaultFormatter": "dbaeumer.vscode-eslint", + "eslint.format.enable": true } From 13a4d0c8cc16d23058036d9de478549d4451aa7f Mon Sep 17 00:00:00 2001 From: Jonathan Barrow Date: Sun, 8 Jan 2023 09:08:49 -0500 Subject: [PATCH 4/6] Fixed linting/prettier errors --- src/main.js | 258 ++++++++++++++++++++++++++++------------------------ 1 file changed, 138 insertions(+), 120 deletions(-) diff --git a/src/main.js b/src/main.js index 46ff221..b9c8acc 100644 --- a/src/main.js +++ b/src/main.js @@ -1,154 +1,172 @@ const corsHeaders = { - 'Access-Control-Allow-Origin': '*', - 'Access-Control-Allow-Methods': 'GET,HEAD,POST,OPTIONS', - 'Access-Control-Max-Age': '86400', + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Methods': 'GET,HEAD,POST,OPTIONS', + 'Access-Control-Max-Age': '86400', }; async function handleRequest(oRequest, destination, iteration = 0) { - console.log(`PROXYING ${destination}${iteration ? ' ON ITERATION ' + iteration : ''}`); + console.log( + `PROXYING ${destination}${iteration ? ' ON ITERATION ' + iteration : ''}`, + ); - // Create a new mutable request object for the destination - const request = new Request(destination, oRequest); - request.headers.set('Origin', new URL(destination).origin); + // Create a new mutable request object for the destination + const request = new Request(destination, oRequest); + request.headers.set('Origin', new URL(destination).origin); - // TODO - Make cookie handling better. PHPSESSID overwrites all other cookie related headers + // TODO - Make cookie handling better. PHPSESSID overwrites all other cookie related headers - // Add custom X headers from client - // These headers are usually forbidden to be set by fetch - if (oRequest.headers.has('X-Cookie')) { - request.headers.set('Cookie', oRequest.headers.get('X-Cookie')); - request.headers.delete('X-Cookie'); - } + // Add custom X headers from client + // These headers are usually forbidden to be set by fetch + if (oRequest.headers.has('X-Cookie')) { + request.headers.set('Cookie', oRequest.headers.get('X-Cookie')); + request.headers.delete('X-Cookie'); + } - if (request.headers.has('X-Referer')) { - request.headers.set('Referer', request.headers.get('X-Referer')); - request.headers.delete('X-Referer'); - } + if (request.headers.has('X-Referer')) { + request.headers.set('Referer', request.headers.get('X-Referer')); + request.headers.delete('X-Referer'); + } - if (request.headers.has('X-Origin')) { - request.headers.set('Origin', request.headers.get('X-Origin')); - request.headers.delete('X-Origin'); - } + if (request.headers.has('X-Origin')) { + request.headers.set('Origin', request.headers.get('X-Origin')); + request.headers.delete('X-Origin'); + } - // Set PHPSESSID cookie - if (request.headers.get('PHPSESSID')) { - request.headers.set('Cookie', `PHPSESSID=${request.headers.get('PHPSESSID')}`); - } + // Set PHPSESSID cookie + if (request.headers.get('PHPSESSID')) { + request.headers.set( + 'Cookie', + `PHPSESSID=${request.headers.get('PHPSESSID')}`, + ); + } - // Set User Agent, if not exists - const useragent = request.headers.get('User-Agent'); - if (!useragent) { - request.headers.set('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:93.0) Gecko/20100101 Firefox/93.0'); - } + // Set User Agent, if not exists + const useragent = request.headers.get('User-Agent'); + if (!useragent) { + request.headers.set( + 'User-Agent', + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:93.0) Gecko/20100101 Firefox/93.0', + ); + } - // Fetch the new resource - const oResponse = await fetch(request); + // Fetch the new resource + const oResponse = await fetch(request); - // If the server returned a redirect, follow it - if ( - (oResponse.status === 302 || oResponse.status === 301) && - oResponse.headers.get('location') - ) { - // Server tried to redirect too many times - if (iteration > 5) { - return event.respondWith( - new Response('418 Too many redirects', { - status: 418 - }), - ); - } + // If the server returned a redirect, follow it + if ( + (oResponse.status === 302 || oResponse.status === 301) && + oResponse.headers.get('location') + ) { + // Server tried to redirect too many times + if (iteration > 5) { + return event.respondWith( + new Response('418 Too many redirects', { + status: 418, + }), + ); + } - // Handle and return the request for the redirected destination - return await handleRequest(request, oResponse.headers.get('location'), iteration + 1); - } + // Handle and return the request for the redirected destination + return await handleRequest( + request, + oResponse.headers.get('location'), + iteration + 1, + ); + } - // Create mutable response using the original response as init - const response = new Response(oResponse.body, oResponse); + // Create mutable response using the original response as init + const response = new Response(oResponse.body, oResponse); - // Set CORS headers - response.headers.set('Access-Control-Allow-Origin', '*'); - response.headers.set('Access-Control-Expose-Headers', '*'); + // Set CORS headers + response.headers.set('Access-Control-Allow-Origin', '*'); + response.headers.set('Access-Control-Expose-Headers', '*'); - const cookiesToSet = response.headers.get('Set-Cookie'); + const cookiesToSet = response.headers.get('Set-Cookie'); - // Transfer Set-Cookie to X-Set-Cookie - // Normally the Set-Cookie header is not accessible to fetch clients - if (cookiesToSet) { - response.headers.set('X-Set-Cookie', response.headers.get('Set-Cookie')); - } + // Transfer Set-Cookie to X-Set-Cookie + // Normally the Set-Cookie header is not accessible to fetch clients + if (cookiesToSet) { + response.headers.set('X-Set-Cookie', response.headers.get('Set-Cookie')); + } - // Set PHPSESSID cookie - if (cookiesToSet && cookiesToSet.includes('PHPSESSID') && cookiesToSet.includes(';')) { - let phpsessid = cookies.slice(cookies.search('PHPSESSID') + 10); - phpsessid = phpsessid.slice(0, phpsessid.search(';')); + // Set PHPSESSID cookie + if ( + cookiesToSet && + cookiesToSet.includes('PHPSESSID') && + cookiesToSet.includes(';') + ) { + let phpsessid = cookies.slice(cookies.search('PHPSESSID') + 10); + phpsessid = phpsessid.slice(0, phpsessid.search(';')); - response.headers.set('PHPSESSID', phpsessid); - } + response.headers.set('PHPSESSID', phpsessid); + } - // Append to/Add Vary header so browser will cache response correctly - response.headers.append('Vary', 'Origin'); + // Append to/Add Vary header so browser will cache response correctly + response.headers.append('Vary', 'Origin'); - return response; + return response; } function handleOptions(request) { - // Make sure the necessary headers are present - // for this to be a valid pre-flight request - const headers = request.headers; - let response = new Response(null, { - headers: { - Allow: 'GET, HEAD, POST, OPTIONS' - } - }); + // Make sure the necessary headers are present + // for this to be a valid pre-flight request + const headers = request.headers; + let response = new Response(null, { + headers: { + Allow: 'GET, HEAD, POST, OPTIONS', + }, + }); - if ( - headers.get('Origin') !== null && - headers.get('Access-Control-Request-Method') !== null && - headers.get('Access-Control-Request-Headers') !== null - ) { - response = new Response(null, { - headers: { - ...corsHeaders, - // Allow all future content Request headers to go back to browser - // such as Authorization (Bearer) or X-Client-Name-Version - 'Access-Control-Allow-Headers': request.headers.get('Access-Control-Request-Headers') - } - }); - } + if ( + headers.get('Origin') !== null && + headers.get('Access-Control-Request-Method') !== null && + headers.get('Access-Control-Request-Headers') !== null + ) { + response = new Response(null, { + headers: { + ...corsHeaders, + // Allow all future content Request headers to go back to browser + // such as Authorization (Bearer) or X-Client-Name-Version + 'Access-Control-Allow-Headers': request.headers.get( + 'Access-Control-Request-Headers', + ), + }, + }); + } - return response; + return response; } addEventListener('fetch', (event) => { - const request = event.request; - const url = new URL(request.url); - const destination = url.searchParams.get('destination'); + const request = event.request; + const url = new URL(request.url); + const destination = url.searchParams.get('destination'); - console.log(`HTTP ${request.method} - ${request.url}`); + console.log(`HTTP ${request.method} - ${request.url}`); - let response = new Response('404 Not Found', { - status: 404 - }); + let response = new Response('404 Not Found', { + status: 404, + }); - if (request.method === 'OPTIONS') { - // Handle CORS preflight requests - response = handleOptions(request); - } else if (!destination) { - response = new Response('200 OK', { - status: 200, - headers: { - Allow: 'GET, HEAD, POST, OPTIONS', - 'Access-Control-Allow-Origin': '*', - } - }); - } else if ( - request.method === 'GET' || - request.method === 'HEAD' || - request.method === 'POST' - ) { - // Handle request - response = handleRequest(request, destination); - } + if (request.method === 'OPTIONS') { + // Handle CORS preflight requests + response = handleOptions(request); + } else if (!destination) { + response = new Response('200 OK', { + status: 200, + headers: { + Allow: 'GET, HEAD, POST, OPTIONS', + 'Access-Control-Allow-Origin': '*', + }, + }); + } else if ( + request.method === 'GET' || + request.method === 'HEAD' || + request.method === 'POST' + ) { + // Handle request + response = handleRequest(request, destination); + } - event.respondWith(response); -}); \ No newline at end of file + event.respondWith(response); +}); From f9f46cde0b2fd785554ded14bd3441c81f2119c6 Mon Sep 17 00:00:00 2001 From: Jelle van Snik Date: Sun, 8 Jan 2023 20:26:22 +0100 Subject: [PATCH 5/6] make redirection with a body work --- src/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.js b/src/main.js index b9c8acc..0b4b21c 100644 --- a/src/main.js +++ b/src/main.js @@ -50,7 +50,7 @@ async function handleRequest(oRequest, destination, iteration = 0) { } // Fetch the new resource - const oResponse = await fetch(request); + const oResponse = await fetch(request.clone()); // If the server returned a redirect, follow it if ( From cc7c09d1991678e7ff43a068a202373ce0307f8d Mon Sep 17 00:00:00 2001 From: Jelle van Snik Date: Sun, 8 Jan 2023 20:27:03 +0100 Subject: [PATCH 6/6] fix event error --- src/main.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/main.js b/src/main.js index 0b4b21c..98888c0 100644 --- a/src/main.js +++ b/src/main.js @@ -59,11 +59,9 @@ async function handleRequest(oRequest, destination, iteration = 0) { ) { // Server tried to redirect too many times if (iteration > 5) { - return event.respondWith( - new Response('418 Too many redirects', { - status: 418, - }), - ); + return new Response('418 Too many redirects', { + status: 418, + }); } // Handle and return the request for the redirected destination