jspurefix
Version:
pure node js fix engine
127 lines (115 loc) • 4.42 kB
text/typescript
import { TcpDuplex } from '../duplex'
import { MsgTransport } from '../factory'
import { FixAcceptor } from '../fix-acceptor'
import { IJsFixConfig, IJsFixLogger } from '../../config'
import { createServer as netCreateServer, Server, Socket } from 'net'
import { createServer as tlsCreateServer, TlsOptions, TLSSocket } from 'tls'
import { TlsOptionsFactory } from './tls-options-factory'
import { inject, injectable } from 'tsyringe'
import { DITokens } from '../../runtime/di-tokens'
export class TcpAcceptor extends FixAcceptor {
private server: Server
private readonly logger: IJsFixLogger
private nextId: number = 0
constructor ( public readonly config: IJsFixConfig) {
super(config.description.application ?? null)
this.logger = config.logFactory.logger(`${config.description.application?.name}:TcpAcceptor`)
const tlsOptions: TlsOptions | null = this.tlsOptions()
if (tlsOptions) {
this.tlsServer()
} else {
this.unsecureServer()
}
this.server.on('error', (err: Error) => {
throw err
})
}
getId (): number {
this.nextId++
return this.nextId
}
tlsServer (): void {
try {
const config: IJsFixConfig = this.config
const tcp = this.config.description.application?.tcp
const tlsOptions: TlsOptions | null = tcp?.tls ? TlsOptionsFactory.getTlsOptions(tcp.tls) : null
this.logger.info('create tls server')
if (!tlsOptions) return
this.server = tlsCreateServer(tlsOptions, (tlsSocket: TLSSocket) => {
if (tcp?.tls?.enableTrace) {
this.logger.info('enabling tls session trace')
tlsSocket.enableTrace()
}
if (tlsSocket.authorized) {
tlsSocket.setEncoding('utf8')
const id: number = this.getId()
this.logger.info(`tls creates session ${id} ${tlsSocket.authorized}`)
this.onSocket(id, tlsSocket, config)
} else {
this.logger.info('no transport created on tls with no authorized connection')
}
})
} catch (e) {
this.logger.error(e)
throw e
}
}
unsecureServer (): void {
try {
const config = this.config
this.logger.info('create unsecured server')
this.server = netCreateServer((socket: Socket) => {
const id: number = this.getId()
this.logger.info(`net creates session ${id}`)
socket.setNoDelay(true)
this.onSocket(id, socket, config)
})
} catch (e) {
this.logger.error(e)
throw e
}
}
tlsOptions (): TlsOptions | null {
const tcp = this.config.description.application?.tcp
return tcp?.tls ? TlsOptionsFactory.getTlsOptions(tcp?.tls) : null
}
private onSocket (id: number, socket: Socket, config: IJsFixConfig): void {
const transport: MsgTransport = new MsgTransport(id, config, new TcpDuplex(socket))
this.saveTransport(id, transport)
transport.receiver.on('end', () => {
this.harvestTransport(id)
})
transport.receiver.on('error', (e: Error) => {
this.logger.error(e)
this.harvestTransport(id)
})
}
public listen (): void {
const port = this.config.description.application?.tcp?.port
this.logger.info(`start to listen ${port}`)
this.server.on('connection', () => {
this.logger.info('insecure connection established')
})
this.server.on('secureConnection', (s) => {
this.logger.info(`secure connection; client authorized: ${s.authorized}`)
})
this.server.listen(port)
}
public close (callback?: (err?: Error) => void): void {
const port = this.config.description.application?.tcp?.port ?? -1
this.logger.info(`close listener on port ${port}`)
this.server.close(callback)
}
private saveTransport (tid: number, transport: MsgTransport): void {
this.transports[tid] = transport
const keys: string[] = Object.keys(this.transports)
this.logger.info(`new transport id = ${tid} created total transports = ${keys.length}`)
this.emit('transport', transport)
}
private harvestTransport (tid: number): void {
delete this.transports[tid]
const keys: string[] = Object.keys(this.transports)
this.logger.info(`transport ${tid} ends total transports = ${keys.length}`)
}
}