xjs-common 10.1.1 → 10.1.3

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.
@@ -4,4 +4,6 @@ export declare namespace UHttp {
4
4
  function concatParamsWithEncoding(end: string, params: {
5
5
  [k: string]: string | string[];
6
6
  }): string;
7
+ /** normalize object keys to lower case. */
8
+ function normalizeHeaders(headers: Record<string, any>): Record<string, any>;
7
9
  }
@@ -22,4 +22,11 @@ var UHttp;
22
22
  return result.substring(0, result.length - 1);
23
23
  }
24
24
  UHttp.concatParamsWithEncoding = concatParamsWithEncoding;
25
+ /** normalize object keys to lower case. */
26
+ function normalizeHeaders(headers) {
27
+ if (!headers)
28
+ return {};
29
+ return Object.entries(headers).reduce((a, b) => { a[b[0].toLowerCase()] = b[1]; return a; }, {});
30
+ }
31
+ UHttp.normalizeHeaders = normalizeHeaders;
25
32
  })(UHttp = exports.UHttp || (exports.UHttp = {}));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xjs-common",
3
- "version": "10.1.1",
3
+ "version": "10.1.3",
4
4
  "description": "library modules for typescript that bundled general-purpose implementations.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,20 +0,0 @@
1
- import { Type } from "../../const/types";
2
- export declare const smbl_tm: unique symbol;
3
- export interface TypeDesc {
4
- t?: Type;
5
- req?: boolean;
6
- ary?: TypeDesc;
7
- rec?: boolean;
8
- }
9
- export interface TypeMap {
10
- [k: string]: TypeDesc;
11
- }
12
- /** decorators to be validated by {@link UType.validate} */
13
- export declare namespace DValidate {
14
- function string(target: Object, propKey: string): void;
15
- function number(target: Object, propKey: string): void;
16
- function boolean(target: Object, propKey: string): void;
17
- function required(target: Object, propKey: string): void;
18
- function array(elmDesc?: TypeDesc): (target: Object, propKey: string) => void;
19
- function recursive(target: Object, propKey: string): void;
20
- }
@@ -1,64 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DValidate = exports.smbl_tm = void 0;
4
- const types_1 = require("../../const/types");
5
- const xjs_err_1 = require("../../obj/xjs-err");
6
- const s_errCode = 30;
7
- exports.smbl_tm = Symbol("typeMap");
8
- /** decorators to be validated by {@link UType.validate} */
9
- var DValidate;
10
- (function (DValidate) {
11
- function string(target, propKey) {
12
- setTypeDesc(target, propKey, types_1.Type.string);
13
- }
14
- DValidate.string = string;
15
- function number(target, propKey) {
16
- setTypeDesc(target, propKey, types_1.Type.number);
17
- }
18
- DValidate.number = number;
19
- function boolean(target, propKey) {
20
- setTypeDesc(target, propKey, types_1.Type.boolean);
21
- }
22
- DValidate.boolean = boolean;
23
- function setTypeDesc(target, propKey, t) {
24
- setDesc(target, propKey, (td) => {
25
- if (td.t)
26
- throw new xjs_err_1.XjsErr(s_errCode, "decorator to express type is duplicate.");
27
- td.t = t;
28
- });
29
- }
30
- function required(target, propKey) {
31
- setDesc(target, propKey, (td) => td.req = true);
32
- }
33
- DValidate.required = required;
34
- function array(elmDesc = {}) {
35
- return (target, propKey) => setDesc(target, propKey, (td) => td.ary = elmDesc);
36
- }
37
- DValidate.array = array;
38
- function recursive(target, propKey) {
39
- setDesc(target, propKey, (td) => td.rec = true);
40
- }
41
- DValidate.recursive = recursive;
42
- function setDesc(target, propKey, setter) {
43
- const map = target[exports.smbl_tm] ?? {};
44
- map[propKey] ??= { t: null, req: false, rec: false, ary: null };
45
- const td = map[propKey];
46
- setter(td);
47
- let ex1 = null, ex2 = null;
48
- if (td.t && td.rec) {
49
- ex1 = "type";
50
- ex2 = "recursive flag";
51
- }
52
- if (td.t && td.ary) {
53
- ex1 = "type";
54
- ex2 = "array";
55
- }
56
- if (td.ary && td.rec) {
57
- ex1 = "array";
58
- ex2 = "recursive flag";
59
- }
60
- if (ex1 && ex2)
61
- throw new xjs_err_1.XjsErr(s_errCode, `decorator to express ${ex1} and ${ex2} are exclusive.`);
62
- Object.defineProperty(target, exports.smbl_tm, { value: map });
63
- }
64
- })(DValidate = exports.DValidate || (exports.DValidate = {}));
@@ -1,51 +0,0 @@
1
- /// <reference types="node" />
2
- import { MaybeArray } from "../const/types";
3
- interface FileStatus {
4
- isFile(): boolean;
5
- isDirectory(): boolean;
6
- isBlockDevice(): boolean;
7
- isCharacterDevice(): boolean;
8
- isSymbolicLink(): boolean;
9
- isFIFO(): boolean;
10
- isSocket(): boolean;
11
- }
12
- export declare namespace UFile {
13
- function mkdir(p: MaybeArray<string>): boolean;
14
- function write(p: MaybeArray<string>, c: string): void;
15
- /**
16
- * remove a file. no error if the file to be removed doesn't exist.
17
- */
18
- function rm(p: MaybeArray<string>): void;
19
- function exists(p: MaybeArray<string>): boolean;
20
- /**
21
- * return a file status. if the file of the status doesn't exist, this returns `null`.
22
- */
23
- function status(p: MaybeArray<string>): FileStatus;
24
- function read(p: MaybeArray<string>): Buffer;
25
- function read(p: MaybeArray<string>, encoding: BufferEncoding): string;
26
- function cp(from: MaybeArray<string>, to: MaybeArray<string>): void;
27
- function mv(from: MaybeArray<string>, to: MaybeArray<string>): void;
28
- function ls(p: MaybeArray<string>): string[];
29
- /**
30
- * check availability to export a file with specified directory and file name.
31
- * if it doesn't, retry to check after appending incremental number (e.g. `.1`) to the filename.
32
- * @param dir destination directory path.
33
- * @param fname file name wanna export to.
34
- * @returns exportable file path.
35
- */
36
- function reserveFilePath(dir: MaybeArray<string>, fname: string): string;
37
- /**
38
- * decompress zip file. this depends on os enviroment due to using the os command.
39
- * currently this supports only windows (installed `tar` or `unzip` in gnu compiler) and linux systems (installed `unzip`).
40
- * @param zipPath zip file to be unzipped.
41
- * @param destDir directory that the decompress files export to.
42
- */
43
- function unzip(zipPath: MaybeArray<string>, destDir?: MaybeArray<string>): void;
44
- /**
45
- * @deprecated since v9.1.4
46
- * @removed this will be removed in v10.
47
- * @replaced it should uses this without namespace `UFile`.
48
- */
49
- function joinPath(...p: MaybeArray<string>[]): string;
50
- }
51
- export {};
@@ -1,171 +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.UFile = void 0;
27
- const child_process_1 = require("child_process");
28
- const fs = __importStar(require("fs"));
29
- const path = __importStar(require("path"));
30
- const u_type_1 = require("./u-type");
31
- const xjs_err_1 = require("../obj/xjs-err");
32
- const s_errCode = 40;
33
- var UFile;
34
- (function (UFile) {
35
- function mkdir(p) {
36
- const dirPath = joinPath(p);
37
- const e = fs.existsSync(dirPath);
38
- if (!e)
39
- fs.mkdirSync(dirPath, { recursive: true });
40
- else if (!fs.statSync(dirPath).isDirectory())
41
- throw new xjs_err_1.XjsErr(s_errCode, "Already exists a file (not directory) on the path.");
42
- return !e;
43
- }
44
- UFile.mkdir = mkdir;
45
- function write(p, c) {
46
- fs.writeFileSync(joinPath(p), c);
47
- }
48
- UFile.write = write;
49
- /**
50
- * remove a file. no error if the file to be removed doesn't exist.
51
- */
52
- function rm(p) {
53
- const pt = joinPath(p);
54
- if (fs.existsSync(pt))
55
- fs.rmSync(pt, { recursive: true });
56
- }
57
- UFile.rm = rm;
58
- function exists(p) {
59
- return !!p && fs.existsSync(joinPath(p));
60
- }
61
- UFile.exists = exists;
62
- /**
63
- * return a file status. if the file of the status doesn't exist, this returns `null`.
64
- */
65
- function status(p) {
66
- const pt = joinPath(p);
67
- return fs.existsSync(pt) ? fs.statSync(pt) : null;
68
- }
69
- UFile.status = status;
70
- function read(p, encoding) {
71
- const f = joinPath(p);
72
- if (!fs.existsSync(f))
73
- throw new xjs_err_1.XjsErr(s_errCode, `No file found => ${f}`);
74
- return fs.readFileSync(f, encoding);
75
- }
76
- UFile.read = read;
77
- function cp(from, to) {
78
- const f = joinPath(from), t = joinPath(to);
79
- if (!fs.existsSync(f))
80
- throw new xjs_err_1.XjsErr(s_errCode, `No file found => ${f}`);
81
- fs.copyFileSync(f, t);
82
- }
83
- UFile.cp = cp;
84
- function mv(from, to) {
85
- const f = joinPath(from), t = joinPath(to);
86
- if (!fs.existsSync(f))
87
- throw new xjs_err_1.XjsErr(s_errCode, `No file found => ${f}`);
88
- fs.renameSync(f, t);
89
- }
90
- UFile.mv = mv;
91
- function ls(p) {
92
- const pt = joinPath(p);
93
- if (!pt || !fs.statSync(pt).isDirectory())
94
- throw new xjs_err_1.XjsErr(s_errCode, "Specified path for ls is not directory.");
95
- return fs.readdirSync(pt);
96
- }
97
- UFile.ls = ls;
98
- /**
99
- * check availability to export a file with specified directory and file name.
100
- * if it doesn't, retry to check after appending incremental number (e.g. `.1`) to the filename.
101
- * @param dir destination directory path.
102
- * @param fname file name wanna export to.
103
- * @returns exportable file path.
104
- */
105
- function reserveFilePath(dir, fname) {
106
- const pt = joinPath(dir);
107
- if (!pt || !fs.statSync(pt).isDirectory())
108
- throw new xjs_err_1.XjsErr(s_errCode, "Specified directory path is not directory.");
109
- if (!fname || fname.match(/[\\/:*?"<>|]/))
110
- throw new xjs_err_1.XjsErr(s_errCode, "Specified filename is invalid due to empty or including disallowed characters.");
111
- let dest = joinPath(pt, fname), i = 1;
112
- while (fs.existsSync(dest))
113
- dest = joinPath(pt, `${fname}.${i++}`);
114
- return dest;
115
- }
116
- UFile.reserveFilePath = reserveFilePath;
117
- /**
118
- * decompress zip file. this depends on os enviroment due to using the os command.
119
- * currently this supports only windows (installed `tar` or `unzip` in gnu compiler) and linux systems (installed `unzip`).
120
- * @param zipPath zip file to be unzipped.
121
- * @param destDir directory that the decompress files export to.
122
- */
123
- function unzip(zipPath, destDir) {
124
- if (!exists(zipPath))
125
- throw new xjs_err_1.XjsErr(s_errCode, "There is no file on the zip path.");
126
- if (!!destDir && !exists(destDir))
127
- throw new xjs_err_1.XjsErr(s_errCode, "The destination directory is not found.");
128
- let cmd = "unzip", options = null, availableCmd = true;
129
- if (destDir)
130
- options = `-d "${destDir}"`;
131
- const check = () => { try {
132
- (0, child_process_1.execSync)(`${cmd} --help`, { stdio: "ignore" });
133
- }
134
- catch {
135
- availableCmd = false;
136
- } };
137
- check();
138
- if (process.platform === "win32") {
139
- if (!availableCmd) {
140
- cmd = "tar";
141
- options = "-xf";
142
- availableCmd = true;
143
- if (destDir)
144
- options = `-C "${destDir}" ${options}`;
145
- check();
146
- }
147
- }
148
- else if (process.platform === "linux") {
149
- }
150
- else
151
- throw new xjs_err_1.XjsErr(s_errCode, "The os running on is not supported for xjs unzip.");
152
- if (!availableCmd)
153
- throw new xjs_err_1.XjsErr(s_errCode, `"${cmd}" command is not installed.`);
154
- try {
155
- (0, child_process_1.execSync)([cmd, options, `"${zipPath}"`].filter(e => e).join(" "), { stdio: "ignore" });
156
- }
157
- catch (e) {
158
- throw new xjs_err_1.XjsErr(s_errCode, "Something went wrong at unzip.", e);
159
- }
160
- }
161
- UFile.unzip = unzip;
162
- /**
163
- * @deprecated since v9.1.4
164
- * @removed this will be removed in v10.
165
- * @replaced it should uses this without namespace `UFile`.
166
- */
167
- function joinPath(...p) {
168
- return path.join(...p.flatMap(u_type_1.UType.takeAsArray));
169
- }
170
- UFile.joinPath = joinPath;
171
- })(UFile = exports.UFile || (exports.UFile = {}));
@@ -1,8 +0,0 @@
1
- import { Type } from "../const/types";
2
- export declare class TypeExp<T> {
3
- private t;
4
- private readonly _m;
5
- constructor(t: Type.string | Type.number | Type.bigint | Type.null | Type.symbol | Type.undefined);
6
- constructor(t: Type.object, model: any);
7
- match(o: any): o is T;
8
- }
@@ -1,23 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.TypeExp = void 0;
4
- const types_1 = require("../const/types");
5
- class TypeExp {
6
- t;
7
- _m;
8
- constructor(t, model) {
9
- this.t = t;
10
- this._m = model;
11
- }
12
- match(o) {
13
- if (typeof o !== this.t)
14
- return false;
15
- if (this.t !== types_1.Type.object)
16
- return true;
17
- const isArray = Array.isArray(o);
18
- if (isArray !== Array.isArray(this._m))
19
- return false;
20
- return true;
21
- }
22
- }
23
- exports.TypeExp = TypeExp;
@@ -1,64 +0,0 @@
1
- import { ClientOption, HttpResponse, IHttpClient, RequestOption } from "./i-http-client";
2
- import { Loggable } from "../../const/types";
3
- export declare const s_clientMode: {
4
- nodejs: {
5
- id: number;
6
- cipherOrder: any;
7
- };
8
- chrome: {
9
- id: number;
10
- cipherOrder: number[];
11
- };
12
- firefox: {
13
- id: number;
14
- cipherOrder: number[];
15
- };
16
- };
17
- export declare class HttpResolverContext implements IHttpClient {
18
- readonly cmv: number;
19
- private _l;
20
- private readonly _als;
21
- private readonly _mode;
22
- private readonly _ciphers;
23
- private readonly _proxyConfig?;
24
- private readonly _chHeaders;
25
- private _cookies?;
26
- get clientMode(): string;
27
- constructor(cmv: number, op?: ClientOption, _l?: Loggable);
28
- /**
29
- * request GET to the url.
30
- * @param url target url.
31
- * @param op.headers http headers.
32
- * @param op.ignoreQuery {@link RequestOption.ignoreQuery}
33
- * @param op.downloadPath {@link RequestOption.downloadPath}
34
- * @param op.timeout {@link RequestOption.timeout}
35
- * @returns string encoded by utf-8 as response payload.
36
- */
37
- get(url: string, op?: RequestOption & {
38
- outerRedirectCount?: number;
39
- }): Promise<HttpResponse>;
40
- /**
41
- * request POST to the url.
42
- * @param url target url.
43
- * @param payload request payload. if this is an object, it is treated as json.
44
- * @param op.headers http headers.
45
- * @param op.ignoreQuery {@link RequestOption.ignoreQuery}
46
- * @param op.downloadPath {@link RequestOption.downloadPath}
47
- * @param op.timeout {@link RequestOption.timeout}
48
- * @returns string encoded by utf-8 as response payload.
49
- */
50
- post(url: string, payload: any, op?: RequestOption): Promise<HttpResponse>;
51
- private createProxyAgent;
52
- private getIn;
53
- private postIn;
54
- private reqHttps;
55
- private processResponse;
56
- private resolveDownloadPath;
57
- private handleRedirect;
58
- private createCiphers;
59
- private setCookies;
60
- private storeCookies;
61
- private log;
62
- private warn;
63
- private error;
64
- }
@@ -1,343 +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 xjs_err_1 = require("../../obj/xjs-err");
36
- const u_http_1 = require("../../func/u-http");
37
- const u_array_1 = require("../../func/u-array");
38
- const http_method_1 = require("../../const/http-method");
39
- const u_type_1 = require("../../func/u-type");
40
- const u_file_1 = require("../../func/u-file");
41
- const u_string_1 = require("../../func/u-string");
42
- const u_1 = require("../../func/u");
43
- exports.s_clientMode = {
44
- nodejs: { id: 0, cipherOrder: null },
45
- chrome: { id: 1, cipherOrder: [2, 0, 1] },
46
- firefox: { id: 2, cipherOrder: [2, 1, 0] }
47
- };
48
- const s_errCode = 200;
49
- const s_redirectLimit = 5;
50
- const s_mode2headers = new Map([
51
- [exports.s_clientMode.firefox, (cmv) => ({
52
- "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
53
- "Accept-Encoding": "gzip, deflate, br",
54
- "Accept-Language": "en-US,en;q=0.5",
55
- "Sec-Fetch-Dest": "document",
56
- "Sec-Fetch-Mode": "navigate",
57
- "Sec-Fetch-Site": "none",
58
- "Sec-Fetch-User": "?1",
59
- "Upgrade-Insecure-Requests": "1",
60
- "User-Agent": `Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:${cmv}.0) Gecko/20100101 Firefox/${cmv}.0`
61
- })],
62
- [exports.s_clientMode.chrome, (cmv) => {
63
- const uad = cmv < 130
64
- ? `"Not/A)Brand";v="8", "Chromium";v="${cmv}", "Google Chrome";v="${cmv}"`
65
- : `"Chromium";v="${cmv}", "Not:A-Brand";v="24", "Google Chrome";v="${cmv}"`;
66
- const ch = {
67
- "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",
68
- "Accept-Encoding": "gzip, deflate, br, zstd",
69
- "Accept-Language": "en-US,en;q=0.9",
70
- "Sec-Ch-Ua": uad,
71
- "Sec-Ch-Ua-Mobile": "?0",
72
- "Sec-Ch-Ua-Platform": "Windows",
73
- "Sec-Fetch-Dest": "document",
74
- "Sec-Fetch-Mode": "navigate",
75
- "Sec-Fetch-Site": "none",
76
- "Sec-Fetch-User": "?1",
77
- "Upgrade-Insecure-Requests": "1",
78
- "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`
79
- };
80
- if (cmv >= 124)
81
- ch["Priority"] = "u=0, i";
82
- return ch;
83
- }]
84
- ]);
85
- class HttpResolverContext {
86
- cmv;
87
- _l;
88
- _als = new async_hooks_1.AsyncLocalStorage();
89
- _mode;
90
- _ciphers;
91
- _proxyConfig;
92
- _chHeaders;
93
- _cookies;
94
- get clientMode() { return Object.keys(exports.s_clientMode).find((_, i) => i === this._mode.id); }
95
- constructor(cmv, op, _l = console) {
96
- this.cmv = cmv;
97
- this._l = _l;
98
- this._mode = op?.mode ?? u_array_1.UArray.randomPick([exports.s_clientMode.chrome, exports.s_clientMode.firefox]);
99
- this._proxyConfig = op?.proxy;
100
- if (this._mode.id > 0) {
101
- this._ciphers = this.createCiphers(this._mode);
102
- this._chHeaders = s_mode2headers.get(this._mode)(this.cmv);
103
- }
104
- }
105
- /**
106
- * request GET to the url.
107
- * @param url target url.
108
- * @param op.headers http headers.
109
- * @param op.ignoreQuery {@link RequestOption.ignoreQuery}
110
- * @param op.downloadPath {@link RequestOption.downloadPath}
111
- * @param op.timeout {@link RequestOption.timeout}
112
- * @returns string encoded by utf-8 as response payload.
113
- */
114
- async get(url, op) {
115
- const u = new url_1.URL(url);
116
- const proxyAgent = this._proxyConfig && await this.createProxyAgent(u);
117
- const rc = { redirectCount: op?.outerRedirectCount ?? 0, proxyAgent };
118
- Object.assign(rc, op);
119
- return await this._als.run(rc, this.getIn, u).finally(() => proxyAgent?.destroy());
120
- }
121
- /**
122
- * request POST to the url.
123
- * @param url target url.
124
- * @param payload request payload. if this is an object, it is treated as json.
125
- * @param op.headers http headers.
126
- * @param op.ignoreQuery {@link RequestOption.ignoreQuery}
127
- * @param op.downloadPath {@link RequestOption.downloadPath}
128
- * @param op.timeout {@link RequestOption.timeout}
129
- * @returns string encoded by utf-8 as response payload.
130
- */
131
- async post(url, payload, op) {
132
- const u = new url_1.URL(url);
133
- const proxyAgent = this._proxyConfig && await this.createProxyAgent(u);
134
- const rc = { redirectCount: 0, proxyAgent };
135
- Object.assign(rc, op);
136
- return await this._als.run(rc, this.postIn, u, payload).finally(() => proxyAgent?.destroy());
137
- }
138
- createProxyAgent(u) {
139
- const conf = this._proxyConfig;
140
- return new Promise((resolve, reject) => {
141
- const headers = {};
142
- if (conf.auth)
143
- headers['Proxy-Authorization'] = `Basic ${Buffer.from(conf.auth.name + ':' + conf.auth.pass).toString('base64')}`;
144
- const req = (0, http_1.request)({
145
- host: conf.server,
146
- port: conf.port,
147
- method: http_method_1.HttpMethod.Connect,
148
- path: `${u.hostname}:443`,
149
- headers
150
- }).on('connect', (res, socket) => {
151
- if (res.statusCode === 200)
152
- resolve(new https_1.Agent({ socket, keepAlive: true }));
153
- else
154
- reject(new xjs_err_1.XjsErr(s_errCode, "Could not connect to proxy."));
155
- });
156
- req.on('error', reject);
157
- req.on('timeout', () => {
158
- req.destroy();
159
- reject(new xjs_err_1.XjsErr(s_errCode, "The http request timeout, maybe server did not respond."));
160
- });
161
- req.end();
162
- });
163
- }
164
- getIn = async (u) => {
165
- const params = {};
166
- const rc = this._als.getStore();
167
- params.method = http_method_1.HttpMethod.Get;
168
- params.headers = rc.headers ?? {};
169
- return await this.reqHttps(u, params);
170
- };
171
- postIn = async (u, payload) => {
172
- const params = {};
173
- const rc = this._als.getStore();
174
- params.method = http_method_1.HttpMethod.Post;
175
- params.headers = rc.headers ?? {};
176
- let p = payload;
177
- if (u_type_1.UType.isObject(payload)) {
178
- p = JSON.stringify(payload);
179
- params.headers["Content-Length"] = p.length;
180
- params.headers["Content-Type"] = "application/json";
181
- }
182
- return await this.reqHttps(u, params, p);
183
- };
184
- reqHttps(u, params, payload) {
185
- const rc = this._als.getStore();
186
- params.timeout = rc.timeout ?? 0;
187
- params.protocol = u.protocol;
188
- params.host = u.host;
189
- params.path = (rc.ignoreQuery || !u.search) ? u.pathname : `${u.pathname}${u.search}`;
190
- params.agent = rc.proxyAgent;
191
- if (this._mode.id > 0) {
192
- params.ciphers = this._ciphers;
193
- params.headers = params.headers ? Object.assign(params.headers, this._chHeaders) : this._chHeaders;
194
- }
195
- if (this._cookies)
196
- this.setCookies(params.headers);
197
- return new Promise((resolve, reject) => {
198
- const req = (0, https_1.request)(params, (res) => this.processResponse(resolve, reject, rc, params.host, res));
199
- req.on('error', reject);
200
- req.on('timeout', () => {
201
- req.destroy();
202
- reject(new xjs_err_1.XjsErr(s_errCode, "The http request timeout, maybe server did not respond."));
203
- });
204
- if (payload)
205
- req.write(payload);
206
- req.end();
207
- });
208
- }
209
- processResponse(resolve, reject, rc, host, res) {
210
- if (res.headers["set-cookie"])
211
- this.storeCookies(res.headers["set-cookie"]);
212
- const sc = u_http_1.UHttp.statusCategoryOf(res.statusCode);
213
- if (sc === 3) {
214
- this.handleRedirect(res, host).then(resolve).catch(reject).finally(() => res.destroy());
215
- return;
216
- }
217
- if (res.headers["content-disposition"]?.trim().startsWith("attachment")) {
218
- try {
219
- const dest = this.resolveDownloadPath(rc.downloadPath, res.headers["content-disposition"]);
220
- const stream = fs.createWriteStream(dest);
221
- res.pipe(stream);
222
- stream.on("finish", () => stream.close());
223
- stream.on("close", () => resolve({ headers: res.headers }));
224
- stream.on("error", reject);
225
- return;
226
- }
227
- catch (e) {
228
- if (e instanceof xjs_err_1.XjsErr)
229
- reject(e);
230
- else {
231
- this.error(e);
232
- reject(new xjs_err_1.XjsErr(s_errCode, "Failed to download a file."));
233
- }
234
- }
235
- }
236
- const bfs = [];
237
- const contentEncofing = res.headers["content-encoding"]?.toLocaleLowerCase();
238
- res.on('data', chunk => bfs.push(chunk));
239
- res.on('end', () => {
240
- try {
241
- let retBuf = Buffer.concat(bfs);
242
- if (contentEncofing == "gzip")
243
- retBuf = zlib.gunzipSync(retBuf);
244
- else if (contentEncofing == "br")
245
- retBuf = zlib.brotliDecompressSync(retBuf);
246
- const data = retBuf.toString("utf8");
247
- if (sc !== 2) {
248
- if (data.trim())
249
- this.warn(data);
250
- reject(new xjs_err_1.XjsErr(s_errCode, `Https received a error status ${res.statusCode}`));
251
- }
252
- else
253
- resolve({ payload: data, headers: res.headers });
254
- }
255
- catch (e) {
256
- reject(e);
257
- }
258
- });
259
- }
260
- resolveDownloadPath(opPath, disposition) {
261
- const appendFname = (d) => {
262
- const fname = disposition.split(";")
263
- .find(f => f.trim().startsWith("filename"))
264
- ?.replace(/^\s+filename\s+=/, "").trim()
265
- ?? u_file_1.UFile.reserveFilePath(d, `xjs-download_${u_string_1.UString.simpleTime()}`);
266
- return (0, u_1.joinPath)(d, fname);
267
- };
268
- if (opPath) {
269
- const st = u_file_1.UFile.status(opPath);
270
- if (!st || st.isFile()) {
271
- if (!u_file_1.UFile.exists(path.dirname(opPath)))
272
- throw new xjs_err_1.XjsErr(s_errCode, "Directory of the download file was not found.");
273
- return opPath;
274
- }
275
- if (st.isDirectory()) {
276
- if (!u_file_1.UFile.exists(opPath))
277
- throw new xjs_err_1.XjsErr(s_errCode, "Directory of the download path was not found.");
278
- return appendFname(opPath);
279
- }
280
- }
281
- return appendFname("./");
282
- }
283
- async handleRedirect(res, host) {
284
- const rc = this._als.getStore();
285
- if (!res.headers.location)
286
- throw new xjs_err_1.XjsErr(s_errCode, "Received http redirection, but no location header found.");
287
- if (rc.redirectCount++ > s_redirectLimit)
288
- throw new xjs_err_1.XjsErr(s_errCode, "Count of http redirection exceeds limit.");
289
- this.log(`Redirect to ${res.headers.location}. (count is ${rc.redirectCount})`);
290
- const dest = res.headers.location.startsWith("http") ? res.headers.location : `https://${host}${res.headers.location}`;
291
- if (rc.outerRedirectCount)
292
- throw new xjs_err_1.XjsErr(-1, dest);
293
- const u = new url_1.URL(dest);
294
- // consider for proxy which implements reverse proxy.
295
- if (rc.proxyAgent) {
296
- rc.proxyAgent?.destroy();
297
- rc.proxyAgent = await this.createProxyAgent(u);
298
- }
299
- return await this.getIn(u);
300
- }
301
- createCiphers(mode) {
302
- const defaultCiphers = tls.DEFAULT_CIPHERS.split(':');
303
- return [
304
- defaultCiphers[mode.cipherOrder[0]],
305
- defaultCiphers[mode.cipherOrder[1]],
306
- defaultCiphers[mode.cipherOrder[2]],
307
- ...u_array_1.UArray.shuffle(defaultCiphers.slice(3))
308
- ].join(':');
309
- }
310
- setCookies(headers) {
311
- const exp = this._cookies["expires"];
312
- if (exp && new Date(exp).getTime() <= Date.now()) {
313
- this._cookies = null;
314
- this.log("Cookies was cleared due to an expiraion.");
315
- }
316
- else
317
- headers.cookie = Object.keys(this._cookies)
318
- .filter(ckk => !["expires", "max-age"].includes(ckk))
319
- .map(ckk => `${ckk}=${this._cookies[ckk]};`).join(" ");
320
- }
321
- storeCookies(cookies) {
322
- this._cookies ??= {};
323
- cookies.filter(c => c).flatMap(c => c.split(";"))
324
- .map(c => {
325
- const idx = c.indexOf("=");
326
- return idx !== -1 && [c.substring(0, idx).toLowerCase().trim(), c.substring(idx + 1)];
327
- })
328
- .filter(cp => cp && cp[0] && !["secure", "path", "domain", "samesite"].includes(cp[0]))
329
- .forEach(cp => this._cookies[cp[0]] = cp[1]);
330
- this.log("Store cookies from set-cookie headers.");
331
- this.log(JSON.stringify(this._cookies));
332
- }
333
- log(msg) {
334
- this._l.log(`[http-resolver] ${msg}`);
335
- }
336
- warn(msg) {
337
- this._l.warn(`[http-resolver] ${msg}`);
338
- }
339
- error(msg) {
340
- this._l.error(`[http-resolver] ${msg}`);
341
- }
342
- }
343
- exports.HttpResolverContext = HttpResolverContext;
@@ -1,36 +0,0 @@
1
- import { Loggable } from "../../const/types";
2
- import { HttpResolverContext } from "./http-resolver-context";
3
- import { ClientOption, HttpResponse, IHttpClient, RequestOption } from "./i-http-client";
4
- export interface ClientMode {
5
- id: number;
6
- cipherOrder: number[];
7
- }
8
- export interface ProxyConfig {
9
- server: string;
10
- port: number;
11
- auth?: {
12
- name: string;
13
- pass: string;
14
- };
15
- }
16
- export declare class HttpResolver implements IHttpClient {
17
- private _baseCmv;
18
- private _l;
19
- /**
20
- * @param _baseCmv chrome major version refered when construct a user agent, and the version will be randomized between `n` to `n-4`.
21
- * @param _l custom logger. default is `console`.
22
- */
23
- constructor(_baseCmv?: number, _l?: Loggable);
24
- /**
25
- * create a http client as new context that keeps some states. (browser type, cookies, ciphers order, etc...)
26
- * @param op.mode {@link s_clientMode} that is imitated. default is random between chrome or firefox.
27
- * @param op.proxy proxy configuration.
28
- * @returns a http client as new context.
29
- */
30
- newContext(op?: ClientOption): HttpResolverContext;
31
- get(url: string, op?: RequestOption & ClientOption & {
32
- redirectAsNewRequest?: boolean;
33
- }): Promise<HttpResponse>;
34
- post(url: string, payload: any, op?: RequestOption & ClientOption): Promise<HttpResponse>;
35
- private fixCmv;
36
- }
@@ -1,53 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.HttpResolver = void 0;
4
- const xjs_err_1 = require("../../obj/xjs-err");
5
- const http_resolver_context_1 = require("./http-resolver-context");
6
- const s_cmvRange = 5;
7
- const s_defaultCmv = 137;
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_err_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,63 +0,0 @@
1
- /// <reference types="node" />
2
- import { IncomingHttpHeaders, OutgoingHttpHeaders } from "http";
3
- import { ClientMode, ProxyConfig } from "./http-resolver";
4
- export interface ClientOption {
5
- mode?: ClientMode;
6
- proxy?: ProxyConfig;
7
- }
8
- export interface RequestOption {
9
- headers?: OutgoingHttpHeaders;
10
- /**
11
- * if true, query part in the `url` is ignored.
12
- */
13
- ignoreQuery?: boolean;
14
- /**
15
- * destination directory or file path for download. this is only used when `Content-Disposition` header exists.
16
- * default is current directory of the process with `filename` of the disposition.
17
- */
18
- downloadPath?: string;
19
- /**
20
- * timeout milliseconds to wait for socket inactivity. default is infinity.
21
- */
22
- timeout?: number;
23
- }
24
- export interface HttpResponse {
25
- /**
26
- * http headers in the response.
27
- */
28
- headers?: IncomingHttpHeaders;
29
- /**
30
- * string encoded by utf-8 as response payload.
31
- */
32
- payload?: string;
33
- }
34
- export interface IHttpClient {
35
- /**
36
- * request GET to the url with new context.
37
- * @param url target url.
38
- * @param op.headers http headers.
39
- * @param op.mode {@link s_clientMode} that is imitated. default is random between chrome or firefox.
40
- * @param op.proxy proxy configuration.
41
- * @param op.ignoreQuery {@link RequestOption.ignoreQuery}
42
- * @param op.downloadPath {@link RequestOption.downloadPath}
43
- * @param op.timeout {@link RequestOption.timeout}
44
- * @param op.redirectAsNewRequest handle redirect as new request. this may be efficient when using proxy which is implemented reverse proxy.
45
- * @returns http response. {@link HttpResponse}
46
- */
47
- get(url: string, op?: RequestOption & ClientOption & {
48
- redirectAsNewRequest?: boolean;
49
- }): Promise<HttpResponse>;
50
- /**
51
- * request POST to the url with new context.
52
- * @param url target url.
53
- * @param payload request payload. if this is an object, it is treated as json.
54
- * @param op.headers http headers.
55
- * @param op.mode {@link s_clientMode} that is imitated. default is random between chrome or firefox.
56
- * @param op.proxy proxy configuration.
57
- * @param op.ignoreQuery {@link RequestOption.ignoreQuery}
58
- * @param op.downloadPath {@link RequestOption.downloadPath}
59
- * @param op.timeout {@link RequestOption.timeout}
60
- * @returns http response. {@link HttpResponse}
61
- */
62
- post(url: string, payload: any, op?: RequestOption & ClientOption): Promise<HttpResponse>;
63
- }
@@ -1,2 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,53 +0,0 @@
1
- export declare const s_clientMode: Record<string, ClientMode>;
2
- interface ClientMode {
3
- id: number;
4
- cipherOrder: number[];
5
- }
6
- interface ProxyConfig {
7
- server: string;
8
- port: number;
9
- auth?: {
10
- name: string;
11
- pass: string;
12
- };
13
- }
14
- export declare class HttpResolver {
15
- private _baseCmv;
16
- private _l;
17
- private readonly _als;
18
- private readonly _mode2headers;
19
- /**
20
- * @param _baseCmv chrome major version refered when construct a user agent, and the version will be randomized between `n` to `n-4`.
21
- * @param _l custom logger. default is `console`.
22
- */
23
- constructor(_baseCmv: number, _l?: {
24
- log: (msg: any) => void;
25
- warn: (msg: any) => void;
26
- });
27
- /**
28
- * request to the url with GET.
29
- * @param url target url.
30
- * @param op.mode {@link s_clientMode} that is imitated. default is random between chrome or firefox.
31
- * @param op.proxy proxy configuration.
32
- * @param op.ignoreQuery if true, query part in the `url` is ignored.
33
- * @param op.redirectAsNewRequest handle redirect as new request. this may be efficient when using proxy which is implemented reverse proxy.
34
- * @returns string encoded by utf-8 as response payload.
35
- */
36
- get(url: string, op?: {
37
- mode?: ClientMode;
38
- ignoreQuery?: boolean;
39
- proxy?: ProxyConfig;
40
- redirectAsNewRequest?: boolean;
41
- }): Promise<any>;
42
- private fixCmv;
43
- private createProxyAgent;
44
- private getIn;
45
- private reqHttps;
46
- private handleRedirect;
47
- private createCiphers;
48
- private setCookies;
49
- private storeCookies;
50
- private log;
51
- private warn;
52
- }
53
- export {};
@@ -1,255 +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.HttpResolver = exports.s_clientMode = void 0;
27
- const tls = __importStar(require("tls"));
28
- const zlib = __importStar(require("zlib"));
29
- const url_1 = require("url");
30
- const https_1 = require("https");
31
- const http_1 = require("http");
32
- const async_hooks_1 = require("async_hooks");
33
- const xjs_err_1 = require("../obj/xjs-err");
34
- const u_http_1 = require("../func/u-http");
35
- const u_array_1 = require("../func/u-array");
36
- exports.s_clientMode = {
37
- nodejs: { id: 0, cipherOrder: null },
38
- chrome: { id: 1, cipherOrder: [2, 0, 1] },
39
- firefox: { id: 2, cipherOrder: [2, 1, 0] }
40
- };
41
- const s_cmvRange = 5;
42
- const s_timeout = 1000 * 20;
43
- const s_errCode = 200;
44
- class HttpResolver {
45
- _baseCmv;
46
- _l;
47
- _als = new async_hooks_1.AsyncLocalStorage();
48
- _mode2headers = new Map([
49
- [exports.s_clientMode.firefox, (cmv) => ({
50
- "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
51
- "Accept-Encoding": "gzip, deflate, br",
52
- "Accept-Language": "en-US,en;q=0.5",
53
- "Sec-Fetch-Dest": "document",
54
- "Sec-Fetch-Mode": "navigate",
55
- "Sec-Fetch-Site": "none",
56
- "Sec-Fetch-User": "?1",
57
- "Upgrade-Insecure-Requests": "1",
58
- "User-Agent": `Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:${cmv}.0) Gecko/20100101 Firefox/${cmv}.0`
59
- })],
60
- [exports.s_clientMode.chrome, (cmv) => {
61
- const uad = cmv < 130
62
- ? `"Not/A)Brand";v="8", "Chromium";v="${cmv}", "Google Chrome";v="${cmv}"`
63
- : `"Chromium";v="${cmv}", "Not:A-Brand";v="24", "Google Chrome";v="${cmv}"`;
64
- const ch = {
65
- "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",
66
- "Accept-Encoding": "gzip, deflate, br, zstd",
67
- "Accept-Language": "en-US,en;q=0.9",
68
- "Sec-Ch-Ua": uad,
69
- "Sec-Ch-Ua-Mobile": "?0",
70
- "Sec-Ch-Ua-Platform": "Windows",
71
- "Sec-Fetch-Dest": "document",
72
- "Sec-Fetch-Mode": "navigate",
73
- "Sec-Fetch-Site": "none",
74
- "Sec-Fetch-User": "?1",
75
- "Upgrade-Insecure-Requests": "1",
76
- "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`
77
- };
78
- if (cmv >= 124)
79
- ch["Priority"] = "u=0, i";
80
- return ch;
81
- }]
82
- ]);
83
- /**
84
- * @param _baseCmv chrome major version refered when construct a user agent, and the version will be randomized between `n` to `n-4`.
85
- * @param _l custom logger. default is `console`.
86
- */
87
- constructor(_baseCmv, _l = console) {
88
- this._baseCmv = _baseCmv;
89
- this._l = _l;
90
- }
91
- /**
92
- * request to the url with GET.
93
- * @param url target url.
94
- * @param op.mode {@link s_clientMode} that is imitated. default is random between chrome or firefox.
95
- * @param op.proxy proxy configuration.
96
- * @param op.ignoreQuery if true, query part in the `url` is ignored.
97
- * @param op.redirectAsNewRequest handle redirect as new request. this may be efficient when using proxy which is implemented reverse proxy.
98
- * @returns string encoded by utf-8 as response payload.
99
- */
100
- async get(url, op) {
101
- const cmv = this.fixCmv();
102
- const m = op?.mode ?? u_array_1.UArray.randomPick([exports.s_clientMode.chrome, exports.s_clientMode.firefox]);
103
- const ciphers = this.createCiphers(m);
104
- const u = new url_1.URL(url);
105
- const proxyAgent = op?.proxy && await this.createProxyAgent(u, op.proxy);
106
- this._l.log(`Starts to request with ${Object.keys(exports.s_clientMode).find((_, i) => i === m.id)}:${cmv}.`);
107
- const rc = {
108
- mode: m, cmv, ciphers,
109
- ignoreQuery: !!op?.ignoreQuery,
110
- redirectCount: 0, redirectAsNewRequest: !!op?.redirectAsNewRequest,
111
- proxyAgent, proxyConfig: op?.proxy
112
- };
113
- return await this._als.run(rc, this.getIn, u).finally(() => proxyAgent?.destroy());
114
- }
115
- fixCmv() {
116
- return this._baseCmv - Math.floor(Math.random() * s_cmvRange);
117
- }
118
- createProxyAgent(u, conf) {
119
- return new Promise((resolve, reject) => {
120
- const headers = {};
121
- if (conf.auth)
122
- headers['Proxy-Authorization'] = `Basic ${Buffer.from(conf.auth.name + ':' + conf.auth.pass).toString('base64')}`;
123
- const req = (0, http_1.request)({
124
- host: conf.server,
125
- port: conf.port,
126
- method: 'CONNECT',
127
- path: `${u.hostname}:443`,
128
- headers
129
- }).on('connect', (res, socket) => {
130
- if (res.statusCode === 200)
131
- resolve(new https_1.Agent({ socket, keepAlive: true }));
132
- else
133
- reject(new xjs_err_1.XjsErr(s_errCode, "Could not connect to proxy."));
134
- });
135
- req.on('error', reject);
136
- req.on('timeout', () => {
137
- req.destroy();
138
- reject(new xjs_err_1.XjsErr(s_errCode, "The http request timeout, maybe server did not respond."));
139
- });
140
- req.end();
141
- });
142
- }
143
- getIn = async (u) => {
144
- const params = {};
145
- const rc = this._als.getStore();
146
- const toRefresh = rc.redirectCount > 0 && rc.redirectAsNewRequest;
147
- params.method = "GET";
148
- params.protocol = u.protocol;
149
- params.host = u.host;
150
- params.path = (rc.ignoreQuery || !u.search) ? u.pathname : `${u.pathname}${u.search}`;
151
- params.agent = toRefresh ? await this.createProxyAgent(u, rc.proxyConfig) : rc.proxyAgent;
152
- if (toRefresh) {
153
- rc.cmv = this.fixCmv();
154
- rc.ciphers = this.createCiphers(rc.mode);
155
- }
156
- return await this.reqHttps(params, null);
157
- };
158
- reqHttps(params, postData) {
159
- params.timeout = s_timeout;
160
- const rc = this._als.getStore();
161
- if (rc.mode.id > 0) {
162
- params.ciphers = rc.ciphers;
163
- const chHeaders = this._mode2headers.get(rc.mode)(rc.cmv);
164
- params.headers = params.headers ? Object.assign(params.headers, chHeaders) : chHeaders;
165
- }
166
- if (rc.cookies && !rc.redirectAsNewRequest)
167
- this.setCookies(params.headers);
168
- return new Promise((resolve, reject) => {
169
- const req = (0, https_1.request)(params, (res) => {
170
- const sc = u_http_1.UHttp.statusCategoryOf(res.statusCode);
171
- if (sc === 3) {
172
- this.handleRedirect(res, params.host).then(resolve).catch(reject).finally(() => res.destroy());
173
- return;
174
- }
175
- const bfs = [];
176
- const contentEncofing = res.headers["content-encoding"]?.toLocaleLowerCase();
177
- res.on('data', chunk => bfs.push(chunk));
178
- res.on('end', () => {
179
- try {
180
- let retBuf = Buffer.concat(bfs);
181
- if (contentEncofing == "gzip")
182
- retBuf = zlib.gunzipSync(retBuf);
183
- else if (contentEncofing == "br")
184
- retBuf = zlib.brotliDecompressSync(retBuf);
185
- const data = retBuf.toString("utf8");
186
- if (sc !== 2) {
187
- if (data.trim())
188
- this.warn(data);
189
- reject(new xjs_err_1.XjsErr(s_errCode, `Https received a error status ${res.statusCode}`));
190
- }
191
- else
192
- resolve(data);
193
- }
194
- catch (e) {
195
- reject(e);
196
- }
197
- });
198
- });
199
- req.on('error', reject);
200
- req.on('timeout', () => {
201
- req.destroy();
202
- reject(new xjs_err_1.XjsErr(s_errCode, "The http request timeout, maybe server did not respond."));
203
- });
204
- if (postData)
205
- req.write(postData);
206
- req.end();
207
- });
208
- }
209
- handleRedirect(res, host) {
210
- const rc = this._als.getStore();
211
- if (!res.headers.location)
212
- throw new xjs_err_1.XjsErr(s_errCode, "Received http redirection, but no location header found.");
213
- if (rc.redirectCount++ > 2)
214
- throw new xjs_err_1.XjsErr(s_errCode, "Count of http redirection exceeds limit.");
215
- if (res.headers["set-cookie"] && !rc.redirectAsNewRequest)
216
- this.storeCookies(res.headers["set-cookie"]);
217
- this.log(`Redirect to ${res.headers.location}. (count is ${rc.redirectCount})`);
218
- const dest = res.headers.location.startsWith("http") ? res.headers.location : `https://${host}${res.headers.location}`;
219
- if (rc.redirectAsNewRequest)
220
- rc.proxyAgent?.destroy();
221
- return this.getIn(new url_1.URL(dest));
222
- }
223
- createCiphers(mode) {
224
- const defaultCiphers = tls.DEFAULT_CIPHERS.split(':');
225
- return [
226
- defaultCiphers[mode.cipherOrder[0]],
227
- defaultCiphers[mode.cipherOrder[1]],
228
- defaultCiphers[mode.cipherOrder[2]],
229
- ...u_array_1.UArray.shuffle(defaultCiphers.slice(3))
230
- ].join(':');
231
- }
232
- setCookies(headers) {
233
- const cks = this._als.getStore().cookies;
234
- headers.cookie = Object.keys(cks).map(ckk => `${ckk}=${cks[ckk]};`).join(" ");
235
- }
236
- storeCookies(cookies) {
237
- this._als.getStore().cookies ??= {};
238
- cookies.filter(c => c).flatMap(c => c.split(";"))
239
- .map(c => {
240
- const idx = c.indexOf("=");
241
- return idx !== -1 && [c.substring(0, idx).toLowerCase().trim(), c.substring(idx + 1)];
242
- })
243
- .filter(cp => cp && cp[0] && !["secure", "path", "domain", "samesite"].includes(cp[0]))
244
- .forEach(cp => this._als.getStore().cookies[cp[0]] = cp[1]);
245
- this.log("Store cookies from set-cookie headers.");
246
- this.log(JSON.stringify(this._als.getStore().cookies));
247
- }
248
- log(msg) {
249
- this._l.log(`[http-resolver] ${msg}`);
250
- }
251
- warn(msg) {
252
- this._l.warn(`[http-resolver] ${msg}`);
253
- }
254
- }
255
- exports.HttpResolver = HttpResolver;