@flexent/protocomm-http 2.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 +33 -0
- package/out/main/HttpProtocolHandler.d.ts +15 -0
- package/out/main/HttpProtocolHandler.js +92 -0
- package/out/main/HttpProtocolHandler.js.map +1 -0
- package/out/main/index.d.ts +1 -0
- package/out/main/index.js +2 -0
- package/out/main/index.js.map +1 -0
- package/package.json +26 -0
package/README.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# @flexent/protocomm-http
|
|
2
|
+
|
|
3
|
+
HTTP handler for serving `@flexent/protocomm` protocols through `@flexent/http-server`.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
Extend `HttpProtocolHandler` in request scope and provide a protocol index plus implementation.
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { HttpProtocolHandler } from '@flexent/protocomm-http';
|
|
11
|
+
import { dep } from 'mesh-ioc';
|
|
12
|
+
|
|
13
|
+
import { appProtocol, type AppProtocol } from './protocol.js';
|
|
14
|
+
import { AppProtocolImpl } from './AppProtocolImpl.js';
|
|
15
|
+
|
|
16
|
+
export class AppHttpProtocolHandler extends HttpProtocolHandler<AppProtocol> {
|
|
17
|
+
|
|
18
|
+
@dep() protected appProtocolImpl!: AppProtocolImpl;
|
|
19
|
+
|
|
20
|
+
prefix = '/rpc';
|
|
21
|
+
|
|
22
|
+
get protocol() {
|
|
23
|
+
return appProtocol;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
get protocolImpl() {
|
|
27
|
+
return this.appProtocolImpl;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Query methods are served with `GET`. Command methods are served with `POST`.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type HttpContext, HttpHandler, type HttpNext } from '@flexent/http-server';
|
|
2
|
+
import { type Logger } from '@flexent/logger';
|
|
3
|
+
import { type DomainMethodStat, type ProtocolIndex } from '@flexent/protocomm';
|
|
4
|
+
import { Event } from 'nanoevent';
|
|
5
|
+
export declare abstract class HttpProtocolHandler<P> extends HttpHandler {
|
|
6
|
+
protected logger: Logger;
|
|
7
|
+
methodStats: Event<DomainMethodStat>;
|
|
8
|
+
prefix: string;
|
|
9
|
+
abstract get protocol(): ProtocolIndex<P>;
|
|
10
|
+
abstract get protocolImpl(): P;
|
|
11
|
+
handle(ctx: HttpContext, next: HttpNext): Promise<void>;
|
|
12
|
+
protected handleProtocolMethod(ctx: HttpContext, next: HttpNext, domainName: string, methodName: string): Promise<void>;
|
|
13
|
+
protected parsePath(path: string): [string, string];
|
|
14
|
+
protected parseParams(ctx: HttpContext): Promise<unknown>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
import { NotFoundError } from '@flexent/errors';
|
|
11
|
+
import { HttpHandler } from '@flexent/http-server';
|
|
12
|
+
import { MethodNotFound, } from '@flexent/protocomm';
|
|
13
|
+
import { dep } from 'mesh-ioc';
|
|
14
|
+
import { Event } from 'nanoevent';
|
|
15
|
+
export class HttpProtocolHandler extends HttpHandler {
|
|
16
|
+
constructor() {
|
|
17
|
+
super(...arguments);
|
|
18
|
+
this.methodStats = new Event();
|
|
19
|
+
this.prefix = '';
|
|
20
|
+
}
|
|
21
|
+
async handle(ctx, next) {
|
|
22
|
+
if (!ctx.path.startsWith(this.prefix)) {
|
|
23
|
+
return await next();
|
|
24
|
+
}
|
|
25
|
+
const [domainName, methodName] = this.parsePath(ctx.path);
|
|
26
|
+
const startedAt = Date.now();
|
|
27
|
+
try {
|
|
28
|
+
await this.handleProtocolMethod(ctx, next, domainName, methodName);
|
|
29
|
+
this.methodStats.emit({
|
|
30
|
+
domain: domainName,
|
|
31
|
+
method: methodName,
|
|
32
|
+
latency: Date.now() - startedAt,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
this.methodStats.emit({
|
|
37
|
+
domain: domainName,
|
|
38
|
+
method: methodName,
|
|
39
|
+
latency: Date.now() - startedAt,
|
|
40
|
+
error: error.name,
|
|
41
|
+
});
|
|
42
|
+
throw error;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async handleProtocolMethod(ctx, next, domainName, methodName) {
|
|
46
|
+
const { methodDef, reqSchema, resSchema } = this.protocol.lookupMethod(domainName, methodName);
|
|
47
|
+
const expectedMethod = methodDef.type === 'query' ? 'GET' : 'POST';
|
|
48
|
+
if (ctx.method !== expectedMethod) {
|
|
49
|
+
return await next();
|
|
50
|
+
}
|
|
51
|
+
const params = await this.parseParams(ctx);
|
|
52
|
+
const decodedParams = reqSchema.decode(params, { strictRequired: true });
|
|
53
|
+
this.logger.debug(`>>> ${domainName}.${methodName}`, { params: decodedParams });
|
|
54
|
+
const domainImpl = this.protocolImpl[domainName];
|
|
55
|
+
const methodImpl = domainImpl?.[methodName];
|
|
56
|
+
if (!methodImpl) {
|
|
57
|
+
throw new MethodNotFound(`${domainName}.${methodName}`);
|
|
58
|
+
}
|
|
59
|
+
ctx.state.domainName = domainName;
|
|
60
|
+
ctx.state.methodName = methodName;
|
|
61
|
+
ctx.state.methodDef = methodDef;
|
|
62
|
+
ctx.state.params = decodedParams;
|
|
63
|
+
const res = await methodImpl.call(domainImpl, decodedParams);
|
|
64
|
+
const decodedRes = resSchema.decode(res);
|
|
65
|
+
this.logger.debug(`<<< ${domainName}.${methodName}`, { result: decodedRes });
|
|
66
|
+
ctx.status = 200;
|
|
67
|
+
ctx.responseBody = decodedRes;
|
|
68
|
+
}
|
|
69
|
+
parsePath(path) {
|
|
70
|
+
if (!path.startsWith(this.prefix)) {
|
|
71
|
+
throw new NotFoundError('Endpoint not found');
|
|
72
|
+
}
|
|
73
|
+
const pathWithoutPrefix = path.substring(this.prefix.length);
|
|
74
|
+
const match = /^\/([a-zA-Z0-9]+)\/([a-zA-Z0-9]+)$/.exec(pathWithoutPrefix);
|
|
75
|
+
if (!match) {
|
|
76
|
+
throw new NotFoundError('Endpoint not found');
|
|
77
|
+
}
|
|
78
|
+
return [match[1], match[2]];
|
|
79
|
+
}
|
|
80
|
+
async parseParams(ctx) {
|
|
81
|
+
if (ctx.method === 'GET') {
|
|
82
|
+
return ctx.query;
|
|
83
|
+
}
|
|
84
|
+
const body = await ctx.readRequestBody();
|
|
85
|
+
return body ?? {};
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
__decorate([
|
|
89
|
+
dep(),
|
|
90
|
+
__metadata("design:type", Function)
|
|
91
|
+
], HttpProtocolHandler.prototype, "logger", void 0);
|
|
92
|
+
//# sourceMappingURL=HttpProtocolHandler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HttpProtocolHandler.js","sourceRoot":"","sources":["../../src/main/HttpProtocolHandler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './HttpProtocolHandler.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/main/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@flexent/protocomm-http",
|
|
3
|
+
"version": "2.4.0",
|
|
4
|
+
"description": "HTTP handler for protocomm RPC protocols",
|
|
5
|
+
"author": "Boris Okunskiy",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"sideEffects": false,
|
|
9
|
+
"exports": {
|
|
10
|
+
".": "./out/main/index.js"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"out/main/**/*"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"test": "NODE_ENV=test mocha"
|
|
17
|
+
},
|
|
18
|
+
"peerDependencies": {
|
|
19
|
+
"@flexent/errors": "^2.4.0",
|
|
20
|
+
"@flexent/http-server": "^2.4.0",
|
|
21
|
+
"@flexent/logger": "^2.4.0",
|
|
22
|
+
"@flexent/protocomm": "^2.4.0",
|
|
23
|
+
"mesh-ioc": "^4.3.1",
|
|
24
|
+
"nanoevent": "^1.0.0"
|
|
25
|
+
}
|
|
26
|
+
}
|