File: channels\ipc\ipcclientchannel.cs
Project: ndp\clr\src\managedlibraries\remoting\System.Runtime.Remoting.csproj (System.Runtime.Remoting)
// ==++==
// 
//   Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// ==--==
//===========================================================================
//  File:       IpcClientChannel.cs
//  Author:   Microsoft@Microsoft.Com
//  Summary:    Implements a channel that transmits method calls over Ipc.
//
//==========================================================================
 
using System;
using System.Collections;
using System.IO;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Messaging;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Threading;
using System.Text;
using System.Globalization;
using System.Security.Permissions;
 
 
namespace System.Runtime.Remoting.Channels.Ipc
{
    internal delegate IClientChannelSinkStack AsyncMessageDelegate(IMessage msg,
                                                               ITransportHeaders requestHeaders, Stream requestStream,
                                                               out ITransportHeaders responseHeaders, out Stream responseStream,
                                                               IClientChannelSinkStack sinkStack);
    
    public class IpcClientChannel : IChannelSender, ISecurableChannel
    {
        private int    _channelPriority = 1;  // channel priority
        private String _channelName = "ipc client"; // channel name
        private bool _secure = false; // default is unsecure
        private IDictionary _prop = null;
        
        private IClientChannelSinkProvider _sinkProvider = null; // sink chain provider
        
 
        public IpcClientChannel()
        {
            SetupChannel();        
        } // IpcClientChannel
 
 
        public IpcClientChannel(String name, IClientChannelSinkProvider sinkProvider)
        {
            _channelName = name;
            _sinkProvider = sinkProvider;
 
            SetupChannel();
        }
 
 
        // constructor used by config file
        public IpcClientChannel(IDictionary properties, IClientChannelSinkProvider sinkProvider)
        {
            if (properties != null)
            {
                _prop = properties;
                foreach (DictionaryEntry entry in properties)
                {
                    switch ((String)entry.Key)
                    {
                    case "name": _channelName = (String)entry.Value; break;
                    case "priority": _channelPriority = Convert.ToInt32(entry.Value, CultureInfo.InvariantCulture); break;
                    case "secure": _secure = Convert.ToBoolean(entry.Value, CultureInfo.InvariantCulture); break;
                    default: 
                         break;
                    }
                }
            }
 
            _sinkProvider = sinkProvider;
 
            SetupChannel();
        } // IpcClientChannel
 
 
        private void SetupChannel()
        {
            if (_sinkProvider != null)
            {
                CoreChannel.AppendProviderToClientProviderChain(
                    _sinkProvider, new IpcClientTransportSinkProvider(_prop));                                                
            }
            else
                _sinkProvider = CreateDefaultClientProviderChain();
        } // SetupChannel
 
        
        //
        // ISecurableChannel implementation
        //
        public bool IsSecured
        {
            [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.Infrastructure, Infrastructure=true)]
            get { return _secure; }
            [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.Infrastructure, Infrastructure=true)]
            set { _secure = value; }
        }
        
        //
        // IChannel implementation
        //
 
        public int ChannelPriority
        {
            [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.Infrastructure, Infrastructure=true)]
            get { return _channelPriority; }    
        }
 
        public String ChannelName
        {
            [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.Infrastructure, Infrastructure=true)]
            get { return _channelName; }
        }
 
        // returns channelURI and places object uri into out parameter
        [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.Infrastructure, Infrastructure=true)]
        public String Parse(String url, out String objectURI)
        {            
            return IpcChannelHelper.ParseURL(url, out objectURI);
        } // Parse
 
        //
        // end of IChannel implementation
        // 
 
 
 
        //
        // IChannelSender implementation
        //
 
        [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.Infrastructure, Infrastructure=true)]
        public virtual IMessageSink CreateMessageSink(String url, Object remoteChannelData, out String objectURI)
        {
            // Set the out parameters
            objectURI = null;
            String channelURI = null;
 
            
            if (url != null) // Is this a well known object?
            {
                // Parse returns null if this is not one of our url's
                channelURI = Parse(url, out objectURI);
            }
            else // determine if we want to connect based on the channel data
            {
                if (remoteChannelData != null)
                {
                    if (remoteChannelData is IChannelDataStore)
                    {
                        IChannelDataStore cds = (IChannelDataStore)remoteChannelData;
 
                        // see if this is an Ipc uri
                        String simpleChannelUri = Parse(cds.ChannelUris[0], out objectURI);
                        if (simpleChannelUri != null)
                            channelURI = cds.ChannelUris[0];
                    }
                }
            }
 
            if (null != channelURI)
            {
                if (url == null)
                    url = channelURI;
 
                IClientChannelSink sink = _sinkProvider.CreateSink(this, url, remoteChannelData);
                
                // return sink after making sure that it implements IMessageSink
                IMessageSink msgSink = sink as IMessageSink;
                if ((sink != null) && (msgSink == null))
                {
                    throw new RemotingException(
                        CoreChannel.GetResourceString(
                            "Remoting_Channels_ChannelSinkNotMsgSink"));
                }
                    
                return msgSink;           
            }
 
            return null;
        } // CreateMessageSink
 
 
        //
        // end of IChannelSender implementation
        //
 
        private IClientChannelSinkProvider CreateDefaultClientProviderChain()
        {
            IClientChannelSinkProvider chain = new BinaryClientFormatterSinkProvider();            
            IClientChannelSinkProvider sink = chain;
            
            sink.Next = new IpcClientTransportSinkProvider( _prop);
            
            return chain;
        } // CreateDefaultClientProviderChain
 
    } // class IpcClientChannel
 
 
 
 
    internal class IpcClientTransportSinkProvider : IClientChannelSinkProvider
    {
        IDictionary _prop = null;
        internal IpcClientTransportSinkProvider(IDictionary properties )
        {
            _prop = properties;
        }    
   
        public IClientChannelSink CreateSink(IChannelSender channel, String url, 
                                             Object remoteChannelData)
        {
            // url is set to the channel uri in CreateMessageSink        
            IpcClientTransportSink sink = new IpcClientTransportSink(url, (IpcClientChannel) channel);
            if (_prop != null)
            {
                // Tansfer all properties from the channel to the sink
                foreach(Object key in _prop.Keys)
                {
                    sink[key] = _prop[key];
                }
            }
            return sink;
        }
 
        public IClientChannelSinkProvider Next
        {
            get { return null; }
            set { throw new NotSupportedException(); }
        }
    } // class IpcClientTransportSinkProvider
 
 
 
    internal class IpcClientTransportSink : BaseChannelSinkWithProperties, IClientChannelSink
    {
        // Port cache
        private ConnectionCache portCache = new ConnectionCache();
        
        private IpcClientChannel _channel = null;
 
        // port name
        private String _portName;
        private const String TokenImpersonationLevelKey = "tokenimpersonationlevel";
        private bool authSet = false;
        private const String ConnectionTimeoutKey = "connectiontimeout";
        private TokenImpersonationLevel _tokenImpersonationLevel = TokenImpersonationLevel.Identification; 
        private int _timeout = 1000; // Default is one second
        private static ICollection s_keySet = null;
 
        internal IpcClientTransportSink(String channelURI, IpcClientChannel channel)
        {
            String objectURI;
            _channel = channel;
            // Parse the URI 
            String simpleChannelUri = IpcChannelHelper.ParseURL(channelURI, out objectURI);
            // extract machine name and port
            int start = simpleChannelUri.IndexOf("://");
            start += 3;
 
            _portName = simpleChannelUri.Substring(start);;
        } // IpcClientTransportSink
 
        internal ConnectionCache Cache { get { return portCache; } }
 
        public void ProcessMessage(IMessage msg,
                                   ITransportHeaders requestHeaders, Stream requestStream,
                                   out ITransportHeaders responseHeaders, out Stream responseStream)
        {
            InternalRemotingServices.RemotingTrace("IpcClientChannel::ProcessMessage");
            IpcPort port = null;
 
            // the call to SendRequest can block a func eval, so we want to notify the debugger that we're
            // about to call a blocking operation. 
            System.Diagnostics.Debugger.NotifyOfCrossThreadDependency();            
 
            // The authentication config entries are only valid if secure is true
            if (authSet && ! _channel.IsSecured)
                throw new RemotingException(CoreChannel.GetResourceString(
                                                "Remoting_Ipc_AuthenticationConfig"));
 
            port = portCache.GetConnection(_portName, _channel.IsSecured, _tokenImpersonationLevel, _timeout);
 
            IMethodCallMessage mcm = (IMethodCallMessage)msg;
            int requestLength = (int)requestStream.Length;
 
            Stream ipcStream = new PipeStream(port);
            IpcClientHandler handler = new IpcClientHandler(port, ipcStream, this);
            handler.SendRequest(msg, requestHeaders, requestStream);
            responseHeaders = handler.ReadHeaders();
            responseStream = handler.GetResponseStream();
 
            // The client port will be returned to the cache
            //   when the response stream is closed.
 
        } // ProcessMessage
 
        private IClientChannelSinkStack AsyncProcessMessage(IMessage msg,
                                   ITransportHeaders requestHeaders, Stream requestStream,
                                   out ITransportHeaders responseHeaders, out Stream responseStream, 
                                   IClientChannelSinkStack sinkStack)
        {
            ProcessMessage(msg, requestHeaders, requestStream, out responseHeaders, out responseStream);
            return sinkStack;
        }
 
        public void AsyncProcessRequest(IClientChannelSinkStack sinkStack, IMessage msg,
                                        ITransportHeaders headers, Stream requestStream)
        {
            InternalRemotingServices.RemotingTrace("IpcClientTransportSink::AsyncProcessRequest");
            ITransportHeaders responseHeaders;
            Stream responseStream;
            // Invoke ProcessMessage asynchronously. This should be improved
            // by using Datagram message 
            AsyncCallback callback = new AsyncCallback(this.AsyncFinishedCallback);
            AsyncMessageDelegate async = new AsyncMessageDelegate(this.AsyncProcessMessage);
            async.BeginInvoke(msg, headers, requestStream , out responseHeaders, 
                                                         out responseStream, sinkStack, callback, null);
        } // AsyncProcessRequest
 
        private void AsyncFinishedCallback(IAsyncResult ar)
        {
            IClientChannelSinkStack sinkStack = null;
            try            
            {
                AsyncMessageDelegate d = (AsyncMessageDelegate)(((AsyncResult)ar).AsyncDelegate);
                ITransportHeaders responseHeaders;
                Stream responseStream;
                sinkStack = d.EndInvoke(out responseHeaders, out responseStream, ar);
                // call down the sink chain
                sinkStack.AsyncProcessResponse(responseHeaders, responseStream);
            }
            catch (Exception e)
            {
                try
                {
                    if (sinkStack != null)
                        sinkStack.DispatchException(e);
                }
                catch
                {
                    // Fatal Error.. ignore
                }
            }
 
        }
 
        public void AsyncProcessResponse(IClientResponseChannelSinkStack sinkStack, Object state,
                                         ITransportHeaders headers, Stream stream)
        {
            // We don't have to implement this since we are always last in the chain.
            throw new NotSupportedException();
        } // AsyncProcessRequest
        
 
        
        public Stream GetRequestStream(IMessage msg, ITransportHeaders headers)
        {
            // Currently, we require a memory stream be handed to us since we need
            //   the length before sending.      
            return null; 
        } // GetRequestStream
 
 
        public IClientChannelSink NextChannelSink
        {
            get { return null; }
        } // Next
 
 
 
        //
        // Properties
        //
        public override Object this[Object key]
        {
            get
            {
                String keyStr = key as String;
                if (keyStr == null)
                    return null;
            
                switch (keyStr.ToLower(CultureInfo.InvariantCulture))
                {
                    case TokenImpersonationLevelKey: return _tokenImpersonationLevel.ToString();
                    case ConnectionTimeoutKey: return _timeout;
                    default:
                        break;
                } // switch (keyStr.ToLower(CultureInfo.InvariantCulture))
 
                return null; 
            }
        
            set
            {
                String keyStr = key as String;
                if (keyStr == null)
                    return;
    
                switch (keyStr.ToLower(CultureInfo.InvariantCulture))
                {
                    case TokenImpersonationLevelKey: _tokenImpersonationLevel = (TokenImpersonationLevel)(value is TokenImpersonationLevel ? value :
                                                                                            Enum.Parse(typeof(TokenImpersonationLevel),
                                                                                               (String)value, true)); 
                                                     authSet = true;
                          break;
                    case ConnectionTimeoutKey: _timeout = Convert.ToInt32(value, CultureInfo.InvariantCulture); 
                          break;
                    default:
                          break;
                } // switch (keyStr.ToLower(CultureInfo.InvariantCulturey))
            }
        } // this[]   
        
 
        public override ICollection Keys
        {
            get
            {
                if (s_keySet == null)
                {
                    // Don't need to synchronize. Doesn't matter if the list gets
                    // generated twice.
                }
 
                return s_keySet;
            }
        } // Keys
 
        //
        // end of Properties
        //
 
    } // class IpcClientTransportSink
 
 
} // namespace namespace System.Runtime.Remoting.Channels.Ipc