lupine.api 1.1.48 → 1.1.49
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/package.json +1 -1
- package/src/api/handle-status.ts +6 -2
- package/src/app/web-listener copy.ts +274 -0
- package/src/app/web-listener.ts +31 -5
- package/src/app/web-server.ts +16 -4
- package/src/lib/utils/is-type.ts +1 -1
package/package.json
CHANGED
package/src/api/handle-status.ts
CHANGED
|
@@ -10,6 +10,10 @@ export const handler304 = (res: ServerResponse, msg?: string | object, contentTy
|
|
|
10
10
|
handlerResponse(res, 304, '304 Not Modified', msg, contentType);
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
+
export const handler400 = (res: ServerResponse, msg?: string | object, contentType?: string) => {
|
|
14
|
+
handlerResponse(res, 400, '400 Bad Request', msg, contentType);
|
|
15
|
+
};
|
|
16
|
+
|
|
13
17
|
export const handler403 = (res: ServerResponse, msg?: string | object, contentType?: string) => {
|
|
14
18
|
handlerResponse(res, 403, '403 Forbidden', msg, contentType);
|
|
15
19
|
};
|
|
@@ -30,8 +34,8 @@ export const handler500 = (res: ServerResponse, msg?: string | object, contentTy
|
|
|
30
34
|
handlerResponse(res, 500, '500 Internal Server Error', msg, contentType);
|
|
31
35
|
};
|
|
32
36
|
|
|
33
|
-
export const
|
|
34
|
-
handlerResponse(res,
|
|
37
|
+
export const handler503 = (res: ServerResponse, msg?: string | object, contentType?: string) => {
|
|
38
|
+
handlerResponse(res, 503, '503 Service Unavailable', msg, contentType);
|
|
35
39
|
};
|
|
36
40
|
|
|
37
41
|
export const handlerResponse = (
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import { IncomingMessage, ServerResponse } from 'http';
|
|
2
|
+
import { Logger } from '../lib/logger';
|
|
3
|
+
import crypto from 'crypto';
|
|
4
|
+
import { parseCookies } from '../lib/utils/cookie-util';
|
|
5
|
+
import { WebProcessor } from './web-processor';
|
|
6
|
+
import { handler403, handler404, handler500, handler503, SimpleStorage } from '../api';
|
|
7
|
+
import { JsonObject, AsyncStorageProps, ServerRequest, SetCookieProps } from '../models';
|
|
8
|
+
import { HostToPath } from './host-to-path';
|
|
9
|
+
import { serializeCookie } from '../lib/utils/cookie-util';
|
|
10
|
+
const logger = new Logger('listener');
|
|
11
|
+
|
|
12
|
+
let MAX_REQUEST_SIZE = 1024 * 1024 * 5;
|
|
13
|
+
export const setMaxRequestSize = (size: number) => {
|
|
14
|
+
MAX_REQUEST_SIZE = size;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// The maximum number of requests being processed. If there are no requests for 10 minutes, this number will be reset to 0.
|
|
18
|
+
let MAX_REQUEST_COUNT = 100;
|
|
19
|
+
let REQUEST_COUNT = 0;
|
|
20
|
+
export const setMaxRequestCount = (count: number) => {
|
|
21
|
+
MAX_REQUEST_COUNT = count;
|
|
22
|
+
};
|
|
23
|
+
export const getRequestCount = () => {
|
|
24
|
+
return REQUEST_COUNT;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
let REQUEST_TIMEOUT = 1000 * 30;
|
|
28
|
+
export const setRequestTimeout = (timeout: number) => {
|
|
29
|
+
REQUEST_TIMEOUT = timeout;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
let accessControlAllowHosts: string[] = [];
|
|
33
|
+
export const setAccessControlAllowHost = (allowHosts: string[]) => {
|
|
34
|
+
accessControlAllowHosts = allowHosts;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
let SERVER_NAME: string = 'nginx/1.19.2';
|
|
38
|
+
export const setServerName = (serverName: string) => {
|
|
39
|
+
SERVER_NAME = serverName;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
let IP_LIMIT_RATE_PER_SECOND = 3;
|
|
43
|
+
export const setIpLimitRatePerSecond = (rate: number) => {
|
|
44
|
+
IP_LIMIT_RATE_PER_SECOND = rate;
|
|
45
|
+
};
|
|
46
|
+
let IP_LIMIT_RATE_PER_MINUTE = 60;
|
|
47
|
+
export const setIpLimitRatePerMinute = (rate: number) => {
|
|
48
|
+
IP_LIMIT_RATE_PER_MINUTE = rate;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
let IP_BLOCK_TIME = 60_000;
|
|
52
|
+
export const setIpBlockTime = (time: number) => {
|
|
53
|
+
IP_BLOCK_TIME = time;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const rawMiddlewareIpLimit = (req: IncomingMessage, res: ServerResponse, next: () => void) => {
|
|
57
|
+
const clientIp = req.socket.remoteAddress || 'unknown';
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export type RawMiddleware = (req: IncomingMessage, res: ServerResponse, next: () => void) => void;
|
|
61
|
+
|
|
62
|
+
let lastRequestTime = new Date().getTime();
|
|
63
|
+
|
|
64
|
+
// type ProcessRequest = (req: ServerRequest, res: ServerResponse) => void;
|
|
65
|
+
export class WebListener {
|
|
66
|
+
// process requests before business logic, for example IP filter, rate limit, etc.
|
|
67
|
+
rawMiddlewares: RawMiddleware[];
|
|
68
|
+
processor: WebProcessor;
|
|
69
|
+
|
|
70
|
+
constructor(processRequest: WebProcessor) {
|
|
71
|
+
this.rawMiddlewares = [];
|
|
72
|
+
this.processor = processRequest;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
addRawMiddlewareChain(middleware: RawMiddleware) {
|
|
76
|
+
this.rawMiddlewares.push(middleware);
|
|
77
|
+
}
|
|
78
|
+
async handleRawMiddlewares(req: IncomingMessage, res: ServerResponse) {
|
|
79
|
+
const runChain = (list: RawMiddleware[], context: { req: IncomingMessage; res: ServerResponse }) => {
|
|
80
|
+
const dispatch = async (i: number) => {
|
|
81
|
+
const fn = list[i];
|
|
82
|
+
if (!fn) return;
|
|
83
|
+
await fn(context.req, context.res, () => dispatch(i + 1));
|
|
84
|
+
};
|
|
85
|
+
return dispatch(0);
|
|
86
|
+
};
|
|
87
|
+
await runChain(this.rawMiddlewares, { req, res });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async listener(reqOrigin: IncomingMessage, res: ServerResponse) {
|
|
91
|
+
// If there is no request in the last 10 minutes, reset the request count.
|
|
92
|
+
if (new Date().getTime() - lastRequestTime > 1000 * 60 * 10) {
|
|
93
|
+
if (REQUEST_COUNT != 0) {
|
|
94
|
+
// in case any errors skipped (--REQUEST_COUNT)
|
|
95
|
+
logger.warn(`!!!!!!!!!! ========== REQUEST_COUNT is not counted properly: ${REQUEST_COUNT}`);
|
|
96
|
+
}
|
|
97
|
+
REQUEST_COUNT = 0;
|
|
98
|
+
lastRequestTime = new Date().getTime();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// back-pressure
|
|
102
|
+
if (REQUEST_COUNT > MAX_REQUEST_COUNT) {
|
|
103
|
+
logger.warn(`Too many requests, count: ${REQUEST_COUNT} > ${MAX_REQUEST_COUNT}`);
|
|
104
|
+
handler503(res, 'Server is busy, please retry later.');
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
await this.handleRawMiddlewares(reqOrigin, res);
|
|
109
|
+
if (res.writableEnded || res.headersSent) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// const requestStart = process.hrtime.bigint();
|
|
114
|
+
const uuid = crypto.randomUUID();
|
|
115
|
+
const url = reqOrigin.url || '';
|
|
116
|
+
const requestInfo = `uuid: ${uuid}, Access url: ${url}`;
|
|
117
|
+
const req = reqOrigin as ServerRequest;
|
|
118
|
+
|
|
119
|
+
const host = (req.headers.host || '').split(':')[0]; // req.headers.host contains port
|
|
120
|
+
const hostPath = HostToPath.findHostPath(host);
|
|
121
|
+
if (!hostPath || !hostPath.webPath || !hostPath.appName) {
|
|
122
|
+
const msg = `Web root is not defined properly for host: ${host}.`;
|
|
123
|
+
logger.error(msg);
|
|
124
|
+
handler404(res, msg);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
REQUEST_COUNT++;
|
|
129
|
+
logger.debug(
|
|
130
|
+
`Request started. Count: ${REQUEST_COUNT}, Log uuid: ${uuid}, access: ${
|
|
131
|
+
req.headers.host
|
|
132
|
+
}, url: ${url}, time: ${new Date().toISOString()}, from: ${req.socket.remoteAddress}`
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
const urlSplit = url.split('?');
|
|
136
|
+
req.setTimeout(REQUEST_TIMEOUT);
|
|
137
|
+
req.on('timeout', () => {
|
|
138
|
+
REQUEST_COUNT--;
|
|
139
|
+
logger.warn('timeout');
|
|
140
|
+
req.destroy(new Error('timeout handling'));
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
const jsonFn = (): JsonObject | undefined => {
|
|
144
|
+
if (!req.locals._json && req.locals.body) {
|
|
145
|
+
const sBody = req.locals.body.toString();
|
|
146
|
+
if (!sBody) {
|
|
147
|
+
req.locals._json = undefined;
|
|
148
|
+
} else {
|
|
149
|
+
try {
|
|
150
|
+
req.locals._json = JSON.parse(sBody);
|
|
151
|
+
} catch (err: any) {
|
|
152
|
+
logger.warn(`JSON.parse error: ${err.message}`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return req.locals._json;
|
|
157
|
+
};
|
|
158
|
+
const cookiesFn = (): SimpleStorage => {
|
|
159
|
+
if (!req.locals._cookies) {
|
|
160
|
+
req.locals._cookies = new SimpleStorage(req.headers ? parseCookies(req.headers.cookie) : {});
|
|
161
|
+
}
|
|
162
|
+
return req.locals._cookies;
|
|
163
|
+
};
|
|
164
|
+
const setCookieFn = (name: string, value: string, options: SetCookieProps): void => {
|
|
165
|
+
const cookies: string[] = [];
|
|
166
|
+
const cookiesOld = res.getHeader('Set-Cookie');
|
|
167
|
+
if (cookiesOld) {
|
|
168
|
+
if (!Array.isArray(cookiesOld)) {
|
|
169
|
+
cookies.push(cookiesOld as any);
|
|
170
|
+
} else {
|
|
171
|
+
cookies.push(...cookiesOld);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const cookiePair = serializeCookie(name, value, options);
|
|
176
|
+
cookies.push(cookiePair);
|
|
177
|
+
res.setHeader('Set-Cookie', cookies);
|
|
178
|
+
|
|
179
|
+
const localCookies = req.locals.cookies();
|
|
180
|
+
localCookies.set(name, value);
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
req.locals = {
|
|
184
|
+
uuid,
|
|
185
|
+
host,
|
|
186
|
+
url,
|
|
187
|
+
hostPath,
|
|
188
|
+
// urlSections: urlSplit[0].split('/').filter((i) => !!i),
|
|
189
|
+
query: new URLSearchParams(urlSplit[1] || ''),
|
|
190
|
+
urlWithoutQuery: urlSplit[0],
|
|
191
|
+
urlParameters: new SimpleStorage({}),
|
|
192
|
+
body: undefined,
|
|
193
|
+
json: jsonFn,
|
|
194
|
+
cookies: cookiesFn,
|
|
195
|
+
setCookie: setCookieFn,
|
|
196
|
+
clearCookie: (name: string) => {
|
|
197
|
+
res.setHeader('Set-Cookie', `${name}=; max-age=0`);
|
|
198
|
+
},
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
let bigRequest = false;
|
|
202
|
+
let totalLength = 0;
|
|
203
|
+
const bodyData: any[] = [];
|
|
204
|
+
req.on('error', (err: any) => {
|
|
205
|
+
REQUEST_COUNT--;
|
|
206
|
+
logger.error(`${requestInfo}, count: ${REQUEST_COUNT}, Request Error: `, err);
|
|
207
|
+
handler500(res, `listener error: ${err && err.message}`);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
req.on('data', (chunk: any) => {
|
|
211
|
+
totalLength += chunk.length;
|
|
212
|
+
logger.debug(`${requestInfo}, Request data length: ${chunk.length}, total: ${totalLength}`);
|
|
213
|
+
// Limit Request Size
|
|
214
|
+
if (!bigRequest && totalLength < MAX_REQUEST_SIZE) {
|
|
215
|
+
bodyData.push(chunk);
|
|
216
|
+
} else {
|
|
217
|
+
if (!bigRequest) {
|
|
218
|
+
bigRequest = true;
|
|
219
|
+
logger.warn(`Warn, request data is too big: ${totalLength} > ${MAX_REQUEST_SIZE}`);
|
|
220
|
+
}
|
|
221
|
+
req.socket.destroy();
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
req.on('end', async () => {
|
|
226
|
+
try {
|
|
227
|
+
if (bigRequest) {
|
|
228
|
+
logger.warn(`Request data is too big to process, url: ${req.locals.url}`);
|
|
229
|
+
handler403(res, `Request data is too big to process, url: ${req.locals.url}`);
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const body = Buffer.concat(bodyData);
|
|
234
|
+
const contentType = req.headers['content-type'];
|
|
235
|
+
logger.debug(`url: ${url}, Request body length: ${body.length}, contentType: ${contentType}`);
|
|
236
|
+
req.locals.body = body;
|
|
237
|
+
|
|
238
|
+
res.setHeader('Server', SERVER_NAME);
|
|
239
|
+
if (accessControlAllowHosts.includes(host)) {
|
|
240
|
+
const allowOrigin = req.headers.origin && req.headers.origin !== 'null' ? req.headers.origin : '*';
|
|
241
|
+
res.setHeader('Access-Control-Allow-Origin', allowOrigin);
|
|
242
|
+
res.setHeader('Access-Control-Allow-Credentials', 'true');
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const store: AsyncStorageProps = {
|
|
246
|
+
uuid: uuid,
|
|
247
|
+
hostPath: hostPath,
|
|
248
|
+
appName: hostPath.appName,
|
|
249
|
+
locals: req.locals,
|
|
250
|
+
lang: req.locals.cookies().get('lang', 'en') || 'en',
|
|
251
|
+
};
|
|
252
|
+
await this.processor.processRequest(store, req, res);
|
|
253
|
+
} finally {
|
|
254
|
+
REQUEST_COUNT--;
|
|
255
|
+
}
|
|
256
|
+
// await new Promise(resolve => setTimeout(resolve, 3000));
|
|
257
|
+
|
|
258
|
+
// asyncLocalStorage.run(store, async () => {
|
|
259
|
+
// try {
|
|
260
|
+
// await onEnd();
|
|
261
|
+
// } catch (error: any) {
|
|
262
|
+
// logger.error(`url: ${url}, Request end error: `, error.message);
|
|
263
|
+
// }
|
|
264
|
+
|
|
265
|
+
// lastRequestTime = new Date().getTime();
|
|
266
|
+
// const requestEnd = process.hrtime.bigint();
|
|
267
|
+
// REQUEST_COUNT--;
|
|
268
|
+
// logger.debug(
|
|
269
|
+
// `Request finished. Count: ${REQUEST_COUNT}, url: ${url}, time: ${new Date().toISOString()}, duration: ${Number(requestEnd - requestStart) / 1000000} ms`
|
|
270
|
+
// );
|
|
271
|
+
// });
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
}
|
package/src/app/web-listener.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { Logger } from '../lib/logger';
|
|
|
3
3
|
import crypto from 'crypto';
|
|
4
4
|
import { parseCookies } from '../lib/utils/cookie-util';
|
|
5
5
|
import { WebProcessor } from './web-processor';
|
|
6
|
-
import { handler403, handler404, handler500, SimpleStorage } from '../api';
|
|
6
|
+
import { handler403, handler404, handler500, handler503, SimpleStorage } from '../api';
|
|
7
7
|
import { JsonObject, AsyncStorageProps, ServerRequest, SetCookieProps } from '../models';
|
|
8
8
|
import { HostToPath } from './host-to-path';
|
|
9
9
|
import { serializeCookie } from '../lib/utils/cookie-util';
|
|
@@ -15,7 +15,7 @@ export const setMaxRequestSize = (size: number) => {
|
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
// The maximum number of requests being processed. If there are no requests for 10 minutes, this number will be reset to 0.
|
|
18
|
-
let MAX_REQUEST_COUNT =
|
|
18
|
+
let MAX_REQUEST_COUNT = 100;
|
|
19
19
|
let REQUEST_COUNT = 0;
|
|
20
20
|
export const setMaxRequestCount = (count: number) => {
|
|
21
21
|
MAX_REQUEST_COUNT = count;
|
|
@@ -39,17 +39,37 @@ export const setServerName = (serverName: string) => {
|
|
|
39
39
|
SERVER_NAME = serverName;
|
|
40
40
|
};
|
|
41
41
|
|
|
42
|
+
export type RawMiddleware = (req: IncomingMessage, res: ServerResponse, next: () => void) => void;
|
|
43
|
+
|
|
42
44
|
let lastRequestTime = new Date().getTime();
|
|
43
45
|
|
|
44
46
|
// type ProcessRequest = (req: ServerRequest, res: ServerResponse) => void;
|
|
45
47
|
export class WebListener {
|
|
48
|
+
// process requests before business logic, for example IP filter, rate limit, etc.
|
|
49
|
+
rawMiddlewares: RawMiddleware[];
|
|
46
50
|
processor: WebProcessor;
|
|
47
51
|
|
|
48
52
|
constructor(processRequest: WebProcessor) {
|
|
53
|
+
this.rawMiddlewares = [];
|
|
49
54
|
this.processor = processRequest;
|
|
50
55
|
}
|
|
51
56
|
|
|
52
|
-
|
|
57
|
+
addRawMiddlewareChain(middleware: RawMiddleware) {
|
|
58
|
+
this.rawMiddlewares.push(middleware);
|
|
59
|
+
}
|
|
60
|
+
async handleRawMiddlewares(req: IncomingMessage, res: ServerResponse) {
|
|
61
|
+
const runChain = (list: RawMiddleware[], context: { req: IncomingMessage; res: ServerResponse }) => {
|
|
62
|
+
const dispatch = async (i: number) => {
|
|
63
|
+
const fn = list[i];
|
|
64
|
+
if (!fn) return;
|
|
65
|
+
await fn(context.req, context.res, () => dispatch(i + 1));
|
|
66
|
+
};
|
|
67
|
+
return dispatch(0);
|
|
68
|
+
};
|
|
69
|
+
await runChain(this.rawMiddlewares, { req, res });
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async listener(reqOrigin: IncomingMessage, res: ServerResponse) {
|
|
53
73
|
// If there is no request in the last 10 minutes, reset the request count.
|
|
54
74
|
if (new Date().getTime() - lastRequestTime > 1000 * 60 * 10) {
|
|
55
75
|
if (REQUEST_COUNT != 0) {
|
|
@@ -60,9 +80,15 @@ export class WebListener {
|
|
|
60
80
|
lastRequestTime = new Date().getTime();
|
|
61
81
|
}
|
|
62
82
|
|
|
83
|
+
// back-pressure
|
|
63
84
|
if (REQUEST_COUNT > MAX_REQUEST_COUNT) {
|
|
64
85
|
logger.warn(`Too many requests, count: ${REQUEST_COUNT} > ${MAX_REQUEST_COUNT}`);
|
|
65
|
-
|
|
86
|
+
handler503(res, 'Server is busy, please retry later.');
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
await this.handleRawMiddlewares(reqOrigin, res);
|
|
91
|
+
if (res.writableEnded || res.headersSent) {
|
|
66
92
|
return;
|
|
67
93
|
}
|
|
68
94
|
|
|
@@ -78,7 +104,7 @@ export class WebListener {
|
|
|
78
104
|
const msg = `Web root is not defined properly for host: ${host}.`;
|
|
79
105
|
logger.error(msg);
|
|
80
106
|
handler404(res, msg);
|
|
81
|
-
return
|
|
107
|
+
return;
|
|
82
108
|
}
|
|
83
109
|
|
|
84
110
|
REQUEST_COUNT++;
|
package/src/app/web-server.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { WebListener } from './web-listener';
|
|
|
4
4
|
import * as fs from 'fs';
|
|
5
5
|
import * as http from 'http';
|
|
6
6
|
import * as https from 'https';
|
|
7
|
-
import { IncomingMessage } from 'http';
|
|
7
|
+
import { IncomingMessage, ServerResponse } from 'http';
|
|
8
8
|
import { Duplex } from 'stream';
|
|
9
9
|
import { WebProcessor } from './web-processor';
|
|
10
10
|
import { DebugService } from '../api/debug-service';
|
|
@@ -23,13 +23,25 @@ export class WebServer {
|
|
|
23
23
|
DebugService.handleUpgrade(req, socket, head);
|
|
24
24
|
} else {
|
|
25
25
|
logger.error(`Unexpected web socket access: ${req.url} from ${clientIp}`);
|
|
26
|
-
socket.write(
|
|
26
|
+
socket.write('HTTP/1.1 404 Not Found\r\n\r\n');
|
|
27
27
|
socket.destroy();
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
async listenerWrap(reqOrigin: IncomingMessage, res: ServerResponse) {
|
|
32
|
+
try {
|
|
33
|
+
await this.webListener.listener.bind(this.webListener)(reqOrigin, res);
|
|
34
|
+
} catch (err) {
|
|
35
|
+
console.error('Request error:', err);
|
|
36
|
+
if (!res.headersSent) {
|
|
37
|
+
res.statusCode = 500;
|
|
38
|
+
res.end('Internal Server Error');
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
31
43
|
startHttp(httpPort: number, bindIp?: string, timeout?: number) {
|
|
32
|
-
const httpServer = http.createServer(this.
|
|
44
|
+
const httpServer = http.createServer(this.listenerWrap.bind(this));
|
|
33
45
|
if (typeof timeout === 'number') httpServer.setTimeout(timeout);
|
|
34
46
|
|
|
35
47
|
httpServer.on('upgrade', this.handleUpgrade.bind(this));
|
|
@@ -69,7 +81,7 @@ export class WebServer {
|
|
|
69
81
|
);
|
|
70
82
|
}
|
|
71
83
|
|
|
72
|
-
const httpsServer = https.createServer(httpsOptions, this.
|
|
84
|
+
const httpsServer = https.createServer(httpsOptions, this.listenerWrap.bind(this));
|
|
73
85
|
httpsServer.on('upgrade', this.handleUpgrade.bind(this));
|
|
74
86
|
|
|
75
87
|
if (typeof timeout === 'number') {
|
package/src/lib/utils/is-type.ts
CHANGED
|
@@ -17,7 +17,7 @@ export class IsType {
|
|
|
17
17
|
|
|
18
18
|
static isObject(input: any) {
|
|
19
19
|
// null is not object
|
|
20
|
-
return input
|
|
20
|
+
return input !== null && Object.prototype.toString.call(input) === '[object Object]';
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
static isObjectEmpty(obj: any) {
|