aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar dave caruso <me@paperdave.net> 2023-08-31 22:31:01 -0700
committerGravatar dave caruso <me@paperdave.net> 2023-08-31 22:31:01 -0700
commitd0a58d329f67708fa3427882f04c7268746d937d (patch)
treef83de8b45833e9976f258ddf9bd31fbfd602f82d
parentb07bb3ce174b500b567bb760e82d4696c88a8b28 (diff)
downloadbun-dave/es5-class-helper.tar.gz
bun-dave/es5-class-helper.tar.zst
bun-dave/es5-class-helper.zip
add es5ClassCompatdave/es5-class-helper
-rw-r--r--src/js/internal/shared.ts23
-rw-r--r--src/js/node/fs.js1047
-rw-r--r--src/js/node/http.ts6
-rw-r--r--src/js/node/net.js941
-rw-r--r--src/js/node/tls.js310
5 files changed, 1128 insertions, 1199 deletions
diff --git a/src/js/internal/shared.ts b/src/js/internal/shared.ts
index 98a5b0a71..0b20572ef 100644
--- a/src/js/internal/shared.ts
+++ b/src/js/internal/shared.ts
@@ -29,8 +29,31 @@ function hideFromStack(...fns) {
}
}
+/**
+ * This utility ensures that old JS code that uses functions for classes still works.
+ * Taken from https://github.com/microsoft/vscode/blob/main/src/vs/workbench/api/common/extHostTypes.ts
+ */
+function es5ClassCompat(target: Function): any {
+ const interceptFunctions = {
+ apply: function () {
+ const args = arguments.length === 1 ? [] : arguments[1];
+ return Reflect.construct(target, args, arguments[0].constructor);
+ },
+ call: function () {
+ if (arguments.length === 0) {
+ return Reflect.construct(target, []);
+ } else {
+ const [thisArg, ...restArgs] = arguments;
+ return Reflect.construct(target, restArgs, thisArg.constructor);
+ }
+ },
+ };
+ return Object.assign(target, interceptFunctions);
+}
+
export default {
NotImplementedError,
throwNotImplemented,
hideFromStack,
+ es5ClassCompat,
};
diff --git a/src/js/node/fs.js b/src/js/node/fs.js
index ab6816904..48960d3ee 100644
--- a/src/js/node/fs.js
+++ b/src/js/node/fs.js
@@ -5,9 +5,9 @@ const EventEmitter = require("node:events");
const promises = require("node:fs/promises");
const Stream = require("node:stream");
const { isArrayBufferView } = require("node:util/types");
+const { es5ClassCompat } = require("$shared");
const constants = $processBindingConstants.fs;
-const { COPYFILE_EXCL } = constants;
var fs = Bun.fs();
class FSWatcher extends EventEmitter {
@@ -382,338 +382,320 @@ var defaultReadStreamOptions = {
autoDestroy: true,
};
-var ReadStreamClass;
+class ReadStream extends Stream._getNativeReadableStreamPrototype(2, Stream.Readable) {
+ constructor(pathOrFd, options = defaultReadStreamOptions) {
+ if (typeof options !== "object" || !options) {
+ throw new TypeError("Expected options to be an object");
+ }
-ReadStream = (function (InternalReadStream) {
- ReadStreamClass = InternalReadStream;
- Object.defineProperty(ReadStreamClass.prototype, Symbol.toStringTag, {
- value: "ReadStream",
- enumerable: false,
- });
- function ReadStream(path, options) {
- return new InternalReadStream(path, options);
- }
- ReadStream.prototype = InternalReadStream.prototype;
- return Object.defineProperty(ReadStream, Symbol.hasInstance, {
- value(instance) {
- return instance instanceof InternalReadStream;
- },
- });
-})(
- class ReadStream extends Stream._getNativeReadableStreamPrototype(2, Stream.Readable) {
- constructor(pathOrFd, options = defaultReadStreamOptions) {
- if (typeof options !== "object" || !options) {
- throw new TypeError("Expected options to be an object");
- }
+ var {
+ flags = defaultReadStreamOptions.flags,
+ encoding = defaultReadStreamOptions.encoding,
+ mode = defaultReadStreamOptions.mode,
+ autoClose = defaultReadStreamOptions.autoClose,
+ emitClose = defaultReadStreamOptions.emitClose,
+ start = defaultReadStreamOptions.start,
+ end = defaultReadStreamOptions.end,
+ autoDestroy = defaultReadStreamOptions.autoClose,
+ fs = defaultReadStreamOptions.fs,
+ highWaterMark = defaultReadStreamOptions.highWaterMark,
+ fd = defaultReadStreamOptions.fd,
+ } = options;
+
+ if (pathOrFd?.constructor?.name === "URL") {
+ pathOrFd = Bun.fileURLToPath(pathOrFd);
+ }
- var {
- flags = defaultReadStreamOptions.flags,
- encoding = defaultReadStreamOptions.encoding,
- mode = defaultReadStreamOptions.mode,
- autoClose = defaultReadStreamOptions.autoClose,
- emitClose = defaultReadStreamOptions.emitClose,
- start = defaultReadStreamOptions.start,
- end = defaultReadStreamOptions.end,
- autoDestroy = defaultReadStreamOptions.autoClose,
- fs = defaultReadStreamOptions.fs,
- highWaterMark = defaultReadStreamOptions.highWaterMark,
- fd = defaultReadStreamOptions.fd,
- } = options;
-
- if (pathOrFd?.constructor?.name === "URL") {
+ // This is kinda hacky but we create a temporary object to assign props that we will later pull into the `this` context after we call super
+ var tempThis = {};
+ if (fd != null) {
+ if (typeof fd !== "number") {
+ throw new TypeError("Expected options.fd to be a number");
+ }
+ tempThis.fd = tempThis[readStreamPathOrFdSymbol] = fd;
+ tempThis.autoClose = false;
+ } else if (typeof pathOrFd === "string") {
+ if (pathOrFd.startsWith("file://")) {
pathOrFd = Bun.fileURLToPath(pathOrFd);
}
+ if (pathOrFd.length === 0) {
+ throw new TypeError("Expected path to be a non-empty string");
+ }
+ tempThis.path = tempThis.file = tempThis[readStreamPathOrFdSymbol] = pathOrFd;
+ } else if (typeof pathOrFd === "number") {
+ pathOrFd |= 0;
+ if (pathOrFd < 0) {
+ throw new TypeError("Expected fd to be a positive integer");
+ }
+ tempThis.fd = tempThis[readStreamPathOrFdSymbol] = pathOrFd;
- // This is kinda hacky but we create a temporary object to assign props that we will later pull into the `this` context after we call super
- var tempThis = {};
- if (fd != null) {
- if (typeof fd !== "number") {
- throw new TypeError("Expected options.fd to be a number");
- }
- tempThis.fd = tempThis[readStreamPathOrFdSymbol] = fd;
- tempThis.autoClose = false;
- } else if (typeof pathOrFd === "string") {
- if (pathOrFd.startsWith("file://")) {
- pathOrFd = Bun.fileURLToPath(pathOrFd);
- }
- if (pathOrFd.length === 0) {
- throw new TypeError("Expected path to be a non-empty string");
- }
- tempThis.path = tempThis.file = tempThis[readStreamPathOrFdSymbol] = pathOrFd;
- } else if (typeof pathOrFd === "number") {
- pathOrFd |= 0;
- if (pathOrFd < 0) {
- throw new TypeError("Expected fd to be a positive integer");
- }
- tempThis.fd = tempThis[readStreamPathOrFdSymbol] = pathOrFd;
+ tempThis.autoClose = false;
+ } else {
+ throw new TypeError("Expected a path or file descriptor");
+ }
- tempThis.autoClose = false;
- } else {
- throw new TypeError("Expected a path or file descriptor");
- }
+ // If fd not open for this file, open it
+ if (tempThis.fd === undefined) {
+ // NOTE: this fs is local to constructor, from options
+ tempThis.fd = fs.openSync(pathOrFd, flags, mode);
+ }
+ // Get FileRef from fd
+ var fileRef = Bun.file(tempThis.fd);
+
+ // Get the stream controller
+ // We need the pointer to the underlying stream controller for the NativeReadable
+ var stream = fileRef.stream();
+ var native = $direct(stream);
+ if (!native) {
+ $debug("no native readable stream");
+ throw new Error("no native readable stream");
+ }
+ var { stream: ptr } = native;
+
+ super(ptr, {
+ ...options,
+ encoding,
+ autoDestroy,
+ autoClose,
+ emitClose,
+ highWaterMark,
+ });
- // If fd not open for this file, open it
- if (tempThis.fd === undefined) {
- // NOTE: this fs is local to constructor, from options
- tempThis.fd = fs.openSync(pathOrFd, flags, mode);
- }
- // Get FileRef from fd
- var fileRef = Bun.file(tempThis.fd);
-
- // Get the stream controller
- // We need the pointer to the underlying stream controller for the NativeReadable
- var stream = fileRef.stream();
- var native = $direct(stream);
- if (!native) {
- $debug("no native readable stream");
- throw new Error("no native readable stream");
- }
- var { stream: ptr } = native;
-
- super(ptr, {
- ...options,
- encoding,
- autoDestroy,
- autoClose,
- emitClose,
- highWaterMark,
- });
-
- // Assign the tempThis props to this
- Object.assign(this, tempThis);
- this.#fileRef = fileRef;
-
- this.end = end;
- this._read = this.#internalRead;
- this.start = start;
- this.flags = flags;
- this.mode = mode;
- this.emitClose = emitClose;
-
- this[readStreamPathFastPathSymbol] =
- start === 0 &&
- end === Infinity &&
- autoClose &&
- fs === defaultReadStreamOptions.fs &&
- // is it an encoding which we don't need to decode?
- (encoding === "buffer" ||
- encoding === "binary" ||
- encoding == null ||
- encoding === "utf-8" ||
- encoding === "utf8");
- this._readableState.autoClose = autoDestroy = autoClose;
- this._readableState.highWaterMark = highWaterMark;
-
- if (start !== undefined) {
- this.pos = start;
- }
+ // Assign the tempThis props to this
+ Object.assign(this, tempThis);
+ this.#fileRef = fileRef;
+
+ this.end = end;
+ this._read = this.#internalRead;
+ this.start = start;
+ this.flags = flags;
+ this.mode = mode;
+ this.emitClose = emitClose;
+
+ this[readStreamPathFastPathSymbol] =
+ start === 0 &&
+ end === Infinity &&
+ autoClose &&
+ fs === defaultReadStreamOptions.fs &&
+ // is it an encoding which we don't need to decode?
+ (encoding === "buffer" ||
+ encoding === "binary" ||
+ encoding == null ||
+ encoding === "utf-8" ||
+ encoding === "utf8");
+ this._readableState.autoClose = autoDestroy = autoClose;
+ this._readableState.highWaterMark = highWaterMark;
+
+ if (start !== undefined) {
+ this.pos = start;
}
- #fileRef;
- #fs;
- file;
- path;
- fd = null;
- flags;
- mode;
- start;
- end;
- pos;
- bytesRead = 0;
- #fileSize = -1;
- _read;
-
- [readStreamSymbol] = true;
- [readStreamPathOrFdSymbol];
- [readStreamPathFastPathSymbol];
-
- _construct(callback) {
- if (super._construct) {
- super._construct(callback);
- } else {
- callback();
- }
- this.emit("open", this.fd);
- this.emit("ready");
+ }
+ #fileRef;
+ #fs;
+ file;
+ path;
+ fd = null;
+ flags;
+ mode;
+ start;
+ end;
+ pos;
+ bytesRead = 0;
+ #fileSize = -1;
+ _read;
+
+ [readStreamSymbol] = true;
+ [readStreamPathOrFdSymbol];
+ [readStreamPathFastPathSymbol];
+
+ _construct(callback) {
+ if (super._construct) {
+ super._construct(callback);
+ } else {
+ callback();
}
+ this.emit("open", this.fd);
+ this.emit("ready");
+ }
- _destroy(err, cb) {
- super._destroy(err, cb);
- try {
- var fd = this.fd;
- this[readStreamPathFastPathSymbol] = false;
-
- if (!fd) {
- cb(err);
- } else {
- this.#fs.close(fd, er => {
- cb(er || err);
- });
- this.fd = null;
- }
- } catch (e) {
- throw e;
+ _destroy(err, cb) {
+ super._destroy(err, cb);
+ try {
+ var fd = this.fd;
+ this[readStreamPathFastPathSymbol] = false;
+
+ if (!fd) {
+ cb(err);
+ } else {
+ this.#fs.close(fd, er => {
+ cb(er || err);
+ });
+ this.fd = null;
}
+ } catch (e) {
+ throw e;
}
+ }
- close(cb) {
- if (typeof cb === "function") Stream.eos(this, cb);
- this.destroy();
- }
+ close(cb) {
+ if (typeof cb === "function") Stream.eos(this, cb);
+ this.destroy();
+ }
- push(chunk) {
- // Is it even possible for this to be less than 1?
- var bytesRead = chunk?.length ?? 0;
- if (bytesRead > 0) {
- this.bytesRead += bytesRead;
- var currPos = this.pos;
- // Handle case of going through bytes before pos if bytesRead is less than pos
- // If pos is undefined, we are reading through the whole file
- // Otherwise we started from somewhere in the middle of the file
- if (currPos !== undefined) {
- // At this point we still haven't hit our `start` point
- // We should discard this chunk and exit
- if (this.bytesRead < currPos) {
- return true;
- }
- // At this point, bytes read is greater than our starting position
- // If the current position is still the starting position, that means
- // this is the first chunk where we care about the bytes read
- // and we need to subtract the bytes read from the start position (n) and slice the last n bytes
- if (currPos === this.start) {
- var n = this.bytesRead - currPos;
- chunk = chunk.slice(-n);
- var [_, ...rest] = arguments;
- this.pos = this.bytesRead;
- if (this.end !== undefined && this.bytesRead > this.end) {
- chunk = chunk.slice(0, this.end - this.start + 1);
- }
- return super.push(chunk, ...rest);
- }
- var end = this.end;
- // This is multi-chunk read case where we go passed the end of the what we want to read in the last chunk
- if (end !== undefined && this.bytesRead > end) {
- chunk = chunk.slice(0, end - currPos + 1);
- var [_, ...rest] = arguments;
- this.pos = this.bytesRead;
- return super.push(chunk, ...rest);
+ push(chunk) {
+ // Is it even possible for this to be less than 1?
+ var bytesRead = chunk?.length ?? 0;
+ if (bytesRead > 0) {
+ this.bytesRead += bytesRead;
+ var currPos = this.pos;
+ // Handle case of going through bytes before pos if bytesRead is less than pos
+ // If pos is undefined, we are reading through the whole file
+ // Otherwise we started from somewhere in the middle of the file
+ if (currPos !== undefined) {
+ // At this point we still haven't hit our `start` point
+ // We should discard this chunk and exit
+ if (this.bytesRead < currPos) {
+ return true;
+ }
+ // At this point, bytes read is greater than our starting position
+ // If the current position is still the starting position, that means
+ // this is the first chunk where we care about the bytes read
+ // and we need to subtract the bytes read from the start position (n) and slice the last n bytes
+ if (currPos === this.start) {
+ var n = this.bytesRead - currPos;
+ chunk = chunk.slice(-n);
+ var [_, ...rest] = arguments;
+ this.pos = this.bytesRead;
+ if (this.end !== undefined && this.bytesRead > this.end) {
+ chunk = chunk.slice(0, this.end - this.start + 1);
}
+ return super.push(chunk, ...rest);
+ }
+ var end = this.end;
+ // This is multi-chunk read case where we go passed the end of the what we want to read in the last chunk
+ if (end !== undefined && this.bytesRead > end) {
+ chunk = chunk.slice(0, end - currPos + 1);
+ var [_, ...rest] = arguments;
this.pos = this.bytesRead;
+ return super.push(chunk, ...rest);
}
+ this.pos = this.bytesRead;
}
-
- return super.push(...arguments);
}
- // #
+ return super.push(...arguments);
+ }
- // n should be the the highwatermark passed from Readable.read when calling internal _read (_read is set to this private fn in this class)
- #internalRead(n) {
- // pos is the current position in the file
- // by default, if a start value is provided, pos starts at this.start
- var { pos, end, bytesRead, fd, encoding } = this;
+ // #
- n =
- pos !== undefined // if there is a pos, then we are reading from that specific position in the file
- ? Math.min(end - pos + 1, n) // takes smaller of length of the rest of the file to read minus the cursor position, or the highwatermark
- : Math.min(end - bytesRead + 1, n); // takes the smaller of the length of the rest of the file from the bytes that we have marked read, or the highwatermark
+ // n should be the the highwatermark passed from Readable.read when calling internal _read (_read is set to this private fn in this class)
+ #internalRead(n) {
+ // pos is the current position in the file
+ // by default, if a start value is provided, pos starts at this.start
+ var { pos, end, bytesRead, fd, encoding } = this;
- $debug("n @ fs.ReadStream.#internalRead, after clamp", n);
+ n =
+ pos !== undefined // if there is a pos, then we are reading from that specific position in the file
+ ? Math.min(end - pos + 1, n) // takes smaller of length of the rest of the file to read minus the cursor position, or the highwatermark
+ : Math.min(end - bytesRead + 1, n); // takes the smaller of the length of the rest of the file from the bytes that we have marked read, or the highwatermark
- // If n is 0 or less, then we read all the file, push null to stream, ending it
- if (n <= 0) {
- this.push(null);
- return;
- }
+ $debug("n @ fs.ReadStream.#internalRead, after clamp", n);
- // At this point, n is the lesser of the length of the rest of the file to read or the highwatermark
- // Which means n is the maximum number of bytes to read
-
- // Basically if we don't know the file size yet, then check it
- // Then if n is bigger than fileSize, set n to be fileSize
- // This is a fast path to avoid allocating more than the file size for a small file (is this respected by native stream though)
- if (this.#fileSize === -1 && bytesRead === 0 && pos === undefined) {
- var stat = fstatSync(fd);
- this.#fileSize = stat.size;
- if (this.#fileSize > 0 && n > this.#fileSize) {
- n = this.#fileSize + 1;
- }
- $debug("fileSize", this.#fileSize);
- }
+ // If n is 0 or less, then we read all the file, push null to stream, ending it
+ if (n <= 0) {
+ this.push(null);
+ return;
+ }
- // At this point, we know the file size and how much we want to read of the file
- this[kIoDone] = false;
- var res = super._read(n);
- $debug("res -- undefined? why?", res);
- if ($isPromise(res)) {
- var then = res?.then;
- if (then && $isCallable(then)) {
- res.then(
- () => {
- this[kIoDone] = true;
- // Tell ._destroy() that it's safe to close the fd now.
- if (this.destroyed) {
- this.emit(kIoDone);
- }
- },
- er => {
- this[kIoDone] = true;
- this.#errorOrDestroy(er);
- },
- );
- }
- } else {
- this[kIoDone] = true;
- if (this.destroyed) {
- this.emit(kIoDone);
- this.#errorOrDestroy(new Error("ERR_STREAM_PREMATURE_CLOSE"));
- }
+ // At this point, n is the lesser of the length of the rest of the file to read or the highwatermark
+ // Which means n is the maximum number of bytes to read
+
+ // Basically if we don't know the file size yet, then check it
+ // Then if n is bigger than fileSize, set n to be fileSize
+ // This is a fast path to avoid allocating more than the file size for a small file (is this respected by native stream though)
+ if (this.#fileSize === -1 && bytesRead === 0 && pos === undefined) {
+ var stat = fstatSync(fd);
+ this.#fileSize = stat.size;
+ if (this.#fileSize > 0 && n > this.#fileSize) {
+ n = this.#fileSize + 1;
}
+ $debug("fileSize", this.#fileSize);
}
- #errorOrDestroy(err, sync = null) {
- var {
- _readableState: r = { destroyed: false, autoDestroy: false },
- _writableState: w = { destroyed: false, autoDestroy: false },
- } = this;
-
- if (w?.destroyed || r?.destroyed) {
- return this;
+ // At this point, we know the file size and how much we want to read of the file
+ this[kIoDone] = false;
+ var res = super._read(n);
+ $debug("res -- undefined? why?", res);
+ if ($isPromise(res)) {
+ var then = res?.then;
+ if (then && $isCallable(then)) {
+ res.then(
+ () => {
+ this[kIoDone] = true;
+ // Tell ._destroy() that it's safe to close the fd now.
+ if (this.destroyed) {
+ this.emit(kIoDone);
+ }
+ },
+ er => {
+ this[kIoDone] = true;
+ this.#errorOrDestroy(er);
+ },
+ );
}
- if (r?.autoDestroy || w?.autoDestroy) this.destroy(err);
- else if (err) {
- this.emit("error", err);
+ } else {
+ this[kIoDone] = true;
+ if (this.destroyed) {
+ this.emit(kIoDone);
+ this.#errorOrDestroy(new Error("ERR_STREAM_PREMATURE_CLOSE"));
}
}
+ }
- pause() {
- this[readStreamPathFastPathSymbol] = false;
- return super.pause();
- }
+ #errorOrDestroy(err, sync = null) {
+ var {
+ _readableState: r = { destroyed: false, autoDestroy: false },
+ _writableState: w = { destroyed: false, autoDestroy: false },
+ } = this;
- resume() {
- this[readStreamPathFastPathSymbol] = false;
- return super.resume();
+ if (w?.destroyed || r?.destroyed) {
+ return this;
}
-
- unshift(...args) {
- this[readStreamPathFastPathSymbol] = false;
- return super.unshift(...args);
+ if (r?.autoDestroy || w?.autoDestroy) this.destroy(err);
+ else if (err) {
+ this.emit("error", err);
}
+ }
- pipe(dest, pipeOpts) {
- if (this[readStreamPathFastPathSymbol] && (pipeOpts?.end ?? true) && this._readableState?.pipes?.length === 0) {
- if (writeStreamPathFastPathSymbol in dest && dest[writeStreamPathFastPathSymbol]) {
- if (dest[writeStreamPathFastPathCallSymbol](this, pipeOpts)) {
- return this;
- }
+ pause() {
+ this[readStreamPathFastPathSymbol] = false;
+ return super.pause();
+ }
+
+ resume() {
+ this[readStreamPathFastPathSymbol] = false;
+ return super.resume();
+ }
+
+ unshift(...args) {
+ this[readStreamPathFastPathSymbol] = false;
+ return super.unshift(...args);
+ }
+
+ pipe(dest, pipeOpts) {
+ if (this[readStreamPathFastPathSymbol] && (pipeOpts?.end ?? true) && this._readableState?.pipes?.length === 0) {
+ if (writeStreamPathFastPathSymbol in dest && dest[writeStreamPathFastPathSymbol]) {
+ if (dest[writeStreamPathFastPathCallSymbol](this, pipeOpts)) {
+ return this;
}
}
-
- this[readStreamPathFastPathSymbol] = false;
- return super.pipe(dest, pipeOpts);
}
- },
-);
+
+ this[readStreamPathFastPathSymbol] = false;
+ return super.pipe(dest, pipeOpts);
+ }
+}
+es5ClassCompat(ReadStream);
function createReadStream(path, options) {
return new ReadStream(path, options);
@@ -734,324 +716,287 @@ var defaultWriteStreamOptions = {
},
};
-var WriteStreamClass;
-WriteStream = (function (InternalWriteStream) {
- WriteStreamClass = InternalWriteStream;
- Object.defineProperty(WriteStreamClass.prototype, Symbol.toStringTag, {
- value: "WritesStream",
- enumerable: false,
- });
-
- function WriteStream(path, options) {
- return new InternalWriteStream(path, options);
- }
- WriteStream.prototype = InternalWriteStream.prototype;
- return Object.defineProperty(WriteStream, Symbol.hasInstance, {
- value(instance) {
- return instance instanceof InternalWriteStream;
- },
- });
-})(
- class WriteStream extends Stream.NativeWritable {
- constructor(path, options = defaultWriteStreamOptions) {
- if (!options) {
- throw new TypeError("Expected options to be an object");
- }
-
- var {
- fs = defaultWriteStreamOptions.fs,
- start = defaultWriteStreamOptions.start,
- flags = defaultWriteStreamOptions.flags,
- mode = defaultWriteStreamOptions.mode,
- autoClose = true,
- emitClose = false,
- autoDestroy = autoClose,
- encoding = defaultWriteStreamOptions.encoding,
- fd = defaultWriteStreamOptions.fd,
- pos = defaultWriteStreamOptions.pos,
- } = options;
-
- var tempThis = {};
- if (fd != null) {
- if (typeof fd !== "number") {
- throw new Error("Expected options.fd to be a number");
- }
- tempThis.fd = fd;
- tempThis[writeStreamPathFastPathSymbol] = false;
- } else if (typeof path === "string") {
- if (path.length === 0) {
- throw new TypeError("Expected a non-empty path");
- }
-
- if (path.startsWith("file:")) {
- path = Bun.fileURLToPath(path);
- }
+class WriteStream extends Stream.NativeWritable {
+ constructor(path, options = defaultWriteStreamOptions) {
+ if (!options) {
+ throw new TypeError("Expected options to be an object");
+ }
- tempThis.path = path;
- tempThis.fd = null;
- tempThis[writeStreamPathFastPathSymbol] =
- autoClose &&
- (start === undefined || start === 0) &&
- fs.write === defaultWriteStreamOptions.fs.write &&
- fs.close === defaultWriteStreamOptions.fs.close;
+ var {
+ fs = defaultWriteStreamOptions.fs,
+ start = defaultWriteStreamOptions.start,
+ flags = defaultWriteStreamOptions.flags,
+ mode = defaultWriteStreamOptions.mode,
+ autoClose = true,
+ emitClose = false,
+ autoDestroy = autoClose,
+ encoding = defaultWriteStreamOptions.encoding,
+ fd = defaultWriteStreamOptions.fd,
+ pos = defaultWriteStreamOptions.pos,
+ } = options;
+
+ var tempThis = {};
+ if (fd != null) {
+ if (typeof fd !== "number") {
+ throw new Error("Expected options.fd to be a number");
}
-
- if (tempThis.fd == null) {
- tempThis.fd = fs.openSync(path, flags, mode);
+ tempThis.fd = fd;
+ tempThis[writeStreamPathFastPathSymbol] = false;
+ } else if (typeof path === "string") {
+ if (path.length === 0) {
+ throw new TypeError("Expected a non-empty path");
}
- super(tempThis.fd, {
- ...options,
- decodeStrings: false,
- autoDestroy,
- emitClose,
- fd: tempThis,
- });
- Object.assign(this, tempThis);
-
- if (typeof fs?.write !== "function") {
- throw new TypeError("Expected fs.write to be a function");
+ if (path.startsWith("file:")) {
+ path = Bun.fileURLToPath(path);
}
- if (typeof fs?.close !== "function") {
- throw new TypeError("Expected fs.close to be a function");
- }
+ tempThis.path = path;
+ tempThis.fd = null;
+ tempThis[writeStreamPathFastPathSymbol] =
+ autoClose &&
+ (start === undefined || start === 0) &&
+ fs.write === defaultWriteStreamOptions.fs.write &&
+ fs.close === defaultWriteStreamOptions.fs.close;
+ }
- if (typeof fs?.open !== "function") {
- throw new TypeError("Expected fs.open to be a function");
- }
+ if (tempThis.fd == null) {
+ tempThis.fd = fs.openSync(path, flags, mode);
+ }
- if (typeof path === "object" && path) {
- if (path instanceof URL) {
- path = Bun.fileURLToPath(path);
- }
- }
+ super(tempThis.fd, {
+ ...options,
+ decodeStrings: false,
+ autoDestroy,
+ emitClose,
+ fd: tempThis,
+ });
+ Object.assign(this, tempThis);
- if (typeof path !== "string" && typeof fd !== "number") {
- throw new TypeError("Expected a path or file descriptor");
- }
+ if (typeof fs?.write !== "function") {
+ throw new TypeError("Expected fs.write to be a function");
+ }
- this.start = start;
- this.#fs = fs;
- this.flags = flags;
- this.mode = mode;
+ if (typeof fs?.close !== "function") {
+ throw new TypeError("Expected fs.close to be a function");
+ }
- if (this.start !== undefined) {
- this.pos = this.start;
- }
+ if (typeof fs?.open !== "function") {
+ throw new TypeError("Expected fs.open to be a function");
+ }
- if (encoding !== defaultWriteStreamOptions.encoding) {
- this.setDefaultEncoding(encoding);
- if (encoding !== "buffer" && encoding !== "utf8" && encoding !== "utf-8" && encoding !== "binary") {
- this[writeStreamPathFastPathSymbol] = false;
- }
+ if (typeof path === "object" && path) {
+ if (path instanceof URL) {
+ path = Bun.fileURLToPath(path);
}
}
- get autoClose() {
- return this._writableState.autoDestroy;
+ if (typeof path !== "string" && typeof fd !== "number") {
+ throw new TypeError("Expected a path or file descriptor");
}
- set autoClose(val) {
- this._writableState.autoDestroy = val;
- }
+ this.start = start;
+ this.#fs = fs;
+ this.flags = flags;
+ this.mode = mode;
- destroySoon = this.end; // TODO: what is this for?
-
- // noop, node has deprecated this
- open() {}
-
- path;
- fd;
- flags;
- mode;
- #fs;
- bytesWritten = 0;
- pos;
- [writeStreamPathFastPathSymbol];
- [writeStreamSymbol] = true;
- start;
-
- [writeStreamPathFastPathCallSymbol](readStream, pipeOpts) {
- if (!this[writeStreamPathFastPathSymbol]) {
- return false;
- }
+ if (this.start !== undefined) {
+ this.pos = this.start;
+ }
- if (this.fd !== null) {
+ if (encoding !== defaultWriteStreamOptions.encoding) {
+ this.setDefaultEncoding(encoding);
+ if (encoding !== "buffer" && encoding !== "utf8" && encoding !== "utf-8" && encoding !== "binary") {
this[writeStreamPathFastPathSymbol] = false;
- return false;
}
-
- this[kIoDone] = false;
- readStream[kIoDone] = false;
- return Bun.write(this[writeStreamPathFastPathSymbol], readStream[readStreamPathOrFdSymbol]).then(
- bytesWritten => {
- readStream[kIoDone] = this[kIoDone] = true;
- this.bytesWritten += bytesWritten;
- readStream.bytesRead += bytesWritten;
- this.end();
- readStream.close();
- },
- err => {
- readStream[kIoDone] = this[kIoDone] = true;
- this.#errorOrDestroy(err);
- readStream.emit("error", err);
- },
- );
}
+ }
+
+ get autoClose() {
+ return this._writableState.autoDestroy;
+ }
- isBunFastPathEnabled() {
- return this[writeStreamPathFastPathSymbol];
+ set autoClose(val) {
+ this._writableState.autoDestroy = val;
+ }
+
+ destroySoon = this.end; // TODO: what is this for?
+
+ // noop, node has deprecated this
+ open() {}
+
+ path;
+ fd;
+ flags;
+ mode;
+ #fs;
+ bytesWritten = 0;
+ pos;
+ [writeStreamPathFastPathSymbol];
+ [writeStreamSymbol] = true;
+ start;
+
+ [writeStreamPathFastPathCallSymbol](readStream, pipeOpts) {
+ if (!this[writeStreamPathFastPathSymbol]) {
+ return false;
}
- disableBunFastPath() {
+ if (this.fd !== null) {
this[writeStreamPathFastPathSymbol] = false;
+ return false;
}
- #handleWrite(er, bytes) {
- if (er) {
- return this.#errorOrDestroy(er);
- }
+ this[kIoDone] = false;
+ readStream[kIoDone] = false;
+ return Bun.write(this[writeStreamPathFastPathSymbol], readStream[readStreamPathOrFdSymbol]).then(
+ bytesWritten => {
+ readStream[kIoDone] = this[kIoDone] = true;
+ this.bytesWritten += bytesWritten;
+ readStream.bytesRead += bytesWritten;
+ this.end();
+ readStream.close();
+ },
+ err => {
+ readStream[kIoDone] = this[kIoDone] = true;
+ this.#errorOrDestroy(err);
+ readStream.emit("error", err);
+ },
+ );
+ }
- this.bytesWritten += bytes;
- }
+ isBunFastPathEnabled() {
+ return this[writeStreamPathFastPathSymbol];
+ }
- #internalClose(err, cb) {
- this[writeStreamPathFastPathSymbol] = false;
- var fd = this.fd;
- this.#fs.close(fd, er => {
- this.fd = null;
- cb(err || er);
- });
+ disableBunFastPath() {
+ this[writeStreamPathFastPathSymbol] = false;
+ }
+
+ #handleWrite(er, bytes) {
+ if (er) {
+ return this.#errorOrDestroy(er);
}
- _construct(callback) {
- if (typeof this.fd === "number") {
- callback();
- return;
- }
+ this.bytesWritten += bytes;
+ }
+ #internalClose(err, cb) {
+ this[writeStreamPathFastPathSymbol] = false;
+ var fd = this.fd;
+ this.#fs.close(fd, er => {
+ this.fd = null;
+ cb(err || er);
+ });
+ }
+
+ _construct(callback) {
+ if (typeof this.fd === "number") {
callback();
- this.emit("open", this.fd);
- this.emit("ready");
+ return;
}
- _destroy(err, cb) {
- if (this.fd === null) {
- return cb(err);
- }
+ callback();
+ this.emit("open", this.fd);
+ this.emit("ready");
+ }
- if (this[kIoDone]) {
- this.once(kIoDone, () => this.#internalClose(err, cb));
- return;
- }
+ _destroy(err, cb) {
+ if (this.fd === null) {
+ return cb(err);
+ }
- this.#internalClose(err, cb);
+ if (this[kIoDone]) {
+ this.once(kIoDone, () => this.#internalClose(err, cb));
+ return;
}
- [kIoDone] = false;
+ this.#internalClose(err, cb);
+ }
- close(cb) {
- if (cb) {
- if (this.closed) {
- process.nextTick(cb);
- return;
- }
- this.on("close", cb);
- }
+ [kIoDone] = false;
- // If we are not autoClosing, we should call
- // destroy on 'finish'.
- if (!this.autoClose) {
- this.on("finish", this.destroy);
+ close(cb) {
+ if (cb) {
+ if (this.closed) {
+ process.nextTick(cb);
+ return;
}
+ this.on("close", cb);
+ }
- // We use end() instead of destroy() because of
- // https://github.com/nodejs/node/issues/2006
- this.end();
+ // If we are not autoClosing, we should call
+ // destroy on 'finish'.
+ if (!this.autoClose) {
+ this.on("finish", this.destroy);
}
- write(chunk, encoding = this._writableState.defaultEncoding, cb) {
- this[writeStreamPathFastPathSymbol] = false;
- if (typeof chunk === "string") {
- chunk = Buffer.from(chunk, encoding);
- }
+ // We use end() instead of destroy() because of
+ // https://github.com/nodejs/node/issues/2006
+ this.end();
+ }
- // TODO: Replace this when something like lseek is available
- var native = this.pos === undefined;
- const callback = native
- ? (err, bytes) => {
- this[kIoDone] = false;
- this.#handleWrite(err, bytes);
- this.emit(kIoDone);
- if (cb) !err ? cb() : cb(err);
- }
- : () => {};
- this[kIoDone] = true;
- if (this._write) {
- return this._write(chunk, encoding, callback);
- } else {
- return super.write(chunk, encoding, callback, native);
- }
+ write(chunk, encoding = this._writableState.defaultEncoding, cb) {
+ this[writeStreamPathFastPathSymbol] = false;
+ if (typeof chunk === "string") {
+ chunk = Buffer.from(chunk, encoding);
}
- end(chunk, encoding, cb) {
- var native = this.pos === undefined;
- return super.end(chunk, encoding, cb, native);
+ // TODO: Replace this when something like lseek is available
+ var native = this.pos === undefined;
+ const callback = native
+ ? (err, bytes) => {
+ this[kIoDone] = false;
+ this.#handleWrite(err, bytes);
+ this.emit(kIoDone);
+ if (cb) !err ? cb() : cb(err);
+ }
+ : () => {};
+ this[kIoDone] = true;
+ if (this._write) {
+ return this._write(chunk, encoding, callback);
+ } else {
+ return super.write(chunk, encoding, callback, native);
}
+ }
- _write = undefined;
- _writev = undefined;
+ end(chunk, encoding, cb) {
+ var native = this.pos === undefined;
+ return super.end(chunk, encoding, cb, native);
+ }
- get pending() {
- return this.fd === null;
- }
+ _write = undefined;
+ _writev = undefined;
- _destroy(err, cb) {
- this.close(err, cb);
- }
+ get pending() {
+ return this.fd === null;
+ }
- #errorOrDestroy(err) {
- var {
- _readableState: r = { destroyed: false, autoDestroy: false },
- _writableState: w = { destroyed: false, autoDestroy: false },
- } = this;
+ _destroy(err, cb) {
+ this.close(err, cb);
+ }
- if (w?.destroyed || r?.destroyed) {
- return this;
- }
- if (r?.autoDestroy || w?.autoDestroy) this.destroy(err);
- else if (err) {
- this.emit("error", err);
- }
+ #errorOrDestroy(err) {
+ var {
+ _readableState: r = { destroyed: false, autoDestroy: false },
+ _writableState: w = { destroyed: false, autoDestroy: false },
+ } = this;
+
+ if (w?.destroyed || r?.destroyed) {
+ return this;
}
- },
-);
+ if (r?.autoDestroy || w?.autoDestroy) this.destroy(err);
+ else if (err) {
+ this.emit("error", err);
+ }
+ }
+}
+es5ClassCompat(WriteStream);
function createWriteStream(path, options) {
// const WriteStream = getLazyWriteStream();
return new WriteStream(path, options);
}
-// NOTE: This was too smart and doesn't actually work
-// WriteStream = Object.defineProperty(
-// function WriteStream(path, options) {
-// var _InternalWriteStream = getLazyWriteStream();
-// return new _InternalWriteStream(path, options);
-// },
-// Symbol.hasInstance,
-// { value: (instance) => instance[writeStreamSymbol] === true },
-// );
-
-// ReadStream = Object.defineProperty(
-// function ReadStream(path, options) {
-// var _InternalReadStream = getLazyReadStream();
-// return new _InternalReadStream(path, options);
-// },
-// Symbol.hasInstance,
-// { value: (instance) => instance[readStreamSymbol] === true },
-// );
-
Object.defineProperties(fs, {
createReadStream: {
value: createReadStream,
@@ -1209,10 +1154,6 @@ export default {
writeSync,
writev,
writevSync,
- [Symbol.for("::bunternal::")]: {
- ReadStreamClass,
- WriteStreamClass,
- },
// get WriteStream() {
// return getLazyWriteStream();
// },
diff --git a/src/js/node/http.ts b/src/js/node/http.ts
index 0d41cf996..f1dc5c4d5 100644
--- a/src/js/node/http.ts
+++ b/src/js/node/http.ts
@@ -1,4 +1,5 @@
// Hardcoded module "node:http"
+const { es5ClassCompat } = require("$shared");
const EventEmitter = require("node:events");
const { isTypedArray } = require("node:util/types");
const { Duplex, Readable, Writable } = require("node:stream");
@@ -555,6 +556,7 @@ class Server extends EventEmitter {
}
setTimeout(msecs, callback) {}
}
+es5ClassCompat(Server);
function assignHeaders(object, req) {
var headers = req.headers.toJSON();
@@ -736,6 +738,7 @@ class IncomingMessage extends Readable {
throw new Error("not implemented");
}
}
+es5ClassCompat(IncomingMessage);
function emitErrorNt(msg, err, callback) {
callback(err);
@@ -952,6 +955,7 @@ class OutgoingMessage extends Writable {
return this;
}
}
+es5ClassCompat(OutgoingMessage);
let OriginalWriteHeadFn, OriginalImplicitHeadFn;
class ServerResponse extends Writable {
@@ -1188,6 +1192,7 @@ class ServerResponse extends Writable {
return this;
}
}
+es5ClassCompat(ServerResponse);
OriginalWriteHeadFn = ServerResponse.prototype.writeHead;
OriginalImplicitHeadFn = ServerResponse.prototype._implicitHeader;
@@ -1607,6 +1612,7 @@ class ClientRequest extends OutgoingMessage {
return this;
}
}
+es5ClassCompat(ClientRequest);
function urlToHttpOptions(url) {
var { protocol, hostname, hash, search, pathname, href, port, username, password } = url;
diff --git a/src/js/node/net.js b/src/js/node/net.js
index 0513f44d3..c4efe2f62 100644
--- a/src/js/node/net.js
+++ b/src/js/node/net.js
@@ -21,6 +21,7 @@
// USE OR OTHER DEALINGS IN THE SOFTWARE.
const { Duplex } = require("node:stream");
const EventEmitter = require("node:events");
+const { es5ClassCompat } = require("$shared");
// IPv4 Segment
const v4Seg = "(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])";
@@ -66,394 +67,398 @@ const bunSocketServerOptions = Symbol.for("::bunnetserveroptions::");
const bunSocketInternal = Symbol.for("::bunnetsocketinternal::");
const bunTLSConnectOptions = Symbol.for("::buntlsconnectoptions::");
-var SocketClass;
-const Socket = (function (InternalSocket) {
- SocketClass = InternalSocket;
- Object.defineProperty(SocketClass.prototype, Symbol.toStringTag, {
- value: "Socket",
- enumerable: false,
- });
-
- return Object.defineProperty(
- function Socket(options) {
- return new InternalSocket(options);
- },
- Symbol.hasInstance,
- {
- value(instance) {
- return instance instanceof InternalSocket;
- },
- },
- );
-})(
- class Socket extends Duplex {
- static #Handlers = {
- close: Socket.#Close,
- connectError(socket, error) {
- const self = socket.data;
- self.emit("error", error);
- },
- data({ data: self }, buffer) {
- self.bytesRead += buffer.length;
- const queue = self.#readQueue;
-
- if (queue.isEmpty()) {
- if (self.push(buffer)) return;
- }
- queue.push(buffer);
- },
- drain: Socket.#Drain,
- end: Socket.#Close,
- error(socket, error) {
- const self = socket.data;
- const callback = self.#writeCallback;
- if (callback) {
- self.#writeCallback = null;
- callback(error);
- }
- self.emit("error", error);
- },
- open(socket) {
- const self = socket.data;
- socket.timeout(self.timeout);
- socket.ref();
- self[bunSocketInternal] = socket;
- self.connecting = false;
- const options = self[bunTLSConnectOptions];
-
- if (options) {
- const { session } = options;
- if (session) {
- self.setSession(session);
- }
- }
-
- if (!self.#upgraded) {
- // this is not actually emitted on nodejs when socket used on the connection
- // this is already emmited on non-TLS socket and on TLS socket is emmited secureConnect after handshake
- self.emit("connect", self);
- }
-
- Socket.#Drain(socket);
- },
- handshake(socket, success, verifyError) {
- const { data: self } = socket;
-
- self._securePending = false;
- self.secureConnecting = false;
- self._secureEstablished = !!success;
- self.emit("secure", self);
-
- const { checkServerIdentity } = self[bunTLSConnectOptions];
- if (!verifyError && typeof checkServerIdentity === "function" && self.servername) {
- const cert = self.getPeerCertificate(true);
- verifyError = checkServerIdentity(self.servername, cert);
- }
-
- if (self._requestCert || self._rejectUnauthorized) {
- if (verifyError) {
- self.authorized = false;
- self.authorizationError = verifyError.code || verifyError.message;
- if (self._rejectUnauthorized) {
- self.destroy(verifyError);
- return;
- }
- }
- } else {
- self.authorized = true;
- }
- self.emit("secureConnect", verifyError);
- },
- timeout(socket) {
- const self = socket.data;
- self.emit("timeout", self);
- },
- binaryType: "buffer",
- };
-
- static #Close(socket) {
+class Socket extends Duplex {
+ static #Handlers = {
+ close: Socket.#Close,
+ connectError(socket, error) {
const self = socket.data;
- if (self.#closed) return;
- self.#closed = true;
- //socket cannot be used after close
- self[bunSocketInternal] = null;
+ self.emit("error", error);
+ },
+ data({ data: self }, buffer) {
+ self.bytesRead += buffer.length;
const queue = self.#readQueue;
+
if (queue.isEmpty()) {
- if (self.push(null)) return;
+ if (self.push(buffer)) return;
}
- queue.push(null);
- }
-
- static #Drain(socket) {
+ queue.push(buffer);
+ },
+ drain: Socket.#Drain,
+ end: Socket.#Close,
+ error(socket, error) {
const self = socket.data;
-
const callback = self.#writeCallback;
if (callback) {
- const chunk = self.#writeChunk;
- const written = socket.write(chunk);
-
- self.bytesWritten += written;
- if (written < chunk.length) {
- self.#writeChunk = chunk.slice(written);
- } else {
- self.#writeCallback = null;
- self.#writeChunk = null;
- callback(null);
+ self.#writeCallback = null;
+ callback(error);
+ }
+ self.emit("error", error);
+ },
+ open(socket) {
+ const self = socket.data;
+ socket.timeout(self.timeout);
+ socket.ref();
+ self[bunSocketInternal] = socket;
+ self.connecting = false;
+ const options = self[bunTLSConnectOptions];
+
+ if (options) {
+ const { session } = options;
+ if (session) {
+ self.setSession(session);
}
}
- }
- static [bunSocketServerHandlers] = {
- data: Socket.#Handlers.data,
- close(socket) {
- Socket.#Handlers.close(socket);
- this.data[bunSocketServerConnections]--;
- },
- end(socket) {
- Socket.#Handlers.end(socket);
- this.data[bunSocketServerConnections]--;
- },
- open(socket) {
- const self = this.data;
- const options = self[bunSocketServerOptions];
- const { pauseOnConnect, connectionListener, InternalSocketClass, requestCert, rejectUnauthorized } = options;
- const _socket = new InternalSocketClass({});
- _socket.isServer = true;
- _socket._requestCert = requestCert;
- _socket._rejectUnauthorized = rejectUnauthorized;
-
- _socket.#attach(this.localPort, socket);
- if (self.maxConnections && self[bunSocketServerConnections] >= self.maxConnections) {
- const data = {
- localAddress: _socket.localAddress,
- localPort: _socket.localPort,
- localFamily: _socket.localFamily,
- remoteAddress: _socket.remoteAddress,
- remotePort: _socket.remotePort,
- remoteFamily: _socket.remoteFamily || "IPv4",
- };
-
- socket.end();
-
- self.emit("drop", data);
- return;
- }
- // the duplex implementation start paused, so we resume when pauseOnConnect is falsy
- if (!pauseOnConnect) {
- _socket.resume();
- }
+ if (!self.#upgraded) {
+ // this is not actually emitted on nodejs when socket used on the connection
+ // this is already emmited on non-TLS socket and on TLS socket is emmited secureConnect after handshake
+ self.emit("connect", self);
+ }
- self[bunSocketServerConnections]++;
+ Socket.#Drain(socket);
+ },
+ handshake(socket, success, verifyError) {
+ const { data: self } = socket;
+
+ self._securePending = false;
+ self.secureConnecting = false;
+ self._secureEstablished = !!success;
+ self.emit("secure", self);
+
+ const { checkServerIdentity } = self[bunTLSConnectOptions];
+ if (!verifyError && typeof checkServerIdentity === "function" && self.servername) {
+ const cert = self.getPeerCertificate(true);
+ verifyError = checkServerIdentity(self.servername, cert);
+ }
- if (typeof connectionListener == "function") {
- if (InternalSocketClass.name === "TLSSocket") {
- // add secureConnection event handler
- self.once("secureConnection", () => connectionListener(_socket));
- } else {
- connectionListener(_socket);
+ if (self._requestCert || self._rejectUnauthorized) {
+ if (verifyError) {
+ self.authorized = false;
+ self.authorizationError = verifyError.code || verifyError.message;
+ if (self._rejectUnauthorized) {
+ self.destroy(verifyError);
+ return;
}
}
+ } else {
+ self.authorized = true;
+ }
+ self.emit("secureConnect", verifyError);
+ },
+ timeout(socket) {
+ const self = socket.data;
+ self.emit("timeout", self);
+ },
+ binaryType: "buffer",
+ };
+
+ static #Close(socket) {
+ const self = socket.data;
+ if (self.#closed) return;
+ self.#closed = true;
+ //socket cannot be used after close
+ self[bunSocketInternal] = null;
+ const queue = self.#readQueue;
+ if (queue.isEmpty()) {
+ if (self.push(null)) return;
+ }
+ queue.push(null);
+ }
- self.emit("connection", _socket);
- },
- handshake(socket, success, verifyError) {
- const { data: self } = socket;
- self.emit("secure", self);
-
- self._securePending = false;
- self.secureConnecting = false;
- self._secureEstablished = !!success;
-
- if (self._requestCert || self._rejectUnauthorized) {
- if (verifyError) {
- self.authorized = false;
- self.authorizationError = verifyError.code || verifyError.message;
- if (self._rejectUnauthorized) {
- self.destroy(verifyError);
- return;
- }
- }
- } else {
- self.authorized = true;
- }
- self.emit("secureConnection", verifyError);
- },
- error(socket, error) {
- Socket.#Handlers.error(socket, error);
- this.data.emit("error", error);
- },
- timeout: Socket.#Handlers.timeout,
- connectError: Socket.#Handlers.connectError,
- drain: Socket.#Handlers.drain,
- binaryType: "buffer",
- };
+ static #Drain(socket) {
+ const self = socket.data;
- bytesRead = 0;
- bytesWritten = 0;
- #closed = false;
- connecting = false;
- localAddress = "127.0.0.1";
- #readQueue = $createFIFO();
- remotePort;
- [bunSocketInternal] = null;
- [bunTLSConnectOptions] = null;
- timeout = 0;
- #writeCallback;
- #writeChunk;
- #pendingRead;
-
- isServer = false;
- _handle;
- _parent;
- _parentWrap;
- #socket;
- #upgraded;
-
- constructor(options) {
- const { socket, signal, write, read, allowHalfOpen = false, ...opts } = options || {};
- super({
- ...opts,
- allowHalfOpen,
- readable: true,
- writable: true,
- });
- this._handle = this;
- this._parent = this;
- this._parentWrap = this;
- this.#pendingRead = undefined;
- this.#upgraded = false;
- if (socket instanceof Socket) {
- this.#socket = socket;
+ const callback = self.#writeCallback;
+ if (callback) {
+ const chunk = self.#writeChunk;
+ const written = socket.write(chunk);
+
+ self.bytesWritten += written;
+ if (written < chunk.length) {
+ self.#writeChunk = chunk.slice(written);
+ } else {
+ self.#writeCallback = null;
+ self.#writeChunk = null;
+ callback(null);
}
- signal?.once("abort", () => this.destroy());
- this.once("connect", () => this.emit("ready"));
}
+ }
- address() {
- return {
- address: this.localAddress,
- family: this.localFamily,
- port: this.localPort,
- };
- }
+ static [bunSocketServerHandlers] = {
+ data: Socket.#Handlers.data,
+ close(socket) {
+ Socket.#Handlers.close(socket);
+ this.data[bunSocketServerConnections]--;
+ },
+ end(socket) {
+ Socket.#Handlers.end(socket);
+ this.data[bunSocketServerConnections]--;
+ },
+ open(socket) {
+ const self = this.data;
+ const options = self[bunSocketServerOptions];
+ const { pauseOnConnect, connectionListener, InternalSocketClass, requestCert, rejectUnauthorized } = options;
+ const _socket = new InternalSocketClass({});
+ _socket.isServer = true;
+ _socket._requestCert = requestCert;
+ _socket._rejectUnauthorized = rejectUnauthorized;
+
+ _socket.#attach(this.localPort, socket);
+ if (self.maxConnections && self[bunSocketServerConnections] >= self.maxConnections) {
+ const data = {
+ localAddress: _socket.localAddress,
+ localPort: _socket.localPort,
+ localFamily: _socket.localFamily,
+ remoteAddress: _socket.remoteAddress,
+ remotePort: _socket.remotePort,
+ remoteFamily: _socket.remoteFamily || "IPv4",
+ };
- get bufferSize() {
- return this.writableLength;
- }
+ socket.end();
- #attach(port, socket) {
- this.remotePort = port;
- socket.data = this;
- socket.timeout(this.timeout);
- socket.ref();
- this[bunSocketInternal] = socket;
- this.connecting = false;
- if (!this.#upgraded) {
- // this is not actually emitted on nodejs when socket used on the connection
- // this is already emmited on non-TLS socket and on TLS socket is emmited secureConnect after handshake
- this.emit("connect", this);
+ self.emit("drop", data);
+ return;
+ }
+ // the duplex implementation start paused, so we resume when pauseOnConnect is falsy
+ if (!pauseOnConnect) {
+ _socket.resume();
}
- Socket.#Drain(socket);
- }
- connect(port, host, connectListener) {
- var path;
- var connection = this.#socket;
- var _checkServerIdentity = undefined;
- if (typeof port === "string") {
- path = port;
- port = undefined;
+ self[bunSocketServerConnections]++;
- if (typeof host === "function") {
- connectListener = host;
- host = undefined;
+ if (typeof connectionListener == "function") {
+ if (InternalSocketClass.name === "TLSSocket") {
+ // add secureConnection event handler
+ self.once("secureConnection", () => connectionListener(_socket));
+ } else {
+ connectionListener(_socket);
}
- } else if (typeof host == "function") {
- if (typeof port === "string") {
- path = port;
- port = undefined;
+ }
+
+ self.emit("connection", _socket);
+ },
+ handshake(socket, success, verifyError) {
+ const { data: self } = socket;
+ self.emit("secure", self);
+
+ self._securePending = false;
+ self.secureConnecting = false;
+ self._secureEstablished = !!success;
+
+ if (self._requestCert || self._rejectUnauthorized) {
+ if (verifyError) {
+ self.authorized = false;
+ self.authorizationError = verifyError.code || verifyError.message;
+ if (self._rejectUnauthorized) {
+ self.destroy(verifyError);
+ return;
+ }
}
+ } else {
+ self.authorized = true;
+ }
+ self.emit("secureConnection", verifyError);
+ },
+ error(socket, error) {
+ Socket.#Handlers.error(socket, error);
+ this.data.emit("error", error);
+ },
+ timeout: Socket.#Handlers.timeout,
+ connectError: Socket.#Handlers.connectError,
+ drain: Socket.#Handlers.drain,
+ binaryType: "buffer",
+ };
+
+ bytesRead = 0;
+ bytesWritten = 0;
+ #closed = false;
+ connecting = false;
+ localAddress = "127.0.0.1";
+ #readQueue = $createFIFO();
+ remotePort;
+ [bunSocketInternal] = null;
+ [bunTLSConnectOptions] = null;
+ timeout = 0;
+ #writeCallback;
+ #writeChunk;
+ #pendingRead;
+
+ isServer = false;
+ _handle;
+ _parent;
+ _parentWrap;
+ #socket;
+ #upgraded;
+
+ constructor(options) {
+ const { socket, signal, write, read, allowHalfOpen = false, ...opts } = options || {};
+ super({
+ ...opts,
+ allowHalfOpen,
+ readable: true,
+ writable: true,
+ });
+ this._handle = this;
+ this._parent = this;
+ this._parentWrap = this;
+ this.#pendingRead = undefined;
+ this.#upgraded = false;
+ if (socket instanceof Socket) {
+ this.#socket = socket;
+ }
+ signal?.once("abort", () => this.destroy());
+ this.once("connect", () => this.emit("ready"));
+ }
+
+ address() {
+ return {
+ address: this.localAddress,
+ family: this.localFamily,
+ port: this.localPort,
+ };
+ }
+ get bufferSize() {
+ return this.writableLength;
+ }
+
+ #attach(port, socket) {
+ this.remotePort = port;
+ socket.data = this;
+ socket.timeout(this.timeout);
+ socket.ref();
+ this[bunSocketInternal] = socket;
+ this.connecting = false;
+ if (!this.#upgraded) {
+ // this is not actually emitted on nodejs when socket used on the connection
+ // this is already emmited on non-TLS socket and on TLS socket is emmited secureConnect after handshake
+ this.emit("connect", this);
+ }
+ Socket.#Drain(socket);
+ }
+
+ connect(port, host, connectListener) {
+ var path;
+ var connection = this.#socket;
+ var _checkServerIdentity = undefined;
+ if (typeof port === "string") {
+ path = port;
+ port = undefined;
+
+ if (typeof host === "function") {
connectListener = host;
host = undefined;
}
- if (typeof port == "object") {
- var {
- port,
- host,
- path,
- socket,
- // TODOs
- localAddress,
- localPort,
- family,
- hints,
- lookup,
- noDelay,
- keepAlive,
- keepAliveInitialDelay,
- requestCert,
- rejectUnauthorized,
- pauseOnConnect,
- servername,
- checkServerIdentity,
- session,
- } = port;
- _checkServerIdentity = checkServerIdentity;
- this.servername = servername;
- if (socket) {
- connection = socket;
- }
+ } else if (typeof host == "function") {
+ if (typeof port === "string") {
+ path = port;
+ port = undefined;
}
- if (!pauseOnConnect) {
- this.resume();
+ connectListener = host;
+ host = undefined;
+ }
+ if (typeof port == "object") {
+ var {
+ port,
+ host,
+ path,
+ socket,
+ // TODOs
+ localAddress,
+ localPort,
+ family,
+ hints,
+ lookup,
+ noDelay,
+ keepAlive,
+ keepAliveInitialDelay,
+ requestCert,
+ rejectUnauthorized,
+ pauseOnConnect,
+ servername,
+ checkServerIdentity,
+ session,
+ } = port;
+ _checkServerIdentity = checkServerIdentity;
+ this.servername = servername;
+ if (socket) {
+ connection = socket;
}
- this.connecting = true;
- this.remotePort = port;
+ }
- const bunTLS = this[bunTlsSymbol];
- var tls = undefined;
+ if (!pauseOnConnect) {
+ this.resume();
+ }
+ this.connecting = true;
+ this.remotePort = port;
- if (typeof bunTLS === "function") {
- tls = bunTLS.call(this, port, host, true);
- // Client always request Cert
- this._requestCert = true;
- this._rejectUnauthorized = rejectUnauthorized;
-
- if (tls) {
- tls.rejectUnauthorized = rejectUnauthorized;
- tls.requestCert = true;
- tls.session = session || tls.session;
- this.servername = tls.servername;
- tls.checkServerIdentity = _checkServerIdentity || tls.checkServerIdentity;
- this[bunTLSConnectOptions] = tls;
- if (!connection && tls.socket) {
- connection = tls.socket;
- }
- }
- if (connection) {
- if (
- typeof connection !== "object" ||
- !(connection instanceof Socket) ||
- typeof connection[bunTlsSymbol] === "function"
- ) {
- throw new TypeError("socket must be an instance of net.Socket");
- }
- }
- this.authorized = false;
- this.secureConnecting = true;
- this._secureEstablished = false;
- this._securePending = true;
+ const bunTLS = this[bunTlsSymbol];
+ var tls = undefined;
- if (connectListener) this.on("secureConnect", connectListener);
- } else if (connectListener) this.on("connect", connectListener);
- // start using existing connection
+ if (typeof bunTLS === "function") {
+ tls = bunTLS.call(this, port, host, true);
+ // Client always request Cert
+ this._requestCert = true;
+ this._rejectUnauthorized = rejectUnauthorized;
+ if (tls) {
+ tls.rejectUnauthorized = rejectUnauthorized;
+ tls.requestCert = true;
+ tls.session = session || tls.session;
+ this.servername = tls.servername;
+ tls.checkServerIdentity = _checkServerIdentity || tls.checkServerIdentity;
+ this[bunTLSConnectOptions] = tls;
+ if (!connection && tls.socket) {
+ connection = tls.socket;
+ }
+ }
if (connection) {
- const socket = connection[bunSocketInternal];
+ if (
+ typeof connection !== "object" ||
+ !(connection instanceof Socket) ||
+ typeof connection[bunTlsSymbol] === "function"
+ ) {
+ throw new TypeError("socket must be an instance of net.Socket");
+ }
+ }
+ this.authorized = false;
+ this.secureConnecting = true;
+ this._secureEstablished = false;
+ this._securePending = true;
+
+ if (connectListener) this.on("secureConnect", connectListener);
+ } else if (connectListener) this.on("connect", connectListener);
+ // start using existing connection
+
+ if (connection) {
+ const socket = connection[bunSocketInternal];
+
+ if (socket) {
+ this.connecting = true;
+ this.#upgraded = true;
+ const result = socket.upgradeTLS({
+ data: this,
+ tls,
+ socket: Socket.#Handlers,
+ });
+ if (result) {
+ const [raw, tls] = result;
+ // replace socket
+ connection[bunSocketInternal] = raw;
+ raw.timeout(raw.timeout);
+ raw.connecting = false;
+ this[bunSocketInternal] = tls;
+ } else {
+ this[bunSocketInternal] = null;
+ throw new Error("Invalid socket");
+ }
+ } else {
+ // wait to be connected
+ connection.once("connect", () => {
+ const socket = connection[bunSocketInternal];
+ if (!socket) return;
- if (socket) {
this.connecting = true;
this.#upgraded = true;
const result = socket.upgradeTLS({
@@ -461,6 +466,7 @@ const Socket = (function (InternalSocket) {
tls,
socket: Socket.#Handlers,
});
+
if (result) {
const [raw, tls] = result;
// replace socket
@@ -472,161 +478,136 @@ const Socket = (function (InternalSocket) {
this[bunSocketInternal] = null;
throw new Error("Invalid socket");
}
- } else {
- // wait to be connected
- connection.once("connect", () => {
- const socket = connection[bunSocketInternal];
- if (!socket) return;
-
- this.connecting = true;
- this.#upgraded = true;
- const result = socket.upgradeTLS({
- data: this,
- tls,
- socket: Socket.#Handlers,
- });
-
- if (result) {
- const [raw, tls] = result;
- // replace socket
- connection[bunSocketInternal] = raw;
- raw.timeout(raw.timeout);
- raw.connecting = false;
- this[bunSocketInternal] = tls;
- } else {
- this[bunSocketInternal] = null;
- throw new Error("Invalid socket");
- }
- });
- }
- } else if (path) {
- // start using unix socket
- bunConnect({
- data: this,
- unix: path,
- socket: Socket.#Handlers,
- tls,
- }).catch(error => {
- this.emit("error", error);
- });
- } else {
- // default start
- bunConnect({
- data: this,
- hostname: host || "localhost",
- port: port,
- socket: Socket.#Handlers,
- tls,
- }).catch(error => {
- this.emit("error", error);
});
}
- return this;
+ } else if (path) {
+ // start using unix socket
+ bunConnect({
+ data: this,
+ unix: path,
+ socket: Socket.#Handlers,
+ tls,
+ }).catch(error => {
+ this.emit("error", error);
+ });
+ } else {
+ // default start
+ bunConnect({
+ data: this,
+ hostname: host || "localhost",
+ port: port,
+ socket: Socket.#Handlers,
+ tls,
+ }).catch(error => {
+ this.emit("error", error);
+ });
}
+ return this;
+ }
- _destroy(err, callback) {
- this[bunSocketInternal]?.end();
- callback(err);
- }
+ _destroy(err, callback) {
+ this[bunSocketInternal]?.end();
+ callback(err);
+ }
- _final(callback) {
- this[bunSocketInternal]?.end();
- callback();
- }
+ _final(callback) {
+ this[bunSocketInternal]?.end();
+ callback();
+ }
- get localAddress() {
- return "127.0.0.1";
- }
+ get localAddress() {
+ return "127.0.0.1";
+ }
- get localFamily() {
- return "IPv4";
- }
+ get localFamily() {
+ return "IPv4";
+ }
- get localPort() {
- return this[bunSocketInternal]?.localPort;
- }
+ get localPort() {
+ return this[bunSocketInternal]?.localPort;
+ }
- get pending() {
- return this.connecting;
- }
+ get pending() {
+ return this.connecting;
+ }
- _read(size) {
- const queue = this.#readQueue;
- let chunk;
- while ((chunk = queue.peek())) {
- if (!this.push(chunk)) return;
- queue.shift();
- }
+ _read(size) {
+ const queue = this.#readQueue;
+ let chunk;
+ while ((chunk = queue.peek())) {
+ if (!this.push(chunk)) return;
+ queue.shift();
}
+ }
- get readyState() {
- if (this.connecting) return "opening";
- if (this.readable) {
- return this.writable ? "open" : "readOnly";
- } else {
- return this.writable ? "writeOnly" : "closed";
- }
+ get readyState() {
+ if (this.connecting) return "opening";
+ if (this.readable) {
+ return this.writable ? "open" : "readOnly";
+ } else {
+ return this.writable ? "writeOnly" : "closed";
}
+ }
- ref() {
- this[bunSocketInternal]?.ref();
- }
+ ref() {
+ this[bunSocketInternal]?.ref();
+ }
- get remoteAddress() {
- return this[bunSocketInternal]?.remoteAddress;
- }
+ get remoteAddress() {
+ return this[bunSocketInternal]?.remoteAddress;
+ }
- get remoteFamily() {
- return "IPv4";
- }
+ get remoteFamily() {
+ return "IPv4";
+ }
- resetAndDestroy() {
- this[bunSocketInternal]?.end();
- }
+ resetAndDestroy() {
+ this[bunSocketInternal]?.end();
+ }
- setKeepAlive(enable = false, initialDelay = 0) {
- // TODO
- return this;
- }
+ setKeepAlive(enable = false, initialDelay = 0) {
+ // TODO
+ return this;
+ }
- setNoDelay(noDelay = true) {
- // TODO
- return this;
- }
+ setNoDelay(noDelay = true) {
+ // TODO
+ return this;
+ }
- setTimeout(timeout, callback) {
- this[bunSocketInternal]?.timeout(timeout);
- this.timeout = timeout;
- if (callback) this.once("timeout", callback);
- return this;
- }
+ setTimeout(timeout, callback) {
+ this[bunSocketInternal]?.timeout(timeout);
+ this.timeout = timeout;
+ if (callback) this.once("timeout", callback);
+ return this;
+ }
- unref() {
- this[bunSocketInternal]?.unref();
- }
+ unref() {
+ this[bunSocketInternal]?.unref();
+ }
- _write(chunk, encoding, callback) {
- if (typeof chunk == "string" && encoding !== "ascii") chunk = Buffer.from(chunk, encoding);
- var written = this[bunSocketInternal]?.write(chunk);
- if (written == chunk.length) {
- callback();
- } else if (this.#writeCallback) {
- callback(new Error("overlapping _write()"));
- } else {
- if (written > 0) {
- if (typeof chunk == "string") {
- chunk = chunk.slice(written);
- } else {
- chunk = chunk.subarray(written);
- }
+ _write(chunk, encoding, callback) {
+ if (typeof chunk == "string" && encoding !== "ascii") chunk = Buffer.from(chunk, encoding);
+ var written = this[bunSocketInternal]?.write(chunk);
+ if (written == chunk.length) {
+ callback();
+ } else if (this.#writeCallback) {
+ callback(new Error("overlapping _write()"));
+ } else {
+ if (written > 0) {
+ if (typeof chunk == "string") {
+ chunk = chunk.slice(written);
+ } else {
+ chunk = chunk.subarray(written);
}
-
- this.#writeCallback = callback;
- this.#writeChunk = chunk;
}
+
+ this.#writeCallback = callback;
+ this.#writeChunk = chunk;
}
- },
-);
+ }
+}
+es5ClassCompat(Socket);
function createConnection(port, host, connectListener) {
if (typeof port === "object") {
@@ -861,6 +842,7 @@ class Server extends EventEmitter {
return this;
}
}
+es5ClassCompat(Server);
function emitErrorNextTick(self, error) {
self.emit("error", error);
@@ -890,5 +872,4 @@ export default {
isIPv4,
isIPv6,
Socket,
- [Symbol.for("::bunternal::")]: SocketClass,
};
diff --git a/src/js/node/tls.js b/src/js/node/tls.js
index fc2d9065a..2ed350e02 100644
--- a/src/js/node/tls.js
+++ b/src/js/node/tls.js
@@ -1,7 +1,8 @@
// Hardcoded module "node:tls"
const { isArrayBufferView, isTypedArray } = require("node:util/types");
+const { es5ClassCompat } = require("$shared");
const net = require("node:net");
-const { Server: NetServer, [Symbol.for("::bunternal::")]: InternalTCPSocket } = net;
+const { Server: NetServer, Socket: TCPSocket } = net;
const bunSocketInternal = Symbol.for("::bunnetsocketinternal::");
const { rootCertificates, canonicalizeIP } = $lazy("internal/tls");
@@ -203,7 +204,7 @@ function checkServerIdentity(hostname, cert) {
}
}
-var InternalSecureContext = class SecureContext {
+class SecureContext {
context;
constructor(options) {
@@ -258,11 +259,8 @@ var InternalSecureContext = class SecureContext {
}
this.context = context;
}
-};
-
-function SecureContext(options) {
- return new InternalSecureContext(options);
}
+es5ClassCompat(SecureContext);
function createSecureContext(options) {
return new SecureContext(options);
@@ -299,188 +297,168 @@ function translatePeerCertificate(c) {
const buntls = Symbol.for("::buntls::");
-var SocketClass;
-const TLSSocket = (function (InternalTLSSocket) {
- SocketClass = InternalTLSSocket;
- Object.defineProperty(SocketClass.prototype, Symbol.toStringTag, {
- value: "TLSSocket",
- enumerable: false,
- });
-
- return Object.defineProperty(
- function Socket(options) {
- return new InternalTLSSocket(options);
- },
- Symbol.hasInstance,
- {
- value(instance) {
- return instance instanceof InternalTLSSocket;
- },
- },
- );
-})(
- class TLSSocket extends InternalTCPSocket {
- #secureContext;
- ALPNProtocols;
- #socket;
- #checkServerIdentity;
- #session;
-
- constructor(socket, options) {
- super(socket instanceof InternalTCPSocket ? options : options || socket);
- options = options || socket || {};
- if (typeof options === "object") {
- const { ALPNProtocols } = options;
- if (ALPNProtocols) {
- convertALPNProtocols(ALPNProtocols, this);
- }
- if (socket instanceof InternalTCPSocket) {
- this.#socket = socket;
- }
+class TLSSocket extends TCPSocket {
+ #secureContext;
+ ALPNProtocols;
+ #socket;
+ #checkServerIdentity;
+ #session;
+
+ constructor(socket, options) {
+ super(socket instanceof TCPSocket ? options : options || socket);
+ options = options || socket || {};
+ if (typeof options === "object") {
+ const { ALPNProtocols } = options;
+ if (ALPNProtocols) {
+ convertALPNProtocols(ALPNProtocols, this);
+ }
+ if (socket instanceof TCPSocket) {
+ this.#socket = socket;
}
-
- this.#secureContext = options.secureContext || createSecureContext(options);
- this.authorized = false;
- this.secureConnecting = true;
- this._secureEstablished = false;
- this._securePending = true;
- this.#checkServerIdentity = options.checkServerIdentity || checkServerIdentity;
- this.#session = options.session || null;
}
- _secureEstablished = false;
- _securePending = true;
- _newSessionPending;
- _controlReleased;
- secureConnecting = false;
- _SNICallback;
- servername;
- authorized = false;
- authorizationError;
- #renegotiationDisabled = false;
-
- encrypted = true;
-
- _start() {
- // some frameworks uses this _start internal implementation is suposed to start TLS handshake/connect
- this.connect();
- }
+ this.#secureContext = options.secureContext || createSecureContext(options);
+ this.authorized = false;
+ this.secureConnecting = true;
+ this._secureEstablished = false;
+ this._securePending = true;
+ this.#checkServerIdentity = options.checkServerIdentity || checkServerIdentity;
+ this.#session = options.session || null;
+ }
- getSession() {
- return this[bunSocketInternal]?.getSession();
- }
+ _secureEstablished = false;
+ _securePending = true;
+ _newSessionPending;
+ _controlReleased;
+ secureConnecting = false;
+ _SNICallback;
+ servername;
+ authorized = false;
+ authorizationError;
+ #renegotiationDisabled = false;
- getEphemeralKeyInfo() {
- return this[bunSocketInternal]?.getEphemeralKeyInfo();
- }
+ encrypted = true;
- getCipher() {
- return this[bunSocketInternal]?.getCipher();
- }
+ _start() {
+ // some frameworks uses this _start internal implementation is suposed to start TLS handshake/connect
+ this.connect();
+ }
- getSharedSigalgs() {
- return this[bunSocketInternal]?.getSharedSigalgs();
- }
+ getSession() {
+ return this[bunSocketInternal]?.getSession();
+ }
- getProtocol() {
- return this[bunSocketInternal]?.getTLSVersion();
- }
+ getEphemeralKeyInfo() {
+ return this[bunSocketInternal]?.getEphemeralKeyInfo();
+ }
- getFinished() {
- return this[bunSocketInternal]?.getTLSFinishedMessage() || undefined;
- }
+ getCipher() {
+ return this[bunSocketInternal]?.getCipher();
+ }
- getPeerFinished() {
- return this[bunSocketInternal]?.getTLSPeerFinishedMessage() || undefined;
- }
- isSessionReused() {
- return !!this.#session;
- }
+ getSharedSigalgs() {
+ return this[bunSocketInternal]?.getSharedSigalgs();
+ }
- renegotiate() {
- if (this.#renegotiationDisabled) {
- const error = new Error("ERR_TLS_RENEGOTIATION_DISABLED: TLS session renegotiation disabled for this socket");
- error.name = "ERR_TLS_RENEGOTIATION_DISABLED";
- throw error;
- }
+ getProtocol() {
+ return this[bunSocketInternal]?.getTLSVersion();
+ }
- throw Error("Not implented in Bun yet");
- }
- disableRenegotiation() {
- this.#renegotiationDisabled = true;
- }
- getTLSTicket() {
- return this[bunSocketInternal]?.getTLSTicket();
- }
- exportKeyingMaterial(length, label, context) {
- if (context) {
- return this[bunSocketInternal]?.exportKeyingMaterial(length, label, context);
- }
- return this[bunSocketInternal]?.exportKeyingMaterial(length, label);
+ getFinished() {
+ return this[bunSocketInternal]?.getTLSFinishedMessage() || undefined;
+ }
+
+ getPeerFinished() {
+ return this[bunSocketInternal]?.getTLSPeerFinishedMessage() || undefined;
+ }
+ isSessionReused() {
+ return !!this.#session;
+ }
+
+ renegotiate() {
+ if (this.#renegotiationDisabled) {
+ const error = new Error("ERR_TLS_RENEGOTIATION_DISABLED: TLS session renegotiation disabled for this socket");
+ error.name = "ERR_TLS_RENEGOTIATION_DISABLED";
+ throw error;
}
- setMaxSendFragment(size) {
- return this[bunSocketInternal]?.setMaxSendFragment(size) || false;
+ throw Error("Not implented in Bun yet");
+ }
+ disableRenegotiation() {
+ this.#renegotiationDisabled = true;
+ }
+ getTLSTicket() {
+ return this[bunSocketInternal]?.getTLSTicket();
+ }
+ exportKeyingMaterial(length, label, context) {
+ if (context) {
+ return this[bunSocketInternal]?.exportKeyingMaterial(length, label, context);
}
+ return this[bunSocketInternal]?.exportKeyingMaterial(length, label);
+ }
- // only for debug purposes so we just mock for now
- enableTrace() {}
+ setMaxSendFragment(size) {
+ return this[bunSocketInternal]?.setMaxSendFragment(size) || false;
+ }
- setServername(name) {
- if (this.isServer) {
- let error = new Error("ERR_TLS_SNI_FROM_SERVER: Cannot issue SNI from a TLS server-side socket");
- error.name = "ERR_TLS_SNI_FROM_SERVER";
- throw error;
- }
- // if the socket is detached we can't set the servername but we set this property so when open will auto set to it
- this.servername = name;
- this[bunSocketInternal]?.setServername(name);
- }
- setSession(session) {
- this.#session = session;
- if (typeof session === "string") session = Buffer.from(session, "latin1");
- return this[bunSocketInternal]?.setSession(session);
- }
- getPeerCertificate(abbreviated) {
- const cert =
- arguments.length < 1
- ? this[bunSocketInternal]?.getPeerCertificate()
- : this[bunSocketInternal]?.getPeerCertificate(abbreviated);
- if (cert) {
- return translatePeerCertificate(cert);
- }
- }
- getCertificate() {
- // need to implement certificate on socket.zig
- const cert = this[bunSocketInternal]?.getCertificate();
- if (cert) {
- // It's not a peer cert, but the formatting is identical.
- return translatePeerCertificate(cert);
- }
+ // only for debug purposes so we just mock for now
+ enableTrace() {}
+
+ setServername(name) {
+ if (this.isServer) {
+ let error = new Error("ERR_TLS_SNI_FROM_SERVER: Cannot issue SNI from a TLS server-side socket");
+ error.name = "ERR_TLS_SNI_FROM_SERVER";
+ throw error;
}
- getPeerX509Certificate() {
- throw Error("Not implented in Bun yet");
+ // if the socket is detached we can't set the servername but we set this property so when open will auto set to it
+ this.servername = name;
+ this[bunSocketInternal]?.setServername(name);
+ }
+ setSession(session) {
+ this.#session = session;
+ if (typeof session === "string") session = Buffer.from(session, "latin1");
+ return this[bunSocketInternal]?.setSession(session);
+ }
+ getPeerCertificate(abbreviated) {
+ const cert =
+ arguments.length < 1
+ ? this[bunSocketInternal]?.getPeerCertificate()
+ : this[bunSocketInternal]?.getPeerCertificate(abbreviated);
+ if (cert) {
+ return translatePeerCertificate(cert);
}
- getX509Certificate() {
- throw Error("Not implented in Bun yet");
+ }
+ getCertificate() {
+ // need to implement certificate on socket.zig
+ const cert = this[bunSocketInternal]?.getCertificate();
+ if (cert) {
+ // It's not a peer cert, but the formatting is identical.
+ return translatePeerCertificate(cert);
}
+ }
+ getPeerX509Certificate() {
+ throw Error("Not implented in Bun yet");
+ }
+ getX509Certificate() {
+ throw Error("Not implented in Bun yet");
+ }
- get alpnProtocol() {
- return this[bunSocketInternal]?.alpnProtocol;
- }
+ get alpnProtocol() {
+ return this[bunSocketInternal]?.alpnProtocol;
+ }
- [buntls](port, host) {
- return {
- socket: this.#socket,
- ALPNProtocols: this.ALPNProtocols,
- serverName: this.servername || host || "localhost",
- checkServerIdentity: this.#checkServerIdentity,
- session: this.#session,
- ...this.#secureContext,
- };
- }
- },
-);
+ [buntls](port, host) {
+ return {
+ socket: this.#socket,
+ ALPNProtocols: this.ALPNProtocols,
+ serverName: this.servername || host || "localhost",
+ checkServerIdentity: this.#checkServerIdentity,
+ session: this.#session,
+ ...this.#secureContext,
+ };
+ }
+}
+es5ClassCompat(TLSSocket);
class Server extends NetServer {
key;