Skip to content

Commit ea4a5f4

Browse files
committed
feat(http): add headers support to Clash HTTP/HTTPS proxies
- Add headers field output to getClashNodes for HTTP and HTTPS proxy types - Parse headers from Clash configuration input in ClashProvider - Update username/password to conditionally include in output when present - Add comprehensive test coverage for HTTP/HTTPS proxies with and without headers Also handles optional username/password fields from validators change. Fixes #321 (part 2/2)
1 parent 6b30f9a commit ea4a5f4

File tree

3 files changed

+124
-4
lines changed

3 files changed

+124
-4
lines changed

src/provider/ClashProvider.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,7 @@ export const parseClashConfig = (
462462
port: item.port,
463463
username: item.username /* istanbul ignore next */ || '',
464464
password: item.password /* istanbul ignore next */ || '',
465+
...(item.headers ? { headers: item.headers } : null),
465466
} as HttpNodeConfig
466467
}
467468

@@ -474,6 +475,7 @@ export const parseClashConfig = (
474475
password: item.password || '',
475476
tls13: tls13 ?? false,
476477
skipCertVerify: item['skip-cert-verify'] === true,
478+
...(item.headers ? { headers: item.headers } : null),
477479
} as HttpsNodeConfig
478480

479481
case 'snell':

src/utils/__tests__/clash.test.ts

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1204,3 +1204,111 @@ test('getClashNodes', async (t) => {
12041204
],
12051205
)
12061206
})
1207+
1208+
test('getClashNodes - HTTP/HTTPS with headers', async (t) => {
1209+
t.deepEqual(
1210+
clash.getClashNodes([
1211+
{
1212+
type: NodeTypeEnum.HTTP,
1213+
nodeName: 'HTTP Proxy',
1214+
hostname: '1.1.1.1',
1215+
port: 8080,
1216+
username: 'user',
1217+
password: 'pass',
1218+
headers: {
1219+
'X-Custom': 'value',
1220+
Host: 'example.com',
1221+
},
1222+
},
1223+
]),
1224+
[
1225+
{
1226+
type: 'http',
1227+
name: 'HTTP Proxy',
1228+
server: '1.1.1.1',
1229+
port: 8080,
1230+
username: 'user',
1231+
password: 'pass',
1232+
headers: {
1233+
'X-Custom': 'value',
1234+
Host: 'example.com',
1235+
},
1236+
},
1237+
],
1238+
)
1239+
1240+
t.deepEqual(
1241+
clash.getClashNodes([
1242+
{
1243+
type: NodeTypeEnum.HTTP,
1244+
nodeName: 'HTTP Proxy No Headers',
1245+
hostname: '1.1.1.1',
1246+
port: 8080,
1247+
username: 'user',
1248+
password: 'pass',
1249+
},
1250+
]),
1251+
[
1252+
{
1253+
type: 'http',
1254+
name: 'HTTP Proxy No Headers',
1255+
server: '1.1.1.1',
1256+
port: 8080,
1257+
username: 'user',
1258+
password: 'pass',
1259+
},
1260+
],
1261+
)
1262+
1263+
t.deepEqual(
1264+
clash.getClashNodes([
1265+
{
1266+
type: NodeTypeEnum.HTTPS,
1267+
nodeName: 'HTTPS Proxy',
1268+
hostname: '1.1.1.1',
1269+
port: 443,
1270+
username: 'user',
1271+
password: 'pass',
1272+
tls13: false,
1273+
skipCertVerify: false,
1274+
headers: {
1275+
'X-Custom': 'value',
1276+
},
1277+
},
1278+
]),
1279+
[
1280+
{
1281+
type: 'http',
1282+
name: 'HTTPS Proxy',
1283+
server: '1.1.1.1',
1284+
port: 443,
1285+
username: 'user',
1286+
password: 'pass',
1287+
tls: true,
1288+
'skip-cert-verify': false,
1289+
headers: {
1290+
'X-Custom': 'value',
1291+
},
1292+
},
1293+
],
1294+
)
1295+
1296+
t.deepEqual(
1297+
clash.getClashNodes([
1298+
{
1299+
type: NodeTypeEnum.HTTP,
1300+
nodeName: 'HTTP No Auth',
1301+
hostname: '1.1.1.1',
1302+
port: 8080,
1303+
},
1304+
]),
1305+
[
1306+
{
1307+
type: 'http',
1308+
name: 'HTTP No Auth',
1309+
server: '1.1.1.1',
1310+
port: 8080,
1311+
},
1312+
],
1313+
)
1314+
})

src/utils/clash.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -337,10 +337,15 @@ function nodeListMapper(nodeConfig: PossibleNodeConfigType) {
337337
name: nodeConfig.nodeName,
338338
server: nodeConfig.hostname,
339339
port: nodeConfig.port,
340-
username: nodeConfig.username /* istanbul ignore next */ || '',
341-
password: nodeConfig.password /* istanbul ignore next */ || '',
340+
...(typeof nodeConfig.username === 'string'
341+
? { username: nodeConfig.username }
342+
: null),
343+
...(typeof nodeConfig.password === 'string'
344+
? { password: nodeConfig.password }
345+
: null),
342346
tls: true,
343347
'skip-cert-verify': nodeConfig.skipCertVerify === true,
348+
...(nodeConfig.headers ? { headers: nodeConfig.headers } : null),
344349
} as const
345350

346351
case NodeTypeEnum.HTTP:
@@ -349,8 +354,13 @@ function nodeListMapper(nodeConfig: PossibleNodeConfigType) {
349354
name: nodeConfig.nodeName,
350355
server: nodeConfig.hostname,
351356
port: nodeConfig.port,
352-
username: nodeConfig.username /* istanbul ignore next */ || '',
353-
password: nodeConfig.password /* istanbul ignore next */ || '',
357+
...(typeof nodeConfig.username === 'string'
358+
? { username: nodeConfig.username }
359+
: null),
360+
...(typeof nodeConfig.password === 'string'
361+
? { password: nodeConfig.password }
362+
: null),
363+
...(nodeConfig.headers ? { headers: nodeConfig.headers } : null),
354364
} as const
355365

356366
case NodeTypeEnum.Trojan:

0 commit comments

Comments
 (0)