Skip to content

Commit f4749b4

Browse files
Merge pull request #706 from denoland/main
Create a new pull request by comparing changes across two branches
2 parents 80aec1d + 637b1d5 commit f4749b4

File tree

8 files changed

+84
-25
lines changed

8 files changed

+84
-25
lines changed

ext/cache/lib.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ pub enum CacheError {
3333
}
3434

3535
#[derive(Clone)]
36-
pub struct CreateCache<C: Cache + 'static>(pub Arc<dyn Fn() -> C>);
36+
pub struct CreateCache<C: Cache + 'static>(
37+
pub Arc<dyn Fn() -> Result<C, CacheError>>,
38+
);
3739

3840
deno_core::extension!(deno_cache,
3941
deps = [ deno_webidl, deno_web, deno_url, deno_fetch ],
@@ -231,7 +233,7 @@ where
231233
if let Some(cache) = state.try_borrow::<CA>() {
232234
Ok(cache.clone())
233235
} else if let Some(create_cache) = state.try_borrow::<CreateCache<CA>>() {
234-
let cache = create_cache.0();
236+
let cache = create_cache.0()?;
235237
state.put(cache);
236238
Ok(state.borrow::<CA>().clone())
237239
} else {

ext/cache/sqlite.rs

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ pub struct SqliteBackedCache {
4242
}
4343

4444
impl SqliteBackedCache {
45-
pub fn new(cache_storage_dir: PathBuf) -> Self {
45+
pub fn new(cache_storage_dir: PathBuf) -> Result<Self, CacheError> {
4646
{
4747
std::fs::create_dir_all(&cache_storage_dir)
4848
.expect("failed to create cache dir");
@@ -57,18 +57,14 @@ impl SqliteBackedCache {
5757
PRAGMA synchronous=NORMAL;
5858
PRAGMA optimize;
5959
";
60-
connection
61-
.execute_batch(initial_pragmas)
62-
.expect("failed to execute pragmas");
63-
connection
64-
.execute(
65-
"CREATE TABLE IF NOT EXISTS cache_storage (
60+
connection.execute_batch(initial_pragmas)?;
61+
connection.execute(
62+
"CREATE TABLE IF NOT EXISTS cache_storage (
6663
id INTEGER PRIMARY KEY,
6764
cache_name TEXT NOT NULL UNIQUE
6865
)",
69-
(),
70-
)
71-
.expect("failed to create cache_storage table");
66+
(),
67+
)?;
7268
connection
7369
.execute(
7470
"CREATE TABLE IF NOT EXISTS request_response_list (
@@ -86,12 +82,11 @@ impl SqliteBackedCache {
8682
UNIQUE (cache_id, request_url)
8783
)",
8884
(),
89-
)
90-
.expect("failed to create request_response_list table");
91-
SqliteBackedCache {
85+
)?;
86+
Ok(SqliteBackedCache {
9287
connection: Arc::new(Mutex::new(connection)),
9388
cache_storage_dir,
94-
}
89+
})
9590
}
9691
}
9792
}

ext/fetch/23_request.js

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -269,19 +269,25 @@ class Request {
269269
/** @type {AbortSignal} */
270270
get [_signal]() {
271271
const signal = this[_signalCache];
272-
// This signal not been created yet, and the request is still in progress
273-
if (signal === undefined) {
272+
// This signal has not been created yet, but the request has already completed
273+
if (signal === false) {
274274
const signal = newSignal();
275275
this[_signalCache] = signal;
276+
signal[signalAbort](signalAbortError);
276277
return signal;
277278
}
278-
// This signal has not been created yet, but the request has already completed
279-
if (signal === false) {
279+
280+
// This signal not been created yet, and the request is still in progress
281+
if (signal === undefined) {
280282
const signal = newSignal();
281283
this[_signalCache] = signal;
282-
signal[signalAbort](signalAbortError);
283284
return signal;
284285
}
286+
287+
if (!signal.aborted && this[_request].isCancelled) {
288+
signal[signalAbort](signalAbortError);
289+
}
290+
285291
return signal;
286292
}
287293
get [_mimeType]() {

ext/http/00_serve.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
op_http_cancel,
1212
op_http_close,
1313
op_http_close_after_finish,
14+
op_http_get_request_cancelled,
1415
op_http_get_request_headers,
1516
op_http_get_request_method_and_url,
1617
op_http_read_request_body,
@@ -373,6 +374,13 @@ class InnerRequest {
373374
get external() {
374375
return this.#external;
375376
}
377+
378+
get isCancelled() {
379+
if (this.#external === null) {
380+
return true;
381+
}
382+
return op_http_get_request_cancelled(this.#external);
383+
}
376384
}
377385

378386
class CallbackContext {

ext/http/http_next.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,14 @@ fn set_response(
700700
http.complete();
701701
}
702702

703+
#[op2(fast)]
704+
pub fn op_http_get_request_cancelled(external: *const c_void) -> bool {
705+
let http =
706+
// SAFETY: op is called with external.
707+
unsafe { clone_external!(external, "op_http_get_request_cancelled") };
708+
http.cancelled()
709+
}
710+
703711
/// Returned promise resolves when body streaming finishes.
704712
/// Call [`op_http_close_after_finish`] when done with the external.
705713
#[op2(async)]

ext/http/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ deno_core::extension!(
113113
http_next::op_http_get_request_header,
114114
http_next::op_http_get_request_headers,
115115
http_next::op_http_get_request_method_and_url<HTTP>,
116+
http_next::op_http_get_request_cancelled,
116117
http_next::op_http_read_request_body,
117118
http_next::op_http_serve_on<HTTP>,
118119
http_next::op_http_serve<HTTP>,

tests/unit/command_test.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,27 +14,31 @@ Deno.test(
1414
const enc = new TextEncoder();
1515
const cwd = await Deno.makeTempDir({ prefix: "deno_command_test" });
1616

17+
const exitCodeFileLock = "deno_was_here.lock";
1718
const exitCodeFile = "deno_was_here";
1819
const programFile = "poll_exit.ts";
1920
const program = `
21+
const file = await Deno.open("${exitCodeFileLock}", { write: true, create: true });
2022
async function tryExit() {
23+
await file.lock(true);
2124
try {
2225
const code = parseInt(await Deno.readTextFile("${exitCodeFile}"));
2326
Deno.exit(code);
2427
} catch {
2528
// Retry if we got here before deno wrote the file.
2629
setTimeout(tryExit, 0.01);
30+
} finally {
31+
await file.unlock();
2732
}
2833
}
2934
3035
tryExit();
3136
`;
32-
3337
Deno.writeFileSync(`${cwd}/${programFile}`, enc.encode(program));
3438

3539
const command = new Deno.Command(Deno.execPath(), {
3640
cwd,
37-
args: ["run", "--allow-read", programFile],
41+
args: ["run", "-RW", programFile],
3842
stdout: "inherit",
3943
stderr: "inherit",
4044
});
@@ -43,12 +47,18 @@ tryExit();
4347
// Write the expected exit code *after* starting deno.
4448
// This is how we verify that `Child` is actually asynchronous.
4549
const code = 84;
46-
Deno.writeFileSync(`${cwd}/${exitCodeFile}`, enc.encode(`${code}`));
4750

51+
await using file = await Deno.open(`${cwd}/${exitCodeFileLock}`, {
52+
write: true,
53+
create: true,
54+
});
55+
await file.lock(true);
56+
Deno.writeFileSync(`${cwd}/${exitCodeFile}`, enc.encode(`${code}`));
57+
await file.unlock();
4858
const status = await child.status;
4959
await Deno.remove(cwd, { recursive: true });
50-
assertEquals(status.success, false);
5160
assertEquals(status.code, code);
61+
assertEquals(status.success, false);
5262
assertEquals(status.signal, null);
5363
},
5464
);

tests/unit/serve_test.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4270,3 +4270,32 @@ Deno.test({
42704270
assertEquals(hostname, "0.0.0.0");
42714271
await server.shutdown();
42724272
});
4273+
4274+
Deno.test({
4275+
name: "AbortSignal aborted when request is cancelled",
4276+
}, async () => {
4277+
const { promise, resolve } = Promise.withResolvers<void>();
4278+
4279+
let cancelled = false;
4280+
4281+
const server = Deno.serve({
4282+
hostname: "0.0.0.0",
4283+
port: servePort,
4284+
onListen: () => resolve(),
4285+
}, async (request) => {
4286+
request.signal.addEventListener("abort", () => cancelled = true);
4287+
assert(!request.signal.aborted);
4288+
await new Promise((resolve) => setTimeout(resolve, 3000)); // abort during waiting
4289+
assert(request.signal.aborted);
4290+
return new Response("Ok");
4291+
});
4292+
4293+
await promise;
4294+
await fetch(`http://localhost:${servePort}/`, {
4295+
signal: AbortSignal.timeout(1000),
4296+
}).catch(() => {});
4297+
4298+
await server.shutdown();
4299+
4300+
assert(cancelled);
4301+
});

0 commit comments

Comments
 (0)