fix(dlob-worker): allow forcing IPv6 egress
This commit is contained in:
@@ -1,4 +1,6 @@
|
|||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
|
import * as http from 'node:http';
|
||||||
|
import * as https from 'node:https';
|
||||||
import process from 'node:process';
|
import process from 'node:process';
|
||||||
import { setTimeout as sleep } from 'node:timers/promises';
|
import { setTimeout as sleep } from 'node:timers/promises';
|
||||||
|
|
||||||
@@ -29,6 +31,15 @@ function envList(name, fallbackCsv) {
|
|||||||
.filter(Boolean);
|
.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() {
|
function resolveConfig() {
|
||||||
const tokensPath =
|
const tokensPath =
|
||||||
process.env.HASURA_TOKENS_FILE ||
|
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')
|
const dlobHttpBase = String(process.env.DLOB_HTTP_URL || process.env.DLOB_HTTP_BASE || 'https://dlob.drift.trade')
|
||||||
.trim()
|
.trim()
|
||||||
.replace(/\/$/, '');
|
.replace(/\/$/, '');
|
||||||
|
const dlobForceIpv6 = envBool('DLOB_FORCE_IPV6', false);
|
||||||
|
|
||||||
const markets = envList('DLOB_MARKETS', 'PUMP-PERP,SOL-PERP,1MBONK-PERP,BTC-PERP,ETH-PERP');
|
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);
|
const depth = clampInt(process.env.DLOB_DEPTH, 1, 50, 10);
|
||||||
@@ -69,6 +81,7 @@ function resolveConfig() {
|
|||||||
hasuraAdminSecret,
|
hasuraAdminSecret,
|
||||||
hasuraAuthToken,
|
hasuraAuthToken,
|
||||||
dlobHttpBase,
|
dlobHttpBase,
|
||||||
|
dlobForceIpv6,
|
||||||
markets,
|
markets,
|
||||||
depth,
|
depth,
|
||||||
pollMs,
|
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) {
|
async function graphqlRequest(cfg, query, variables) {
|
||||||
const headers = { 'content-type': 'application/json' };
|
const headers = { 'content-type': 'application/json' };
|
||||||
if (cfg.hasuraAuthToken) {
|
if (cfg.hasuraAuthToken) {
|
||||||
@@ -234,7 +288,15 @@ async function fetchL2(cfg, marketName) {
|
|||||||
const u = new URL(`${cfg.dlobHttpBase}/l2`);
|
const u = new URL(`${cfg.dlobHttpBase}/l2`);
|
||||||
u.searchParams.set('marketName', marketName);
|
u.searchParams.set('marketName', marketName);
|
||||||
u.searchParams.set('depth', String(cfg.depth));
|
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();
|
const text = await res.text();
|
||||||
if (!res.ok) throw new Error(`DLOB HTTP ${res.status}: ${text}`);
|
if (!res.ok) throw new Error(`DLOB HTTP ${res.status}: ${text}`);
|
||||||
return JSON.parse(text);
|
return JSON.parse(text);
|
||||||
@@ -310,6 +372,7 @@ async function main() {
|
|||||||
hasuraUrl: cfg.hasuraUrl,
|
hasuraUrl: cfg.hasuraUrl,
|
||||||
hasuraAuth: cfg.hasuraAuthToken ? 'bearer' : cfg.hasuraAdminSecret ? 'admin-secret' : 'none',
|
hasuraAuth: cfg.hasuraAuthToken ? 'bearer' : cfg.hasuraAdminSecret ? 'admin-secret' : 'none',
|
||||||
dlobHttpBase: cfg.dlobHttpBase,
|
dlobHttpBase: cfg.dlobHttpBase,
|
||||||
|
dlobForceIpv6: cfg.dlobForceIpv6,
|
||||||
markets: cfg.markets,
|
markets: cfg.markets,
|
||||||
depth: cfg.depth,
|
depth: cfg.depth,
|
||||||
pollMs: cfg.pollMs,
|
pollMs: cfg.pollMs,
|
||||||
|
|||||||
Reference in New Issue
Block a user