fix: deprecation warning with node 24

Massimo Melina committed Feb 14, 2026 at 19:03 UTC 35ddebc36f971667e221fe687c3dddfb7dbee00d
2 files changed +21 -7
src/outboundProxy.ts
+3 -3
@@ -1,13 +1,13 @@
1 import { configReady, defineConfig } from './config'
2 -import { parse } from 'node:url'
3 -import { httpStream, httpString } from './util-http'
2 +import { httpStream, httpString, parseHttpUrl } from './util-http'
3 import { reg } from './util-os'
4 import { IS_WINDOWS } from './const'
5 import { CFG, prefix } from './cross'
6
7 const outboundProxy = defineConfig(CFG.outbound_proxy, '', v => {
8 + if (!v) return
9 try {
10 - parse(v) // just validate
10 + parseHttpUrl(v) // just validate
11 httpStream.defaultProxy = v
12 if (!v || process.env.HFS_SKIP_PROXY_TEST) return
13 const test = 'https://google.com'
src/util-http.ts
+18 -4
@@ -1,12 +1,13 @@
1 // This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
3 -import { parse } from 'node:url'
3 +import { urlToHttpOptions } from 'node:url'
4 import https from 'node:https'
5 import http, { IncomingMessage } from 'node:http'
6 import { Readable } from 'node:stream'
7 import _ from 'lodash'
8 import { text as stream2string, buffer } from 'node:stream/consumers'
9 import * as tls from 'node:tls'
10 +import { enforceStarting } from './cross'
11 export { stream2string }
12
13 export async function httpString(url: string, options?: XRequestOptions): Promise<string> {
@@ -47,13 +48,13 @@ export function httpStream(url: string, { body, proxy, jar, noRedirect, httpThro
48 if (!(body instanceof Readable))
49 options.headers['content-length'] ??= Buffer.byteLength(body)
50 }
50 - const { auth, ...parsed } = parse(url)
51 + const { auth, ...parsed } = parseHttpUrl(url)
52 const hostJar = jar && (jar[parsed.hostname || ''] ||= {})
53 if (hostJar) {
54 options.headers.cookie = _.map(hostJar, (v,k) => `${k}=${v}; `).join('')
55 + (options.headers.cookie || '') // preserve parameter
56 }
56 - const proxyParsed = proxy ? parse(proxy) : null
57 + const proxyParsed = proxy ? parseHttpUrl(proxy) : null
58 Object.assign(options, _.pick(proxyParsed || parsed, ['hostname', 'port', 'path', 'protocol']))
59 if (auth) {
60 options.auth = auth
@@ -62,7 +63,7 @@ export function httpStream(url: string, { body, proxy, jar, noRedirect, httpThro
63 }
64 if (proxy) {
65 options.path = url // full url as path
65 - options.headers.host ??= parse(url).host || undefined // keep original host header
66 + options.headers.host ??= parsed.host || undefined // keep original host header
67 }
68 // this needs the prefix "proxy-"
69 const proxyAuth = proxyParsed?.auth ? { 'proxy-authorization': `Basic ${Buffer.from(proxyParsed.auth, 'utf8').toString('base64')}` } : undefined
@@ -136,3 +137,16 @@ export function httpStream(url: string, { body, proxy, jar, noRedirect, httpThro
137 abort() { controller.abort() }
138 })
139 }
140 +
141 +// works the same way as the now deprecated url.parse()
142 +export function parseHttpUrl(url: string) {
143 + const parsed = new URL(url)
144 + const options = urlToHttpOptions(parsed)
145 + const withoutHash = url.split('#', 1)[0]!
146 + const authority = /^[a-z][a-z\d+.-]*:\/\/[^/?#]*/i.exec(withoutHash)?.[0]
147 + return {
148 + ...options,
149 + host: parsed.host,
150 + path: !authority ? '/' : enforceStarting('/', withoutHash.slice(authority.length)), // unresolved paths are useful in our tests
151 + }
152 +}