objective-http 1.2.6 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +71 -43
- package/package.json +1 -1
- package/src/js/bun/Bunttp.js +2 -5
- package/src/js/bun/client/index.js +2 -2
- package/src/js/bun/client/request/index.js +3 -0
- package/src/js/bun/client/response/index.js +3 -0
- package/src/js/bun/server/index.js +2 -2
- package/src/js/bun/server/request/InputRequest.js +5 -6
- package/src/js/bun/server/request/index.js +3 -0
- package/src/js/bun/server/response/OutputResponse.js +1 -25
- package/src/js/bun/server/response/index.js +3 -0
- package/src/js/client/index.js +2 -2
- package/src/js/client/request/OutputRequest.js +25 -41
- package/src/js/client/request/index.js +3 -0
- package/src/js/client/response/InputResponse.js +23 -21
- package/src/js/client/response/index.js +3 -0
- package/src/js/index.js +1 -5
- package/src/js/server/LoggedServer.js +6 -7
- package/src/js/server/Server.js +30 -26
- package/src/js/server/endpoint/Endpoint.js +7 -14
- package/src/js/server/endpoint/Endpoints.js +25 -15
- package/src/js/server/endpoint/index.js +4 -0
- package/src/js/server/index.js +3 -9
- package/src/js/server/request/InputRequest.js +23 -24
- package/src/js/server/request/LoggedInputRequest.js +13 -13
- package/src/js/server/request/index.js +4 -0
- package/src/js/server/response/LoggedOutputResponse.js +0 -4
- package/src/js/server/response/OutputResponse.js +1 -25
- package/src/js/server/response/index.js +4 -0
- package/src/js/server/endpoint/LoggedEndpoint.js +0 -30
- package/src/js/server/request/JsonInputRequest.js +0 -51
- package/src/js/server/response/JsonOutputResponse.js +0 -45
package/README.md
CHANGED
|
@@ -13,27 +13,30 @@ const createServerFunction = require('node:http').createServer;
|
|
|
13
13
|
const {
|
|
14
14
|
Server,
|
|
15
15
|
LoggedServer,
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
16
|
+
endpoint: {
|
|
17
|
+
Endpoint,
|
|
18
|
+
Endpoints
|
|
19
|
+
},
|
|
20
|
+
request: {
|
|
21
|
+
InputRequest,
|
|
22
|
+
LoggedInputRequest,
|
|
23
|
+
},
|
|
24
|
+
response: {
|
|
25
|
+
OutputResponse,
|
|
26
|
+
LoggedOutputResponse
|
|
27
|
+
}
|
|
25
28
|
} = require('objective-http').server;
|
|
26
29
|
|
|
27
30
|
new LoggedServer(
|
|
28
31
|
new Server(
|
|
29
32
|
new Endpoints([
|
|
30
|
-
new MyFirstEndpoint(
|
|
31
|
-
new MySecondEndpoint(
|
|
32
|
-
new MyThirdEndpoint(
|
|
33
|
+
new MyFirstEndpoint(),
|
|
34
|
+
new MySecondEndpoint(),
|
|
35
|
+
new MyThirdEndpoint()
|
|
33
36
|
]),
|
|
34
37
|
{port: server_port},
|
|
35
|
-
new LoggedInputRequest(new
|
|
36
|
-
new LoggedOutputResponse(new
|
|
38
|
+
new LoggedInputRequest(new InputRequest(), console),
|
|
39
|
+
new LoggedOutputResponse(new OutputResponse(), console),
|
|
37
40
|
createServerFunction
|
|
38
41
|
),
|
|
39
42
|
console
|
|
@@ -77,8 +80,12 @@ class MyEndpoint {
|
|
|
77
80
|
const requestFunction = require('node:http').request;
|
|
78
81
|
|
|
79
82
|
const {
|
|
80
|
-
|
|
81
|
-
|
|
83
|
+
request: {
|
|
84
|
+
OutputRequest
|
|
85
|
+
},
|
|
86
|
+
response: {
|
|
87
|
+
InputResponse
|
|
88
|
+
}
|
|
82
89
|
} = require('objective-http').client;
|
|
83
90
|
|
|
84
91
|
|
|
@@ -127,27 +134,30 @@ const createServerFunction = require('node:http').createServer;
|
|
|
127
134
|
const {
|
|
128
135
|
Server,
|
|
129
136
|
LoggedServer,
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
137
|
+
endpoint: {
|
|
138
|
+
Endpoint,
|
|
139
|
+
Endpoints
|
|
140
|
+
},
|
|
141
|
+
request: {
|
|
142
|
+
InputRequest,
|
|
143
|
+
LoggedInputRequest,
|
|
144
|
+
},
|
|
145
|
+
response: {
|
|
146
|
+
OutputResponse,
|
|
147
|
+
LoggedOutputResponse
|
|
148
|
+
}
|
|
139
149
|
} = require('objective-http').server;
|
|
140
150
|
|
|
141
151
|
new LoggedServer(
|
|
142
152
|
new Server(
|
|
143
153
|
new Endpoints([
|
|
144
|
-
new MyFirstEndpoint(
|
|
145
|
-
new MySecondEndpoint(
|
|
146
|
-
new MyThirdEndpoint(
|
|
154
|
+
new MyFirstEndpoint(),
|
|
155
|
+
new MySecondEndpoint(),
|
|
156
|
+
new MyThirdEndpoint()
|
|
147
157
|
]),
|
|
148
158
|
{port: server_port},
|
|
149
|
-
new LoggedInputRequest(new
|
|
150
|
-
new LoggedOutputResponse(new
|
|
159
|
+
new LoggedInputRequest(new InputRequest(), console),
|
|
160
|
+
new LoggedOutputResponse(new OutputResponse(), console),
|
|
151
161
|
createServerFunction
|
|
152
162
|
),
|
|
153
163
|
console
|
|
@@ -163,15 +173,25 @@ const createServerFunction = require('objective-http').bun.bunttp.createServer;
|
|
|
163
173
|
const {
|
|
164
174
|
Server,
|
|
165
175
|
LoggedServer,
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
176
|
+
endpoint: {
|
|
177
|
+
Endpoint,
|
|
178
|
+
Endpoints
|
|
179
|
+
},
|
|
180
|
+
request: {
|
|
181
|
+
LoggedInputRequest,
|
|
182
|
+
},
|
|
183
|
+
response: {
|
|
184
|
+
LoggedOutputResponse
|
|
185
|
+
}
|
|
186
|
+
} = require('objective-http').server;
|
|
187
|
+
|
|
188
|
+
const {
|
|
189
|
+
request: {
|
|
190
|
+
InputRequest
|
|
191
|
+
},
|
|
192
|
+
response: {
|
|
193
|
+
OutputResponse
|
|
194
|
+
}
|
|
175
195
|
} = require('objective-http').bun.server;
|
|
176
196
|
```
|
|
177
197
|
|
|
@@ -184,8 +204,12 @@ It should work with `node` and `bun`:
|
|
|
184
204
|
const requestFunction = require('node:http').request;
|
|
185
205
|
|
|
186
206
|
const {
|
|
187
|
-
|
|
188
|
-
|
|
207
|
+
request: {
|
|
208
|
+
OutputRequest
|
|
209
|
+
},
|
|
210
|
+
response: {
|
|
211
|
+
InputResponse
|
|
212
|
+
}
|
|
189
213
|
} = require('objective-http').client;
|
|
190
214
|
|
|
191
215
|
await (new OutputRequest(new InputResponse(), requestFunction)
|
|
@@ -203,7 +227,11 @@ In order for the code to be executed only by `bun`, you need to make changes to
|
|
|
203
227
|
const requestFunction = require('objective-http').bun.bunttp.request;
|
|
204
228
|
|
|
205
229
|
const {
|
|
206
|
-
|
|
207
|
-
|
|
230
|
+
request: {
|
|
231
|
+
OutputRequest
|
|
232
|
+
},
|
|
233
|
+
response: {
|
|
234
|
+
InputResponse
|
|
235
|
+
}
|
|
208
236
|
} = require('objective-http').bun.client;
|
|
209
237
|
```
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"objective-http","version":"1.
|
|
1
|
+
{"name":"objective-http","version":"1.4.0","description":"Proxy classes for creating a http server","keywords":["web","web-server","http","http-server","oop"],"author":{"name":"volatilization","email":"volatilization@yandex.ru"},"repository":{"url":"git+https://github.com/volatilization/objective-http.git"},"license":"LGPL-3.0-only","main":"src/js/index.js"}
|
package/src/js/bun/Bunttp.js
CHANGED
|
@@ -12,16 +12,13 @@ module.exports = class Bunttp {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
listen(options, cb) {
|
|
15
|
-
|
|
16
|
-
const server = Bun.serve(config);
|
|
15
|
+
this.#server = Bun.serve({...this.#serverConfig, port: options.port});
|
|
17
16
|
cb();
|
|
18
|
-
return new Bunttp(config, server);
|
|
19
17
|
}
|
|
20
18
|
|
|
21
19
|
close(cb) {
|
|
22
|
-
|
|
20
|
+
this.#server.stop();
|
|
23
21
|
cb();
|
|
24
|
-
return new Bunttp(this.#serverConfig, server);
|
|
25
22
|
}
|
|
26
23
|
|
|
27
24
|
request = fetch;
|
|
@@ -16,8 +16,10 @@ module.exports = class InputRequest {
|
|
|
16
16
|
return new InputRequest(
|
|
17
17
|
this.#inputStream,
|
|
18
18
|
{
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
route: {
|
|
20
|
+
method: this.#inputStream.method,
|
|
21
|
+
path: new URL(this.#inputStream.url).pathname
|
|
22
|
+
},
|
|
21
23
|
query: new URL(this.#inputStream.url).searchParams,
|
|
22
24
|
headers: this.#inputStream.headers,
|
|
23
25
|
body: Buffer.from(await (await this.#inputStream.blob()).arrayBuffer())
|
|
@@ -30,10 +32,7 @@ module.exports = class InputRequest {
|
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
route() {
|
|
33
|
-
return
|
|
34
|
-
method: this.#options.method.toString().toUpperCase(),
|
|
35
|
-
path: this.#options.path.toString().toLowerCase()
|
|
36
|
-
}
|
|
35
|
+
return this.#options.route;
|
|
37
36
|
}
|
|
38
37
|
|
|
39
38
|
query() {
|
|
@@ -7,14 +7,10 @@ module.exports = class OutputResponse {
|
|
|
7
7
|
this.#outputStream = outputStream;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
copy(
|
|
10
|
+
copy(outputStream = this.#outputStream, options = this.#options) {
|
|
11
11
|
return new OutputResponse({...{statusCode: 200, headers: {}}, ...options}, outputStream);
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
update(options) {
|
|
15
|
-
return new OutputResponse(this.#mergeOptions(this.#options, options), this.#outputStream);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
14
|
flush() {
|
|
19
15
|
try {
|
|
20
16
|
return new Response(this.#options.body, {
|
|
@@ -26,24 +22,4 @@ module.exports = class OutputResponse {
|
|
|
26
22
|
throw new Error(e.message, {cause: 'INVALID_RESPONSE'});
|
|
27
23
|
}
|
|
28
24
|
}
|
|
29
|
-
|
|
30
|
-
#mergeOptions(existedOptions, newOptions) {
|
|
31
|
-
if (newOptions == null) {
|
|
32
|
-
return existedOptions;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
if (newOptions.statusCode != null) {
|
|
36
|
-
existedOptions.statusCode = newOptions.statusCode;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (newOptions.body != null) {
|
|
40
|
-
existedOptions.body = newOptions.body;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
if (newOptions.headers != null) {
|
|
44
|
-
existedOptions.headers = {...existedOptions.headers, ...newOptions.headers};
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return existedOptions;
|
|
48
|
-
}
|
|
49
25
|
}
|
package/src/js/client/index.js
CHANGED
|
@@ -10,56 +10,40 @@ module.exports = class OutputRequest {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
copy(options = this.#options, response = this.#response, http = this.#requestFunction) {
|
|
13
|
-
return new OutputRequest(response, http,
|
|
13
|
+
return new OutputRequest(response, http, {method: 'GET', ...options});
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
send() {
|
|
17
17
|
return new Promise((resolve, reject) => {
|
|
18
18
|
try {
|
|
19
|
-
this.#
|
|
20
|
-
this.#
|
|
21
|
-
this.#options
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
await this.#flushResponseInputStream(responseInputStream, response, resolve, reject);
|
|
19
|
+
const requestOutputStream = this.#requestFunction(
|
|
20
|
+
this.#options.url,
|
|
21
|
+
this.#options,
|
|
22
|
+
async (responseInputStream) => {
|
|
23
|
+
try {
|
|
24
|
+
resolve(await this.#response
|
|
25
|
+
.copy(responseInputStream)
|
|
26
|
+
.flush());
|
|
27
|
+
|
|
28
|
+
} catch (e) {
|
|
29
|
+
reject(new Error(e.message, {cause: 'INVALID_REQUEST'}));
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
requestOutputStream.once('error', e => {
|
|
34
|
+
reject(new Error(e.message, {cause: 'INVALID_REQUEST'}));
|
|
36
35
|
});
|
|
37
|
-
}
|
|
38
36
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
await this.#flushResponseInputStream(responseInputStream, response, resolve, reject);
|
|
43
|
-
});
|
|
44
|
-
}
|
|
37
|
+
if (this.#needToByWritten(this.#options)) {
|
|
38
|
+
requestOutputStream.write(this.#options.body);
|
|
39
|
+
}
|
|
45
40
|
|
|
46
|
-
|
|
47
|
-
try {
|
|
48
|
-
resolve(await response
|
|
49
|
-
.copy(responseInputStream)
|
|
50
|
-
.flush());
|
|
41
|
+
requestOutputStream.end();
|
|
51
42
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
#sendRequestOutputStream(requestOutputStream, options) {
|
|
58
|
-
if (this.#needToByWritten(options)) {
|
|
59
|
-
requestOutputStream.write(options.body);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
requestOutputStream.end();
|
|
43
|
+
} catch (e) {
|
|
44
|
+
reject(new Error(e.message, {cause: 'INVALID_REQUEST'}));
|
|
45
|
+
}
|
|
46
|
+
});
|
|
63
47
|
}
|
|
64
48
|
|
|
65
49
|
#needToByWritten(options) {
|
|
@@ -11,27 +11,6 @@ module.exports = class InputResponse {
|
|
|
11
11
|
return new InputResponse(inputStream, options);
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
flush() {
|
|
15
|
-
return new Promise((resolve, reject) => {
|
|
16
|
-
try {
|
|
17
|
-
this.#inputStream.once('error', (e) => reject(new Error(e.message, {cause: 'INVALID_RESPONSE'})));
|
|
18
|
-
|
|
19
|
-
let chunks = [];
|
|
20
|
-
this.#inputStream.on('data', (chunk) => chunks.push(chunk));
|
|
21
|
-
this.#inputStream.on('end', () => resolve(new InputResponse(this.#inputStream,
|
|
22
|
-
{
|
|
23
|
-
statusCode: this.#inputStream.statusCode,
|
|
24
|
-
headers: new Headers(this.#inputStream.headers),
|
|
25
|
-
body: Buffer.concat(chunks)
|
|
26
|
-
}
|
|
27
|
-
)));
|
|
28
|
-
|
|
29
|
-
} catch (e) {
|
|
30
|
-
throw new Error(e.message, {cause: 'INVALID_RESPONSE'});
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
|
|
35
14
|
statusCode() {
|
|
36
15
|
return this.#options.statusCode;
|
|
37
16
|
}
|
|
@@ -43,4 +22,27 @@ module.exports = class InputResponse {
|
|
|
43
22
|
body() {
|
|
44
23
|
return this.#options.body;
|
|
45
24
|
}
|
|
25
|
+
|
|
26
|
+
async flush() {
|
|
27
|
+
return await new Promise((resolve, reject) => {
|
|
28
|
+
try {
|
|
29
|
+
this.#inputStream.once('error', (e) => reject(new Error(e.message, {cause: 'INVALID_RESPONSE'})));
|
|
30
|
+
|
|
31
|
+
let chunks = [];
|
|
32
|
+
this.#inputStream.on('data', (chunk) => chunks.push(chunk));
|
|
33
|
+
this.#inputStream.on('end', () => resolve(
|
|
34
|
+
new InputResponse(
|
|
35
|
+
this.#inputStream,
|
|
36
|
+
{
|
|
37
|
+
statusCode: this.#inputStream.statusCode,
|
|
38
|
+
headers: new Headers(this.#inputStream.headers),
|
|
39
|
+
body: Buffer.concat(chunks)
|
|
40
|
+
}
|
|
41
|
+
)
|
|
42
|
+
));
|
|
43
|
+
} catch (e) {
|
|
44
|
+
throw new Error(e.message, {cause: 'INVALID_RESPONSE'});
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
46
48
|
};
|
package/src/js/index.js
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
module.exports = {
|
|
2
2
|
server: require('./server'),
|
|
3
3
|
client: require('./client'),
|
|
4
|
-
bun:
|
|
5
|
-
server: {...require('./server'), ...require('./bun').server},
|
|
6
|
-
client: {...require('./client'), ...require('./bun').client},
|
|
7
|
-
bunttp: require('./bun').bunttp
|
|
8
|
-
}
|
|
4
|
+
bun: require('./bun')
|
|
9
5
|
}
|
|
@@ -7,10 +7,14 @@ module.exports = class LoggedServer {
|
|
|
7
7
|
this.#logger = logger;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
options() {
|
|
11
|
+
return this.#origin.options();
|
|
12
|
+
}
|
|
13
|
+
|
|
10
14
|
async start() {
|
|
11
15
|
const server = await this.#origin.start();
|
|
12
16
|
|
|
13
|
-
this.#logger.debug(`HttpServer is running at port: ${
|
|
17
|
+
this.#logger.debug(`HttpServer is running at port: ${this.#origin.options().port}`);
|
|
14
18
|
|
|
15
19
|
return new LoggedServer(server, this.#logger);
|
|
16
20
|
}
|
|
@@ -18,13 +22,8 @@ module.exports = class LoggedServer {
|
|
|
18
22
|
async stop() {
|
|
19
23
|
const server = await this.#origin.stop();
|
|
20
24
|
|
|
21
|
-
this.#logger.debug(`HttpServer at port: ${
|
|
25
|
+
this.#logger.debug(`HttpServer at port: ${this.#origin.options().port} is stopped`);
|
|
22
26
|
|
|
23
27
|
return new LoggedServer(server, this.#logger);
|
|
24
28
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
options() {
|
|
28
|
-
return this.#origin.options();
|
|
29
|
-
}
|
|
30
29
|
}
|
package/src/js/server/Server.js
CHANGED
|
@@ -15,31 +15,39 @@ module.exports = class Server {
|
|
|
15
15
|
this.#server = server;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
options() {
|
|
19
|
+
return this.#options;
|
|
20
|
+
}
|
|
21
|
+
|
|
18
22
|
start() {
|
|
19
23
|
const server = this.#createServerFunction(async (requestStream, responseStream) => {
|
|
20
24
|
try {
|
|
21
|
-
return await
|
|
22
|
-
.copy(
|
|
23
|
-
|
|
24
|
-
.
|
|
25
|
-
|
|
25
|
+
return await this.#response
|
|
26
|
+
.copy(responseStream,
|
|
27
|
+
await this.#endpoints
|
|
28
|
+
.handle(await this.#request
|
|
29
|
+
.copy(requestStream)
|
|
30
|
+
.flush()))
|
|
26
31
|
.flush();
|
|
27
32
|
|
|
28
33
|
} catch (e) {
|
|
29
34
|
if (e.cause === 'INVALID_REQUEST') {
|
|
30
35
|
return this.#response
|
|
31
|
-
.copy(
|
|
32
|
-
statusCode: 400,
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
.copy(responseStream,
|
|
37
|
+
{statusCode: 400, body: e.message})
|
|
38
|
+
.flush();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (e.cause === 'HANDLER_NOT_FOUND') {
|
|
42
|
+
return this.#response
|
|
43
|
+
.copy(responseStream,
|
|
44
|
+
{statusCode: 501, body: e.message})
|
|
35
45
|
.flush();
|
|
36
46
|
}
|
|
37
47
|
|
|
38
48
|
return this.#response
|
|
39
|
-
.copy(
|
|
40
|
-
statusCode: 500,
|
|
41
|
-
body: 'Unexpected server error.'
|
|
42
|
-
}, responseStream)
|
|
49
|
+
.copy(responseStream,
|
|
50
|
+
{statusCode: 500, body: 'Unexpected server error.'})
|
|
43
51
|
.flush();
|
|
44
52
|
}
|
|
45
53
|
});
|
|
@@ -47,21 +55,21 @@ module.exports = class Server {
|
|
|
47
55
|
return new Promise(resolve => {
|
|
48
56
|
server.listen(
|
|
49
57
|
this.#options,
|
|
50
|
-
() =>
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
server));
|
|
58
|
-
}
|
|
58
|
+
() => resolve(new Server(
|
|
59
|
+
this.#endpoints,
|
|
60
|
+
this.#options,
|
|
61
|
+
this.#request,
|
|
62
|
+
this.#response,
|
|
63
|
+
this.#createServerFunction,
|
|
64
|
+
server))
|
|
59
65
|
);
|
|
60
66
|
});
|
|
61
67
|
}
|
|
62
68
|
|
|
63
69
|
stop() {
|
|
64
70
|
return new Promise(resolve => {
|
|
71
|
+
console.log('stop ', this.#server);
|
|
72
|
+
|
|
65
73
|
this.#server.close(
|
|
66
74
|
() => resolve(new Server(
|
|
67
75
|
this.#endpoints,
|
|
@@ -72,8 +80,4 @@ module.exports = class Server {
|
|
|
72
80
|
);
|
|
73
81
|
});
|
|
74
82
|
}
|
|
75
|
-
|
|
76
|
-
options() {
|
|
77
|
-
return this.#options;
|
|
78
|
-
}
|
|
79
83
|
};
|
|
@@ -1,24 +1,17 @@
|
|
|
1
1
|
module.exports = class Endpoint {
|
|
2
|
-
#
|
|
3
|
-
#path;
|
|
2
|
+
#route;
|
|
4
3
|
|
|
5
|
-
constructor(
|
|
6
|
-
this.#
|
|
7
|
-
this.#path = path;
|
|
4
|
+
constructor(route) {
|
|
5
|
+
this.#route = route;
|
|
8
6
|
}
|
|
9
7
|
|
|
10
|
-
|
|
11
|
-
return
|
|
8
|
+
route() {
|
|
9
|
+
return this.#route;
|
|
12
10
|
}
|
|
13
11
|
|
|
14
|
-
|
|
12
|
+
async handle() {
|
|
15
13
|
return {
|
|
16
|
-
|
|
17
|
-
path: this.#path.toString().toLowerCase()
|
|
14
|
+
statusCode: 200
|
|
18
15
|
};
|
|
19
16
|
}
|
|
20
|
-
|
|
21
|
-
async handle(request) {
|
|
22
|
-
return {};
|
|
23
|
-
}
|
|
24
17
|
}
|
|
@@ -1,26 +1,36 @@
|
|
|
1
1
|
module.exports = class Endpoints {
|
|
2
|
-
#
|
|
2
|
+
#map;
|
|
3
3
|
|
|
4
|
-
constructor(collection = []) {
|
|
5
|
-
this.#
|
|
4
|
+
constructor(collection = [], map = new Map()) {
|
|
5
|
+
this.#map = map;
|
|
6
|
+
collection.forEach((endpoint) => {
|
|
7
|
+
if (!this.#map.has(endpoint.route().path.toString().toLowerCase())) {
|
|
8
|
+
this.#map.set(endpoint.route().path.toString().toLowerCase(), new Map());
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
this.#map
|
|
12
|
+
.get(endpoint.route().path.toString().toLowerCase())
|
|
13
|
+
.set(endpoint.route().method.toString().toUpperCase(),
|
|
14
|
+
endpoint);
|
|
15
|
+
});
|
|
6
16
|
}
|
|
7
17
|
|
|
8
|
-
copy(collection = this.#
|
|
9
|
-
return new Endpoints(collection);
|
|
18
|
+
copy(collection, map = this.#map) {
|
|
19
|
+
return new Endpoints(collection, map);
|
|
10
20
|
}
|
|
11
21
|
|
|
12
22
|
async handle(request) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
statusCode: 501,
|
|
20
|
-
body: 'There are no handler for request.'
|
|
21
|
-
}
|
|
23
|
+
if (!this.#map.has(request.route().path.toString().toLowerCase())
|
|
24
|
+
|| !this.#map
|
|
25
|
+
.get(request.route().path.toString().toLowerCase())
|
|
26
|
+
.has(request.route().method.toString().toUpperCase())
|
|
27
|
+
) {
|
|
28
|
+
throw new Error('There are no handler for request.', {cause: 'HANDLER_NOT_FOUND'});
|
|
22
29
|
}
|
|
23
30
|
|
|
24
|
-
return await
|
|
31
|
+
return await this.#map
|
|
32
|
+
.get(request.route().path.toString().toLowerCase())
|
|
33
|
+
.get(request.route().method.toString().toUpperCase())
|
|
34
|
+
.handle(request);
|
|
25
35
|
}
|
|
26
36
|
}
|
package/src/js/server/index.js
CHANGED
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
module.exports = {
|
|
2
2
|
Server: require('./Server'),
|
|
3
3
|
LoggedServer: require('./LoggedServer'),
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
InputRequest: require('./request/InputRequest'),
|
|
8
|
-
JsonInputRequest: require('./request/JsonInputRequest'),
|
|
9
|
-
LoggedInputRequest: require('./request/LoggedInputRequest'),
|
|
10
|
-
OutputResponse: require('./response/OutputResponse'),
|
|
11
|
-
JsonOutputResponse: require('./response/JsonOutputResponse'),
|
|
12
|
-
LoggedOutputResponse: require('./response/LoggedOutputResponse')
|
|
4
|
+
endpoint: require('./endpoint'),
|
|
5
|
+
request: require('./request'),
|
|
6
|
+
response: require('./response')
|
|
13
7
|
}
|
|
@@ -11,6 +11,22 @@ module.exports = class InputRequest {
|
|
|
11
11
|
return new InputRequest(inputStream, options);
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
route() {
|
|
15
|
+
return this.#options.route;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
query() {
|
|
19
|
+
return this.#options.query;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
body() {
|
|
23
|
+
return this.#options.body;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
headers() {
|
|
27
|
+
return this.#options.headers;
|
|
28
|
+
}
|
|
29
|
+
|
|
14
30
|
flush() {
|
|
15
31
|
return new Promise((resolve, reject) => {
|
|
16
32
|
try {
|
|
@@ -23,36 +39,19 @@ module.exports = class InputRequest {
|
|
|
23
39
|
this.#inputStream.on('end', () => resolve(new InputRequest(
|
|
24
40
|
this.#inputStream,
|
|
25
41
|
{
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
42
|
+
route: {
|
|
43
|
+
method: this.#inputStream.method,
|
|
44
|
+
path: new URL(this.#inputStream.url, 'http://url').pathname
|
|
45
|
+
},
|
|
46
|
+
query: new URL(this.#inputStream.url, 'http://url').searchParams,
|
|
29
47
|
headers: new Headers(this.#inputStream.headers),
|
|
30
|
-
body: Buffer.concat(chunks)
|
|
48
|
+
body: Buffer.concat(chunks),
|
|
31
49
|
}
|
|
32
50
|
)));
|
|
33
51
|
|
|
34
52
|
} catch (e) {
|
|
35
|
-
|
|
53
|
+
reject(new Error(e.message, {cause: 'INVALID_REQUEST'}));
|
|
36
54
|
}
|
|
37
55
|
});
|
|
38
56
|
}
|
|
39
|
-
|
|
40
|
-
route() {
|
|
41
|
-
return {
|
|
42
|
-
method: this.#options.method.toString().toUpperCase(),
|
|
43
|
-
path: this.#options.path.toString().toLowerCase()
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
query() {
|
|
48
|
-
return this.#options.query;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
body() {
|
|
52
|
-
return this.#options.body;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
headers() {
|
|
56
|
-
return this.#options.headers;
|
|
57
|
-
}
|
|
58
57
|
};
|
|
@@ -13,19 +13,6 @@ module.exports = class LoggedInputRequest {
|
|
|
13
13
|
return new LoggedInputRequest(origin, logger, inputStream);
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
async flush() {
|
|
17
|
-
this.#logger.debug(`HttpRequest: [${this.#inputStream.method}] ${this.#inputStream.url} ${JSON.stringify(this.#inputStream.headers)}`);
|
|
18
|
-
|
|
19
|
-
try {
|
|
20
|
-
return new LoggedInputRequest(await this.#origin.flush(), this.#logger);
|
|
21
|
-
|
|
22
|
-
} catch (e) {
|
|
23
|
-
this.#logger.error(`HttpRequest: [${this.#inputStream.method}] ${this.#inputStream.url} error: ${e.message}`, e);
|
|
24
|
-
|
|
25
|
-
throw e;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
16
|
route() {
|
|
30
17
|
return this.#origin.route();
|
|
31
18
|
}
|
|
@@ -41,4 +28,17 @@ module.exports = class LoggedInputRequest {
|
|
|
41
28
|
headers() {
|
|
42
29
|
return this.#origin.headers();
|
|
43
30
|
}
|
|
31
|
+
|
|
32
|
+
async flush() {
|
|
33
|
+
this.#logger.debug(`HttpRequest: [${this.#inputStream.method}] ${this.#inputStream.url} ${JSON.stringify(this.#inputStream.headers)}`);
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
return new LoggedInputRequest(await this.#origin.flush(), this.#logger);
|
|
37
|
+
|
|
38
|
+
} catch (e) {
|
|
39
|
+
this.#logger.error(`HttpRequest: [${this.#inputStream.method}] ${this.#inputStream.url} error: ${e.message}`, e);
|
|
40
|
+
|
|
41
|
+
throw e;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
44
|
}
|
|
@@ -11,10 +11,6 @@ module.exports = class LoggedOutputResponse {
|
|
|
11
11
|
return new LoggedOutputResponse(origin, logger);
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
update(options) {
|
|
15
|
-
return new LoggedOutputResponse(this.#origin.update(options), this.#logger);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
14
|
flush() {
|
|
19
15
|
const outputStream = this.#loggedFlush();
|
|
20
16
|
|
|
@@ -7,14 +7,10 @@ module.exports = class OutputResponse {
|
|
|
7
7
|
this.#outputStream = outputStream;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
copy(
|
|
10
|
+
copy(outputStream = this.#outputStream, options = this.#options) {
|
|
11
11
|
return new OutputResponse({...{statusCode: 200, headers: {}}, ...options}, outputStream);
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
update(options) {
|
|
15
|
-
return new OutputResponse(this.#mergeOptions(this.#options, options), this.#outputStream);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
14
|
flush() {
|
|
19
15
|
try {
|
|
20
16
|
this.#outputStream.writeHead(this.#options.statusCode, this.#options.headers)
|
|
@@ -29,24 +25,4 @@ module.exports = class OutputResponse {
|
|
|
29
25
|
this.#outputStream.end();
|
|
30
26
|
}
|
|
31
27
|
}
|
|
32
|
-
|
|
33
|
-
#mergeOptions(existedOptions, newOptions) {
|
|
34
|
-
if (newOptions == null) {
|
|
35
|
-
return existedOptions;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (newOptions.statusCode != null) {
|
|
39
|
-
existedOptions.statusCode = newOptions.statusCode;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (newOptions.body != null) {
|
|
43
|
-
existedOptions.body = newOptions.body;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (newOptions.headers != null) {
|
|
47
|
-
existedOptions.headers = {...existedOptions.headers, ...newOptions.headers};
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return existedOptions;
|
|
51
|
-
}
|
|
52
28
|
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
module.exports = class LoggedEndpoint {
|
|
2
|
-
#origin;
|
|
3
|
-
#logger;
|
|
4
|
-
|
|
5
|
-
constructor(origin, logger) {
|
|
6
|
-
this.#origin = origin;
|
|
7
|
-
this.#logger = logger;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
copy(method, path, logger = this.#logger, origin = this.#origin.copy(method, path)) {
|
|
11
|
-
return new LoggedEndpoint(origin, logger);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
route() {
|
|
15
|
-
return this.#origin.route();
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
async handle(request) {
|
|
19
|
-
this.#logger.debug(`HttpEndpoint's handling [${request.route().method}] ${request.route().path}`);
|
|
20
|
-
|
|
21
|
-
try {
|
|
22
|
-
return await this.#origin.handle(request);
|
|
23
|
-
|
|
24
|
-
} catch (e) {
|
|
25
|
-
this.#logger.error(`HttpEndpoint's handling [${request.route().method}] ${request.route().path} error: ${e.message}`, e);
|
|
26
|
-
|
|
27
|
-
throw e;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
module.exports = class JsonInputRequest {
|
|
2
|
-
#origin;
|
|
3
|
-
#inputStream;
|
|
4
|
-
|
|
5
|
-
constructor(origin, inputStream) {
|
|
6
|
-
this.#origin = origin;
|
|
7
|
-
this.#inputStream = inputStream;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
copy(inputStream, options, origin = this.#origin.copy(inputStream, options)) {
|
|
11
|
-
return new JsonInputRequest(origin, inputStream);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
async flush() {
|
|
15
|
-
if (this.#useChunkMethod(this.#inputStream.method) && !this.#validHeaders(new Headers(this.#inputStream.headers))) {
|
|
16
|
-
throw new Error('Wrong content-type. Only application/json accepted.', {cause: 'INVALID_REQUEST'});
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
return new JsonInputRequest(await this.#origin.flush(), this.#inputStream);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
route() {
|
|
23
|
-
return this.#origin.route();
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
query() {
|
|
27
|
-
return this.#origin.query();
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
body() {
|
|
31
|
-
try {
|
|
32
|
-
return JSON.parse(this.#origin.body().toString());
|
|
33
|
-
|
|
34
|
-
} catch (e) {
|
|
35
|
-
throw new Error('Wrong body format. Only JSON accepted.', {cause: 'INVALID_REQUEST'});
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
headers() {
|
|
40
|
-
return this.#origin.headers();
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
#useChunkMethod(requestMethod) {
|
|
44
|
-
return ['POST', 'PUT'].some(method => method === requestMethod.toString().toUpperCase())
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
#validHeaders(requestHeaders) {
|
|
48
|
-
return new Headers(requestHeaders).has('content-type')
|
|
49
|
-
&& new RegExp('^application\/json').test(new Headers(requestHeaders).get('content-type'));
|
|
50
|
-
}
|
|
51
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
module.exports = class JsonOutputResponse {
|
|
2
|
-
#origin;
|
|
3
|
-
#options;
|
|
4
|
-
|
|
5
|
-
constructor(origin, options) {
|
|
6
|
-
this.#origin = origin;
|
|
7
|
-
this.#options = options;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
copy(options, outputStream, origin = this.#origin.copy(options, outputStream)) {
|
|
11
|
-
return new JsonOutputResponse(origin, options);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
update(options) {
|
|
15
|
-
return new JsonOutputResponse(this.#origin.update(options));
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
flush() {
|
|
19
|
-
const body = this.#options.body;
|
|
20
|
-
|
|
21
|
-
if (body == null) {
|
|
22
|
-
return this.#origin.flush();
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
if (typeof body === 'string') {
|
|
26
|
-
try {
|
|
27
|
-
JSON.parse(body);
|
|
28
|
-
|
|
29
|
-
return this.#origin
|
|
30
|
-
.update({headers: {'Content-Type': 'application/json; charset=utf-8'}})
|
|
31
|
-
.flush();
|
|
32
|
-
|
|
33
|
-
} catch (e) {
|
|
34
|
-
return this.#origin.flush();
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return this.#origin
|
|
39
|
-
.update({
|
|
40
|
-
headers: {'Content-Type': 'application/json; charset=utf-8'},
|
|
41
|
-
body: JSON.stringify(body)
|
|
42
|
-
})
|
|
43
|
-
.flush();
|
|
44
|
-
}
|
|
45
|
-
};
|