objective-http 1.0.4 → 1.1.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 +40 -1
- package/package.json +1 -1
- package/src/js/client/request/OutputRequest.js +69 -0
- package/src/js/client/response/InputResponse.js +43 -0
- package/src/js/index.js +4 -0
- package/src/js/server/request/InputRequest.js +13 -27
- package/src/js/server/request/JsonInputRequest.js +0 -4
package/README.md
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
# objective-http
|
|
2
2
|
Proxy classes for creating a http server
|
|
3
3
|
|
|
4
|
-
##
|
|
4
|
+
## Server
|
|
5
|
+
|
|
6
|
+
There are all ```Server``` classes feature.
|
|
7
|
+
Your endpoints must implement ```Endpoint``` class interface (```route``` and ```handle``` functions).
|
|
5
8
|
|
|
6
9
|
``` javascript
|
|
7
10
|
const http = require('node:http');
|
|
@@ -40,4 +43,40 @@ new ClusteredServer(
|
|
|
40
43
|
cluster,
|
|
41
44
|
{workers: workers_count}
|
|
42
45
|
).start();
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Client
|
|
49
|
+
|
|
50
|
+
``` javascript
|
|
51
|
+
const https = require('node:https');
|
|
52
|
+
const {
|
|
53
|
+
OutputRequest,
|
|
54
|
+
InputResponse
|
|
55
|
+
} = require('objective-http').client;
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
const response = await new OutputRequest(
|
|
59
|
+
https,
|
|
60
|
+
new InputResponse(),
|
|
61
|
+
{
|
|
62
|
+
url: 'https://example.com',
|
|
63
|
+
method: 'POST',
|
|
64
|
+
body: 'test body'
|
|
65
|
+
})
|
|
66
|
+
.send();
|
|
67
|
+
|
|
68
|
+
console.log(response.body().toString());
|
|
69
|
+
|
|
70
|
+
//or
|
|
71
|
+
|
|
72
|
+
const request = new OutputRequest(https, new InputResponse());
|
|
73
|
+
|
|
74
|
+
const otherResponse = await (request
|
|
75
|
+
.copy({
|
|
76
|
+
url: 'https://example.com',
|
|
77
|
+
method: 'POST',
|
|
78
|
+
body: 'test body'
|
|
79
|
+
}))
|
|
80
|
+
.send()
|
|
81
|
+
|
|
43
82
|
```
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"objective-http","version":"1.0
|
|
1
|
+
{"name":"objective-http","version":"1.1.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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
module.exports = class OutputRequest {
|
|
2
|
+
#http;
|
|
3
|
+
#response;
|
|
4
|
+
#options;
|
|
5
|
+
|
|
6
|
+
constructor(http, response, options) {
|
|
7
|
+
this.#http = http;
|
|
8
|
+
this.#response = response;
|
|
9
|
+
this.#options = {method: 'GET', ...options};
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
copy(options = this.#options, response = this.#response, http = this.#http) {
|
|
13
|
+
return new OutputRequest(http, response, {method: 'GET', ...options});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
send() {
|
|
17
|
+
return new Promise((resolve, reject) => {
|
|
18
|
+
try {
|
|
19
|
+
this.#sendRequestOutputStream(
|
|
20
|
+
this.#configureRequestOutputStream(this.#http, this.#response, this.#options, resolve, reject),
|
|
21
|
+
this.#options);
|
|
22
|
+
|
|
23
|
+
} catch (e) {
|
|
24
|
+
throw new Error(e.message, {cause: 'INVALID_REQUEST'});
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
#configureRequestOutputStream(http, response, options, resolve, reject) {
|
|
30
|
+
if (options.url != null) {
|
|
31
|
+
return http.request(
|
|
32
|
+
options.url,
|
|
33
|
+
options,
|
|
34
|
+
async (responseInputStream) => {
|
|
35
|
+
await this.#flushResponseInputStream(responseInputStream, response, resolve, reject);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return http.request(
|
|
40
|
+
options,
|
|
41
|
+
async (responseInputStream) => {
|
|
42
|
+
await this.#flushResponseInputStream(responseInputStream, response, resolve, reject);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async #flushResponseInputStream(responseInputStream, response, resolve, reject) {
|
|
47
|
+
try {
|
|
48
|
+
resolve(await response
|
|
49
|
+
.copy(responseInputStream)
|
|
50
|
+
.flush());
|
|
51
|
+
|
|
52
|
+
} catch (e) {
|
|
53
|
+
reject(e);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
#sendRequestOutputStream(requestOutputStream, options) {
|
|
58
|
+
if (this.#needToByWritten(options)) {
|
|
59
|
+
requestOutputStream.write(options.body);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
requestOutputStream.end();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
#needToByWritten(options) {
|
|
66
|
+
return ['POST', 'PUT'].some(method => method === options.method.toString().toUpperCase())
|
|
67
|
+
&& (options.body != null && typeof options.body === 'string');
|
|
68
|
+
}
|
|
69
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module.exports = class InputResponse {
|
|
2
|
+
#inputStream;
|
|
3
|
+
#options;
|
|
4
|
+
|
|
5
|
+
constructor(inputStream, options) {
|
|
6
|
+
this.#inputStream = inputStream;
|
|
7
|
+
this.#options = options;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
copy(inputStream = this.#inputStream, options = this.#options) {
|
|
11
|
+
return new InputResponse(inputStream, options);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
flush() {
|
|
15
|
+
return new Promise((resolve, reject) => {
|
|
16
|
+
this.#inputStream.once('error', (e) =>
|
|
17
|
+
reject(new Error(e.message, {cause: 'INVALID_RESPONSE'}))
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
let chunks = [];
|
|
21
|
+
this.#inputStream.on('data', (chunk) => chunks.push(chunk));
|
|
22
|
+
this.#inputStream.on('end', () => resolve(new InputResponse(this.#inputStream,
|
|
23
|
+
{
|
|
24
|
+
statusCode: this.#inputStream.statusCode,
|
|
25
|
+
headers: new Headers(this.#inputStream.headers),
|
|
26
|
+
body: Buffer.concat(chunks)
|
|
27
|
+
}
|
|
28
|
+
)));
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
statusCode() {
|
|
33
|
+
return this.#options.statusCode;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
headers() {
|
|
37
|
+
return this.#options.headers;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
body() {
|
|
41
|
+
return this.#options.body;
|
|
42
|
+
}
|
|
43
|
+
};
|
package/src/js/index.js
CHANGED
|
@@ -12,5 +12,9 @@ module.exports = {
|
|
|
12
12
|
OutputResponse: require('./server/response/OutputResponse'),
|
|
13
13
|
JsonOutputResponse: require('./server/response/JsonOutputResponse'),
|
|
14
14
|
LoggedOutputResponse: require('./server/response/LoggedOutputResponse')
|
|
15
|
+
},
|
|
16
|
+
client: {
|
|
17
|
+
OutputRequest: require('./client/request/OutputRequest'),
|
|
18
|
+
InputResponse: require('./client/response/InputResponse')
|
|
15
19
|
}
|
|
16
20
|
}
|
|
@@ -12,23 +12,22 @@ module.exports = class InputRequest {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
flush() {
|
|
15
|
-
return new Promise((resolve) => {
|
|
16
|
-
this.#inputStream.once('error', (e) =>
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
if (!this.#isChunkedInputStream(this.#inputStream)) {
|
|
21
|
-
return resolve(new InputRequest(
|
|
22
|
-
this.#inputStream,
|
|
23
|
-
this.#extractOptionsFromInputStream(this.#inputStream)
|
|
24
|
-
));
|
|
25
|
-
}
|
|
15
|
+
return new Promise((resolve, reject) => {
|
|
16
|
+
this.#inputStream.once('error', (e) =>
|
|
17
|
+
reject(new Error(e.message, {cause: 'INVALID_REQUEST'}))
|
|
18
|
+
);
|
|
26
19
|
|
|
27
20
|
let chunks = [];
|
|
28
21
|
this.#inputStream.on('data', (chunk) => chunks.push(chunk));
|
|
29
22
|
this.#inputStream.on('end', () => resolve(new InputRequest(
|
|
30
23
|
this.#inputStream,
|
|
31
|
-
{
|
|
24
|
+
{
|
|
25
|
+
method: this.#inputStream.method,
|
|
26
|
+
path: new URL(this.#inputStream.url, 'http://dummy').pathname,
|
|
27
|
+
query: new URL(this.#inputStream.url, 'http://dummy').searchParams,
|
|
28
|
+
headers: new Headers(this.#inputStream.headers),
|
|
29
|
+
body: Buffer.concat(chunks)
|
|
30
|
+
}
|
|
32
31
|
)));
|
|
33
32
|
});
|
|
34
33
|
}
|
|
@@ -37,7 +36,7 @@ module.exports = class InputRequest {
|
|
|
37
36
|
return {
|
|
38
37
|
method: this.#options.method.toString().toUpperCase(),
|
|
39
38
|
path: this.#options.path.toString().toLowerCase()
|
|
40
|
-
}
|
|
39
|
+
};
|
|
41
40
|
}
|
|
42
41
|
|
|
43
42
|
query() {
|
|
@@ -51,17 +50,4 @@ module.exports = class InputRequest {
|
|
|
51
50
|
headers() {
|
|
52
51
|
return this.#options.headers;
|
|
53
52
|
}
|
|
54
|
-
|
|
55
|
-
#isChunkedInputStream(inputStream) {
|
|
56
|
-
return ['POST', 'PUT'].some(method => method === inputStream.method.toString().toUpperCase());
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
#extractOptionsFromInputStream(inputStream) {
|
|
60
|
-
return {
|
|
61
|
-
method: inputStream.method,
|
|
62
|
-
path: new URL(inputStream.url, 'http://dummy').pathname,
|
|
63
|
-
query: new URL(inputStream.url, 'http://dummy').searchParams,
|
|
64
|
-
headers: inputStream.headers
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
}
|
|
53
|
+
};
|