cvitool 1.0.6
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/README.md +9 -0
- package/build/src/cutil.d.ts +9 -0
- package/build/src/cutil.js +37 -0
- package/build/src/hgo.d.ts +48 -0
- package/build/src/hgo.js +265 -0
- package/build/src/streamhelper.d.ts +9 -0
- package/build/src/streamhelper.js +61 -0
- package/index.d.ts +78 -0
- package/index.js +9 -0
- package/package.json +32 -0
- package/src/cutil.ts +47 -0
- package/src/hgo.ts +300 -0
- package/src/streamhelper.ts +59 -0
package/README.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
interface randomStringOptions {
|
|
2
|
+
special?: boolean;
|
|
3
|
+
lowercase?: boolean;
|
|
4
|
+
upperCase?: boolean;
|
|
5
|
+
number?: boolean;
|
|
6
|
+
specials?: string;
|
|
7
|
+
}
|
|
8
|
+
declare function randomString(length: number, options?: randomStringOptions): string;
|
|
9
|
+
export { randomStringOptions, randomString };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.randomString = void 0;
|
|
4
|
+
const crypto_1 = require("crypto");
|
|
5
|
+
function randomString(length, options) {
|
|
6
|
+
let { special = false, lowercase = true, upperCase = true, number = true, specials } = options || {};
|
|
7
|
+
if (specials) {
|
|
8
|
+
special = true;
|
|
9
|
+
}
|
|
10
|
+
if (!special && !lowercase && !upperCase && !number) {
|
|
11
|
+
throw new Error('randomString|must choose one of (special|lowercase|upperCase|number)');
|
|
12
|
+
}
|
|
13
|
+
const numbers = '0123456789';
|
|
14
|
+
const lowercaseLetters = 'abcdefghijklmnopqrstuvwxyz';
|
|
15
|
+
const upperCaseLetters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
|
16
|
+
specials = specials || '~!@#$%^*()_+-=[]{}|;:,./<>?';
|
|
17
|
+
let targetStr = '';
|
|
18
|
+
if (special) {
|
|
19
|
+
targetStr += specials;
|
|
20
|
+
}
|
|
21
|
+
if (lowercase) {
|
|
22
|
+
targetStr += lowercaseLetters;
|
|
23
|
+
}
|
|
24
|
+
if (upperCase) {
|
|
25
|
+
targetStr += upperCaseLetters;
|
|
26
|
+
}
|
|
27
|
+
if (number) {
|
|
28
|
+
targetStr += numbers;
|
|
29
|
+
}
|
|
30
|
+
let result = '';
|
|
31
|
+
while (length > 0) {
|
|
32
|
+
result += targetStr.charAt((0, crypto_1.randomInt)(targetStr.length));
|
|
33
|
+
length--;
|
|
34
|
+
}
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
exports.randomString = randomString;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
/// <reference types="node" />
|
|
3
|
+
/// <reference types="node" />
|
|
4
|
+
/// <reference types="node" />
|
|
5
|
+
import * as https from 'https';
|
|
6
|
+
import * as http from 'http';
|
|
7
|
+
import { ReadStream } from 'fs';
|
|
8
|
+
type Method = 'get' | 'put' | 'post' | 'delete' | 'patch' | 'head';
|
|
9
|
+
type ResType = 'json' | 'buffer' | 'stream' | 'text';
|
|
10
|
+
interface CustomObject {
|
|
11
|
+
[key: string]: any;
|
|
12
|
+
}
|
|
13
|
+
interface baseReqOptions {
|
|
14
|
+
timeout?: number;
|
|
15
|
+
method?: Method;
|
|
16
|
+
agent?: http.Agent | https.Agent;
|
|
17
|
+
headers?: {
|
|
18
|
+
[key: string]: string;
|
|
19
|
+
};
|
|
20
|
+
resType?: ResType;
|
|
21
|
+
}
|
|
22
|
+
interface reqOptions extends baseReqOptions {
|
|
23
|
+
query?: {
|
|
24
|
+
[key: string]: string;
|
|
25
|
+
};
|
|
26
|
+
body?: {
|
|
27
|
+
[key: string]: any;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
interface reqSendBufferOptions extends baseReqOptions {
|
|
31
|
+
buffer: Buffer;
|
|
32
|
+
}
|
|
33
|
+
interface reqSendStreamOptions extends baseReqOptions {
|
|
34
|
+
stream: ReadStream;
|
|
35
|
+
}
|
|
36
|
+
interface reqSendMultiPartOptions extends baseReqOptions {
|
|
37
|
+
form: any;
|
|
38
|
+
}
|
|
39
|
+
interface ResData {
|
|
40
|
+
reqUrl: string;
|
|
41
|
+
resHeaders: CustomObject;
|
|
42
|
+
resBody: http.IncomingMessage | CustomObject | string | Buffer | null;
|
|
43
|
+
}
|
|
44
|
+
declare function request(url: string, options?: reqOptions): Promise<ResData>;
|
|
45
|
+
declare function reqSendBuffer(url: string, options: reqSendBufferOptions): Promise<ResData>;
|
|
46
|
+
declare function reqSendStream(url: string, options: reqSendStreamOptions): Promise<ResData>;
|
|
47
|
+
declare function reqSendMultiPart(url: string, options: reqSendMultiPartOptions): Promise<ResData>;
|
|
48
|
+
export { reqOptions, reqSendBufferOptions, reqSendStreamOptions, reqSendMultiPartOptions, ResData, request, reqSendBuffer, reqSendStream, reqSendMultiPart, };
|
package/build/src/hgo.js
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.reqSendMultiPart = exports.reqSendStream = exports.reqSendBuffer = exports.request = void 0;
|
|
13
|
+
const querystring = require("querystring");
|
|
14
|
+
const https = require("https");
|
|
15
|
+
const http = require("http");
|
|
16
|
+
function getTimeOutMessage(timeout) {
|
|
17
|
+
return `request timeout of ${timeout} ms`;
|
|
18
|
+
}
|
|
19
|
+
function getProtocol(url) {
|
|
20
|
+
return url.startsWith('https') ? https : http;
|
|
21
|
+
}
|
|
22
|
+
function request(url, options) {
|
|
23
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
24
|
+
const { query = {}, body = {}, headers = {}, timeout = 5000, method = 'get', agent, resType = 'json' } = options || {};
|
|
25
|
+
if (Object.keys(query).length !== 0) {
|
|
26
|
+
if (url.indexOf('?') > -1) {
|
|
27
|
+
url = url + '&' + querystring.stringify(query);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
url = url + '?' + querystring.stringify(query);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const data = JSON.stringify(body);
|
|
34
|
+
const protocol = getProtocol(url);
|
|
35
|
+
const isbodyEmpty = Object.keys(body).length === 0;
|
|
36
|
+
const baseHeaders = {
|
|
37
|
+
'Content-Type': 'application/json;charset=utf-8',
|
|
38
|
+
'Content-length': isbodyEmpty ? 0 : Buffer.byteLength(data)
|
|
39
|
+
};
|
|
40
|
+
return new Promise((resolve, reject) => {
|
|
41
|
+
const req = protocol.request(url, {
|
|
42
|
+
timeout,
|
|
43
|
+
headers: Object.assign(headers, baseHeaders),
|
|
44
|
+
method,
|
|
45
|
+
agent
|
|
46
|
+
}, res => {
|
|
47
|
+
resHandld(res, resolve, reject, resType, method);
|
|
48
|
+
});
|
|
49
|
+
req.on('timeout', () => {
|
|
50
|
+
req.destroy();
|
|
51
|
+
reject(new Error(getTimeOutMessage(timeout)));
|
|
52
|
+
});
|
|
53
|
+
req.on('error', e => {
|
|
54
|
+
req.destroy();
|
|
55
|
+
reject(e);
|
|
56
|
+
});
|
|
57
|
+
if (isbodyEmpty) {
|
|
58
|
+
req.end();
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
req.write(data, (e) => {
|
|
62
|
+
req.end();
|
|
63
|
+
if (e) {
|
|
64
|
+
req.destroy();
|
|
65
|
+
reject(e);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
exports.request = request;
|
|
73
|
+
function reqSendBuffer(url, options) {
|
|
74
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
75
|
+
const { timeout = 60000, headers = {}, buffer, method = 'post', agent, resType = 'json' } = options;
|
|
76
|
+
const protocol = getProtocol(url);
|
|
77
|
+
return new Promise((resolve, reject) => {
|
|
78
|
+
const req = protocol.request(url, {
|
|
79
|
+
timeout,
|
|
80
|
+
headers: Object.assign(headers, { 'Content-Length': buffer.byteLength }),
|
|
81
|
+
method,
|
|
82
|
+
agent
|
|
83
|
+
}, res => {
|
|
84
|
+
resHandld(res, resolve, reject, resType, method);
|
|
85
|
+
});
|
|
86
|
+
req.on('timeout', () => {
|
|
87
|
+
req.destroy();
|
|
88
|
+
reject(new Error(getTimeOutMessage(timeout)));
|
|
89
|
+
});
|
|
90
|
+
req.on('error', e => {
|
|
91
|
+
req.destroy();
|
|
92
|
+
reject(e);
|
|
93
|
+
});
|
|
94
|
+
req.write(buffer, (e) => {
|
|
95
|
+
req.end();
|
|
96
|
+
if (e) {
|
|
97
|
+
req.destroy();
|
|
98
|
+
reject(e);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
exports.reqSendBuffer = reqSendBuffer;
|
|
105
|
+
function reqSendStream(url, options) {
|
|
106
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
107
|
+
const { timeout = 60000, method = 'post', stream, headers = {}, agent, resType = 'json' } = options;
|
|
108
|
+
const protocol = getProtocol(url);
|
|
109
|
+
return new Promise((resolve, reject) => {
|
|
110
|
+
const baseHeaders = {
|
|
111
|
+
'Content-Type': 'application/octet-stream',
|
|
112
|
+
'Transfer-Encoding': 'chunked',
|
|
113
|
+
Connection: 'keep-alive'
|
|
114
|
+
};
|
|
115
|
+
const req = protocol.request(url, {
|
|
116
|
+
timeout,
|
|
117
|
+
headers: Object.assign(headers, baseHeaders),
|
|
118
|
+
method,
|
|
119
|
+
agent
|
|
120
|
+
}, res => {
|
|
121
|
+
resHandld(res, resolve, reject, resType, method);
|
|
122
|
+
});
|
|
123
|
+
req.on('timeout', () => {
|
|
124
|
+
req.destroy();
|
|
125
|
+
reject(new Error(getTimeOutMessage(timeout)));
|
|
126
|
+
});
|
|
127
|
+
req.on('error', e => {
|
|
128
|
+
req.destroy();
|
|
129
|
+
reject(e);
|
|
130
|
+
});
|
|
131
|
+
stream.on('data', chunk => {
|
|
132
|
+
req.write(chunk, e => {
|
|
133
|
+
if (e) {
|
|
134
|
+
req.destroy();
|
|
135
|
+
reject(e);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
stream.on('end', () => {
|
|
140
|
+
req.end();
|
|
141
|
+
stream.close();
|
|
142
|
+
});
|
|
143
|
+
stream.on('error', e => {
|
|
144
|
+
req.destroy();
|
|
145
|
+
stream.close();
|
|
146
|
+
reject(e);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
exports.reqSendStream = reqSendStream;
|
|
152
|
+
function reqSendMultiPart(url, options) {
|
|
153
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
154
|
+
const { timeout = 60000, headers = {}, form, agent, resType = 'json' } = options;
|
|
155
|
+
const protocol = getProtocol(url);
|
|
156
|
+
return new Promise((resolve, reject) => {
|
|
157
|
+
const req = protocol.request(url, {
|
|
158
|
+
timeout,
|
|
159
|
+
headers: Object.assign(headers, Object.assign({}, form.getHeaders())),
|
|
160
|
+
method: 'post',
|
|
161
|
+
agent
|
|
162
|
+
}, res => {
|
|
163
|
+
resHandld(res, resolve, reject, resType, 'post');
|
|
164
|
+
});
|
|
165
|
+
req.on('timeout', () => {
|
|
166
|
+
req.destroy();
|
|
167
|
+
reject(new Error(getTimeOutMessage(timeout)));
|
|
168
|
+
});
|
|
169
|
+
req.on('error', e => {
|
|
170
|
+
req.destroy();
|
|
171
|
+
reject(e);
|
|
172
|
+
});
|
|
173
|
+
form.pipe(req);
|
|
174
|
+
form.on('end', () => {
|
|
175
|
+
req.end();
|
|
176
|
+
});
|
|
177
|
+
form.on('error', e => {
|
|
178
|
+
req.destroy();
|
|
179
|
+
form.destroy();
|
|
180
|
+
reject(e);
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
exports.reqSendMultiPart = reqSendMultiPart;
|
|
186
|
+
function resHandld(res, resolve, reject, resType, method) {
|
|
187
|
+
const reqUrl = `${res.req.protocol}//${res.req.host}${res.req.path}`;
|
|
188
|
+
const resHeaders = {};
|
|
189
|
+
for (let i = 0; i < res.rawHeaders.length; i += 2) {
|
|
190
|
+
resHeaders[res.rawHeaders[i]] = res.rawHeaders[i + 1];
|
|
191
|
+
if (i === res.rawHeaders.length - 2) {
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (res.statusCode !== 200) {
|
|
196
|
+
errHandle(reject, res, reqUrl, resHeaders);
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
const resData = {
|
|
200
|
+
reqUrl,
|
|
201
|
+
resHeaders,
|
|
202
|
+
resBody: null
|
|
203
|
+
};
|
|
204
|
+
if (method === 'head') {
|
|
205
|
+
resolve(resData);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
if (resType === 'stream') {
|
|
209
|
+
resData.resBody = res;
|
|
210
|
+
resolve(resData);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
let resBody;
|
|
214
|
+
const chunks = [];
|
|
215
|
+
res.on('data', chunk => {
|
|
216
|
+
chunks.push(chunk);
|
|
217
|
+
});
|
|
218
|
+
res.on('end', () => {
|
|
219
|
+
const buffer = Buffer.concat(chunks);
|
|
220
|
+
if (resType === 'buffer') {
|
|
221
|
+
resBody = buffer;
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
const responseStr = buffer.toString();
|
|
225
|
+
if (resType === 'text') {
|
|
226
|
+
resBody = responseStr;
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
try {
|
|
230
|
+
resBody = JSON.parse(responseStr);
|
|
231
|
+
}
|
|
232
|
+
catch (e) {
|
|
233
|
+
resBody = resData;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
resData.resBody = resBody;
|
|
238
|
+
resolve(resData);
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
function errHandle(reject, res, reqUrl, resHeaders) {
|
|
242
|
+
const chunks = [];
|
|
243
|
+
res.on('data', chunk => {
|
|
244
|
+
chunks.push(chunk);
|
|
245
|
+
});
|
|
246
|
+
res.on('end', () => {
|
|
247
|
+
const buffer = Buffer.concat(chunks);
|
|
248
|
+
const resData = buffer.toString();
|
|
249
|
+
let resBody;
|
|
250
|
+
try {
|
|
251
|
+
resBody = JSON.parse(resData);
|
|
252
|
+
}
|
|
253
|
+
catch (e) {
|
|
254
|
+
resBody = resData;
|
|
255
|
+
}
|
|
256
|
+
const err = new Error();
|
|
257
|
+
err.code = res.statusCode;
|
|
258
|
+
err.name = 'statusCodeError';
|
|
259
|
+
err.message = `${res.statusCode}|${res.statusMessage}`;
|
|
260
|
+
err.resBody = resBody;
|
|
261
|
+
err.reqUrl = reqUrl;
|
|
262
|
+
err.resHeaders = resHeaders;
|
|
263
|
+
reject(err);
|
|
264
|
+
});
|
|
265
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { ReadStream, WriteStream } from 'fs';
|
|
3
|
+
interface pipeOptions {
|
|
4
|
+
timeout?: number;
|
|
5
|
+
readBytesPreSec?: number;
|
|
6
|
+
}
|
|
7
|
+
declare function pipe(source: ReadStream, target: WriteStream, options?: pipeOptions): Promise<void>;
|
|
8
|
+
declare function limitStreamFlowingRate(stream: ReadStream, readBytesPreSec: number): void;
|
|
9
|
+
export { pipeOptions, pipe, limitStreamFlowingRate };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.limitStreamFlowingRate = exports.pipe = void 0;
|
|
13
|
+
function pipe(source, target, options) {
|
|
14
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
15
|
+
const { timeout, readBytesPreSec } = options || {};
|
|
16
|
+
return new Promise((resolve, reject) => {
|
|
17
|
+
if (readBytesPreSec) {
|
|
18
|
+
limitStreamFlowingRate(source, readBytesPreSec);
|
|
19
|
+
}
|
|
20
|
+
source.pipe(target);
|
|
21
|
+
target.on('finish', () => {
|
|
22
|
+
resolve();
|
|
23
|
+
});
|
|
24
|
+
source.on('error', e => {
|
|
25
|
+
reject(e);
|
|
26
|
+
});
|
|
27
|
+
if (timeout) {
|
|
28
|
+
setTimeout(() => {
|
|
29
|
+
if (!target.closed) {
|
|
30
|
+
target.destroy();
|
|
31
|
+
source.destroy();
|
|
32
|
+
const downloadErr = new Error(`文件下载超时|${(timeout / 1000).toFixed(3)}s`);
|
|
33
|
+
reject(downloadErr);
|
|
34
|
+
}
|
|
35
|
+
}, timeout);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
exports.pipe = pipe;
|
|
41
|
+
function limitStreamFlowingRate(stream, readBytesPreSec) {
|
|
42
|
+
let start = 0;
|
|
43
|
+
let calReadBytesTotal = 0;
|
|
44
|
+
stream.on('data', (chunk) => {
|
|
45
|
+
if (!start) {
|
|
46
|
+
start = Date.now();
|
|
47
|
+
}
|
|
48
|
+
calReadBytesTotal += chunk.length;
|
|
49
|
+
const fromStartSecs = Math.ceil((Date.now() - start) / 1000);
|
|
50
|
+
if (calReadBytesTotal > fromStartSecs * readBytesPreSec && fromStartSecs > 0) {
|
|
51
|
+
const stopTime = Math.ceil((fromStartSecs - ((Date.now() - start) / 1000)) * 1000);
|
|
52
|
+
if (stopTime > 0) {
|
|
53
|
+
stream.pause();
|
|
54
|
+
setTimeout(() => {
|
|
55
|
+
stream.resume();
|
|
56
|
+
}, stopTime);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
exports.limitStreamFlowingRate = limitStreamFlowingRate;
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { ReadStream, WriteStream } from 'fs';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
reqOptions,
|
|
5
|
+
reqSendBufferOptions,
|
|
6
|
+
reqSendStreamOptions,
|
|
7
|
+
reqSendMultiPartOptions,
|
|
8
|
+
ResData
|
|
9
|
+
} from './src/hgo';
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
pipeOptions,
|
|
13
|
+
} from './src/streamhelper';
|
|
14
|
+
|
|
15
|
+
import {
|
|
16
|
+
randomStringOptions
|
|
17
|
+
} from './src/cutil';
|
|
18
|
+
|
|
19
|
+
interface Hgo {
|
|
20
|
+
/**
|
|
21
|
+
* 发出普通请求
|
|
22
|
+
* @param url
|
|
23
|
+
* @param options
|
|
24
|
+
*/
|
|
25
|
+
request(url: string, options?: reqOptions): Promise<ResData>,
|
|
26
|
+
/**
|
|
27
|
+
* 发出传送buffer请求
|
|
28
|
+
* @param url
|
|
29
|
+
* @param options
|
|
30
|
+
*/
|
|
31
|
+
reqSendBuffer(url: string, options: reqSendBufferOptions): Promise<ResData>,
|
|
32
|
+
/**
|
|
33
|
+
* 发出传送stream请求
|
|
34
|
+
* @param url
|
|
35
|
+
* @param options
|
|
36
|
+
*/
|
|
37
|
+
reqSendStream(url: string, options: reqSendStreamOptions): Promise<ResData>,
|
|
38
|
+
/**
|
|
39
|
+
* 发出传送表单formData请求
|
|
40
|
+
* @param url
|
|
41
|
+
* @param options
|
|
42
|
+
*/
|
|
43
|
+
reqSendMultiPart(url: string, options: reqSendMultiPartOptions): Promise<ResData>,
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface StreamHelper {
|
|
47
|
+
/**
|
|
48
|
+
* 实现一个promise流传输
|
|
49
|
+
* @param source
|
|
50
|
+
* @param target
|
|
51
|
+
* @param options
|
|
52
|
+
*/
|
|
53
|
+
pipe(source: ReadStream, target: WriteStream, options?: pipeOptions): Promise<void>,
|
|
54
|
+
/**
|
|
55
|
+
* stream传输限流
|
|
56
|
+
* @param stream
|
|
57
|
+
* @param readBytesPreSec
|
|
58
|
+
*/
|
|
59
|
+
limitStreamFlowingRate(stream: ReadStream, readBytesPreSec: number): void
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
interface Cutil {
|
|
63
|
+
/**
|
|
64
|
+
* 获取一个随机字符串
|
|
65
|
+
* @param length
|
|
66
|
+
* @param options
|
|
67
|
+
*/
|
|
68
|
+
randomString(length: number, options?: randomStringOptions): string
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
declare const hgo: Hgo;
|
|
72
|
+
declare const streamhelper: StreamHelper;
|
|
73
|
+
declare const cutil: Cutil;
|
|
74
|
+
export {
|
|
75
|
+
hgo,
|
|
76
|
+
streamhelper,
|
|
77
|
+
cutil
|
|
78
|
+
}
|
package/index.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cvitool",
|
|
3
|
+
"version": "1.0.6",
|
|
4
|
+
"description": "cvitool",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "npm run build && node ./build/test/index.test.js",
|
|
8
|
+
"build": "rm -rf build && tsc"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {},
|
|
11
|
+
"devDependencies": {
|
|
12
|
+
"@typescript-eslint/eslint-plugin": "5.54.0",
|
|
13
|
+
"@typescript-eslint/parser": "5.54.0",
|
|
14
|
+
"eslint": "7.32.0",
|
|
15
|
+
"eslint-config-standard": "16.0.3",
|
|
16
|
+
"@types/node": "20.10.4"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"build/src",
|
|
20
|
+
"src",
|
|
21
|
+
"index.js",
|
|
22
|
+
"index.d.ts",
|
|
23
|
+
"package.json"
|
|
24
|
+
],
|
|
25
|
+
"keywords": [],
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": ""
|
|
29
|
+
},
|
|
30
|
+
"author": "",
|
|
31
|
+
"license": "ISC"
|
|
32
|
+
}
|
package/src/cutil.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { randomInt } from 'crypto';
|
|
2
|
+
|
|
3
|
+
interface randomStringOptions {
|
|
4
|
+
special?: boolean,
|
|
5
|
+
lowercase?: boolean,
|
|
6
|
+
upperCase?: boolean,
|
|
7
|
+
number?: boolean
|
|
8
|
+
specials?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function randomString(length: number, options?: randomStringOptions) {
|
|
12
|
+
let { special = false, lowercase = true, upperCase = true, number = true, specials } = options || {};
|
|
13
|
+
if (specials) {
|
|
14
|
+
special = true;
|
|
15
|
+
}
|
|
16
|
+
if (!special && !lowercase && !upperCase && !number) {
|
|
17
|
+
throw new Error('randomString|must choose one of (special|lowercase|upperCase|number)');
|
|
18
|
+
}
|
|
19
|
+
const numbers = '0123456789';
|
|
20
|
+
const lowercaseLetters = 'abcdefghijklmnopqrstuvwxyz';
|
|
21
|
+
const upperCaseLetters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
|
22
|
+
specials = specials || '~!@#$%^*()_+-=[]{}|;:,./<>?';
|
|
23
|
+
let targetStr = '';
|
|
24
|
+
if (special) {
|
|
25
|
+
targetStr += specials;
|
|
26
|
+
}
|
|
27
|
+
if (lowercase) {
|
|
28
|
+
targetStr += lowercaseLetters;
|
|
29
|
+
}
|
|
30
|
+
if (upperCase) {
|
|
31
|
+
targetStr += upperCaseLetters;
|
|
32
|
+
}
|
|
33
|
+
if (number) {
|
|
34
|
+
targetStr += numbers;
|
|
35
|
+
}
|
|
36
|
+
let result = '';
|
|
37
|
+
while (length > 0) {
|
|
38
|
+
result += targetStr.charAt(randomInt(targetStr.length));
|
|
39
|
+
length--;
|
|
40
|
+
}
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export {
|
|
45
|
+
randomStringOptions,
|
|
46
|
+
randomString
|
|
47
|
+
};
|
package/src/hgo.ts
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import * as querystring from 'querystring';
|
|
2
|
+
import * as https from 'https';
|
|
3
|
+
import * as http from 'http';
|
|
4
|
+
import { ReadStream } from 'fs';
|
|
5
|
+
|
|
6
|
+
type Method = 'get' | 'put' | 'post' | 'delete' | 'patch' | 'head';
|
|
7
|
+
type ResType = 'json' | 'buffer' | 'stream' | 'text';
|
|
8
|
+
|
|
9
|
+
interface CustomObject {
|
|
10
|
+
[key: string]: any
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface baseReqOptions {
|
|
14
|
+
timeout?: number,
|
|
15
|
+
method?: Method,
|
|
16
|
+
agent?: http.Agent | https.Agent,
|
|
17
|
+
headers?: {
|
|
18
|
+
[key: string]: string
|
|
19
|
+
},
|
|
20
|
+
resType?: ResType
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface reqOptions extends baseReqOptions {
|
|
24
|
+
query?: {
|
|
25
|
+
[key: string]: string
|
|
26
|
+
},
|
|
27
|
+
body?: {
|
|
28
|
+
[key: string]: any
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface reqSendBufferOptions extends baseReqOptions {
|
|
33
|
+
buffer: Buffer
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface reqSendStreamOptions extends baseReqOptions {
|
|
37
|
+
stream: ReadStream
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface reqSendMultiPartOptions extends baseReqOptions {
|
|
41
|
+
form: any
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface ResData {
|
|
45
|
+
reqUrl: string,
|
|
46
|
+
resHeaders: CustomObject,
|
|
47
|
+
resBody: http.IncomingMessage | CustomObject | string | Buffer | null
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function getTimeOutMessage(timeout: number): string {
|
|
51
|
+
return `request timeout of ${timeout} ms`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function getProtocol(url: string) {
|
|
55
|
+
return url.startsWith('https') ? https : http;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function request(url: string, options?: reqOptions): Promise<ResData> {
|
|
59
|
+
const { query = {}, body = {}, headers = {}, timeout = 5000, method = 'get', agent, resType = 'json' } = options || {};
|
|
60
|
+
if (Object.keys(query).length !== 0) {
|
|
61
|
+
if (url.indexOf('?') > -1) {
|
|
62
|
+
url = url + '&' + querystring.stringify(query);
|
|
63
|
+
} else {
|
|
64
|
+
url = url + '?' + querystring.stringify(query);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const data = JSON.stringify(body);
|
|
68
|
+
const protocol = getProtocol(url);
|
|
69
|
+
const isbodyEmpty = Object.keys(body).length === 0;
|
|
70
|
+
const baseHeaders = {
|
|
71
|
+
'Content-Type': 'application/json;charset=utf-8',
|
|
72
|
+
'Content-length': isbodyEmpty ? 0 : Buffer.byteLength(data)
|
|
73
|
+
};
|
|
74
|
+
return new Promise((resolve, reject) => {
|
|
75
|
+
const req = protocol.request(url, {
|
|
76
|
+
timeout,
|
|
77
|
+
headers: Object.assign(headers, baseHeaders),
|
|
78
|
+
method,
|
|
79
|
+
agent
|
|
80
|
+
}, res => {
|
|
81
|
+
resHandld(res, resolve, reject, resType, method);
|
|
82
|
+
});
|
|
83
|
+
req.on('timeout', () => {
|
|
84
|
+
req.destroy();
|
|
85
|
+
reject(new Error(getTimeOutMessage(timeout)));
|
|
86
|
+
});
|
|
87
|
+
req.on('error', e => {
|
|
88
|
+
req.destroy();
|
|
89
|
+
reject(e);
|
|
90
|
+
});
|
|
91
|
+
if (isbodyEmpty) {
|
|
92
|
+
req.end();
|
|
93
|
+
} else {
|
|
94
|
+
req.write(data, (e) => {
|
|
95
|
+
req.end();
|
|
96
|
+
if (e) {
|
|
97
|
+
req.destroy();
|
|
98
|
+
reject(e);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function reqSendBuffer(url: string, options: reqSendBufferOptions): Promise<ResData> {
|
|
106
|
+
const { timeout = 60000, headers = {}, buffer, method = 'post', agent, resType = 'json' } = options;
|
|
107
|
+
const protocol = getProtocol(url);
|
|
108
|
+
return new Promise((resolve, reject) => {
|
|
109
|
+
const req = protocol.request(url, {
|
|
110
|
+
timeout,
|
|
111
|
+
headers: Object.assign(headers, { 'Content-Length': buffer.byteLength }),
|
|
112
|
+
method,
|
|
113
|
+
agent
|
|
114
|
+
}, res => {
|
|
115
|
+
resHandld(res, resolve, reject, resType, method);
|
|
116
|
+
});
|
|
117
|
+
req.on('timeout', () => {
|
|
118
|
+
req.destroy();
|
|
119
|
+
reject(new Error(getTimeOutMessage(timeout)));
|
|
120
|
+
});
|
|
121
|
+
req.on('error', e => {
|
|
122
|
+
req.destroy();
|
|
123
|
+
reject(e);
|
|
124
|
+
});
|
|
125
|
+
req.write(buffer, (e) => {
|
|
126
|
+
req.end();
|
|
127
|
+
if (e) {
|
|
128
|
+
req.destroy();
|
|
129
|
+
reject(e);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async function reqSendStream(url: string, options: reqSendStreamOptions): Promise<ResData> {
|
|
136
|
+
const { timeout = 60000, method = 'post', stream, headers = {}, agent, resType = 'json' } = options;
|
|
137
|
+
const protocol = getProtocol(url);
|
|
138
|
+
return new Promise((resolve, reject) => {
|
|
139
|
+
const baseHeaders = {
|
|
140
|
+
'Content-Type': 'application/octet-stream',
|
|
141
|
+
'Transfer-Encoding': 'chunked',
|
|
142
|
+
Connection: 'keep-alive'
|
|
143
|
+
};
|
|
144
|
+
const req = protocol.request(url, {
|
|
145
|
+
timeout,
|
|
146
|
+
headers: Object.assign(headers, baseHeaders),
|
|
147
|
+
method,
|
|
148
|
+
agent
|
|
149
|
+
}, res => {
|
|
150
|
+
resHandld(res, resolve, reject, resType, method);
|
|
151
|
+
});
|
|
152
|
+
req.on('timeout', () => {
|
|
153
|
+
req.destroy();
|
|
154
|
+
reject(new Error(getTimeOutMessage(timeout)));
|
|
155
|
+
});
|
|
156
|
+
req.on('error', e => {
|
|
157
|
+
req.destroy();
|
|
158
|
+
reject(e);
|
|
159
|
+
});
|
|
160
|
+
stream.on('data', chunk => {
|
|
161
|
+
req.write(chunk, e => {
|
|
162
|
+
if (e) {
|
|
163
|
+
req.destroy();
|
|
164
|
+
reject(e);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
stream.on('end', () => {
|
|
169
|
+
req.end();
|
|
170
|
+
stream.close();
|
|
171
|
+
});
|
|
172
|
+
stream.on('error', e => {
|
|
173
|
+
req.destroy();
|
|
174
|
+
stream.close();
|
|
175
|
+
reject(e);
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async function reqSendMultiPart(url: string, options: reqSendMultiPartOptions): Promise<ResData> {
|
|
181
|
+
const { timeout = 60000, headers = {}, form, agent, resType = 'json' } = options;
|
|
182
|
+
const protocol = getProtocol(url);
|
|
183
|
+
return new Promise((resolve, reject) => {
|
|
184
|
+
const req = protocol.request(url, {
|
|
185
|
+
timeout,
|
|
186
|
+
headers: Object.assign(headers, { ...form.getHeaders() }),
|
|
187
|
+
method: 'post',
|
|
188
|
+
agent
|
|
189
|
+
}, res => {
|
|
190
|
+
resHandld(res, resolve, reject, resType, 'post');
|
|
191
|
+
});
|
|
192
|
+
req.on('timeout', () => {
|
|
193
|
+
req.destroy();
|
|
194
|
+
reject(new Error(getTimeOutMessage(timeout)));
|
|
195
|
+
});
|
|
196
|
+
req.on('error', e => {
|
|
197
|
+
req.destroy();
|
|
198
|
+
reject(e);
|
|
199
|
+
});
|
|
200
|
+
form.pipe(req);
|
|
201
|
+
form.on('end', () => {
|
|
202
|
+
req.end();
|
|
203
|
+
});
|
|
204
|
+
form.on('error', e => {
|
|
205
|
+
req.destroy();
|
|
206
|
+
form.destroy();
|
|
207
|
+
reject(e);
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function resHandld(res: http.IncomingMessage, resolve: any, reject: any, resType: ResType, method: Method) {
|
|
213
|
+
const reqUrl = `${(res as any).req.protocol}//${(res as any).req.host}${(res as any).req.path}`;
|
|
214
|
+
const resHeaders: CustomObject = {};
|
|
215
|
+
for (let i = 0; i < res.rawHeaders.length; i += 2) {
|
|
216
|
+
resHeaders[res.rawHeaders[i]] = res.rawHeaders[i + 1];
|
|
217
|
+
if (i === res.rawHeaders.length - 2) {
|
|
218
|
+
break;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if (res.statusCode !== 200) {
|
|
222
|
+
errHandle(reject, res, reqUrl, resHeaders);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
const resData = {
|
|
226
|
+
reqUrl,
|
|
227
|
+
resHeaders,
|
|
228
|
+
resBody: null
|
|
229
|
+
};
|
|
230
|
+
if (method === 'head') {
|
|
231
|
+
resolve(resData);
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
if (resType === 'stream') {
|
|
235
|
+
resData.resBody = res;
|
|
236
|
+
resolve(resData);
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
let resBody: Buffer | object | string;
|
|
240
|
+
const chunks = [];
|
|
241
|
+
res.on('data', chunk => {
|
|
242
|
+
chunks.push(chunk);
|
|
243
|
+
});
|
|
244
|
+
res.on('end', () => {
|
|
245
|
+
const buffer = Buffer.concat(chunks);
|
|
246
|
+
if (resType === 'buffer') {
|
|
247
|
+
resBody = buffer;
|
|
248
|
+
} else {
|
|
249
|
+
const responseStr = buffer.toString();
|
|
250
|
+
if (resType === 'text') {
|
|
251
|
+
resBody = responseStr;
|
|
252
|
+
} else {
|
|
253
|
+
try {
|
|
254
|
+
resBody = JSON.parse(responseStr);
|
|
255
|
+
} catch (e) {
|
|
256
|
+
resBody = resData;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
resData.resBody = resBody;
|
|
261
|
+
resolve(resData);
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function errHandle(reject: any, res: http.IncomingMessage, reqUrl: string, resHeaders: CustomObject) {
|
|
266
|
+
const chunks = [];
|
|
267
|
+
res.on('data', chunk => {
|
|
268
|
+
chunks.push(chunk);
|
|
269
|
+
});
|
|
270
|
+
res.on('end', () => {
|
|
271
|
+
const buffer = Buffer.concat(chunks);
|
|
272
|
+
const resData = buffer.toString();
|
|
273
|
+
let resBody: object | string;
|
|
274
|
+
try {
|
|
275
|
+
resBody = JSON.parse(resData);
|
|
276
|
+
} catch (e) {
|
|
277
|
+
resBody = resData;
|
|
278
|
+
}
|
|
279
|
+
const err: any = new Error();
|
|
280
|
+
err.code = res.statusCode;
|
|
281
|
+
err.name = 'statusCodeError';
|
|
282
|
+
err.message = `${res.statusCode}|${res.statusMessage}`;
|
|
283
|
+
err.resBody = resBody;
|
|
284
|
+
err.reqUrl = reqUrl;
|
|
285
|
+
err.resHeaders = resHeaders;
|
|
286
|
+
reject(err);
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export {
|
|
291
|
+
reqOptions,
|
|
292
|
+
reqSendBufferOptions,
|
|
293
|
+
reqSendStreamOptions,
|
|
294
|
+
reqSendMultiPartOptions,
|
|
295
|
+
ResData,
|
|
296
|
+
request,
|
|
297
|
+
reqSendBuffer,
|
|
298
|
+
reqSendStream,
|
|
299
|
+
reqSendMultiPart,
|
|
300
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { ReadStream, WriteStream } from 'fs';
|
|
2
|
+
|
|
3
|
+
interface pipeOptions {
|
|
4
|
+
timeout?: number,
|
|
5
|
+
readBytesPreSec?: number
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
async function pipe(source: ReadStream, target: WriteStream, options?: pipeOptions): Promise<void> {
|
|
9
|
+
const { timeout, readBytesPreSec } = options || {};
|
|
10
|
+
return new Promise((resolve, reject) => {
|
|
11
|
+
if (readBytesPreSec) {
|
|
12
|
+
limitStreamFlowingRate(source, readBytesPreSec);
|
|
13
|
+
}
|
|
14
|
+
source.pipe(target);
|
|
15
|
+
target.on('finish', () => {
|
|
16
|
+
resolve();
|
|
17
|
+
});
|
|
18
|
+
source.on('error', e => {
|
|
19
|
+
reject(e);
|
|
20
|
+
});
|
|
21
|
+
if (timeout) {
|
|
22
|
+
setTimeout(() => {
|
|
23
|
+
if (!target.closed) {
|
|
24
|
+
target.destroy();
|
|
25
|
+
source.destroy();
|
|
26
|
+
const downloadErr = new Error(`文件下载超时|${(timeout / 1000).toFixed(3)}s`);
|
|
27
|
+
reject(downloadErr);
|
|
28
|
+
}
|
|
29
|
+
}, timeout);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function limitStreamFlowingRate(stream: ReadStream, readBytesPreSec: number) {
|
|
35
|
+
let start = 0;
|
|
36
|
+
let calReadBytesTotal = 0;
|
|
37
|
+
stream.on('data', (chunk) => {
|
|
38
|
+
if (!start) {
|
|
39
|
+
start = Date.now();
|
|
40
|
+
}
|
|
41
|
+
calReadBytesTotal += chunk.length;
|
|
42
|
+
const fromStartSecs = Math.ceil((Date.now() - start) / 1000);
|
|
43
|
+
if (calReadBytesTotal > fromStartSecs * readBytesPreSec && fromStartSecs > 0) {
|
|
44
|
+
const stopTime = Math.ceil((fromStartSecs - ((Date.now() - start) / 1000)) * 1000);
|
|
45
|
+
if (stopTime > 0) {
|
|
46
|
+
stream.pause();
|
|
47
|
+
setTimeout(() => {
|
|
48
|
+
stream.resume();
|
|
49
|
+
}, stopTime);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export {
|
|
56
|
+
pipeOptions,
|
|
57
|
+
pipe,
|
|
58
|
+
limitStreamFlowingRate
|
|
59
|
+
};
|