2016-03-30 14:20:52 -07:00
/********************************************************************++
* Copyright (c) Microsoft Corporation. All rights reserved.
* --********************************************************************/
using System.Collections.Generic ;
using System.Management.Automation.Remoting ;
using System.Management.Automation.Runspaces ;
using System.Management.Automation.Remoting.Server ;
using Dbg = System . Management . Automation . Diagnostics ;
namespace System.Management.Automation
{
/// <summary>
/// Handles all data structure handler communication with the client
/// runspace pool
/// </summary>
internal class ServerRunspacePoolDataStructureHandler
{
#region Constructors
/// <summary>
/// Constructor which takes a server runspace pool driver and
/// creates an associated ServerRunspacePoolDataStructureHandler
/// </summary>
/// <param name="driver"></param>
/// <param name="transportManager"></param>
internal ServerRunspacePoolDataStructureHandler ( ServerRunspacePoolDriver driver ,
AbstractServerSessionTransportManager transportManager )
{
2016-07-29 13:02:49 -07:00
_clientRunspacePoolId = driver . InstanceId ;
_transportManager = transportManager ;
2016-03-30 14:20:52 -07:00
}
#endregion Constructors
2016-07-29 13:02:49 -07:00
2016-03-30 14:20:52 -07:00
#region Data Structure Handler Methods
/// <summary>
/// Send a message with application private data to the client
/// </summary>
/// <param name="applicationPrivateData">applicationPrivateData to send</param>
/// <param name="serverCapability">server capability negotiated during initial exchange of remoting messages / session capabilities of client and server</param>
internal void SendApplicationPrivateDataToClient ( PSPrimitiveDictionary applicationPrivateData , RemoteSessionCapability serverCapability )
{
// make server's PSVersionTable available to the client using ApplicationPrivateData
PSPrimitiveDictionary applicationPrivateDataWithVersionTable =
PSPrimitiveDictionary . CloneAndAddPSVersionTable ( applicationPrivateData );
// override the hardcoded version numbers with the stuff that was reported to the client during negotiation
PSPrimitiveDictionary versionTable = ( PSPrimitiveDictionary ) applicationPrivateDataWithVersionTable [ PSVersionInfo . PSVersionTableName ];
versionTable [ PSVersionInfo . PSRemotingProtocolVersionName ] = serverCapability . ProtocolVersion ;
versionTable [ PSVersionInfo . SerializationVersionName ] = serverCapability . SerializationVersion ;
// Pass back the true PowerShell version to the client via application private data.
versionTable [ PSVersionInfo . PSVersionName ] = PSVersionInfo . PSVersion ;
RemoteDataObject data = RemotingEncoder . GenerateApplicationPrivateData (
2016-07-29 13:02:49 -07:00
_clientRunspacePoolId , applicationPrivateDataWithVersionTable );
2016-03-30 14:20:52 -07:00
SendDataAsync ( data );
}
/// <summary>
/// Send a message with the RunspacePoolStateInfo to the client
/// </summary>
/// <param name="stateInfo">state info to send</param>
internal void SendStateInfoToClient ( RunspacePoolStateInfo stateInfo )
{
RemoteDataObject data = RemotingEncoder . GenerateRunspacePoolStateInfo (
2016-07-29 13:02:49 -07:00
_clientRunspacePoolId , stateInfo );
2016-03-30 14:20:52 -07:00
SendDataAsync ( data );
}
/// <summary>
/// Send a message with the PSEventArgs to the client
/// </summary>
/// <param name="e">event to send</param>
internal void SendPSEventArgsToClient ( PSEventArgs e )
{
2016-07-29 13:02:49 -07:00
RemoteDataObject data = RemotingEncoder . GeneratePSEventArgs ( _clientRunspacePoolId , e );
2016-03-30 14:20:52 -07:00
SendDataAsync ( data );
}
/// <summary>
/// called when session is connected from a new client
/// call into the sessionconnect handlers for each associated powershell dshandler
/// </summary>
internal void ProcessConnect ()
{
List < ServerPowerShellDataStructureHandler > dsHandlers ;
2016-07-29 13:02:49 -07:00
lock ( _associationSyncObject )
2016-03-30 14:20:52 -07:00
{
2016-07-29 13:02:49 -07:00
dsHandlers = new List < ServerPowerShellDataStructureHandler >( _associatedShells . Values );
2016-03-30 14:20:52 -07:00
}
foreach ( var dsHandler in dsHandlers )
{
dsHandler . ProcessConnect ();
}
}
/// <summary>
/// Process the data received from the runspace pool on
/// the server
/// </summary>
/// <param name="receivedData">data received</param>
internal void ProcessReceivedData ( RemoteDataObject < PSObject > receivedData )
{
if ( receivedData == null )
{
throw PSTraceSource . NewArgumentNullException ( "receivedData" );
}
Dbg . Assert ( receivedData . TargetInterface == RemotingTargetInterface . RunspacePool ,
"RemotingTargetInterface must be Runspace" );
switch ( receivedData . DataType )
{
case RemotingDataType . CreatePowerShell :
{
Dbg . Assert ( CreateAndInvokePowerShell != null ,
"The ServerRunspacePoolDriver should subscribe to all data structure handler events" );
CreateAndInvokePowerShell . SafeInvoke ( this , new RemoteDataEventArgs < RemoteDataObject < PSObject >>( receivedData ));
}
break ;
case RemotingDataType . GetCommandMetadata :
{
Dbg . Assert ( GetCommandMetadata != null ,
"The ServerRunspacePoolDriver should subscribe to all data structure handler events" );
GetCommandMetadata . SafeInvoke ( this , new RemoteDataEventArgs < RemoteDataObject < PSObject >>( receivedData ));
}
break ;
case RemotingDataType . RemoteRunspaceHostResponseData :
{
Dbg . Assert ( HostResponseReceived != null ,
"The ServerRunspacePoolDriver should subscribe to all data structure handler events" );
RemoteHostResponse remoteHostResponse = RemoteHostResponse . Decode ( receivedData . Data );
//part of host message robustness algo. Now the host response is back, report to transport that
// execution status is back to running
2016-07-29 13:02:49 -07:00
_transportManager . ReportExecutionStatusAsRunning ();
2016-03-30 14:20:52 -07:00
HostResponseReceived . SafeInvoke ( this , new RemoteDataEventArgs < RemoteHostResponse >( remoteHostResponse ));
}
break ;
case RemotingDataType . SetMaxRunspaces :
{
Dbg . Assert ( SetMaxRunspacesReceived != null ,
"The ServerRunspacePoolDriver should subscribe to all data structure handler events" );
SetMaxRunspacesReceived . SafeInvoke ( this , new RemoteDataEventArgs < PSObject >( receivedData . Data ));
}
break ;
case RemotingDataType . SetMinRunspaces :
{
Dbg . Assert ( SetMinRunspacesReceived != null ,
"The ServerRunspacePoolDriver should subscribe to all data structure handler events" );
SetMinRunspacesReceived . SafeInvoke ( this , new RemoteDataEventArgs < PSObject >( receivedData . Data ));
}
break ;
case RemotingDataType . AvailableRunspaces :
{
Dbg . Assert ( GetAvailableRunspacesReceived != null ,
"The ServerRunspacePoolDriver should subscribe to all data structure handler events" );
GetAvailableRunspacesReceived . SafeInvoke ( this , new RemoteDataEventArgs < PSObject >( receivedData . Data ));
}
break ;
case RemotingDataType . ResetRunspaceState :
{
Dbg . Assert ( ResetRunspaceState != null ,
"The ServerRunspacePoolDriver should subscribe to all data structure handler events." );
ResetRunspaceState . SafeInvoke ( this , new RemoteDataEventArgs < PSObject >( receivedData . Data ));
}
break ;
} // switch...
}
/// <summary>
/// Creates a powershell data structure handler from this runspace pool
/// </summary>
/// <param name="instanceId">powershell instance id</param>
/// <param name="runspacePoolId">runspace pool id</param>
/// <param name="remoteStreamOptions">remote stream options</param>
/// <param name="localPowerShell">local PowerShell object</param>
/// <returns>ServerPowerShellDataStructureHandler</returns>
internal ServerPowerShellDataStructureHandler CreatePowerShellDataStructureHandler (
Guid instanceId , Guid runspacePoolId , RemoteStreamOptions remoteStreamOptions , PowerShell localPowerShell )
{
// start with pool's transport manager.
2016-07-29 13:02:49 -07:00
AbstractServerTransportManager cmdTransportManager = _transportManager ;
2016-03-30 14:20:52 -07:00
if ( instanceId != Guid . Empty )
{
2016-07-29 13:02:49 -07:00
cmdTransportManager = _transportManager . GetCommandTransportManager ( instanceId );
2016-03-30 14:20:52 -07:00
Dbg . Assert ( cmdTransportManager . TypeTable != null , "This should be already set in managed C++ code" );
}
ServerPowerShellDataStructureHandler dsHandler =
new ServerPowerShellDataStructureHandler ( instanceId , runspacePoolId , remoteStreamOptions , cmdTransportManager , localPowerShell );
2016-07-29 13:02:49 -07:00
lock ( _associationSyncObject )
2016-03-30 14:20:52 -07:00
{
2016-07-29 13:02:49 -07:00
_associatedShells . Add ( dsHandler . PowerShellId , dsHandler );
2016-03-30 14:20:52 -07:00
}
dsHandler . RemoveAssociation += new EventHandler ( HandleRemoveAssociation );
return dsHandler ;
}
/// <summary>
/// Returns the currently active PowerShell datastructure handler.
/// </summary>
/// <returns>
/// ServerPowerShellDataStructureHandler if one is present, null otherwise.
/// </returns>
internal ServerPowerShellDataStructureHandler GetPowerShellDataStructureHandler ()
{
2016-07-29 13:02:49 -07:00
lock ( _associationSyncObject )
2016-03-30 14:20:52 -07:00
{
2016-07-29 13:02:49 -07:00
if ( _associatedShells . Count > 0 )
2016-03-30 14:20:52 -07:00
{
2016-07-29 13:02:49 -07:00
foreach ( object o in _associatedShells . Values )
2016-03-30 14:20:52 -07:00
{
ServerPowerShellDataStructureHandler result = o as ServerPowerShellDataStructureHandler ;
if ( result != null )
{
return result ;
}
}
}
}
return null ;
}
/// <summary>
/// dispatch the message to the associated powershell data structure handler
/// </summary>
/// <param name="rcvdData">message to dispatch</param>
internal void DispatchMessageToPowerShell ( RemoteDataObject < PSObject > rcvdData )
{
ServerPowerShellDataStructureHandler dsHandler =
GetAssociatedPowerShellDataStructureHandler ( rcvdData . PowerShellId );
// if data structure handler is not found, then association has already been
// removed, discard message
if ( dsHandler != null )
{
dsHandler . ProcessReceivedData ( rcvdData );
}
}
/// <summary>
/// Send the specified response to the client. The client call will
/// be blocked on the same
/// </summary>
/// <param name="callId">call id on the client</param>
/// <param name="response">response to send</param>
internal void SendResponseToClient ( long callId , object response )
{
2016-07-29 13:02:49 -07:00
RemoteDataObject message =
RemotingEncoder . GenerateRunspacePoolOperationResponse ( _clientRunspacePoolId , response , callId );
2016-03-30 14:20:52 -07:00
SendDataAsync ( message );
}
/// <summary>
/// TypeTable used for Serialization/Deserialization.
/// </summary>
internal TypeTable TypeTable
{
2016-07-29 13:02:49 -07:00
get { return _transportManager . TypeTable ; }
set { _transportManager . TypeTable = value ; }
2016-03-30 14:20:52 -07:00
}
#endregion Data Structure Handler Methods
#region Data Structure Handler events
/// <summary>
/// This event is raised whenever there is a request from the
/// client to create a powershell on the server and invoke it
/// </summary>
internal event EventHandler < RemoteDataEventArgs < RemoteDataObject < PSObject >>> CreateAndInvokePowerShell ;
/// <summary>
/// This event is raised whenever there is a request from the
/// client to run command discovery pipeline
/// </summary>
internal event EventHandler < RemoteDataEventArgs < RemoteDataObject < PSObject >>> GetCommandMetadata ;
/// <summary>
/// This event is raised when a host call response is received
/// </summary>
internal event EventHandler < RemoteDataEventArgs < RemoteHostResponse >> HostResponseReceived ;
/// <summary>
/// This event is raised when there is a request to modify the
/// maximum runspaces in the runspace pool
/// </summary>
internal event EventHandler < RemoteDataEventArgs < PSObject >> SetMaxRunspacesReceived ;
/// <summary>
/// This event is raised when there is a request to modify the
/// minimum runspaces in the runspace pool
/// </summary>
internal event EventHandler < RemoteDataEventArgs < PSObject >> SetMinRunspacesReceived ;
/// <summary>
/// This event is raised when there is a request to get the
/// available runspaces in the runspace pool
/// </summary>
internal event EventHandler < RemoteDataEventArgs < PSObject >> GetAvailableRunspacesReceived ;
/// <summary>
/// This event is raised when the client requests the runspace state
/// to be reset.
/// </summary>
internal event EventHandler < RemoteDataEventArgs < PSObject >> ResetRunspaceState ;
#endregion Data Structure Handler events
#region Private Methods
/// <summary>
/// Send the data specified as a RemoteDataObject asynchronously
/// to the runspace pool on the remote session
/// </summary>
/// <param name="data">data to send</param>
/// <remarks>This overload takes a RemoteDataObject and should
/// be the one thats used to send data from within this
/// data structure handler class</remarks>
private void SendDataAsync ( RemoteDataObject data )
{
Dbg . Assert ( null != data , "Cannot send null object." );
2016-07-29 13:02:49 -07:00
_transportManager . SendDataToClient ( data , true );
2016-03-30 14:20:52 -07:00
}
/// <summary>
/// Get the associated powershell data structure handler for the specified
/// powershell id
/// </summary>
/// <param name="clientPowerShellId">powershell id for the
/// powershell data structure handler</param>
/// <returns>ServerPowerShellDataStructureHandler</returns>
internal ServerPowerShellDataStructureHandler GetAssociatedPowerShellDataStructureHandler
( Guid clientPowerShellId )
{
ServerPowerShellDataStructureHandler dsHandler = null ;
2016-07-29 13:02:49 -07:00
lock ( _associationSyncObject )
2016-03-30 14:20:52 -07:00
{
2016-07-29 13:02:49 -07:00
bool success = _associatedShells . TryGetValue ( clientPowerShellId , out dsHandler );
2016-03-30 14:20:52 -07:00
if (! success )
{
dsHandler = null ;
}
}
return dsHandler ;
}
/// <summary>
/// Remove the association of the powershell from the runspace pool
/// </summary>
/// <param name="sender">sender of this event</param>
/// <param name="e">unused</param>
private void HandleRemoveAssociation ( object sender , EventArgs e )
{
Dbg . Assert ( sender is ServerPowerShellDataStructureHandler , @"sender of the event
must be ServerPowerShellDataStructureHandler" );
ServerPowerShellDataStructureHandler dsHandler = sender as ServerPowerShellDataStructureHandler ;
2016-07-29 13:02:49 -07:00
lock ( _associationSyncObject )
2016-03-30 14:20:52 -07:00
{
2016-07-29 13:02:49 -07:00
_associatedShells . Remove ( dsHandler . PowerShellId );
2016-03-30 14:20:52 -07:00
}
// let session transport manager remove its association of command transport manager.
2016-07-29 13:02:49 -07:00
_transportManager . RemoveCommandTransportManager ( dsHandler . PowerShellId );
2016-03-30 14:20:52 -07:00
}
#endregion Private Methods
#region Private Members
2016-07-29 13:02:49 -07:00
private Guid _clientRunspacePoolId ;
2016-03-30 14:20:52 -07:00
// transport manager using which this
// runspace pool driver handles all client
// communication
2016-07-29 13:02:49 -07:00
private AbstractServerSessionTransportManager _transportManager ;
private Dictionary < Guid , ServerPowerShellDataStructureHandler > _associatedShells
2016-03-30 14:20:52 -07:00
= new Dictionary < Guid , ServerPowerShellDataStructureHandler >();
2016-07-29 13:02:49 -07:00
// powershell data structure handlers associated with this
// runspace pool data structure handler
private object _associationSyncObject = new object ();
// object to synchronize operations to above
2016-03-30 14:20:52 -07:00
#endregion Private Members
}
/// <summary>
/// Handles all PowerShell data structure handler communication
/// with the client side PowerShell
/// </summary>
internal class ServerPowerShellDataStructureHandler
{
#region Private Members
// transport manager using which this
// powershell driver handles all client
// communication
2016-07-29 13:02:49 -07:00
private AbstractServerTransportManager _transportManager ;
private Guid _clientRunspacePoolId ;
private Guid _clientPowerShellId ;
private RemoteStreamOptions _streamSerializationOptions ;
private Runspace _rsUsedToInvokePowerShell ;
2016-03-30 14:20:52 -07:00
#endregion Private Members
#region Constructors
/// <summary>
/// Default constructor for creating ServerPowerShellDataStructureHandler
/// instance
/// </summary>
/// <param name="instanceId">powershell instance id</param>
/// <param name="runspacePoolId">runspace pool id</param>
/// <param name="remoteStreamOptions">remote stream options</param>
/// <param name="transportManager">transport manager</param>
/// <param name="localPowerShell">local powershell object</param>
internal ServerPowerShellDataStructureHandler ( Guid instanceId , Guid runspacePoolId , RemoteStreamOptions remoteStreamOptions ,
AbstractServerTransportManager transportManager , PowerShell localPowerShell )
{
2016-07-29 13:02:49 -07:00
_clientPowerShellId = instanceId ;
_clientRunspacePoolId = runspacePoolId ;
_transportManager = transportManager ;
_streamSerializationOptions = remoteStreamOptions ;
2016-03-30 14:20:52 -07:00
transportManager . Closing += HandleTransportClosing ;
if ( localPowerShell != null )
{
localPowerShell . RunspaceAssigned +=
new EventHandler < PSEventArgs < Runspace >>( LocalPowerShell_RunspaceAssigned );
}
}
2016-07-29 13:02:49 -07:00
private void LocalPowerShell_RunspaceAssigned ( object sender , PSEventArgs < Runspace > e )
2016-03-30 14:20:52 -07:00
{
2016-07-29 13:02:49 -07:00
_rsUsedToInvokePowerShell = e . Args ;
2016-03-30 14:20:52 -07:00
}
#endregion Constructors
#region Data Structure Handler Methods
/// <summary>
/// Prepare transport manager to send data to client.
/// </summary>
internal void Prepare ()
{
// When Guid.Empty is used, PowerShell must be using pool's transport manager
// to send data to client. so we dont need to prepare command transport manager
2016-07-29 13:02:49 -07:00
if ( _clientPowerShellId != Guid . Empty )
2016-03-30 14:20:52 -07:00
{
2016-07-29 13:02:49 -07:00
_transportManager . Prepare ();
2016-03-30 14:20:52 -07:00
}
}
/// <summary>
/// Send the state information to the client
/// </summary>
/// <param name="stateInfo">state information to be
/// sent to the client</param>
internal void SendStateChangedInformationToClient ( PSInvocationStateInfo
stateInfo )
{
Dbg . Assert (( stateInfo . State == PSInvocationState . Completed ) ||
( stateInfo . State == PSInvocationState . Failed ) ||
( stateInfo . State == PSInvocationState . Stopped ),
"SendStateChangedInformationToClient should be called to notify a termination state" );
SendDataAsync ( RemotingEncoder . GeneratePowerShellStateInfo (
2016-07-29 13:02:49 -07:00
stateInfo , _clientPowerShellId , _clientRunspacePoolId ));
2016-03-30 14:20:52 -07:00
// Close the transport manager only if the PowerShell Guid != Guid.Empty.
// When Guid.Empty is used, PowerShell must be using pool's transport manager
// to send data to client.
2016-07-29 13:02:49 -07:00
if ( _clientPowerShellId != Guid . Empty )
2016-03-30 14:20:52 -07:00
{
// no need to listen for closing events as we are initiating the close
2016-07-29 13:02:49 -07:00
_transportManager . Closing -= HandleTransportClosing ;
2016-03-30 14:20:52 -07:00
// if terminal state is reached close the transport manager instead of letting
2016-08-28 05:34:28 +00:00
// the client initiate the close.
2016-07-29 13:02:49 -07:00
_transportManager . Close ( null );
2016-03-30 14:20:52 -07:00
}
}
/// <summary>
/// Send the output data to the client
/// </summary>
/// <param name="data">data to send</param>
internal void SendOutputDataToClient ( PSObject data )
{
SendDataAsync ( RemotingEncoder . GeneratePowerShellOutput ( data ,
2016-07-29 13:02:49 -07:00
_clientPowerShellId , _clientRunspacePoolId ));
2016-03-30 14:20:52 -07:00
}
/// <summary>
/// Send the error record to client
/// </summary>
/// <param name="errorRecord">error record to send</param>
internal void SendErrorRecordToClient ( ErrorRecord errorRecord )
{
2016-07-29 13:02:49 -07:00
errorRecord . SerializeExtendedInfo = ( _streamSerializationOptions & RemoteStreamOptions . AddInvocationInfoToErrorRecord ) != 0 ;
2016-03-30 14:20:52 -07:00
SendDataAsync ( RemotingEncoder . GeneratePowerShellError (
2016-07-29 13:02:49 -07:00
errorRecord , _clientRunspacePoolId , _clientPowerShellId ));
2016-03-30 14:20:52 -07:00
}
/// <summary>
/// Send the specified warning record to client
/// </summary>
/// <param name="record">warning record</param>
internal void SendWarningRecordToClient ( WarningRecord record )
{
2016-07-29 13:02:49 -07:00
record . SerializeExtendedInfo = ( _streamSerializationOptions & RemoteStreamOptions . AddInvocationInfoToWarningRecord ) != 0 ;
2016-03-30 14:20:52 -07:00
SendDataAsync ( RemotingEncoder . GeneratePowerShellInformational (
2016-07-29 13:02:49 -07:00
record , _clientRunspacePoolId , _clientPowerShellId , RemotingDataType . PowerShellWarning ));
2016-03-30 14:20:52 -07:00
}
/// <summary>
/// Send the specified debug record to client
/// </summary>
/// <param name="record">debug record</param>
internal void SendDebugRecordToClient ( DebugRecord record )
{
2016-07-29 13:02:49 -07:00
record . SerializeExtendedInfo = ( _streamSerializationOptions & RemoteStreamOptions . AddInvocationInfoToDebugRecord ) != 0 ;
2016-03-30 14:20:52 -07:00
SendDataAsync ( RemotingEncoder . GeneratePowerShellInformational (
2016-07-29 13:02:49 -07:00
record , _clientRunspacePoolId , _clientPowerShellId , RemotingDataType . PowerShellDebug ));
2016-03-30 14:20:52 -07:00
}
/// <summary>
/// Send the specified verbose record to client
/// </summary>
/// <param name="record">warning record</param>
internal void SendVerboseRecordToClient ( VerboseRecord record )
{
2016-07-29 13:02:49 -07:00
record . SerializeExtendedInfo = ( _streamSerializationOptions & RemoteStreamOptions . AddInvocationInfoToVerboseRecord ) != 0 ;
2016-03-30 14:20:52 -07:00
SendDataAsync ( RemotingEncoder . GeneratePowerShellInformational (
2016-07-29 13:02:49 -07:00
record , _clientRunspacePoolId , _clientPowerShellId , RemotingDataType . PowerShellVerbose ));
2016-03-30 14:20:52 -07:00
}
/// <summary>
/// Send the specified progress record to client
/// </summary>
/// <param name="record">progress record</param>
internal void SendProgressRecordToClient ( ProgressRecord record )
{
SendDataAsync ( RemotingEncoder . GeneratePowerShellInformational (
2016-07-29 13:02:49 -07:00
record , _clientRunspacePoolId , _clientPowerShellId ));
2016-03-30 14:20:52 -07:00
}
/// <summary>
/// Send the specified information record to client
/// </summary>
/// <param name="record">information record</param>
internal void SendInformationRecordToClient ( InformationRecord record )
{
SendDataAsync ( RemotingEncoder . GeneratePowerShellInformational (
2016-07-29 13:02:49 -07:00
record , _clientRunspacePoolId , _clientPowerShellId ));
2016-03-30 14:20:52 -07:00
}
/// <summary>
/// called when session is connected from a new client
/// calls into observers of this event.
2016-08-28 05:34:28 +00:00
/// observers include corresponding driver that shutdown
2016-03-30 14:20:52 -07:00
/// input stream is present
/// </summary>
internal void ProcessConnect ()
{
OnSessionConnected . SafeInvoke ( this , EventArgs . Empty );
}
/// <summary>
/// Process the data received from the powershell on
/// the client
/// </summary>
/// <param name="receivedData">data received</param>
internal void ProcessReceivedData ( RemoteDataObject < PSObject > receivedData )
{
if ( receivedData == null )
{
throw PSTraceSource . NewArgumentNullException ( "receivedData" );
}
Dbg . Assert ( receivedData . TargetInterface == RemotingTargetInterface . PowerShell ,
"RemotingTargetInterface must be PowerShell" );
switch ( receivedData . DataType )
{
case RemotingDataType . StopPowerShell :
{
Dbg . Assert ( StopPowerShellReceived != null ,
"ServerPowerShellDriver should subscribe to all data structure handler events" );
StopPowerShellReceived . SafeInvoke ( this , EventArgs . Empty );
}
break ;
case RemotingDataType . PowerShellInput :
{
Dbg . Assert ( InputReceived != null ,
"ServerPowerShellDriver should subscribe to all data structure handler events" );
InputReceived . SafeInvoke ( this , new RemoteDataEventArgs < object >( receivedData . Data ));
}
break ;
2016-07-29 13:02:49 -07:00
2016-03-30 14:20:52 -07:00
case RemotingDataType . PowerShellInputEnd :
{
Dbg . Assert ( InputEndReceived != null ,
"ServerPowerShellDriver should subscribe to all data structure handler events" );
InputEndReceived . SafeInvoke ( this , EventArgs . Empty );
}
break ;
case RemotingDataType . RemotePowerShellHostResponseData :
{
Dbg . Assert ( HostResponseReceived != null ,
2016-09-07 21:27:57 +00:00
"ServerPowerShellDriver should subscribe to all data structure handler events" );
2016-03-30 14:20:52 -07:00
RemoteHostResponse remoteHostResponse = RemoteHostResponse . Decode ( receivedData . Data );
//part of host message robustness algo. Now the host response is back, report to transport that
// execution status is back to running
2016-07-29 13:02:49 -07:00
_transportManager . ReportExecutionStatusAsRunning ();
2016-03-30 14:20:52 -07:00
HostResponseReceived . SafeInvoke ( this , new RemoteDataEventArgs < RemoteHostResponse >( remoteHostResponse ));
}
break ;
} // switch ...
}
2016-07-29 13:02:49 -07:00
/// <summary>
2016-03-30 14:20:52 -07:00
/// Raise a remove association event. This is raised
/// when the powershell has gone into a terminal state
/// and the runspace pool need not maintain any further
/// associations
/// </summary>
internal void RaiseRemoveAssociationEvent ()
{
Dbg . Assert ( RemoveAssociation != null , @"The ServerRunspacePoolDataStructureHandler should subscribe
to the RemoveAssociation event of ServerPowerShellDataStructureHandler" );
RemoveAssociation . SafeInvoke ( this , EventArgs . Empty );
}
/// <summary>
/// Creates a ServerRemoteHost which is associated with this powershell.
/// </summary>
/// <param name="powerShellHostInfo">Host information about the host associated
/// PowerShell object on the client.</param>
/// <param name="runspaceServerRemoteHost">Host associated with the RunspacePool
/// on the server.</param>
/// <returns>A new ServerRemoteHost for the PowerShell.</returns>
internal ServerRemoteHost GetHostAssociatedWithPowerShell (
HostInfo powerShellHostInfo ,
ServerRemoteHost runspaceServerRemoteHost )
{
HostInfo hostInfo ;
// If host was null use the runspace's host for this powershell; otherwise,
// use the HostInfo to create a proxy host of the powershell's host.
if ( powerShellHostInfo . UseRunspaceHost )
{
hostInfo = runspaceServerRemoteHost . HostInfo ;
}
else
{
hostInfo = powerShellHostInfo ;
}
// If the host was not null on the client, then the PowerShell object should
// get a brand spanking new host.
2016-07-29 13:02:49 -07:00
return new ServerRemoteHost ( _clientRunspacePoolId , _clientPowerShellId , hostInfo ,
_transportManager , runspaceServerRemoteHost . Runspace , runspaceServerRemoteHost as ServerDriverRemoteHost );
2016-03-30 14:20:52 -07:00
}
#endregion Data Structure Handler Methods
#region Data Structure Handler events
/// <summary>
/// this event is raised when the state of associated
/// powershell is terminal and the runspace pool has
/// to detach the association
/// </summary>
internal event EventHandler RemoveAssociation ;
/// <summary>
/// this event is raised when the a message to stop the
/// powershell is received from the client
/// </summary>
internal event EventHandler StopPowerShellReceived ;
/// <summary>
/// This event is raised when an input object is received
/// from the client
/// </summary>
internal event EventHandler < RemoteDataEventArgs < object >> InputReceived ;
/// <summary>
/// This event is raised when end of input is received from
/// the client
/// </summary>
internal event EventHandler InputEndReceived ;
/// <summary>
/// raised when server session is connected from a new client
/// </summary>
internal event EventHandler OnSessionConnected ;
/// <summary>
/// This event is raised when a host response is received
/// </summary>
internal event EventHandler < RemoteDataEventArgs < RemoteHostResponse >> HostResponseReceived ;
#endregion Data Structure Handler events
#region Internal Methods
/// <summary>
/// client powershell id
/// </summary>
internal Guid PowerShellId
{
get
{
2016-07-29 13:02:49 -07:00
return _clientPowerShellId ;
2016-03-30 14:20:52 -07:00
}
}
/// <summary>
/// Runspace used to invoke PowerShell, this is used by the steppable
/// pipeline driver.
/// </summary>
internal Runspace RunspaceUsedToInvokePowerShell
{
2016-07-29 13:02:49 -07:00
get { return _rsUsedToInvokePowerShell ; }
2016-03-30 14:20:52 -07:00
}
#endregion Internal Methods
#region Private Methods
/// <summary>
/// Send the data specified as a RemoteDataObject asynchronously
/// to the runspace pool on the remote session
/// </summary>
/// <param name="data">data to send</param>
/// <remarks>This overload takes a RemoteDataObject and should
/// be the one thats used to send data from within this
/// data structure handler class</remarks>
private void SendDataAsync ( RemoteDataObject data )
{
Dbg . Assert ( null != data , "Cannot send null object." );
// this is from a command execution..let transport manager collect
// as much data as possible and send bigger buffer to client.
2016-07-29 13:02:49 -07:00
_transportManager . SendDataToClient ( data , false );
2016-03-30 14:20:52 -07:00
}
/// <summary>
/// Handle transport manager's closing event.
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
private void HandleTransportClosing ( object sender , EventArgs args )
{
StopPowerShellReceived . SafeInvoke ( this , args );
}
#endregion Private Methods
}
}