Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

javascript intercept http fetch from web worker + file urls

I'd like to intercept fetch from all parts and libraries in my application, and at the same time I'd like not to break possibility of working with the application via file URL - it is useful for Electron and mobile devices (via WebView). For now, I've found two possible ways of doing this:

  1. something like here

    const realFetch = window.fetch;
    window.fetch = function() {
    // do something
    return realFetch.apply(this, arguments)
    }
    
  2. something like here, with service worker registration:

main.js:

if ('serviceWorker' in navigator) {
  window.addEventListener('load', function() {
  navigator.serviceWorker.register('sw.js').then(function(registration) {
    console.log('Service worker registered with scope: ', registration.scope);
  }, function(err) {
  console.log('ServiceWorker registration failed: ', err);
});
});
}

sw.js:

self.addEventListener('fetch', function(event) {
  event.respondWith(
  // intercept requests by handling event.request here
  );
});

With the first approach I cannot intercept fetch requests from web workers. The second approach doesn't work with file URLs, and I want my application to work via file URL due to it allows me to use the app via Electron for desktops or WebView for Android. Is there any other way for intercepting fetch requests?

P.S. I cannot modify the worker I'm trying to intercept requests from.

Update: On basis of @Ciro Corvino's answer, I've tried the third approach: to start my own worker before anythnig else and try to redefine fetch from there. Didn't work for me, unfortunately, here is the code:

function redefineFetch() {
console.log('inside worker');
if (self.fetch == null) {
    console.log('null!');
} else {
    console.log(self.fetch.toString());
}
const originalFetch: WindowOrWorkerGlobalScope['fetch'] = self.fetch;
self.fetch = (input: RequestInfo, init: RequestInit) => {
    console.log('overridden');
    return originalFetch(input, init);
}
}
const blob = new Blob(['(' +
redefineFetch.toString() + ')()'], {type: 'text/javascript'});

const blobUrl = window.URL.createObjectURL(blob);
const w = new Worker(blobUrl);

I'm sure that this code starts before the other workers (I've added a timeout), but this doesn't redefine fetch for the other workers. Can someone explain why or fix the solution?

Update 2: Apparently each worker has it's own private WorkerGlobalScope, otherwise there would be no sense to use messages for inter-worker communications. Probably, another fix for my problem could be in overriding Worker constructor, if this is possible. Will check it.

like image 601
Anton Pilyak Avatar asked Feb 27 '19 10:02

Anton Pilyak


People also ask

How do I intercept HTTP request in Javascript?

To intercept HTTP requests, use the webRequest API. This API enables you to add listeners for various stages of making an HTTP request. In the listeners, you can: Get access to request headers and bodies and response headers.

Can service worker send HTTP request?

Using Service Workers, you can build your own custom HTTP responses, including editing their headers. This functionality makes Service Workers extremely powerful—which is why you can understand that they need to serve requests over HTTPS.

Does fetch have interceptors?

The Fetch API doesn't support interceptors natively. However, there are other libraries for making HTTP calls that support interceptors.

Is service worker intercepted?

Service Workers are a special type of Web Worker with the ability to intercept, modify, and respond to all network requests using the Fetch API. Service Workers can access the Cache API, and asynchronous client-side data stores, such as IndexedDB , to store resources.


1 Answers

Just try to override the fetch method of the current WorkerGlobalScope into main javascript context (window) and into each js file run in a dedicated worker context calling this function:

note that the self property returns the specialized scope for each context

 //works in each worker context you call it and enable fetch interception
 function EnableFetchWithArguments() {
   const originalCtxFetch = self.fetch;
   self.fetch = function() {
     // Get the parameter in arguments
     // Intercept the parameter here 
     return originalCtxFetch.apply(this, arguments)
   }
 }

see for reference and browser compatiblity: WorkerGlobalScope

like image 149
Ciro Corvino Avatar answered Oct 21 '22 05:10

Ciro Corvino