tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

fetch-rewrite-worker.js (6193B)


      1 // By default, this worker responds to fetch events with
      2 // respondWith(fetch(request)). Additionally, if the request has a &url
      3 // parameter, it fetches the provided URL instead. Because it forwards fetch
      4 // events to this other URL, it is called the "fetch rewrite" worker.
      5 //
      6 // The worker also looks for other params on the request to do more custom
      7 // behavior, like falling back to network or throwing an error.
      8 
      9 function get_query_params(url) {
     10  var search = (new URL(url)).search;
     11  if (!search) {
     12    return {};
     13  }
     14  var ret = {};
     15  var params = search.substring(1).split('&');
     16  params.forEach(function(param) {
     17      var element = param.split('=');
     18      ret[decodeURIComponent(element[0])] = decodeURIComponent(element[1]);
     19    });
     20  return ret;
     21 }
     22 
     23 function get_request_init(base, params) {
     24  var init = {};
     25  init['method'] = params['method'] || base['method'];
     26  init['mode'] = params['mode'] || base['mode'];
     27  if (init['mode'] == 'navigate') {
     28    init['mode'] = 'same-origin';
     29  }
     30  init['credentials'] = params['credentials'] || base['credentials'];
     31  init['redirect'] = params['redirect-mode'] || base['redirect'];
     32  return init;
     33 }
     34 
     35 self.addEventListener('fetch', function(event) {
     36    var params = get_query_params(event.request.url);
     37    var init = get_request_init(event.request, params);
     38    var url = params['url'];
     39    if (params['ignore']) {
     40      return;
     41    }
     42    if (params['throw']) {
     43      throw new Error('boom');
     44    }
     45    if (params['reject']) {
     46      event.respondWith(new Promise(function(resolve, reject) {
     47          reject();
     48        }));
     49      return;
     50    }
     51    if (params['resolve-null']) {
     52      event.respondWith(new Promise(function(resolve) {
     53          resolve(null);
     54        }));
     55      return;
     56    }
     57    if (params['generate-png']) {
     58      var binary = atob(
     59          'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAA' +
     60          'RnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAAhSURBVDhPY3wro/Kf' +
     61          'gQLABKXJBqMGjBoAAqMGDLwBDAwAEsoCTFWunmQAAAAASUVORK5CYII=');
     62      var array = new Uint8Array(binary.length);
     63      for(var i = 0; i < binary.length; i++) {
     64        array[i] = binary.charCodeAt(i);
     65      };
     66      event.respondWith(new Response(new Blob([array], {type: 'image/png'})));
     67      return;
     68    }
     69    if (params['check-ua-header']) {
     70      var ua = event.request.headers.get('User-Agent');
     71      if (ua) {
     72        // We have a user agent!
     73        event.respondWith(new Response(new Blob([ua])));
     74      } else {
     75        // We don't have a user-agent!
     76        event.respondWith(new Response(new Blob(["NO_UA"])));
     77      }
     78      return;
     79    }
     80    if (params['check-accept-header']) {
     81      var accept = event.request.headers.get('Accept');
     82      if (accept) {
     83        event.respondWith(new Response(accept));
     84      } else {
     85        event.respondWith(new Response('NO_ACCEPT'));
     86      }
     87      return;
     88    }
     89    event.respondWith(new Promise(function(resolve, reject) {
     90        var request = event.request;
     91        if (url) {
     92          request = new Request(url, init);
     93        } else if (params['change-request']) {
     94          request = new Request(request, init);
     95        }
     96        const response_promise = params['navpreload'] ? event.preloadResponse
     97                                                      : fetch(request);
     98        response_promise.then(function(response) {
     99          var expectedType = params['expected_type'];
    100          if (expectedType && response.type !== expectedType) {
    101            // Resolve a JSON object with a failure instead of rejecting
    102            // in order to distinguish this from a NetworkError, which
    103            // may be expected even if the type is correct.
    104            resolve(new Response(JSON.stringify({
    105              result: 'failure',
    106              detail: 'got ' + response.type + ' Response.type instead of ' +
    107                      expectedType
    108            })));
    109          }
    110 
    111          var expectedRedirected = params['expected_redirected'];
    112          if (typeof expectedRedirected !== 'undefined') {
    113            var expected_redirected = (expectedRedirected === 'true');
    114            if(response.redirected !== expected_redirected) {
    115              // This is simply determining how to pass an error to the outer
    116              // test case(fetch-request-redirect.https.html).
    117              var execptedResolves = params['expected_resolves'];
    118              if (execptedResolves === 'true') {
    119                // Reject a JSON object with a failure since promise is expected
    120                // to be resolved.
    121                reject(new Response(JSON.stringify({
    122                  result: 'failure',
    123                  detail: 'got '+ response.redirected +
    124                          ' Response.redirected instead of ' +
    125                          expectedRedirected
    126                })));
    127              } else {
    128                // Resolve a JSON object with a failure since promise is
    129                // expected to be rejected.
    130                resolve(new Response(JSON.stringify({
    131                  result: 'failure',
    132                  detail: 'got '+ response.redirected +
    133                          ' Response.redirected instead of ' +
    134                          expectedRedirected
    135                })));
    136              }
    137            }
    138          }
    139 
    140          if (params['clone']) {
    141            response = response.clone();
    142          }
    143 
    144          // |cache| means to bounce responses through Cache Storage and back.
    145          if (params['cache']) {
    146            var cacheName = "cached-fetches-" + performance.now() + "-" +
    147                            event.request.url;
    148            var cache;
    149            var cachedResponse;
    150            return self.caches.open(cacheName).then(function(opened) {
    151              cache = opened;
    152              return cache.put(request, response);
    153            }).then(function() {
    154              return cache.match(request);
    155            }).then(function(cached) {
    156              cachedResponse = cached;
    157              return self.caches.delete(cacheName);
    158            }).then(function() {
    159               resolve(cachedResponse);
    160            });
    161          } else {
    162            resolve(response);
    163          }
    164        }, reject)
    165      }));
    166  });