Skip to content

Commit 2c1d933

Browse files
author
k8w
committedJun 8, 2022
## [3.4.0-beta.1] - 2022-06-08
### Added - Support using the same name with API and message
1 parent b48fde6 commit 2c1d933

12 files changed

+314
-218
lines changed
 

‎.mocharc.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,14 @@ module.exports = {
1515
'./test/cases/inner.test.ts',
1616
'./test/cases/inputJSON.test.ts',
1717
'./test/cases/inputBuffer.test.ts',
18+
'./test/cases/https.test.ts',
19+
'./test/cases/httpsJSON.test.ts',
20+
'./test/cases/wss.test.ts',
21+
'./test/cases/wssJSON.test.ts',
22+
1823
],
1924
// parallel: false,
2025

2126
// 'expose-gc': true,
22-
// fgrep: 'ObjectId'
27+
// fgrep: 'Same-name'
2328
}

‎CHANGELOG.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
# CHANGELOG
22

3-
## [3.4.0-beta.0] - 2022-06-08
3+
## [3.4.0-beta.1] - 2022-06-08
44
### Added
55
- `https` options for `HttpServer`
66
- `wss` options for `WsServer`
7+
- Support using the same name with API and message
78

89
## [3.3.3] - 2022-06-07
910
### Fixed

‎package-lock.json

+69-177
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "tsrpc",
3-
"version": "3.4.0-beta.0",
3+
"version": "3.4.0-beta.1",
44
"description": "A TypeScript RPC Framework, with runtime type checking and built-in serialization, support both HTTP and WebSocket.",
55
"main": "index.js",
66
"exports": {
@@ -38,8 +38,8 @@
3838
"author": "k8w",
3939
"license": "MIT",
4040
"devDependencies": {
41-
"@microsoft/api-documenter": "^7.17.16",
42-
"@microsoft/api-extractor": "^7.24.2",
41+
"@microsoft/api-documenter": "^7.17.17",
42+
"@microsoft/api-extractor": "^7.25.0",
4343
"@types/chai": "^4.3.1",
4444
"@types/mocha": "^8.2.3",
4545
"@types/node": "^15.14.9",
@@ -48,16 +48,16 @@
4848
"mocha": "^9.2.2",
4949
"mongodb": "^4.7.0",
5050
"nyc": "^15.1.0",
51-
"rollup": "^2.75.5",
52-
"rollup-plugin-typescript2": "^0.31.2",
53-
"ts-node": "^10.8.0",
54-
"typescript": "^4.7.2"
51+
"rollup": "^2.75.6",
52+
"rollup-plugin-typescript2": "^0.32.1",
53+
"ts-node": "^10.8.1",
54+
"typescript": "^4.7.3"
5555
},
5656
"dependencies": {
5757
"@types/ws": "^7.4.7",
5858
"chalk": "^4.1.2",
5959
"tsbuffer": "^2.2.3",
60-
"tsrpc-base-client": "^2.0.6",
60+
"tsrpc-base-client": "^2.0.8-dev.1",
6161
"tsrpc-proto": "^1.4.2",
6262
"uuid": "^8.3.2",
6363
"ws": "^7.5.8"

‎src/server/base/BaseServer.ts

+48-22
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import chalk from "chalk";
22
import * as path from "path";
33
import { TSBuffer } from 'tsbuffer';
4-
import { ApiService, Counter, Flow, getCustomObjectIdTypes, MsgHandlerManager, MsgService, ParsedServerInput, ServiceMap, ServiceMapUtil, TransportDataUtil } from 'tsrpc-base-client';
4+
import { Counter, Flow, getCustomObjectIdTypes, MsgHandlerManager, MsgService, ParsedServerInput, ServiceMap, ServiceMapUtil, TransportDataUtil } from 'tsrpc-base-client';
55
import { ApiReturn, ApiServiceDef, BaseServiceType, Logger, LogLevel, ServerInputData, ServiceProto, setLogLevel, TsrpcError, TsrpcErrorType } from 'tsrpc-proto';
66
import { getClassObjectId } from "../../models/getClassObjectId";
77
import { ApiCallInner } from "../inner/ApiCallInner";
@@ -59,7 +59,16 @@ export abstract class BaseServer<ServiceType extends BaseServiceType = BaseServi
5959
* Before processing the received data, usually be used to encryption / decryption.
6060
* Return `null | undefined` would ignore the buffer.
6161
*/
62-
preRecvDataFlow: new Flow<{ conn: BaseConnection<ServiceType>, data: string | Uint8Array | object, serviceName?: string }>(),
62+
preRecvDataFlow: new Flow<{
63+
conn: BaseConnection<ServiceType>,
64+
data: string | Uint8Array | object,
65+
/**
66+
* @deprecated use `serviceId` instead
67+
*/
68+
serviceName?: string,
69+
/** Parsed service id */
70+
serviceId?: number
71+
}>(),
6372
/**
6473
* Before send out data to network, usually be used to encryption / decryption.
6574
* Return `null | undefined` would not send the buffer.
@@ -221,7 +230,7 @@ export abstract class BaseServer<ServiceType extends BaseServiceType = BaseServi
221230
/**
222231
* Process the buffer, after the `preRecvBufferFlow`.
223232
*/
224-
async _onRecvData(conn: BaseConnection<ServiceType>, data: string | Uint8Array | object, serviceName?: string) {
233+
async _onRecvData(conn: BaseConnection<ServiceType>, data: string | Uint8Array | object, serviceId?: number) {
225234
// 非 OPENED 状态 停止接受新的请求
226235
if (!(conn instanceof InnerConnection) && this.status !== ServerStatus.Opened) {
227236
return;
@@ -246,13 +255,28 @@ export abstract class BaseServer<ServiceType extends BaseServiceType = BaseServi
246255
return;
247256
}
248257

249-
let pre = await this.flows.preRecvDataFlow.exec({ conn: conn, data: data, serviceName: serviceName }, conn.logger);
258+
// Pre flow
259+
const preServiceName = serviceId ? this.serviceMap.id2Service[serviceId].name : undefined;
260+
let pre = await this.flows.preRecvDataFlow.exec({
261+
conn: conn,
262+
data: data,
263+
serviceId: serviceId,
264+
serviceName: preServiceName
265+
}, conn.logger);
250266
if (!pre) {
251267
conn.logger.debug('[preRecvDataFlow] Canceled');
252268
return;
253269
}
254270
data = pre.data;
255-
serviceName = pre.serviceName;
271+
272+
// Pre flow res
273+
if (pre.serviceId !== serviceId) {
274+
serviceId = pre.serviceId
275+
}
276+
// @deprecated 兼容
277+
else if (pre.serviceName && pre.serviceName !== preServiceName) {
278+
serviceId = this.serviceMap.apiName2Service[pre.serviceName]?.id;
279+
}
256280

257281
// @deprecated preRecvBuffer
258282
if (data instanceof Uint8Array) {
@@ -265,7 +289,7 @@ export abstract class BaseServer<ServiceType extends BaseServiceType = BaseServi
265289
}
266290

267291
// Parse Call
268-
let opInput = this._parseServerInput(this.tsbuffer, this.serviceMap, data, serviceName);
292+
let opInput = this._parseServerInput(this.tsbuffer, this.serviceMap, data, serviceId);
269293
if (!opInput.isSucc) {
270294
this.onInputDataError(opInput.errMsg, conn, data);
271295
return;
@@ -668,7 +692,8 @@ export abstract class BaseServer<ServiceType extends BaseServiceType = BaseServi
668692
if (apiName.startsWith('/')) {
669693
apiName = apiName.slice(1);
670694
}
671-
if (!this.serviceMap.apiName2Service[apiName]) {
695+
const service = this.serviceMap.apiName2Service[apiName];
696+
if (!service) {
672697
return {
673698
isSucc: false,
674699
err: new TsrpcError(`Invalid service name: ${apiName}`, {
@@ -690,7 +715,7 @@ export abstract class BaseServer<ServiceType extends BaseServiceType = BaseServi
690715
}
691716
});
692717

693-
this._onRecvData(conn, req, apiName);
718+
this._onRecvData(conn, req, service.id);
694719
})
695720
}
696721

@@ -716,7 +741,7 @@ export abstract class BaseServer<ServiceType extends BaseServiceType = BaseServi
716741
})
717742
}
718743

719-
protected _parseServerInput(tsbuffer: TSBuffer, serviceMap: ServiceMap, data: string | Uint8Array | object, serviceName?: string): { isSucc: true, result: ParsedServerInput } | { isSucc: false, errMsg: string } {
744+
protected _parseServerInput(tsbuffer: TSBuffer, serviceMap: ServiceMap, data: string | Uint8Array | object, serviceId?: number): { isSucc: true, result: ParsedServerInput } | { isSucc: false, errMsg: string } {
720745
if (data instanceof Uint8Array) {
721746
let opServerInputData = TransportDataUtil.tsbuffer.decode(data, 'ServerInputData');
722747

@@ -773,31 +798,32 @@ export abstract class BaseServer<ServiceType extends BaseServiceType = BaseServi
773798
let body: any;
774799
let sn: number | undefined;
775800

776-
// Parse serviceName / body / sn
777-
let service: ApiService | MsgService | undefined;
778-
const oriServiceName = serviceName;
779-
if (serviceName == undefined) {
801+
// Parse serviceId / body / sn
802+
if (serviceId == undefined) {
803+
// 未指定 serviceId,必然是长连接,传入格式应该是数组 [serviceName, body, sn?]
780804
if (!Array.isArray(json)) {
781805
return { isSucc: false, errMsg: `Invalid request format: unresolved service name.` };
782806
}
783-
serviceName = json[0] as string;
807+
784808
body = json[1];
785809
sn = json[2];
810+
811+
// Parse serviceId
812+
const serviceName = json[0] as string;
813+
const isMsg = sn === undefined;
814+
serviceId = (isMsg ? serviceMap.msgName2Service : serviceMap.apiName2Service)[serviceName]?.id;
815+
if (serviceId === undefined) {
816+
return { isSucc: false, errMsg: `Invalid ${isMsg ? 'msg' : 'api'} path: ${serviceName}` };
817+
}
786818
}
787819
else {
788820
body = json;
789821
}
790822

791823
// Get Service
792-
service = serviceMap.apiName2Service[serviceName] ?? serviceMap.msgName2Service[serviceName];
824+
let service = serviceMap.id2Service[serviceId];
793825
if (!service) {
794-
let errMsg = `Invalid service name: ${chalk.cyan.underline(serviceName)}`;
795-
796-
// 可能是 JSON 模式下,jsonHostPath 未设置妥当的原因,此时给予友好提示
797-
if (oriServiceName) {
798-
// TODO
799-
}
800-
826+
let errMsg = `Invalid service id: ${chalk.cyan.underline(serviceId)}`;
801827
return { isSucc: false, errMsg: errMsg };
802828
}
803829

‎src/server/http/HttpServer.ts

+18-2
Original file line numberDiff line numberDiff line change
@@ -89,12 +89,28 @@ export class HttpServer<ServiceType extends BaseServiceType = any> extends BaseS
8989
let url = conn.httpReq.url!;
9090

9191
let urlEndPos = url.indexOf('?');
92+
let isMsg: boolean = false;
9293
if (urlEndPos > -1) {
93-
url = url.slice(0, urlEndPos);
94+
isMsg = url.slice(urlEndPos + 1).split('&').some(v => v === 'type=msg');
95+
url = url.slice(0, urlEndPos);
9496
}
9597

98+
// Parse serviceId
9699
let serviceName = url.slice(this.options.jsonHostPath.length);
97-
this._onRecvData(conn, buf.toString(), serviceName);
100+
let serviceId: number | undefined;
101+
if (isMsg) {
102+
serviceId = this.serviceMap.msgName2Service[serviceName]?.id;
103+
}
104+
else {
105+
serviceId = this.serviceMap.apiName2Service[serviceName]?.id
106+
}
107+
108+
const data = buf.toString();
109+
if (serviceId === undefined) {
110+
this.onInputDataError(`Invalid ${isMsg ? 'msg' : 'api'} path: ${serviceName}`, conn, data)
111+
return;
112+
}
113+
this._onRecvData(conn, data, serviceId);
98114
}
99115
else {
100116
this._onRecvData(conn, buf);

‎test/cases/http.test.ts

+33-1
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ describe('HTTP Server & Client basic', function () {
196196
logger: serverLogger,
197197
// debugBuf: true
198198
});
199-
199+
await server.autoImplementApi(path.resolve(__dirname, '../api'))
200200
await server.start();
201201

202202
let client = new HttpClient(getProto(), {
@@ -223,6 +223,38 @@ describe('HTTP Server & Client basic', function () {
223223
})
224224
})
225225

226+
it('Same-name msg and api', async function () {
227+
let server = new HttpServer(getProto(), {
228+
port: 3001,
229+
logger: serverLogger,
230+
debugBuf: true
231+
});
232+
233+
await server.autoImplementApi(path.resolve(__dirname, '../api'))
234+
await server.start();
235+
236+
let client = new HttpClient(getProto(), {
237+
server: 'http://127.0.0.1:3001',
238+
logger: clientLogger,
239+
debugBuf: true
240+
});
241+
242+
let ret = await client.callApi('Test', { name: 'xxx' });
243+
assert.ok(ret.isSucc);
244+
245+
return new Promise(rs => {
246+
server.listenMsg('Test', async v => {
247+
assert.deepStrictEqual(v.msg, { content: 'abc' });
248+
await server.stop();
249+
rs();
250+
});
251+
252+
client.sendMsg('Test', {
253+
content: 'abc'
254+
});
255+
})
256+
});
257+
226258
it('abort', async function () {
227259
let server = new HttpServer(getProto(), {
228260
logger: serverLogger

‎test/cases/httpJSON.test.ts

+36-2
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ async function testApi(server: HttpServer<ServiceType>, client: HttpClient<Servi
9090
}
9191
}
9292

93-
describe('HTTP Server & Client basic', function () {
93+
describe('HTTP JSON Server & Client basic', function () {
9494
it('implement API manually', async function () {
9595
let server = new HttpServer(getProto(), {
9696
json: true,
@@ -232,6 +232,40 @@ describe('HTTP Server & Client basic', function () {
232232
})
233233
})
234234

235+
it('Same-name msg and api', async function () {
236+
let server = new HttpServer(getProto(), {
237+
port: 3001,
238+
json: true,
239+
logger: serverLogger,
240+
// debugBuf: true
241+
});
242+
243+
await server.autoImplementApi(path.resolve(__dirname, '../api'))
244+
await server.start();
245+
246+
let client = new HttpClient(getProto(), {
247+
server: 'http://127.0.0.1:3001',
248+
json: true,
249+
logger: clientLogger,
250+
// debugBuf: true
251+
});
252+
253+
let ret = await client.callApi('Test', { name: 'xxx' });
254+
assert.ok(ret.isSucc);
255+
256+
return new Promise(rs => {
257+
server.listenMsg('Test', async v => {
258+
assert.deepStrictEqual(v.msg, { content: 'abc' });
259+
await server.stop();
260+
rs();
261+
});
262+
263+
client.sendMsg('Test', {
264+
content: 'abc'
265+
});
266+
})
267+
});
268+
235269
it('abort', async function () {
236270
let server = new HttpServer(getProto(), {
237271
json: true,
@@ -531,7 +565,7 @@ describe('HTTP Server & Client basic', function () {
531565
})
532566
})
533567

534-
describe('HTTP Flows', function () {
568+
describe('HTTP JSON Flows', function () {
535569
it('Server conn flow', async function () {
536570
let server = new HttpServer(getProto(), {
537571
json: true,

‎test/cases/ws.test.ts

+33
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,39 @@ describe('WS Server & Client basic', function () {
194194
})
195195
});
196196

197+
it('Same-name msg and api', async function () {
198+
let server = new WsServer(getProto(), {
199+
port: 3000,
200+
logger: serverLogger,
201+
// debugBuf: true
202+
});
203+
204+
await server.autoImplementApi(path.resolve(__dirname, '../api'))
205+
await server.start();
206+
207+
let client = new WsClient(getProto(), {
208+
server: 'ws://127.0.0.1:3000',
209+
logger: clientLogger,
210+
// debugBuf: true
211+
});
212+
await client.connect();
213+
214+
let ret = await client.callApi('Test', { name: 'xxx' });
215+
assert.ok(ret.isSucc);
216+
217+
return new Promise(rs => {
218+
server.listenMsg('Test', async v => {
219+
assert.deepStrictEqual(v.msg, { content: 'abc' });
220+
await server.stop();
221+
rs();
222+
});
223+
224+
client.sendMsg('Test', {
225+
content: 'abc'
226+
});
227+
})
228+
});
229+
197230
it('server send msg', async function () {
198231
let server = new WsServer(getProto(), {
199232
port: 3001,

‎test/cases/wsJSON.test.ts

+37-2
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ async function testApi(server: WsServer<ServiceType>, client: WsClient<ServiceTy
8888
}
8989
}
9090

91-
describe('WS Server & Client basic', function () {
91+
describe('WS JSON Server & Client basic', function () {
9292
it('cannot callApi before connect', async function () {
9393
let client = new WsClient(getProto(), {
9494
json: true,
@@ -246,6 +246,41 @@ describe('WS Server & Client basic', function () {
246246
})
247247
});
248248

249+
it('Same-name msg and api', async function () {
250+
let server = new WsServer(getProto(), {
251+
port: 3000,
252+
logger: serverLogger,
253+
json: true,
254+
// debugBuf: true
255+
});
256+
257+
await server.autoImplementApi(path.resolve(__dirname, '../api'))
258+
await server.start();
259+
260+
let client = new WsClient(getProto(), {
261+
server: 'ws://127.0.0.1:3000',
262+
logger: clientLogger,
263+
json: true,
264+
// debugBuf: true
265+
});
266+
await client.connect();
267+
268+
let ret = await client.callApi('Test', { name: 'xxx' });
269+
assert.ok(ret.isSucc);
270+
271+
return new Promise(rs => {
272+
server.listenMsg('Test', async v => {
273+
assert.deepStrictEqual(v.msg, { content: 'abc' });
274+
await server.stop();
275+
rs();
276+
});
277+
278+
client.sendMsg('Test', {
279+
content: 'abc'
280+
});
281+
})
282+
});
283+
249284
it('server send msg', async function () {
250285
let server = new WsServer(getProto(), {
251286
json: true,
@@ -855,7 +890,7 @@ describe('WS Server & Client basic', function () {
855890
})
856891
})
857892

858-
describe('WS Flows', function () {
893+
describe('WS JSON Flows', function () {
859894
it('Server conn flow', async function () {
860895
let server = new WsServer(getProto(), {
861896
json: true,

‎test/proto/MsgTest.ts

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export interface MsgTest {
2+
content: string
3+
}

‎test/proto/serviceProto.ts

+21-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { ServiceProto } from 'tsrpc-proto';
22
import { ReqTest, ResTest } from './a/b/c/PtlTest';
33
import { MsgChat } from './MsgChat';
4+
import { MsgTest } from './MsgTest';
45
import { ReqObjId, ResObjId } from './PtlObjId';
56
import { ReqTest as ReqTest_1, ResTest as ResTest_1 } from './PtlTest';
67

@@ -20,12 +21,13 @@ export interface ServiceType {
2021
}
2122
},
2223
msg: {
23-
"Chat": MsgChat
24+
"Chat": MsgChat,
25+
"Test": MsgTest
2426
}
2527
}
2628

2729
export const serviceProto: ServiceProto<ServiceType> = {
28-
"version": 1,
30+
"version": 2,
2931
"services": [
3032
{
3133
"id": 0,
@@ -37,6 +39,11 @@ export const serviceProto: ServiceProto<ServiceType> = {
3739
"name": "Chat",
3840
"type": "msg"
3941
},
42+
{
43+
"id": 4,
44+
"name": "Test",
45+
"type": "msg"
46+
},
4047
{
4148
"id": 2,
4249
"name": "ObjId",
@@ -115,6 +122,18 @@ export const serviceProto: ServiceProto<ServiceType> = {
115122
}
116123
]
117124
},
125+
"MsgTest/MsgTest": {
126+
"type": "Interface",
127+
"properties": [
128+
{
129+
"id": 0,
130+
"name": "content",
131+
"type": {
132+
"type": "String"
133+
}
134+
}
135+
]
136+
},
118137
"PtlObjId/ReqObjId": {
119138
"type": "Interface",
120139
"properties": [

0 commit comments

Comments
 (0)
Please sign in to comment.