Fetch from HTTP site by Chrome Extension

I would like to know how to fetch from a HTTP site (not HTTPS) from Chrome Extension (v3 manifest).

Currently I’m using a Tampermonkey userscript, which works perfectly fine so far. And for some reason, I want to migrate the script to my own Chrome extension.
The userscript is below:

    function requestServer(){
        return new Promise((resolve, reject) => {
            GM.xmlHttpRequest({
                method: "POST",
                url: 'http://221.xxx.yyy.zz/login',
                headers: {'Content-Type':'application/x-www-form-urlencoded;charset=utf-8'},
                data: 'username=abc_ENQUIRY&password=123456',
                onload: e => resolve(e.response),
                onerror: reject,
                ontimeout: reject,
            });
        });
    }

As you can see the userscript is currently using xmlHttprequest, I first tried to rewrite the request back to the original xmlHttprequest syntax, instead of using tampermonkey api
The following is how it’s write:

function requestServer(){
    return new Promise((resolve, reject) => {
        var xhRequest = new XMLHttpRequest();
        xhRequest.open('POST', 'http://221.xxx.yyy.zz/login', true);
        xhRequest.onload = function () {
            if (this.status >= 200 && this.status < 300) {
                resolve(xhRequest);
            } else {
                reject(xhRequest);
            }
        };
        xhRequest.onerror = function () {
            reject(xhRequest);
        };
        xhRequest.setRequestHeader('Content-Type','application/x-www-form-urlencoded;charset=utf-8');
        xhRequest.send('username=abc_ENQUIRY&password=123456');
    });
}

But the following error is shown:

Mixed Content: The page at 'https://w3.abcdef.com/' was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint 'http://221.xxx.yyy.zz/login'. This request has been blocked; the content must be served over HTTPS.

I searched the solution of this is to move the request to background script (Ref: https://medium.com/@chantlong/performing-http-https-requests-in-a-chrome-extension-ccba3367d2f8, sending request from https to http from chrome extension). And moving it to background means I need to change from old xmlhttprequest to the newer fetch.

And now it’s how the function moved to background/service-worker script:

function requestServer(){
    fetch('http://221.xxx.yyy.zz/login', {
        method: 'POST', 
        headers: {'Content-Type': 'application/x-www-form-urlencoded'},
        body: 'username=abc_ENQUIRY&password=123456'
    })
    .then(console.log("SUCCESS"))
    .catch(console.log("FAIL???"));
}

And the following error is shown:

Uncaught (in promise) TypeError: Failed to fetch

In fact, the xmlrequest is simulating the login form submission from the website http://221.xxx.yyy.zz/login. The following is the “Copy as fetch” of the xml request if I really using the login page itself:

fetch("http://221.xxx.yyy.zz/login", {
  "headers": {
    "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
    "accept-language": "en,zh-HK;q=0.9,zh-TW;q=0.8,zh;q=0.7,en-US;q=0.6",
    "cache-control": "max-age=0",
    "content-type": "application/x-www-form-urlencoded",
    "upgrade-insecure-requests": "1"
  },
  "referrer": "http://221.xxx.yyy.zz/login",
  "referrerPolicy": "strict-origin-when-cross-origin",
  "body": "username=abc_ENQUIRY&password=123456",
  "method": "POST",
  "mode": "cors",
  "credentials": "include"
});

And this is the “Copy as fetch” of the xml request if I use tampermonkey script:

fetch("http://221.xxx.yyy.zz/login", {
  "headers": {
    "accept": "*/*",
    "accept-language": "en,zh-HK;q=0.9,zh-TW;q=0.8,zh;q=0.7,en-US;q=0.6",
    "content-type": "application/x-www-form-urlencoded;charset=UTF-8"
  },
  "referrerPolicy": "strict-origin-when-cross-origin",
  "body": "username=abc_ENQUIRY&password=123456",
  "method": "POST",
  "mode": "cors",
  "credentials": "include"
});

I have sort of imitates the fetch request, by putting different headers, such as CORS mode, putting referrer and referrer policy, but seems no luck. Btw, I found that no matter what URL I put on the referrer, the referrer is empty on request’s request header in developer tools

I’m thinking to using Offscreen document to imitate the fetch request like the tampermonkey does (it seems Tampermonkey is still using Manifest v2, as the xmlrequest is called by background.html, but in this way, the request data and its response need to be sent across a few script, from content script to service worker script to offscreen document script, which seems quite complicated. So I personally not really want to try/use such method.

So I would like to know how to fetch a HTTP site from an extension (in a simply way), it can be directly from a content script or from a service worker script. Appreciate your help. Thank you so much:)