Giter VIP home page Giter VIP logo

Comments (8)

Darkseal avatar Darkseal commented on August 26, 2024

Hi there,
first of all, thank you for using my script 👍

As for your specific request, are you experiencing issue with the content replacement feature or with those files not being proxied as a whole?

As for the content replacement feature, CORSflare will only perform it on text/html files, unless explicitly instructed otherwise, so maybe you just need to add the CSV and TSV content types to the CORSflare's replacement_content_types configuration constant (see here):

const replacement_content_types = ['text/html', 'text/csv', 'text/tsv', 'text/tab-separated-values'];

If the issue is about those files not being proxied, please send me your CORSflare.js file so that I can test it out and see what's going on: be sure to obfuscate your content IDs and/or tokens, just leave the root URL so that I can understand how to properly reproduce it with my own Google Sheet files.

from corsflare.

katriellucas avatar katriellucas commented on August 26, 2024

The question is specific about the content not being proxified, I'm not using other features and I don't mind anyone seeing the data, it's just mock-up. 😃

Here is the code, I forgot to mention that I was using papaparse to parse the csv 😅, but I tested if it worked on other platforms just to be sure, it was flawless on dropbox and netlify, so, I guessed it was Google problem.

<html>
<head>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/PapaParse/5.3.0/papaparse.min.js"></script>
</head>
<body>
  <script>
    Papa.parse("https://square-lake-54f9.gomerk.workers.dev/spreadsheets/d/e/2PACX-1vSe-UCupCM9CIkK-CP01nOT7yyW5kC0mAGnqcDoEgaiWgKNsSytZS4js2flxi_ATogt8dthIpV46NiT/pub?gid=1814306385&single=true&output=csv", {
      download: true,
      complete: function(results) {
      console.log(results);
    }
  });
  </script>
</body>
</html>

And the CORSflare.js code:

// ----------------------------------------------------------------------------------
// CORSflare - v1.0.4
// ref.: https://github.com/Darkseal/CORSflare
// A lightweight JavaScript CORS Reverse Proxy designed to run in a Cloudflare Worker
// ----------------------------------------------------------------------------------

// ----------------------------------------------------------------------------------
// CONFIGURATION SETTINGS
// ----------------------------------------------------------------------------------

// The hostname of the upstream website to proxy(example: `www.google.com`).
const upstream = 'docs.google.com';


// ** This is the public gsheet - csv ** //
//https://docs.google.com/spreadsheets/d/e/2PACX-1vSe-UCupCM9CIkK-CP01nOT7yyW5kC0mAGnqcDoEgaiWgKNsSytZS4js2flxi_ATogt8dthIpV46NiT/pub?gid=1814306385&single=true&output=csv


// The hostname of the upstream website to proxy for requests coming from mobile devices(example: `www.google.com`).
// if the upstream website doesn't have a dedicated hostname for mobile devices, you can set it to NULL.
const upstream_mobile = null;

// Custom pathname for the upstream website ('/' will work for most scenarios)
const upstream_path = '/';

// An array of countries and regions that won't be able to use the proxy.
const blocked_regions = ['CN', 'KP', 'SY', 'PK', 'CU'];

// An array of IP addresses that won't be able to use the proxy.
const blocked_ip_addresses = ['0.0.0.0', '127.0.0.1'];

// Set this value to TRUE to fetch the upstream website using HTTPS, FALSE to use HTTP.
// If the upstream website doesn't support HTTPS, this must be set to FALSE; also, if the proxy is HTTPS,
// you'll need to enable the replacement_rules rule to HTTPS proxy an HTTP-only website (see below).
const https = true;

// Set this value to TRUE to forcefully apply the "SameSite=None" and "Secure" directives to all cookies generated by the upstream.
// If you plan to put this proxy within a <iframe> and allow users to POST FORM data, you might need this option to make auth cookies work.
// ref.: https://sites.google.com/a/chromium.org/dev/updates/same-site/faq
// ref.: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite
// NOTE: This is an experimental feature and could prevent some cookies from being generated due to a known bug of the Fetch API
// affecting the "Set-Cookie" response headers: use it at your own risk.
const set_cookie_samesite_none = false;

// an array of HTTP Response Headers to add (or to update, in case they're already present in the upstream response)
const http_response_headers_set = {
    // use these headers to bypass DENY and SAMEORIGIN policies for IFRAME, OBJECT, EMBED and so on for most browsers.
    // NOTE: be sure to replace "https://www.example.com" with the domain of the HTML page containing the IFRAME.
    // ref.: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
    // ref.: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
    'X-Frame-Options': 'ALLOW FROM https://www.example.com', // IE
    'Content-Security-Policy': "frame-ancestors 'self' https://www.example.com;", // Chrome, Firefox, etc.

    // use this header to bypass the same-origin policy for XMLHttpRequest, Fetch API and so on
    // ref.: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
    'Access-Control-Allow-Origin': '*',

    // use this header to accept (and respond to) preflight requests when the request's credentials mode is set to 'include'
    // ref.: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials
    'Access-Control-Allow-Credentials': true,

    // use this header to override the Cache-Control settings of the upstream pages. Allowed values include:
    // 'must-revalidate', 'no-cache', 'no-store', 'no-transform', 'public', 'private', 
    // 'proxy-revalidate', 'max-age=<seconds>', 's-maxage=<seconds>'.
    // ref.: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
    // 'Cache-Control': 'no-cache'
};

// an array of HTTP Response Headers to delete (if present in the upstream response)
const http_response_headers_delete = [
    'Content-Security-Policy-Report-Only',
    'Clear-Site-Data'
];

// ----------------------------------------------------------------------------------
// TEXT REPLACEMENT RULES
// ----------------------------------------------------------------------------------
// The replacement_rules array can be used to configure the text replacement rules
// that will be applied by the proxy before serving any text/html resource back to the user.
// The common usage of such rules is to "fix" non-standard internal URLs and/or local paths
// within the upstream's HTML pages (css, js, internal links, custom fonts, and so on) and force them 
// to pass to the proxy; however, they can also be used to alter the response content in various ways
// (change a logo, modify the page title, add a custom css/js, and so on).

// Each rule must be defined in the following way:

// '<source_string>' : '<replacement_string>'

// The following dynamic placeholder can be used within the source and replacement strings:

// {upstream_hostname}  : will be replaced with the upstream's hostname
// {proxy_hostname}     : will be replaced with this proxy's hostname

// HINT: Rules are processed from top to bottom: put the most specific rules before the generic ones.

const replacement_rules = {

    // enable this rule only if you need to HTTPS proxy an HTTP-only website
    'http://{upstream_hostname}/': 'https://{proxy_hostname}/',

    // this rule should be always enabled (replaces the upstream hostname for internal links, CSS, JS, and so on)
    '{upstream_hostname}': '{proxy_hostname}',

}

// the replacement_rules will be only applied to the returned content 
// with the following content types specified by the replacement_content_types array.
const replacement_content_types = ['text/csv, text/html'];

// Set this value to TRUE to allow RegEx syntax in replacement rules (see URLs below for details):
// - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
// - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Cheatsheet
// NOTE: if RegEx syntax is enabled, RegEx special chars in search patterns will have to be escaped
// using a double back slash (\\) accordingly.
const replacement_use_regex = true;



// ----------------------------------------------------------------------------------
// MAIN CODE
// ----------------------------------------------------------------------------------

var regexp_upstreamHostname = (replacement_use_regex)
    ? new RegExp('{upstream_hostname}', 'g')
    : null;
var regexp_proxyHostname = (replacement_use_regex)
    ? new RegExp('{proxy_hostname}', 'g')
    : null;

addEventListener('fetch', event => {
    event.respondWith(fetchAndApply(event.request));
})

async function fetchAndApply(request) {
    var r = request.headers.get('cf-ipcountry');
    const region = (r) ? r.toUpperCase() : null;
    const ip_address = request.headers.get('cf-connecting-ip');
    const user_agent = request.headers.get('user-agent');

    let response = null;
    let url = new URL(request.url);
    let url_hostname = url.hostname;

    if (https == true) {
        url.protocol = 'https:';
    } else {
        url.protocol = 'http:';
    }

    if (upstream_mobile && await is_mobile_user_agent(user_agent)) {
        var upstream_domain = upstream_mobile;
    } else {
        var upstream_domain = upstream;
    }

    url.host = upstream_domain;
    if (url.pathname == '/') {
        url.pathname = upstream_path;
    } else {
        url.pathname = upstream_path + url.pathname;
    }

    if (blocked_regions.includes(region) || blocked_ip_addresses.includes(ip_address)) {
        response = new Response('Access denied', {
            status: 403
        });
    } else {
        let method = request.method;
        let request_headers = request.headers;
        let new_request_headers = new Headers(request_headers);
        let request_content_type = new_request_headers.get('content-type');

        new_request_headers.set('Host', upstream_domain);
        new_request_headers.set('Origin', upstream_domain);
        new_request_headers.set('Referer', url.protocol + '//' + url_hostname);

        var params = {
            method: method,
            headers: new_request_headers,
            // this is required to properly handle standard HTTP redirects, as we need to alter the "location" header:
            // the default "follow" value would auto-resolve them with the upstream URL, which is not what we want.
            redirect: 'manual'
        }

        // if the request is supposed to contain Form Data, populates the request body accordingly
        if (method.toUpperCase() === "POST" && request_content_type) {
            let request_content_type_toLower = request_content_type.toLowerCase();
            if (request_content_type_toLower.includes("application/x-www-form-urlencoded")
                || request_content_type_toLower.includes("multipart/form-data")
                || request_content_type_toLower.includes("application/json")
            ) {
                let reqText = await request.text(); // TODO: this won't work for multipart/form-data
                if (reqText) {
                    params.body = reqText;
                }
            }
        }

        let original_response = await fetch(url.href, params);

        connection_upgrade = new_request_headers.get("Upgrade");
        if (connection_upgrade && connection_upgrade.toLowerCase() == "websocket") {
            return original_response;
        }

        let original_response_clone = original_response.clone();
        let response_headers = original_response_clone.headers;
        let response_status = original_response_clone.status;
        let original_text = null;
        let new_response_headers = new Headers(response_headers);
        let new_response_status = response_status;

        if (http_response_headers_set) {
            for (let k in http_response_headers_set) {
                var v = http_response_headers_set[k];
                new_response_headers.set(k, v);
            }
        }

        if (http_response_headers_delete) {
            for (let k of http_response_headers_delete) {
                new_response_headers.delete(k);
            }
        }

        // Patch "x-pjax-url" header to handle pushState ajax redirects
        if (new_response_headers.get("x-pjax-url")) {
            new_response_headers.set("x-pjax-url", new_response_headers.get("x-pjax-url")
                .replace(url.protocol + "//", "https://")
                .replace(upstream_domain, url_hostname));
        }

        // Patch "location" header to handle standard 301/302/303/307/308 HTTP redirects
        if (new_response_headers.get("location")) {
            new_response_headers.set("location", new_response_headers.get("location")
                .replace(url.protocol + "//", "https://")
                .replace(upstream_domain, url_hostname));
        }

        // Patch "set-cookie" headers by forcefully apply "SameSite=None" and "Secure" directives to allow cross-domain usage
        // (if "set_cookie_samesite_none" is set to TRUE: see that configuration option's comment block for details and references)
        if (set_cookie_samesite_none && new_response_headers.has("set-cookie")) {
            // NOTE: unfortunately the Fetch API Headers object doesn't support multiple Set-Cookie headers due to a bug in Fetch API's
            // "Headers" interface, as they are merged into a single comma-separated string (which is incompatible with most browsers).
            // ref.: https://stackoverflow.com/questions/63204093/how-to-get-set-multiple-set-cookie-response-headers-using-fetch-api
            // For that very reason, we can only support the * first * set-cookie header here.
            var firstCookie = new_response_headers.get("set-cookie").split(',').shift();
            new_response_headers.set("set-cookie", firstCookie
                .split("SameSite=Lax; Secure").join("")
                .split("SameSite=Lax").join("")
                .split("SameSite=Strict; Secure").join("")
                .split("SameSite=Strict").join("")
                .split("SameSite=None; Secure").join("")
                .split("SameSite=None").join("")
                .replace(/^;+$/g, '')
                + "; SameSite=None; Secure");
        }

        let response_content_type = new_response_headers.get('content-type');
        if (response_content_type
            && replacement_content_types.some(v => response_content_type.toLowerCase().includes(v))) {
            original_text = await replace_response_text(original_response_clone, upstream_domain, url_hostname);
        } else {
            original_text = original_response_clone.body;
        }

        response = new Response(original_text, {
            status: new_response_status,
            headers: new_response_headers
        })
    }
    return response;
}

async function replace_response_text(response, upstream_domain, host_name) {
    let text = await response.text()
    if (replacement_rules) {
        for (let k in replacement_rules) {
            var v = replacement_rules[k];

            if (replacement_use_regex) {
                k = k.replace(regexp_upstreamHostname, upstream_domain);
                k = k.replace(regexp_proxyHostname, host_name);
                v = v.replace(regexp_upstreamHostname, upstream_domain);
                v = v.replace(regexp_proxyHostname, host_name);
                text = text.replace(new RegExp(k, 'g'), v);
            }
            else {
                k = k.split('{upstream_hostname}').join(upstream_domain);
                k = k.split('{proxy_hostname}').join(host_name);
                v = v.split('{upstream_hostname}').join(upstream_domain);
                v = v.split('{proxy_hostname}').join(host_name);
                text = text.split(k).join(v);
            }
        }
    }
    return text;
}

async function is_mobile_user_agent(user_agent_info) {
    var agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];
    for (var v = 0; v < agents.length; v++) {
        if (user_agent_info.indexOf(agents[v]) > 0) {
            return true;
        }
    }
    return false;
}

from corsflare.

Darkseal avatar Darkseal commented on August 26, 2024

Hello there,

The issue with Google Sheets is due to the fact that the URL you're calling doesn't directly serve the CSV file: instead, it issues a 307 redirect to another page (located on a VARIABLE EXTERNAL domain, such as doc-0s-94-sheets.googleusercontent.com) which not only also has a CORS policy applied, but does only accept a single request before expiring due to a OTP unique string placed in the URL itself: to put it in other words, it's a request/redirect nightmare explicitly designed to prevent exploits such as the one I'm using with CORSflare.

That said, I still managed to work around it (!) with a little tweak of the CORSflare source code: in a nutshell, the proxy now detects the redirects to "googleusercontent.com" URLs and - if that's the case - sends the redirect back to the client adding a dedicated GET parameter that - if present - will be used to setup a new upstream on-the-fly, thus circumventing what Google is trying to avoid. I'm not sure if the explanation is clear enough, but I tested it and it works :) (until they change their existing request/redirect security behaviour to prevent this hack).

To use the new version and (hopefully) fix your issue, just get CORSflare v1.0.5 - which I've just released to GitHub - and change the new upstream_allow_override configuration setting to true, so that the GDrive patch will automatically kick in.

SECURITY WARNING: Be wary that activating this setting might lead to some unexpected consequencies (in a nutshell, third parties will be able to change their default upstream by manually adding the upstream_get_parameter to their HTTP requests), but it shouldn't be a problem in most scenarios; maybe consider changing the upstream_get_parameter to a random string to reduce such risk.

from corsflare.

katriellucas avatar katriellucas commented on August 26, 2024

Sadly, it doesn't seem to be working for me (though, I'm surely doing something wrong), if I understood right:

  • Download and use the lastest Corsflare
  • Change the "upstream_get_parameter" to true
  • Put "upstream" to docs.google.com
  • Use same html that I was using before.

Wields a 404 error:

image

I will post my Corsflare again:

// ----------------------------------------------------------------------------------
// CORSflare - v1.0.5
// ref.: https://github.com/Darkseal/CORSflare
// A lightweight JavaScript CORS Reverse Proxy designed to run in a Cloudflare Worker
// ----------------------------------------------------------------------------------



// ----------------------------------------------------------------------------------
// CONFIGURATION SETTINGS
// ----------------------------------------------------------------------------------

// The hostname of the upstream website to proxy(example: `www.google.com`).
const upstream = 'docs.google.com';

// The hostname of the upstream website to proxy for requests coming from mobile devices(example: `www.google.com`).
// if the upstream website doesn't have a dedicated hostname for mobile devices, you can set it to NULL.
const upstream_mobile = null;

// Custom pathname for the upstream website ('/' will work for most scenarios)
const upstream_path = '/';

// Set it to TRUE to allow the default upstream to be overridden with a customizable GET parameter (see `upstream_get_parameter` below), FALSE to prevent that.
// WARNING: this feature will allow third-parties to replace this proxy's default upstream with an upstream of their choice: activate it only if you know 
// what you're doing (see ref. below): also, it could be wise to change the default `upstream_get_parameter` with a unique string to reduce the risk of hijacks.
// ref.: https://github.com/Darkseal/CORSflare/issues/1
const upstream_allow_override = true;

// The GET parameter that can be used to override the default upstream if `upstream_allow_override` is set to TRUE.
const upstream_get_parameter = 'CORSflare_upstream';

// An array of countries and regions that won't be able to use the proxy.
const blocked_regions = ['CN', 'KP', 'SY', 'PK', 'CU'];

// An array of IP addresses that won't be able to use the proxy.
const blocked_ip_addresses = ['0.0.0.0', '127.0.0.1'];

// Set this value to TRUE to fetch the upstream website using HTTPS, FALSE to use HTTP.
// If the upstream website doesn't support HTTPS, this must be set to FALSE; also, if the proxy is HTTPS,
// you'll need to enable the replacement_rules rule to HTTPS proxy an HTTP-only website (see below).
const https = true;

// Set this value to TRUE to forcefully apply the "SameSite=None" and "Secure" directives to all cookies generated by the upstream.
// If you plan to put this proxy within a <iframe> and allow users to POST FORM data, you might need this option to make auth cookies work.
// ref.: https://sites.google.com/a/chromium.org/dev/updates/same-site/faq
// ref.: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite
// NOTE: This is an experimental feature and could prevent some cookies from being generated due to a known bug of the Fetch API
// affecting the "Set-Cookie" response headers: use it at your own risk.
const set_cookie_samesite_none = false;

// an array of HTTP Response Headers to add (or to update, in case they're already present in the upstream response)
const http_response_headers_set = {
    // use these headers to bypass DENY and SAMEORIGIN policies for IFRAME, OBJECT, EMBED and so on for most browsers.
    // NOTE: be sure to replace "https://www.example.com" with the domain of the HTML page containing the IFRAME.
    // ref.: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
    // ref.: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
    'X-Frame-Options': 'ALLOW FROM https://www.example.com', // IE
    'Content-Security-Policy': "frame-ancestors 'self' https://www.example.com;", // Chrome, Firefox, etc.

    // use this header to bypass the same-origin policy for XMLHttpRequest, Fetch API and so on
    // ref.: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
    'Access-Control-Allow-Origin': '*',

    // use this header to accept (and respond to) preflight requests when the request's credentials mode is set to 'include'
    // ref.: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials
    'Access-Control-Allow-Credentials': true,

    // use this header to override the Cache-Control settings of the upstream pages. Allowed values include:
    // 'must-revalidate', 'no-cache', 'no-store', 'no-transform', 'public', 'private', 
    // 'proxy-revalidate', 'max-age=<seconds>', 's-maxage=<seconds>'.
    // ref.: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
    // 'Cache-Control': 'no-cache'
};

// an array of HTTP Response Headers to delete (if present in the upstream response)
const http_response_headers_delete = [
    'Content-Security-Policy-Report-Only',
    'Clear-Site-Data'
];

// ----------------------------------------------------------------------------------
// TEXT REPLACEMENT RULES
// ----------------------------------------------------------------------------------
// The replacement_rules array can be used to configure the text replacement rules
// that will be applied by the proxy before serving any text/html resource back to the user.
// The common usage of such rules is to "fix" non-standard internal URLs and/or local paths
// within the upstream's HTML pages (css, js, internal links, custom fonts, and so on) and force them 
// to pass to the proxy; however, they can also be used to alter the response content in various ways
// (change a logo, modify the page title, add a custom css/js, and so on).

// Each rule must be defined in the following way:

// '<source_string>' : '<replacement_string>'

// The following dynamic placeholder can be used within the source and replacement strings:

// {upstream_hostname}  : will be replaced with the upstream's hostname
// {proxy_hostname}     : will be replaced with this proxy's hostname

// HINT: Rules are processed from top to bottom: put the most specific rules before the generic ones.

const replacement_rules = {

    // enable this rule only if you need to HTTPS proxy an HTTP-only website
    'http://{upstream_hostname}/': 'https://{proxy_hostname}/',

    // this rule should be always enabled (replaces the upstream hostname for internal links, CSS, JS, and so on)
    '{upstream_hostname}': '{proxy_hostname}',

}

// the replacement_rules will be only applied to the returned content 
// with the following content types specified by the replacement_content_types array.
const replacement_content_types = ['text/csv, text/html'];

// Set this value to TRUE to allow RegEx syntax in replacement rules (see URLs below for details):
// - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
// - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Cheatsheet
// NOTE: if RegEx syntax is enabled, RegEx special chars in search patterns will have to be escaped
// using a double back slash (\\) accordingly.
const replacement_use_regex = true;



// ----------------------------------------------------------------------------------
// MAIN CODE
// ----------------------------------------------------------------------------------

var regexp_upstreamHostname = (replacement_use_regex)
    ? new RegExp('{upstream_hostname}', 'g')
    : null;
var regexp_proxyHostname = (replacement_use_regex)
    ? new RegExp('{proxy_hostname}', 'g')
    : null;

addEventListener('fetch', event => {
    event.respondWith(fetchAndApply(event.request));
})

async function fetchAndApply(request) {
    var r = request.headers.get('cf-ipcountry');
    const region = (r) ? r.toUpperCase() : null;
    const ip_address = request.headers.get('cf-connecting-ip');
    const user_agent = request.headers.get('user-agent');

    let response = null;
    let url = new URL(request.url);
    let url_hostname = url.hostname;
    let upstream_GET = (upstream_allow_override)
        ? url.searchParams.get(upstream_get_parameter)
        : null;

    if (https == true) {
        url.protocol = 'https:';
    } else {
        url.protocol = 'http:';
    }

    var upstream_domain = null;
    if (upstream_GET) {
        upstream_domain = upstream_GET;
    }
    if (upstream_mobile && await is_mobile_user_agent(user_agent)) {
        upstream_domain = upstream_mobile;
    } else {
        upstream_domain = upstream;
    }

    url.host = upstream_domain;
    if (url.pathname == '/') {
        url.pathname = upstream_path;
    } else {
        url.pathname = upstream_path + url.pathname;
    }

    if (blocked_regions.includes(region) || blocked_ip_addresses.includes(ip_address)) {
        response = new Response('Access denied', {
            status: 403
        });
    } else {
        let method = request.method;
        let request_headers = request.headers;
        let new_request_headers = new Headers(request_headers);
        let request_content_type = new_request_headers.get('content-type');

        new_request_headers.set('Host', upstream_domain);
        new_request_headers.set('Origin', upstream_domain);
        new_request_headers.set('Referer', url.protocol + '//' + url_hostname);

        var params = {
            method: method,
            headers: new_request_headers,
            // this is required to properly handle standard HTTP redirects, as we need to alter the "location" header:
            // the default "follow" value would auto-resolve them with the upstream URL, which is not what we want.
            redirect: 'manual'
        }

        // if the request is supposed to contain Form Data, populates the request body accordingly
        if (method.toUpperCase() === "POST" && request_content_type) {
            let request_content_type_toLower = request_content_type.toLowerCase();
            if (request_content_type_toLower.includes("application/x-www-form-urlencoded")
                || request_content_type_toLower.includes("multipart/form-data")
                || request_content_type_toLower.includes("application/json")
            ) {
                let reqText = await request.text(); // TODO: this won't work for multipart/form-data
                if (reqText) {
                    params.body = reqText;
                }
            }
        }

        let original_response = await fetch(url.href, params);

        connection_upgrade = new_request_headers.get("Upgrade");
        if (connection_upgrade && connection_upgrade.toLowerCase() == "websocket") {
            return original_response;
        }

        let original_response_clone = original_response.clone();
        let response_headers = original_response_clone.headers;
        let response_status = original_response_clone.status;
        let original_text = null;
        let new_response_headers = new Headers(response_headers);
        let new_response_status = response_status;

        if (http_response_headers_set) {
            for (let k in http_response_headers_set) {
                var v = http_response_headers_set[k];
                new_response_headers.set(k, v);
            }
        }

        if (http_response_headers_delete) {
            for (let k of http_response_headers_delete) {
                new_response_headers.delete(k);
            }
        }

        // Patch "x-pjax-url" header to handle pushState ajax redirects
        if (new_response_headers.get("x-pjax-url")) {
            new_response_headers.set("x-pjax-url", new_response_headers.get("x-pjax-url")
                .replace(url.protocol + "//", "https://")
                .replace(upstream_domain, url_hostname));
        }

        // Patch "location" header to handle standard 301/302/303/307/308 HTTP redirects
        if (new_response_headers.get("location")) {
            var location = new_response_headers.get("location");

            // specific behaviour for GDrive-based redirects: see https://github.com/Darkseal/CORSflare/issues/1 for details.
            if (upstream_allow_override && location.includes("googleusercontent.com")) {
                var new_upstream = location.substring(8, location.indexOf("/", 8));
                location = location + "&" + upstream_get_parameter + "=" + new_upstream;
                new_response_headers.set("location", location
                    .replace(url.protocol + "//", "https://")
                    .replace(new_upstream, url_hostname));
            }
            else {
                new_response_headers.set("location", location
                    .replace(url.protocol + "//", "https://")
                    .replace(upstream_domain, url_hostname));
            }
        }

        // Patch "set-cookie" headers by forcefully apply "SameSite=None" and "Secure" directives to allow cross-domain usage
        // (if "set_cookie_samesite_none" is set to TRUE: see that configuration option's comment block for details and references)
        if (set_cookie_samesite_none && new_response_headers.has("set-cookie")) {
            // NOTE: unfortunately the Fetch API Headers object doesn't support multiple Set-Cookie headers due to a bug in Fetch API's
            // "Headers" interface, as they are merged into a single comma-separated string (which is incompatible with most browsers).
            // ref.: https://stackoverflow.com/questions/63204093/how-to-get-set-multiple-set-cookie-response-headers-using-fetch-api
            // For that very reason, we can only support the * first * set-cookie header here.
            var firstCookie = new_response_headers.get("set-cookie").split(',').shift();
            new_response_headers.set("set-cookie", firstCookie
                .split("SameSite=Lax; Secure").join("")
                .split("SameSite=Lax").join("")
                .split("SameSite=Strict; Secure").join("")
                .split("SameSite=Strict").join("")
                .split("SameSite=None; Secure").join("")
                .split("SameSite=None").join("")
                .replace(/^;+$/g, '')
                + "; SameSite=None; Secure");
        }

        let response_content_type = new_response_headers.get('content-type');
        if (response_content_type
            && replacement_content_types.some(v => response_content_type.toLowerCase().includes(v))) {
            original_text = await replace_response_text(original_response_clone, upstream_domain, url_hostname);
        } else {
            original_text = original_response_clone.body;
        }

        response = new Response(original_text, {
            status: new_response_status,
            headers: new_response_headers
        })
    }
    return response;
}

async function replace_response_text(response, upstream_domain, host_name) {
    let text = await response.text()
    if (replacement_rules) {
        for (let k in replacement_rules) {
            var v = replacement_rules[k];

            if (replacement_use_regex) {
                k = k.replace(regexp_upstreamHostname, upstream_domain);
                k = k.replace(regexp_proxyHostname, host_name);
                v = v.replace(regexp_upstreamHostname, upstream_domain);
                v = v.replace(regexp_proxyHostname, host_name);
                text = text.replace(new RegExp(k, 'g'), v);
            }
            else {
                k = k.split('{upstream_hostname}').join(upstream_domain);
                k = k.split('{proxy_hostname}').join(host_name);
                v = v.split('{upstream_hostname}').join(upstream_domain);
                v = v.split('{proxy_hostname}').join(host_name);
                text = text.split(k).join(v);
            }
        }
    }
    return text;
}

async function is_mobile_user_agent(user_agent_info) {
    var agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];
    for (var v = 0; v < agents.length; v++) {
        if (user_agent_info.indexOf(agents[v]) > 0) {
            return true;
        }
    }
    return false;
}

from corsflare.

Darkseal avatar Darkseal commented on August 26, 2024

That's very strange: your file and mine are basically identical... and mine works! You can check it out at the following URL:

https://googlesheets.ryadel.workers.dev/

Do you manage to make it work using my proxy by any chance?

from corsflare.

katriellucas avatar katriellucas commented on August 26, 2024

Wow, it works with yours... that is indeed very strange. I'm on Brazil, maybe it's a local/country thing?

--- EDIT ---

Could you send me a copy of your code?

from corsflare.

Darkseal avatar Darkseal commented on August 26, 2024

OK, I think I found the bug: try to download CORSflare from GitHub now, then change upstream to docs.google.com and upstream_allow_override to true.

IMPORTANT: DO NOT change upstream_get_parameter to true as you were saying in one of your replies, cause this won't work: leave upstream_get_parameter as it is for now.

from corsflare.

katriellucas avatar katriellucas commented on August 26, 2024

Thank you so much!!! And I'm sorry for causing so much trouble, I see where my error was. 😅
It works perfectly now. 👍

from corsflare.

Related Issues (9)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.