/**
 * Copyright (c) 2021 Expo, Inc.
 * Copyright (c) 2018 Drifty Co.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */ import plist from '@expo/plist' ; import type * as net from 'net' ; import { parsePlistBuffer } from '../../../../../utils/parseBinaryPlistAsync' ; const BPLIST_MAGIC = Buffer . from ( 'bplist00' ) ; export type ProtocolReaderCallback = ( resp : any , err ? : Error ) => void ; export class ProtocolReaderFactory < T > { constructor ( private ProtocolReader ) { } create ( callback ) { return new this . ProtocolReader ( callback ) ; } } export abstract class ProtocolReader { protected body ! : Buffer ; protected bodyLength ! : number ; protected buffer = Buffer . alloc ( 0 ) ; constructor ( protected headerSize , protected callback ) { this . onData = this . onData . bind ( this ) ; } /** Returns length of body, or -1 if header doesn't contain length */ protected abstract parseHeader ( data ) ; protected abstract parseBody ( data ) ; onData ( data ? ) { try { this . buffer = data ? Buffer . concat ( [ this . buffer , data ] ) : this . buffer ; if ( ! this . bodyLength ) { if ( this . buffer . length < this . headerSize ) { return ; } this . bodyLength = this . parseHeader ( this . buffer ) ; this . buffer = this . buffer . slice ( this . headerSize ) ; if ( ! this . buffer . length ) { return ; } } if ( this . buffer . length < this . bodyLength ) { return ; } if ( this . bodyLength === - 1 ) { this . callback ( this . parseBody ( this . buffer ) ) ; this . buffer = Buffer . alloc ( 0 ) ; } else { this . body = this . buffer . slice ( 0 , this . bodyLength ) ; this . bodyLength -= this . body . length ; if ( ! this . bodyLength ) { this . callback ( this . parseBody ( this . body ) ) ; } this . buffer = this . buffer . slice ( this . body . length ) ; if ( this . buffer . length ) { this . onData ( ) ; } } } catch ( err ) { this . callback ( null , err ) ; } } } export abstract class PlistProtocolReader extends ProtocolReader { protected parseBody ( body ) { if ( BPLIST_MAGIC . compare ( body , 0 , 8 ) === 0 ) { return parsePlistBuffer ( body ) ; } else { return plist . parse ( body . toString ( 'utf8' ) ) ; } } } export interface ProtocolWriter { write ( sock , msg ) : void ; } export abstract class ProtocolClient < MessageType = any > { constructor ( public socket , protected readerFactory , protected writer ) { } sendMessage < ResponseType = any > ( msg ) ; sendMessage < CallbackType = void , ResponseType = any > ( msg , callback ) ; sendMessage < CallbackType = void , ResponseType = any > ( msg , callback ? ) { return new Promise < ResponseType | CallbackType > ( ( resolve , reject ) => { const reader = this . readerFactory . create ( async ( resp , err ? ) => { if ( err ) { reject ( err ) ; return ; } if ( callback ) { callback ( resp , ( value ) => { this . socket . removeListener ( 'data' , reader . onData ) ; resolve ( value ) ; } , reject ) ; } else { this . socket . removeListener ( 'data' , reader . onData ) ; resolve ( resp ) ; } } ) ; this . socket . on ( 'data' , reader . onData ) ; this . writer . write ( this . socket , msg ) ; } ) ; } }