feat(dns) resolveSrv (#1870)

* initial steps

* in-progress resolveSrv support

* fix memory leak and ZigGlobalObject

* promise.resolve + fix priority and weight mistake

* fix node dns export

* little better test

* add poll_ref to keep alive loop in callback for resolveSrv and lookup

* add test for error case in resolveSrc

* fix Bun.dns.resolveSrv type
This commit is contained in:
Ciro Spaciari
2023-01-22 19:31:08 -03:00
committed by GitHub
parent 8d692f1511
commit 481dbf7c6e
12 changed files with 1129 additions and 32 deletions

View File

@@ -1,8 +1,444 @@
{
"name": "bun-error",
"version": "1.0.0",
"lockfileVersion": 1,
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "bun-error",
"version": "1.0.0",
"license": "MIT",
"dependencies": {
"esbuild": "latest",
"react": "^17.0.2",
"react-dom": "^17.0.2"
},
"devDependencies": {
"@types/react": "^17.0.39"
}
},
"node_modules/@types/prop-types": {
"version": "15.7.5",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
"integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==",
"dev": true
},
"node_modules/@types/react": {
"version": "17.0.47",
"resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.47.tgz",
"integrity": "sha512-mk0BL8zBinf2ozNr3qPnlu1oyVTYq+4V7WA76RgxUAtf0Em/Wbid38KN6n4abEkvO4xMTBWmnP1FtQzgkEiJoA==",
"dev": true,
"dependencies": {
"@types/prop-types": "*",
"@types/scheduler": "*",
"csstype": "^3.0.2"
}
},
"node_modules/@types/scheduler": {
"version": "0.16.2",
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
"integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==",
"dev": true
},
"node_modules/csstype": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
"integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==",
"dev": true
},
"node_modules/esbuild": {
"version": "0.14.48",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.48.tgz",
"integrity": "sha512-w6N1Yn5MtqK2U1/WZTX9ZqUVb8IOLZkZ5AdHkT6x3cHDMVsYWC7WPdiLmx19w3i4Rwzy5LqsEMtVihG3e4rFzA==",
"hasInstallScript": true,
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=12"
},
"optionalDependencies": {
"esbuild-android-64": "0.14.48",
"esbuild-android-arm64": "0.14.48",
"esbuild-darwin-64": "0.14.48",
"esbuild-darwin-arm64": "0.14.48",
"esbuild-freebsd-64": "0.14.48",
"esbuild-freebsd-arm64": "0.14.48",
"esbuild-linux-32": "0.14.48",
"esbuild-linux-64": "0.14.48",
"esbuild-linux-arm": "0.14.48",
"esbuild-linux-arm64": "0.14.48",
"esbuild-linux-mips64le": "0.14.48",
"esbuild-linux-ppc64le": "0.14.48",
"esbuild-linux-riscv64": "0.14.48",
"esbuild-linux-s390x": "0.14.48",
"esbuild-netbsd-64": "0.14.48",
"esbuild-openbsd-64": "0.14.48",
"esbuild-sunos-64": "0.14.48",
"esbuild-windows-32": "0.14.48",
"esbuild-windows-64": "0.14.48",
"esbuild-windows-arm64": "0.14.48"
}
},
"node_modules/esbuild-android-64": {
"version": "0.14.48",
"resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.48.tgz",
"integrity": "sha512-3aMjboap/kqwCUpGWIjsk20TtxVoKck8/4Tu19rubh7t5Ra0Yrpg30Mt1QXXlipOazrEceGeWurXKeFJgkPOUg==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-android-arm64": {
"version": "0.14.48",
"resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.48.tgz",
"integrity": "sha512-vptI3K0wGALiDq+EvRuZotZrJqkYkN5282iAfcffjI5lmGG9G1ta/CIVauhY42MBXwEgDJkweiDcDMRLzBZC4g==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-darwin-64": {
"version": "0.14.48",
"resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.48.tgz",
"integrity": "sha512-gGQZa4+hab2Va/Zww94YbshLuWteyKGD3+EsVon8EWTWhnHFRm5N9NbALNbwi/7hQ/hM1Zm4FuHg+k6BLsl5UA==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-darwin-arm64": {
"version": "0.14.48",
"resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.48.tgz",
"integrity": "sha512-bFjnNEXjhZT+IZ8RvRGNJthLWNHV5JkCtuOFOnjvo5pC0sk2/QVk0Qc06g2PV3J0TcU6kaPC3RN9yy9w2PSLEA==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-freebsd-64": {
"version": "0.14.48",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.48.tgz",
"integrity": "sha512-1NOlwRxmOsnPcWOGTB10JKAkYSb2nue0oM1AfHWunW/mv3wERfJmnYlGzL3UAOIUXZqW8GeA2mv+QGwq7DToqA==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-freebsd-arm64": {
"version": "0.14.48",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.48.tgz",
"integrity": "sha512-gXqKdO8wabVcYtluAbikDH2jhXp+Klq5oCD5qbVyUG6tFiGhrC9oczKq3vIrrtwcxDQqK6+HDYK8Zrd4bCA9Gw==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-32": {
"version": "0.14.48",
"resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.48.tgz",
"integrity": "sha512-ghGyDfS289z/LReZQUuuKq9KlTiTspxL8SITBFQFAFRA/IkIvDpnZnCAKTCjGXAmUqroMQfKJXMxyjJA69c/nQ==",
"cpu": [
"ia32"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-64": {
"version": "0.14.48",
"resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.48.tgz",
"integrity": "sha512-vni3p/gppLMVZLghI7oMqbOZdGmLbbKR23XFARKnszCIBpEMEDxOMNIKPmMItQrmH/iJrL1z8Jt2nynY0bE1ug==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-arm": {
"version": "0.14.48",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.48.tgz",
"integrity": "sha512-+VfSV7Akh1XUiDNXgqgY1cUP1i2vjI+BmlyXRfVz5AfV3jbpde8JTs5Q9sYgaoq5cWfuKfoZB/QkGOI+QcL1Tw==",
"cpu": [
"arm"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-arm64": {
"version": "0.14.48",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.48.tgz",
"integrity": "sha512-3CFsOlpoxlKPRevEHq8aAntgYGYkE1N9yRYAcPyng/p4Wyx0tPR5SBYsxLKcgPB9mR8chHEhtWYz6EZ+H199Zw==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-mips64le": {
"version": "0.14.48",
"resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.48.tgz",
"integrity": "sha512-cs0uOiRlPp6ymknDnjajCgvDMSsLw5mST2UXh+ZIrXTj2Ifyf2aAP3Iw4DiqgnyYLV2O/v/yWBJx+WfmKEpNLA==",
"cpu": [
"mips64el"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-ppc64le": {
"version": "0.14.48",
"resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.48.tgz",
"integrity": "sha512-+2F0vJMkuI0Wie/wcSPDCqXvSFEELH7Jubxb7mpWrA/4NpT+/byjxDz0gG6R1WJoeDefcrMfpBx4GFNN1JQorQ==",
"cpu": [
"ppc64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-riscv64": {
"version": "0.14.48",
"resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.48.tgz",
"integrity": "sha512-BmaK/GfEE+5F2/QDrIXteFGKnVHGxlnK9MjdVKMTfvtmudjY3k2t8NtlY4qemKSizc+QwyombGWTBDc76rxePA==",
"cpu": [
"riscv64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-s390x": {
"version": "0.14.48",
"resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.48.tgz",
"integrity": "sha512-tndw/0B9jiCL+KWKo0TSMaUm5UWBLsfCKVdbfMlb3d5LeV9WbijZ8Ordia8SAYv38VSJWOEt6eDCdOx8LqkC4g==",
"cpu": [
"s390x"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-netbsd-64": {
"version": "0.14.48",
"resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.48.tgz",
"integrity": "sha512-V9hgXfwf/T901Lr1wkOfoevtyNkrxmMcRHyticybBUHookznipMOHoF41Al68QBsqBxnITCEpjjd4yAos7z9Tw==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-openbsd-64": {
"version": "0.14.48",
"resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.48.tgz",
"integrity": "sha512-+IHf4JcbnnBl4T52egorXMatil/za0awqzg2Vy6FBgPcBpisDWT2sVz/tNdrK9kAqj+GZG/jZdrOkj7wsrNTKA==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-sunos-64": {
"version": "0.14.48",
"resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.48.tgz",
"integrity": "sha512-77m8bsr5wOpOWbGi9KSqDphcq6dFeJyun8TA+12JW/GAjyfTwVtOnN8DOt6DSPUfEV+ltVMNqtXUeTeMAxl5KA==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-windows-32": {
"version": "0.14.48",
"resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.48.tgz",
"integrity": "sha512-EPgRuTPP8vK9maxpTGDe5lSoIBHGKO/AuxDncg5O3NkrPeLNdvvK8oywB0zGaAZXxYWfNNSHskvvDgmfVTguhg==",
"cpu": [
"ia32"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-windows-64": {
"version": "0.14.48",
"resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.48.tgz",
"integrity": "sha512-YmpXjdT1q0b8ictSdGwH3M8VCoqPpK1/UArze3X199w6u8hUx3V8BhAi1WjbsfDYRBanVVtduAhh2sirImtAvA==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-windows-arm64": {
"version": "0.14.48",
"resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.48.tgz",
"integrity": "sha512-HHaOMCsCXp0rz5BT2crTka6MPWVno121NKApsGs/OIW5QC0ggC69YMGs1aJct9/9FSUF4A1xNE/cLvgB5svR4g==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
},
"node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"dependencies": {
"js-tokens": "^3.0.0 || ^4.0.0"
},
"bin": {
"loose-envify": "cli.js"
}
},
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/react": {
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz",
"integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==",
"dependencies": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/react-dom": {
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz",
"integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==",
"dependencies": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"scheduler": "^0.20.2"
},
"peerDependencies": {
"react": "17.0.2"
}
},
"node_modules/scheduler": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz",
"integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==",
"dependencies": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
}
}
},
"dependencies": {
"@types/prop-types": {
"version": "15.7.5",

View File

@@ -650,13 +650,13 @@ declare module "dns" {
* ```
* @since v0.1.27
*/
// export function resolveSrv(
// hostname: string,
// callback: (err: ErrnoException | null, addresses: SrvRecord[]) => void,
// ): void;
// export namespace resolveSrv {
// function __promisify__(hostname: string): Promise<SrvRecord[]>;
// }
export function resolveSrv(
hostname: string,
callback: (err: ErrnoException | null, addresses: SrvRecord[]) => void,
): void;
export namespace resolveSrv {
function __promisify__(hostname: string): Promise<SrvRecord[]>;
}
/**
* Uses the DNS protocol to resolve text queries (`TXT` records) for the`hostname`. The `records` argument passed to the `callback` function is a
* two-dimensional array of the text records available for `hostname` (e.g.`[ ['v=spf1 ip4:0.0.0.0 ', '~all' ] ]`). Each sub-array contains TXT chunks of
@@ -867,7 +867,7 @@ declare module "dns" {
// resolveNs: typeof resolveNs;
// resolvePtr: typeof resolvePtr;
// resolveSoa: typeof resolveSoa;
// resolveSrv: typeof resolveSrv;
resolveSrv: typeof resolveSrv;
// resolveTxt: typeof resolveTxt;
// reverse: typeof reverse;
/**

View File

@@ -15,7 +15,7 @@ declare module "dns/promises" {
// MxRecord,
// NaptrRecord,
// SoaRecord,
// SrvRecord,
SrvRecord,
ResolveWithTtlOptions,
RecordWithTtl,
ResolveOptions,
@@ -144,7 +144,7 @@ declare module "dns/promises" {
// function resolve(hostname: string, rrtype: "NS"): Promise<string[]>;
// function resolve(hostname: string, rrtype: "PTR"): Promise<string[]>;
// function resolve(hostname: string, rrtype: "SOA"): Promise<SoaRecord>;
// function resolve(hostname: string, rrtype: "SRV"): Promise<SrvRecord[]>;
function resolveSrv(hostname: string): Promise<SrvRecord[]>;
// function resolve(hostname: string, rrtype: "TXT"): Promise<string[][]>;
// function resolve(
// hostname: string,
@@ -319,7 +319,7 @@ declare module "dns/promises" {
* ```
* @since v10.6.0
*/
// function resolveSrv(hostname: string): Promise<SrvRecord[]>;
function resolveSrv(hostname: string): Promise<SrvRecord[]>;
/**
* Uses the DNS protocol to resolve text queries (`TXT` records) for the`hostname`. On success, the `Promise` is resolved with a two-dimensional array
* of the text records available for `hostname` (e.g.`[ ['v=spf1 ip4:0.0.0.0 ', '~all' ] ]`). Each sub-array contains TXT chunks of
@@ -390,7 +390,7 @@ declare module "dns/promises" {
// resolveNs: typeof resolveNs;
// resolvePtr: typeof resolvePtr;
// resolveSoa: typeof resolveSoa;
// resolveSrv: typeof resolveSrv;
resolveSrv: typeof resolveSrv;
// resolveTxt: typeof resolveTxt;
// reverse: typeof reverse;
// setLocalAddress(ipv4?: string, ipv6?: string): void;

View File

@@ -614,6 +614,111 @@ pub const GetAddrInfo = struct {
};
};
pub const ResolveSrvInfoRequest = struct {
const log = Output.scoped(.ResolveSrvInfoRequest, false);
resolver_for_caching: ?*DNSResolver = null,
hash: u64 = 0,
cache: ResolveSrvInfoRequest.CacheConfig = ResolveSrvInfoRequest.CacheConfig{},
head: SrvLookup,
tail: *SrvLookup = undefined,
task: bun.ThreadPool.Task = undefined,
pub fn init(
cache: DNSResolver.SrvCacheHit,
resolver: ?*DNSResolver,
name: [] const u8,
globalThis: *JSC.JSGlobalObject,
comptime cache_field: []const u8,
) !*ResolveSrvInfoRequest {
var request = try globalThis.allocator().create(ResolveSrvInfoRequest);
var hasher = std.hash.Wyhash.init(0);
hasher.update(name);
const hash = hasher.final();
var poll_ref = JSC.PollRef.init();
poll_ref.ref(globalThis.bunVM());
request.* = .{
.resolver_for_caching = resolver,
.hash = hash,
.head = .{
.poll_ref = poll_ref,
.globalThis = globalThis,
.promise = JSC.JSPromise.Strong.init(globalThis),
.allocated = false,
},
};
request.tail = &request.head;
if (cache == .new) {
request.resolver_for_caching = resolver;
request.cache = ResolveSrvInfoRequest.CacheConfig{
.pending_cache = true,
.entry_cache = false,
.pos_in_pending = @truncate(u5, @field(resolver.?, cache_field).indexOf(cache.new).?),
.name_len = @truncate(u9, name.len),
};
cache.new.lookup = request;
}
return request;
}
pub const Task = bun.JSC.WorkTask(ResolveSrvInfoRequest, false);
pub const CacheConfig = packed struct(u16) {
pending_cache: bool = false,
entry_cache: bool = false,
pos_in_pending: u5 = 0,
name_len: u9 = 0,
};
pub const PendingCacheKey = struct {
hash: u64,
len: u16,
lookup: *ResolveSrvInfoRequest = undefined,
pub fn append(this: *PendingCacheKey, srv_lookup: *SrvLookup) void {
var tail = this.lookup.tail;
tail.next = srv_lookup;
this.lookup.tail = srv_lookup;
}
pub fn init(name: []const u8) PendingCacheKey {
var hasher = std.hash.Wyhash.init(0);
hasher.update(name);
const hash = hasher.final();
return PendingCacheKey{
.hash = hash,
.len = @truncate(u16, name.len),
.lookup = undefined,
};
}
};
pub fn onCaresComplete(this: *ResolveSrvInfoRequest, err_: ?c_ares.Error, timeout: i32, result: ?*c_ares.struct_ares_srv_reply) void {
if (this.resolver_for_caching) |resolver| {
if (this.cache.pending_cache) {
resolver.drainPendingSrvCares(
this.cache.pos_in_pending,
err_,
timeout,
result,
);
return;
}
}
var head = this.head;
bun.default_allocator.destroy(this);
head.processResolveSrv(err_, timeout, result);
}
};
pub const GetAddrInfoRequest = struct {
const log = Output.scoped(.GetAddrInfoRequest, false);
@@ -634,12 +739,15 @@ pub const GetAddrInfoRequest = struct {
comptime cache_field: []const u8,
) !*GetAddrInfoRequest {
var request = try globalThis.allocator().create(GetAddrInfoRequest);
var poll_ref = JSC.PollRef.init();
poll_ref.ref(globalThis.bunVM());
request.* = .{
.backend = backend,
.resolver_for_caching = resolver,
.hash = query.hash(),
.head = .{
.globalThis = globalThis,
.poll_ref = poll_ref,
.promise = JSC.JSPromise.Strong.init(globalThis),
.allocated = false,
},
@@ -832,6 +940,82 @@ pub const GetAddrInfoRequest = struct {
}
};
pub const SrvLookup = struct {
const log = Output.scoped(.SrvLookup, true);
globalThis: *JSC.JSGlobalObject = undefined,
promise: JSC.JSPromise.Strong,
poll_ref: JSC.PollRef,
allocated: bool = false,
next: ?*SrvLookup = null,
pub fn init(globalThis: *JSC.JSGlobalObject, allocator: std.mem.Allocator) !*SrvLookup {
var this = try allocator.create(SrvLookup);
var poll_ref = JSC.PollRef.init();
poll_ref.ref(globalThis.bunVM());
this.* = .{
.globalThis = globalThis,
.promise = JSC.JSPromise.Strong.init(globalThis),
.poll_ref = poll_ref,
.allocated = true,
};
return this;
}
pub fn processResolveSrv(this: *SrvLookup, err_: ?c_ares.Error, _: i32, result: ?*c_ares.struct_ares_srv_reply) void {
if (err_) |err| {
var promise = this.promise;
var globalThis = this.globalThis;
const error_value = globalThis.createErrorInstance("SRV lookup failed: {s}", .{err.label()});
error_value.put(
globalThis,
JSC.ZigString.static("code"),
JSC.ZigString.init(err.code()).toValueGC(globalThis),
);
promise.reject(globalThis, error_value);
this.deinit();
return;
}
if (result == null or result.?.next == null) {
var promise = this.promise;
var globalThis = this.globalThis;
const error_value = globalThis.createErrorInstance("SRV lookup failed: {s}", .{"No results"});
error_value.put(
globalThis,
JSC.ZigString.static("code"),
JSC.ZigString.init("EUNREACHABLE").toValueGC(globalThis),
);
promise.reject(globalThis, error_value);
this.deinit();
return;
}
var node = result.?;
const array = node.toJSArray(this.globalThis.allocator(), this.globalThis);
this.onComplete(array);
return;
}
pub fn onComplete(this: *SrvLookup, result: JSC.JSValue) void {
var promise = this.promise;
var globalThis = this.globalThis;
this.promise = .{};
promise.resolve(globalThis, result);
this.deinit();
}
pub fn deinit(this: *SrvLookup) void {
this.poll_ref.unrefOnNextTick(this.globalThis.bunVM());
if (this.allocated)
this.globalThis.allocator().destroy(this);
}
};
pub const DNSLookup = struct {
const log = Output.scoped(.DNSLookup, true);
@@ -839,11 +1023,16 @@ pub const DNSLookup = struct {
promise: JSC.JSPromise.Strong,
allocated: bool = false,
next: ?*DNSLookup = null,
poll_ref: JSC.PollRef,
pub fn init(globalThis: *JSC.JSGlobalObject, allocator: std.mem.Allocator) !*DNSLookup {
var this = try allocator.create(DNSLookup);
var poll_ref = JSC.PollRef.init();
poll_ref.ref(globalThis.bunVM());
this.* = .{
.globalThis = globalThis,
.poll_ref = poll_ref,
.promise = JSC.JSPromise.Strong.init(globalThis),
.allocated = true,
};
@@ -893,9 +1082,8 @@ pub const DNSLookup = struct {
JSC.ZigString.init(err.code()).toValueGC(globalThis),
);
this.deinit();
promise.reject(globalThis, error_value);
this.deinit();
return;
}
@@ -909,8 +1097,8 @@ pub const DNSLookup = struct {
JSC.ZigString.init("EUNREACHABLE").toValueGC(globalThis),
);
this.deinit();
promise.reject(globalThis, error_value);
this.deinit();
return;
}
@@ -927,11 +1115,12 @@ pub const DNSLookup = struct {
var globalThis = this.globalThis;
this.promise = .{};
promise.resolve(globalThis, result);
this.deinit();
promise.resolveOnNextTick(globalThis, result);
}
pub fn deinit(this: *DNSLookup) void {
this.poll_ref.unrefOnNextTick(this.globalThis.bunVM());
if (this.allocated)
this.globalThis.allocator().destroy(this);
}
@@ -961,10 +1150,12 @@ pub const DNSResolver = struct {
polls: std.AutoArrayHashMap(i32, ?*JSC.FilePoll) = undefined,
pending_host_cache_cares: PendingCache = PendingCache.init(),
pending_srv_cache_cares: SrvPendingCache = SrvPendingCache.init(),
pending_host_cache_native: PendingCache = PendingCache.init(),
// entry_host_cache: std.BoundedArray(128)
const PendingCache = bun.HiveArray(GetAddrInfoRequest.PendingCacheKey, 32);
const SrvPendingCache = bun.HiveArray(ResolveSrvInfoRequest.PendingCacheKey, 32);
fn getKey(this: *DNSResolver, index: u8, comptime cache_name: []const u8) GetAddrInfoRequest.PendingCacheKey {
var cache: *PendingCache = &@field(this, cache_name);
@@ -978,6 +1169,60 @@ pub const DNSResolver = struct {
return entry;
}
fn getSrvKey(this: *DNSResolver, index: u8, comptime cache_name: []const u8) ResolveSrvInfoRequest.PendingCacheKey {
var cache: *SrvPendingCache = &@field(this, cache_name);
std.debug.assert(!cache.available.isSet(index));
const entry = cache.buffer[index];
cache.buffer[index] = undefined;
var available = cache.available;
available.set(index);
cache.available = available;
return entry;
}
pub fn drainPendingSrvCares(this: *DNSResolver, index: u8, err: ?c_ares.Error, timeout: i32, result: ?*c_ares.struct_ares_srv_reply) void {
const key = this.getSrvKey(index, "pending_srv_cache_cares");
var addr = result orelse {
var pending: ?*SrvLookup = key.lookup.head.next;
key.lookup.head.processResolveSrv(err, timeout, null);
bun.default_allocator.destroy(key.lookup);
while (pending) |value| {
pending = value.next;
value.processResolveSrv(err, timeout, null);
}
return;
};
var pending: ?*SrvLookup = key.lookup.head.next;
var prev_global = key.lookup.head.globalThis;
var array = addr.toJSArray(this.vm.allocator, prev_global);
defer addr.deinit();
array.ensureStillAlive();
key.lookup.head.onComplete(array);
bun.default_allocator.destroy(key.lookup);
array.ensureStillAlive();
while (pending) |value| {
var new_global = value.globalThis;
if (prev_global != new_global) {
array = addr.toJSArray(this.vm.allocator, new_global);
prev_global = new_global;
}
pending = value.next;
{
array.ensureStillAlive();
value.onComplete(array);
array.ensureStillAlive();
}
}
}
pub fn drainPendingHostCares(this: *DNSResolver, index: u8, err: ?c_ares.Error, timeout: i32, result: ?*c_ares.AddrInfo) void {
const key = this.getKey(index, "pending_host_cache_cares");
@@ -1070,6 +1315,39 @@ pub const DNSResolver = struct {
new: *GetAddrInfoRequest.PendingCacheKey,
disabled: void,
};
pub const SrvCacheHit = union(enum) {
inflight: *ResolveSrvInfoRequest.PendingCacheKey,
new: *ResolveSrvInfoRequest.PendingCacheKey,
disabled: void,
};
pub fn getOrPutIntoSrvPendingCache(
this: *DNSResolver,
key: ResolveSrvInfoRequest.PendingCacheKey,
comptime field: std.meta.FieldEnum(DNSResolver),
) SrvCacheHit {
var cache: *SrvPendingCache = &@field(this, @tagName(field));
var inflight_iter = cache.available.iterator(
.{
.kind = .unset,
},
);
while (inflight_iter.next()) |index| {
var entry: *ResolveSrvInfoRequest.PendingCacheKey = &cache.buffer[index];
if (entry.hash == key.hash and entry.len == key.len) {
return .{ .inflight = entry };
}
}
if (cache.get()) |new| {
new.hash = key.hash;
new.len = key.len;
return .{ .new = new };
}
return .{ .disabled = {} };
}
pub fn getOrPutIntoPendingCache(
this: *DNSResolver,
@@ -1330,6 +1608,80 @@ pub const DNSResolver = struct {
};
}
pub fn resolveSrv(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue {
const arguments = callframe.arguments(2);
if (arguments.len < 1) {
globalThis.throwNotEnoughArguments("resolveSrv", 2, arguments.len);
return .zero;
}
const name_value = arguments.ptr[0];
if (name_value.isEmptyOrUndefinedOrNull() or !name_value.isString()) {
globalThis.throwInvalidArgumentType("resolveSrv", "hostname", "string");
return .zero;
}
const name_str = name_value.toStringOrNull(globalThis) orelse {
return .zero;
};
if (name_str.length() == 0) {
globalThis.throwInvalidArgumentType("resolveSrv", "hostname", "non-empty string");
return .zero;
}
const name = name_str.toSlice(globalThis, bun.default_allocator);
defer name.deinit();
var vm = globalThis.bunVM();
var resolver = vm.rareData().globalDNSResolver(vm);
return resolver.doResolveSrv(name.slice(), globalThis);
}
pub fn doResolveSrv(this: *DNSResolver, name: []const u8, globalThis: *JSC.JSGlobalObject) JSC.JSValue {
var channel: *c_ares.Channel = switch (this.getChannel()) {
.result => |res| res,
.err => |err| {
const system_error = JSC.SystemError{
.errno = -1,
.code = JSC.ZigString.init(err.code()),
.message = JSC.ZigString.init(err.label()),
};
globalThis.throwValue(system_error.toErrorInstance(globalThis));
return .zero;
},
};
const key = ResolveSrvInfoRequest.PendingCacheKey.init(name);
var cache = this.getOrPutIntoSrvPendingCache(key, .pending_srv_cache_cares);
if (cache == .inflight) {
var srv_lookup = SrvLookup.init(globalThis, globalThis.allocator()) catch unreachable;
cache.inflight.append(srv_lookup);
return srv_lookup.promise.value();
}
var request = ResolveSrvInfoRequest.init(
cache,
this,
name,
globalThis,
"pending_srv_cache_cares",
) catch unreachable;
const promise = request.tail.promise.value();
channel.resolveSrv(
name,
ResolveSrvInfoRequest,
request,
ResolveSrvInfoRequest.onCaresComplete,
);
return promise;
}
pub fn c_aresLookupWithNormalizedName(this: *DNSResolver, query: GetAddrInfo, globalThis: *JSC.JSGlobalObject) JSC.JSValue {
var channel: *c_ares.Channel = switch (this.getChannel()) {
.result => |res| res,
@@ -1386,6 +1738,12 @@ pub const DNSResolver = struct {
.name = "Bun__DNSResolver__lookup",
},
);
@export(
resolveSrv,
.{
.name = "Bun__DNSResolver__resolveSrv",
},
);
}
// pub fn lookupService(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue {
// const arguments = callframe.arguments(3);

View File

@@ -3225,19 +3225,22 @@ pub const PollRef = struct {
if (this.status != .active)
return;
this.status = .inactive;
log("unref", .{});
vm.uws_event_loop.?.num_polls -= 1;
vm.uws_event_loop.?.active -= 1;
vm.uws_event_loop.?.unref();
}
/// Prevent a poll from keeping the process alive on the next tick.
pub fn unrefOnNextTick(this: *PollRef, vm: *JSC.VirtualMachine) void {
if (this.status != .active)
return;
this.status = .inactive;
vm.uws_event_loop.?.nextTick(*uws.Loop, vm.uws_event_loop.?, uws.Loop.unref);
}
/// Allow a poll to keep the process alive.
pub fn ref(this: *PollRef, vm: *JSC.VirtualMachine) void {
if (this.status != .inactive)
return;
log("ref", .{});
this.status = .active;
vm.uws_event_loop.?.num_polls += 1;
vm.uws_event_loop.?.active += 1;
vm.uws_event_loop.?.ref();
}
};

View File

@@ -2169,6 +2169,7 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionPerformMicrotask, (JSGlobalObject * globalObj
}
extern "C" EncodedJSValue Bun__DNSResolver__lookup(JSGlobalObject*, JSC::CallFrame*);
extern "C" EncodedJSValue Bun__DNSResolver__resolveSrv(JSGlobalObject*, JSC::CallFrame*);
JSC_DEFINE_HOST_FUNCTION(jsFunctionPerformMicrotaskVariadic, (JSGlobalObject * globalObject, CallFrame* callframe))
{
@@ -3321,9 +3322,11 @@ void GlobalObject::installAPIGlobals(JSClassRef* globals, int count, JSC::VM& vm
JSC::JSObject* dnsObject = JSC::constructEmptyObject(this);
dnsObject->putDirectNativeFunction(vm, this, JSC::Identifier::fromString(vm, "lookup"_s), 2, Bun__DNSResolver__lookup, ImplementationVisibility::Public, NoIntrinsic,
JSC::PropertyAttribute::Function | JSC::PropertyAttribute::DontDelete | 0);
dnsObject->putDirectNativeFunction(vm, this, JSC::Identifier::fromString(vm, "resolveSrv"_s), 2, Bun__DNSResolver__resolveSrv, ImplementationVisibility::Public, NoIntrinsic,
JSC::PropertyAttribute::Function | JSC::PropertyAttribute::DontDelete | 0);
object->putDirect(vm, PropertyName(identifier), dnsObject, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | 0);
}
{
JSC::Identifier identifier = JSC::Identifier::fromString(vm, "plugin"_s);
JSFunction* pluginFunction = JSFunction::create(vm, this, 1, String("plugin"_s), jsFunctionBunPlugin, ImplementationVisibility::Public, NoIntrinsic);

View File

@@ -2860,7 +2860,7 @@ pub const JSValue = enum(JSValueReprInt) {
return switch (comptime Number) {
JSValue => number,
f32, f64 => jsNumberFromDouble(@as(f64, number)),
u8, i16, i32, c_int, i8, u16 => jsNumberFromInt32(@intCast(i32, number)),
c_ushort, u8, i16, i32, c_int, i8, u16 => jsNumberFromInt32(@intCast(i32, number)),
u32, u52, c_uint, i64 => jsNumberFromInt64(@intCast(i64, number)),
usize, u64 => jsNumberFromUint64(@intCast(u64, number)),
comptime_int => switch (number) {

View File

@@ -1,4 +1,4 @@
// only resolve4, resolve, lookup, and resolve6 are implemented.
// only resolve4, resolve, lookup, resolve6 and resolveSrv are implemented.
const { dns } = globalThis.Bun;
@@ -25,6 +25,22 @@ function lookup(domain, options, callback) {
);
}
function resolveSrv(hostname, callback) {
if (typeof callback != "function") {
throw new TypeError("callback must be a function");
}
dns.resolveSrv(hostname, callback).then(
(results) => {
callback(null, results);
},
(error) => {
callback(error);
},
);
}
function lookupService(address, port, callback) {
if (typeof callback != "function") {
throw new TypeError("callback must be a function");
@@ -138,7 +154,18 @@ var InternalResolver = class Resolver {
}
resolveSrv(hostname, callback) {
callback(null, []);
if (typeof callback != "function") {
throw new TypeError("callback must be a function");
}
dns.resolveSrv(hostname, callback).then(
(results) => {
callback(null, results);
},
(error) => {
callback(error);
},
);
}
resolveCaa(hostname, callback) {
@@ -209,6 +236,10 @@ export const promises = {
return dns.lookup(hostname, { family: 6 });
},
resolveSrv(hostname) {
return dns.resolveSrv(hostname);
},
Resolver: class Resolver {
constructor(options) {}
@@ -259,7 +290,7 @@ export const promises = {
}
resolveSrv(hostname) {
return Promise.resolve([]);
return dns.resolveSrv(hostname);
}
resolveCaa(hostname) {
@@ -286,7 +317,6 @@ for (const key of [
"resolveNs",
"resolvePtr",
"resolveSoa",
"resolveSrv",
"resolveTxt",
"reverse",
]) {

View File

@@ -11,6 +11,145 @@ pub const ares_socket_t = c_int;
pub const ares_sock_state_cb = ?*const fn (?*anyopaque, ares_socket_t, c_int, c_int) callconv(.C) void;
pub const struct_apattern = opaque {};
const fd_set = c.fd_set;
pub const NSClass = enum(c_int) {
/// Cookie.
ns_c_invalid = 0,
/// Internet.
ns_c_in = 1,
/// unallocated/unsupported.
ns_c_2 = 2,
/// MIT Chaos-net.
ns_c_chaos = 3,
/// MIT Hesiod.
ns_c_hs = 4,
/// Query class values which do not appear in resource records
/// for prereq. sections in update requests
ns_c_none = 254,
/// Wildcard match.
ns_c_any = 255,
ns_c_max = 65536,
};
pub const NSType = enum(c_int) {
/// Cookie.
ns_t_invalid = 0,
/// Host address.
ns_t_a = 1,
/// Authoritative server.
ns_t_ns = 2,
/// Mail destination.
ns_t_md = 3,
/// Mail forwarder.
ns_t_mf = 4,
/// Canonical name.
ns_t_cname = 5,
/// Start of authority zone.
ns_t_soa = 6,
/// Mailbox domain name.
ns_t_mb = 7,
/// Mail group member.
ns_t_mg = 8,
/// Mail rename name.
ns_t_mr = 9,
/// Null resource record.
ns_t_null = 10,
/// Well known service.
ns_t_wks = 11,
/// Domain name pointer.
ns_t_ptr = 12,
/// Host information.
ns_t_hinfo = 13,
/// Mailbox information.
ns_t_minfo = 14,
/// Mail routing information.
ns_t_mx = 15,
/// Text strings.
ns_t_txt = 16,
/// Responsible person.
ns_t_rp = 17,
/// AFS cell database.
ns_t_afsdb = 18,
/// X_25 calling address.
ns_t_x25 = 19,
/// ISDN calling address.
ns_t_isdn = 20,
/// Router.
ns_t_rt = 21,
/// NSAP address.
ns_t_nsap = 22,
/// Reverse NSAP lookup (deprecated).
ns_t_nsap_ptr = 23,
/// Security signature.
ns_t_sig = 24,
/// Security key.
ns_t_key = 25,
/// X.400 mail mapping.
ns_t_px = 26,
/// Geographical position (withdrawn).
ns_t_gpos = 27,
/// Ip6 Address.
ns_t_aaaa = 28,
/// Location Information.
ns_t_loc = 29,
/// Next domain (security).
ns_t_nxt = 30,
/// Endpoint identifier.
ns_t_eid = 31,
/// Nimrod Locator.
ns_t_nimloc = 32,
/// Server Selection.
ns_t_srv = 33,
/// ATM Address
ns_t_atma = 34,
/// Naming Authority PoinTeR
ns_t_naptr = 35,
/// Key Exchange
ns_t_kx = 36,
/// Certification record
ns_t_cert = 37,
/// IPv6 address (deprecates AAAA)
ns_t_a6 = 38,
/// Non-terminal DNAME (for IPv6)
ns_t_dname = 39,
/// Kitchen sink (experimentatl)
ns_t_sink = 40,
/// EDNS0 option (meta-RR)
ns_t_opt = 41,
/// Address prefix list (RFC3123)
ns_t_apl = 42,
/// Delegation Signer (RFC4034)
ns_t_ds = 43,
/// SSH Key Fingerprint (RFC4255)
ns_t_sshfp = 44,
/// Resource Record Signature (RFC4034)
ns_t_rrsig = 46,
/// Next Secure (RFC4034)
ns_t_nsec = 47,
/// DNS Public Key (RFC4034)
ns_t_dnskey = 48,
/// Transaction key
ns_t_tkey = 249,
/// Transaction signature.
ns_t_tsig = 250,
/// Incremental zone transfer.
ns_t_ixfr = 251,
/// Transfer zone of authority.
ns_t_axfr = 252,
/// Transfer mailbox records.
ns_t_mailb = 253,
/// Transfer mail agent records.
ns_t_maila = 254,
/// Wildcard match.
ns_t_any = 255,
/// Uniform Resource Identifier (RFC7553)
ns_t_uri = 256,
/// Certification Authority Authorization.
ns_t_caa = 257,
ns_t_max = 65536,
_,
};
pub const Options = extern struct {
flags: c_int = 0,
timeout: c_int = 0,
@@ -152,6 +291,7 @@ pub const AddrInfo_hints = extern struct {
return this.ai_flags == 0 and this.ai_family == 0 and this.ai_socktype == 0 and this.ai_protocol == 0;
}
};
pub const Channel = opaque {
pub fn init(comptime Container: type, this: *Container) ?Error {
var channel: *Channel = undefined;
@@ -273,6 +413,21 @@ pub const Channel = opaque {
ares_getaddrinfo(this, host_ptr, port_ptr, hints_, AddrInfo.callbackWrapper(Type, callback), ctx);
}
pub fn resolveSrv(this: *Channel, name: []const u8, comptime Type: type, ctx: *Type, comptime callback: struct_ares_srv_reply.Callback(Type)) void {
var name_buf: [1024]u8 = undefined;
const name_ptr: ?[*:0]const u8 = brk: {
if (name.len == 0 or name.len >= 1023) {
break :brk null;
}
const len = @min(name_buf.len, name_buf.len - 1);
@memcpy(&name_buf, name.ptr, len);
name_buf[len] = 0;
break :brk name_buf[0..len :0];
};
ares_query(this, name_ptr, NSClass.ns_c_in, NSType.ns_t_srv, struct_ares_srv_reply.callbackWrapper(Type, callback), ctx);
}
pub inline fn process(this: *Channel, fd: i32, readable: bool, writable: bool) void {
ares_process_fd(
this,
@@ -334,7 +489,7 @@ pub const ares_socket_functions = extern struct {
};
pub extern fn ares_set_socket_functions(channel: *Channel, funcs: ?*const ares_socket_functions, user_data: ?*anyopaque) void;
pub extern fn ares_send(channel: *Channel, qbuf: [*c]const u8, qlen: c_int, callback: ares_callback, arg: ?*anyopaque) void;
pub extern fn ares_query(channel: *Channel, name: [*c]const u8, dnsclass: c_int, @"type": c_int, callback: ares_callback, arg: ?*anyopaque) void;
pub extern fn ares_query(channel: *Channel, name: [*c]const u8, dnsclass: NSClass, @"type": NSType, callback: ares_callback, arg: ?*anyopaque) void;
pub extern fn ares_search(channel: *Channel, name: [*c]const u8, dnsclass: c_int, @"type": c_int, callback: ares_callback, arg: ?*anyopaque) void;
pub extern fn ares_gethostbyname(channel: *Channel, name: [*c]const u8, family: c_int, callback: ares_host_callback, arg: ?*anyopaque) void;
pub extern fn ares_gethostbyname_file(channel: *Channel, name: [*c]const u8, family: c_int, host: [*:null]?*struct_hostent) c_int;
@@ -372,11 +527,90 @@ pub const struct_ares_caa_reply = extern struct {
length: usize,
};
pub const struct_ares_srv_reply = extern struct {
next: [*c]struct_ares_srv_reply,
next: ?*struct_ares_srv_reply,
host: [*c]u8,
priority: c_ushort,
weight: c_ushort,
port: c_ushort,
const JSC = bun.JSC;
pub fn toJSArray(this: *struct_ares_srv_reply, parent_allocator: std.mem.Allocator, globalThis: *JSC.JSGlobalObject) JSC.JSValue {
var stack = std.heap.stackFallback(2048, parent_allocator);
var arena = std.heap.ArenaAllocator.init(stack.get());
defer arena.deinit();
var allocator = arena.allocator();
var count: usize = 0;
var srv: ?*struct_ares_srv_reply = this;
while (srv != null) : (srv = srv.?.next) {
count += 1;
}
const array = JSC.JSValue.createEmptyArray(globalThis, count);
srv = this;
var i: u32 = 0;
while (srv != null) {
var node = srv.?;
array.putIndex(globalThis, i, node.toJS(globalThis, allocator));
srv = node.next;
i += 1;
}
return array;
}
pub fn toJS(this: *struct_ares_srv_reply, globalThis: *JSC.JSGlobalObject, _: std.mem.Allocator) JSC.JSValue {
var obj = JSC.JSValue.createEmptyObject(globalThis, 4);
// {
// priority: 10,
// weight: 5,
// port: 21223,
// name: 'service.example.com'
// }
obj.put(globalThis, JSC.ZigString.static("priority"), JSC.JSValue.jsNumber(this.weight));
obj.put(globalThis, JSC.ZigString.static("weight"), JSC.JSValue.jsNumber(this.weight));
obj.put(globalThis, JSC.ZigString.static("port"), JSC.JSValue.jsNumber(this.port));
const len = bun.len(this.host);
var host = this.host[0..len];
obj.put(globalThis, JSC.ZigString.static("name"), JSC.ZigString.fromUTF8(host).toValueGC(globalThis));
return obj;
}
pub fn Callback(comptime Type: type) type {
return fn (*Type, status: ?Error, timeouts: i32, results: ?*struct_ares_srv_reply) void;
}
pub fn callbackWrapper(
comptime Type: type,
comptime function: Callback(Type),
) ares_callback {
return &struct {
pub fn handleSrv(ctx: ?*anyopaque, status: c_int, timeouts: c_int, buffer: [*c]u8, buffer_length: c_int) callconv(.C) void {
var this = bun.cast(*Type, ctx.?);
if (status != ARES_SUCCESS) {
function(this, Error.get(status), timeouts, null);
return;
}
var srv_start: [*c]struct_ares_srv_reply = undefined;
var result = ares_parse_srv_reply(buffer, buffer_length, &srv_start);
if (result != ARES_SUCCESS) {
function(this, Error.get(result), timeouts, null);
return;
}
function(this, null, timeouts, srv_start);
}
}.handleSrv;
}
pub fn deinit(this: *struct_ares_srv_reply) void {
ares_free_data(this);
}
};
pub const struct_ares_mx_reply = extern struct {
next: [*c]struct_ares_mx_reply,

View File

@@ -429,6 +429,7 @@ pub const Loop = extern struct {
ready_polls: [1024]EventType align(16),
const EventType = if (Environment.isLinux) std.os.linux.epoll_event else if (Environment.isMac) std.os.system.kevent64_s;
const log = bun.Output.scoped(.Loop, false);
pub const InternalLoopData = extern struct {
pub const us_internal_async = opaque {};
@@ -452,6 +453,17 @@ pub const Loop = extern struct {
}
};
pub fn ref(this: *Loop) void {
log("ref", .{});
this.num_polls += 1;
this.active += 1;
}
pub fn unref(this: *Loop) void {
log("unref", .{});
this.num_polls -= 1;
this.active -= 1;
}
pub fn get() ?*Loop {
return uws_get_loop();
}

View File

@@ -31,3 +31,24 @@ test("dns.lookup (example.com)", (done) => {
done(err);
});
});
//TODO: use a bun.sh SRV for testing
test("dns.resolveSrv (_test._tcp.test.socketify.dev)", (done) => {
dns.resolveSrv("_test._tcp.test.socketify.dev", (err, results) => {
expect(err).toBeNull();
expect(results instanceof Array).toBe(true);
expect(results[0].name).toBe("_dc-srv.130c90ab9de1._test._tcp.test.socketify.dev");
expect(results[0].priority).toBe(50);
expect(results[0].weight).toBe(50);
expect(results[0].port).toBe(80);
done(err);
});
});
test("dns.resolveSrv (_test._tcp.invalid.localhost)", (done) => {
dns.resolveSrv("_test._tcp.invalid.localhost", (err, results) => {
expect(err).toBeTruthy();
expect(results).toBeUndefined(true);
done();
});
});

View File

@@ -51,4 +51,4 @@ describe("dns.lookup", () => {
},
);
}
});
});