Skip to content

Commit

Permalink
add node:net module (#3315)
Browse files Browse the repository at this point in the history
* add node:net module

* fix eslint errors due to missing @types/node

* add tests that uses nodejs sidecar

* remove unnecessary compat flag

* try to fix windows build

* disable node:net tests on windows

* do not throw on setNoDelay
  • Loading branch information
anonrig authored Jan 14, 2025
1 parent 7053d6c commit 12dff19
Show file tree
Hide file tree
Showing 14 changed files with 2,768 additions and 21 deletions.
82 changes: 64 additions & 18 deletions build/wd_test.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -65,30 +65,71 @@ def _wd_test_impl(ctx):
if is_windows:
# Batch script executables must end with ".bat"
executable = ctx.actions.declare_file("%s_wd_test.bat" % ctx.label.name)
ctx.actions.write(
output = executable,
# PowerShell correctly handles forward slashes in executable paths generated by Bazel (e.g. "bazel-bin/src/workerd/server/workerd.exe")
content = "powershell -Command \"%*\" `-dTEST_TMPDIR=$ENV:TEST_TMPDIR\r\n",
is_executable = True,
)
content = """
@echo off
setlocal EnableDelayedExpansion
REM Start sidecar if specified
if not "%SIDECAR%" == "" (
start /b "" "%SIDECAR%" > nul 2>&1
set SIDECAR_PID=!ERRORLEVEL!
timeout /t 1 > nul
)
REM Run the actual test
powershell -Command \"%*\" `-dTEST_TMPDIR=$ENV:TEST_TMPDIR
set TEST_EXIT=!ERRORLEVEL!
REM Cleanup sidecar if it was started
if defined SIDECAR_PID (
taskkill /F /PID !SIDECAR_PID! > nul 2>&1
)
exit /b !TEST_EXIT!
""".replace("$(SIDECAR)", ctx.file.sidecar.path if ctx.file.sidecar else "")
else:
executable = ctx.outputs.executable
ctx.actions.write(
output = executable,
content = """
#! /bin/sh
echo
echo \\(cd `pwd` \\&\\& \"$@\" -dTEST_TMPDIR=$TEST_TMPDIR\\)
echo
exec \"$@\" -dTEST_TMPDIR=$TEST_TMPDIR
""",
is_executable = True,
)
content = """#!/bin/sh
set -e
cleanup() {
if [ ! -z "$SIDECAR_PID" ]; then
kill $SIDECAR_PID 2>/dev/null || true
fi
}
trap cleanup EXIT
# Start sidecar if specified
if [ ! -z "$(SIDECAR)" ]; then
"$(SIDECAR)" & SIDECAR_PID=$!
# Wait until the process is ready
sleep 3
fi
# Run the actual test
"$@" -dTEST_TMPDIR=$TEST_TMPDIR
""".replace("$(SIDECAR)", ctx.file.sidecar.short_path if ctx.file.sidecar else "")

ctx.actions.write(
output = executable,
content = content,
is_executable = True,
)

runfiles = ctx.runfiles(files = ctx.files.data)
if ctx.file.sidecar:
runfiles = runfiles.merge(ctx.runfiles(files = [ctx.file.sidecar]))

# Also merge the sidecar's own runfiles if it has any
default_runfiles = ctx.attr.sidecar[DefaultInfo].default_runfiles
if default_runfiles:
runfiles = runfiles.merge(default_runfiles)

return [
DefaultInfo(
executable = executable,
runfiles = ctx.runfiles(files = ctx.files.data),
runfiles = runfiles,
),
]

Expand All @@ -104,6 +145,11 @@ _wd_test = rule(
),
"flags": attr.string_list(),
"data": attr.label_list(allow_files = True),
"sidecar": attr.label(
allow_single_file = True,
executable = True,
cfg = "exec",
),
"_platforms_os_windows": attr.label(default = "@platforms//os:windows"),
},
)
49 changes: 49 additions & 0 deletions src/node/internal/internal_errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,55 @@ export class DnsError extends NodeError {
}
}

export class ERR_OPTION_NOT_IMPLEMENTED extends NodeError {
constructor(name: string | symbol) {
if (typeof name === 'symbol') {
name = (name as symbol).description!;
}
super(
'ERR_OPTION_NOT_IMPLEMENTED',
`The ${name} option is not implemented`
);
}
}

export class ERR_SOCKET_BAD_PORT extends NodeError {
constructor(name: string, port: any, allowZero: boolean) {
const operator = allowZero ? '>=' : '>';
super(
'ERR_SOCKET_BAD_PORT',
`${name} should be ${operator} 0 and < 65536. Received ${typeof port}.`
);
}
}

export class EPIPE extends NodeError {
constructor() {
super('EPIPE', 'This socket has been ended by the other party');
}
}

export class ERR_SOCKET_CLOSED_BEFORE_CONNECTION extends NodeError {
constructor() {
super(
'ERR_SOCKET_CLOSED_BEFORE_CONNETION',
'Socket closed before connection established'
);
}
}

export class ERR_SOCKET_CLOSED extends NodeError {
constructor() {
super('ERR_SOCKET_CLOSED', 'Socket is closed');
}
}

export class ERR_SOCKET_CONNECTING extends NodeError {
constructor() {
super('ERR_SOCKET_CONNECTING', 'Socket is already connecting');
}
}

export function aggregateTwoErrors(innerError: any, outerError: any) {
if (innerError && outerError && innerError !== outerError) {
if (Array.isArray(outerError.errors)) {
Expand Down
25 changes: 25 additions & 0 deletions src/node/internal/sockets.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import type { Buffer } from 'node-internal:internal_buffer';

declare namespace sockets {
function connect(
input: string,
options: Record<string, unknown>
): {
opened: Promise<void>;
closed: Promise<void>;
close(): Promise<void>;
readable: {
getReader(options: Record<string, string>): {
close(): Promise<void>;
read(value: unknown): Promise<{ value: Buffer; done: boolean }>;
};
};
writable: {
getWriter(): {
close(): Promise<void>;
write(data: string | ArrayBufferView): Promise<void>;
};
};
};
}
export default sockets;
7 changes: 7 additions & 0 deletions src/node/internal/streams_duplex.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export {
Duplex,
Writable,
WritableOptions,
Readable,
ReadableOptions,
} from 'node:stream';
1 change: 0 additions & 1 deletion src/node/internal/streams_util.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/no-redundant-type-constituents */
import type { FinishedOptions } from 'node:stream';

type FinishedStream =
Expand Down
21 changes: 20 additions & 1 deletion src/node/internal/validators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,12 @@ import { normalizeEncoding } from 'node-internal:internal_utils';
import {
ERR_INVALID_ARG_TYPE,
ERR_INVALID_ARG_VALUE,
ERR_SOCKET_BAD_PORT,
ERR_OUT_OF_RANGE,
} from 'node-internal:internal_errors';
import { default as bufferUtil } from 'node-internal:buffer';

// TODO(someday): Not current implementing parseFileMode, validatePort
// TODO(someday): Not current implementing parseFileMode

export function isInt32(value: unknown): value is number {
// @ts-expect-error Due to value being unknown
Expand Down Expand Up @@ -301,6 +302,23 @@ export function checkRangesOrGetDefault(
return number;
}

export function validatePort(
port: unknown,
name = 'Port',
allowZero = true
): number {
if (
(typeof port !== 'number' && typeof port !== 'string') ||
(typeof port === 'string' && port.trim().length === 0) ||
+port !== +port >>> 0 ||
+port > 0xffff ||
(port === 0 && !allowZero)
) {
throw new ERR_SOCKET_BAD_PORT(name, port, allowZero);
}
return +port | 0;
}

export default {
isInt32,
isUint32,
Expand All @@ -316,6 +334,7 @@ export default {
validateOneOf,
validateString,
validateUint32,
validatePort,

// Zlib specific
checkFiniteNumber,
Expand Down
1 change: 0 additions & 1 deletion src/node/internal/zlib.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,6 @@ export abstract class CompressionStream {
public [owner_symbol]: Zlib;
// Not used by C++ implementation but required to be Node.js compatible.
public inOff: number;
/* eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents */
public buffer: NodeJS.TypedArray | null;
public cb: () => void;
public availOutBefore: number;
Expand Down
Loading

0 comments on commit 12dff19

Please sign in to comment.