All files / microservices/client client-tcp.ts

87.5% Statements 42/48
83.33% Branches 15/18
78.57% Functions 11/14
86.96% Lines 40/46
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 1211x 1x 1x 1x 1x                       1x 1x   1x   17x     17x       17x 17x     17x           4x     4x 4x   4x                   4x 4x 4x       3x 3x 3x     3x 1x           3x                     3x 3x       5x   1x   5x       1x       4x 4x             3x 3x   2x 2x   2x   1x        
import { Logger } from '@nestjs/common';
import * as JsonSocket from 'json-socket';
import * as net from 'net';
import { share, tap } from 'rxjs/operators';
import {
  CLOSE_EVENT,
  ERROR_EVENT,
  MESSAGE_EVENT,
  TCP_DEFAULT_HOST,
  TCP_DEFAULT_PORT,
} from '../constants';
import { PacketId, ReadPacket, WritePacket } from '../interfaces';
import {
  ClientOptions,
  TcpClientOptions,
} from '../interfaces/client-metadata.interface';
import { ClientProxy } from './client-proxy';
import { ECONNREFUSED } from './constants';
 
export class ClientTCP extends ClientProxy {
  protected connection: Promise<any>;
  private readonly logger = new Logger(ClientTCP.name);
  private readonly port: number;
  private readonly host: string;
  private isConnected = false;
  private socket: JsonSocket;
 
  constructor(options: ClientOptions['options']) {
    super();
    this.port =
      this.getOptionsProp<TcpClientOptions>(options, 'port') ||
      TCP_DEFAULT_PORT;
    this.host =
      this.getOptionsProp<TcpClientOptions>(options, 'host') ||
      TCP_DEFAULT_HOST;
  }
 
  public connect(): Promise<any> {
    Iif (this.isConnected && this.connection) {
      return this.connection;
    }
    this.socket = this.createSocket();
    this.bindEvents(this.socket);
 
    const source$ = this.connect$(this.socket._socket).pipe(
      tap(() => {
        this.isConnected = true;
        this.socket.on(MESSAGE_EVENT, (buffer: WritePacket & PacketId) =>
          this.handleResponse(buffer),
        );
      }),
      share(),
    );
 
    this.socket.connect(this.port, this.host);
    this.connection = source$.toPromise();
    return this.connection;
  }
 
  public handleResponse(buffer: WritePacket & PacketId) {
    const { err, response, isDisposed, id } = buffer;
    const callback = this.routingMap.get(id);
    Iif (!callback) {
      return undefined;
    }
    if (isDisposed || err) {
      callback({
        err,
        response: null,
        isDisposed: true,
      });
    }
    callback({
      err,
      response,
    });
  }
 
  public createSocket(): JsonSocket {
    return new JsonSocket(new net.Socket());
  }
 
  public close() {
    this.socket && this.socket.end();
    this.handleClose();
  }
 
  public bindEvents(socket: JsonSocket) {
    socket.on(
      ERROR_EVENT,
      err => err.code !== ECONNREFUSED && this.handleError(err),
    );
    socket.on(CLOSE_EVENT, () => this.handleClose());
  }
 
  public handleError(err: any) {
    this.logger.error(err);
  }
 
  public handleClose() {
    this.isConnected = false;
    this.socket = null;
  }
 
  protected publish(
    partialPacket: ReadPacket,
    callback: (packet: WritePacket) => any,
  ): Function {
    try {
      const packet = this.assignPacketId(partialPacket);
 
      this.routingMap.set(packet.id, callback);
      this.socket.sendMessage(packet);
 
      return () => this.routingMap.delete(packet.id);
    } catch (err) {
      callback({ err });
    }
  }
}