Compare commits

...

3 Commits

Author SHA1 Message Date
Jarred-Sumner
fe89330204 Sync Node.js tests with upstream 2025-05-30 00:11:47 +00:00
Jarred Sumner
6aff0d89b1 Add http test and handle early fetch 2025-05-29 17:10:38 -07:00
Meghan Denny
576f66c149 fix test-net-server-drop-connections.js (#19995) 2025-05-29 13:55:25 -07:00
4 changed files with 126 additions and 10 deletions

View File

@@ -93,7 +93,7 @@ function ClientRequest(input, options, cb) {
const pushChunk = chunk => {
this[kBodyChunks].push(chunk);
if (writeCount > 1) {
if (writeCount > 0) {
startFetch();
}
resolveNextChunk?.(false);

View File

@@ -88,7 +88,6 @@ function isIP(s): 0 | 4 | 6 {
}
const bunTlsSymbol = Symbol.for("::buntls::");
const bunSocketServerConnections = Symbol.for("::bunnetserverconnections::");
const bunSocketServerOptions = Symbol.for("::bunnetserveroptions::");
const owner_symbol = Symbol("owner_symbol");
@@ -339,7 +338,7 @@ const ServerHandlers: SocketHandler = {
const data = this.data;
if (!data) return;
data.server[bunSocketServerConnections]--;
data.server._connections--;
{
if (!data[kclosed]) {
data[kclosed] = true;
@@ -385,7 +384,7 @@ const ServerHandlers: SocketHandler = {
return;
}
}
if (self.maxConnections && self[bunSocketServerConnections] >= self.maxConnections) {
if (self.maxConnections != null && self._connections >= self.maxConnections) {
const data = {
localAddress: _socket.localAddress,
localPort: _socket.localPort || this.localPort,
@@ -404,7 +403,7 @@ const ServerHandlers: SocketHandler = {
const bunTLS = _socket[bunTlsSymbol];
const isTLS = typeof bunTLS === "function";
self[bunSocketServerConnections]++;
self._connections++;
if (pauseOnConnect) {
_socket.pause();
@@ -2075,7 +2074,6 @@ function Server(options?, connectionListener?) {
// https://nodejs.org/api/net.html#netcreateserveroptions-connectionlistener
const {
maxConnections, //
allowHalfOpen = false,
keepAlive = false,
keepAliveInitialDelay = 0,
@@ -2092,7 +2090,6 @@ function Server(options?, connectionListener?) {
this._unref = false;
this.listeningId = 1;
this[bunSocketServerConnections] = 0;
this[bunSocketServerOptions] = undefined;
this.allowHalfOpen = allowHalfOpen;
this.keepAlive = keepAlive;
@@ -2100,7 +2097,6 @@ function Server(options?, connectionListener?) {
this.highWaterMark = highWaterMark;
this.pauseOnConnect = Boolean(pauseOnConnect);
this.noDelay = noDelay;
this.maxConnections = Number.isSafeInteger(maxConnections) && maxConnections > 0 ? maxConnections : 0;
options.connectionListener = connectionListener;
this[bunSocketServerOptions] = options;
@@ -2163,7 +2159,7 @@ Server.prototype[Symbol.asyncDispose] = function () {
};
Server.prototype._emitCloseIfDrained = function _emitCloseIfDrained() {
if (this._handle || this[bunSocketServerConnections] > 0) {
if (this._handle || this._connections > 0) {
return;
}
process.nextTick(() => {
@@ -2192,7 +2188,7 @@ Server.prototype.getConnections = function getConnections(callback) {
//in Bun case we will never error on getConnections
//node only errors if in the middle of the couting the server got disconnected, what never happens in Bun
//if disconnected will only pass null as well and 0 connected
callback(null, this._handle ? this[bunSocketServerConnections] : 0);
callback(null, this._handle ? this._connections : 0);
}
return this;
};

View File

@@ -0,0 +1,79 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict';
const common = require('../common');
// Verify that ECONNRESET is raised when writing to a http request
// where the server has ended the socket.
const assert = require('assert');
const http = require('http');
const kResponseDestroyed = Symbol('kResponseDestroyed');
const server = http.createServer(function(req, res) {
req.on('data', common.mustCall(function() {
res.destroy();
server.emit(kResponseDestroyed);
}));
});
server.listen(0, function() {
const req = http.request({
port: this.address().port,
path: '/',
method: 'POST'
});
server.once(kResponseDestroyed, common.mustCall(function() {
req.write('hello');
}));
req.on('error', common.mustCall(function(er) {
assert.strictEqual(req.res, null);
switch (er.code) {
// This is the expected case
case 'ECONNRESET':
break;
// On Windows, this sometimes manifests as ECONNABORTED
case 'ECONNABORTED':
break;
// This test is timing sensitive so an EPIPE is not out of the question.
// It should be infrequent, given the 50 ms timeout, but not impossible.
case 'EPIPE':
break;
default:
// Write to a torn down client should RESET or ABORT
assert.fail(`Unexpected error code ${er.code}`);
}
assert.strictEqual(req.outputData.length, 0);
server.close();
}));
req.on('response', common.mustNotCall());
req.write('hello', common.mustSucceed());
});

View File

@@ -0,0 +1,41 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const net = require('net');
let firstSocket;
const dormantServer = net.createServer(common.mustNotCall());
const server = net.createServer(common.mustCall((socket) => {
firstSocket = socket;
}));
dormantServer.maxConnections = 0;
server.maxConnections = 1;
dormantServer.on('drop', common.mustCall((data) => {
assert.strictEqual(!!data.localAddress, true);
assert.strictEqual(!!data.localPort, true);
assert.strictEqual(!!data.remoteAddress, true);
assert.strictEqual(!!data.remotePort, true);
assert.strictEqual(!!data.remoteFamily, true);
dormantServer.close();
}));
server.on('drop', common.mustCall((data) => {
assert.strictEqual(!!data.localAddress, true);
assert.strictEqual(!!data.localPort, true);
assert.strictEqual(!!data.remoteAddress, true);
assert.strictEqual(!!data.remotePort, true);
assert.strictEqual(!!data.remoteFamily, true);
firstSocket.destroy();
server.close();
}));
dormantServer.listen(0, () => {
net.createConnection(dormantServer.address().port);
});
server.listen(0, () => {
net.createConnection(server.address().port);
net.createConnection(server.address().port);
});