Compare commits

..

1 Commits

Author SHA1 Message Date
Meghan Denny
c6e3381346 fix test-net-connect-reset-after-destroy.js 2025-05-28 22:45:50 -07:00
18 changed files with 198 additions and 510 deletions

26
.cursor/Dockerfile Normal file
View File

@@ -0,0 +1,26 @@
FROM ghcr.io/oven-sh/bun-development-docker-image:prebuilt
# Create a non-root user for development
RUN useradd -m -s /bin/bash ubuntu
# Create workspace directory and give ubuntu user access
RUN mkdir -p /workspace && \
chown -R ubuntu:ubuntu /workspace && \
# Allow ubuntu user to use sudo without password for development tasks
apt-get update && apt-get install -y sudo && \
echo "ubuntu ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers && \
rm -rf /var/lib/apt/lists/*
# Set the user and working directory
USER ubuntu
WORKDIR /workspace/bun
# Configure Git for the ubuntu user (agents often need this)
RUN git config --global user.name "Background Agent" && \
git config --global user.email "agent@cursor.com" && \
git config --global init.defaultBranch main
# Ensure PATH includes Bun binaries for the ubuntu user
ENV PATH="/workspace/bun/build/debug:/workspace/bun/build/release:/usr/local/bin:${PATH}"
RUN git pull

View File

@@ -1,3 +1,7 @@
{
"build": {
"dockerfile": "Dockerfile",
"context": "."
},
"terminals": []
}

View File

@@ -858,8 +858,7 @@ function getSshKeys() {
const sshFiles = readdirSync(sshPath, { withFileTypes: true, encoding: "utf-8" });
const publicPaths = sshFiles
.filter(entry => entry.isFile() && entry.name.endsWith(".pub"))
.map(({ name }) => join(sshPath, name))
.filter(path => !readFile(path, { cache: true }).startsWith("ssh-ed25519"));
.map(({ name }) => join(sshPath, name));
sshKeys.push(
...publicPaths.map(publicPath => ({

View File

@@ -2030,14 +2030,13 @@ enum class BunProcessStdinFdType : int32_t {
extern "C" BunProcessStdinFdType Bun__Process__getStdinFdType(void*, int fd);
extern "C" void Bun__ForceFileSinkToBeSynchronousForProcessObjectStdio(JSC::JSGlobalObject*, JSC::EncodedJSValue);
static JSValue constructStdioWriteStream(JSC::JSGlobalObject* globalObject, JSC::JSObject* processObject, int fd)
static JSValue constructStdioWriteStream(JSC::JSGlobalObject* globalObject, int fd)
{
auto& vm = JSC::getVM(globalObject);
auto scope = DECLARE_CATCH_SCOPE(vm);
JSC::JSFunction* getStdioWriteStream = JSC::JSFunction::create(vm, globalObject, processObjectInternalsGetStdioWriteStreamCodeGenerator(vm), globalObject);
JSC::MarkedArgumentBuffer args;
args.append(processObject);
args.append(JSC::jsNumber(fd));
args.append(jsBoolean(bun_stdio_tty[fd]));
BunProcessStdinFdType fdType = Bun__Process__getStdinFdType(Bun::vm(vm), fd);
@@ -2046,11 +2045,8 @@ static JSValue constructStdioWriteStream(JSC::JSGlobalObject* globalObject, JSC:
JSC::CallData callData = JSC::getCallData(getStdioWriteStream);
auto result = JSC::profiledCall(globalObject, ProfilingReason::API, getStdioWriteStream, callData, globalObject->globalThis(), args);
if (auto* exception = scope.exception()) {
Zig::GlobalObject::reportUncaughtExceptionAtEventLoop(globalObject, exception);
scope.clearException();
return jsUndefined();
}
scope.assertNoExceptionExceptTermination();
CLEAR_AND_RETURN_IF_EXCEPTION(scope, jsUndefined());
ASSERT_WITH_MESSAGE(JSC::isJSArray(result), "Expected an array from getStdioWriteStream");
JSC::JSArray* resultObject = JSC::jsCast<JSC::JSArray*>(result);
@@ -2081,12 +2077,12 @@ static JSValue constructStdioWriteStream(JSC::JSGlobalObject* globalObject, JSC:
static JSValue constructStdout(VM& vm, JSObject* processObject)
{
return constructStdioWriteStream(processObject->globalObject(), processObject, 1);
return constructStdioWriteStream(processObject->globalObject(), 1);
}
static JSValue constructStderr(VM& vm, JSObject* processObject)
{
return constructStdioWriteStream(processObject->globalObject(), processObject, 2);
return constructStdioWriteStream(processObject->globalObject(), 2);
}
#if OS(WINDOWS)
@@ -2096,22 +2092,17 @@ static JSValue constructStderr(VM& vm, JSObject* processObject)
static JSValue constructStdin(VM& vm, JSObject* processObject)
{
auto* globalObject = processObject->globalObject();
auto scope = DECLARE_CATCH_SCOPE(vm);
JSC::JSFunction* getStdinStream = JSC::JSFunction::create(vm, globalObject, processObjectInternalsGetStdinStreamCodeGenerator(vm), globalObject);
auto scope = DECLARE_THROW_SCOPE(vm);
JSC::JSFunction* getStdioWriteStream = JSC::JSFunction::create(vm, globalObject, processObjectInternalsGetStdinStreamCodeGenerator(vm), globalObject);
JSC::MarkedArgumentBuffer args;
args.append(processObject);
args.append(JSC::jsNumber(STDIN_FILENO));
args.append(jsBoolean(bun_stdio_tty[STDIN_FILENO]));
BunProcessStdinFdType fdType = Bun__Process__getStdinFdType(Bun::vm(vm), STDIN_FILENO);
args.append(jsNumber(static_cast<int32_t>(fdType)));
JSC::CallData callData = JSC::getCallData(getStdinStream);
JSC::CallData callData = JSC::getCallData(getStdioWriteStream);
auto result = JSC::profiledCall(globalObject, ProfilingReason::API, getStdinStream, callData, globalObject, args);
if (auto* exception = scope.exception()) {
Zig::GlobalObject::reportUncaughtExceptionAtEventLoop(globalObject, exception);
scope.clearException();
return jsUndefined();
}
auto result = JSC::profiledCall(globalObject, ProfilingReason::API, getStdioWriteStream, callData, globalObject, args);
RETURN_IF_EXCEPTION(scope, {});
return result;
}

View File

@@ -30,13 +30,8 @@ const enum BunProcessStdinFdType {
socket = 2,
}
export function getStdioWriteStream(
process: typeof globalThis.process,
fd: number,
isTTY: boolean,
_fdType: BunProcessStdinFdType,
) {
$assert(fd === 1 || fd === 2, `Expected fd to be 1 or 2, got ${fd}`);
export function getStdioWriteStream(fd, isTTY: boolean, _fdType: BunProcessStdinFdType) {
$assert(typeof fd === "number", `Expected fd to be a number, got ${typeof fd}`);
let stream;
if (isTTY) {
@@ -79,14 +74,9 @@ export function getStdioWriteStream(
return [stream, underlyingSink];
}
export function getStdinStream(
process: typeof globalThis.process,
fd: number,
isTTY: boolean,
fdType: BunProcessStdinFdType,
) {
$assert(fd === 0);
export function getStdinStream(fd, isTTY: boolean, fdType: BunProcessStdinFdType) {
const native = Bun.stdin.stream();
// @ts-expect-error
const source = native.$bunNativePtr;
var reader: ReadableStreamDefaultReader<Uint8Array> | undefined;
@@ -256,12 +246,7 @@ export function getStdinStream(
return stream;
}
export function initializeNextTickQueue(
process: typeof globalThis.process,
nextTickQueue,
drainMicrotasksFn,
reportUncaughtExceptionFn,
) {
export function initializeNextTickQueue(process, nextTickQueue, drainMicrotasksFn, reportUncaughtExceptionFn) {
var queue;
var process;
var nextTickQueue = nextTickQueue;

View File

@@ -24,7 +24,6 @@ const Duplex = require("internal/streams/duplex");
const { getDefaultHighWaterMark } = require("internal/streams/state");
const EventEmitter = require("node:events");
let dns: typeof import("node:dns");
const os = require("node:os");
const [addServerName, upgradeDuplexToTLS, isNamedPipeSocket, getBufferedAmount] = $zig(
"socket.zig",
@@ -88,21 +87,8 @@ function isIP(s): 0 | 4 | 6 {
return 0;
}
function isLocalAddress(addr: string): boolean {
if (!addr) return false;
if (addr === "0.0.0.0" || addr === "::") return true;
const interfaces = os.networkInterfaces();
for (const name in interfaces) {
const list = interfaces[name];
if (!list) continue;
for (const info of list) {
if (info && info.address === addr) return true;
}
}
return false;
}
const bunTlsSymbol = Symbol.for("::buntls::");
const bunSocketServerConnections = Symbol.for("::bunnetserverconnections::");
const bunSocketServerOptions = Symbol.for("::bunnetserveroptions::");
const owner_symbol = Symbol("owner_symbol");
@@ -132,7 +118,11 @@ function endNT(socket, callback, err) {
callback(err);
}
function emitCloseNT(self, hasError) {
self.emit("close", hasError);
if (hasError) {
self.emit("close", hasError);
} else {
self.emit("close");
}
}
function detachSocket(self) {
if (!self) self = this;
@@ -244,6 +234,13 @@ const SocketHandlers: SocketHandler = {
callback(error);
}
if (error?.syscall === "connect") {
const ex = new ExceptionWithHostPort(error.errno, "connect", self._host, self._port);
self.emit("error", ex);
if (!self.destroyed) process.nextTick(destroyNT, self, ex);
return;
}
self.emit("error", error);
},
open(socket) {
@@ -353,7 +350,7 @@ const ServerHandlers: SocketHandler = {
const data = this.data;
if (!data) return;
data.server._connections--;
data.server[bunSocketServerConnections]--;
{
if (!data[kclosed]) {
data[kclosed] = true;
@@ -399,7 +396,7 @@ const ServerHandlers: SocketHandler = {
return;
}
}
if (self.maxConnections != null && self._connections >= self.maxConnections) {
if (self.maxConnections && self[bunSocketServerConnections] >= self.maxConnections) {
const data = {
localAddress: _socket.localAddress,
localPort: _socket.localPort || this.localPort,
@@ -418,7 +415,7 @@ const ServerHandlers: SocketHandler = {
const bunTLS = _socket[bunTlsSymbol];
const isTLS = typeof bunTLS === "function";
self._connections++;
self[bunSocketServerConnections]++;
if (pauseOnConnect) {
_socket.pause();
@@ -923,7 +920,7 @@ Socket.prototype.connect = function connect(...args) {
}).catch(error => {
if (!this.destroyed) {
this.emit("error", error);
this.emit("close", true);
this.emit("close");
}
});
}
@@ -1177,7 +1174,7 @@ Socket.prototype._destroy = function _destroy(err, callback) {
callback(err);
} else {
callback(err);
process.nextTick(emitCloseNT, this, false);
process.nextTick(emitCloseNT, this);
}
};
@@ -1717,23 +1714,13 @@ function internalConnect(self, options, address, port, addressType, localAddress
if (localAddress || localPort) {
if (addressType === 4) {
localAddress ||= DEFAULT_IPV4_ADDR;
if (!isLocalAddress(localAddress)) {
const UV_EADDRNOTAVAIL = -4090;
const ex = new ExceptionWithHostPort(UV_EADDRNOTAVAIL, "bind", localAddress, localPort);
self.destroy(ex);
return;
}
// TODO: bind socket when supported
// TODO:
// err = self._handle.bind(localAddress, localPort);
} else {
// addressType === 6
localAddress ||= DEFAULT_IPV6_ADDR;
if (!isLocalAddress(localAddress)) {
const UV_EADDRNOTAVAIL = -4090;
const ex = new ExceptionWithHostPort(UV_EADDRNOTAVAIL, "bind", localAddress, localPort);
self.destroy(ex);
return;
}
// TODO: bind6 socket when supported
// TODO:
// err = self._handle.bind6(localAddress, localPort, flags);
}
$debug(
"connect: binding to localAddress: %s and localPort: %d (addressType: %d)",
@@ -2099,6 +2086,7 @@ function Server(options?, connectionListener?) {
// https://nodejs.org/api/net.html#netcreateserveroptions-connectionlistener
const {
maxConnections, //
allowHalfOpen = false,
keepAlive = false,
keepAliveInitialDelay = 0,
@@ -2115,6 +2103,7 @@ function Server(options?, connectionListener?) {
this._unref = false;
this.listeningId = 1;
this[bunSocketServerConnections] = 0;
this[bunSocketServerOptions] = undefined;
this.allowHalfOpen = allowHalfOpen;
this.keepAlive = keepAlive;
@@ -2122,6 +2111,7 @@ 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;
@@ -2184,7 +2174,7 @@ Server.prototype[Symbol.asyncDispose] = function () {
};
Server.prototype._emitCloseIfDrained = function _emitCloseIfDrained() {
if (this._handle || this._connections > 0) {
if (this._handle || this[bunSocketServerConnections] > 0) {
return;
}
process.nextTick(() => {
@@ -2213,7 +2203,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._connections : 0);
callback(null, this._handle ? this[bunSocketServerConnections] : 0);
}
return this;
};
@@ -2466,7 +2456,7 @@ function emitErrorNextTick(self, error) {
function emitErrorAndCloseNextTick(self, error) {
self.emit("error", error);
self.emit("close", true);
self.emit("close");
}
function addServerAbortSignalOption(self, options) {

View File

@@ -7671,15 +7671,7 @@ fn NewParser_(
ifStmtScopeIndex = try p.pushScopeForParsePass(js_ast.Scope.Kind.block, loc);
}
var scopeIndex: usize = 0;
var pushedScopeForFunctionArgs = false;
// Push scope if the current lexer token is an open parenthesis token.
// That is, the parser is about parsing function arguments
if (p.lexer.token == .t_open_paren) {
scopeIndex = try p.pushScopeForParsePass(js_ast.Scope.Kind.function_args, p.lexer.loc());
pushedScopeForFunctionArgs = true;
}
const scopeIndex = try p.pushScopeForParsePass(js_ast.Scope.Kind.function_args, p.lexer.loc());
var func = try p.parseFn(name, FnOrArrowDataParse{
.needs_async_loc = loc,
.async_range = asyncRange orelse logger.Range.None,
@@ -7695,7 +7687,7 @@ fn NewParser_(
if (comptime is_typescript_enabled) {
// Don't output anything if it's just a forward declaration of a function
if ((opts.is_typescript_declare or func.flags.contains(.is_forward_declaration)) and pushedScopeForFunctionArgs) {
if (opts.is_typescript_declare or func.flags.contains(.is_forward_declaration)) {
p.popAndDiscardScope(scopeIndex);
// Balance the fake block scope introduced above
@@ -7711,9 +7703,7 @@ fn NewParser_(
}
}
if (pushedScopeForFunctionArgs) {
p.popScope();
}
p.popScope();
// Only declare the function after we know if it had a body or not. Otherwise
// TypeScript code such as this will double-declare the symbol:
@@ -12615,19 +12605,14 @@ fn NewParser_(
p.allow_in = true;
const loc = p.lexer.loc();
var pushedScopeForFunctionBody = false;
if (p.lexer.token == .t_open_brace) {
_ = try p.pushScopeForParsePass(Scope.Kind.function_body, p.lexer.loc());
pushedScopeForFunctionBody = true;
}
_ = try p.pushScopeForParsePass(Scope.Kind.function_body, p.lexer.loc());
defer p.popScope();
try p.lexer.expect(.t_open_brace);
var opts = ParseStatementOptions{};
const stmts = try p.parseStmtsUpTo(.t_close_brace, &opts);
try p.lexer.next();
if (pushedScopeForFunctionBody) p.popScope();
p.allow_in = oldAllowIn;
p.fn_or_arrow_data_parse = oldFnOrArrowData;
return G.FnBody{ .loc = loc, .stmts = stmts };

View File

@@ -3462,18 +3462,6 @@ describe("await can only be used inside an async function message", () => {
});
});
describe("malformed function definition does not crash due to invalid scope initialization", () => {
it("fails with a parse error and exits cleanly", async () => {
const tests = ["function:", "function a() {function:}"];
for (const code of tests) {
for (const loader of ["js", "ts"]) {
const transpiler = new Bun.Transpiler({ loader });
expect(() => transpiler.transformSync(code)).toThrow("Parse error");
}
}
});
});
it("does not crash with 9 comments and typescript type skipping", () => {
const cmd = [bunExe(), "build", "--minify-identifiers", join(import.meta.dir, "fixtures", "9-comments.ts")];
const { stdout, stderr, exitCode } = Bun.spawnSync({

View File

@@ -1114,20 +1114,3 @@ it("should handle user assigned `default` properties", async () => {
await promise;
});
it.each(["stdin", "stdout", "stderr"])("%s stream accessor should handle exceptions without crashing", stream => {
expect([
/* js */ `
const old = process;
process = null;
try {
old.${stream};
} catch {}
if (typeof old.${stream} !== "undefined") {
console.log("wrong");
}
`,
"",
1,
]).toRunInlineFixture();
});

View File

@@ -1,32 +0,0 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const http = require('http');
const invalidLocalAddress = '1.2.3.4';
const server = http.createServer(function(req, res) {
console.log(`Connect from: ${req.connection.remoteAddress}`);
req.on('end', function() {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end(`You are from: ${req.connection.remoteAddress}`);
});
req.resume();
});
server.listen(0, '127.0.0.1', common.mustCall(function() {
http.request({
host: 'localhost',
port: this.address().port,
path: '/',
method: 'GET',
localAddress: invalidLocalAddress
}, function(res) {
assert.fail('unexpectedly got response from server');
}).on('error', common.mustCall(function(e) {
console.log(`client got error: ${e.message}`);
server.close();
})).end();
}));

View File

@@ -4,80 +4,117 @@ const common = require('../common');
const { createMockedLookup } = require('../common/dns');
const assert = require('assert');
const { createConnection, createServer, setDefaultAutoSelectFamily } = require('net');
const {
createConnection,
createServer,
setDefaultAutoSelectFamily,
} = require('net');
const autoSelectFamilyAttemptTimeout = common.defaultAutoSelectFamilyAttemptTimeout;
const autoSelectFamilyAttemptTimeout =
common.defaultAutoSelectFamilyAttemptTimeout;
// Test that IPV4 is reached by default if IPV6 is not reachable and the default is enabled
{
const ipv4Server = createServer((socket) => {
socket.on('data', common.mustCall(() => {
socket.write('response-ipv4');
socket.end();
}));
socket.on(
'data',
common.mustCall(() => {
socket.write('response-ipv4');
socket.end();
}),
);
});
ipv4Server.listen(0, '127.0.0.1', common.mustCall(() => {
setDefaultAutoSelectFamily(true);
ipv4Server.listen(
0,
'127.0.0.1',
common.mustCall(() => {
setDefaultAutoSelectFamily(true);
const connection = createConnection({
host: 'example.org',
port: ipv4Server.address().port,
lookup: createMockedLookup('::1', '127.0.0.1'),
autoSelectFamilyAttemptTimeout,
});
const connection = createConnection({
host: 'example.org',
port: ipv4Server.address().port,
lookup: createMockedLookup('::1', '127.0.0.1'),
autoSelectFamilyAttemptTimeout,
});
let response = '';
connection.setEncoding('utf-8');
let response = '';
connection.setEncoding('utf-8');
connection.on('data', (chunk) => {
response += chunk;
});
connection.on('data', (chunk) => {
response += chunk;
});
connection.on('end', common.mustCall(() => {
assert.strictEqual(response, 'response-ipv4');
ipv4Server.close();
}));
connection.on(
'end',
common.mustCall(() => {
assert.strictEqual(response, 'response-ipv4');
ipv4Server.close();
}),
);
connection.write('request');
}));
connection.write('request');
}),
);
}
// Test that IPV4 is not reached by default if IPV6 is not reachable and the default is disabled
{
const ipv4Server = createServer((socket) => {
socket.on('data', common.mustCall(() => {
socket.write('response-ipv4');
socket.end();
}));
socket.on(
'data',
common.mustCall(() => {
socket.write('response-ipv4');
socket.end();
}),
);
});
ipv4Server.listen(0, '127.0.0.1', common.mustCall(() => {
setDefaultAutoSelectFamily(false);
ipv4Server.listen(
0,
'127.0.0.1',
common.mustCall(() => {
setDefaultAutoSelectFamily(false);
const port = ipv4Server.address().port;
const port = ipv4Server.address().port;
const connection = createConnection({
host: 'example.org',
port,
lookup: createMockedLookup('::1', '127.0.0.1'),
});
const connection = createConnection({
host: 'example.org',
port,
lookup: createMockedLookup('::1', '127.0.0.1'),
});
connection.on('ready', common.mustNotCall());
connection.on('error', common.mustCall((error) => {
if (common.hasIPv6) {
assert.strictEqual(error.code, 'ECONNREFUSED');
assert.strictEqual(error.message, `connect ECONNREFUSED ::1:${port}`);
} else if (error.code === 'EAFNOSUPPORT') {
assert.strictEqual(error.message, `connect EAFNOSUPPORT ::1:${port} - Local (undefined:undefined)`);
} else if (error.code === 'EUNATCH') {
assert.strictEqual(error.message, `connect EUNATCH ::1:${port} - Local (:::0)`);
} else {
assert.strictEqual(error.code, 'EADDRNOTAVAIL');
assert.strictEqual(error.message, `connect EADDRNOTAVAIL ::1:${port} - Local (:::0)`);
}
connection.on('ready', common.mustNotCall());
connection.on(
'error',
common.mustCall((error) => {
if (common.hasIPv6) {
assert.strictEqual(error.code, 'ECONNREFUSED');
assert.strictEqual(
error.message,
`connect ECONNREFUSED ::1:${port}`,
);
} else if (error.code === 'EAFNOSUPPORT') {
assert.strictEqual(
error.message,
`connect EAFNOSUPPORT ::1:${port} - Local (undefined:undefined)`,
);
} else if (error.code === 'EUNATCH') {
assert.strictEqual(
error.message,
`connect EUNATCH ::1:${port} - Local (:::0)`,
);
} else {
assert.strictEqual(error.code, 'EADDRNOTAVAIL');
assert.strictEqual(
error.message,
`connect EADDRNOTAVAIL ::1:${port} - Local (:::0)`,
);
}
ipv4Server.close();
}));
}));
ipv4Server.close();
}),
);
}),
);
}

View File

@@ -1,78 +0,0 @@
// 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';
require('../common');
const assert = require('assert');
const net = require('net');
let bytesRead = 0;
let bytesWritten = 0;
let count = 0;
const tcp = net.Server(function(s) {
console.log('tcp server connection');
// trigger old mode.
s.resume();
s.on('end', function() {
bytesRead += s.bytesRead;
console.log(`tcp socket disconnect #${count}`);
});
});
tcp.listen(0, function doTest() {
console.error('listening');
const socket = net.createConnection(this.address().port);
socket.on('connect', function() {
count++;
console.error('CLIENT connect #%d', count);
socket.write('foo', function() {
console.error('CLIENT: write cb');
socket.end('bar');
});
});
socket.on('finish', function() {
bytesWritten += socket.bytesWritten;
console.error('CLIENT end event #%d', count);
});
socket.on('close', function() {
console.error('CLIENT close event #%d', count);
console.log(`Bytes read: ${bytesRead}`);
console.log(`Bytes written: ${bytesWritten}`);
if (count < 2) {
console.error('RECONNECTING');
socket.connect(tcp.address().port);
} else {
tcp.close();
}
});
});
process.on('exit', function() {
assert.strictEqual(bytesRead, 12);
assert.strictEqual(bytesWritten, 12);
});

View File

@@ -0,0 +1,29 @@
'use strict';
const common = require('../common');
const net = require('net');
const assert = require('assert');
const server = net.createServer();
server.listen(0, common.mustCall(function() {
const port = server.address().port;
const conn = net.createConnection(port);
server.on('connection', (socket) => {
socket.on('error', common.expectsError({
code: 'ECONNRESET',
message: 'read ECONNRESET',
name: 'Error'
}));
});
conn.on('connect', common.mustCall(function() {
assert.strictEqual(conn, conn.resetAndDestroy().destroy());
conn.on('error', common.mustNotCall());
conn.write(Buffer.from('fzfzfzfzfz'), common.expectsError({
code: 'ERR_STREAM_DESTROYED',
message: 'Cannot call write after a stream was destroyed',
name: 'Error'
}));
server.close();
}));
}));

View File

@@ -1,88 +0,0 @@
// 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';
require('../common');
const assert = require('assert');
const net = require('net');
const N = 50;
let client_recv_count = 0;
let client_end_count = 0;
let disconnect_count = 0;
const server = net.createServer(function(socket) {
console.error('SERVER: got socket connection');
socket.resume();
console.error('SERVER connect, writing');
socket.write('hello\r\n');
socket.on('end', () => {
console.error('SERVER socket end, calling end()');
socket.end();
});
socket.on('close', (had_error) => {
console.log(`SERVER had_error: ${JSON.stringify(had_error)}`);
assert.strictEqual(had_error, false);
});
});
server.listen(0, function() {
console.log('SERVER listening');
const client = net.createConnection(this.address().port);
client.setEncoding('UTF8');
client.on('connect', () => {
console.error('CLIENT connected', client._writableState);
});
client.on('data', function(chunk) {
client_recv_count += 1;
console.log(`client_recv_count ${client_recv_count}`);
assert.strictEqual(chunk, 'hello\r\n');
console.error('CLIENT: calling end', client._writableState);
client.end();
});
client.on('end', () => {
console.error('CLIENT end');
client_end_count++;
});
client.on('close', (had_error) => {
console.log('CLIENT disconnect');
assert.strictEqual(had_error, false);
if (disconnect_count++ < N)
client.connect(server.address().port); // reconnect
else
server.close();
});
});
process.on('exit', () => {
assert.strictEqual(disconnect_count, N + 1);
assert.strictEqual(client_recv_count, N + 1);
assert.strictEqual(client_end_count, N + 1);
});

View File

@@ -1,41 +0,0 @@
'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);
});

View File

@@ -1,41 +0,0 @@
'use strict';
const common = require('../common');
// Skip test in FreeBSD jails
if (common.inFreeBSDJail)
common.skip('In a FreeBSD jail');
const assert = require('assert');
const net = require('net');
let conns = 0;
const clientLocalPorts = [];
const serverRemotePorts = [];
const client = new net.Socket();
const server = net.createServer((socket) => {
serverRemotePorts.push(socket.remotePort);
socket.end();
});
server.on('close', common.mustCall(() => {
// Client and server should agree on the ports used
assert.deepStrictEqual(serverRemotePorts, clientLocalPorts);
assert.strictEqual(conns, 2);
}));
server.listen(0, common.localhostIPv4, connect);
function connect() {
if (conns === 2) {
server.close();
return;
}
conns++;
client.once('close', connect);
assert.strictEqual(
client,
client.connect(server.address().port, common.localhostIPv4, () => {
clientLocalPorts.push(client.localPort);
})
);
}

View File

@@ -1,17 +0,0 @@
'use strict';
const common = require('../common');
const net = require('net');
const assert = require('assert');
const c = net.createConnection(common.PORT);
c.on('connect', common.mustNotCall());
c.on('error', common.mustCall(function(error) {
// Family autoselection might be skipped if only a single address is returned by DNS.
const failedAttempt = Array.isArray(error.errors) ? error.errors[0] : error;
assert.strictEqual(failedAttempt.code, 'ECONNREFUSED');
assert.strictEqual(failedAttempt.port, common.PORT);
assert.match(failedAttempt.address, /^(127\.0\.0\.1|::1)$/);
}));

View File

@@ -1,32 +0,0 @@
// 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');
const net = require('net');
const assert = require('assert');
const c = net.createConnection(common.PORT);
c.on('connect', common.mustNotCall());
c.on('error', common.mustCall((e) => {
assert.strictEqual(c.connecting, false);
assert.strictEqual(e.code, 'ECONNREFUSED');
}));