Bug 1532530 - Avoid loss of upload data by webRequest API r=kmag
authorRob Wu <rob@robwu.nl>
Mon, 25 Mar 2019 21:16:49 +0000
changeset 466015 574c141c8dd619a00ffa90fc40c2ba614afb46da
parent 466014 40425ded3bad6ec5db3e3a2cba23cf3a0e447f75
child 466016 a0dcfd3d82f2f8c2407afbb45de34d455156a048
push id1
push userpvanderbeken@mozilla.com
push dateThu, 28 Mar 2019 13:34:35 +0000
reviewerskmag
bugs1532530
milestone68.0a1
Bug 1532530 - Avoid loss of upload data by webRequest API r=kmag When an extension requests access to the request body of a request, `nsConverterInputStream` is used to parse the input streams that make up a request body. These input streams are later (re)used to upload the form data to the original destination (server). `nsConverterInputStream`'s destructor does however close the input streams, which results in data loss when the object is garbage-collected before the upload completes. This patch fixes the issue by explicitly nulling the underlying stream before returning from the form parser. Differential Revision: https://phabricator.services.mozilla.com/D24539
toolkit/modules/addons/WebRequestUpload.jsm
--- a/toolkit/modules/addons/WebRequestUpload.jsm
+++ b/toolkit/modules/addons/WebRequestUpload.jsm
@@ -187,16 +187,17 @@ function* getStreams(outerStream) {
  *        decoding errors.
  *
  * @returns {Map<string, Array<string>> | null}
  */
 function parseFormData(stream, channel, lenient = false) {
   const BUFFER_SIZE = 8192;
 
   let touchedStreams = new Set();
+  let converterStreams = [];
 
   /**
    * Creates a converter input stream from the given raw input stream,
    * and adds it to the list of streams to be rewound at the end of
    * parsing.
    *
    * Returns null if the given raw stream cannot be rewound.
    *
@@ -205,20 +206,22 @@ function parseFormData(stream, channel, 
    * @returns {ConverterInputStream | null}
    */
   function createTextStream(stream) {
     if (!(stream instanceof Ci.nsISeekableStream)) {
       return null;
     }
 
     touchedStreams.add(stream);
-    return ConverterInputStream(
+    let converterStream = ConverterInputStream(
       stream, "UTF-8", 0,
       lenient ? Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER
               : 0);
+    converterStreams.push(converterStream);
+    return converterStream;
   }
 
   /**
    * Reads a string of no more than the given length from the given text
    * stream.
    *
    * @param {ConverterInputStream} stream
    *        The stream to read.
@@ -377,16 +380,22 @@ function parseFormData(stream, channel, 
 
       case "application/x-www-form-urlencoded":
         return parseUrlEncoded(stream);
     }
   } finally {
     for (let stream of touchedStreams) {
       rewind(stream);
     }
+    for (let converterStream of converterStreams) {
+      // Release the reference to the underlying input stream, to prevent the
+      // destructor of nsConverterInputStream from closing the stream, which
+      // would cause uploads to break.
+      converterStream.init(null, null, 0, 0);
+    }
   }
 
   return null;
 }
 
 /**
  * Parses the form data of the given stream as either multipart/form-data or
  * x-www-form-urlencoded, and returns a map of its fields.