mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-07 08:39:22 +00:00
LibWeb/FileAPI: Handle an aborted stream in Blob::get_stream() close
The streams AO that we were calling to close the stream would assert if it was not in a readable state. This version of close is exported publicly in the streams specification, and properly handles this situation. Fixes a crash in the imported test, and happens to fix some others!
This commit is contained in:
parent
667a568526
commit
caf959f06c
Notes:
github-actions[bot]
2025-05-31 13:14:02 +00:00
Author: https://github.com/shannonbooth
Commit: caf959f06c
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4941
5 changed files with 132 additions and 9 deletions
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022-2024, Kenneth Myhra <kennethmyhra@serenityos.org>
|
* Copyright (c) 2022-2024, Kenneth Myhra <kennethmyhra@serenityos.org>
|
||||||
* Copyright (c) 2023, Shannon Booth <shannon@serenityos.org>
|
* Copyright (c) 2023-2025, Shannon Booth <shannon@serenityos.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -357,7 +357,7 @@ GC::Ref<Streams::ReadableStream> Blob::get_stream()
|
||||||
// FIXME: Spec bug: https://github.com/w3c/FileAPI/issues/206
|
// FIXME: Spec bug: https://github.com/w3c/FileAPI/issues/206
|
||||||
//
|
//
|
||||||
// We need to close the stream so that the stream will finish reading.
|
// We need to close the stream so that the stream will finish reading.
|
||||||
Streams::readable_stream_close(*stream);
|
stream->close();
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
Harness status: OK
|
||||||
|
|
||||||
|
Found 18 tests
|
||||||
|
|
||||||
|
18 Pass
|
||||||
|
Pass Calling arrayBuffer() on an aborted request
|
||||||
|
Pass Aborting a request after calling arrayBuffer()
|
||||||
|
Pass Calling arrayBuffer() on an aborted consumed empty request
|
||||||
|
Pass Calling arrayBuffer() on an aborted consumed nonempty request
|
||||||
|
Pass Calling blob() on an aborted request
|
||||||
|
Pass Aborting a request after calling blob()
|
||||||
|
Pass Calling blob() on an aborted consumed empty request
|
||||||
|
Pass Calling blob() on an aborted consumed nonempty request
|
||||||
|
Pass Calling formData() on an aborted request
|
||||||
|
Pass Aborting a request after calling formData()
|
||||||
|
Pass Calling formData() on an aborted consumed nonempty request
|
||||||
|
Pass Calling json() on an aborted request
|
||||||
|
Pass Aborting a request after calling json()
|
||||||
|
Pass Calling json() on an aborted consumed nonempty request
|
||||||
|
Pass Calling text() on an aborted request
|
||||||
|
Pass Aborting a request after calling text()
|
||||||
|
Pass Calling text() on an aborted consumed empty request
|
||||||
|
Pass Calling text() on an aborted consumed nonempty request
|
|
@ -2,8 +2,8 @@ Harness status: OK
|
||||||
|
|
||||||
Found 40 tests
|
Found 40 tests
|
||||||
|
|
||||||
28 Pass
|
33 Pass
|
||||||
12 Fail
|
7 Fail
|
||||||
Pass Consume response's body: from text to text
|
Pass Consume response's body: from text to text
|
||||||
Pass Consume response's body: from text to blob
|
Pass Consume response's body: from text to blob
|
||||||
Pass Consume response's body: from text to arrayBuffer
|
Pass Consume response's body: from text to arrayBuffer
|
||||||
|
@ -13,13 +13,13 @@ Fail Consume response's body: from text with correct multipart type to formData
|
||||||
Pass Consume response's body: from text without correct multipart type to formData (error case)
|
Pass Consume response's body: from text without correct multipart type to formData (error case)
|
||||||
Pass Consume response's body: from text with correct urlencoded type to formData
|
Pass Consume response's body: from text with correct urlencoded type to formData
|
||||||
Pass Consume response's body: from text without correct urlencoded type to formData (error case)
|
Pass Consume response's body: from text without correct urlencoded type to formData (error case)
|
||||||
Fail Consume response's body: from blob to blob
|
Pass Consume response's body: from blob to blob
|
||||||
Fail Consume response's body: from blob to text
|
Pass Consume response's body: from blob to text
|
||||||
Fail Consume response's body: from blob to arrayBuffer
|
Pass Consume response's body: from blob to arrayBuffer
|
||||||
Fail Consume response's body: from blob to json
|
Pass Consume response's body: from blob to json
|
||||||
Fail Consume response's body: from blob with correct multipart type to formData
|
Fail Consume response's body: from blob with correct multipart type to formData
|
||||||
Pass Consume response's body: from blob without correct multipart type to formData (error case)
|
Pass Consume response's body: from blob without correct multipart type to formData (error case)
|
||||||
Fail Consume response's body: from blob with correct urlencoded type to formData
|
Pass Consume response's body: from blob with correct urlencoded type to formData
|
||||||
Pass Consume response's body: from blob without correct urlencoded type to formData (error case)
|
Pass Consume response's body: from blob without correct urlencoded type to formData (error case)
|
||||||
Pass Consume response's body: from FormData to formData
|
Pass Consume response's body: from FormData to formData
|
||||||
Pass Consume response's body: from FormData without correct type to formData (error case)
|
Pass Consume response's body: from FormData without correct type to formData (error case)
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
<!doctype html>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<meta name="timeout" content="long">
|
||||||
|
<script>
|
||||||
|
self.GLOBAL = {
|
||||||
|
isWindow: function() { return true; },
|
||||||
|
isWorker: function() { return false; },
|
||||||
|
isShadowRealm: function() { return false; },
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<script src="../../../resources/testharness.js"></script>
|
||||||
|
<script src="../../../resources/testharnessreport.js"></script>
|
||||||
|
|
||||||
|
<div id=log></div>
|
||||||
|
<script src="../../../fetch/api/abort/request.any.js"></script>
|
|
@ -0,0 +1,85 @@
|
||||||
|
// META: timeout=long
|
||||||
|
// META: global=window,worker
|
||||||
|
|
||||||
|
const BODY_FUNCTION_AND_DATA = {
|
||||||
|
arrayBuffer: null,
|
||||||
|
blob: null,
|
||||||
|
formData: new FormData(),
|
||||||
|
json: new Blob(["{}"]),
|
||||||
|
text: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const [bodyFunction, body] of Object.entries(BODY_FUNCTION_AND_DATA)) {
|
||||||
|
promise_test(async () => {
|
||||||
|
const controller = new AbortController();
|
||||||
|
const signal = controller.signal;
|
||||||
|
const request = new Request("../resources/data.json", {
|
||||||
|
method: "post",
|
||||||
|
signal,
|
||||||
|
body,
|
||||||
|
});
|
||||||
|
|
||||||
|
controller.abort();
|
||||||
|
await request[bodyFunction]();
|
||||||
|
assert_true(
|
||||||
|
true,
|
||||||
|
`An aborted request should still be able to run ${bodyFunction}()`
|
||||||
|
);
|
||||||
|
}, `Calling ${bodyFunction}() on an aborted request`);
|
||||||
|
|
||||||
|
promise_test(async () => {
|
||||||
|
const controller = new AbortController();
|
||||||
|
const signal = controller.signal;
|
||||||
|
const request = new Request("../resources/data.json", {
|
||||||
|
method: "post",
|
||||||
|
signal,
|
||||||
|
body,
|
||||||
|
});
|
||||||
|
|
||||||
|
const p = request[bodyFunction]();
|
||||||
|
controller.abort();
|
||||||
|
await p;
|
||||||
|
assert_true(
|
||||||
|
true,
|
||||||
|
`An aborted request should still be able to run ${bodyFunction}()`
|
||||||
|
);
|
||||||
|
}, `Aborting a request after calling ${bodyFunction}()`);
|
||||||
|
|
||||||
|
if (!body) {
|
||||||
|
promise_test(async () => {
|
||||||
|
const controller = new AbortController();
|
||||||
|
const signal = controller.signal;
|
||||||
|
const request = new Request("../resources/data.json", {
|
||||||
|
method: "post",
|
||||||
|
signal,
|
||||||
|
body,
|
||||||
|
});
|
||||||
|
|
||||||
|
// consuming happens synchronously, so don't wait
|
||||||
|
fetch(request).catch(() => {});
|
||||||
|
|
||||||
|
controller.abort();
|
||||||
|
await request[bodyFunction]();
|
||||||
|
assert_true(
|
||||||
|
true,
|
||||||
|
`An aborted consumed request should still be able to run ${bodyFunction}() when empty`
|
||||||
|
);
|
||||||
|
}, `Calling ${bodyFunction}() on an aborted consumed empty request`);
|
||||||
|
}
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const controller = new AbortController();
|
||||||
|
const signal = controller.signal;
|
||||||
|
const request = new Request("../resources/data.json", {
|
||||||
|
method: "post",
|
||||||
|
signal,
|
||||||
|
body: body || new Blob(["foo"]),
|
||||||
|
});
|
||||||
|
|
||||||
|
// consuming happens synchronously, so don't wait
|
||||||
|
fetch(request).catch(() => {});
|
||||||
|
|
||||||
|
controller.abort();
|
||||||
|
await promise_rejects_js(t, TypeError, request[bodyFunction]());
|
||||||
|
}, `Calling ${bodyFunction}() on an aborted consumed nonempty request`);
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue