mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 10:28:47 +00:00
feat(https/fetch): Support custom ca/cert/key in fetch (#11322)
Co-authored-by: Liz3 (Yann HN) <accs@liz3.net>
This commit is contained in:
@@ -211,6 +211,7 @@ pub const ServerConfig = struct {
|
||||
}
|
||||
|
||||
pub const SSLConfig = struct {
|
||||
requires_custom_request_ctx: bool = false,
|
||||
server_name: [*c]const u8 = null,
|
||||
|
||||
key_file_name: [*c]const u8 = null,
|
||||
@@ -281,6 +282,71 @@ pub const ServerConfig = struct {
|
||||
return ctx_opts;
|
||||
}
|
||||
|
||||
pub fn isSame(thisConfig: *const SSLConfig, otherConfig: *const SSLConfig) bool {
|
||||
{ //strings
|
||||
const fields = .{
|
||||
"server_name",
|
||||
"key_file_name",
|
||||
"cert_file_name",
|
||||
"ca_file_name",
|
||||
"dh_params_file_name",
|
||||
"passphrase",
|
||||
"ssl_ciphers",
|
||||
"protos",
|
||||
};
|
||||
|
||||
inline for (fields) |field| {
|
||||
const lhs = @field(thisConfig, field);
|
||||
const rhs = @field(otherConfig, field);
|
||||
if (lhs != null and rhs != null) {
|
||||
if (!stringsEqual(lhs, rhs))
|
||||
return false;
|
||||
} else if (lhs != null or rhs != null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
//numbers
|
||||
const fields = .{ "secure_options", "request_cert", "reject_unauthorized", "low_memory_mode" };
|
||||
|
||||
inline for (fields) |field| {
|
||||
const lhs = @field(thisConfig, field);
|
||||
const rhs = @field(otherConfig, field);
|
||||
if (lhs != rhs)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// complex fields
|
||||
const fields = .{ "key", "ca", "cert" };
|
||||
inline for (fields) |field| {
|
||||
const lhs_count = @field(thisConfig, field ++ "_count");
|
||||
const rhs_count = @field(otherConfig, field ++ "_count");
|
||||
if (lhs_count != rhs_count)
|
||||
return false;
|
||||
if (lhs_count > 0) {
|
||||
const lhs = @field(thisConfig, field);
|
||||
const rhs = @field(otherConfig, field);
|
||||
for (0..lhs_count) |i| {
|
||||
if (!stringsEqual(lhs.?[i], rhs.?[i]))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
fn stringsEqual(a: [*c]const u8, b: [*c]const u8) bool {
|
||||
const lhs = bun.asByteSlice(a);
|
||||
const rhs = bun.asByteSlice(b);
|
||||
return strings.eqlLong(lhs, rhs, true);
|
||||
}
|
||||
|
||||
pub fn deinit(this: *SSLConfig) void {
|
||||
const fields = .{
|
||||
"server_name",
|
||||
@@ -367,6 +433,7 @@ pub const ServerConfig = struct {
|
||||
return null;
|
||||
}
|
||||
any = true;
|
||||
result.requires_custom_request_ctx = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -386,11 +453,13 @@ pub const ServerConfig = struct {
|
||||
native_array[valid_count] = bun.default_allocator.dupeZ(u8, sliced) catch unreachable;
|
||||
valid_count += 1;
|
||||
any = true;
|
||||
result.requires_custom_request_ctx = true;
|
||||
}
|
||||
} else if (BlobFileContentResult.init("key", item, global, exception)) |content| {
|
||||
if (content.data.len > 0) {
|
||||
native_array[valid_count] = content.data.ptr;
|
||||
valid_count += 1;
|
||||
result.requires_custom_request_ctx = true;
|
||||
any = true;
|
||||
} else {
|
||||
// mark and free all CA's
|
||||
@@ -422,6 +491,7 @@ pub const ServerConfig = struct {
|
||||
result.key = native_array;
|
||||
result.key_count = 1;
|
||||
any = true;
|
||||
result.requires_custom_request_ctx = true;
|
||||
} else {
|
||||
result.deinit();
|
||||
return null;
|
||||
@@ -434,6 +504,7 @@ pub const ServerConfig = struct {
|
||||
if (sliced.len > 0) {
|
||||
native_array[0] = bun.default_allocator.dupeZ(u8, sliced) catch unreachable;
|
||||
any = true;
|
||||
result.requires_custom_request_ctx = true;
|
||||
result.key = native_array;
|
||||
result.key_count = 1;
|
||||
} else {
|
||||
@@ -460,6 +531,7 @@ pub const ServerConfig = struct {
|
||||
return null;
|
||||
}
|
||||
any = true;
|
||||
result.requires_custom_request_ctx = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -473,6 +545,7 @@ pub const ServerConfig = struct {
|
||||
}
|
||||
|
||||
any = true;
|
||||
result.requires_custom_request_ctx = true;
|
||||
} else {
|
||||
global.throwInvalidArguments("ALPNProtocols argument must be an string, Buffer or TypedArray", .{});
|
||||
result.deinit();
|
||||
@@ -496,11 +569,13 @@ pub const ServerConfig = struct {
|
||||
native_array[valid_count] = bun.default_allocator.dupeZ(u8, sliced) catch unreachable;
|
||||
valid_count += 1;
|
||||
any = true;
|
||||
result.requires_custom_request_ctx = true;
|
||||
}
|
||||
} else if (BlobFileContentResult.init("cert", item, global, exception)) |content| {
|
||||
if (content.data.len > 0) {
|
||||
native_array[valid_count] = content.data.ptr;
|
||||
valid_count += 1;
|
||||
result.requires_custom_request_ctx = true;
|
||||
any = true;
|
||||
} else {
|
||||
// mark and free all CA's
|
||||
@@ -532,6 +607,7 @@ pub const ServerConfig = struct {
|
||||
result.cert = native_array;
|
||||
result.cert_count = 1;
|
||||
any = true;
|
||||
result.requires_custom_request_ctx = true;
|
||||
} else {
|
||||
result.deinit();
|
||||
return null;
|
||||
@@ -544,6 +620,7 @@ pub const ServerConfig = struct {
|
||||
if (sliced.len > 0) {
|
||||
native_array[0] = bun.default_allocator.dupeZ(u8, sliced) catch unreachable;
|
||||
any = true;
|
||||
result.requires_custom_request_ctx = true;
|
||||
result.cert = native_array;
|
||||
result.cert_count = 1;
|
||||
} else {
|
||||
@@ -587,6 +664,7 @@ pub const ServerConfig = struct {
|
||||
if (sliced.len > 0) {
|
||||
result.ssl_ciphers = bun.default_allocator.dupeZ(u8, sliced.slice()) catch unreachable;
|
||||
any = true;
|
||||
result.requires_custom_request_ctx = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -596,6 +674,7 @@ pub const ServerConfig = struct {
|
||||
if (sliced.len > 0) {
|
||||
result.server_name = bun.default_allocator.dupeZ(u8, sliced.slice()) catch unreachable;
|
||||
any = true;
|
||||
result.requires_custom_request_ctx = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -615,12 +694,14 @@ pub const ServerConfig = struct {
|
||||
native_array[valid_count] = bun.default_allocator.dupeZ(u8, sliced) catch unreachable;
|
||||
valid_count += 1;
|
||||
any = true;
|
||||
result.requires_custom_request_ctx = true;
|
||||
}
|
||||
} else if (BlobFileContentResult.init("ca", item, global, exception)) |content| {
|
||||
if (content.data.len > 0) {
|
||||
native_array[valid_count] = content.data.ptr;
|
||||
valid_count += 1;
|
||||
any = true;
|
||||
result.requires_custom_request_ctx = true;
|
||||
} else {
|
||||
// mark and free all CA's
|
||||
result.cert = native_array;
|
||||
@@ -651,6 +732,7 @@ pub const ServerConfig = struct {
|
||||
result.ca = native_array;
|
||||
result.ca_count = 1;
|
||||
any = true;
|
||||
result.requires_custom_request_ctx = true;
|
||||
} else {
|
||||
result.deinit();
|
||||
return null;
|
||||
@@ -663,6 +745,7 @@ pub const ServerConfig = struct {
|
||||
if (sliced.len > 0) {
|
||||
native_array[0] = bun.default_allocator.dupeZ(u8, sliced) catch unreachable;
|
||||
any = true;
|
||||
result.requires_custom_request_ctx = true;
|
||||
result.ca = native_array;
|
||||
result.ca_count = 1;
|
||||
} else {
|
||||
|
||||
@@ -35,6 +35,8 @@ const JSGlobalObject = JSC.JSGlobalObject;
|
||||
const NullableAllocator = bun.NullableAllocator;
|
||||
const DataURL = @import("../../resolver/data_url.zig").DataURL;
|
||||
|
||||
const SSLConfig = @import("../api/server.zig").ServerConfig.SSLConfig;
|
||||
|
||||
const VirtualMachine = JSC.VirtualMachine;
|
||||
const Task = JSC.Task;
|
||||
const JSPrinter = bun.js_printer;
|
||||
@@ -1635,6 +1637,7 @@ pub const Fetch = struct {
|
||||
.disable_decompression = fetch_options.disable_decompression,
|
||||
.reject_unauthorized = fetch_options.reject_unauthorized,
|
||||
.verbose = fetch_options.verbose,
|
||||
.tls_props = fetch_options.ssl_config,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1693,6 +1696,7 @@ pub const Fetch = struct {
|
||||
memory_reporter: *JSC.MemoryReportingAllocator,
|
||||
check_server_identity: JSC.Strong = .{},
|
||||
unix_socket_path: ZigString.Slice,
|
||||
ssl_config: ?*SSLConfig = null,
|
||||
};
|
||||
|
||||
pub fn queue(
|
||||
@@ -1899,6 +1903,8 @@ pub const Fetch = struct {
|
||||
blob,
|
||||
};
|
||||
var url_type = URLType.remote;
|
||||
|
||||
var ssl_config: ?*SSLConfig = null;
|
||||
var reject_unauthorized = script_ctx.bundler.env.getTLSRejectUnauthorized();
|
||||
var check_server_identity: JSValue = .zero;
|
||||
|
||||
@@ -2079,6 +2085,15 @@ pub const Fetch = struct {
|
||||
|
||||
if (options.get(ctx, "tls")) |tls| {
|
||||
if (!tls.isEmptyOrUndefinedOrNull() and tls.isObject()) {
|
||||
if (SSLConfig.inJS(globalThis, tls, exception)) |config| {
|
||||
if (ssl_config) |existing_conf| {
|
||||
existing_conf.deinit();
|
||||
bun.default_allocator.destroy(existing_conf);
|
||||
ssl_config = null;
|
||||
}
|
||||
ssl_config = bun.default_allocator.create(SSLConfig) catch bun.outOfMemory();
|
||||
ssl_config.?.* = config;
|
||||
}
|
||||
if (tls.get(ctx, "rejectUnauthorized")) |reject| {
|
||||
if (reject.isBoolean()) {
|
||||
reject_unauthorized = reject.asBoolean();
|
||||
@@ -2086,7 +2101,6 @@ pub const Fetch = struct {
|
||||
reject_unauthorized = reject.to(i32) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (tls.get(ctx, "checkServerIdentity")) |checkServerIdentity| {
|
||||
if (checkServerIdentity.isCell() and checkServerIdentity.isCallable(globalThis.vm())) {
|
||||
check_server_identity = checkServerIdentity;
|
||||
@@ -2100,9 +2114,13 @@ pub const Fetch = struct {
|
||||
var href = JSC.URL.hrefFromJS(proxy_arg, globalThis);
|
||||
if (href.tag == .Dead) {
|
||||
const err = JSC.toTypeError(.ERR_INVALID_ARG_VALUE, "fetch() proxy URL is invalid", .{}, ctx);
|
||||
// clean hostname if any
|
||||
if (hostname) |host| {
|
||||
allocator.free(host);
|
||||
// clean hostname and tls props if any
|
||||
if (ssl_config) |conf| {
|
||||
conf.deinit();
|
||||
bun.default_allocator.destroy(conf);
|
||||
}
|
||||
if (hostname) |hn| {
|
||||
bun.default_allocator.free(hn);
|
||||
hostname = null;
|
||||
}
|
||||
allocator.free(url_proxy_buffer);
|
||||
@@ -2231,8 +2249,8 @@ pub const Fetch = struct {
|
||||
if (str.isEmpty()) {
|
||||
const err = JSC.toTypeError(.ERR_INVALID_ARG_VALUE, fetch_error_blank_url, .{}, ctx);
|
||||
// clean hostname if any
|
||||
if (hostname) |host| {
|
||||
allocator.free(host);
|
||||
if (hostname) |hn| {
|
||||
bun.default_allocator.free(hn);
|
||||
hostname = null;
|
||||
}
|
||||
return JSPromise.rejectedPromiseValue(globalThis, err);
|
||||
@@ -2253,8 +2271,8 @@ pub const Fetch = struct {
|
||||
|
||||
url = ZigURL.fromString(allocator, str) catch {
|
||||
// clean hostname if any
|
||||
if (hostname) |host| {
|
||||
allocator.free(host);
|
||||
if (hostname) |hn| {
|
||||
bun.default_allocator.free(hn);
|
||||
hostname = null;
|
||||
}
|
||||
const err = JSC.toTypeError(.ERR_INVALID_ARG_VALUE, "fetch() URL is invalid", .{}, ctx);
|
||||
@@ -2363,6 +2381,10 @@ pub const Fetch = struct {
|
||||
|
||||
if (options.get(ctx, "tls")) |tls| {
|
||||
if (!tls.isEmptyOrUndefinedOrNull() and tls.isObject()) {
|
||||
if (SSLConfig.inJS(globalThis, tls, exception)) |config| {
|
||||
ssl_config = bun.default_allocator.create(SSLConfig) catch bun.outOfMemory();
|
||||
ssl_config.?.* = config;
|
||||
}
|
||||
if (tls.get(ctx, "rejectUnauthorized")) |reject| {
|
||||
if (reject.isBoolean()) {
|
||||
reject_unauthorized = reject.asBoolean();
|
||||
@@ -2389,6 +2411,11 @@ pub const Fetch = struct {
|
||||
allocator.free(host);
|
||||
hostname = null;
|
||||
}
|
||||
|
||||
if (ssl_config) |conf| {
|
||||
conf.deinit();
|
||||
bun.default_allocator.destroy(conf);
|
||||
}
|
||||
allocator.free(url_proxy_buffer);
|
||||
is_error = true;
|
||||
return JSPromise.rejectedPromiseValue(globalThis, err);
|
||||
@@ -2708,6 +2735,7 @@ pub const Fetch = struct {
|
||||
.url_proxy_buffer = url_proxy_buffer,
|
||||
.signal = signal,
|
||||
.globalThis = globalThis,
|
||||
.ssl_config = ssl_config,
|
||||
.hostname = hostname,
|
||||
.memory_reporter = memory_reporter,
|
||||
.check_server_identity = if (check_server_identity.isEmptyOrUndefinedOrNull()) .{} else JSC.Strong.create(check_server_identity, globalThis),
|
||||
|
||||
65
src/http.zig
65
src/http.zig
@@ -29,6 +29,8 @@ const SOCK = os.SOCK;
|
||||
const Arena = @import("./mimalloc_arena.zig").Arena;
|
||||
const ZlibPool = @import("./http/zlib.zig");
|
||||
const BoringSSL = bun.BoringSSL;
|
||||
const X509 = @import("./bun.js/api/bun/x509.zig");
|
||||
const SSLConfig = @import("./bun.js/api/server.zig").ServerConfig.SSLConfig;
|
||||
|
||||
const URLBufferPool = ObjectPool([8192]u8, null, false, 10);
|
||||
const uws = bun.uws;
|
||||
@@ -47,6 +49,8 @@ var dead_socket = @as(*DeadSocket, @ptrFromInt(1));
|
||||
var socket_async_http_abort_tracker = std.AutoArrayHashMap(u32, uws.InternalSocket).init(bun.default_allocator);
|
||||
var async_http_id: std.atomic.Value(u32) = std.atomic.Value(u32).init(0);
|
||||
const MAX_REDIRECT_URL_LENGTH = 128 * 1024;
|
||||
var custom_ssl_context_map = std.AutoArrayHashMap(*SSLConfig, *NewHTTPContext(true)).init(bun.default_allocator);
|
||||
|
||||
const print_every = 0;
|
||||
var print_every_i: usize = 0;
|
||||
|
||||
@@ -377,6 +381,34 @@ fn NewHTTPContext(comptime ssl: bool) type {
|
||||
return @as(*BoringSSL.SSL_CTX, @ptrCast(this.us_socket_context.getNativeHandle(true)));
|
||||
}
|
||||
|
||||
pub fn deinit(this: *@This()) void {
|
||||
this.us_socket_context.deinit(ssl);
|
||||
uws.us_socket_context_free(@as(c_int, @intFromBool(ssl)), this.us_socket_context);
|
||||
bun.default_allocator.destroy(this);
|
||||
}
|
||||
|
||||
pub fn initWithClientConfig(this: *@This(), client: *HTTPClient) !void {
|
||||
if (!comptime ssl) {
|
||||
unreachable;
|
||||
}
|
||||
var opts = client.tls_props.?.asUSockets();
|
||||
opts.request_cert = 1;
|
||||
opts.reject_unauthorized = 0;
|
||||
const socket = uws.us_create_bun_socket_context(ssl_int, http_thread.loop.loop, @sizeOf(usize), opts);
|
||||
if (socket == null) {
|
||||
return error.FailedToOpenSocket;
|
||||
}
|
||||
this.us_socket_context = socket.?;
|
||||
this.sslCtx().setup();
|
||||
|
||||
HTTPSocket.configure(
|
||||
this.us_socket_context,
|
||||
false,
|
||||
anyopaque,
|
||||
Handler,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn init(this: *@This()) !void {
|
||||
if (comptime ssl) {
|
||||
const opts: uws.us_bun_socket_context_options_t = .{
|
||||
@@ -770,6 +802,34 @@ pub const HTTPThread = struct {
|
||||
return try this.context(is_ssl).connectSocket(client, client.unix_socket_path.slice());
|
||||
}
|
||||
|
||||
if (comptime is_ssl) {
|
||||
const needs_own_context = client.tls_props != null and client.tls_props.?.requires_custom_request_ctx;
|
||||
if (needs_own_context) {
|
||||
var requested_config = client.tls_props.?;
|
||||
for (custom_ssl_context_map.keys()) |other_config| {
|
||||
if (requested_config.isSame(other_config)) {
|
||||
// we free the callers config since we have a existing one
|
||||
requested_config.deinit();
|
||||
bun.default_allocator.destroy(requested_config);
|
||||
client.tls_props = other_config;
|
||||
return try custom_ssl_context_map.get(other_config).?.connect(client, client.url.hostname, client.url.getPortAuto());
|
||||
}
|
||||
}
|
||||
// we need the config so dont free it
|
||||
var custom_context = try bun.default_allocator.create(NewHTTPContext(is_ssl));
|
||||
custom_context.initWithClientConfig(client) catch |err| {
|
||||
requested_config.deinit();
|
||||
client.tls_props = null;
|
||||
bun.default_allocator.destroy(custom_context);
|
||||
return err;
|
||||
};
|
||||
try custom_ssl_context_map.put(requested_config, custom_context);
|
||||
// We might deinit the socket context, so we disable keepalive to make sure we don't
|
||||
// free it while in use.
|
||||
client.disable_keepalive = true;
|
||||
return try custom_context.connect(client, client.url.hostname, client.url.getPortAuto());
|
||||
}
|
||||
}
|
||||
if (client.http_proxy) |url| {
|
||||
return try this.context(is_ssl).connect(client, url.hostname, url.getPortAuto());
|
||||
}
|
||||
@@ -1490,6 +1550,7 @@ disable_keepalive: bool = false,
|
||||
disable_decompression: bool = false,
|
||||
state: InternalState = .{},
|
||||
|
||||
tls_props: ?*SSLConfig = null,
|
||||
result_callback: HTTPClientResult.Callback = undefined,
|
||||
|
||||
/// Some HTTP servers (such as npm) report Last-Modified times but ignore If-Modified-Since.
|
||||
@@ -1749,6 +1810,7 @@ pub const AsyncHTTP = struct {
|
||||
disable_keepalive: ?bool = null,
|
||||
disable_decompression: ?bool = null,
|
||||
reject_unauthorized: ?bool = null,
|
||||
tls_props: ?*SSLConfig = null,
|
||||
};
|
||||
|
||||
pub fn init(
|
||||
@@ -1811,6 +1873,9 @@ pub const AsyncHTTP = struct {
|
||||
if (options.reject_unauthorized) |val| {
|
||||
this.client.reject_unauthorized = val;
|
||||
}
|
||||
if (options.tls_props) |val| {
|
||||
this.client.tls_props = val;
|
||||
}
|
||||
|
||||
if (options.http_proxy) |proxy| {
|
||||
// Username between 0 and 4096 chars
|
||||
|
||||
@@ -1388,6 +1388,7 @@ class ClientRequest extends OutgoingMessage {
|
||||
#protocol;
|
||||
#method;
|
||||
#port;
|
||||
#tls = null;
|
||||
#useDefaultPort;
|
||||
#joinDuplicateHeaders;
|
||||
#maxHeaderSize;
|
||||
@@ -1402,7 +1403,6 @@ class ClientRequest extends OutgoingMessage {
|
||||
#timeoutTimer?: Timer = undefined;
|
||||
#options;
|
||||
#finished;
|
||||
#tls;
|
||||
|
||||
_httpMessage;
|
||||
|
||||
@@ -1459,6 +1459,11 @@ class ClientRequest extends OutgoingMessage {
|
||||
callback();
|
||||
}
|
||||
|
||||
_ensureTls() {
|
||||
if (this.#tls === null) this.#tls = {};
|
||||
return this.#tls;
|
||||
}
|
||||
|
||||
_final(callback) {
|
||||
this.#finished = true;
|
||||
this[kAbortController] = new AbortController();
|
||||
@@ -1482,6 +1487,8 @@ class ClientRequest extends OutgoingMessage {
|
||||
} else {
|
||||
url = `${this.#protocol}//${this.#host}${this.#useDefaultPort ? "" : ":" + this.#port}${this.#path}`;
|
||||
}
|
||||
const tls =
|
||||
this.#protocol === "https:" && this.#tls ? { ...this.#tls, serverName: this.#tls.servername } : undefined;
|
||||
try {
|
||||
const fetchOptions: any = {
|
||||
method,
|
||||
@@ -1489,7 +1496,6 @@ class ClientRequest extends OutgoingMessage {
|
||||
body: body && method !== "GET" && method !== "HEAD" && method !== "OPTIONS" ? body : undefined,
|
||||
redirect: "manual",
|
||||
signal: this[kAbortController].signal,
|
||||
|
||||
// Timeouts are handled via this.setTimeout.
|
||||
timeout: false,
|
||||
// Disable auto gzip/deflate
|
||||
@@ -1694,7 +1700,48 @@ class ClientRequest extends OutgoingMessage {
|
||||
}
|
||||
|
||||
this.#joinDuplicateHeaders = _joinDuplicateHeaders;
|
||||
if (options.pfx) {
|
||||
throw new Error("pfx is not supported");
|
||||
}
|
||||
if (options.rejectUnauthorized !== undefined) this._ensureTls().rejectUnauthorized = options.rejectUnauthorized;
|
||||
if (options.ca) {
|
||||
if (!isValidTLSArray(options.ca))
|
||||
throw new TypeError(
|
||||
"ca argument must be an string, Buffer, TypedArray, BunFile or an array containing string, Buffer, TypedArray or BunFile",
|
||||
);
|
||||
this._ensureTls().ca = options.ca;
|
||||
}
|
||||
if (options.cert) {
|
||||
if (!isValidTLSArray(options.cert))
|
||||
throw new TypeError(
|
||||
"cert argument must be an string, Buffer, TypedArray, BunFile or an array containing string, Buffer, TypedArray or BunFile",
|
||||
);
|
||||
this._ensureTls().cert = options.cert;
|
||||
}
|
||||
if (options.key) {
|
||||
if (!isValidTLSArray(options.key))
|
||||
throw new TypeError(
|
||||
"key argument must be an string, Buffer, TypedArray, BunFile or an array containing string, Buffer, TypedArray or BunFile",
|
||||
);
|
||||
this._ensureTls().key = options.key;
|
||||
}
|
||||
if (options.passphrase) {
|
||||
if (typeof options.passphrase !== "string") throw new TypeError("passphrase argument must be a string");
|
||||
this._ensureTls().passphrase = options.passphrase;
|
||||
}
|
||||
if (options.ciphers) {
|
||||
if (typeof options.ciphers !== "string") throw new TypeError("ciphers argument must be a string");
|
||||
this._ensureTls().ciphers = options.ciphers;
|
||||
}
|
||||
if (options.servername) {
|
||||
if (typeof options.servername !== "string") throw new TypeError("servername argument must be a string");
|
||||
this._ensureTls().servername = options.servername;
|
||||
}
|
||||
|
||||
if (options.secureOptions) {
|
||||
if (typeof options.secureOptions !== "number") throw new TypeError("secureOptions argument must be a string");
|
||||
this._ensureTls().secureOptions = options.secureOptions;
|
||||
}
|
||||
this.#path = options.path || "/";
|
||||
if (cb) {
|
||||
this.once("response", cb);
|
||||
@@ -1723,7 +1770,6 @@ class ClientRequest extends OutgoingMessage {
|
||||
this.#reusedSocket = false;
|
||||
this.#host = host;
|
||||
this.#protocol = protocol;
|
||||
this.#tls = options.tls;
|
||||
|
||||
const timeout = options.timeout;
|
||||
if (timeout !== undefined && timeout !== 0) {
|
||||
|
||||
26
test/js/node/http/fixtures/openssl_localhost.crt
Normal file
26
test/js/node/http/fixtures/openssl_localhost.crt
Normal file
@@ -0,0 +1,26 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEazCCAtOgAwIBAgIRAOmVTIueDOoEBqjyAsX9r+wwDQYJKoZIhvcNAQELBQAw
|
||||
gZ0xHjAcBgNVBAoTFW1rY2VydCBkZXZlbG9wbWVudCBDQTE5MDcGA1UECwwwbHVk
|
||||
dmlnQEx1ZHZpZ3MtTWFjQm9vay1Qcm8ubG9jYWwgKEx1ZHZpZyBIb3ptYW4pMUAw
|
||||
PgYDVQQDDDdta2NlcnQgbHVkdmlnQEx1ZHZpZ3MtTWFjQm9vay1Qcm8ubG9jYWwg
|
||||
KEx1ZHZpZyBIb3ptYW4pMB4XDTI0MDUyNTEyMzIyN1oXDTI2MDgyNTEyMzIyN1ow
|
||||
ZDEnMCUGA1UEChMebWtjZXJ0IGRldmVsb3BtZW50IGNlcnRpZmljYXRlMTkwNwYD
|
||||
VQQLDDBsdWR2aWdATHVkdmlncy1NYWNCb29rLVByby5sb2NhbCAoTHVkdmlnIEhv
|
||||
em1hbikwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDt6FinGWRCnrNN
|
||||
T8xJfV1ldr3yfrgOun7D4+88xVDErI3/CA2EdbME8d51V8hRjv6aVG+Wgyo82AE0
|
||||
W96wmDtDmToNO4UJO5NPuMCqwdCfph7QGKMt9GRDKLC5qwwBHnrDxAUyTqxSpDWn
|
||||
zoNUsGf80Cxr9wzzv95cnxpHiuJL1KnHCgkAXwNdWh0Rg63oa5+xilBxV1UdzaKY
|
||||
VAMIO5s0w9d4nK6U+6w+u8YrH3nTFceUTJguo4Jv9jGPl6fLL4BE++fljMQK3imi
|
||||
/afvXoic9UpKMcEeZ2TxVEuaTqLplvoWe9TUentge4i7OGWSlBeHIs7B1ujL3CcC
|
||||
e0uwZcgvAgMBAAGjXjBcMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEF
|
||||
BQcDATAfBgNVHSMEGDAWgBRm53Lp693sneRIcClR8vuEt7zJMDAUBgNVHREEDTAL
|
||||
gglsb2NhbGhvc3QwDQYJKoZIhvcNAQELBQADggGBACHrhzpedmGWVSGtmIwYZ3W7
|
||||
Y4nwWi5V4uhgRc3f7zb4xHIqmILWGO1nijoKrqN9/L4Ac1ePrlZBjl5eSC6maev1
|
||||
C021cbm1WczMIpg5ua3rj1X511AtX2tHv8fDiiDMZS2KqnJ7lErcL6PiCtRowUBN
|
||||
dmJ32PgFPdSWVZ0r3QUaqjE7kURx4F65Q1M1dYcpSfZlluEdDnqmKyG6Cdn7+7le
|
||||
d3FnUEnBS1yovg/l04nIxwNQNhAO7/bqLYC7yPHK39yFG+9YuSe6Wu9pkIT+a0xb
|
||||
Lhv/UkAZB3EwZYFQgjbS+lmRgNT+jIjipm8MM1Awn+a9+s+Twc9dXqAD9lv5wPhh
|
||||
pJG7rlj7zNd7oSAUzbAbOvVivH2IpYtwhc4vrKFZHGH6YsZBR7l8vLdCRHqaUc5h
|
||||
9D2FXVvYA0WY3tX3clYo/p4UsNeYayAT1gWT6Vmx8IPi6fvDqUO8bAsvEHE+YJ4B
|
||||
ABqWvmOc7wU/rLpLiBmIy4Fmk33mSk2FyEVmDucGWw==
|
||||
-----END CERTIFICATE-----
|
||||
28
test/js/node/http/fixtures/openssl_localhost.key
Normal file
28
test/js/node/http/fixtures/openssl_localhost.key
Normal file
@@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDt6FinGWRCnrNN
|
||||
T8xJfV1ldr3yfrgOun7D4+88xVDErI3/CA2EdbME8d51V8hRjv6aVG+Wgyo82AE0
|
||||
W96wmDtDmToNO4UJO5NPuMCqwdCfph7QGKMt9GRDKLC5qwwBHnrDxAUyTqxSpDWn
|
||||
zoNUsGf80Cxr9wzzv95cnxpHiuJL1KnHCgkAXwNdWh0Rg63oa5+xilBxV1UdzaKY
|
||||
VAMIO5s0w9d4nK6U+6w+u8YrH3nTFceUTJguo4Jv9jGPl6fLL4BE++fljMQK3imi
|
||||
/afvXoic9UpKMcEeZ2TxVEuaTqLplvoWe9TUentge4i7OGWSlBeHIs7B1ujL3CcC
|
||||
e0uwZcgvAgMBAAECggEALhd6vXz84K9Qe6T/JinEo3i62jVUwX2+O4N4gSSVPlVT
|
||||
+Vn9DHGlKksV11QXej2i9BFxwQ5Oa5VJvnQiE8KakMEp7xBd+Ojy5Fod8bc1DQkp
|
||||
JRXw32Fe32gNvRr3a2wVSsI6Y4G8fxJTVtx6sziuHNvUD2LAvqSolvc4Jy4wI5KD
|
||||
YrmFCwY7T4iUPa7tAzzU1il+iXyQympsjZtWau4JwyPbrCeN0t68yzsp0tRmfWKJ
|
||||
xK+T86F01Lf1gsXUQ+xzx5GMZHkRBjWBK4GwQhDkz6ap69q905bhgBPWyrKLWq5v
|
||||
yuusyIFbhIRCR7G7ZsJzjSdYivopMKpprbH6aAjWiQKBgQD6wL5iFuaQbyXTp8us
|
||||
nsrpONjxpQKeO3kxklSjxbr9ZYGVcqp8cMl2FDJcepRTDVoqm0knIMdOttUkuPs7
|
||||
E6RvoL/McN/2OgDgL9ovr2ow49l/QB4MD6HgMq4V4rVEtJqPlmG9AjmmpUEfHnCn
|
||||
c/BHo151/udJtfDy3ZJX1rI3gwKBgQDy4sqtxr7ecLd3hMsVqxhfy0p34WDTiPdp
|
||||
3X5p9eWYXLmKoJDVNH7/BecgN/LcDjIF31HcIbELqEHuHdE1arsTyVOjKtoO5N/1
|
||||
+lRJifJjsD9trX+uuFeIHNclb3jTOV2MU0/pfB4HzgtG/qrtyyF/Sf3bBfDtuMwg
|
||||
cYiOB+Ng5QKBgEaWlcGlMri8IUjo9oQcm4B1+VRlIEyM73wN9ne4BQCqX4VDp0yq
|
||||
r3vnCZpRA4oxuw09c6VpK9Iz0+KnlEm4KNUnynZx3ApDn9V8gw5jciBbM/IHia3Z
|
||||
hLdJbQpKLL8vnEcJjXAYvUP1R1TMS+hH0f9ItSHAZTmx1yd3Smghzz+jAoGAFxAf
|
||||
7LZVg2uykB/E5O7VJqt4C8AT4KI91AibK1aVEY2kdJxghE4yzOZzluSZI/oZF+On
|
||||
sz5jwFaexAyCxA65atyQG4tDH2zuMz4s6Lq3kG246CI0YJPSg/MxHrXiBDSLRHrY
|
||||
uLP3aghPm9Msyd2i9aJB/50lznzgrSf6rnnjRl0CgYEA32d7xxNG8bThR2JFzbO3
|
||||
1NuK2UEIkKWUgMNy+hOuSHXXVzMCYT9lj2oMDPN4wnF0SnOcQm1xfQO21m0UxHyz
|
||||
h/q6Fxmg+wl60zO9/oq5hU55oja3Sx1DqubicEWSaFdYuD9tMtHxWZAuUIkD3zX1
|
||||
3XlcuJOjhCuZxRoS9O1Otro=
|
||||
-----END PRIVATE KEY-----
|
||||
29
test/js/node/http/fixtures/openssl_localhost_ca.pem
Normal file
29
test/js/node/http/fixtures/openssl_localhost_ca.pem
Normal file
@@ -0,0 +1,29 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFCzCCA3OgAwIBAgIQBg0eUuH8A64LETs9IrQIbzANBgkqhkiG9w0BAQsFADCB
|
||||
nTEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMTkwNwYDVQQLDDBsdWR2
|
||||
aWdATHVkdmlncy1NYWNCb29rLVByby5sb2NhbCAoTHVkdmlnIEhvem1hbikxQDA+
|
||||
BgNVBAMMN21rY2VydCBsdWR2aWdATHVkdmlncy1NYWNCb29rLVByby5sb2NhbCAo
|
||||
THVkdmlnIEhvem1hbikwHhcNMjQwNTI1MTIzMjI3WhcNMzQwNTI1MTIzMjI3WjCB
|
||||
nTEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMTkwNwYDVQQLDDBsdWR2
|
||||
aWdATHVkdmlncy1NYWNCb29rLVByby5sb2NhbCAoTHVkdmlnIEhvem1hbikxQDA+
|
||||
BgNVBAMMN21rY2VydCBsdWR2aWdATHVkdmlncy1NYWNCb29rLVByby5sb2NhbCAo
|
||||
THVkdmlnIEhvem1hbikwggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQDT
|
||||
vKduL//b9hSVZOCrRFPFjpARpB3uAr1sjGd7TVeEdEkeJapO5BrQ4I8Unbtqo5JC
|
||||
2U1lZv5Gl6Odlyc7m60c/F1py15zH6vMggUUshmtSdCxmVmXPBsbYXmuaDkEhxcH
|
||||
+sE/60IfdkX/jw8cVNa5grIy7WbCpHsRxnUIFjij32kfOuvVY5UylEy+j0x6flGH
|
||||
fl+a7nOO4qq6tZXaeBmagg0pAPVK3la6bFZDXPyO5KjwfjIIqF7H9nB5+YlIIIAg
|
||||
GoCLU+1wOMsOzHgQFJcNecoX0k86v0gP9K5SD0+vgW3xbJ6xBdOBWCulWhWMY8Im
|
||||
f66lMBYkJYnVFg6MnNOjl7wIToyy0nNEZvkwwSBhETjXaKyMF1+vEHxYLtbucla9
|
||||
JkVYDC0yU7AhZNKbsyiI+V/M0FMCKW3QZip2q7trst8GnA0vURWXOyj5iZ96nh7X
|
||||
BbNFSkuY0wBBNwbr0p/pTHE/FF6BlBPXl6XQdpXM6/YVvrqj3dOW7P5WUIIU10cC
|
||||
AwEAAaNFMEMwDgYDVR0PAQH/BAQDAgIEMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYD
|
||||
VR0OBBYEFGbncunr3eyd5EhwKVHy+4S3vMkwMA0GCSqGSIb3DQEBCwUAA4IBgQC6
|
||||
h20ry+Z7ma8G4XPGcEKhbwAROGSfYCnygmGC5V1j/Wshcro4/qrts9qDtq6MtCzC
|
||||
5vMB40xSo60EWtDaNQbRhRZHvA1Agkzyi5NnFHQARKn+eSyNV+7wmDWRy9nb5bGH
|
||||
A48mWREOTaQLi6BY6OPvLr376+dzdMx8GL/uMHz/1rQDU1/4e6lRxYPzrSuT8SPe
|
||||
Zb112wpkbJuT69HvbT3mrYQVsagX5qJ1NML2/6+ichB9ou08ZIyksVd+8TKLP/zn
|
||||
QSYhzrgcI5pTnyi2AybKRy07EjcAFNBzKiHP42S4+AudOUYUzdeNxMpgelgTiHjU
|
||||
kkYncgeQ6qXzA3uC4ODTBZWGnslzSATY0IuLvn9/ZcgZmj1GcEeRyaxpkdE7JaX0
|
||||
KIpPD6WIFHSB/6VwjFTUxf49+yW9U9bdaPlWOcHXUtOfoikC/EK1OXfX+sAd4OhE
|
||||
8iyfiWz4jpOK9oBhqGsJLooaU4TXLzfMXYWyIjOOIoZX3QECUFQ4Zw3rJ9oV1A8=
|
||||
-----END CERTIFICATE-----
|
||||
@@ -12,7 +12,7 @@ import http, {
|
||||
IncomingMessage,
|
||||
OutgoingMessage,
|
||||
} from "node:http";
|
||||
import https from "node:https";
|
||||
import https, { createServer as createHttpsServer } from "node:https";
|
||||
import { EventEmitter } from "node:events";
|
||||
import { createServer as createHttpsServer } from "node:https";
|
||||
import { createTest } from "node-harness";
|
||||
@@ -829,6 +829,60 @@ describe("node:http", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("https.request with custom tls options", () => {
|
||||
const createServer = () =>
|
||||
new Promise(resolve => {
|
||||
const server = createHttpsServer(
|
||||
{
|
||||
key: nodefs.readFileSync(path.join(import.meta.dir, "fixtures", "openssl_localhost.key")),
|
||||
cert: nodefs.readFileSync(path.join(import.meta.dir, "fixtures", "openssl_localhost.crt")),
|
||||
rejectUnauthorized: true,
|
||||
},
|
||||
(req, res) => {
|
||||
res.writeHead(200);
|
||||
res.end("hello world");
|
||||
},
|
||||
);
|
||||
|
||||
listen(server, "https").then(url => {
|
||||
resolve({
|
||||
server,
|
||||
close: () => server.close(),
|
||||
url,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("supports custom tls args", async done => {
|
||||
const { url, close } = await createServer();
|
||||
try {
|
||||
const options: https.RequestOptions = {
|
||||
method: "GET",
|
||||
url,
|
||||
port: url.port,
|
||||
ca: nodefs.readFileSync(path.join(import.meta.dir, "fixtures", "openssl_localhost_ca.pem")),
|
||||
};
|
||||
const req = https.request(options, res => {
|
||||
res.on("data", () => null);
|
||||
res.on("end", () => {
|
||||
close();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
req.on("error", error => {
|
||||
close();
|
||||
done(error);
|
||||
});
|
||||
|
||||
req.end();
|
||||
} catch (e) {
|
||||
close();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("signal", () => {
|
||||
it("should abort and close the server", done => {
|
||||
const server = createServer((req, res) => {
|
||||
@@ -2039,3 +2093,62 @@ it("ServerResponse ClientRequest field exposes agent getter", async () => {
|
||||
server.close();
|
||||
}
|
||||
});
|
||||
|
||||
it("should accept custom certs when provided", async () => {
|
||||
const server = https.createServer(
|
||||
{
|
||||
key: nodefs.readFileSync(path.join(import.meta.dir, "fixtures", "openssl_localhost.key")),
|
||||
cert: nodefs.readFileSync(path.join(import.meta.dir, "fixtures", "openssl_localhost.crt")),
|
||||
passphrase: "123123123",
|
||||
},
|
||||
(req, res) => {
|
||||
res.write("Hello from https server");
|
||||
res.end();
|
||||
},
|
||||
);
|
||||
server.listen(0, "localhost");
|
||||
const address = server.address();
|
||||
|
||||
let url_address = address.address;
|
||||
const res = await fetch(`https://localhost:${address.port}`, {
|
||||
tls: {
|
||||
rejectUnauthorized: true,
|
||||
ca: nodefs.readFileSync(path.join(import.meta.dir, "fixtures", "openssl_localhost_ca.pem")),
|
||||
},
|
||||
});
|
||||
const t = await res.text();
|
||||
expect(t).toEqual("Hello from https server");
|
||||
|
||||
server.close();
|
||||
});
|
||||
it("should error with faulty args", async () => {
|
||||
const server = https.createServer(
|
||||
{
|
||||
key: nodefs.readFileSync(path.join(import.meta.dir, "fixtures", "openssl_localhost.key")),
|
||||
cert: nodefs.readFileSync(path.join(import.meta.dir, "fixtures", "openssl_localhost.crt")),
|
||||
passphrase: "123123123",
|
||||
},
|
||||
(req, res) => {
|
||||
res.write("Hello from https server");
|
||||
res.end();
|
||||
},
|
||||
);
|
||||
server.listen(0, "localhost");
|
||||
const address = server.address();
|
||||
|
||||
try {
|
||||
let url_address = address.address;
|
||||
const res = await fetch(`https://localhost:${address.port}`, {
|
||||
tls: {
|
||||
rejectUnauthorized: true,
|
||||
ca: "some invalid value for a ca",
|
||||
},
|
||||
});
|
||||
await res.text();
|
||||
expect(true).toBe("unreacheable");
|
||||
} catch (err) {
|
||||
expect(err.code).toBe("FailedToOpenSocket");
|
||||
expect(err.message).toBe("Was there a typo in the url or port?");
|
||||
}
|
||||
server.close();
|
||||
});
|
||||
|
||||
@@ -49,7 +49,7 @@ it("allow renegotiation in https module", async () => {
|
||||
path: url.pathname,
|
||||
method: "GET",
|
||||
keepalive: false,
|
||||
tls: { rejectUnauthorized: false },
|
||||
rejectUnauthorized: false,
|
||||
},
|
||||
(res: IncomingMessage) => {
|
||||
res.setEncoding("utf8");
|
||||
@@ -79,7 +79,7 @@ it("should fail if renegotiation fails using https", async () => {
|
||||
path: url.pathname,
|
||||
method: "GET",
|
||||
keepalive: false,
|
||||
tls: { rejectUnauthorized: true },
|
||||
rejectUnauthorized: true,
|
||||
},
|
||||
(res: IncomingMessage) => {
|
||||
res.setEncoding("utf8");
|
||||
|
||||
16
test/js/web/fetch/fetch-leak-test-fixture-2.js
generated
16
test/js/web/fetch/fetch-leak-test-fixture-2.js
generated
@@ -11,7 +11,13 @@ var oks = 0;
|
||||
var textLength = 0;
|
||||
Bun.gc(true);
|
||||
const baseline = await (async function runAll() {
|
||||
const resp = await fetch(SERVER);
|
||||
const tls =
|
||||
process.env.NAME === "tls-with-client"
|
||||
? {
|
||||
cert: "-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAKLdQVPy90jjMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV\nBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX\naWRnaXRzIFB0eSBMdGQwHhcNMTkwMjAzMTQ0OTM1WhcNMjAwMjAzMTQ0OTM1WjBF\nMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50\nZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\nCgKCAQEA7i7IIEdICTiSTVx+ma6xHxOtcbd6wGW3nkxlCkJ1UuV8NmY5ovMsGnGD\nhJJtUQ2j5ig5BcJUf3tezqCNW4tKnSOgSISfEAKvpn2BPvaFq3yx2Yjz0ruvcGKp\nDMZBXmB/AAtGyN/UFXzkrcfppmLHJTaBYGG6KnmU43gPkSDy4iw46CJFUOupc51A\nFIz7RsE7mbT1plCM8e75gfqaZSn2k+Wmy+8n1HGyYHhVISRVvPqkS7gVLSVEdTea\nUtKP1Vx/818/HDWk3oIvDVWI9CFH73elNxBkMH5zArSNIBTehdnehyAevjY4RaC/\nkK8rslO3e4EtJ9SnA4swOjCiqAIQEwIDAQABo1AwTjAdBgNVHQ4EFgQUv5rc9Smm\n9c4YnNf3hR49t4rH4yswHwYDVR0jBBgwFoAUv5rc9Smm9c4YnNf3hR49t4rH4ysw\nDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEATcL9CAAXg0u//eYUAlQa\nL+l8yKHS1rsq1sdmx7pvsmfZ2g8ONQGfSF3TkzkI2OOnCBokeqAYuyT8awfdNUtE\nEHOihv4ZzhK2YZVuy0fHX2d4cCFeQpdxno7aN6B37qtsLIRZxkD8PU60Dfu9ea5F\nDDynnD0TUabna6a0iGn77yD8GPhjaJMOz3gMYjQFqsKL252isDVHEDbpVxIzxPmN\nw1+WK8zRNdunAcHikeoKCuAPvlZ83gDQHp07dYdbuZvHwGj0nfxBLc9qt90XsBtC\n4IYR7c/bcLMmKXYf0qoQ4OzngsnPI5M+v9QEHvYWaKVwFY4CTcSNJEwfXw+BAeO5\nOA==\n-----END CERTIFICATE-----",
|
||||
}
|
||||
: null;
|
||||
const resp = await fetch(SERVER, { tls });
|
||||
textLength = Number(resp.headers.get("Content-Length"));
|
||||
if (!textLength) {
|
||||
throw new Error("Content-Length header is not set");
|
||||
@@ -24,7 +30,13 @@ Bun.gc(true);
|
||||
|
||||
for (let j = 0; j < COUNT; j++) {
|
||||
await (async function runAll() {
|
||||
oks += !!(await (await fetch(SERVER)).arrayBuffer())?.byteLength;
|
||||
const tls =
|
||||
process.env.NAME === "tls-with-client"
|
||||
? {
|
||||
cert: "-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAKLdQVPy90jjMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV\nBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX\naWRnaXRzIFB0eSBMdGQwHhcNMTkwMjAzMTQ0OTM1WhcNMjAwMjAzMTQ0OTM1WjBF\nMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50\nZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\nCgKCAQEA7i7IIEdICTiSTVx+ma6xHxOtcbd6wGW3nkxlCkJ1UuV8NmY5ovMsGnGD\nhJJtUQ2j5ig5BcJUf3tezqCNW4tKnSOgSISfEAKvpn2BPvaFq3yx2Yjz0ruvcGKp\nDMZBXmB/AAtGyN/UFXzkrcfppmLHJTaBYGG6KnmU43gPkSDy4iw46CJFUOupc51A\nFIz7RsE7mbT1plCM8e75gfqaZSn2k+Wmy+8n1HGyYHhVISRVvPqkS7gVLSVEdTea\nUtKP1Vx/818/HDWk3oIvDVWI9CFH73elNxBkMH5zArSNIBTehdnehyAevjY4RaC/\nkK8rslO3e4EtJ9SnA4swOjCiqAIQEwIDAQABo1AwTjAdBgNVHQ4EFgQUv5rc9Smm\n9c4YnNf3hR49t4rH4yswHwYDVR0jBBgwFoAUv5rc9Smm9c4YnNf3hR49t4rH4ysw\nDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEATcL9CAAXg0u//eYUAlQa\nL+l8yKHS1rsq1sdmx7pvsmfZ2g8ONQGfSF3TkzkI2OOnCBokeqAYuyT8awfdNUtE\nEHOihv4ZzhK2YZVuy0fHX2d4cCFeQpdxno7aN6B37qtsLIRZxkD8PU60Dfu9ea5F\nDDynnD0TUabna6a0iGn77yD8GPhjaJMOz3gMYjQFqsKL252isDVHEDbpVxIzxPmN\nw1+WK8zRNdunAcHikeoKCuAPvlZ83gDQHp07dYdbuZvHwGj0nfxBLc9qt90XsBtC\n4IYR7c/bcLMmKXYf0qoQ4OzngsnPI5M+v9QEHvYWaKVwFY4CTcSNJEwfXw+BAeO5\nOA==\n-----END CERTIFICATE-----",
|
||||
}
|
||||
: null;
|
||||
oks += !!(await (await fetch(SERVER, { tls })).arrayBuffer())?.byteLength;
|
||||
})();
|
||||
}
|
||||
|
||||
|
||||
@@ -32,10 +32,12 @@ describe("fetch doesn't leak", () => {
|
||||
});
|
||||
|
||||
// This tests for body leakage and Response object leakage.
|
||||
async function runTest(compressed, tls) {
|
||||
async function runTest(compressed, name) {
|
||||
const body = !compressed
|
||||
? new Blob(["some body in here!".repeat(2000000)])
|
||||
: new Blob([Bun.deflateSync(crypto.getRandomValues(new Buffer(65123)))]);
|
||||
|
||||
const tls = name.includes("tls");
|
||||
const headers = {
|
||||
"Content-Type": "application/octet-stream",
|
||||
};
|
||||
@@ -60,6 +62,7 @@ describe("fetch doesn't leak", () => {
|
||||
...bunEnv,
|
||||
SERVER: `${tls ? "https" : "http"}://${server.hostname}:${server.port}`,
|
||||
BUN_JSC_forceRAMSize: (1024 * 1024 * 64).toString("10"),
|
||||
NAME: name,
|
||||
};
|
||||
|
||||
if (tls) {
|
||||
@@ -83,10 +86,10 @@ describe("fetch doesn't leak", () => {
|
||||
|
||||
for (let compressed of [true, false]) {
|
||||
describe(compressed ? "compressed" : "uncompressed", () => {
|
||||
for (let tls of [true, false]) {
|
||||
describe(tls ? "tls" : "tcp", () => {
|
||||
for (let name of ["tcp", "tls", "tls-with-client"]) {
|
||||
describe(name, () => {
|
||||
test("fixture #2", async () => {
|
||||
await runTest(compressed, tls);
|
||||
await runTest(compressed, name);
|
||||
}, 100000);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user