Skip to content

Commit 69ba249

Browse files
Merge branch 'my-fix-branch' of https://github.com/ivibe/nest into ivibe-my-fix-branch
2 parents 48be3ef + 9c3e15a commit 69ba249

12 files changed

Lines changed: 928 additions & 26 deletions

File tree

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@
5959
"grpc": "1.19.0",
6060
"http2": "3.3.7",
6161
"iterare": "1.1.2",
62-
"json-socket": "0.3.0",
6362
"merge-graphql-schemas": "1.5.8",
6463
"mqtt": "2.18.8",
6564
"multer": "1.4.1",

packages/microservices/client/client-tcp.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { Logger } from '@nestjs/common';
2-
import * as JsonSocket from 'json-socket';
32
import * as net from 'net';
43
import { share, tap } from 'rxjs/operators';
54
import {
@@ -14,6 +13,7 @@ import {
1413
ClientOptions,
1514
TcpClientOptions,
1615
} from '../interfaces/client-metadata.interface';
16+
import { JsonSocket } from '../json-socket';
1717
import { ClientProxy } from './client-proxy';
1818
import { ECONNREFUSED } from './constants';
1919

@@ -42,7 +42,7 @@ export class ClientTCP extends ClientProxy {
4242
this.socket = this.createSocket();
4343
this.bindEvents(this.socket);
4444

45-
const source$ = this.connect$(this.socket._socket).pipe(
45+
const source$ = this.connect$(this.socket.netSocket).pipe(
4646
tap(() => {
4747
this.isConnected = true;
4848
this.socket.on(MESSAGE_EVENT, (buffer: WritePacket & PacketId) =>

packages/microservices/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export const RQM_DEFAULT_URL = 'amqp://localhost';
99
export const CONNECT_EVENT = 'connect';
1010
export const DISCONNECT_EVENT = 'disconnect';
1111
export const MESSAGE_EVENT = 'message';
12+
export const DATA_EVENT = 'data';
1213
export const ERROR_EVENT = 'error';
1314
export const CLOSE_EVENT = 'close';
1415
export const SUBSCRIBE = 'subscribe';
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import { Socket } from 'net';
2+
import { StringDecoder } from 'string_decoder';
3+
import {
4+
CLOSE_EVENT,
5+
CONNECT_EVENT,
6+
ERROR_EVENT,
7+
MESSAGE_EVENT,
8+
DATA_EVENT,
9+
} from './constants';
10+
11+
const stringDecoder = new StringDecoder();
12+
13+
export class JsonSocket {
14+
private contentLength: number | null = null;
15+
private buffer = '';
16+
private closed = false;
17+
18+
private readonly delimeter = '#';
19+
20+
public get netSocket() {
21+
return this.socket;
22+
}
23+
24+
constructor(public readonly socket: Socket) {
25+
this.socket.on(DATA_EVENT, this.onData.bind(this));
26+
this.socket.on(CONNECT_EVENT, () => (this.closed = false));
27+
this.socket.on(CLOSE_EVENT, () => (this.closed = true));
28+
this.socket.on(ERROR_EVENT, () => (this.closed = true));
29+
}
30+
31+
public connect(port: number, host: string) {
32+
this.socket.connect(port, host);
33+
return this;
34+
}
35+
36+
public on(event: string, callback: (err?: any) => void) {
37+
this.socket.on(event, callback);
38+
return this;
39+
}
40+
41+
public once(event: string, callback: (err?: any) => void) {
42+
this.socket.once(event, callback);
43+
return this;
44+
}
45+
46+
public end() {
47+
this.socket.end();
48+
return this;
49+
}
50+
51+
public sendMessage(message: any, callback?: (err?: any) => void) {
52+
if (this.closed) {
53+
if (callback) {
54+
callback(new Error('The net socket is closed.'));
55+
}
56+
return;
57+
}
58+
this.socket.write(this.formatMessageData(message), 'utf-8', callback);
59+
}
60+
61+
private onData(dataRaw: Buffer | string) {
62+
const data = Buffer.isBuffer(dataRaw)
63+
? stringDecoder.write(dataRaw)
64+
: dataRaw;
65+
66+
try {
67+
this.handleData(data);
68+
} catch (e) {
69+
this.socket.emit(ERROR_EVENT, e.message);
70+
this.socket.end();
71+
}
72+
}
73+
74+
private handleData(data: string) {
75+
this.buffer += data;
76+
77+
if (this.contentLength == null) {
78+
const i = this.buffer.indexOf(this.delimeter);
79+
80+
/**
81+
* Check if the buffer has the delimeter (#),
82+
* if not, the end of the buffer string might be in the middle of a content length string
83+
*/
84+
if (i !== -1) {
85+
const rawContentLength = this.buffer.substring(0, i);
86+
this.contentLength = parseInt(rawContentLength, 10);
87+
88+
if (isNaN(this.contentLength)) {
89+
this.contentLength = null;
90+
this.buffer = '';
91+
92+
throw new Error(
93+
`Corrupted length value "${rawContentLength}" supplied in a packet`,
94+
);
95+
}
96+
97+
this.buffer = this.buffer.substring(i + 1);
98+
}
99+
}
100+
101+
if (this.contentLength != null) {
102+
const length = this.buffer.length;
103+
104+
if (length === this.contentLength) {
105+
this.handleMessage(this.buffer);
106+
} else if (length > this.contentLength) {
107+
const message = this.buffer.substring(0, this.contentLength);
108+
const rest = this.buffer.substring(this.contentLength);
109+
this.handleMessage(message);
110+
this.onData(rest);
111+
}
112+
}
113+
}
114+
115+
private handleMessage(data: string) {
116+
this.contentLength = null;
117+
this.buffer = '';
118+
let message: object;
119+
120+
try {
121+
message = JSON.parse(data);
122+
} catch (e) {
123+
throw new Error(
124+
`Could not parse JSON: ${e.message}\nRequest data: ${data}`,
125+
);
126+
}
127+
message = message || {};
128+
129+
this.socket.emit(MESSAGE_EVENT, message);
130+
}
131+
132+
private formatMessageData(message: any) {
133+
const messageData = JSON.stringify(message);
134+
const length = messageData.length;
135+
const data = length + this.delimeter + messageData;
136+
return data;
137+
}
138+
}

packages/microservices/server/server-tcp.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { isString, isUndefined } from '@nestjs/common/utils/shared.utils';
2-
import * as JsonSocket from 'json-socket';
32
import * as net from 'net';
4-
import { Server as NetSocket } from 'net';
3+
import { Server as NetSocket, Socket } from 'net';
54
import { Observable } from 'rxjs';
65
import {
76
CLOSE_EVENT,
@@ -15,6 +14,7 @@ import {
1514
MicroserviceOptions,
1615
TcpOptions,
1716
} from '../interfaces/microservice-configuration.interface';
17+
import { JsonSocket } from '../json-socket';
1818
import { Server } from './server';
1919

2020
export class ServerTCP extends Server implements CustomTransportStrategy {
@@ -39,15 +39,16 @@ export class ServerTCP extends Server implements CustomTransportStrategy {
3939
this.server.close();
4040
}
4141

42-
public bindHandler<T extends Record<string, any>>(socket: T) {
42+
public bindHandler(socket: Socket) {
4343
const readSocket = this.getSocketInstance(socket);
4444
readSocket.on(MESSAGE_EVENT, async (msg: ReadPacket & PacketId) =>
4545
this.handleMessage(readSocket, msg),
4646
);
47+
readSocket.on(ERROR_EVENT, this.handleError.bind(this));
4748
}
4849

49-
public async handleMessage<T extends Record<string, any>>(
50-
socket: T,
50+
public async handleMessage(
51+
socket: JsonSocket,
5152
packet: ReadPacket & PacketId,
5253
) {
5354
const pattern = !isString(packet.pattern)
@@ -98,7 +99,7 @@ export class ServerTCP extends Server implements CustomTransportStrategy {
9899
this.server.on(CLOSE_EVENT, this.handleClose.bind(this));
99100
}
100101

101-
private getSocketInstance<T>(socket: T): JsonSocket {
102+
private getSocketInstance(socket: Socket): JsonSocket {
102103
return new JsonSocket(socket);
103104
}
104105
}

packages/microservices/test/client/client-tcp.spec.ts

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,7 @@ import { ERROR_EVENT } from '../../constants';
66

77
describe('ClientTCP', () => {
88
let client: ClientTCP;
9-
let socket: {
10-
connect: sinon.SinonStub;
11-
publish: sinon.SinonSpy;
12-
_socket: {
13-
addListener: sinon.SinonStub;
14-
removeListener: sinon.SinonSpy;
15-
once: sinon.SinonStub;
16-
};
17-
on: sinon.SinonStub;
18-
end: sinon.SinonSpy;
19-
sendMessage: sinon.SinonSpy;
20-
};
9+
let socket;
2110
let createSocketStub: sinon.SinonStub;
2211

2312
beforeEach(() => {
@@ -27,9 +16,8 @@ describe('ClientTCP', () => {
2716

2817
socket = {
2918
connect: sinon.stub(),
30-
publish: sinon.spy(),
3119
on: sinon.stub().callsFake(onFakeCallback),
32-
_socket: {
20+
netSocket: {
3321
addListener: sinon.stub().callsFake(onFakeCallback),
3422
removeListener: sinon.spy(),
3523
once: sinon.stub().callsFake(onFakeCallback),
@@ -134,7 +122,9 @@ describe('ClientTCP', () => {
134122
toPromise: () => source,
135123
pipe: () => source,
136124
};
137-
connect$Stub = sinon.stub(client, 'connect$' as any).callsFake(() => source);
125+
connect$Stub = sinon
126+
.stub(client, 'connect$' as any)
127+
.callsFake(() => source);
138128
await client.connect();
139129
});
140130
afterEach(() => {

0 commit comments

Comments
 (0)