diff --git a/src/bun.js/bindings/ErrorCode.cpp b/src/bun.js/bindings/ErrorCode.cpp index 8fbaf2395c..1084365397 100644 --- a/src/bun.js/bindings/ErrorCode.cpp +++ b/src/bun.js/bindings/ErrorCode.cpp @@ -2382,6 +2382,17 @@ JSC_DEFINE_HOST_FUNCTION(Bun::jsFunctionMakeErrorWithCode, (JSC::JSGlobalObject return JSC::JSValue::encode(err); } + case Bun::ErrorCode::ERR_HTTP_CONTENT_LENGTH_MISMATCH: { + auto arg0 = callFrame->argument(1); + auto str0 = arg0.toWTFString(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + auto arg1 = callFrame->argument(2); + auto str1 = arg1.toWTFString(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + auto message = makeString("Response body's content-length of "_s, str0, " byte(s) does not match the content-length of "_s, str1, " byte(s) set in header"_s); + return JSC::JSValue::encode(createError(globalObject, ErrorCode::ERR_HTTP_CONTENT_LENGTH_MISMATCH, message)); + } + case ErrorCode::ERR_IPC_DISCONNECTED: return JSC::JSValue::encode(createError(globalObject, ErrorCode::ERR_IPC_DISCONNECTED, "IPC channel is already disconnected"_s)); case ErrorCode::ERR_SERVER_NOT_RUNNING: @@ -2518,6 +2529,8 @@ JSC_DEFINE_HOST_FUNCTION(Bun::jsFunctionMakeErrorWithCode, (JSC::JSGlobalObject return JSC::JSValue::encode(createError(globalObject, ErrorCode::ERR_HTTP2_TOO_MANY_INVALID_FRAMES, "Too many invalid HTTP/2 frames"_s)); case ErrorCode::ERR_HTTP2_PING_CANCEL: return JSC::JSValue::encode(createError(globalObject, ErrorCode::ERR_HTTP2_PING_CANCEL, "HTTP2 ping cancelled"_s)); + case ErrorCode::ERR_HTTP_TRAILER_INVALID: + return JSC::JSValue::encode(createError(globalObject, ErrorCode::ERR_HTTP_TRAILER_INVALID, "Trailers are invalid with this transfer encoding"_s)); default: { break; diff --git a/src/bun.js/bindings/ErrorCode.ts b/src/bun.js/bindings/ErrorCode.ts index 047aa1eab1..cdf4c6f92b 100644 --- a/src/bun.js/bindings/ErrorCode.ts +++ b/src/bun.js/bindings/ErrorCode.ts @@ -209,6 +209,7 @@ const errors: ErrorCodeMapping = [ ["ERR_POSTGRES_UNSUPPORTED_INTEGER_SIZE", TypeError, "PostgresError"], ["ERR_POSTGRES_UNSUPPORTED_NUMERIC_FORMAT", TypeError, "PostgresError"], ["ERR_PROXY_INVALID_CONFIG", Error], + ["ERR_PROXY_TUNNEL", Error], ["ERR_MYSQL_CONNECTION_CLOSED", Error, "MySQLError"], ["ERR_MYSQL_CONNECTION_TIMEOUT", Error, "MySQLError"], ["ERR_MYSQL_IDLE_TIMEOUT", Error, "MySQLError"], diff --git a/src/codegen/bundle-modules.ts b/src/codegen/bundle-modules.ts index f539171ef6..75bac0b530 100644 --- a/src/codegen/bundle-modules.ts +++ b/src/codegen/bundle-modules.ts @@ -511,6 +511,7 @@ declare module "module" { dts += ` (id: "bun"): typeof import("bun");\n`; dts += ` (id: "bun:test"): typeof import("bun:test");\n`; dts += ` (id: "bun:jsc"): typeof import("bun:jsc");\n`; + dts += ` (id: "node:util/types"): typeof import("node:util/types");\n`; for (let i = 0; i < nativeStartIndex; i++) { const id = moduleList[i]; diff --git a/src/js/builtins.d.ts b/src/js/builtins.d.ts index 570922df8c..6b780c0ef1 100644 --- a/src/js/builtins.d.ts +++ b/src/js/builtins.d.ts @@ -766,6 +766,8 @@ declare function $ERR_ASYNC_CALLBACK(name): TypeError; declare function $ERR_AMBIGUOUS_ARGUMENT(arg, message): TypeError; declare function $ERR_INVALID_FD_TYPE(type): TypeError; declare function $ERR_IP_BLOCKED(ip): Error; +declare function $ERR_HTTP_CONTENT_LENGTH_MISMATCH(bodylen: number, headerlen: number): Error; +declare function $ERR_PROXY_TUNNEL(msg: string): Error; declare function $ERR_IPC_DISCONNECTED(): Error; declare function $ERR_SERVER_NOT_RUNNING(): Error; @@ -837,6 +839,7 @@ declare function $ERR_HTTP2_CONNECT_SCHEME(): Error; declare function $ERR_HTTP2_CONNECT_PATH(): Error; declare function $ERR_HTTP2_TOO_MANY_INVALID_FRAMES(): Error; declare function $ERR_HTTP2_PING_CANCEL(): Error; +declare function $ERR_HTTP_TRAILER_INVALID(): Error; /** * Convert a function to a class-like object. diff --git a/src/js/internal/url.ts b/src/js/internal/url.ts index a863cd7185..bee821cd80 100644 --- a/src/js/internal/url.ts +++ b/src/js/internal/url.ts @@ -19,6 +19,11 @@ function urlToHttpOptions(url) { return options; } +function isURL(self) { + return Boolean(self?.href && self.protocol && self.auth === undefined && self.path === undefined); +} + export default { urlToHttpOptions, + isURL, }; diff --git a/src/js/node/_http_agent.ts b/src/js/node/_http_agent.ts index c9f62b96c6..cb4a8c9427 100644 --- a/src/js/node/_http_agent.ts +++ b/src/js/node/_http_agent.ts @@ -1,3 +1,5 @@ +// Hardcoded module "node:_http_agent" + const EventEmitter = require("node:events"); const { parseProxyConfigFromEnv, kProxyConfig, checkShouldUseProxy, kWaitForProxyTunnel } = require("internal/http"); const { getLazy, kEmptyObject, once } = require("internal/shared"); @@ -146,7 +148,7 @@ Agent.prototype.createConnection = function createConnection(...args) { const shouldUseProxy = checkShouldUseProxy(this[kProxyConfig], options); $debug(`http createConnection should use proxy for ${options.host}:${options.port}:`, shouldUseProxy); if (!shouldUseProxy) { - // @ts-ignore + // @ts-expect-error return net().createConnection(...args); } @@ -155,10 +157,10 @@ Agent.prototype.createConnection = function createConnection(...args) { }; const proxyProtocol = this[kProxyConfig].protocol; if (proxyProtocol === "http:") { - // @ts-ignore + // @ts-expect-error return net().connect(connectOptions, cb); } else if (proxyProtocol === "https:") { - // @ts-ignore + // @ts-expect-error return tls().connect(connectOptions, cb); } // This should be unreachable because proxy config should be null for other protocols. diff --git a/src/js/node/_http_client.ts b/src/js/node/_http_client.ts index 9cd64e9994..79f48065df 100644 --- a/src/js/node/_http_client.ts +++ b/src/js/node/_http_client.ts @@ -1,3 +1,5 @@ +// Hardcoded module "node:_http_client" + const { isIP, isIPv6 } = require("internal/net/isIP"); const { checkIsHttpToken, validateFunction, validateInteger, validateBoolean } = require("internal/validators"); @@ -67,7 +69,7 @@ function emitErrorEventNT(self, err) { } } -function ClientRequest(input, options, cb) { +function ClientRequest(input, options, cb): void { if (!(this instanceof ClientRequest)) { return new (ClientRequest as any)(input, options, cb); } @@ -910,11 +912,11 @@ function ClientRequest(input, options, cb) { this[kEmitState] = 0; this.setSocketKeepAlive = (_enable = true, _initialDelay = 0) => { - $debug(`${NODE_HTTP_WARNING}\n`, "WARN: ClientRequest.setSocketKeepAlive is a no-op"); + $debug("WARN: ClientRequest.setSocketKeepAlive is a no-op"); }; this.setNoDelay = (_noDelay = true) => { - $debug(`${NODE_HTTP_WARNING}\n`, "WARN: ClientRequest.setNoDelay is a no-op"); + $debug("WARN: ClientRequest.setNoDelay is a no-op"); }; this[kClearTimeout] = () => { diff --git a/src/js/node/_http_incoming.ts b/src/js/node/_http_incoming.ts index 83de7a47c7..1cae02ed33 100644 --- a/src/js/node/_http_incoming.ts +++ b/src/js/node/_http_incoming.ts @@ -1,3 +1,5 @@ +// Hardcoded module "node:_http_incoming" + const Readable = require("internal/streams/readable"); const { diff --git a/src/js/node/_http_outgoing.ts b/src/js/node/_http_outgoing.ts index 8a695ce873..a03e01ac91 100644 --- a/src/js/node/_http_outgoing.ts +++ b/src/js/node/_http_outgoing.ts @@ -1,3 +1,5 @@ +// Hardcoded module "node:_http_outgoing" + const { Stream } = require("internal/stream"); const { isUint8Array, validateString } = require("internal/validators"); const { deprecate } = require("internal/util/deprecate"); diff --git a/src/js/node/https.ts b/src/js/node/https.ts index f140f45839..9bf83f69ee 100644 --- a/src/js/node/https.ts +++ b/src/js/node/https.ts @@ -1,13 +1,18 @@ // Hardcoded module "node:https" + const http = require("node:http"); const { urlToHttpOptions } = require("internal/url"); +const { kEmptyObject, once } = require("internal/shared"); +const { kProxyConfig, checkShouldUseProxy, kWaitForProxyTunnel } = require("internal/http"); +const tls = require("node:tls"); +const net = require("node:net"); const ArrayPrototypeShift = Array.prototype.shift; const ObjectAssign = Object.assign; const ArrayPrototypeUnshift = Array.prototype.unshift; function request(...args) { - let options = {}; + let options: any = {}; if (typeof args[0] === "string") { const urlStr = ArrayPrototypeShift.$call(args); @@ -20,7 +25,7 @@ function request(...args) { ObjectAssign.$call(null, options, ArrayPrototypeShift.$call(args)); } - options._defaultAgent = https.globalAgent; + options._defaultAgent = globalAgent; ArrayPrototypeUnshift.$call(args, options); return new http.ClientRequest(...args); @@ -32,24 +37,380 @@ function get(input, options, cb) { return req; } -function Agent(options) { +// When proxying a HTTPS request, the following needs to be done: +// https://datatracker.ietf.org/doc/html/rfc9110#CONNECT +// 1. Send a CONNECT request to the proxy server. +// 2. Wait for 200 connection established response to establish the tunnel. +// 3. Perform TLS handshake with the endpoint over the socket. +// 4. Tunnel the request using the established connection. +// +// This function computes the tunnel configuration for HTTPS requests. +// The handling of the tunnel connection is done in createConnection. +function getTunnelConfigForProxiedHttps(agent, reqOptions) { + if (!agent[kProxyConfig]) return null; + if ((reqOptions.protocol || agent.protocol) !== "https:") return null; + const shouldUseProxy = checkShouldUseProxy(agent[kProxyConfig], reqOptions); + $debug(`getTunnelConfigForProxiedHttps should use proxy for ${reqOptions.host}:${reqOptions.port}:`, shouldUseProxy); + if (!shouldUseProxy) return null; + const { auth, href } = agent[kProxyConfig]; + // The request is a HTTPS request, assemble the payload for establishing the tunnel. + const ipType = net.isIP(reqOptions.host); + // The request target must put IPv6 address in square brackets. + // Here reqOptions is already processed by urlToHttpOptions so we'll add them back if necessary. + // See https://www.rfc-editor.org/rfc/rfc3986#section-3.2.2 + const requestHost = ipType === 6 ? `[${reqOptions.host}]` : reqOptions.host; + const requestPort = reqOptions.port || agent.defaultPort; + const endpoint = `${requestHost}:${requestPort}`; + // The ClientRequest constructor should already have validated the host and the port. + // When the request options come from a string invalid characters would be stripped away, + // when it's an object ERR_INVALID_CHAR would be thrown. Here we just assert in case + // agent.createConnection() is called with invalid options. + $assert(!endpoint.includes("\r")); + $assert(!endpoint.includes("\n")); + + let payload = `CONNECT ${endpoint} HTTP/1.1\r\n`; + if (auth) payload += `proxy-authorization: ${auth}\r\n`; + if (agent.keepAlive || agent.maxSockets !== Infinity) payload += "proxy-connection: keep-alive\r\n"; + payload += `host: ${endpoint}`; + payload += "\r\n\r\n"; + + const result = { + __proto__: null, + proxyTunnelPayload: payload, + requestOptions: { + __proto__: null, + servername: reqOptions.servername || ipType ? undefined : reqOptions.host, + ...reqOptions, + }, + }; + $debug(`updated request for HTTPS proxy ${href} with`, result); + return result; +} + +function establishTunnel(agent, socket, options, tunnelConfig, afterSocket) { + const { proxyTunnelPayload } = tunnelConfig; + // By default, the socket is in paused mode. Read to look for the 200 connection established response. + function read() { + let chunk; + while ((chunk = socket.read()) !== null) { + if (onProxyData(chunk) !== -1) { + break; + } + } + socket.on("readable", read); + } + + function cleanup() { + socket.removeListener("end", onProxyEnd); + socket.removeListener("error", onProxyError); + socket.removeListener("readable", read); + socket.setTimeout(0); // Clear the timeout for the tunnel establishment. + } + + function onProxyError(err) { + $debug("onProxyError", err); + cleanup(); + afterSocket(err, socket); + } + + // Read the headers from the chunks and check for the status code. If it fails we + // clean up the socket and return an error. Otherwise we establish the tunnel. + let buffer = ""; + function onProxyData(chunk) { + const str = chunk.toString(); + $debug("onProxyData", str); + buffer += str; + const headerEndIndex = buffer.indexOf("\r\n\r\n"); + if (headerEndIndex === -1) return headerEndIndex; + const statusLine = buffer.substring(0, buffer.indexOf("\r\n")); + const statusCode = statusLine.split(" ")[1]; + if (statusCode !== "200") { + $debug(`onProxyData receives ${statusCode}, cleaning up`); + cleanup(); + const targetHost = proxyTunnelPayload.split("\r")[0].split(" ")[1]; + const message = `Failed to establish tunnel to ${targetHost} via ${agent[kProxyConfig].href}: ${statusLine}`; + const err = $ERR_PROXY_TUNNEL(message); + // @ts-expect-error + err.statusCode = Number.parseInt(statusCode); + afterSocket(err, socket); + } else { + // https://datatracker.ietf.org/doc/html/rfc9110#CONNECT + // RFC 9110 says that it can be 2xx but in the real world, proxy clients generally only accepts 200. + // Proxy servers are not supposed to send anything after the headers - the payload must be + // be empty. So after this point we will proceed with the tunnel e.g. starting TLS handshake. + $debug("onProxyData receives 200, establishing tunnel"); + cleanup(); + + // Reuse the tunneled socket to perform the TLS handshake with the endpoint, then send the request. + const { requestOptions } = tunnelConfig; + tunnelConfig.requestOptions = null; + requestOptions.socket = socket; + let tunneldSocket; + const onTLSHandshakeError = err => { + $debug("Propagate error event from tunneled socket to tunnel socket"); + afterSocket(err, tunneldSocket); + }; + tunneldSocket = tls.connect(requestOptions, () => { + $debug("TLS handshake over tunnel succeeded"); + tunneldSocket.removeListener("error", onTLSHandshakeError); + afterSocket(null, tunneldSocket); + }); + tunneldSocket.on("free", () => { + $debug("Propagate free event from tunneled socket to tunnel socket"); + socket.emit("free"); + }); + tunneldSocket.on("error", onTLSHandshakeError); + } + return headerEndIndex; + } + + function onProxyEnd() { + cleanup(); + const err = $ERR_PROXY_TUNNEL("Connection to establish proxy tunnel ended unexpectedly"); + afterSocket(err, socket); + } + + const proxyTunnelTimeout = tunnelConfig.requestOptions.timeout; + $debug("proxyTunnelTimeout", proxyTunnelTimeout, options.timeout); + // It may be worth a separate timeout error/event. + // But it also makes sense to treat the tunnel establishment timeout as a normal timeout for the request. + function onProxyTimeout() { + $debug("onProxyTimeout", proxyTunnelTimeout); + cleanup(); + const err = $ERR_PROXY_TUNNEL(`Connection to establish proxy tunnel timed out after ${proxyTunnelTimeout}ms`); + // @ts-expect-error + err.proxyTunnelTimeout = proxyTunnelTimeout; + afterSocket(err, socket); + } + + if (proxyTunnelTimeout && proxyTunnelTimeout > 0) { + $debug("proxy tunnel setTimeout", proxyTunnelTimeout); + socket.setTimeout(proxyTunnelTimeout, onProxyTimeout); + } + + socket.on("error", onProxyError); + socket.on("end", onProxyEnd); + socket.write(proxyTunnelPayload); + + read(); +} + +// HTTPS agents. +// See ProxyConfig in src/js/internal/http.ts for how the connection should be handled when the agent is configured to use a proxy server. +function createConnection(...args) { + // XXX: This signature (port, host, options) is different from all the other createConnection() methods. + let options, cb; + if (args[0] !== null && typeof args[0] === "object") { + options = args[0]; + } else if (args[1] !== null && typeof args[1] === "object") { + options = { ...args[1] }; + } else if (args[2] === null || typeof args[2] !== "object") { + options = {}; + } else { + options = { ...args[2] }; + } + if (typeof args[0] === "number") { + options.port = args[0]; + } + if (typeof args[1] === "string") { + options.host = args[1]; + } + if (typeof args[args.length - 1] === "function") { + cb = args[args.length - 1]; + } + + $debug("createConnection", options); + + if (options._agentKey) { + const session = this._getSession(options._agentKey); + if (session) { + $debug("reuse session for %j", options._agentKey); + options = { + session, + ...options, + }; + } + } + + let socket; + const tunnelConfig = getTunnelConfigForProxiedHttps(this, options); + $debug(`https createConnection should use proxy for ${options.host}:${options.port}:`, tunnelConfig); + + if (!tunnelConfig) { + socket = tls.connect(options); + } else { + const connectOptions = { + ...this[kProxyConfig].proxyConnectionOptions, + }; + $debug("Create proxy socket", connectOptions); + const onError = err => { + cleanupAndPropagate(err, socket); + }; + const proxyTunnelTimeout = tunnelConfig.requestOptions.timeout; + const onTimeout = () => { + const err = $ERR_PROXY_TUNNEL(`Connection to establish proxy tunnel timed out after ${proxyTunnelTimeout}ms`); + // @ts-expect-error + err.proxyTunnelTimeout = proxyTunnelTimeout; + cleanupAndPropagate(err, socket); + }; + const cleanupAndPropagate = once((err, currentSocket) => { + $debug("cleanupAndPropagate", err); + socket.removeListener("error", onError); + socket.removeListener("timeout", onTimeout); + // An error occurred during tunnel establishment, in that case just destroy the socket and propagate the error to the callback. + + // When the error comes from unexpected status code, the stream is still in good shape, + // in that case let req.onSocket handle the destruction instead. + if (err && err.code === "ERR_PROXY_TUNNEL" && !err.statusCode) { + socket.destroy(); + } + // This error should go to: + // -> oncreate in Agent.prototype.createSocket + // -> closure in Agent.prototype.addRequest or Agent.prototype.removeSocket + if (cb) { + cb(err, currentSocket); + } + }); + const onProxyConnection = () => { + socket.removeListener("error", onError); + establishTunnel(this, socket, options, tunnelConfig, cleanupAndPropagate); + }; + if (this[kProxyConfig].protocol === "http:") { + socket = net.connect(connectOptions, onProxyConnection); + } else { + socket = tls.connect(connectOptions, onProxyConnection); + } + + socket.on("error", onError); + if (proxyTunnelTimeout) { + socket.setTimeout(proxyTunnelTimeout, onTimeout); + } + socket[kWaitForProxyTunnel] = true; + } + + if (options._agentKey) { + socket.on("session", session => { + this._cacheSession(options._agentKey, session); + }); + socket.once("close", err => { + if (err) this._evictSession(options._agentKey); + }); + } + + return socket; +} + +type Agent = import("node:https").Agent; +function Agent(options): void { if (!(this instanceof Agent)) return new Agent(options); - http.Agent.$apply(this, [options]); - this.defaultPort = 443; - this.protocol = "https:"; + options = { __proto__: null, ...options }; + options.defaultPort ??= 443; + options.protocol ??= "https:"; + http.Agent.$call(this, options); + this.maxCachedSessions = this.options.maxCachedSessions; if (this.maxCachedSessions === undefined) this.maxCachedSessions = 100; + + this._sessionCache = { + map: {}, + list: [], + }; } $toClass(Agent, "Agent", http.Agent); -Agent.prototype.createConnection = http.createConnection; -var https = { +Agent.prototype.createConnection = createConnection; + +Agent.prototype.getName = function getName(options = kEmptyObject) { + let name = http.Agent.prototype.getName.$call(this, options); + name += ":"; + if (options.ca) name += options.ca; + name += ":"; + if (options.cert) name += options.cert; + name += ":"; + if (options.clientCertEngine) name += options.clientCertEngine; + name += ":"; + if (options.ciphers) name += options.ciphers; + name += ":"; + if (options.key) name += options.key; + name += ":"; + if (options.pfx) name += options.pfx; + name += ":"; + if (options.rejectUnauthorized !== undefined) name += options.rejectUnauthorized; + name += ":"; + if (options.servername && options.servername !== options.host) name += options.servername; + name += ":"; + if (options.minVersion) name += options.minVersion; + name += ":"; + if (options.maxVersion) name += options.maxVersion; + name += ":"; + if (options.secureProtocol) name += options.secureProtocol; + name += ":"; + if (options.crl) name += options.crl; + name += ":"; + if (options.honorCipherOrder !== undefined) name += options.honorCipherOrder; + name += ":"; + if (options.ecdhCurve) name += options.ecdhCurve; + name += ":"; + if (options.dhparam) name += options.dhparam; + name += ":"; + if (options.secureOptions !== undefined) name += options.secureOptions; + name += ":"; + if (options.sessionIdContext) name += options.sessionIdContext; + name += ":"; + if (options.sigalgs) name += JSON.stringify(options.sigalgs); + name += ":"; + if (options.privateKeyIdentifier) name += options.privateKeyIdentifier; + name += ":"; + if (options.privateKeyEngine) name += options.privateKeyEngine; + return name; +}; + +Agent.prototype._getSession = function _getSession(key) { + return this._sessionCache.map[key]; +}; + +Agent.prototype._cacheSession = function _cacheSession(key, session) { + // Cache is disabled + if (this.maxCachedSessions === 0) { + return; + } + // Fast case - update existing entry + if (this._sessionCache.map[key]) { + this._sessionCache.map[key] = session; + return; + } + // Put new entry + if (this._sessionCache.list.length >= this.maxCachedSessions) { + const oldKey = this._sessionCache.list.shift(); + $debug("evicting %j", oldKey); + delete this._sessionCache.map[oldKey]; + } + this._sessionCache.list.push(key); + this._sessionCache.map[key] = session; +}; + +Agent.prototype._evictSession = function _evictSession(key) { + const index = this._sessionCache.list.indexOf(key); + if (index === -1) return; + this._sessionCache.list.splice(index, 1); + delete this._sessionCache.map[key]; +}; + +const globalAgent = new Agent({ + keepAlive: true, + scheduling: "lifo", + timeout: 5000, + // This normalized from both --use-env-proxy and NODE_USE_ENV_PROXY settings. + // proxyEnv: getOptionValue("--use-env-proxy") ? filterEnvForProxies(process.env) : undefined, + proxyEnv: undefined, // TODO: +}); + +export default { Agent, - globalAgent: new Agent({ keepAlive: true, scheduling: "lifo", timeout: 5000 }), + globalAgent, Server: http.Server, createServer: http.createServer, get, request, }; -export default https; diff --git a/test/js/node/test/parallel/test-https-agent-getname.js b/test/js/node/test/parallel/test-https-agent-getname.js new file mode 100644 index 0000000000..2a13ab1c6f --- /dev/null +++ b/test/js/node/test/parallel/test-https-agent-getname.js @@ -0,0 +1,54 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const https = require('https'); + +const agent = new https.Agent(); + +// empty argument +assert.strictEqual( + agent.getName(), + 'localhost::::::::::::::::::::::' +); + +// empty options +assert.strictEqual( + agent.getName({}), + 'localhost::::::::::::::::::::::' +); + +// Pass all options arguments +const options = { + host: '0.0.0.0', + port: 443, + localAddress: '192.168.1.1', + ca: 'ca', + cert: 'cert', + clientCertEngine: 'dynamic', + ciphers: 'ciphers', + crl: [Buffer.from('c'), Buffer.from('r'), Buffer.from('l')], + dhparam: 'dhparam', + ecdhCurve: 'ecdhCurve', + honorCipherOrder: false, + key: 'key', + pfx: 'pfx', + rejectUnauthorized: false, + secureOptions: 0, + secureProtocol: 'secureProtocol', + servername: 'localhost', + sessionIdContext: 'sessionIdContext', + sigalgs: 'sigalgs', + privateKeyIdentifier: 'privateKeyIdentifier', + privateKeyEngine: 'privateKeyEngine', +}; + +assert.strictEqual( + agent.getName(options), + '0.0.0.0:443:192.168.1.1:ca:cert:dynamic:ciphers:key:pfx:false:localhost:' + + '::secureProtocol:c,r,l:false:ecdhCurve:dhparam:0:sessionIdContext:' + + '"sigalgs":privateKeyIdentifier:privateKeyEngine' +); diff --git a/test/js/node/test/parallel/test-https-foafssl.js b/test/js/node/test/parallel/test-https-foafssl.js index d6dde97a41..828da2a5f7 100644 --- a/test/js/node/test/parallel/test-https-foafssl.js +++ b/test/js/node/test/parallel/test-https-foafssl.js @@ -67,7 +67,7 @@ server.listen(0, function() { '-cert', fixtures.path('keys/rsa_cert_foafssl_b.crt'), '-key', fixtures.path('keys/rsa_private_b.pem')]; - const client = spawn(common.opensslCli, args); + const client = spawn(opensslCli, args); client.stdout.on('data', function(data) { console.log('response received'); diff --git a/test/js/node/test/parallel/test-https-options-boolean-check.js b/test/js/node/test/parallel/test-https-options-boolean-check.js index 8ee2ebc993..443f7c76b4 100644 --- a/test/js/node/test/parallel/test-https-options-boolean-check.js +++ b/test/js/node/test/parallel/test-https-options-boolean-check.js @@ -151,4 +151,4 @@ const caArrDataView = toDataView(caCert); ' of Buffer, TypedArray, DataView, or BunFile.' + common.invalidArgTypeHelper(val) }); -}); \ No newline at end of file +}); diff --git a/test/js/node/test/parallel/test-https-unix-socket-self-signed.js b/test/js/node/test/parallel/test-https-unix-socket-self-signed.js index 9de249524a..9db92ac2ae 100644 --- a/test/js/node/test/parallel/test-https-unix-socket-self-signed.js +++ b/test/js/node/test/parallel/test-https-unix-socket-self-signed.js @@ -1,6 +1,5 @@ 'use strict'; const common = require('../common'); -if (common.isWindows) return; // TODO: BUN if (!common.hasCrypto) common.skip('missing crypto');