0
votes

I am trying to add a custom header on all network requests going from application and I am trying to do this via service worker fetch. The content of header needs to come from app(client), so I need to wait for a response from client before responding to any event. Below is my attempt to achieve this

Here is my fetch listener code

function send_message_to_client(client, msg){
    return new Promise(function(resolve, reject){
        var msg_chan = new MessageChannel();

        msg_chan.port1.onmessage = function(event){
            if(event.data.error){
                reject(event.data.error);
            }else{
                resolve(event.data);
            }
        };

        client.postMessage("SW Says: '"+msg+"'", [msg_chan.port2]);
    });
}

self.addEventListener('fetch', function (event) {

    event.waitUntil(async function () {
        const client = await clients.get(event.clientId);
        send_message_to_client(client, "Pass Transaction Details")
            .then(function (m) {
                var req = new Request(event.request.url, {
                    method: event.request.method,
                    headers: event.request.headers,
                    mode: 'same-origin',
                    credentials: event.request.credentials,
                    redirect: 'manual'
                });

                var res_obj = JSON.parse(m);
                req.headers.append('MY_CUSTOM_HEADER', res_obj.hdr_val);
                return event.respondWith(fetch(req));

            })
            .catch(function (error) {
                console.log("Error after event.respondWith call");
                console.log(error);
            });

    }());
});

and here is how I registered this worker and its message listener

navigator.serviceWorker.register('/my-sw.js', {scope: '/'})
    .then(function(reg) {
        navigator.serviceWorker.onmessage = function (e) {
            var msg_reply = {
                "message" : "Replying to SW request",
            };
            msg_reply.hdr_val = sessionStorage.getItem('__data_val');
            console.log("Replying with "+ JSON.stringify(msg_reply));
            e.ports[0].postMessage(JSON.stringify(msg_reply));

        };
    }).catch(function(error) {
    // registration failed
    console.log('Registration failed with ' + error);
});

but apparently its shooting 2 requests, 1 original request and 1 with modified headers.

Any idea what am I doing wrong ? I am a newbie in javascript so pardon me if there is some stupid mistake.

From service worker debug console, I found that its going in catch block right after event.respondWith() call, so something wrong there probably ?

1

1 Answers

0
votes

You must call FetchEvent.respondWith() synchronously in your fetch handler. In your code you are calling waitUntil() synchronously, but calling respondWith() later. By not calling respondWith() before returning from the fetch handler you are telling the browser to continue on with its normal networking path without interception.

I think you want something like this:

self.addEventListener('fetch', function (event) {
    // call respondWith() here
    event.respondWith(async function () {
        const client = await clients.get(event.clientId);
        send_message_to_client(client, "Pass Transaction Details")
            .then(function (m) {
                var req = new Request(event.request.url, {
                    method: event.request.method,
                    headers: event.request.headers,
                    mode: 'same-origin',
                    credentials: event.request.credentials,
                    redirect: 'manual'
                });

                var res_obj = JSON.parse(m);
                req.headers.append('MY_CUSTOM_HEADER', res_obj.hdr_val);
                // just return the response back to the respondWith() here
                return fetch(req);

            })
            // You probably want to add a .catch() to return some reasonable fallback
            // response.