From 5992a54ac38a50882e93edc46e929273b552f337 Mon Sep 17 00:00:00 2001 From: u1 Date: Sat, 10 Jan 2026 10:08:32 +0000 Subject: [PATCH] fix(dlob-worker): allow forcing IPv6 egress --- kustomize/base/dlob-worker/worker.mjs | 65 ++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/kustomize/base/dlob-worker/worker.mjs b/kustomize/base/dlob-worker/worker.mjs index ccc87e9..dd047b4 100644 --- a/kustomize/base/dlob-worker/worker.mjs +++ b/kustomize/base/dlob-worker/worker.mjs @@ -1,4 +1,6 @@ import fs from 'node:fs'; +import * as http from 'node:http'; +import * as https from 'node:https'; import process from 'node:process'; import { setTimeout as sleep } from 'node:timers/promises'; @@ -29,6 +31,15 @@ function envList(name, fallbackCsv) { .filter(Boolean); } +function envBool(name, fallback = false) { + const raw = process.env[name]; + if (raw == null) return fallback; + const v = String(raw).trim().toLowerCase(); + if (['1', 'true', 'yes', 'y', 'on'].includes(v)) return true; + if (['0', 'false', 'no', 'n', 'off'].includes(v)) return false; + return fallback; +} + function resolveConfig() { const tokensPath = process.env.HASURA_TOKENS_FILE || @@ -52,6 +63,7 @@ function resolveConfig() { const dlobHttpBase = String(process.env.DLOB_HTTP_URL || process.env.DLOB_HTTP_BASE || 'https://dlob.drift.trade') .trim() .replace(/\/$/, ''); + const dlobForceIpv6 = envBool('DLOB_FORCE_IPV6', false); const markets = envList('DLOB_MARKETS', 'PUMP-PERP,SOL-PERP,1MBONK-PERP,BTC-PERP,ETH-PERP'); const depth = clampInt(process.env.DLOB_DEPTH, 1, 50, 10); @@ -69,6 +81,7 @@ function resolveConfig() { hasuraAdminSecret, hasuraAuthToken, dlobHttpBase, + dlobForceIpv6, markets, depth, pollMs, @@ -77,6 +90,47 @@ function resolveConfig() { }; } +async function requestText(url, { timeoutMs, family } = {}) { + const u = new URL(url); + const client = u.protocol === 'https:' ? https : http; + + const port = u.port ? Number.parseInt(u.port, 10) : u.protocol === 'https:' ? 443 : 80; + if (!Number.isFinite(port)) throw new Error(`Invalid port for url: ${url}`); + + return await new Promise((resolve, reject) => { + const req = client.request( + { + protocol: u.protocol, + hostname: u.hostname, + port, + path: `${u.pathname}${u.search}`, + method: 'GET', + family, + servername: u.hostname, + headers: { + accept: 'application/json', + }, + }, + (res) => { + let data = ''; + res.setEncoding('utf8'); + res.on('data', (chunk) => { + data += chunk; + }); + res.on('end', () => { + resolve({ status: res.statusCode ?? 0, text: data }); + }); + } + ); + + req.on('error', reject); + req.setTimeout(timeoutMs ?? 5_000, () => { + req.destroy(new Error(`Timeout after ${timeoutMs ?? 5_000}ms`)); + }); + req.end(); + }); +} + async function graphqlRequest(cfg, query, variables) { const headers = { 'content-type': 'application/json' }; if (cfg.hasuraAuthToken) { @@ -234,7 +288,15 @@ async function fetchL2(cfg, marketName) { const u = new URL(`${cfg.dlobHttpBase}/l2`); u.searchParams.set('marketName', marketName); u.searchParams.set('depth', String(cfg.depth)); - const res = await fetch(u.toString(), { signal: AbortSignal.timeout(5_000) }); + + const url = u.toString(); + if (cfg.dlobForceIpv6) { + const { status, text } = await requestText(url, { timeoutMs: 5_000, family: 6 }); + if (status < 200 || status >= 300) throw new Error(`DLOB HTTP ${status}: ${text}`); + return JSON.parse(text); + } + + const res = await fetch(url, { signal: AbortSignal.timeout(5_000) }); const text = await res.text(); if (!res.ok) throw new Error(`DLOB HTTP ${res.status}: ${text}`); return JSON.parse(text); @@ -310,6 +372,7 @@ async function main() { hasuraUrl: cfg.hasuraUrl, hasuraAuth: cfg.hasuraAuthToken ? 'bearer' : cfg.hasuraAdminSecret ? 'admin-secret' : 'none', dlobHttpBase: cfg.dlobHttpBase, + dlobForceIpv6: cfg.dlobForceIpv6, markets: cfg.markets, depth: cfg.depth, pollMs: cfg.pollMs,