import RIL from './ril' ; RIL . install ( ) ; import RAL from '../common/ral' ; import { AbstractMessageReader , DataCallback , AbstractMessageWriter , Message , ReadableStreamMessageReader , WriteableStreamMessageWriter , MessageWriterOptions , MessageReaderOptions , MessageReader , MessageWriter , NullLogger , ConnectionStrategy , ConnectionOptions , MessageConnection , Logger , createMessageConnection as _createMessageConnection , Disposable } from '../common/api' ; import * as path from 'path' ; import * as os from 'os' ; import { ChildProcess } from 'child_process' ; import { randomBytes } from 'crypto' ; import { Server , Socket , createServer , createConnection } from 'net' ; export * from '../common/api' ; export class IPCMessageReader extends AbstractMessageReader { private process : NodeJS . Process | ChildProcess ; public constructor ( process ) { super ( ) ; this . process = process ; let eventEmitter = this . process ; eventEmitter . on ( 'error' , ( error ) => this . fireError ( error ) ) ; eventEmitter . on ( 'close' , ( ) => this . fireClose ( ) ) ; } public listen ( callback ) { ( this . process as NodeJS . EventEmitter ) . on ( 'message' , callback ) ; return Disposable . create ( ( ) => ( this . process as NodeJS . EventEmitter ) . off ( 'message' , callback ) ) ; } } export class IPCMessageWriter extends AbstractMessageWriter implements MessageWriter { private process : NodeJS . Process | ChildProcess ; private errorCount : number ; public constructor ( process ) { super ( ) ; this . process = process ; this . errorCount = 0 ; let eventEmitter = this . process ; eventEmitter . on ( 'error' , ( error ) => this . fireError ( error ) ) ; eventEmitter . on ( 'close' , ( ) => this . fireClose ) ; } public write ( msg ) { try { if ( typeof this . process . send === 'function' ) { ( this . process . send as Function ) ( msg , undefined , undefined , ( error ) => { if ( error ) { this . errorCount ++ ; this . handleError ( error , msg ) ; } else { this . errorCount = 0 ; } } ) ; } return Promise . resolve ( ) ; } catch ( error ) { this . handleError ( error , msg ) ; return Promise . reject ( error ) ; } } private handleError ( error , msg ) { this . errorCount ++ ; this . fireError ( error , msg , this . errorCount ) ; } public end ( ) { } } export class SocketMessageReader extends ReadableStreamMessageReader { public constructor ( socket , encoding = 'utf-8' ) { super ( RIL ( ) . stream . asReadableStream ( socket ) , encoding ) ; } } export class SocketMessageWriter extends WriteableStreamMessageWriter { private socket : Socket ; public constructor ( socket , options ? ) { super ( RIL ( ) . stream . asWritableStream ( socket ) , options ) ; this . socket = socket ; } public dispose ( ) { super . dispose ( ) ; this . socket . destroy ( ) ; } } export class StreamMessageReader extends ReadableStreamMessageReader { public constructor ( readble , encoding ? ) { super ( RIL ( ) . stream . asReadableStream ( readble ) , encoding ) ; } } export class StreamMessageWriter extends WriteableStreamMessageWriter { public constructor ( writable , options ? ) { super ( RIL ( ) . stream . asWritableStream ( writable ) , options ) ; } } const XDG_RUNTIME_DIR = process . env [ 'XDG_RUNTIME_DIR' ] ; const safeIpcPathLengths = new Map ( [ [ 'linux' , 107 ] , [ 'darwin' , 103 ] ] ) ; export function generateRandomPipeName ( ) { const randomSuffix = randomBytes ( 21 ) . toString ( 'hex' ) ; if ( process . platform === 'win32' ) { return `\\\\.\\pipe\\vscode-jsonrpc-${ randomSuffix }-sock` ; } let result ; if ( XDG_RUNTIME_DIR ) { result = path . join ( XDG_RUNTIME_DIR , `vscode-ipc-${ randomSuffix }.sock` ) ; } else { result = path . join ( os . tmpdir ( ) , `vscode-${ randomSuffix }.sock` ) ; } const limit = safeIpcPathLengths . get ( process . platform ) ; if ( limit !== undefined && result . length >= limit ) { RIL ( ) . console . warn ( `WARNING: IPC handle "${ result }" is longer than ${ limit } characters.` ) ; } return result ; } export interface PipeTransport { onConnected ( ) : Promise < [ MessageReader , MessageWriter ] > ; } export function createClientPipeTransport ( pipeName , encoding = 'utf-8' ) { let connectResolve ; const connected = new Promise < [ MessageReader , MessageWriter ] > ( ( resolve , _reject ) => { connectResolve = resolve ; } ) ; return new Promise < PipeTransport > ( ( resolve , reject ) => { let server = createServer ( ( socket ) => { server . close ( ) ; connectResolve ( [ new SocketMessageReader ( socket , encoding ) , new SocketMessageWriter ( socket , encoding ) ] ) ; } ) ; server . on ( 'error' , reject ) ; server . listen ( pipeName , ( ) => { server . removeListener ( 'error' , reject ) ; resolve ( { onConnected : ( ) => { return connected ; } } ) ; } ) ; } ) ; } export function createServerPipeTransport ( pipeName , encoding = 'utf-8' ) { const socket = createConnection ( pipeName ) ; return [ new SocketMessageReader ( socket , encoding ) , new SocketMessageWriter ( socket , encoding ) ] ; } export interface SocketTransport { onConnected ( ) : Promise < [ MessageReader , MessageWriter ] > ; } export function createClientSocketTransport ( port , encoding = 'utf-8' ) { let connectResolve ; const connected = new Promise < [ MessageReader , MessageWriter ] > ( ( resolve , _reject ) => { connectResolve = resolve ; } ) ; return new Promise < SocketTransport > ( ( resolve , reject ) => { const server = createServer ( ( socket ) => { server . close ( ) ; connectResolve ( [ new SocketMessageReader ( socket , encoding ) , new SocketMessageWriter ( socket , encoding ) ] ) ; } ) ; server . on ( 'error' , reject ) ; server . listen ( port , '127.0.0.1' , ( ) => { server . removeListener ( 'error' , reject ) ; resolve ( { onConnected : ( ) => { return connected ; } } ) ; } ) ; } ) ; } export function createServerSocketTransport ( port , encoding = 'utf-8' ) { const socket = createConnection ( port , '127.0.0.1' ) ; return [ new SocketMessageReader ( socket , encoding ) , new SocketMessageWriter ( socket , encoding ) ] ; } function isReadableStream ( value ) { const candidate = value ; return candidate . read !== undefined && candidate . addListener !== undefined ; } function isWritableStream ( value ) { const candidate = value ; return candidate . write !== undefined && candidate . addListener !== undefined ; } export function createMessageConnection ( reader , writer , logger ? , options ? ) ; export function createMessageConnection ( inputStream , outputStream , logger ? , options ? ) ; export function createMessageConnection ( input , output , logger ? , options ? ) { if ( ! logger ) { logger = NullLogger ; } const reader = isReadableStream ( input ) ? new StreamMessageReader ( input ) : input ; const writer = isWritableStream ( output ) ? new StreamMessageWriter ( output ) : output ; if ( ConnectionStrategy . is ( options ) ) { options = { connectionStrategy : options } as ConnectionOptions ; } return _createMessageConnection ( reader , writer , logger , options ) ; }