tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

response-consume.html (20189B)


      1 <!doctype html>
      2 <html>
      3  <head>
      4    <meta charset="utf-8">
      5    <title>Response consume</title>
      6    <meta name="help" href="https://fetch.spec.whatwg.org/#response">
      7    <meta name="help" href="https://fetch.spec.whatwg.org/#body-mixin">
      8    <meta name="author" title="Canon Research France" href="https://www.crf.canon.fr">
      9    <script src="/resources/testharness.js"></script>
     10    <script src="/resources/testharnessreport.js"></script>
     11    <script src="../resources/utils.js"></script>
     12  </head>
     13  <body>
     14    <script>
     15    function blobToFormDataResponse(name, blob) {
     16      var formData = new FormData();
     17      formData.append(name, blob);
     18      return new Response(formData);
     19    }
     20 
     21    function readBlobAsArrayBuffer(blob) {
     22      return new Promise(function(resolve, reject) {
     23        var reader = new FileReader();
     24        reader.onload = function(evt) {
     25          resolve(reader.result);
     26        };
     27        reader.onerror = function(evt) {
     28          reject("Blob's reader failed");
     29        };
     30        reader.readAsArrayBuffer(blob);
     31      });
     32    }
     33 
     34    function blobToTypeViaFetch(blob) {
     35      var url = URL.createObjectURL(blob);
     36      return fetch(url).then(function(response) {
     37        return response.headers.get('Content-Type');
     38      });
     39    }
     40 
     41    function responsePromise(body, responseInit) {
     42      return new Promise(function(resolve, reject) {
     43        resolve(new Response(body, responseInit));
     44      });
     45    }
     46 
     47    function responseStringToMultipartFormTextData(response, name, value) {
     48        assert_true(response.headers.has("Content-Type"), "Response contains Content-Type header");
     49        var boundaryMatches = response.headers.get("Content-Type").match(/;\s*boundary=("?)([^";\s]*)\1/);
     50        assert_true(!!boundaryMatches, "Response contains boundary parameter");
     51        return stringToMultipartFormTextData(boundaryMatches[2], name, value);
     52    }
     53 
     54    function streamResponsePromise(streamData, responseInit) {
     55      return new Promise(function(resolve, reject) {
     56        var stream = new ReadableStream({
     57          start: function(controller) {
     58            controller.enqueue(stringToArray(streamData));
     59            controller.close();
     60          }
     61        });
     62        resolve(new Response(stream, responseInit));
     63      });
     64    }
     65 
     66    function stringToMultipartFormTextData(multipartBoundary, name, value) {
     67      return ('--' + multipartBoundary + '\r\n' +
     68              'Content-Disposition: form-data;name="' + name + '"\r\n' +
     69              '\r\n' +
     70              value + '\r\n' +
     71              '--' + multipartBoundary + '--\r\n');
     72    }
     73 
     74    function checkBodyText(test, response, expectedBody) {
     75      return response.text().then( function(bodyAsText) {
     76        assert_equals(bodyAsText, expectedBody, "Retrieve and verify response's body");
     77        assert_true(response.bodyUsed, "body as text: bodyUsed turned true");
     78      });
     79    }
     80 
     81    function checkBodyBlob(test, response, expectedBody, expectedType) {
     82      return response.blob().then(function(bodyAsBlob) {
     83        assert_equals(bodyAsBlob.type, expectedType || "text/plain", "Blob body type should be computed from the response Content-Type");
     84 
     85        var promise = blobToTypeViaFetch(bodyAsBlob).then(function(type) {
     86          assert_equals(type, expectedType || "text/plain", 'Type via blob URL');
     87          return new Promise( function (resolve, reject) {
     88            var reader = new FileReader();
     89            reader.onload = function(evt) {
     90              resolve(reader.result)
     91            };
     92            reader.onerror = function () {
     93              reject("Blob's reader failed");
     94            };
     95            reader.readAsText(bodyAsBlob);
     96          });
     97        });
     98        return promise.then(function(body) {
     99          assert_equals(body, expectedBody, "Retrieve and verify response's body");
    100          assert_true(response.bodyUsed, "body as blob: bodyUsed turned true");
    101        });
    102      });
    103    }
    104 
    105    function checkBodyArrayBuffer(test, response, expectedBody) {
    106      return response.arrayBuffer().then( function(bodyAsArrayBuffer) {
    107        validateBufferFromString(bodyAsArrayBuffer, expectedBody, "Retrieve and verify response's body");
    108        assert_true(response.bodyUsed, "body as arrayBuffer: bodyUsed turned true");
    109      });
    110    }
    111 
    112    function checkBodyJSON(test, response, expectedBody) {
    113      return response.json().then(function(bodyAsJSON) {
    114        var strBody = JSON.stringify(bodyAsJSON)
    115        assert_equals(strBody, expectedBody, "Retrieve and verify response's body");
    116        assert_true(response.bodyUsed, "body as json: bodyUsed turned true");
    117      });
    118    }
    119 
    120    function checkBodyFormDataMultipart(test, response, expectedBody) {
    121      return response.formData().then(function(bodyAsFormData) {
    122        assert_true(bodyAsFormData instanceof FormData, "Should receive a FormData");
    123        var entryName = "name";
    124        var strBody = responseStringToMultipartFormTextData(response, entryName, bodyAsFormData.get(entryName));
    125        assert_equals(strBody, expectedBody, "Retrieve and verify response's body");
    126        assert_true(response.bodyUsed, "body as formData: bodyUsed turned true");
    127     });
    128    }
    129 
    130    function checkBodyFormDataUrlencoded(test, response, expectedBody) {
    131      return response.formData().then(function(bodyAsFormData) {
    132        assert_true(bodyAsFormData instanceof FormData, "Should receive a FormData");
    133        var entryName = "name";
    134        var strBody = entryName + "=" + bodyAsFormData.get(entryName);
    135        assert_equals(strBody, expectedBody, "Retrieve and verify response's body");
    136        assert_true(response.bodyUsed, "body as formData: bodyUsed turned true");
    137     });
    138    }
    139 
    140    function checkBodyFormDataError(test, response, expectedBody) {
    141      return promise_rejects_js(test, TypeError, response.formData()).then(function() {
    142        assert_true(response.bodyUsed, "body as formData: bodyUsed turned true");
    143      });
    144    }
    145 
    146    function checkResponseBody(responsePromise, expectedBody, checkFunction, bodyTypes) {
    147      promise_test(function(test) {
    148        return responsePromise.then(function(response) {
    149          assert_false(response.bodyUsed, "bodyUsed is false at init");
    150          return checkFunction(test, response, expectedBody);
    151        });
    152      }, "Consume response's body: " + bodyTypes);
    153    }
    154 
    155    var textData = JSON.stringify("This is response's body");
    156    var textResponseInit = { "headers": [["Content-Type", "text/PLAIN"]] };
    157    var blob = new Blob([textData], { "type": "application/octet-stream" });
    158    var multipartBoundary = "boundary-" + Math.random();
    159    var formData = new FormData();
    160    var formTextResponseInit = { "headers": [["Content-Type", 'multipart/FORM-data; boundary="' + multipartBoundary + '"']] };
    161    var formTextData = stringToMultipartFormTextData(multipartBoundary, "name", textData);
    162    var formBlob = new Blob([formTextData]);
    163    var urlSearchParamsData = "name=value";
    164    var urlSearchParams = new URLSearchParams(urlSearchParamsData);
    165    var urlSearchParamsType = "application/x-www-form-urlencoded;charset=UTF-8";
    166    var urlSearchParamsResponseInit = { "headers": [["Content-Type", urlSearchParamsType]] };
    167    var urlSearchParamsBlob = new Blob([urlSearchParamsData], { "type": urlSearchParamsType });
    168    formData.append("name", textData);
    169 
    170    // https://fetch.spec.whatwg.org/#concept-body-package-data
    171    // "UTF-8 decoded without BOM" is used for formData(), either in
    172    // "multipart/form-data" and "application/x-www-form-urlencoded" cases,
    173    // so BOMs in the values should be kept.
    174    // (The "application/x-www-form-urlencoded" cases are tested in
    175    // url/urlencoded-parser.any.js)
    176    var textDataWithBom = "\uFEFFquick\uFEFFfox\uFEFF";
    177    var formTextDataWithBom = stringToMultipartFormTextData(multipartBoundary, "name", textDataWithBom);
    178    var formTextDataWithBomExpectedForMultipartFormData = stringToMultipartFormTextData(multipartBoundary, "name", textDataWithBom);
    179 
    180    checkResponseBody(responsePromise(textData, textResponseInit), textData, checkBodyText, "from text to text");
    181    checkResponseBody(responsePromise(textData, textResponseInit), textData, checkBodyBlob, "from text to blob");
    182    checkResponseBody(responsePromise(textData, textResponseInit), textData, checkBodyArrayBuffer, "from text to arrayBuffer");
    183    checkResponseBody(responsePromise(textData, textResponseInit), textData, checkBodyJSON, "from text to json");
    184    checkResponseBody(responsePromise(formTextData, formTextResponseInit), formTextData, checkBodyFormDataMultipart, "from text with correct multipart type to formData");
    185    checkResponseBody(responsePromise(formTextDataWithBom, formTextResponseInit), formTextDataWithBomExpectedForMultipartFormData, checkBodyFormDataMultipart, "from text with correct multipart type to formData with BOM");
    186    checkResponseBody(responsePromise(formTextData, textResponseInit), undefined, checkBodyFormDataError, "from text without correct multipart type to formData (error case)");
    187    checkResponseBody(responsePromise(urlSearchParamsData, urlSearchParamsResponseInit), urlSearchParamsData, checkBodyFormDataUrlencoded, "from text with correct urlencoded type to formData");
    188    checkResponseBody(responsePromise(urlSearchParamsData, textResponseInit), undefined, checkBodyFormDataError, "from text without correct urlencoded type to formData (error case)");
    189 
    190    checkResponseBody(responsePromise(blob, textResponseInit), textData, checkBodyBlob, "from blob to blob");
    191    checkResponseBody(responsePromise(blob), textData, checkBodyText, "from blob to text");
    192    checkResponseBody(responsePromise(blob), textData, checkBodyArrayBuffer, "from blob to arrayBuffer");
    193    checkResponseBody(responsePromise(blob), textData, checkBodyJSON, "from blob to json");
    194    checkResponseBody(responsePromise(formBlob, formTextResponseInit), formTextData, checkBodyFormDataMultipart, "from blob with correct multipart type to formData");
    195    checkResponseBody(responsePromise(formBlob, textResponseInit), undefined, checkBodyFormDataError, "from blob without correct multipart type to formData (error case)");
    196    checkResponseBody(responsePromise(urlSearchParamsBlob, urlSearchParamsResponseInit), urlSearchParamsData, checkBodyFormDataUrlencoded, "from blob with correct urlencoded type to formData");
    197    checkResponseBody(responsePromise(urlSearchParamsBlob, textResponseInit), undefined, checkBodyFormDataError, "from blob without correct urlencoded type to formData (error case)");
    198 
    199    function checkFormDataResponseBody(responsePromise, expectedName, expectedValue, checkFunction, bodyTypes) {
    200      promise_test(function(test) {
    201        return responsePromise.then(function(response) {
    202          assert_false(response.bodyUsed, "bodyUsed is false at init");
    203          var expectedBody = responseStringToMultipartFormTextData(response, expectedName, expectedValue);
    204          return Promise.resolve().then(function() {
    205            if (checkFunction === checkBodyFormDataMultipart)
    206              return expectedBody;
    207            // Modify expectedBody to use the same spacing for
    208            // Content-Disposition parameters as Response and FormData does.
    209            var response2 = new Response(formData);
    210            return response2.text().then(function(formDataAsText) {
    211              var reName = /[ \t]*;[ \t]*name=/;
    212              var nameMatches = formDataAsText.match(reName);
    213              return expectedBody.replace(reName, nameMatches[0]);
    214            });
    215          }).then(function(expectedBody) {
    216            return checkFunction(test, response, expectedBody);
    217          });
    218        });
    219      }, "Consume response's body: " + bodyTypes);
    220    }
    221 
    222    checkFormDataResponseBody(responsePromise(formData), "name", textData, checkBodyFormDataMultipart, "from FormData to formData");
    223    checkResponseBody(responsePromise(formData, textResponseInit), undefined, checkBodyFormDataError, "from FormData without correct type to formData (error case)");
    224    checkFormDataResponseBody(responsePromise(formData), "name", textData, function(test, response, expectedBody) { return checkBodyBlob(test, response, expectedBody, response.headers.get('Content-Type').toLowerCase()); }, "from FormData to blob");
    225    checkFormDataResponseBody(responsePromise(formData), "name", textData, checkBodyText, "from FormData to text");
    226    checkFormDataResponseBody(responsePromise(formData), "name", textData, checkBodyArrayBuffer, "from FormData to arrayBuffer");
    227 
    228    checkResponseBody(responsePromise(urlSearchParams), urlSearchParamsData, checkBodyFormDataUrlencoded, "from URLSearchParams to formData");
    229    checkResponseBody(responsePromise(urlSearchParams, textResponseInit), urlSearchParamsData, checkBodyFormDataError, "from URLSearchParams without correct type to formData (error case)");
    230    checkResponseBody(responsePromise(urlSearchParams), urlSearchParamsData, function(test, response, expectedBody) { return checkBodyBlob(test, response, expectedBody, "application/x-www-form-urlencoded;charset=utf-8"); }, "from URLSearchParams to blob");
    231    checkResponseBody(responsePromise(urlSearchParams), urlSearchParamsData, checkBodyText, "from URLSearchParams to text");
    232    checkResponseBody(responsePromise(urlSearchParams), urlSearchParamsData, checkBodyArrayBuffer, "from URLSearchParams to arrayBuffer");
    233 
    234    checkResponseBody(streamResponsePromise(textData, textResponseInit), textData, checkBodyBlob, "from stream to blob");
    235    checkResponseBody(streamResponsePromise(textData), textData, checkBodyText, "from stream to text");
    236    checkResponseBody(streamResponsePromise(textData), textData, checkBodyArrayBuffer, "from stream to arrayBuffer");
    237    checkResponseBody(streamResponsePromise(textData), textData, checkBodyJSON, "from stream to json");
    238    checkResponseBody(streamResponsePromise(formTextData, formTextResponseInit), formTextData, checkBodyFormDataMultipart, "from stream with correct multipart type to formData");
    239    checkResponseBody(streamResponsePromise(formTextData), formTextData, checkBodyFormDataError, "from stream without correct multipart type to formData (error case)");
    240    checkResponseBody(streamResponsePromise(urlSearchParamsData, urlSearchParamsResponseInit), urlSearchParamsData, checkBodyFormDataUrlencoded, "from stream with correct urlencoded type to formData");
    241    checkResponseBody(streamResponsePromise(urlSearchParamsData), urlSearchParamsData, checkBodyFormDataError, "from stream without correct urlencoded type to formData (error case)");
    242 
    243    checkResponseBody(fetch("../resources/top.txt"), "top", checkBodyBlob, "from fetch to blob");
    244    checkResponseBody(fetch("../resources/top.txt"), "top", checkBodyText, "from fetch to text");
    245    checkResponseBody(fetch("../resources/top.txt"), "top", checkBodyArrayBuffer, "from fetch to arrayBuffer");
    246    checkResponseBody(fetch("../resources/top.txt"), "top", checkBodyFormDataError, "from fetch without correct type to formData (error case)");
    247 
    248    promise_test(function(test) {
    249      var response = new Response(new Blob([
    250        "--boundary\r\n",
    251        "Content-Disposition: form-data; name=string\r\n",
    252        "\r\nvalue", new Uint8Array([0xC2, 0xA0]), "1\r\n",
    253        "--boundary\r\n",
    254        "Content-Disposition: form-data; name=string-with-default-charset\r\n",
    255        "Content-Type: text/plain; charset=utf-8\r\n",
    256        "\r\nvalue", new Uint8Array([0xC2, 0xA0]), "2\r\n",
    257        "--boundary\r\n",
    258        "Content-Disposition: form-data; name=string-with-non-default-charset\r\n",
    259        "Content-Type: text/plain; charset=iso-8859-1\r\n",
    260        "\r\nvalue", new Uint8Array([0xC2, 0xA0]), "3\r\n",
    261        "--boundary\r\n",
    262        "Content-Disposition: form-data; name=string-with-non-default-type\r\n",
    263        "Content-Type: application/octet-stream\r\n",
    264        "\r\nvalue", new Uint8Array([0xC2, 0xA0]), "4\r\n",
    265        "--boundary\r\n",
    266        "Content-Disposition: form-data; name=file; filename=file1\r\n",
    267        "Content-Type: application/octet-stream; x-param=x-value\r\n",
    268        "\r\n", new Uint8Array([5, 0x0, 0xFF]), "\r\n",
    269        "--boundary\r\n",
    270        "Content-Disposition: form-data; name=\"file-without-type\"; filename=\"file2\"\r\n",
    271        "\r\n", new Uint8Array([6, 0x0, 0x7F, 0xFF]), "\r\n",
    272        "--boundary--\r\n"
    273      ]), { "headers": [["Content-Type", 'multipart/form-data; boundary="boundary"']] });
    274      return response.formData().then(function(bodyAsFormData) {
    275        // Non-file parts must always be decoded using utf-8 encoding.
    276        assert_equals(bodyAsFormData.get("string"), "value\u00A01", "Retrieve and verify response's 1st entry value");
    277        assert_equals(bodyAsFormData.get("string-with-default-charset"), "value\u00A02", "Retrieve and verify response's 2nd entry value");
    278        assert_equals(bodyAsFormData.get("string-with-non-default-charset"), "value\u00A03", "Retrieve and verify response's 3rd entry value");
    279        assert_equals(bodyAsFormData.get("string-with-non-default-type"), "value\u00A04", "Retrieve and verify response's 4th entry value");
    280        // The name of a File must be taken from the filename parameter in
    281        // the Content-Disposition header field.
    282        assert_equals(bodyAsFormData.get("file").name, "file1", "Retrieve and verify response's 5th entry name property");
    283        assert_equals(bodyAsFormData.get("file-without-type").name, "file2", "Retrieve and verify response's 6th entry name property");
    284        // The type of a File must be taken from the Content-Type header field
    285        // which defaults to "text/plain".
    286        assert_equals(bodyAsFormData.get("file").type, "application/octet-stream; x-param=x-value", "Retrieve and verify response's 5th entry type property");
    287        assert_equals(bodyAsFormData.get("file-without-type").type, "text/plain", "Retrieve and verify response's 6th entry type property");
    288 
    289        return Promise.resolve().then(function() {
    290          return blobToFormDataResponse("file", bodyAsFormData.get("file")).text().then(function(bodyAsText) {
    291            // Verify that filename, name and type are preserved.
    292            assert_regexp_match(bodyAsText, /\r\nContent-Disposition: *form-data;([^\r\n]*;)* *filename=("?)file1\2[;\r]/i, "Retrieve and verify response's 5th entry filename parameter");
    293            assert_regexp_match(bodyAsText, /\r\nContent-Disposition: *form-data;([^\r\n]*;)* *name=("?)file\2[;\r]/i, "Retrieve and verify response's 5th entry name parameter");
    294            assert_regexp_match(bodyAsText, /\r\nContent-Type: *application\/octet-stream; x-param=x-value\r\n/i, "Retrieve and verify response's 5th entry type field");
    295            // Verify that the content is preserved.
    296            return readBlobAsArrayBuffer(bodyAsFormData.get("file")).then(function(arrayBuffer) {
    297              assert_array_equals(new Uint8Array(arrayBuffer), new Uint8Array([5, 0x0, 0xFF]), "Retrieve and verify response's 5th entry content");
    298            });
    299          });
    300        }).then(function() {
    301          return blobToFormDataResponse("file-without-type", bodyAsFormData.get("file-without-type")).text().then(function(bodyAsText) {
    302            // Verify that filename, name and type are preserved.
    303            assert_regexp_match(bodyAsText, /\r\nContent-Disposition: *form-data;([^\r\n]*;)* *filename=("?)file2\2[;\r]/i, "Retrieve and verify response's 6th entry filename parameter");
    304            assert_regexp_match(bodyAsText, /\r\nContent-Disposition: *form-data;([^\r\n]*;)* *name=("?)file-without-type\2[;\r]/i, "Retrieve and verify response's 6th entry name parameter");
    305            assert_regexp_match(bodyAsText, /\r\nContent-Type: *text\/plain\r\n/i, "Retrieve and verify response's 6th entry type field");
    306            // Verify that the content is preserved.
    307            return readBlobAsArrayBuffer(bodyAsFormData.get("file-without-type")).then(function(arrayBuffer) {
    308              assert_array_equals(new Uint8Array(arrayBuffer), new Uint8Array([6, 0x0, 0x7F, 0xFF]), "Retrieve and verify response's 6th entry content");
    309            });
    310          });
    311        });
    312      });
    313    }, "Consume response's body: from multipart form data blob to formData");
    314 
    315    </script>
    316  </body>
    317 </html>