import { IRawFrameType } from './types' ; internal const NULL = 0 ; internal const LF = 10 ; internal const CR = 13 ; internal const COLON = 58 ; internal export class Parser { private readonly _encoder = new TextEncoder ( ) ; private readonly _decoder = new TextDecoder ( ) ; private _results : IRawFrameType ; private _token : number [ ] = [ ] ; private _headerKey : string ; private _bodyBytesRemaining : number ; private _onByte : ( byte : number ) => void ; public constructor ( public onFrame , public onIncomingPing ) { this . _initState ( ) ; } public parseChunk ( segment , appendMissingNULLonIncoming = false ) { let chunk ; if ( segment instanceof ArrayBuffer ) { chunk = new Uint8Array ( segment ) ; } else { chunk = this . _encoder . encode ( segment ) ; } if ( appendMissingNULLonIncoming && chunk [ chunk . length - 1 ] !== 0 ) { const chunkWithNull = new Uint8Array ( chunk . length + 1 ) ; chunkWithNull . set ( chunk , 0 ) ; chunkWithNull [ chunk . length ] = 0 ; chunk = chunkWithNull ; } for ( let i = 0 ; i < chunk . length ; i ++ ) { const byte = chunk [ i ] ; this . _onByte ( byte ) ; } } private _collectFrame ( byte ) { if ( byte === NULL ) { return ; } if ( byte === CR ) { return ; } if ( byte === LF ) { this . onIncomingPing ( ) ; return ; } this . _onByte = this . _collectCommand ; this . _reinjectByte ( byte ) ; } private _collectCommand ( byte ) { if ( byte === CR ) { return ; } if ( byte === LF ) { this . _results . command = this . _consumeTokenAsUTF8 ( ) ; this . _onByte = this . _collectHeaders ; return ; } this . _consumeByte ( byte ) ; } private _collectHeaders ( byte ) { if ( byte === CR ) { return ; } if ( byte === LF ) { this . _setupCollectBody ( ) ; return ; } this . _onByte = this . _collectHeaderKey ; this . _reinjectByte ( byte ) ; } private _reinjectByte ( byte ) { this . _onByte ( byte ) ; } private _collectHeaderKey ( byte ) { if ( byte === COLON ) { this . _headerKey = this . _consumeTokenAsUTF8 ( ) ; this . _onByte = this . _collectHeaderValue ; return ; } this . _consumeByte ( byte ) ; } private _collectHeaderValue ( byte ) { if ( byte === CR ) { return ; } if ( byte === LF ) { this . _results . headers . push ( [ this . _headerKey , this . _consumeTokenAsUTF8 ( ) ] ) ; this . _headerKey = undefined ; this . _onByte = this . _collectHeaders ; return ; } this . _consumeByte ( byte ) ; } private _setupCollectBody ( ) { const contentLengthHeader = this . _results . headers . filter ( ( header ) => { return header [ 0 ] === 'content-length' ; } ) [ 0 ] ; if ( contentLengthHeader ) { this . _bodyBytesRemaining = parseInt ( contentLengthHeader [ 1 ] , 10 ) ; this . _onByte = this . _collectBodyFixedSize ; } else { this . _onByte = this . _collectBodyNullTerminated ; } } private _collectBodyNullTerminated ( byte ) { if ( byte === NULL ) { this . _retrievedBody ( ) ; return ; } this . _consumeByte ( byte ) ; } private _collectBodyFixedSize ( byte ) { if ( this . _bodyBytesRemaining -- === 0 ) { this . _retrievedBody ( ) ; return ; } this . _consumeByte ( byte ) ; } private _retrievedBody ( ) { this . _results . binaryBody = this . _consumeTokenAsRaw ( ) ; this . onFrame ( this . _results ) ; this . _initState ( ) ; } private _consumeByte ( byte ) { this . _token . push ( byte ) ; } private _consumeTokenAsUTF8 ( ) { return this . _decoder . decode ( this . _consumeTokenAsRaw ( ) ) ; } private _consumeTokenAsRaw ( ) { const rawResult = new Uint8Array ( this . _token ) ; this . _token = [ ] ; return rawResult ; } private _initState ( ) { this . _results = { command : undefined , headers : [ ] , binaryBody : undefined , } ; this . _token = [ ] ; this . _headerKey = undefined ; this . _onByte = this . _collectFrame ; } }