diff --git a/fetch/content-length/api-and-duplicate-headers.any.js b/fetch/content-length/api-and-duplicate-headers.any.js new file mode 100644 index 00000000000000..8015289f8d661c --- /dev/null +++ b/fetch/content-length/api-and-duplicate-headers.any.js @@ -0,0 +1,23 @@ +promise_test(async t => { + const response = await fetch("resources/identical-duplicates.asis"); + assert_equals(response.statusText, "BLAH"); + assert_equals(response.headers.get("test"), "x, x"); + assert_equals(response.headers.get("content-type"), "text/plain, text/plain"); + assert_equals(response.headers.get("content-length"), "6, 6"); + const text = await response.text(); + assert_equals(text, "Test.\n"); +}, "fetch() and duplicate Content-Length/Content-Type headers"); + +async_test(t => { + const xhr = new XMLHttpRequest(); + xhr.open("GET", "resources/identical-duplicates.asis"); + xhr.send(); + xhr.onload = t.step_func_done(() => { + assert_equals(xhr.statusText, "BLAH"); + assert_equals(xhr.getResponseHeader("test"), "x, x"); + assert_equals(xhr.getResponseHeader("content-type"), "text/plain, text/plain"); + assert_equals(xhr.getResponseHeader("content-length"), "6, 6"); + assert_equals(xhr.getAllResponseHeaders(), "content-length: 6, 6\r\ncontent-type: text/plain, text/plain\r\ntest: x, x\r\n"); + assert_equals(xhr.responseText, "Test.\n"); + }); +}, "XMLHttpRequest and duplicate Content-Length/Content-Type headers"); diff --git a/fetch/content-length/resources/identical-duplicates.asis b/fetch/content-length/resources/identical-duplicates.asis new file mode 100644 index 00000000000000..f38c9a4b8a05ae --- /dev/null +++ b/fetch/content-length/resources/identical-duplicates.asis @@ -0,0 +1,9 @@ +HTTP/1.1 200 BLAH +Test: x +Test: x +Content-Type: text/plain +Content-Type: text/plain +Content-Length: 6 +Content-Length: 6 + +Test. diff --git a/service-workers/service-worker/resources/xhr-content-length-worker.js b/service-workers/service-worker/resources/xhr-content-length-worker.js new file mode 100644 index 00000000000000..604deece2d252d --- /dev/null +++ b/service-workers/service-worker/resources/xhr-content-length-worker.js @@ -0,0 +1,22 @@ +// Service worker for the xhr-content-length test. + +self.addEventListener("fetch", event => { + const url = new URL(event.request.url); + const type = url.searchParams.get("type"); + + if (type === "no-content-length") { + event.respondWith(new Response("Hello!")); + } + + if (type === "larger-content-length") { + event.respondWith(new Response("meeeeh", { headers: [["Content-Length", "10000"]] })); + } + + if (type === "double-content-length") { + event.respondWith(new Response("meeeeh", { headers: [["Content-Length", "10000"], ["Content-Length", "10000"]] })); + } + + if (type === "bogus-content-length") { + event.respondWith(new Response("meeeeh", { headers: [["Content-Length", "test"]] })); + } +}); diff --git a/service-workers/service-worker/xhr-content-length.window.js b/service-workers/service-worker/xhr-content-length.window.js new file mode 100644 index 00000000000000..1ae320e9c3c571 --- /dev/null +++ b/service-workers/service-worker/xhr-content-length.window.js @@ -0,0 +1,55 @@ +// META: script=resources/test-helpers.sub.js + +let frame; + +promise_test(async (t) => { + const scope = "resources/empty.html"; + const script = "resources/xhr-content-length-worker.js"; + const registration = await service_worker_unregister_and_register(t, script, scope); + await wait_for_state(t, registration.installing, "activated"); + frame = await with_iframe(scope); +}, "Setup"); + +promise_test(async t => { + const xhr = new frame.contentWindow.XMLHttpRequest(); + xhr.open("GET", "test?type=no-content-length"); + xhr.send(); + const event = await new Promise(resolve => xhr.onload = resolve); + assert_equals(xhr.getResponseHeader("content-length"), null); + assert_false(event.lengthComputable); + assert_equals(event.total, 0); + assert_equals(event.loaded, xhr.responseText.length); +}, `Synthetic response without Content-Length header`); + +promise_test(async t => { + const xhr = new frame.contentWindow.XMLHttpRequest(); + xhr.open("GET", "test?type=larger-content-length"); + xhr.send(); + const event = await new Promise(resolve => xhr.onload = resolve); + assert_equals(xhr.getResponseHeader("content-length"), "10000"); + assert_true(event.lengthComputable); + assert_equals(event.total, 10000); + assert_equals(event.loaded, xhr.responseText.length); +}, `Synthetic response with Content-Length header with value larger than response body length`); + +promise_test(async t => { + const xhr = new frame.contentWindow.XMLHttpRequest(); + xhr.open("GET", "test?type=double-content-length"); + xhr.send(); + const event = await new Promise(resolve => xhr.onload = resolve); + assert_equals(xhr.getResponseHeader("content-length"), "10000, 10000"); + assert_true(event.lengthComputable); + assert_equals(event.total, 10000); + assert_equals(event.loaded, xhr.responseText.length); +}, `Synthetic response with two Content-Length headers value larger than response body length`); + +promise_test(async t => { + const xhr = new frame.contentWindow.XMLHttpRequest(); + xhr.open("GET", "test?type=bogus-content-length"); + xhr.send(); + const event = await new Promise(resolve => xhr.onload = resolve); + assert_equals(xhr.getResponseHeader("content-length"), "test"); + assert_false(event.lengthComputable); + assert_equals(event.total, 0); + assert_equals(event.loaded, xhr.responseText.length); +}, `Synthetic response with bogus Content-Length header`); diff --git a/xhr/event-upload-progress.any.js b/xhr/event-upload-progress.any.js index 87223c172d50bb..6fe659193b58fc 100644 --- a/xhr/event-upload-progress.any.js +++ b/xhr/event-upload-progress.any.js @@ -7,10 +7,14 @@ const remote = get_host_info().HTTP_REMOTE_ORIGIN + "/xhr/resources/corsenabled. [remote, redirect].forEach(url => { async_test(test => { const client = new XMLHttpRequest(); - client.upload.onprogress = test.step_func_done(); + const data = "On time: " + url; + client.upload.onprogress = test.step_func_done(e => { + assert_true(e.lengthComputable); + assert_equals(e.total, data.length); + }); client.onload = test.unreached_func(); client.open("POST", url); - client.send("On time: " + url); + client.send(data); }, "Upload events registered on time (" + url + ")"); }); diff --git a/xhr/request-content-length.any.js b/xhr/request-content-length.any.js new file mode 100644 index 00000000000000..054d2cce9d27ab --- /dev/null +++ b/xhr/request-content-length.any.js @@ -0,0 +1,31 @@ +async_test(test => { + const client = new XMLHttpRequest(); + const data = "This is 22 bytes long."; + let happened = false; + client.upload.onprogress = test.step_func(e => { + assert_true(e.lengthComputable); + assert_equals(e.total, data.length); + happened = true; + }); + client.onload = test.step_func_done(() => { + assert_true(happened); + assert_true(client.responseText.includes(`Content-Length: ${data.length}`)); + }); + client.open("POST", "resources/echo-headers.py"); + client.send(data); +}, "Uploads need to set the Content-Length header"); + +async_test(test => { + const client = new XMLHttpRequest(); + const data = "blah"; + const url = URL.createObjectURL(new Blob([data])); + client.open("GET", url); + client.send(); + client.onload = test.step_func_done(e => { + assert_true(e.lengthComputable); + assert_equals(e.total, data.length); + assert_equals(e.loaded, data.length); + assert_equals(client.responseText, data); + assert_equals(client.getResponseHeader("Content-Length"), String(data.length)); + }); +}, "Fetched blob: URLs set the Content-Length header");