mirror of
https://github.com/denoland/deno.git
synced 2024-11-25 15:29:32 -05:00
fix(ext/node): server.close()
does graceful shutdown (#24184)
This commit is contained in:
parent
f7a31f4398
commit
1c871a8dc1
3 changed files with 128 additions and 14 deletions
|
@ -753,26 +753,52 @@ function serveHttpOn(context, addr, callback) {
|
||||||
PromisePrototypeCatch(callback(req), promiseErrorHandler);
|
PromisePrototypeCatch(callback(req), promiseErrorHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
if (!context.closing && !context.closed) {
|
if (!context.closing && !context.closed) {
|
||||||
context.closing = op_http_close(rid, false);
|
context.closing = await op_http_close(rid, false);
|
||||||
context.close();
|
context.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
await context.closing;
|
await context.closing;
|
||||||
|
} catch (error) {
|
||||||
|
if (ObjectPrototypeIsPrototypeOf(InterruptedPrototype, error)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ObjectPrototypeIsPrototypeOf(BadResourcePrototype, error)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
context.close();
|
context.close();
|
||||||
context.closed = true;
|
context.closed = true;
|
||||||
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
addr,
|
addr,
|
||||||
finished,
|
finished,
|
||||||
async shutdown() {
|
async shutdown() {
|
||||||
|
try {
|
||||||
if (!context.closing && !context.closed) {
|
if (!context.closing && !context.closed) {
|
||||||
// Shut this HTTP server down gracefully
|
// Shut this HTTP server down gracefully
|
||||||
context.closing = op_http_close(context.serverRid, true);
|
context.closing = op_http_close(context.serverRid, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
await context.closing;
|
await context.closing;
|
||||||
|
} catch (error) {
|
||||||
|
// The server was interrupted
|
||||||
|
if (ObjectPrototypeIsPrototypeOf(InterruptedPrototype, error)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ObjectPrototypeIsPrototypeOf(BadResourcePrototype, error)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
context.closed = true;
|
context.closed = true;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
ref() {
|
ref() {
|
||||||
ref = true;
|
ref = true;
|
||||||
|
|
|
@ -1775,8 +1775,12 @@ export class ServerImpl extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (listening && this.#ac) {
|
if (listening && this.#ac) {
|
||||||
|
if (this.#server) {
|
||||||
|
this.#server.shutdown();
|
||||||
|
} else if (this.#ac) {
|
||||||
this.#ac.abort();
|
this.#ac.abort();
|
||||||
this.#ac = undefined;
|
this.#ac = undefined;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this.#serveDeferred!.resolve();
|
this.#serveDeferred!.resolve();
|
||||||
}
|
}
|
||||||
|
@ -1785,6 +1789,26 @@ export class ServerImpl extends EventEmitter {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
closeAllConnections() {
|
||||||
|
if (this.#hasClosed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.#ac) {
|
||||||
|
this.#ac.abort();
|
||||||
|
this.#ac = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
closeIdleConnections() {
|
||||||
|
if (this.#hasClosed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.#server) {
|
||||||
|
this.#server.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
address() {
|
address() {
|
||||||
return {
|
return {
|
||||||
port: this.#addr.port,
|
port: this.#addr.port,
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import EventEmitter from "node:events";
|
import EventEmitter from "node:events";
|
||||||
import http, { type RequestOptions } from "node:http";
|
import http, { type RequestOptions } from "node:http";
|
||||||
|
import url from "node:url";
|
||||||
import https from "node:https";
|
import https from "node:https";
|
||||||
import net from "node:net";
|
import net from "node:net";
|
||||||
import { assert, assertEquals, fail } from "@std/assert/mod.ts";
|
import { assert, assertEquals, fail } from "@std/assert/mod.ts";
|
||||||
|
@ -1040,3 +1041,66 @@ Deno.test("[node/http] ServerResponse default status code 200", () => {
|
||||||
Deno.test("[node/http] maxHeaderSize is defined", () => {
|
Deno.test("[node/http] maxHeaderSize is defined", () => {
|
||||||
assertEquals(http.maxHeaderSize, 16_384);
|
assertEquals(http.maxHeaderSize, 16_384);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Deno.test("[node/http] server graceful close", async () => {
|
||||||
|
const server = http.createServer(function (_, response) {
|
||||||
|
response.writeHead(200, {});
|
||||||
|
response.end("ok");
|
||||||
|
server.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
const { promise, resolve } = Promise.withResolvers<void>();
|
||||||
|
server.listen(0, function () {
|
||||||
|
// deno-lint-ignore no-explicit-any
|
||||||
|
const port = (server.address() as any).port;
|
||||||
|
const testURL = url.parse(
|
||||||
|
`http://localhost:${port}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
http.request(testURL, function (response) {
|
||||||
|
assertEquals(response.statusCode, 200);
|
||||||
|
response.on("data", function () {});
|
||||||
|
response.on("end", function () {
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
}).end();
|
||||||
|
});
|
||||||
|
|
||||||
|
await promise;
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.test("[node/http] server closeAllConnections shutdown", async () => {
|
||||||
|
const server = http.createServer((_req, res) => {
|
||||||
|
res.writeHead(200, { "Content-Type": "application/json" });
|
||||||
|
res.end(JSON.stringify({
|
||||||
|
data: "Hello World!",
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
server.listen(0);
|
||||||
|
const { promise, resolve } = Promise.withResolvers<void>();
|
||||||
|
setTimeout(() => {
|
||||||
|
server.close(() => resolve());
|
||||||
|
server.closeAllConnections();
|
||||||
|
}, 2000);
|
||||||
|
|
||||||
|
await promise;
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.test("[node/http] server closeIdleConnections shutdown", async () => {
|
||||||
|
const server = http.createServer({ keepAliveTimeout: 60000 }, (_req, res) => {
|
||||||
|
res.writeHead(200, { "Content-Type": "application/json" });
|
||||||
|
res.end(JSON.stringify({
|
||||||
|
data: "Hello World!",
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
server.listen(0);
|
||||||
|
const { promise, resolve } = Promise.withResolvers<void>();
|
||||||
|
setTimeout(() => {
|
||||||
|
server.close(() => resolve());
|
||||||
|
server.closeIdleConnections();
|
||||||
|
}, 2000);
|
||||||
|
|
||||||
|
await promise;
|
||||||
|
});
|
||||||
|
|
Loading…
Reference in a new issue