chromium/headless/test/data/protocol/helpers/http-interceptor.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.

/**
 * A helper class to allow renderer tests to conveniently intercept network
 * requests and provide HTTP headers and body.
 */
(class HttpInterceptor {
  /**
   * @param {!TestRunner} testRunner Host TestRunner instance.
   * @param {!Proxy} dp DevTools session protocol instance.
   * @param {!Page} page TestRunner.Page instance.
   */
  constructor(testRunner, dp) {
    this.testRunner_ = testRunner;
    this.dp_ = dp;
    this.disabledRequestedUrlsLogging = false;
    this.responses_ = new Map();
    this.requestedUrls_ = [];
    this.requestedMethods_ = [];
  }

  /**
   * Initializes the helper returning reference to itself to allow assignment.
   *
   * @return {!object} HttpInterceptor reference.
   */
  async init() {
    await this.dp_.Network.enable();
    await this.dp_.Network.setRequestInterception(
        { patterns: [{ urlPattern: '*' }] });

    this.dp_.Network.onRequestIntercepted(event => {
      const method = event.params.request.method;
      this.requestedMethods_.push(method);

      const url = event.params.request.url
          + (event.params.request.urlFragment || '');
      this.requestedUrls_.push(url);

      var response = this.responses_.get(url);
      if (response) {
        if (!this.disabledRequestedUrlsLogging) {
          this.testRunner_.log(`requested url: ${url}`);
        }
      } else {
        this.testRunner_.log(`requested url: ${url} is not known`);
        this.logResponses();
      }
      const body = (response && response.body) || '';
      const headers = (response && response.headers) || [];
      const headers_with_body = headers.join('\r\n') + '\r\n\r\n' + body;
      this.dp_.Network.continueInterceptedRequest({
        interceptionId: event.params.interceptionId,
        rawResponse: btoa(headers_with_body)
      });
    });

    return this;
  }

  /**
   * Prevents requested urls from being logged. This helps to stabilize tests
   * that request urls in arbitrary order. Use hasRequestedUrls(urls) function
   * to check if expected urls has been requested.
   *
   * @param {boolean} value True if requested url logging is disabled, false
   *     otherwise.
   */
  setDisableRequestedUrlsLogging(value) {
    this.disabledRequestedUrlsLogging = value;
  }

  /**
   * Adds request response.
   *
   * @param {!string} url Request url, including #fragment.
   * @param {?string} body Request response body, optional.
   * @param {?[string]} headers Request response headers, optional.
   */
  addResponse(url, body, headers) {
    this.responses_.set(url, {body, headers});
  }

  /**
   * Logs registered request responses.
   */
  logResponses() {
    this.testRunner_.log(`Responses: ${this.responses_.size}`);
    for (const [url, value] of this.responses_.entries()) {
      this.testRunner_.log(
          `url=${url}\nbody=${value.body}\nheaders=${value.headers}`);
    }
  }

  /**
   * Logs requested methods in the order requests have been received.
   */
  logRequestedMethods() {
    this.testRunner_.log(`Requested methods: ${this.requestedMethods_.length}`);
    for (const method of this.requestedMethods_) {
      this.testRunner_.log(` ${method}`);
    }
  }

  /**
   * Logs requested urls in the order requests have been received.
   */
  logRequestedUrls() {
    this.testRunner_.log(`Requested urls: ${this.requestedUrls_.length}`);
    for (const url of this.requestedUrls_) {
      this.testRunner_.log(` ${url}`);
    }
  }

  /**
   * Checks if specified urls have been requested.
   *
   * @param {[string]} urls Array of urls to check against requested urls.
   */
  hasRequestedUrls(urls) {
    this.testRunner_.log(`Expected requested urls:`);
    for (const url of urls) {
      if (this.requestedUrls_.indexOf(url) >= 0) {
        this.testRunner_.log(` ${url}`);
      } else {
        this.testRunner_.log(` ${url} is MISSING`);
      }
    }
  }

});