Skip to content

Commit 41034ce

Browse files
committed
feat: 规范化 subscription-userinfo
1 parent 6efb19c commit 41034ce

5 files changed

Lines changed: 52 additions & 10 deletions

File tree

backend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "sub-store",
3-
"version": "2.16.57",
3+
"version": "2.16.58",
44
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and Shadowrocket.",
55
"main": "src/main.js",
66
"scripts": {

backend/src/core/proxy-utils/processors/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
validCheck,
2121
flowTransfer,
2222
getRmainingDays,
23+
normalizeFlowHeader,
2324
} from '@/utils/flow';
2425

2526
function isObject(item) {
@@ -1083,6 +1084,7 @@ function createDynamicFunction(name, script, $arguments, $options) {
10831084
flowTransfer,
10841085
validCheck,
10851086
getRmainingDays,
1087+
normalizeFlowHeader,
10861088
};
10871089
if ($.env.isLoon) {
10881090
return new Function(

backend/src/restful/download.js

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
import { ProxyUtils } from '@/core/proxy-utils';
66
import { COLLECTIONS_KEY, SUBS_KEY } from '@/constants';
77
import { findByName } from '@/utils/database';
8-
import { getFlowHeaders } from '@/utils/flow';
8+
import { getFlowHeaders, normalizeFlowHeader } from '@/utils/flow';
99
import $ from '@/core/app';
1010
import { failed } from '@/restful/response';
1111
import { InternalServerError, ResourceNotFoundError } from '@/restful/errors';
@@ -259,7 +259,10 @@ async function downloadSubscription(req, res) {
259259
$arguments.flowUrl,
260260
);
261261
if (flowInfo) {
262-
res.set('subscription-userinfo', flowInfo);
262+
res.set(
263+
'subscription-userinfo',
264+
normalizeFlowHeader(flowInfo),
265+
);
263266
}
264267
}
265268
} catch (err) {
@@ -293,10 +296,9 @@ async function downloadSubscription(req, res) {
293296
}
294297
res.set(
295298
'subscription-userinfo',
296-
[subUserInfo, flowInfo]
297-
.filter((i) => i)
298-
.join('; ')
299-
.replace(/\s*;\s*;\s*/g, ';'),
299+
normalizeFlowHeader(
300+
[subUserInfo, flowInfo].filter((i) => i).join(';'),
301+
),
300302
);
301303
}
302304

@@ -556,7 +558,7 @@ async function downloadCollection(req, res) {
556558
if (subUserInfo) {
557559
res.set(
558560
'subscription-userinfo',
559-
subUserInfo.replace(/\s*;\s*;\s*/g, ';'),
561+
normalizeFlowHeader(subUserInfo),
560562
);
561563
}
562564
if (platform === 'JSON') {

backend/src/restful/file.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { deleteByName, findByName, updateByName } from '@/utils/database';
2-
import { getFlowHeaders } from '@/utils/flow';
2+
import { getFlowHeaders, normalizeFlowHeader } from '@/utils/flow';
33
import { FILES_KEY } from '@/constants';
44
import { failed, success } from '@/restful/response';
55
import $ from '@/core/app';
@@ -148,7 +148,7 @@ async function getFile(req, res) {
148148
if (flowInfo) {
149149
res.set(
150150
'subscription-userinfo',
151-
flowInfo.replace(/\s*;\s*;\s*/g, ';'),
151+
normalizeFlowHeader(flowInfo),
152152
);
153153
}
154154
}

backend/src/utils/flow.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,3 +313,41 @@ export function getRmainingDays(opt = {}) {
313313
$.error(`getRmainingDays failed: ${e.message ?? e}`);
314314
}
315315
}
316+
317+
export function normalizeFlowHeader(flowHeaders) {
318+
try {
319+
// 使用 Map 保持顺序并处理重复键
320+
const kvMap = new Map();
321+
322+
flowHeaders
323+
.split(';')
324+
.map((p) => p.trim())
325+
.filter(Boolean)
326+
.forEach((pair) => {
327+
const eqIndex = pair.indexOf('=');
328+
if (eqIndex === -1) return;
329+
330+
const key = pair.slice(0, eqIndex).trim();
331+
const encodedValue = pair.slice(eqIndex + 1).trim();
332+
333+
// 只保留第一个出现的 key
334+
if (!kvMap.has(key)) {
335+
try {
336+
// 解码 URI 组件并保留原始值作为 fallback
337+
const decodedValue = decodeURIComponent(encodedValue);
338+
kvMap.set(key, decodedValue);
339+
} catch (e) {
340+
kvMap.set(key, encodedValue);
341+
}
342+
}
343+
});
344+
345+
// 拼接标准化字符串
346+
return Array.from(kvMap.entries())
347+
.map(([k, v]) => `${k}=${encodeURIComponent(v)}`) // 重新编码保持兼容性
348+
.join('; ');
349+
} catch (e) {
350+
$.error(`normalizeFlowHeader failed: ${e.message ?? e}`);
351+
return flowHeaders;
352+
}
353+
}

0 commit comments

Comments
 (0)