1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
|
// META: global=window,worker
// META: script=../resources/utils.js
// META: script=/common/utils.js
// META: script=/common/get-host-info.sub.js
const duplex = "half";
async function assertUpload(url, method, createBody, expectedBody) {
const requestInit = {method};
const body = createBody();
if (body) {
requestInit["body"] = body;
requestInit.duplex = "half";
}
const resp = await fetch(url, requestInit);
const text = await resp.text();
assert_equals(text, expectedBody);
}
function testUpload(desc, url, method, createBody, expectedBody) {
promise_test(async () => {
await assertUpload(url, method, createBody, expectedBody);
}, desc);
}
function createStream(chunks) {
return new ReadableStream({
start: (controller) => {
for (const chunk of chunks) {
controller.enqueue(chunk);
}
controller.close();
}
});
}
const url = RESOURCES_DIR + "echo-content.h2.py"
testUpload("Fetch with POST with empty ReadableStream", url,
"POST",
() => {
return new ReadableStream({start: controller => {
controller.close();
}})
},
"");
testUpload("Fetch with POST with ReadableStream", url,
"POST",
() => {
return new ReadableStream({start: controller => {
const encoder = new TextEncoder();
controller.enqueue(encoder.encode("Test"));
controller.close();
}})
},
"Test");
promise_test(async (test) => {
const body = new ReadableStream({start: controller => {
const encoder = new TextEncoder();
controller.enqueue(encoder.encode("Test"));
controller.close();
}});
const resp = await fetch(
"/fetch/connection-pool/resources/network-partition-key.py?"
+ `status=421&uuid=${token()}&partition_id=${self.origin}`
+ `&dispatch=check_partition&addcounter=true`,
{method: "POST", body: body, duplex});
assert_equals(resp.status, 421);
const text = await resp.text();
assert_equals(text, "ok. Request was sent 1 times. 1 connections were created.");
}, "Fetch with POST with ReadableStream on 421 response should return the response and not retry.");
promise_test(async (test) => {
const request = new Request('', {
body: new ReadableStream(),
method: 'POST',
duplex,
});
assert_equals(request.headers.get('Content-Type'), null, `Request should not have a content-type set`);
const response = await fetch('data:a/a;charset=utf-8,test', {
method: 'POST',
body: new ReadableStream(),
duplex,
});
assert_equals(await response.text(), 'test', `Response has correct body`);
}, "Feature detect for POST with ReadableStream");
promise_test(async (test) => {
const request = new Request('data:a/a;charset=utf-8,test', {
body: new ReadableStream(),
method: 'POST',
duplex,
});
assert_equals(request.headers.get('Content-Type'), null, `Request should not have a content-type set`);
const response = await fetch(request);
assert_equals(await response.text(), 'test', `Response has correct body`);
}, "Feature detect for POST with ReadableStream, using request object");
test(() => {
let duplexAccessed = false;
const request = new Request("", {
body: new ReadableStream(),
method: "POST",
get duplex() {
duplexAccessed = true;
return "half";
},
});
assert_equals(
request.headers.get("Content-Type"),
null,
`Request should not have a content-type set`
);
assert_true(duplexAccessed, `duplex dictionary property should be accessed`);
}, "Synchronous feature detect");
// The asserts the synchronousFeatureDetect isn't broken by a partial implementation.
// An earlier feature detect was broken by Safari implementing streaming bodies as part of Request,
// but it failed when passed to fetch().
// This tests ensures that UAs must not implement RequestInit.duplex and streaming request bodies without also implementing the fetch() parts.
promise_test(async () => {
let duplexAccessed = false;
const request = new Request("", {
body: new ReadableStream(),
method: "POST",
get duplex() {
duplexAccessed = true;
return "half";
},
});
const supported =
request.headers.get("Content-Type") === null && duplexAccessed;
// If the feature detect fails, assume the browser is being truthful (other tests pick up broken cases here)
if (!supported) return false;
await assertUpload(
url,
"POST",
() =>
new ReadableStream({
start: (controller) => {
const encoder = new TextEncoder();
controller.enqueue(encoder.encode("Test"));
controller.close();
},
}),
"Test"
);
}, "Synchronous feature detect fails if feature unsupported");
promise_test(async (t) => {
const body = createStream(["hello"]);
const method = "POST";
await promise_rejects_js(t, TypeError, fetch(url, { method, body, duplex }));
}, "Streaming upload with body containing a String");
promise_test(async (t) => {
const body = createStream([null]);
const method = "POST";
await promise_rejects_js(t, TypeError, fetch(url, { method, body, duplex }));
}, "Streaming upload with body containing null");
promise_test(async (t) => {
const body = createStream([33]);
const method = "POST";
await promise_rejects_js(t, TypeError, fetch(url, { method, body, duplex }));
}, "Streaming upload with body containing a number");
promise_test(async (t) => {
const url = "/fetch/api/resources/authentication.py?realm=test";
const body = createStream([]);
const method = "POST";
await promise_rejects_js(t, TypeError, fetch(url, { method, body, duplex }));
}, "Streaming upload should fail on a 401 response");
promise_test(async (t) => {
const abortMessage = 'foo abort';
let streamCancelPromise = new Promise(async res => {
var stream = new ReadableStream({
cancel: function(reason) {
res(reason);
}
});
let abortController = new AbortController();
let fetchPromise = promise_rejects_exactly(t, abortMessage, fetch('', {
method: 'POST',
body: stream,
duplex: 'half',
signal: abortController.signal
}));
abortController.abort(abortMessage);
await fetchPromise;
});
let cancelReason = await streamCancelPromise;
assert_equals(
cancelReason, abortMessage, 'ReadableStream.cancel should be called.');
}, 'ReadbleStream should be closed on signal.abort');
|