oox 0.3.0-beta9 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +29 -32
- package/app.js +131 -143
- package/bin/argv.js +63 -70
- package/bin/cli.js +57 -43
- package/bin/configurer.js +60 -62
- package/bin/loader.mjs +392 -279
- package/bin/proxy-import.js +12 -0
- package/bin/proxy-require.js +88 -0
- package/bin/register.js +46 -55
- package/bin/starter.js +63 -66
- package/index.js +155 -168
- package/index.mjs +4 -4
- package/logger.js +25 -40
- package/modules/http/index.js +234 -192
- package/modules/http/utils.js +74 -73
- package/modules/index.js +86 -88
- package/modules/module.js +11 -16
- package/modules/socketio/client.js +97 -101
- package/modules/socketio/index.js +171 -168
- package/modules/socketio/server.js +188 -136
- package/modules/socketio/socket.js +1 -4
- package/package.json +14 -12
- package/types/app.d.ts +50 -51
- package/types/bin/argv.d.ts +8 -8
- package/types/bin/cli.d.ts +6 -2
- package/types/bin/configurer.d.ts +3 -1
- package/types/bin/proxy-import.d.ts +4 -0
- package/types/bin/proxy-require.d.ts +5 -0
- package/types/bin/register.d.ts +1 -1
- package/types/bin/starter.d.ts +5 -1
- package/types/index.d.ts +78 -76
- package/types/logger.d.ts +5 -4
- package/types/modules/http/index.d.ts +58 -47
- package/types/modules/http/utils.d.ts +14 -17
- package/types/modules/index.d.ts +24 -24
- package/types/modules/module.d.ts +11 -13
- package/types/modules/socketio/client.d.ts +23 -23
- package/types/modules/socketio/index.d.ts +37 -37
- package/types/modules/socketio/server.d.ts +44 -35
- package/types/modules/socketio/socket.d.ts +11 -11
- package/types/utils.d.ts +6 -6
- package/utils.js +57 -63
- package/bin/proxyer.js +0 -61
- package/types/bin/proxyer.d.ts +0 -1
package/modules/http/index.js
CHANGED
|
@@ -1,192 +1,234 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
this.config
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
*
|
|
49
|
-
*/
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
1
|
+
import * as http from 'node:http';
|
|
2
|
+
import * as https from 'node:https';
|
|
3
|
+
import { httpRequest, parseHTTPBody } from './utils.js';
|
|
4
|
+
import * as oox from '../../index.js';
|
|
5
|
+
import Module, { ModuleConfig } from '../module.js';
|
|
6
|
+
export class HTTPConfig extends ModuleConfig {
|
|
7
|
+
// listen port
|
|
8
|
+
port = 0;
|
|
9
|
+
// service path
|
|
10
|
+
path = '/';
|
|
11
|
+
// browser cross origin
|
|
12
|
+
origin = '';
|
|
13
|
+
// https options
|
|
14
|
+
ssl = {
|
|
15
|
+
// enable https
|
|
16
|
+
enabled: false,
|
|
17
|
+
// ssl certificate path
|
|
18
|
+
cert: '',
|
|
19
|
+
// ssl private key path
|
|
20
|
+
key: '',
|
|
21
|
+
// ssl ca path
|
|
22
|
+
ca: ''
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export default class HTTPModule extends Module {
|
|
26
|
+
name = 'http';
|
|
27
|
+
config = new HTTPConfig;
|
|
28
|
+
server = null;
|
|
29
|
+
getUrl() {
|
|
30
|
+
const { host } = oox.config;
|
|
31
|
+
const { port, path } = this.config;
|
|
32
|
+
const protocol = this.config.ssl.enabled ? `https:` : `http:`;
|
|
33
|
+
return `${protocol}//${host}:${port}${path}`;
|
|
34
|
+
}
|
|
35
|
+
setConfig(config) {
|
|
36
|
+
Object.assign(this.config, config);
|
|
37
|
+
if (!config.hasOwnProperty('port')) {
|
|
38
|
+
this.config.port = oox.config.port;
|
|
39
|
+
}
|
|
40
|
+
if (!config.hasOwnProperty('origin')) {
|
|
41
|
+
this.config.origin = oox.config.origin;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
getConfig() {
|
|
45
|
+
return this.config;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* start http service
|
|
49
|
+
*/
|
|
50
|
+
async serve() {
|
|
51
|
+
await this.stop();
|
|
52
|
+
const { port, ssl } = this.config;
|
|
53
|
+
if (ssl.enabled) {
|
|
54
|
+
const fs = await import('node:fs');
|
|
55
|
+
const options = {
|
|
56
|
+
key: ssl.key ? fs.readFileSync(ssl.key) : null,
|
|
57
|
+
cert: ssl.cert ? fs.readFileSync(ssl.cert) : null,
|
|
58
|
+
ca: ssl.ca ? fs.readFileSync(ssl.ca) : null
|
|
59
|
+
};
|
|
60
|
+
if (!options.key || !options.cert) {
|
|
61
|
+
throw new Error('HTTPS enabled but missing key or cert');
|
|
62
|
+
}
|
|
63
|
+
this.server = https.createServer(options, this.requestHandler.bind(this));
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
this.server = http.createServer(this.requestHandler.bind(this));
|
|
67
|
+
}
|
|
68
|
+
this.server.listen(port);
|
|
69
|
+
const address = this.server.address();
|
|
70
|
+
if (!address || 'object' !== typeof address)
|
|
71
|
+
throw new Error('Cannot read http server port');
|
|
72
|
+
this.config.port = address.port;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* stop http service
|
|
76
|
+
*/
|
|
77
|
+
stop() {
|
|
78
|
+
if (this.server && this.server.listening)
|
|
79
|
+
return new Promise((resolve, reject) => {
|
|
80
|
+
this.server.close(function (error) {
|
|
81
|
+
if (error)
|
|
82
|
+
reject(error);
|
|
83
|
+
else
|
|
84
|
+
resolve();
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* browser cross origin
|
|
90
|
+
*/
|
|
91
|
+
cors(request, response) {
|
|
92
|
+
// origin checking
|
|
93
|
+
const origin = this.config.origin;
|
|
94
|
+
const requestOrigin = request.headers.origin;
|
|
95
|
+
if (origin && requestOrigin) {
|
|
96
|
+
if (origin === '*' ||
|
|
97
|
+
origin === requestOrigin ||
|
|
98
|
+
(Array.isArray(origin) && origin.includes(requestOrigin))) {
|
|
99
|
+
response.setHeader('Access-Control-Allow-Origin', requestOrigin);
|
|
100
|
+
response.setHeader('Vary', 'Origin');
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
response.statusCode = 403;
|
|
104
|
+
response.end();
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
response.setHeader('Access-Control-Max-Age', 3600);
|
|
108
|
+
response.setHeader('Access-Control-Allow-Headers', 'x-caller,content-type');
|
|
109
|
+
response.setHeader('Access-Control-Allow-Methods', '*');
|
|
110
|
+
}
|
|
111
|
+
if (request.method === 'OPTIONS') {
|
|
112
|
+
response.statusCode = 204;
|
|
113
|
+
response.end();
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
async requestHandler(request, response) {
|
|
119
|
+
if (!this.cors(request, response))
|
|
120
|
+
return;
|
|
121
|
+
const url = new URL(request.url, request.headers.origin || 'http://localhost');
|
|
122
|
+
if (url.pathname === this.config.path) {
|
|
123
|
+
await this.call(request, response);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
const error = new Error('Invalid URL');
|
|
127
|
+
return this.respond(request, response, {
|
|
128
|
+
success: false,
|
|
129
|
+
error: {
|
|
130
|
+
message: error.message,
|
|
131
|
+
stack: error.stack
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* 从请求里获取调用的接口和参数
|
|
138
|
+
*/
|
|
139
|
+
async getCallArgsFromRequest(request) {
|
|
140
|
+
const args = await parseHTTPBody(request);
|
|
141
|
+
if (!args || 'object' !== typeof args || !args.action)
|
|
142
|
+
throw new Error('Content Invalid');
|
|
143
|
+
else
|
|
144
|
+
return args;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* HTTP-RPC服务器请求监听方法
|
|
148
|
+
*/
|
|
149
|
+
async call(request, response) {
|
|
150
|
+
let callArgs = Object.create(null);
|
|
151
|
+
try {
|
|
152
|
+
callArgs = await this.getCallArgsFromRequest(request);
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
return this.respond(request, response, {
|
|
156
|
+
success: false,
|
|
157
|
+
error: {
|
|
158
|
+
message: error.message,
|
|
159
|
+
stack: error.stack
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
// global unique id
|
|
164
|
+
const traceId = String(request.headers['x-trace-id'] || '');
|
|
165
|
+
// service name, required
|
|
166
|
+
const caller = String(request.headers['x-caller'] || 'anonymous');
|
|
167
|
+
// client ip or caller service ip
|
|
168
|
+
const ip = String(request.headers['x-ip'] || request.socket.remoteAddress || '');
|
|
169
|
+
// startup client ip
|
|
170
|
+
const sourceIP = String(request.headers['x-real-ip'] || '');
|
|
171
|
+
const { action, params = [] } = callArgs;
|
|
172
|
+
const context = oox.genContext({ traceId, caller, sourceIP, ip, callerId: '' });
|
|
173
|
+
const format = await oox.call(action, params, context);
|
|
174
|
+
this.respond(request, response, format);
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* HTTP Response Catch
|
|
178
|
+
*/
|
|
179
|
+
respond(_request, response, returns) {
|
|
180
|
+
let returnsString = '';
|
|
181
|
+
if (!oox.config.errorStack && returns.error) {
|
|
182
|
+
// 不返回错误调用栈信息
|
|
183
|
+
delete returns.error.stack;
|
|
184
|
+
}
|
|
185
|
+
try {
|
|
186
|
+
returnsString = JSON.stringify(returns);
|
|
187
|
+
}
|
|
188
|
+
catch ({ message, stack }) {
|
|
189
|
+
delete returns.body;
|
|
190
|
+
returns.success = false;
|
|
191
|
+
returns.error = {
|
|
192
|
+
message
|
|
193
|
+
};
|
|
194
|
+
if (oox.config.errorStack) {
|
|
195
|
+
// 返回错误调用栈信息
|
|
196
|
+
returns.error.stack = stack;
|
|
197
|
+
}
|
|
198
|
+
returnsString = JSON.stringify(returns);
|
|
199
|
+
}
|
|
200
|
+
response.setHeader('Content-Type', 'application/json');
|
|
201
|
+
response.setHeader('Content-Length', Buffer.byteLength(returnsString));
|
|
202
|
+
response.end(returnsString);
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* HTTP RPC
|
|
206
|
+
*/
|
|
207
|
+
async rpc(url, action, params, context) {
|
|
208
|
+
if (!context || !context.traceId) {
|
|
209
|
+
context = oox.getContext();
|
|
210
|
+
}
|
|
211
|
+
const { traceId, caller, sourceIP } = context;
|
|
212
|
+
const headers = {
|
|
213
|
+
'Content-Type': 'application/json',
|
|
214
|
+
'x-trace-id': String(traceId),
|
|
215
|
+
};
|
|
216
|
+
if (caller)
|
|
217
|
+
headers['x-caller'] = String(caller);
|
|
218
|
+
if (sourceIP)
|
|
219
|
+
headers['x-real-ip'] = sourceIP;
|
|
220
|
+
// headers [ 'x-ip' ] = getIPAddress ( 4 ) [ 0 ]
|
|
221
|
+
const format = await httpRequest(url, {
|
|
222
|
+
headers
|
|
223
|
+
}, JSON.stringify({ action, params }));
|
|
224
|
+
if ('string' === typeof format)
|
|
225
|
+
throw new Error(format);
|
|
226
|
+
const { success, error, body } = format;
|
|
227
|
+
if (success)
|
|
228
|
+
return body;
|
|
229
|
+
else if (error)
|
|
230
|
+
throw new Error(error.message);
|
|
231
|
+
else
|
|
232
|
+
throw new Error('[RPC] Unknown Error');
|
|
233
|
+
}
|
|
234
|
+
}
|
package/modules/http/utils.js
CHANGED
|
@@ -1,73 +1,74 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
if (
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
function
|
|
52
|
-
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
request.
|
|
68
|
-
request.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
1
|
+
import * as http from 'node:http';
|
|
2
|
+
import * as https from 'node:https';
|
|
3
|
+
/**
|
|
4
|
+
* Stream => Buffer
|
|
5
|
+
*/
|
|
6
|
+
export function stream2buffer(stream, totalLength = 0) {
|
|
7
|
+
return new Promise(function (resolve, reject) {
|
|
8
|
+
let buffers = [];
|
|
9
|
+
stream.on('error', reject);
|
|
10
|
+
if (totalLength) {
|
|
11
|
+
stream.on('data', function (data) { buffers.push(data); });
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
stream.on('data', function (data) {
|
|
15
|
+
buffers.push(data);
|
|
16
|
+
totalLength += data.length;
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
stream.on('end', function () { resolve(Buffer.concat(buffers, totalLength)); });
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Request => JSONObject
|
|
24
|
+
*/
|
|
25
|
+
export async function parseHTTPBody(request) {
|
|
26
|
+
if (request.method === 'GET')
|
|
27
|
+
return null;
|
|
28
|
+
let contentType = request.headers['content-type'];
|
|
29
|
+
// application/json; charset=utf-8
|
|
30
|
+
if (contentType)
|
|
31
|
+
contentType = contentType.split(';')[0].trim();
|
|
32
|
+
const contentSize = request.headers['content-length'];
|
|
33
|
+
if (!contentSize)
|
|
34
|
+
return null;
|
|
35
|
+
const buffer = await stream2buffer(request, +contentSize || 0);
|
|
36
|
+
if (contentSize && buffer.length !== +contentSize)
|
|
37
|
+
throw new Error('Content-Length Incorrect');
|
|
38
|
+
switch (contentType) {
|
|
39
|
+
case 'application/json':
|
|
40
|
+
return JSON.parse(buffer.toString());
|
|
41
|
+
case 'text/plain':
|
|
42
|
+
return buffer.toString();
|
|
43
|
+
default:
|
|
44
|
+
return buffer;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* http request
|
|
49
|
+
*/
|
|
50
|
+
export function httpRequest(url, options, body) {
|
|
51
|
+
return new Promise(function (resolve, reject) {
|
|
52
|
+
const urlObj = typeof url === 'string' ? new URL(url) : url;
|
|
53
|
+
const protocol = urlObj.protocol;
|
|
54
|
+
const client = protocol === 'https:' ? https : http;
|
|
55
|
+
const request = client.request(url, options, async function (response) {
|
|
56
|
+
try {
|
|
57
|
+
const result = await parseHTTPBody(response);
|
|
58
|
+
resolve(result);
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
const decoration = new Error(`${response.statusCode} - ${error.message}`);
|
|
62
|
+
reject(decoration);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
request.on('error', reject);
|
|
66
|
+
if (body) {
|
|
67
|
+
request.method = 'POST';
|
|
68
|
+
request.setHeader('Content-Type', 'application/json');
|
|
69
|
+
request.setHeader('Content-Length', Buffer.byteLength(body));
|
|
70
|
+
request.write(body);
|
|
71
|
+
}
|
|
72
|
+
request.end();
|
|
73
|
+
});
|
|
74
|
+
}
|