Bug 125025 - Properly decode response body; r=gasolin draft
authorJan Odvarko <odvarko@gmail.com>
Mon, 02 Oct 2017 18:11:20 +0200
changeset 673452 ab34b4d144ff756e9bc6f1bf8fc7fb3a8fdc2791
parent 673165 44643fce30b43a8981535c335aaccb45006e456b
child 734114 802d2e0e432be7b2453a19e6769fc9d60e0cd20f
push id82588
push userjodvarko@mozilla.com
push dateMon, 02 Oct 2017 16:39:54 +0000
reviewersgasolin
bugs125025
milestone58.0a1
Bug 125025 - Properly decode response body; r=gasolin MozReview-Commit-ID: 1vbWxLfieIq
devtools/client/netmonitor/src/connector/firefox-data-provider.js
devtools/client/netmonitor/src/utils/request-utils.js
--- a/devtools/client/netmonitor/src/connector/firefox-data-provider.js
+++ b/devtools/client/netmonitor/src/connector/firefox-data-provider.js
@@ -2,17 +2,21 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 /* eslint-disable block-scoped-var */
 
 "use strict";
 
 const { EVENTS } = require("../constants");
 const { CurlUtils } = require("devtools/client/shared/curl");
-const { fetchHeaders, formDataURI } = require("../utils/request-utils");
+const {
+  b64DecodeUnicode,
+  fetchHeaders,
+  formDataURI,
+} = require("../utils/request-utils");
 
 /**
  * This object is responsible for fetching additional HTTP
  * data from the backend.
  */
 class FirefoxDataProvider {
   constructor({webConsoleClient, actions}) {
     // Options
@@ -22,17 +26,17 @@ class FirefoxDataProvider {
     // Internal properties
     this.payloadQueue = [];
 
     // Public methods
     this.addRequest = this.addRequest.bind(this);
     this.updateRequest = this.updateRequest.bind(this);
 
     // Internals
-    this.fetchImage = this.fetchImage.bind(this);
+    this.fetchResponseBody = this.fetchResponseBody.bind(this);
     this.fetchRequestHeaders = this.fetchRequestHeaders.bind(this);
     this.fetchResponseHeaders = this.fetchResponseHeaders.bind(this);
     this.fetchPostData = this.fetchPostData.bind(this);
     this.fetchResponseCookies = this.fetchResponseCookies.bind(this);
     this.fetchRequestCookies = this.fetchRequestCookies.bind(this);
     this.getPayloadFromQueue = this.getPayloadFromQueue.bind(this);
     this.isQueuePayloadReady = this.isQueuePayloadReady.bind(this);
     this.pushPayloadToQueue = this.pushPayloadToQueue.bind(this);
@@ -107,17 +111,17 @@ class FirefoxDataProvider {
     let [
       imageObj,
       requestHeadersObj,
       responseHeadersObj,
       postDataObj,
       requestCookiesObj,
       responseCookiesObj,
     ] = await Promise.all([
-      this.fetchImage(mimeType, responseContent),
+      this.fetchResponseBody(mimeType, responseContent),
       this.fetchRequestHeaders(requestHeaders),
       this.fetchResponseHeaders(responseHeaders),
       this.fetchPostData(requestPostData),
       this.fetchRequestCookies(requestCookies),
       this.fetchResponseCookies(responseCookies),
     ]);
 
     let payload = Object.assign({},
@@ -132,24 +136,30 @@ class FirefoxDataProvider {
 
     this.pushPayloadToQueue(id, payload);
 
     if (this.actions.updateRequest && this.isQueuePayloadReady(id)) {
       await this.actions.updateRequest(id, this.getPayloadFromQueue(id).payload, true);
     }
   }
 
-  async fetchImage(mimeType, responseContent) {
+  async fetchResponseBody(mimeType, responseContent) {
     let payload = {};
     if (mimeType && responseContent && responseContent.content) {
       let { encoding, text } = responseContent.content;
       let response = await this.getLongString(text);
 
       if (mimeType.includes("image/")) {
         payload.responseContentDataUri = formDataURI(mimeType, encoding, response);
+      } else if (encoding === "base64") {
+        response = b64DecodeUnicode(response);
+      }
+
+      if (mimeType.includes("text/")) {
+        payload.responseContentDataUri = formDataURI(mimeType, encoding, response);
       }
 
       responseContent.content.text = response;
       payload.responseContent = responseContent;
     }
     return payload;
   }
 
--- a/devtools/client/netmonitor/src/utils/request-utils.js
+++ b/devtools/client/netmonitor/src/utils/request-utils.js
@@ -104,16 +104,36 @@ function decodeUnicodeUrl(string) {
     return decodeURIComponent(string);
   } catch (err) {
     // Ignore error and return input string directly.
   }
   return string;
 }
 
 /**
+ * Decode base64 string. From bytestream, to percent-encoding,
+ * to original string.
+ *
+ * See also: https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding
+ *
+ * @param {string} url - a string
+ * @return {string} decoded string
+ */
+function b64DecodeUnicode(str) {
+  try {
+    return decodeURIComponent(atob(str).split("").map(function (c) {
+      return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2);
+    }).join(""));
+  } catch (err) {
+    // Ignore error and return input string directly.
+  }
+  return str;
+}
+
+/**
  * Helper for getting an abbreviated string for a mime type.
  *
  * @param {string} mimeType - mime type
  * @return {string} abbreviated mime type
  */
 function getAbbreviatedMimeType(mimeType) {
   if (!mimeType) {
     return "";
@@ -370,16 +390,17 @@ function getResponseHeader(item, header)
     if (responseHeader.name.toLowerCase() == header) {
       return responseHeader.value;
     }
   }
   return null;
 }
 
 module.exports = {
+  b64DecodeUnicode,
   getFormDataSections,
   fetchHeaders,
   formDataURI,
   writeHeaderText,
   decodeUnicodeUrl,
   getAbbreviatedMimeType,
   getEndTime,
   getFormattedProtocol,