text: /**
   * @license
   * Copyright The Closure Library Authors.
   * SPDX-License-Identifier: Apache-2.0
   */
  
  /**
   * @fileoverview Provides a utility for tracing and debugging WebChannel
   *     requests.
   *
   */
  
  
  goog.provide('goog.labs.net.webChannel.WebChannelDebug');
  
  goog.require('goog.json');
  goog.require('goog.log');
  goog.requireType('goog.Uri');
  goog.requireType('goog.debug.Loggable');
  goog.requireType('goog.net.XmlHttp.ReadyState');
  
  
  
  /**
   * Logs and keeps a buffer of debugging info for the Channel.
   *
   * @constructor
   * @struct
   * @final
   */
  goog.labs.net.webChannel.WebChannelDebug = function() {
    'use strict';
    /**
     * The logger instance.
     * @const
     * @private {?goog.log.Logger}
     */
    this.logger_ = goog.log.getLogger('goog.labs.net.webChannel.WebChannelDebug');
  
    /**
     * Whether to enable redact. Defaults to true.
     * @private {boolean}
     */
    this.redactEnabled_ = true;
  };
  
  
  goog.scope(function() {
  'use strict';
  var WebChannelDebug = goog.labs.net.webChannel.WebChannelDebug;
  
  
  /**
   * Turns off redact.
   */
  WebChannelDebug.prototype.disableRedact = function() {
    'use strict';
    this.redactEnabled_ = false;
  };
  
  
  /**
   * Logs that the browser went offline during the lifetime of a request.
   * @param {goog.Uri} url The URL being requested.
   */
  WebChannelDebug.prototype.browserOfflineResponse = function(url) {
    'use strict';
    this.info(function() {
      'use strict';
      return 'BROWSER_OFFLINE: ' + url;
    });
  };
  
  
  /**
   * Logs an XmlHttp request..
   * @param {string} verb The request type (GET/POST).
   * @param {goog.Uri} uri The request destination.
   * @param {string|number|undefined} id The request id.
   * @param {number} attempt Which attempt # the request was.
   * @param {?string} postData The data posted in the request.
   */
  WebChannelDebug.prototype.xmlHttpChannelRequest = function(
      verb, uri, id, attempt, postData) {
    'use strict';
    var self = this;
    this.info(function() {
      'use strict';
      return 'XMLHTTP REQ (' + id + ') [attempt ' + attempt + ']: ' + verb +
          '\n' + uri + '\n' + self.maybeRedactPostData_(postData);
    });
  };
  
  
  /**
   * Logs the meta data received from an XmlHttp request.
   * @param {string} verb The request type (GET/POST).
   * @param {goog.Uri} uri The request destination.
   * @param {string|number|undefined} id The request id.
   * @param {number} attempt Which attempt # the request was.
   * @param {goog.net.XmlHttp.ReadyState} readyState The ready state.
   * @param {number} statusCode The HTTP status code.
   */
  WebChannelDebug.prototype.xmlHttpChannelResponseMetaData = function(
      verb, uri, id, attempt, readyState, statusCode) {
    'use strict';
    this.info(function() {
      'use strict';
      return 'XMLHTTP RESP (' + id + ') [ attempt ' + attempt + ']: ' + verb +
          '\n' + uri + '\n' + readyState + ' ' + statusCode;
    });
  };
  
  
  /**
   * Logs the response data received from an XmlHttp request.
   * @param {string|number|undefined} id The request id.
   * @param {?string} responseText The response text.
   * @param {?string=} opt_desc Optional request description.
   */
  WebChannelDebug.prototype.xmlHttpChannelResponseText = function(
      id, responseText, opt_desc) {
    'use strict';
    var self = this;
    this.info(function() {
      'use strict';
      return 'XMLHTTP TEXT (' + id + '): ' + self.redactResponse_(responseText) +
          (opt_desc ? ' ' + opt_desc : '');
    });
  };
  
  
  /**
   * Logs a request timeout.
   * @param {goog.Uri} uri The uri that timed out.
   */
  WebChannelDebug.prototype.timeoutResponse = function(uri) {
    'use strict';
    this.info(function() {
      'use strict';
      return 'TIMEOUT: ' + uri;
    });
  };
  
  
  /**
   * Logs a debug message.
   * @param {!goog.debug.Loggable} text The message.
   */
  WebChannelDebug.prototype.debug = function(text) {
    'use strict';
    goog.log.fine(this.logger_, text);
  };
  
  
  /**
   * Logs an exception
   * @param {Error} e The error or error event.
   * @param {goog.debug.Loggable=} opt_msg The optional message,
   *     defaults to 'Exception'.
   */
  WebChannelDebug.prototype.dumpException = function(e, opt_msg) {
    'use strict';
    goog.log.error(this.logger_, opt_msg || 'Exception', e);
  };
  
  
  /**
   * Logs an info message.
   * @param {!goog.debug.Loggable} text The message.
   */
  WebChannelDebug.prototype.info = function(text) {
    'use strict';
    goog.log.info(this.logger_, text);
  };
  
  
  /**
   * Logs a warning message.
   * @param {!goog.debug.Loggable} text The message.
   */
  WebChannelDebug.prototype.warning = function(text) {
    'use strict';
    goog.log.warning(this.logger_, text);
  };
  
  
  /**
   * Logs a severe message.
   * @param {!goog.debug.Loggable} text The message.
   */
  WebChannelDebug.prototype.severe = function(text) {
    'use strict';
    goog.log.error(this.logger_, text);
  };
  
  
  /**
   * Removes potentially private data from a response so that we don't
   * accidentally save private and personal data to the server logs.
   * @param {?string} responseText A JSON response to clean.
   * @return {?string} The cleaned response.
   * @private
   */
  WebChannelDebug.prototype.redactResponse_ = function(responseText) {
    'use strict';
    if (!this.redactEnabled_) {
      return responseText;
    }
  
    if (!responseText) {
      return null;
    }
  
    try {
      var responseArray = JSON.parse(responseText);
      if (responseArray) {
        for (var i = 0; i < responseArray.length; i++) {
          if (Array.isArray(responseArray[i])) {
            this.maybeRedactArray_(responseArray[i]);
          }
        }
      }
  
      return goog.json.serialize(responseArray);
    } catch (e) {
      this.debug('Exception parsing expected JS array - probably was not JS');
      return responseText;
    }
  };
  
  
  /**
   * Removes data from a response array that may be sensitive.
   * @param {!Array<?>} array The array to clean.
   * @private
   */
  WebChannelDebug.prototype.maybeRedactArray_ = function(array) {
    'use strict';
    if (array.length < 2) {
      return;
    }
    var dataPart = array[1];
    if (!Array.isArray(dataPart)) {
      return;
    }
    if (dataPart.length < 1) {
      return;
    }
  
    var type = dataPart[0];
    if (type != 'noop' && type != 'stop' && type != 'close') {
      // redact all fields in the array
      for (var i = 1; i < dataPart.length; i++) {
        dataPart[i] = '';
      }
    }
  };
  
  
  /**
   * Removes potentially private data from a request POST body so that we don't
   * accidentally save private and personal data to the server logs.
   * @param {?string} data The data string to clean.
   * @return {?string} The data string with sensitive data replaced by 'redacted'.
   * @private
   */
  WebChannelDebug.prototype.maybeRedactPostData_ = function(data) {
    'use strict';
    if (!this.redactEnabled_) {
      return data;
    }
  
    if (!data) {
      return null;
    }
    var out = '';
    var params = data.split('&');
    for (var i = 0; i < params.length; i++) {
      var param = params[i];
      var keyValue = param.split('=');
      if (keyValue.length > 1) {
        var key = keyValue[0];
        var value = keyValue[1];
  
        var keyParts = key.split('_');
        if (keyParts.length >= 2 && keyParts[1] == 'type') {
          out += key + '=' + value + '&';
        } else {
          out += key + '=' +
              'redacted' +
              '&';
        }
      }
    }
    return out;
  };
  });  // goog.scope
  