Compare commits

...

1 Commits

Author SHA1 Message Date
Jarred Sumner
44b3423b9e test(node): add client default header test and fix headers 2025-05-29 16:00:47 -07:00
4 changed files with 106 additions and 11 deletions

View File

@@ -1223,6 +1223,7 @@ pub const FetchTasklet = struct {
.disable_timeout = fetch_options.disable_timeout,
.disable_keepalive = fetch_options.disable_keepalive,
.disable_decompression = fetch_options.disable_decompression,
.disable_default_headers = fetch_options.disable_default_headers,
.reject_unauthorized = fetch_options.reject_unauthorized,
.verbose = fetch_options.verbose,
.tls_props = fetch_options.ssl_config,
@@ -1297,6 +1298,7 @@ pub const FetchTasklet = struct {
disable_timeout: bool,
disable_keepalive: bool,
disable_decompression: bool,
disable_default_headers: bool,
reject_unauthorized: bool,
url: ZigURL,
verbose: http.HTTPVerboseLevel = .none,
@@ -1567,6 +1569,7 @@ pub fn Bun__fetch_(
var disable_timeout = false;
var disable_keepalive = false;
var disable_decompression = false;
var disable_default_headers = false;
var verbose: http.HTTPVerboseLevel = if (vm.log.level.atLeast(.debug)) .headers else .none;
if (verbose == .none) {
verbose = vm.getVerboseFetch();
@@ -1788,6 +1791,38 @@ pub fn Bun__fetch_(
return .zero;
}
// defaultHeaders: boolean | undefined
disable_default_headers = extract_disable_default_headers: {
const objects_to_try = [_]JSValue{
options_object orelse .zero,
request_init_object orelse .zero,
};
inline for (0..2) |i| {
if (objects_to_try[i] != .zero) {
if (try objects_to_try[i].get(globalThis, "defaultHeaders")) |dh_value| {
if (dh_value.isBoolean()) {
break :extract_disable_default_headers !dh_value.asBoolean();
} else if (dh_value.isNumber()) {
break :extract_disable_default_headers dh_value.to(i32) == 0;
}
}
if (globalThis.hasException()) {
is_error = true;
return .zero;
}
}
}
break :extract_disable_default_headers disable_default_headers;
};
if (globalThis.hasException()) {
is_error = true;
return .zero;
}
// "tls: TLSConfig"
ssl_config = extract_ssl_config: {
const objects_to_try = [_]JSValue{
@@ -2675,6 +2710,7 @@ pub fn Bun__fetch_(
.disable_keepalive = disable_keepalive,
.disable_timeout = disable_timeout,
.disable_decompression = disable_decompression,
.disable_default_headers = disable_default_headers,
.reject_unauthorized = reject_unauthorized,
.redirect_type = redirect_type,
.verbose = verbose,

View File

@@ -2275,7 +2275,8 @@ pub const Flags = packed struct(u16) {
is_preconnect_only: bool = false,
is_streaming_request_body: bool = false,
defer_fail_until_connecting_is_complete: bool = false,
_padding: u5 = 0,
disable_default_headers: bool = false,
_padding: u4 = 0,
};
// TODO: reduce the size of this struct
@@ -2559,6 +2560,7 @@ pub const AsyncHTTP = struct {
verbose: ?HTTPVerboseLevel = null,
disable_keepalive: ?bool = null,
disable_decompression: ?bool = null,
disable_default_headers: ?bool = null,
reject_unauthorized: ?bool = null,
tls_props: ?*SSLConfig = null,
};
@@ -2659,6 +2661,9 @@ pub const AsyncHTTP = struct {
if (options.disable_decompression) |val| {
this.client.flags.disable_decompression = val;
}
if (options.disable_default_headers) |val| {
this.client.flags.disable_default_headers = val;
}
if (options.disable_keepalive) |val| {
this.client.flags.disable_keepalive = val;
}
@@ -3005,14 +3010,16 @@ pub fn buildRequest(this: *HTTPClient, body_len: usize) picohttp.Request {
header_count += 1;
}
if (!override_user_agent) {
request_headers_buf[header_count] = user_agent_header;
header_count += 1;
}
if (!this.flags.disable_default_headers) {
if (!override_user_agent) {
request_headers_buf[header_count] = user_agent_header;
header_count += 1;
}
if (!override_accept_header) {
request_headers_buf[header_count] = accept_header;
header_count += 1;
if (!override_accept_header) {
request_headers_buf[header_count] = accept_header;
header_count += 1;
}
}
if (!override_host_header) {
@@ -3023,10 +3030,12 @@ pub fn buildRequest(this: *HTTPClient, body_len: usize) picohttp.Request {
header_count += 1;
}
if (!override_accept_encoding and !this.flags.disable_decompression) {
request_headers_buf[header_count] = accept_encoding_header;
if (!this.flags.disable_default_headers) {
if (!override_accept_encoding and !this.flags.disable_decompression) {
request_headers_buf[header_count] = accept_encoding_header;
header_count += 1;
header_count += 1;
}
}
if (body_len > 0 or this.method.hasRequestBody()) {

View File

@@ -305,6 +305,8 @@ function ClientRequest(input, options, cb) {
timeout: false,
// Disable auto gzip/deflate
decompress: false,
// Do not set Bun's default headers
defaultHeaders: false,
keepalive,
};
let keepOpen = false;

View File

@@ -0,0 +1,48 @@
const common = require('../common');
const assert = require('assert');
const http = require('http');
const { once } = require('events');
const expectedHeaders = {
'DELETE': ['host', 'connection'],
'GET': ['host', 'connection'],
'HEAD': ['host', 'connection'],
'OPTIONS': ['host', 'connection'],
'POST': ['host', 'connection', 'content-length'],
'PUT': ['host', 'connection', 'content-length'],
'TRACE': ['host', 'connection']
};
const expectedMethods = Object.keys(expectedHeaders);
const server = http.createServer(common.mustCall((req, res) => {
res.end();
assert(Object.hasOwn(expectedHeaders, req.method),
`${req.method} was an unexpected method`);
const requestHeaders = Object.keys(req.headers);
for (const header of requestHeaders) {
assert.ok(
expectedHeaders[req.method].includes(header.toLowerCase()),
`${header} should not exist for method ${req.method}`
);
}
assert.strictEqual(
requestHeaders.length,
expectedHeaders[req.method].length,
`some headers were missing for method: ${req.method}`
);
}, expectedMethods.length));
server.listen(0, common.mustCall(() => {
Promise.all(expectedMethods.map(async (method) => {
const request = http.request({
method: method,
port: server.address().port
}).end();
return once(request, 'response');
})).then(common.mustCall(() => { server.close(); }));
}));