import * as net from 'net' ; import { Server , Socket } from 'net' ; import * as nuid from 'nuid' ; import { EventEmitter } from 'events' ; const MAX_CONTROL = 1048576 ; const PING = /^PING\r\n/i ; const CONNECT = /^CONNECT\s+([^\r\n]+)\r\n/i ; function defaultScript ( ) { let script = [ ] ; script . push ( { re : CONNECT , h : sendOk , m : 'connect' } ) ; script . push ( { re : PING , h : sendPong , m : 'ping' } ) ; return script ; } export interface Script { re : RegExp ; h : ScriptHandler ; m : string ; } export interface handler { ( ... args ) : void ; } export interface ScriptHandler { ( socket , match ) : void ; } export interface Client extends Socket { buffer ? : Buffer ; script : Script [ ] ; } export class ScriptedServer extends EventEmitter { port : number ; id : string ; script : Script [ ] ; stream ! : Server ; sockets : Socket [ ] ; buffer ! : Buffer ; constructor ( port , script ? ) { super ( ) ; this . port = port ; this . sockets = [ ] ; this . id = nuid . next ( ) ; this . script = script || defaultScript ( ) ; } stop ( cb ? ) { this . stream . close ( cb ) ; if ( this . sockets ) { this . sockets . forEach ( function ( socket ) { if ( ! socket . destroyed ) { socket . destroy ( ) ; } } ) ; } } ; sendInfo ( socket ) { socket . write ( 'INFO ' + JSON . stringify ( { server_id : 'TEST' , version : '0.0.0' , node : 'node0.0.0' , host : '127.0.0.1' , port : this . port , auth_required : false , ssl_required : false , tls_required : false , tls_verify : false , max_payload : MAX_CONTROL } ) + '\r\n' ) ; } handleData ( server , client ) { return function ( data ) : void { client . emit ( 'debug' , data . toString ( ) ) ; if ( client . buffer ) { client . buffer = Buffer . concat ( [ client . buffer , data ] ) ; } else { client . buffer = data ; } if ( client . buffer ) { let buf = client . buffer . toString ( 'binary' , 0 , MAX_CONTROL ) ; if ( client . script . length ) { let match = client . script [ 0 ] . re . exec ( buf ) ; if ( match ) { client . script [ 0 ] . h ( client , match ) ; let len = match [ 0 ] . length ; if ( len >= client . buffer . length ) { delete client . buffer ; } else { client . buffer = client . buffer . slice ( len ) ; } client . script . shift ( ) ; } else { server . emit ( 'warn' , 'no match:\n' + colorize ( buf ) ) ; } } else { server . emit ( 'info' , 'no more script handlers, ignoring:\n' + colorize ( buf ) ) ; } } } ; } start ( ) { let connecting = true ; return new Promise ( ( resolve , reject ) => { this . stream = net . createServer ( ( socket ) => { this . emit ( 'connect_request' ) ; this . sendInfo ( socket ) ; let client = socket as Client ; client . script = Array . from ( this . script ) ; client . on ( 'data' , this . handleData ( this , client ) ) ; } ) ; this . stream . on ( 'connection' , ( socket ) => { this . sockets . push ( socket ) ; } ) ; this . stream . on ( 'close' , ( ) => { this . emit ( 'close' ) ; } ) ; this . stream . on ( 'error' , ( ex ) => { this . emit ( 'error' , ex ) ; if ( connecting ) { reject ( ) ; } } ) ; this . stream . on ( 'listening' , ( ) => { this . emit ( 'listening' ) ; connecting = false ; this . port = this . stream . address ( ) . port ; resolve ( this . port ) ; } ) ; this . stream . listen ( this . port , ( ) => { } ) ; } ) ; } ; } function colorize ( str ) { return str . replace ( /(?:\r\n)/g , '\\r\\n\n' ) ; } export function sendOk ( socket ) { socket . write ( '+OK\r\n' ) ; } export function sendPing ( socket ) { socket . write ( 'PING\r\n' ) ; } export function sendPong ( socket ) { socket . write ( 'PONG\r\n' ) ; }