dsh-rss 0.6.1 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -0
- package/README.en.md +2 -0
- package/README.md +2 -0
- package/lib/proxy-fetch.js +57 -8
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# 更新记录
|
|
2
2
|
|
|
3
|
+
## 0.6.2 (2026-10-15)
|
|
4
|
+
|
|
5
|
+
- 加固直连、HTTP CONNECT 与 TLS 握手的套接字错误隔离,抓取结束会销毁本次请求仍持有的连接,避免错误在请求结束后逃逸。
|
|
6
|
+
- 更新 Undici 至 8.12.1;新增独立进程的直连失败、代理 TLS 失败和超时取消回归检查。
|
|
7
|
+
|
|
3
8
|
[返回简介](README.md) · [使用说明](docs/USAGE.md) · [验证记录](docs/VALIDATION.md)
|
|
4
9
|
|
|
5
10
|
[历史英文记录](docs/CHANGELOG.en.md)
|
package/README.en.md
CHANGED
|
@@ -8,6 +8,8 @@ Subscribe to and read RSS or Atom feeds, and manage updates from DSH.
|
|
|
8
8
|
|
|
9
9
|
[](https://www.npmjs.com/package/dsh-rss) [](https://www.npmjs.com/package/dsh-rss)
|
|
10
10
|
|
|
11
|
+
Feedback and contributions are welcome: report [issues](https://github.com/STARDUSTLC666/dsh-rss/issues) or submit [pull requests](https://github.com/STARDUSTLC666/dsh-rss/pulls).
|
|
12
|
+
|
|
11
13
|
## What it does
|
|
12
14
|
|
|
13
15
|
- Add feeds, read articles and fetch updates.
|
package/README.md
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
[](https://www.npmjs.com/package/dsh-rss) [](https://www.npmjs.com/package/dsh-rss)
|
|
10
10
|
|
|
11
|
+
欢迎使用,遇到问题或有改进建议,请提交 [issues](https://github.com/STARDUSTLC666/dsh-rss/issues) 和 [PR](https://github.com/STARDUSTLC666/dsh-rss/pulls)。
|
|
12
|
+
|
|
11
13
|
## 功能
|
|
12
14
|
|
|
13
15
|
- 添加订阅、查看文章和抓取更新。
|
package/lib/proxy-fetch.js
CHANGED
|
@@ -1,14 +1,39 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 插件级 HTTP 代理:给 RSS 抓取请求一个走指定代理的 fetch,不影响同进程其他插件。
|
|
3
3
|
*/
|
|
4
|
-
import { Agent, Client, ProxyAgent, fetch as undiciFetch } from 'undici';
|
|
4
|
+
import { Agent, Client, ProxyAgent, buildConnector, fetch as undiciFetch } from 'undici';
|
|
5
5
|
import { isIP } from 'node:net';
|
|
6
6
|
/** One request/redirect hop owns its dispatcher until its body has been read. */
|
|
7
7
|
export function createFeedTransport(url, proxyUrl, addresses) {
|
|
8
8
|
const hostname = url.hostname.replace(/^\[|\]$/g, '');
|
|
9
9
|
const servername = isIP(hostname) === 0 ? hostname : undefined;
|
|
10
|
+
const sockets = new Set();
|
|
11
|
+
const onLateError = () => { };
|
|
12
|
+
function watch(socket) {
|
|
13
|
+
// CONNECT detaches the HTTP client's listeners before handing over its socket.
|
|
14
|
+
// Reattach after handover as well as immediately after socket construction.
|
|
15
|
+
if (!socket.listeners('error').includes(onLateError))
|
|
16
|
+
socket.on('error', onLateError);
|
|
17
|
+
if (!sockets.has(socket)) {
|
|
18
|
+
sockets.add(socket);
|
|
19
|
+
socket.once('close', () => sockets.delete(socket));
|
|
20
|
+
}
|
|
21
|
+
return socket;
|
|
22
|
+
}
|
|
23
|
+
function guarded(connect) {
|
|
24
|
+
return (options, callback) => {
|
|
25
|
+
const socket = connect(options, (error, connected) => {
|
|
26
|
+
if (error !== null)
|
|
27
|
+
callback(error, null);
|
|
28
|
+
else
|
|
29
|
+
callback(null, watch(connected));
|
|
30
|
+
});
|
|
31
|
+
if (socket !== undefined)
|
|
32
|
+
watch(socket);
|
|
33
|
+
};
|
|
34
|
+
}
|
|
10
35
|
const dispatcher = proxyUrl === ''
|
|
11
|
-
? new Agent({ connect: addresses === undefined ? {} : {
|
|
36
|
+
? new Agent({ connect: guarded(buildConnector(addresses === undefined ? {} : {
|
|
12
37
|
lookup: (name, options, callback) => {
|
|
13
38
|
if (name !== hostname) {
|
|
14
39
|
callback(new Error('Unexpected RSS connection hostname'), '', 4);
|
|
@@ -24,23 +49,47 @@ export function createFeedTransport(url, proxyUrl, addresses) {
|
|
|
24
49
|
else
|
|
25
50
|
callback(null, candidates[0].address, candidates[0].family);
|
|
26
51
|
},
|
|
27
|
-
} })
|
|
52
|
+
})) })
|
|
28
53
|
: new ProxyAgent({ uri: proxyUrl, proxyTunnel: true, requestTls: { servername },
|
|
54
|
+
clientFactory: (origin, rawOptions) => {
|
|
55
|
+
const options = rawOptions;
|
|
56
|
+
const connect = typeof options.connect === 'function' ? options.connect : buildConnector(options.connect ?? {});
|
|
57
|
+
return new Client(origin, { ...options, connect: guarded(connect) });
|
|
58
|
+
},
|
|
29
59
|
factory: (origin, options) => {
|
|
30
60
|
const delegate = options.connect;
|
|
31
|
-
if (
|
|
32
|
-
|
|
61
|
+
if (typeof delegate !== 'function')
|
|
62
|
+
throw new Error('RSS proxy did not supply a tunnel connector');
|
|
63
|
+
const endpoint = guarded(buildConnector({ servername }));
|
|
33
64
|
return new Client(origin, { ...options, connect: (socketOptions, callback) => {
|
|
34
|
-
const address = addresses[0]
|
|
65
|
+
const address = addresses?.[0]?.address ?? hostname;
|
|
35
66
|
const port = socketOptions.port || (url.protocol === 'https:' ? '443' : '80');
|
|
36
67
|
const host = (isIP(address) === 6 ? '[' + address + ']' : address) + ':' + port;
|
|
37
|
-
|
|
68
|
+
// Ask ProxyAgent for a raw CONNECT tunnel, then own the TLS socket too.
|
|
69
|
+
// A callback around the final TLS socket alone misses handshake errors.
|
|
70
|
+
delegate({ ...socketOptions, protocol: 'http:', hostname: address, host, port, servername }, (error, tunnel) => {
|
|
71
|
+
if (error !== null) {
|
|
72
|
+
callback(error, null);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
watch(tunnel);
|
|
76
|
+
if (url.protocol !== 'https:') {
|
|
77
|
+
callback(null, tunnel);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
endpoint({ ...socketOptions, hostname: address, host, port, servername, httpSocket: tunnel }, callback);
|
|
81
|
+
});
|
|
38
82
|
} });
|
|
39
83
|
},
|
|
40
84
|
});
|
|
41
85
|
return {
|
|
42
86
|
fetch: async (input, init) => await undiciFetch(input, { ...init, redirect: 'manual', dispatcher }),
|
|
43
|
-
dispose: async () => {
|
|
87
|
+
dispose: async () => {
|
|
88
|
+
for (const socket of sockets)
|
|
89
|
+
socket.destroy();
|
|
90
|
+
await dispatcher.destroy();
|
|
91
|
+
sockets.clear();
|
|
92
|
+
},
|
|
44
93
|
};
|
|
45
94
|
}
|
|
46
95
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-rss",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2",
|
|
4
4
|
"description": "订阅和阅读 RSS / Atom,在 DSH 中查看与管理更新。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"@deepseek-ai/schemastery": "^3.18.3",
|
|
56
56
|
"fast-xml-parser": "^5.0.0",
|
|
57
57
|
"schemastery": "^3.18.0",
|
|
58
|
-
"undici": "^8.
|
|
58
|
+
"undici": "^8.12.1",
|
|
59
59
|
"yaml": "^2.9.0"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|