Compare commits

...

4 Commits

Author SHA1 Message Date
pfg
93ca834b40 comment fix 2025-06-11 15:24:22 -07:00
pfg
b7c693d965 test-tls-server-parent-constructor-options 2025-06-11 15:24:17 -07:00
pfg
26ffc1a78e test-tls-server-capture-rejection 2025-06-10 15:19:13 -07:00
pfg
d5711646c8 net: emit 'connection' event rather than storing connectionListener seperately + add captureRejectionSymbol 2025-06-10 15:18:22 -07:00
5 changed files with 143 additions and 21 deletions

View File

@@ -14,9 +14,9 @@ declare module "bun" {
}
/** $debug is a preprocessor macro that works like a templated console.log, and only runs in debug mode if you pass
* BUN_DEBUG_JS=<module>
* BUN_DEBUG_JS=1 or BUN_DEBUG_<MODULE>=1
*
* So to get node stream to log, you pass BUN_DEBUG_JS=stream or BUN_DEBUG_JS=node:stream
* So to get node stream to log, you pass BUN_DEBUG_STREAM=1 or BUN_DEBUG_NODE_STREAM=1
*
* This only works in debug builds, the log fn is completely removed in release builds.
*/

View File

@@ -360,8 +360,8 @@ const ServerHandlers: SocketHandler<NetSocket> = {
const self = socket.data as any as NetServer;
socket[kServerSocket] = self._handle;
const options = self[bunSocketServerOptions];
const { pauseOnConnect, connectionListener, [kSocketClass]: SClass, requestCert, rejectUnauthorized } = options;
const _socket = new SClass({}) as NetSocket | TLSSocket;
const { pauseOnConnect, allowHalfOpen, [kSocketClass]: SClass, requestCert, rejectUnauthorized } = options;
const _socket = new SClass({ allowHalfOpen }) as NetSocket | TLSSocket;
_socket.isServer = true;
_socket._requestCert = requestCert;
_socket._rejectUnauthorized = rejectUnauthorized;
@@ -405,16 +405,11 @@ const ServerHandlers: SocketHandler<NetSocket> = {
self._connections++;
_socket.server = self;
if (pauseOnConnect) {
if (pauseOnConnect && !isTLS) {
_socket.pause();
}
if (typeof connectionListener === "function") {
this.pauseOnConnect = pauseOnConnect;
if (!isTLS) {
connectionListener.$call(self, _socket);
}
}
this.pauseOnConnect = pauseOnConnect;
self.emit("connection", _socket);
// the duplex implementation start paused, so we resume when pauseOnConnect is falsy
if (!pauseOnConnect && !isTLS) {
@@ -455,19 +450,16 @@ const ServerHandlers: SocketHandler<NetSocket> = {
} else {
self.authorized = true;
}
const connectionListener = server[bunSocketServerOptions]?.connectionListener;
if (typeof connectionListener === "function") {
connectionListener.$call(server, self);
}
server.emit("secureConnection", self);
// after secureConnection event we emmit secure and secureConnect
self.emit("secure", self);
self.emit("secureConnect", verifyError);
if (server.pauseOnConnect) {
self.pause();
} else {
self.resume();
}
server.emit("secureConnection", self);
// after secureConnection event we emit secure and secureConnect
// in node, a 'secure' handler emits 'secureConnection'. but since there is no nextTick, a user program sees 'secureConnection' then 'secure'.
self.emit("secure", self);
self.emit("secureConnect", verifyError);
},
error(socket, error) {
const data = this.data;
@@ -2073,8 +2065,13 @@ function Server(options?, connectionListener?) {
if (typeof options === "function") {
connectionListener = options;
options = {};
this.on("connection", connectionListener);
} else if (options == null || typeof options === "object") {
options = { ...options };
if (typeof connectionListener === "function") {
this.on("connection", connectionListener);
}
} else {
throw $ERR_INVALID_ARG_TYPE("options", ["Object", "Function"], options);
}
@@ -2105,7 +2102,6 @@ function Server(options?, connectionListener?) {
this.pauseOnConnect = Boolean(pauseOnConnect);
this.noDelay = noDelay;
options.connectionListener = connectionListener;
this[bunSocketServerOptions] = options;
if (options.blockList) {
@@ -2442,6 +2438,16 @@ Server.prototype.getsockname = function getsockname(out) {
return out;
};
Server.prototype[EventEmitter.captureRejectionSymbol] = function (err, event, sock) {
switch (event) {
case "connection":
sock.destroy(err);
break;
default:
this.emit("error", err);
}
};
function emitErrorNextTick(self, error) {
self.emit("error", error);
}

View File

@@ -5,6 +5,7 @@ const { Duplex } = require("node:stream");
const addServerName = $newZigFunction("socket.zig", "jsAddServerName", 3);
const { throwNotImplemented } = require("internal/shared");
const { throwOnInvalidTLSArray, DEFAULT_CIPHERS, validateCiphers } = require("internal/tls");
const EventEmitter = require("node:events");
const { Server: NetServer, Socket: NetSocket } = net;
@@ -501,7 +502,11 @@ function Server(options, secureConnectionListener): void {
return new Server(options, secureConnectionListener);
}
NetServer.$apply(this, [options, secureConnectionListener]);
NetServer.$apply(this, [options, () => {}]);
if (secureConnectionListener) {
this.on("secureConnection", secureConnectionListener);
}
this.key = undefined;
this.cert = undefined;
@@ -631,6 +636,15 @@ function Server(options, secureConnectionListener): void {
this.setSecureContext(options);
}
$toClass(Server, "Server", NetServer);
Server.prototype[EventEmitter.captureRejectionSymbol] = function (err, event, sock) {
switch (event) {
case "secureConnection":
sock.destroy(err);
break;
default:
ReflectApply(net.Server.prototype[SymbolFor("nodejs.rejection")], this, [err, event, sock]);
}
};
function createServer(options, connectionListener) {
return new Server(options, connectionListener);

View File

@@ -0,0 +1,34 @@
'use strict';
const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
const assert = require('assert');
const events = require('events');
const fixtures = require('../common/fixtures');
const { createServer, connect } = require('tls');
const cert = fixtures.readKey('rsa_cert.crt');
const key = fixtures.readKey('rsa_private.pem');
events.captureRejections = true;
const server = createServer({ cert, key }, common.mustCall(async (sock) => {
server.close();
const _err = new Error('kaboom');
sock.on('error', common.mustCall((err) => {
assert.strictEqual(err, _err);
}));
throw _err;
}));
server.listen(0, common.mustCall(() => {
const sock = connect({
port: server.address().port,
host: server.address().host,
rejectUnauthorized: false
});
sock.on('close', common.mustCall());
}));

View File

@@ -0,0 +1,68 @@
'use strict';
const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
// Test that `tls.Server` constructor options are passed to the parent
// constructor.
const assert = require('assert');
const fixtures = require('../common/fixtures');
const tls = require('tls');
const options = {
key: fixtures.readKey('agent1-key.pem'),
cert: fixtures.readKey('agent1-cert.pem'),
};
{
const server = tls.createServer(options, common.mustCall((socket) => {
assert.strictEqual(socket.allowHalfOpen, false);
assert.strictEqual(socket.isPaused(), false);
}));
assert.strictEqual(server.allowHalfOpen, false);
assert.strictEqual(server.pauseOnConnect, false);
server.listen(0, common.mustCall(() => {
const socket = tls.connect({
port: server.address().port,
rejectUnauthorized: false
}, common.mustCall(() => {
socket.end();
}));
socket.on('close', () => {
server.close();
});
}));
}
{
const server = tls.createServer({
allowHalfOpen: true,
pauseOnConnect: true,
...options
}, common.mustCall((socket) => {
assert.strictEqual(socket.allowHalfOpen, true);
assert.strictEqual(socket.isPaused(), true);
socket.on('end', socket.end);
}));
assert.strictEqual(server.allowHalfOpen, true);
assert.strictEqual(server.pauseOnConnect, true);
server.listen(0, common.mustCall(() => {
const socket = tls.connect({
port: server.address().port,
rejectUnauthorized: false
}, common.mustCall(() => {
socket.end();
}));
socket.on('close', () => {
server.close();
});
}));
}