chromium/third_party/blink/web_tests/http/tests/inspector-protocol/network/request-interception-on-both-redirect-rewrite.js

(async function(/** @type {import('test_runner').TestRunner} */ testRunner) {
  var {page, session, dp} = await testRunner.startBlank(
      `Tests interception for redirects in a chain but rewrite last response.`);

  var headersMaskList = new Set(['date', 'server', 'last-modified', 'etag', 'keep-alive', 'expires']);
  // Hide these headers which are not shown in newer versions of PHP.
  var headersHideList = new Set(['x-powered-by']);
  var fileNameForRequestId = new Map();
  var responseReceivedEventProimseForFile = new Map();
  var responseReceivedResolverForFile = new Map();

  await setUpInterception();
  testRunner.log('Test Ready.');

  await session.protocol.Network.setRequestInterception({patterns: [
    {urlPattern: '*', interceptionStage: 'Request'},
    {urlPattern: '*', interceptionStage: 'HeadersReceived'}
  ]});
  testRunner.log('Request interception patterns sent.');

  session.evaluate(`fetch('${testRunner.url('../resources/redirect1.php')}').then(r => r.text())`);

  await waitForInterceptionEventAndContinue("/redirect1.php");
  await waitForInterceptionEventAndContinue("/redirect1.php");


  // Should be redirect2.php as redirecting to final.html.
  await waitForInterceptionEventAndContinue("/redirect2.php");
  await waitForInterceptionEventAndContinue("/redirect2.php");

  // Should be final.html as request.
  await waitForInterceptionEventAndContinue("/final.html");
  const interceptionEvent = await waitForInterceptionEvent("/final.html");
  testRunner.log('Modifying final.html\'s response after we receive response.');
  var body = '<html>\n<body>This content was rewritten!</body>\n</html>';
  var dummyHeaders = [
    'HTTP/1.1 200 OK',
    'Date: ' + (new Date()).toUTCString(),
    'Connection: closed',
    'Content-Length: ' + body.size,
    'Content-Type: text/html'
  ];
  testRunner.log('Modifying request with new body.');
  session.protocol.Network.continueInterceptedRequest({
    interceptionId: interceptionEvent.params.interceptionId,
    rawResponse: btoa(dummyHeaders.join('\r\n') + '\r\n\r\n' + body)
  });

  var responseReceivedEvent = await waitForResponseReceivedEvent('final.html');
  var bodyResponse = await session.protocol.Network.getResponseBody({requestId: responseReceivedEvent.params.requestId});
  testRunner.log('');
  testRunner.log('Body content received by renderer for final.html:');
  testRunner.log(bodyResponse.result.base64Encoded ? atob(bodyResponse.result.body) : bodyResponse.result.body);
  testRunner.log('');
  testRunner.completeTest();


  async function setUpInterception() {
    await session.protocol.Network.clearBrowserCookies();
    await session.protocol.Network.clearBrowserCache();
    await session.protocol.Network.setCacheDisabled({cacheDisabled: true});
    await session.protocol.Network.enable();
    await session.protocol.Page.enable();
    await session.protocol.Runtime.enable();
    session.protocol.Network.onRequestWillBeSent(event => {
      fileNameForRequestId.set(event.params.requestId, event.params.request.url.split('/').pop());
    });
    session.protocol.Network.onResponseReceived(event => {
      var fileName = fileNameForRequestId.get(event.params.requestId);
      if (!fileName)
        throw "Expected requestWillBeSent to be executed before responseReceived.";
      var resolver = responseReceivedResolverForFile.get(fileName);
      if (resolver) {
        resolver(event);
        responseReceivedResolverForFile.delete(fileName);
      }
      responseReceivedEventProimseForFile.set(fileName, Promise.resolve(event));
    });
  }

  async function waitForInterceptionEvent(expectedUrlSuffix) {
    const event = await session.protocol.Network.onceRequestIntercepted();
    const url = event.params.request.url;
    if (!url.endsWith(expectedUrlSuffix)) {
      testRunner.log(`FAIL: expected url ending with "${expectedUrlSuffix}", got "${url}"`);
      testRunner.completeTest();
      return null;
    }
    return event;
  }

  async function waitForInterceptionEventAndContinue(expectedUrlSuffix) {
    const event = await waitForInterceptionEvent(expectedUrlSuffix);
    if (!event)
      return;
    logInterceptionEvent(event);
    testRunner.log(`Continuing a request to ${event.params.request.url}`);
    session.protocol.Network.continueInterceptedRequest({interceptionId: event.params.interceptionId});
  }

  function waitForResponseReceivedEvent(fileName) {
    var promise = responseReceivedEventProimseForFile.get(fileName);
    if (!promise) {
      promise = new Promise(resolve => {
        responseReceivedResolverForFile.set(fileName, resolve);
      });
      responseReceivedEventProimseForFile.set(fileName, promise);
    }
    return promise;
  }

  async function logInterceptionEvent(event) {
    testRunner.log('Request Intercepted: ' + event.params.request.url.split('/').pop());
    testRunner.log('  responseErrorReason: ' + event.params.responseErrorReason);
    testRunner.log('  responseStatusCode: ' + event.params.responseStatusCode);
    var responseHeaders = event.params.responseHeaders;
    if (responseHeaders) {
      testRunner.log('  responseHeaders:');
      for (var headerName of Object.keys(event.params.responseHeaders).sort()) {
        var headerValue = event.params.responseHeaders[headerName].split(';')[0]; // Sometimes "; charset=UTF-8" gets in here.
        if (headersHideList.has(headerName.toLowerCase()))
          continue;
        if (headersMaskList.has(headerName.toLowerCase()))
          headerValue = '<Masked>';
        testRunner.log(`    ${headerName}: ${headerValue}`);
      }
    } else {
      testRunner.log('  responseHeaders: <None>');
    }

    if (event.params.redirectUrl)
      testRunner.log('  redirectUrl: ' + event.params.redirectUrl.split('/').pop());

    var bodyData = await session.protocol.Network.getResponseBodyForInterception({interceptionId: event.params.interceptionId});
    if (bodyData.error) {
      testRunner.log('  responseBody:');
      testRunner.log('    Error<' + bodyData.error.message + '>');
    } else {
      testRunner.log('  responseBody:');
      testRunner.log(bodyData.result.base64Encoded ? atob(bodyData.result.body) : bodyData.result.body);
    }
  }
})