chromium/chrome/test/data/extensions/api_test/webrequest/test_extra_headers/test_extra_headers.js

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

var callbackPass = chrome.test.callbackPass;

function getSetCookieUrl(name, value) {
  return getServerURL('set-cookie?' + name + '=' + value);
}

function testModifyHeadersOnRedirect(useExtraHeaders) {
  // Use /echoheader instead of observing headers in onSendHeaders to
  // ensure we're looking at what the server receives. This avoids bugs in the
  // webRequest implementation from being masked.
  var finalURL = getServerURL('echoheader?User-Agent&Accept&X-New-Header');
  var url = getServerURL('server-redirect?' + finalURL);
  var listener = callbackPass(function(details) {
    var headers = details.requestHeaders;

    // Test modification.
    var accept_value;
    for (var i = 0; i < headers.length; i++) {
      if (headers[i].name.toLowerCase() === 'user-agent') {
        headers[i].value = 'foo';
      } else if (headers[i].name.toLowerCase() === 'accept') {
        accept_value = headers[i].value;
      }
    }

    // Test removal.
    chrome.test.assertTrue(accept_value.indexOf('image/webp') >= 0);
    removeHeader(headers, 'accept');

    // Test addition.
    headers.push({name: 'X-New-Header', value: 'Baz'});

    return {requestHeaders: headers};
  });

  var extraInfo = ['requestHeaders', 'blocking'];
  if (useExtraHeaders)
    extraInfo.push('extraHeaders');
  chrome.webRequest.onBeforeSendHeaders.addListener(listener,
      {urls: [finalURL]}, extraInfo);

  navigateAndWait(url, function(tab) {
    chrome.webRequest.onBeforeSendHeaders.removeListener(listener);
    chrome.tabs.executeScript(tab.id, {
      code: 'document.body.innerText'
    }, callbackPass(function(results) {
      chrome.test.assertTrue(results[0].indexOf('foo') >= 0,
          'User-Agent should be modified.');
        chrome.test.assertTrue(results[0].indexOf('image/webp') == -1,
          'Accept should be removed.');
      chrome.test.assertTrue(results[0].indexOf('Baz') >= 0,
          'X-New-Header should be added.');
    }));
  });
}

const scriptUrl = '_test_resources/api_test/webrequest/framework.js';
let loadScript = chrome.test.loadScript(scriptUrl);

loadScript.then(async function() {
  runTests([
  function testSpecialRequestHeadersVisible() {
    // Set a cookie so the cookie request header is set.
    navigateAndWait(getSetCookieUrl('foo', 'bar'), function() {
      var url = getServerURL('echo');
      var extraHeadersListener = callbackPass(function(details) {
        checkHeaders(details.requestHeaders, ['user-agent', 'cookie'], []);
      });
      chrome.webRequest.onBeforeSendHeaders.addListener(extraHeadersListener,
          {urls: [url]}, ['requestHeaders', 'extraHeaders']);

      var standardListener = callbackPass(function(details) {
        checkHeaders(details.requestHeaders, ['user-agent'], ['cookie']);
      });
      chrome.webRequest.onBeforeSendHeaders.addListener(standardListener,
          {urls: [url]}, ['requestHeaders']);

      navigateAndWait(url, function() {
        chrome.webRequest.onBeforeSendHeaders.removeListener(
            extraHeadersListener);
        chrome.webRequest.onBeforeSendHeaders.removeListener(standardListener);
      });
    });
  },

  function testSpecialResponseHeadersVisible() {
    var url = getSetCookieUrl('foo', 'bar');
    var extraHeadersListenerCalledCount = 0;
    function extraHeadersListener(details) {
      extraHeadersListenerCalledCount++;
      checkHeaders(details.responseHeaders, ['set-cookie'], []);
    }
    chrome.webRequest.onHeadersReceived.addListener(extraHeadersListener,
        {urls: [url]}, ['responseHeaders', 'extraHeaders']);
    chrome.webRequest.onResponseStarted.addListener(extraHeadersListener,
        {urls: [url]}, ['responseHeaders', 'extraHeaders']);
    chrome.webRequest.onCompleted.addListener(extraHeadersListener,
        {urls: [url]}, ['responseHeaders', 'extraHeaders']);

    var standardListenerCalledCount = 0;
    function standardListener(details) {
      standardListenerCalledCount++;
      checkHeaders(details.responseHeaders, [], ['set-cookie']);
    }
    chrome.webRequest.onHeadersReceived.addListener(standardListener,
        {urls: [url]}, ['responseHeaders']);
    chrome.webRequest.onResponseStarted.addListener(standardListener,
        {urls: [url]}, ['responseHeaders']);
    chrome.webRequest.onCompleted.addListener(standardListener,
        {urls: [url]}, ['responseHeaders']);

    navigateAndWait(url, function() {
      chrome.test.assertEq(3, standardListenerCalledCount);
      chrome.test.assertEq(3, extraHeadersListenerCalledCount);
      chrome.webRequest.onHeadersReceived.removeListener(extraHeadersListener);
      chrome.webRequest.onResponseStarted.removeListener(extraHeadersListener);
      chrome.webRequest.onCompleted.removeListener(extraHeadersListener);
      chrome.webRequest.onHeadersReceived.removeListener(standardListener);
      chrome.webRequest.onResponseStarted.removeListener(standardListener);
      chrome.webRequest.onCompleted.removeListener(standardListener);
    });
  },

  function testModifySpecialRequestHeaders() {
    // Set a cookie so the cookie request header is set.
    navigateAndWait(getSetCookieUrl('foo', 'bar'), function() {
      var url = getServerURL('echoheader?Cookie');
      var listener = callbackPass(function(details) {
        removeHeader(details.requestHeaders, 'cookie');
        return {requestHeaders: details.requestHeaders};
      });
      chrome.webRequest.onBeforeSendHeaders.addListener(listener,
          {urls: [url]}, ['requestHeaders', 'blocking', 'extraHeaders']);

      navigateAndWait(url, function(tab) {
        chrome.webRequest.onBeforeSendHeaders.removeListener(listener);
        chrome.tabs.executeScript(tab.id, {
          code: 'document.body.innerText'
        }, callbackPass(function(results) {
          chrome.test.assertTrue(results[0].indexOf('bar') == -1,
              'Header not removed.');
        }));
      });
    });
  },

  function testModifySpecialResponseHeaders() {
    var url = getSetCookieUrl('foo', 'bar');
    var headersListener = callbackPass(function(details) {
      checkHeaders(details.responseHeaders, ['set-cookie'], []);
      details.responseHeaders.push({name: 'X-New-Header',
                                    value: 'Foo'});
      return {responseHeaders: details.responseHeaders};
    });
    chrome.webRequest.onHeadersReceived.addListener(headersListener,
        {urls: [url]}, ['responseHeaders', 'blocking', 'extraHeaders']);

    var responseListener = callbackPass(function(details) {
      checkHeaders(details.responseHeaders, ['set-cookie', 'x-new-header'], []);
    });
    chrome.webRequest.onResponseStarted.addListener(responseListener,
        {urls: [url]}, ['responseHeaders', 'extraHeaders']);

    var completedListener = callbackPass(function(details) {
      checkHeaders(details.responseHeaders, ['set-cookie', 'x-new-header'], []);
    });
    chrome.webRequest.onCompleted.addListener(completedListener,
        {urls: [url]}, ['responseHeaders', 'extraHeaders']);

    navigateAndWait(url, function(tab) {
      chrome.webRequest.onHeadersReceived.removeListener(headersListener);
      chrome.webRequest.onResponseStarted.removeListener(responseListener);
      chrome.webRequest.onCompleted.removeListener(completedListener);
    });
  },

  function testCannotModifySpecialRequestHeadersWithoutExtraHeaders() {
    // Set a cookie so the cookie request header is set.
    navigateAndWait(getSetCookieUrl('foo', 'bar'), function() {
      var url = getServerURL('echoheader?Cookie');
      var listener = callbackPass(function(details) {
        removeHeader(details.requestHeaders, 'cookie');
        return {requestHeaders: details.requestHeaders};
      });
      chrome.webRequest.onBeforeSendHeaders.addListener(listener,
          {urls: [url]}, ['requestHeaders', 'blocking']);

      // Add a no-op listener with extraHeaders to make sure it does not affect
      // the other listener.
      var noop = function() {};
      chrome.webRequest.onBeforeSendHeaders.addListener(noop,
          {urls: [url]}, ['requestHeaders', 'blocking', 'extraHeaders']);

      navigateAndWait(url, function(tab) {
        chrome.webRequest.onBeforeSendHeaders.removeListener(noop);
        chrome.webRequest.onBeforeSendHeaders.removeListener(listener);
        chrome.tabs.executeScript(tab.id, {
          code: 'document.body.innerText'
        }, callbackPass(function(results) {
          chrome.test.assertTrue(results[0].indexOf('bar') >= 0,
              'Header should not be removed.');
        }));
      });
    });
  },

  function testModifyUserAgentWithoutExtraHeaders() {
    var url = getServerURL('echoheader?User-Agent');
    var listener = callbackPass(function(details) {
      var headers = details.requestHeaders;
      for (var i = 0; i < headers.length; i++) {
        if (headers[i].name.toLowerCase() === 'user-agent') {
          headers[i].value = 'foo';
          break;
        }
      }
      return {requestHeaders: headers};
    });
    chrome.webRequest.onBeforeSendHeaders.addListener(listener,
        {urls: [url]}, ['requestHeaders', 'blocking']);

    navigateAndWait(url, function(tab) {
      chrome.webRequest.onBeforeSendHeaders.removeListener(listener);
      chrome.tabs.executeScript(tab.id, {
        code: 'document.body.innerText'
      }, callbackPass(function(results) {
        chrome.test.assertTrue(results[0].indexOf('foo') >= 0,
            'User-Agent should be modified.');
      }));
    });
  },

  function testModifyHeadersOnRedirectWithoutExtraHeaders() {
    testModifyHeadersOnRedirect(false);
  },

  function testModifyHeadersOnRedirectWithExtraHeaders() {
    testModifyHeadersOnRedirect(true);
  },

  // Successful Set-Cookie modification is tested in test_blocking_cookie.js.
  function testCannotModifySpecialResponseHeadersWithoutExtraHeaders() {
    // Use unique name and value so other tests don't interfere.
    var url = getSetCookieUrl('theName', 'theValue');
    var listener = callbackPass(function(details) {
      removeHeader(details.responseHeaders, 'set-cookie');
      return {responseHeaders: details.responseHeaders};
    });
    chrome.webRequest.onHeadersReceived.addListener(listener,
        {urls: [url]}, ['responseHeaders', 'blocking']);

    // Add a no-op listener with extraHeaders to make sure it does not affect
    // the other listener.
    var noop = function() {};
    chrome.webRequest.onHeadersReceived.addListener(noop,
        {urls: [url]}, ['responseHeaders', 'blocking', 'extraHeaders']);

    navigateAndWait(url, function(tab) {
      chrome.webRequest.onHeadersReceived.removeListener(noop);
      chrome.webRequest.onHeadersReceived.removeListener(listener);
      chrome.tabs.executeScript(tab.id, {
        code: 'document.cookie'
      }, callbackPass(function(results) {
        chrome.test.assertTrue(results[0].indexOf('theValue') >= 0,
            'Header should not be removed.');
      }));
    });
  },

  function testRedirectToUrlWithExtraHeadersListener() {
    // Set a cookie so the cookie request header is set.
    navigateAndWait(getSetCookieUrl('foo', 'bar'), function() {
      var finalURL = getServerURL('echoheader?Cookie');
      var url = getServerURL('server-redirect?' + finalURL);
      var listener = callbackPass(function(details) {
        removeHeader(details.requestHeaders, 'cookie');
        return {requestHeaders: details.requestHeaders};
      });
      chrome.webRequest.onBeforeSendHeaders.addListener(listener,
          {urls: [finalURL]}, ['requestHeaders', 'blocking', 'extraHeaders']);

      navigateAndWait(url, function(tab) {
        chrome.test.assertEq(finalURL, tab.url);
        chrome.webRequest.onBeforeSendHeaders.removeListener(listener);
        chrome.tabs.executeScript(tab.id, {
          code: 'document.body.innerText'
        }, callbackPass(function(results) {
          chrome.test.assertTrue(results[0].indexOf('bar') == -1,
              'Header not removed.');
        }));
      });
    });
  },
])});