mirror of
https://github.com/denoland/deno.git
synced 2024-11-21 15:04:11 -05:00
fix(ext/node/net): emit error
before close
when connection is refused (#24656)
This commit is contained in:
parent
29934d558c
commit
199a8ca4c5
5 changed files with 48 additions and 16 deletions
|
@ -531,16 +531,17 @@ export class Socket extends EventEmitter {
|
||||||
healthCheck(this);
|
healthCheck(this);
|
||||||
stopReceiving(this);
|
stopReceiving(this);
|
||||||
|
|
||||||
state.handle!.close();
|
state.handle!.close(() => {
|
||||||
|
// Deviates from the Node implementation to avoid leaking the timer ops at 'close' event
|
||||||
|
defaultTriggerAsyncIdScope(
|
||||||
|
this[asyncIdSymbol],
|
||||||
|
nextTick,
|
||||||
|
socketCloseNT,
|
||||||
|
this,
|
||||||
|
);
|
||||||
|
});
|
||||||
state.handle = null;
|
state.handle = null;
|
||||||
|
|
||||||
defaultTriggerAsyncIdScope(
|
|
||||||
this[asyncIdSymbol],
|
|
||||||
nextTick,
|
|
||||||
socketCloseNT,
|
|
||||||
this,
|
|
||||||
);
|
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,13 +25,12 @@
|
||||||
// - https://github.com/nodejs/node/blob/master/src/handle_wrap.h
|
// - https://github.com/nodejs/node/blob/master/src/handle_wrap.h
|
||||||
|
|
||||||
// TODO(petamoriken): enable prefer-primordials for node polyfills
|
// TODO(petamoriken): enable prefer-primordials for node polyfills
|
||||||
// deno-lint-ignore-file prefer-primordials
|
|
||||||
|
|
||||||
import { unreachable } from "ext:deno_node/_util/asserts.ts";
|
import { unreachable } from "ext:deno_node/_util/asserts.ts";
|
||||||
import {
|
import {
|
||||||
AsyncWrap,
|
AsyncWrap,
|
||||||
providerType,
|
providerType,
|
||||||
} from "ext:deno_node/internal_binding/async_wrap.ts";
|
} from "ext:deno_node/internal_binding/async_wrap.ts";
|
||||||
|
import { nextTick } from "ext:deno_node/_process/process.ts";
|
||||||
|
|
||||||
export class HandleWrap extends AsyncWrap {
|
export class HandleWrap extends AsyncWrap {
|
||||||
constructor(provider: providerType) {
|
constructor(provider: providerType) {
|
||||||
|
@ -40,7 +39,9 @@ export class HandleWrap extends AsyncWrap {
|
||||||
|
|
||||||
close(cb: () => void = () => {}) {
|
close(cb: () => void = () => {}) {
|
||||||
this._onClose();
|
this._onClose();
|
||||||
queueMicrotask(cb);
|
// We need to delay 'cb' at least 2 ticks to avoid "close" event happenning before "error" event in net.Socket
|
||||||
|
// See https://github.com/denoland/deno/pull/24656 for more information
|
||||||
|
nextTick(nextTick, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
ref() {
|
ref() {
|
||||||
|
|
|
@ -846,7 +846,10 @@ Deno.test(
|
||||||
"[node/http] client upgrade",
|
"[node/http] client upgrade",
|
||||||
{ permissions: { net: true } },
|
{ permissions: { net: true } },
|
||||||
async () => {
|
async () => {
|
||||||
const { promise, resolve } = Promise.withResolvers<void>();
|
const { promise: serverClosed, resolve: resolveServer } = Promise
|
||||||
|
.withResolvers<void>();
|
||||||
|
const { promise: socketClosed, resolve: resolveSocket } = Promise
|
||||||
|
.withResolvers<void>();
|
||||||
const server = http.createServer((req, res) => {
|
const server = http.createServer((req, res) => {
|
||||||
// @ts-ignore: It exists on TLSSocket
|
// @ts-ignore: It exists on TLSSocket
|
||||||
assert(!req.socket.encrypted);
|
assert(!req.socket.encrypted);
|
||||||
|
@ -887,12 +890,16 @@ Deno.test(
|
||||||
// @ts-ignore it's a socket for real
|
// @ts-ignore it's a socket for real
|
||||||
serverSocket!.end();
|
serverSocket!.end();
|
||||||
server.close(() => {
|
server.close(() => {
|
||||||
resolve();
|
resolveServer();
|
||||||
|
});
|
||||||
|
socket.on("close", () => {
|
||||||
|
resolveSocket();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
await promise;
|
await serverClosed;
|
||||||
|
await socketClosed;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { assert, assertEquals } from "@std/assert/mod.ts";
|
||||||
import * as path from "@std/path/mod.ts";
|
import * as path from "@std/path/mod.ts";
|
||||||
import * as http from "node:http";
|
import * as http from "node:http";
|
||||||
|
|
||||||
Deno.test("[node/net] close event emits after error event", async () => {
|
Deno.test("[node/net] close event emits after error event - when host is not found", async () => {
|
||||||
const socket = net.createConnection(27009, "doesnotexist");
|
const socket = net.createConnection(27009, "doesnotexist");
|
||||||
const events: ("error" | "close")[] = [];
|
const events: ("error" | "close")[] = [];
|
||||||
const errorEmitted = Promise.withResolvers<void>();
|
const errorEmitted = Promise.withResolvers<void>();
|
||||||
|
@ -24,6 +24,25 @@ Deno.test("[node/net] close event emits after error event", async () => {
|
||||||
assertEquals(events, ["error", "close"]);
|
assertEquals(events, ["error", "close"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Deno.test("[node/net] close event emits after error event - when connection is refused", async () => {
|
||||||
|
const socket = net.createConnection(27009, "127.0.0.1");
|
||||||
|
const events: ("error" | "close")[] = [];
|
||||||
|
const errorEmitted = Promise.withResolvers<void>();
|
||||||
|
const closeEmitted = Promise.withResolvers<void>();
|
||||||
|
socket.once("error", () => {
|
||||||
|
events.push("error");
|
||||||
|
errorEmitted.resolve();
|
||||||
|
});
|
||||||
|
socket.once("close", () => {
|
||||||
|
events.push("close");
|
||||||
|
closeEmitted.resolve();
|
||||||
|
});
|
||||||
|
await Promise.all([errorEmitted.promise, closeEmitted.promise]);
|
||||||
|
|
||||||
|
// `error` happens before `close`
|
||||||
|
assertEquals(events, ["error", "close"]);
|
||||||
|
});
|
||||||
|
|
||||||
Deno.test("[node/net] the port is available immediately after close callback", async () => {
|
Deno.test("[node/net] the port is available immediately after close callback", async () => {
|
||||||
const deferred = Promise.withResolvers<void>();
|
const deferred = Promise.withResolvers<void>();
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,7 @@ for (
|
||||||
conn.close();
|
conn.close();
|
||||||
outgoing.destroy();
|
outgoing.destroy();
|
||||||
listener.close();
|
listener.close();
|
||||||
|
await new Promise((resolve) => outgoing.on("close", resolve));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,6 +94,7 @@ Connection: close
|
||||||
|
|
||||||
// https://github.com/denoland/deno/pull/20120
|
// https://github.com/denoland/deno/pull/20120
|
||||||
Deno.test("tls.connect mid-read tcp->tls upgrade", async () => {
|
Deno.test("tls.connect mid-read tcp->tls upgrade", async () => {
|
||||||
|
const { promise, resolve } = Promise.withResolvers<void>();
|
||||||
const ctl = new AbortController();
|
const ctl = new AbortController();
|
||||||
const serve = Deno.serve({
|
const serve = Deno.serve({
|
||||||
port: 8443,
|
port: 8443,
|
||||||
|
@ -119,8 +121,10 @@ Deno.test("tls.connect mid-read tcp->tls upgrade", async () => {
|
||||||
conn.destroy();
|
conn.destroy();
|
||||||
ctl.abort();
|
ctl.abort();
|
||||||
});
|
});
|
||||||
|
conn.on("close", resolve);
|
||||||
|
|
||||||
await serve.finished;
|
await serve.finished;
|
||||||
|
await promise;
|
||||||
});
|
});
|
||||||
|
|
||||||
Deno.test("tls.createServer creates a TLS server", async () => {
|
Deno.test("tls.createServer creates a TLS server", async () => {
|
||||||
|
@ -136,6 +140,7 @@ Deno.test("tls.createServer creates a TLS server", async () => {
|
||||||
socket.destroy();
|
socket.destroy();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
socket.on("close", () => deferred.resolve());
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
server.listen(0, async () => {
|
server.listen(0, async () => {
|
||||||
|
@ -166,7 +171,6 @@ Deno.test("tls.createServer creates a TLS server", async () => {
|
||||||
|
|
||||||
conn.close();
|
conn.close();
|
||||||
server.close();
|
server.close();
|
||||||
deferred.resolve();
|
|
||||||
});
|
});
|
||||||
await deferred.promise;
|
await deferred.promise;
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue