xjs-node 1.0.6 → 2.0.0
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/LICENSE +201 -201
- package/dist/index.js +1 -23
- package/dist/types.d.ts +260 -0
- package/package.json +11 -8
- package/dist/func/u-file.d.ts +0 -45
- package/dist/func/u-file.js +0 -161
- package/dist/func/u.d.ts +0 -3
- package/dist/func/u.js +0 -42
- package/dist/index.d.ts +0 -5
- package/dist/prcs/http/http-resolver-context.d.ts +0 -60
- package/dist/prcs/http/http-resolver-context.js +0 -326
- package/dist/prcs/http/http-resolver.d.ts +0 -51
- package/dist/prcs/http/http-resolver.js +0 -53
- package/dist/prcs/http/i-http-client.d.ts +0 -90
- package/dist/prcs/http/i-http-client.js +0 -2
|
@@ -1,326 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
-
if (mod && mod.__esModule) return mod;
|
|
20
|
-
var result = {};
|
|
21
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
-
__setModuleDefault(result, mod);
|
|
23
|
-
return result;
|
|
24
|
-
};
|
|
25
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.HttpResolverContext = exports.s_clientMode = void 0;
|
|
27
|
-
const tls = __importStar(require("tls"));
|
|
28
|
-
const zlib = __importStar(require("zlib"));
|
|
29
|
-
const fs = __importStar(require("fs"));
|
|
30
|
-
const path = __importStar(require("path"));
|
|
31
|
-
const url_1 = require("url");
|
|
32
|
-
const https_1 = require("https");
|
|
33
|
-
const http_1 = require("http");
|
|
34
|
-
const async_hooks_1 = require("async_hooks");
|
|
35
|
-
const u_file_1 = require("../../func/u-file");
|
|
36
|
-
const u_1 = require("../../func/u");
|
|
37
|
-
const xjs_common_1 = require("xjs-common");
|
|
38
|
-
const stream_1 = require("stream");
|
|
39
|
-
exports.s_clientMode = {
|
|
40
|
-
nodejs: { id: 0, cipherOrder: null },
|
|
41
|
-
chrome: { id: 1, cipherOrder: [2, 0, 1] },
|
|
42
|
-
firefox: { id: 2, cipherOrder: [2, 1, 0] }
|
|
43
|
-
};
|
|
44
|
-
const s_errCode = 1200;
|
|
45
|
-
const s_redirectLimit = 5;
|
|
46
|
-
const s_mode2headers = new Map([
|
|
47
|
-
[exports.s_clientMode.firefox, (cmv) => ({
|
|
48
|
-
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
|
|
49
|
-
"accept-encoding": "gzip, deflate, br",
|
|
50
|
-
"accept-language": "en-US,en;q=0.5",
|
|
51
|
-
"sec-fetch-dest": "document",
|
|
52
|
-
"sec-fetch-mode": "navigate",
|
|
53
|
-
"sec-fetch-site": "none",
|
|
54
|
-
"sec-fetch-user": "?1",
|
|
55
|
-
"upgrade-insecure-requests": "1",
|
|
56
|
-
"user-agent": `Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:${cmv}.0) Gecko/20100101 Firefox/${cmv}.0`
|
|
57
|
-
})],
|
|
58
|
-
[exports.s_clientMode.chrome, (cmv) => {
|
|
59
|
-
const uad = cmv < 130
|
|
60
|
-
? `"Not/A)Brand";v="8", "Chromium";v="${cmv}", "Google Chrome";v="${cmv}"`
|
|
61
|
-
: `"Chromium";v="${cmv}", "Not:A-Brand";v="24", "Google Chrome";v="${cmv}"`;
|
|
62
|
-
const ch = {
|
|
63
|
-
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
|
|
64
|
-
"accept-encoding": "gzip, deflate, br, zstd",
|
|
65
|
-
"accept-language": "en-US,en;q=0.9",
|
|
66
|
-
"sec-ch-ua": uad,
|
|
67
|
-
"sec-ch-ua-mobile": "?0",
|
|
68
|
-
"sec-ch-ua-platform": '"Windows"',
|
|
69
|
-
"sec-fetch-dest": "document",
|
|
70
|
-
"sec-fetch-mode": "navigate",
|
|
71
|
-
"sec-fetch-site": "none",
|
|
72
|
-
"sec-fetch-user": "?1",
|
|
73
|
-
"upgrade-insecure-requests": "1",
|
|
74
|
-
"user-agent": `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${cmv}.0.0.0 Safari/537.36`
|
|
75
|
-
};
|
|
76
|
-
if (cmv >= 124)
|
|
77
|
-
ch["priority"] = "u=0, i";
|
|
78
|
-
return ch;
|
|
79
|
-
}]
|
|
80
|
-
]);
|
|
81
|
-
class HttpResolverContext {
|
|
82
|
-
cmv;
|
|
83
|
-
_l;
|
|
84
|
-
_als = new async_hooks_1.AsyncLocalStorage();
|
|
85
|
-
_mode;
|
|
86
|
-
_ciphers;
|
|
87
|
-
_proxyConfig;
|
|
88
|
-
_chHeaders;
|
|
89
|
-
_cookies;
|
|
90
|
-
get clientMode() { return Object.keys(exports.s_clientMode).find((_, i) => i === this._mode.id); }
|
|
91
|
-
constructor(cmv, op, _l = console) {
|
|
92
|
-
this.cmv = cmv;
|
|
93
|
-
this._l = _l;
|
|
94
|
-
this._mode = op?.mode ?? xjs_common_1.UArray.randomPick([exports.s_clientMode.chrome, exports.s_clientMode.firefox]);
|
|
95
|
-
this._proxyConfig = op?.proxy;
|
|
96
|
-
if (this._mode.id > 0) {
|
|
97
|
-
this._ciphers = this.createCiphers(this._mode);
|
|
98
|
-
this._chHeaders = s_mode2headers.get(this._mode)(this.cmv);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
async get(url, op) {
|
|
102
|
-
const u = new url_1.URL(url);
|
|
103
|
-
const proxyAgent = this._proxyConfig && await this.createProxyAgent(u);
|
|
104
|
-
const rc = { redirectCount: op?.outerRedirectCount ?? 0, proxyAgent };
|
|
105
|
-
Object.assign(rc, op);
|
|
106
|
-
return await this._als.run(rc, this.getIn, u).finally(() => proxyAgent?.destroy());
|
|
107
|
-
}
|
|
108
|
-
async post(url, payload, op) {
|
|
109
|
-
const u = new url_1.URL(url);
|
|
110
|
-
const proxyAgent = this._proxyConfig && await this.createProxyAgent(u);
|
|
111
|
-
const rc = { redirectCount: 0, proxyAgent };
|
|
112
|
-
Object.assign(rc, op);
|
|
113
|
-
return await this._als.run(rc, this.postIn, u, payload).finally(() => proxyAgent?.destroy());
|
|
114
|
-
}
|
|
115
|
-
createProxyAgent(u) {
|
|
116
|
-
const conf = this._proxyConfig;
|
|
117
|
-
return new Promise((resolve, reject) => {
|
|
118
|
-
const headers = {};
|
|
119
|
-
if (conf.auth)
|
|
120
|
-
headers['proxy-authorization'] = `Basic ${Buffer.from(conf.auth.name + ':' + conf.auth.pass).toString('base64')}`;
|
|
121
|
-
const req = (0, http_1.request)({
|
|
122
|
-
host: conf.server,
|
|
123
|
-
port: conf.port,
|
|
124
|
-
method: xjs_common_1.HttpMethod.Connect,
|
|
125
|
-
path: `${u.hostname}:443`,
|
|
126
|
-
headers
|
|
127
|
-
}).on('connect', (res, socket) => {
|
|
128
|
-
if (res.statusCode === 200)
|
|
129
|
-
resolve(new https_1.Agent({ socket, keepAlive: true }));
|
|
130
|
-
else
|
|
131
|
-
reject(new xjs_common_1.XjsErr(s_errCode, "Could not connect to proxy."));
|
|
132
|
-
});
|
|
133
|
-
req.on('error', reject);
|
|
134
|
-
req.on('timeout', () => {
|
|
135
|
-
req.destroy();
|
|
136
|
-
reject(new xjs_common_1.XjsErr(s_errCode, "The http request timeout, maybe server did not respond."));
|
|
137
|
-
});
|
|
138
|
-
req.end();
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
getIn = async (u) => {
|
|
142
|
-
const params = {};
|
|
143
|
-
const rc = this._als.getStore();
|
|
144
|
-
params.method = xjs_common_1.HttpMethod.Get;
|
|
145
|
-
params.headers = xjs_common_1.UHttp.normalizeHeaders(rc.headers);
|
|
146
|
-
return await this.reqHttps(u, params);
|
|
147
|
-
};
|
|
148
|
-
postIn = async (u, payload) => {
|
|
149
|
-
const params = {};
|
|
150
|
-
const rc = this._als.getStore();
|
|
151
|
-
params.method = xjs_common_1.HttpMethod.Post;
|
|
152
|
-
params.headers = xjs_common_1.UHttp.normalizeHeaders(rc.headers);
|
|
153
|
-
let p = payload;
|
|
154
|
-
if (p instanceof stream_1.Stream) {
|
|
155
|
-
params.headers["content-type"] ??= "application/octet-stream";
|
|
156
|
-
}
|
|
157
|
-
else if (xjs_common_1.UType.isObject(payload)) {
|
|
158
|
-
p = JSON.stringify(payload);
|
|
159
|
-
params.headers["content-length"] = p.length;
|
|
160
|
-
params.headers["content-type"] = "application/json";
|
|
161
|
-
}
|
|
162
|
-
return await this.reqHttps(u, params, p);
|
|
163
|
-
};
|
|
164
|
-
reqHttps(u, params, payload) {
|
|
165
|
-
const rc = this._als.getStore();
|
|
166
|
-
params.timeout = rc.timeout ?? 0;
|
|
167
|
-
params.protocol = u.protocol;
|
|
168
|
-
params.host = u.host;
|
|
169
|
-
params.path = (rc.ignoreQuery || !u.search) ? u.pathname : `${u.pathname}${u.search}`;
|
|
170
|
-
params.agent = rc.proxyAgent;
|
|
171
|
-
if (this._mode.id > 0) {
|
|
172
|
-
params.ciphers = this._ciphers;
|
|
173
|
-
params.headers = params.headers ? Object.assign(params.headers, this._chHeaders) : this._chHeaders;
|
|
174
|
-
}
|
|
175
|
-
if (this._cookies)
|
|
176
|
-
this.setCookies(params.headers);
|
|
177
|
-
return new Promise((resolve, reject) => {
|
|
178
|
-
const req = (0, https_1.request)(params, (res) => this.processResponse(resolve, reject, rc, params.host, res));
|
|
179
|
-
req.on('error', reject);
|
|
180
|
-
req.on('timeout', () => {
|
|
181
|
-
req.destroy();
|
|
182
|
-
reject(new xjs_common_1.XjsErr(s_errCode, "The http request timeout, maybe server did not respond."));
|
|
183
|
-
});
|
|
184
|
-
if (payload)
|
|
185
|
-
if (payload instanceof stream_1.Stream)
|
|
186
|
-
payload.pipe(req);
|
|
187
|
-
else
|
|
188
|
-
req.write(payload);
|
|
189
|
-
req.end();
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
processResponse(resolve, reject, rc, host, res) {
|
|
193
|
-
if (res.headers["set-cookie"])
|
|
194
|
-
this.storeCookies(res.headers["set-cookie"]);
|
|
195
|
-
const sc = xjs_common_1.UHttp.statusCategoryOf(res.statusCode);
|
|
196
|
-
if (sc === 3) {
|
|
197
|
-
this.handleRedirect(res, host).then(resolve).catch(reject).finally(() => res.destroy());
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
|
-
if (res.headers["content-disposition"]?.trim().startsWith("attachment")) {
|
|
201
|
-
try {
|
|
202
|
-
const dest = this.resolveDownloadPath(rc.downloadPath, res.headers["content-disposition"]);
|
|
203
|
-
const stream = fs.createWriteStream(dest);
|
|
204
|
-
res.pipe(stream);
|
|
205
|
-
stream.on("finish", () => stream.close());
|
|
206
|
-
stream.on("close", () => resolve({ headers: res.headers }));
|
|
207
|
-
stream.on("error", reject);
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
catch (e) {
|
|
211
|
-
if (e instanceof xjs_common_1.XjsErr)
|
|
212
|
-
reject(e);
|
|
213
|
-
else {
|
|
214
|
-
this.error(e);
|
|
215
|
-
reject(new xjs_common_1.XjsErr(s_errCode, "Failed to download a file."));
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
const bfs = [];
|
|
220
|
-
const contentEncofing = res.headers["content-encoding"]?.toLocaleLowerCase();
|
|
221
|
-
res.on('data', chunk => bfs.push(chunk));
|
|
222
|
-
res.on('end', () => {
|
|
223
|
-
try {
|
|
224
|
-
let retBuf = Buffer.concat(bfs);
|
|
225
|
-
if (contentEncofing == "gzip")
|
|
226
|
-
retBuf = zlib.gunzipSync(retBuf);
|
|
227
|
-
else if (contentEncofing == "br")
|
|
228
|
-
retBuf = zlib.brotliDecompressSync(retBuf);
|
|
229
|
-
const data = rc.responseType === "buffer" ? retBuf : retBuf.toString("utf8");
|
|
230
|
-
if (sc !== 2) {
|
|
231
|
-
if (xjs_common_1.UType.isString(data) && data.trim())
|
|
232
|
-
this.warn(data);
|
|
233
|
-
reject(new xjs_common_1.XjsErr(s_errCode, `Https received a error status ${res.statusCode}`));
|
|
234
|
-
}
|
|
235
|
-
else
|
|
236
|
-
resolve({ payload: data, headers: res.headers });
|
|
237
|
-
}
|
|
238
|
-
catch (e) {
|
|
239
|
-
reject(e);
|
|
240
|
-
}
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
resolveDownloadPath(opPath, disposition) {
|
|
244
|
-
const appendFname = (d) => {
|
|
245
|
-
const fname = disposition.split(";")
|
|
246
|
-
.find(f => f.trim().startsWith("filename"))
|
|
247
|
-
?.replace(/^\s+filename\s+=/, "").trim()
|
|
248
|
-
?? u_file_1.UFile.reserveFilePath(d, `xjs-download_${xjs_common_1.UString.simpleTime()}`);
|
|
249
|
-
return (0, u_1.joinPath)(d, fname);
|
|
250
|
-
};
|
|
251
|
-
if (opPath) {
|
|
252
|
-
const st = u_file_1.UFile.status(opPath);
|
|
253
|
-
if (!st || st.isFile()) {
|
|
254
|
-
if (!u_file_1.UFile.exists(path.dirname(opPath)))
|
|
255
|
-
throw new xjs_common_1.XjsErr(s_errCode, "Directory of the download file was not found.");
|
|
256
|
-
return opPath;
|
|
257
|
-
}
|
|
258
|
-
if (st.isDirectory()) {
|
|
259
|
-
if (!u_file_1.UFile.exists(opPath))
|
|
260
|
-
throw new xjs_common_1.XjsErr(s_errCode, "Directory of the download path was not found.");
|
|
261
|
-
return appendFname(opPath);
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
return appendFname("./");
|
|
265
|
-
}
|
|
266
|
-
async handleRedirect(res, host) {
|
|
267
|
-
const rc = this._als.getStore();
|
|
268
|
-
if (!res.headers.location)
|
|
269
|
-
throw new xjs_common_1.XjsErr(s_errCode, "Received http redirection, but no location header found.");
|
|
270
|
-
if (rc.redirectCount++ > s_redirectLimit)
|
|
271
|
-
throw new xjs_common_1.XjsErr(s_errCode, "Count of http redirection exceeds limit.");
|
|
272
|
-
this.log(`Redirect to ${res.headers.location}. (count is ${rc.redirectCount})`);
|
|
273
|
-
const dest = res.headers.location.startsWith("http") ? res.headers.location : `https://${host}${res.headers.location}`;
|
|
274
|
-
if (rc.outerRedirectCount)
|
|
275
|
-
throw new xjs_common_1.XjsErr(-1, dest);
|
|
276
|
-
const u = new url_1.URL(dest);
|
|
277
|
-
// consider for proxy which implements reverse proxy.
|
|
278
|
-
if (rc.proxyAgent) {
|
|
279
|
-
rc.proxyAgent?.destroy();
|
|
280
|
-
rc.proxyAgent = await this.createProxyAgent(u);
|
|
281
|
-
}
|
|
282
|
-
return await this.getIn(u);
|
|
283
|
-
}
|
|
284
|
-
createCiphers(mode) {
|
|
285
|
-
const defaultCiphers = tls.DEFAULT_CIPHERS.split(':');
|
|
286
|
-
return [
|
|
287
|
-
defaultCiphers[mode.cipherOrder[0]],
|
|
288
|
-
defaultCiphers[mode.cipherOrder[1]],
|
|
289
|
-
defaultCiphers[mode.cipherOrder[2]],
|
|
290
|
-
...xjs_common_1.UArray.shuffle(defaultCiphers.slice(3))
|
|
291
|
-
].join(':');
|
|
292
|
-
}
|
|
293
|
-
setCookies(headers) {
|
|
294
|
-
const exp = this._cookies["expires"];
|
|
295
|
-
if (exp && new Date(exp).getTime() <= Date.now()) {
|
|
296
|
-
this._cookies = null;
|
|
297
|
-
this.log("Cookies was cleared due to an expiraion.");
|
|
298
|
-
}
|
|
299
|
-
else
|
|
300
|
-
headers.cookie = Object.keys(this._cookies)
|
|
301
|
-
.filter(ckk => !["expires", "max-age"].includes(ckk))
|
|
302
|
-
.map(ckk => `${ckk}=${this._cookies[ckk]};`).join(" ");
|
|
303
|
-
}
|
|
304
|
-
storeCookies(cookies) {
|
|
305
|
-
this._cookies ??= {};
|
|
306
|
-
cookies.filter(c => c).flatMap(c => c.split(";"))
|
|
307
|
-
.map(c => {
|
|
308
|
-
const idx = c.indexOf("=");
|
|
309
|
-
return idx !== -1 && [c.substring(0, idx).toLowerCase().trim(), c.substring(idx + 1)];
|
|
310
|
-
})
|
|
311
|
-
.filter(cp => cp && cp[0] && !["secure", "path", "domain", "samesite"].includes(cp[0]))
|
|
312
|
-
.forEach(cp => this._cookies[cp[0]] = cp[1]);
|
|
313
|
-
this.log("Store cookies from set-cookie headers.");
|
|
314
|
-
this.log(JSON.stringify(this._cookies));
|
|
315
|
-
}
|
|
316
|
-
log(msg) {
|
|
317
|
-
this._l.log(`[http-resolver] ${msg}`);
|
|
318
|
-
}
|
|
319
|
-
warn(msg) {
|
|
320
|
-
this._l.warn(`[http-resolver] ${msg}`);
|
|
321
|
-
}
|
|
322
|
-
error(msg) {
|
|
323
|
-
this._l.error(`[http-resolver] ${msg}`);
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
exports.HttpResolverContext = HttpResolverContext;
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
2
|
-
import { Loggable } from "xjs-common";
|
|
3
|
-
import { HttpResolverContext } from "./http-resolver-context";
|
|
4
|
-
import { ClientOption, HttpResponse, IHttpClient, RequestOption } from "./i-http-client";
|
|
5
|
-
export interface ClientMode {
|
|
6
|
-
id: number;
|
|
7
|
-
cipherOrder: number[];
|
|
8
|
-
}
|
|
9
|
-
export interface ProxyConfig {
|
|
10
|
-
server: string;
|
|
11
|
-
port: number;
|
|
12
|
-
auth?: {
|
|
13
|
-
name: string;
|
|
14
|
-
pass: string;
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
export declare class HttpResolver implements IHttpClient {
|
|
18
|
-
private _baseCmv;
|
|
19
|
-
private _l;
|
|
20
|
-
/**
|
|
21
|
-
* @param _baseCmv chrome major version refered when construct a user agent, and the version will be randomized between `n` to `n-4`.
|
|
22
|
-
* @param _l custom logger. default is `console`.
|
|
23
|
-
*/
|
|
24
|
-
constructor(_baseCmv?: number, _l?: Loggable);
|
|
25
|
-
/**
|
|
26
|
-
* create a http client as new context that keeps some states. (browser type, cookies, ciphers order, etc...)
|
|
27
|
-
* @param op.mode {@link s_clientMode} that is imitated. default is random between chrome or firefox.
|
|
28
|
-
* @param op.proxy proxy configuration.
|
|
29
|
-
* @returns a http client as new context.
|
|
30
|
-
*/
|
|
31
|
-
newContext(op?: ClientOption): HttpResolverContext;
|
|
32
|
-
get(url: string, op?: RequestOption & ClientOption & {
|
|
33
|
-
redirectAsNewRequest?: boolean;
|
|
34
|
-
responseType: "string";
|
|
35
|
-
}): Promise<HttpResponse<string>>;
|
|
36
|
-
get(url: string, op?: RequestOption & ClientOption & {
|
|
37
|
-
redirectAsNewRequest?: boolean;
|
|
38
|
-
responseType: "buffer";
|
|
39
|
-
}): Promise<HttpResponse<Buffer>>;
|
|
40
|
-
get(url: string, op?: RequestOption & ClientOption & {
|
|
41
|
-
redirectAsNewRequest?: boolean;
|
|
42
|
-
}): Promise<HttpResponse<string>>;
|
|
43
|
-
post(url: string, payload: any, op?: RequestOption & ClientOption & {
|
|
44
|
-
responseType: "string";
|
|
45
|
-
}): Promise<HttpResponse<string>>;
|
|
46
|
-
post(url: string, payload: any, op?: RequestOption & ClientOption & {
|
|
47
|
-
responseType: "buffer";
|
|
48
|
-
}): Promise<HttpResponse<Buffer>>;
|
|
49
|
-
post(url: string, payload: any, op?: RequestOption & ClientOption): Promise<HttpResponse<string>>;
|
|
50
|
-
private fixCmv;
|
|
51
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.HttpResolver = void 0;
|
|
4
|
-
const xjs_common_1 = require("xjs-common");
|
|
5
|
-
const http_resolver_context_1 = require("./http-resolver-context");
|
|
6
|
-
const s_cmvRange = 5;
|
|
7
|
-
const s_defaultCmv = 138;
|
|
8
|
-
class HttpResolver {
|
|
9
|
-
_baseCmv;
|
|
10
|
-
_l;
|
|
11
|
-
/**
|
|
12
|
-
* @param _baseCmv chrome major version refered when construct a user agent, and the version will be randomized between `n` to `n-4`.
|
|
13
|
-
* @param _l custom logger. default is `console`.
|
|
14
|
-
*/
|
|
15
|
-
constructor(_baseCmv = s_defaultCmv, _l = console) {
|
|
16
|
-
this._baseCmv = _baseCmv;
|
|
17
|
-
this._l = _l;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* create a http client as new context that keeps some states. (browser type, cookies, ciphers order, etc...)
|
|
21
|
-
* @param op.mode {@link s_clientMode} that is imitated. default is random between chrome or firefox.
|
|
22
|
-
* @param op.proxy proxy configuration.
|
|
23
|
-
* @returns a http client as new context.
|
|
24
|
-
*/
|
|
25
|
-
newContext(op) {
|
|
26
|
-
return new http_resolver_context_1.HttpResolverContext(this.fixCmv(), op, this._l);
|
|
27
|
-
}
|
|
28
|
-
async get(url, op) {
|
|
29
|
-
let redirectCount = op?.redirectAsNewRequest && -1;
|
|
30
|
-
const bindOp = () => {
|
|
31
|
-
const option = Object.assign({}, op);
|
|
32
|
-
if (redirectCount)
|
|
33
|
-
Object.assign(option, { outerRedirectCount: ++redirectCount });
|
|
34
|
-
return option;
|
|
35
|
-
};
|
|
36
|
-
try {
|
|
37
|
-
return await this.newContext(op).get(url, bindOp());
|
|
38
|
-
}
|
|
39
|
-
catch (e) {
|
|
40
|
-
if (!(e instanceof xjs_common_1.XjsErr) || e.code !== -1)
|
|
41
|
-
throw e;
|
|
42
|
-
else
|
|
43
|
-
return await this.newContext(op).get(e.message, bindOp());
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
async post(url, payload, op) {
|
|
47
|
-
return await this.newContext(op).post(url, payload, op);
|
|
48
|
-
}
|
|
49
|
-
fixCmv() {
|
|
50
|
-
return this._baseCmv - Math.floor(Math.random() * s_cmvRange);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
exports.HttpResolver = HttpResolver;
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
2
|
-
/// <reference types="node" />
|
|
3
|
-
import { IncomingHttpHeaders, OutgoingHttpHeaders } from "http";
|
|
4
|
-
import { ClientMode, ProxyConfig } from "./http-resolver";
|
|
5
|
-
export interface ClientOption {
|
|
6
|
-
/**
|
|
7
|
-
* {@link s_clientMode} that is imitated. default is random between chrome or firefox.
|
|
8
|
-
*/
|
|
9
|
-
mode?: ClientMode;
|
|
10
|
-
/**
|
|
11
|
-
* proxy configuration.
|
|
12
|
-
*/
|
|
13
|
-
proxy?: ProxyConfig;
|
|
14
|
-
}
|
|
15
|
-
export interface RequestOption {
|
|
16
|
-
headers?: OutgoingHttpHeaders;
|
|
17
|
-
/**
|
|
18
|
-
* if true, query part in the `url` is ignored.
|
|
19
|
-
*/
|
|
20
|
-
ignoreQuery?: boolean;
|
|
21
|
-
/**
|
|
22
|
-
* destination directory or file path for download. this is only used when `Content-Disposition` header exists.
|
|
23
|
-
* default is current directory of the process with `filename` of the disposition.
|
|
24
|
-
*/
|
|
25
|
-
downloadPath?: string;
|
|
26
|
-
/**
|
|
27
|
-
* timeout milliseconds to wait for socket inactivity. default is infinity.
|
|
28
|
-
*/
|
|
29
|
-
timeout?: number;
|
|
30
|
-
/**
|
|
31
|
-
* type of response payload. default is string (utf-8).
|
|
32
|
-
*/
|
|
33
|
-
responseType?: "string" | "buffer";
|
|
34
|
-
}
|
|
35
|
-
export interface HttpResponse<T = string | Buffer> {
|
|
36
|
-
/**
|
|
37
|
-
* http headers in the response.
|
|
38
|
-
*/
|
|
39
|
-
headers?: IncomingHttpHeaders;
|
|
40
|
-
/**
|
|
41
|
-
* response payload which has a type depends on {@link RequestOption.responseType}.
|
|
42
|
-
*/
|
|
43
|
-
payload?: T;
|
|
44
|
-
}
|
|
45
|
-
export interface IHttpClient {
|
|
46
|
-
/**
|
|
47
|
-
* request GET to the url with new context.
|
|
48
|
-
* @param url target url. (currently https only)
|
|
49
|
-
* @param op.headers http headers.
|
|
50
|
-
* @param op.mode {@link s_clientMode} that is imitated. default is random between chrome or firefox.
|
|
51
|
-
* @param op.proxy proxy configuration.
|
|
52
|
-
* @param op.ignoreQuery {@link RequestOption.ignoreQuery}
|
|
53
|
-
* @param op.downloadPath {@link RequestOption.downloadPath}
|
|
54
|
-
* @param op.timeout {@link RequestOption.timeout}
|
|
55
|
-
* @param op.responseType {@link RequestOption.responseType}
|
|
56
|
-
* @param op.redirectAsNewRequest handle redirect as new request. this may be efficient when using proxy which is implemented reverse proxy.
|
|
57
|
-
* @returns http response. {@link HttpResponse}
|
|
58
|
-
*/
|
|
59
|
-
get(url: string, op?: RequestOption & ClientOption & {
|
|
60
|
-
redirectAsNewRequest?: boolean;
|
|
61
|
-
responseType: "string";
|
|
62
|
-
}): Promise<HttpResponse<string>>;
|
|
63
|
-
get(url: string, op?: RequestOption & ClientOption & {
|
|
64
|
-
redirectAsNewRequest?: boolean;
|
|
65
|
-
responseType: "buffer";
|
|
66
|
-
}): Promise<HttpResponse<Buffer>>;
|
|
67
|
-
get(url: string, op?: RequestOption & ClientOption & {
|
|
68
|
-
redirectAsNewRequest?: boolean;
|
|
69
|
-
}): Promise<HttpResponse<string>>;
|
|
70
|
-
/**
|
|
71
|
-
* request POST to the url with new context.
|
|
72
|
-
* @param url target url. (currently https only)
|
|
73
|
-
* @param payload request payload. if this is a Stream, pipe will be used, otherwise if an object, this is treated as json.
|
|
74
|
-
* @param op.headers http headers.
|
|
75
|
-
* @param op.mode {@link s_clientMode} that is imitated. default is random between chrome or firefox.
|
|
76
|
-
* @param op.proxy proxy configuration.
|
|
77
|
-
* @param op.ignoreQuery {@link RequestOption.ignoreQuery}
|
|
78
|
-
* @param op.downloadPath {@link RequestOption.downloadPath}
|
|
79
|
-
* @param op.timeout {@link RequestOption.timeout}
|
|
80
|
-
* @param op.responseType {@link RequestOption.responseType}
|
|
81
|
-
* @returns http response. {@link HttpResponse}
|
|
82
|
-
*/
|
|
83
|
-
post(url: string, payload: any, op?: RequestOption & ClientOption & {
|
|
84
|
-
responseType: "string";
|
|
85
|
-
}): Promise<HttpResponse<string>>;
|
|
86
|
-
post(url: string, payload: any, op?: RequestOption & ClientOption & {
|
|
87
|
-
responseType: "buffer";
|
|
88
|
-
}): Promise<HttpResponse<Buffer>>;
|
|
89
|
-
post(url: string, payload: any, op?: RequestOption & ClientOption): Promise<HttpResponse<string>>;
|
|
90
|
-
}
|