feat(plugins): enhance SMS sending functionality with raw HTTP handling
- Added support for using the `net` module in the `send-sms` plugin to establish raw HTTP connections. - Refactored the `ztePost` function to `zteHttpPost`, allowing for custom HTTP request formatting and improved error handling. - Introduced a new utility function, `httpBodyFromRaw`, to process raw HTTP responses effectively. - Updated the `script-sandbox.js` to allow the use of the `net` and `node:net` modules in the sandbox environment.
This commit is contained in:
@@ -22,7 +22,9 @@ const ALLOWED_MODULES = new Set([
|
||||
"basic-ftp",
|
||||
"jsonata",
|
||||
"mustache",
|
||||
"net",
|
||||
"node-html-parser",
|
||||
"node:net",
|
||||
"node:stream",
|
||||
"nodemailer",
|
||||
"rss-parser",
|
||||
|
||||
+67
-28
@@ -1,3 +1,5 @@
|
||||
import net from "node:net";
|
||||
|
||||
function passContext(ctx) {
|
||||
if (ctx?.context != null && typeof ctx.context === "object" && !Array.isArray(ctx.context)) {
|
||||
return { ...ctx.context };
|
||||
@@ -65,38 +67,75 @@ function parseResult(text) {
|
||||
}
|
||||
}
|
||||
|
||||
function leftoverFromError(err) {
|
||||
let current = err;
|
||||
for (let i = 0; i < 6 && current; i++) {
|
||||
if (typeof current.data === "string") return current.data;
|
||||
current = current.cause;
|
||||
function httpBodyFromRaw(raw) {
|
||||
const normalized = String(raw).replace(/\r\n/g, "\n");
|
||||
const split = normalized.indexOf("\n\n");
|
||||
return split === -1 ? normalized : normalized.slice(split + 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* ZTE Demo-Webs uses LF-only status lines (`HTTP/1.1 200 OK\n`).
|
||||
* Node 24 fetch/http throw HPE_CR_EXPECTED before any body is read.
|
||||
*/
|
||||
function zteHttpPost(url, fields) {
|
||||
const target = new URL(url);
|
||||
if (target.protocol !== "http:") {
|
||||
throw new Error(`send-sms: only http: is supported (got ${target.protocol})`);
|
||||
}
|
||||
return null;
|
||||
const body = new URLSearchParams(fields).toString();
|
||||
const request = [
|
||||
`POST ${target.pathname}${target.search} HTTP/1.1`,
|
||||
`Host: ${target.host}`,
|
||||
"accept: application/json, text/javascript, */*; q=0.01",
|
||||
"accept-language: en-US,en;q=0.9",
|
||||
"content-type: application/x-www-form-urlencoded; charset=UTF-8",
|
||||
"x-requested-with: XMLHttpRequest",
|
||||
`Referer: ${refererFromUrl(url)}`,
|
||||
`Content-Length: ${Buffer.byteLength(body)}`,
|
||||
"Connection: close",
|
||||
"",
|
||||
body,
|
||||
].join("\r\n");
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const socket = net.connect(
|
||||
{ host: target.hostname, port: Number(target.port) || 80 },
|
||||
() => {
|
||||
socket.write(request);
|
||||
},
|
||||
);
|
||||
const chunks = [];
|
||||
let settled = false;
|
||||
const finish = (raw) => {
|
||||
if (settled) return;
|
||||
settled = true;
|
||||
socket.removeAllListeners();
|
||||
socket.destroy();
|
||||
resolve(parseResult(httpBodyFromRaw(raw)));
|
||||
};
|
||||
socket.on("data", (chunk) => {
|
||||
chunks.push(chunk);
|
||||
const raw = Buffer.concat(chunks).toString("utf8");
|
||||
const parsed = parseResult(httpBodyFromRaw(raw));
|
||||
if (parsed.result !== undefined) finish(raw);
|
||||
});
|
||||
socket.on("end", () => finish(Buffer.concat(chunks).toString("utf8")));
|
||||
socket.on("error", (err) => {
|
||||
if (settled) return;
|
||||
settled = true;
|
||||
reject(err);
|
||||
});
|
||||
socket.setTimeout(15000, () => {
|
||||
if (settled) return;
|
||||
settled = true;
|
||||
socket.destroy();
|
||||
reject(new Error("ZTE HTTP timeout"));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function ztePost(url, fields) {
|
||||
const body = new URLSearchParams(fields).toString();
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
accept: "application/json, text/javascript, */*; q=0.01",
|
||||
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
|
||||
"x-requested-with": "XMLHttpRequest",
|
||||
Referer: refererFromUrl(url),
|
||||
},
|
||||
body,
|
||||
});
|
||||
return parseResult(await response.text());
|
||||
} catch (err) {
|
||||
const leftover = leftoverFromError(err);
|
||||
if (leftover != null) {
|
||||
const parsed = parseResult(leftover);
|
||||
log.info({ result: parsed.result ?? parsed }, "send-sms: recovered malformed response");
|
||||
return parsed;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
return zteHttpPost(url, fields);
|
||||
}
|
||||
|
||||
async function sendZteSms(ctx) {
|
||||
|
||||
Reference in New Issue
Block a user