New test.
[mono.git] / mcs / class / System.Runtime.Remoting / System.Runtime.Remoting.Channels.Tcp / TcpServerChannel.cs
index d557622497b0310faaf39c448345e0de0553b72e..300954769ed8d3d11ccbeaf0f8d816e32c297989 100644 (file)
@@ -7,6 +7,27 @@
 // 2002 (C) Copyright, Ximian, Inc.
 //
 
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
 using System.Collections;
 using System.Runtime.Remoting.Messaging;
 using System.Text.RegularExpressions;
@@ -14,6 +35,7 @@ using System.Net.Sockets;
 using System.Net;
 using System.Threading;
 using System.IO;
+using System.Runtime.Remoting.Channels;
 
 namespace System.Runtime.Remoting.Channels.Tcp
 {
@@ -24,20 +46,23 @@ namespace System.Runtime.Remoting.Channels.Tcp
                string host = null;
                int priority = 1;
                bool supressChannelData = false;
-               bool useIpAddress = false;
+               bool useIpAddress = true;
                
                IPAddress bindAddress = IPAddress.Any;
                Thread server_thread = null;
                TcpListener listener;
                TcpServerTransportSink sink;
                ChannelDataStore channel_data;
-               int _maxConcurrentConnections = 100;
-               ArrayList _activeConnections = new ArrayList();
                
+               RemotingThreadPool threadPool;
                
+#if TARGET_JVM
+               private volatile bool stopped = false;
+#endif
+
                void Init (IServerChannelSinkProvider serverSinkProvider) 
                {
-                       if (serverSinkProvider == null) \r
+                       if (serverSinkProvider == null) 
                        {
                                serverSinkProvider = new BinaryServerFormatterSinkProvider ();
                        }
@@ -45,34 +70,30 @@ namespace System.Runtime.Remoting.Channels.Tcp
                        if (host == null)
                        {
                                if (useIpAddress) {
-                                       IPHostEntry he = Dns.Resolve (Dns.GetHostName());
-                                       if (he.AddressList.Length == 0) throw new RemotingException ("IP address could not be determined for this host");
-                                       host = he.AddressList [0].ToString ();
+                                       if (!bindAddress.Equals(IPAddress.Any)) host = bindAddress.ToString ();
+                                       else {
+                                               IPHostEntry he = Dns.Resolve (Dns.GetHostName());
+                                               if (he.AddressList.Length == 0) throw new RemotingException ("IP address could not be determined for this host");
+                                               host = he.AddressList [0].ToString ();
+                                       }
                                }
                                else
                                        host = Dns.GetHostByName(Dns.GetHostName()).HostName;
                        }
                        
-                       string [] uris = null;
-                       
-                       if (port != 0) {
-                               uris = new String [1];
-                               uris [0] = GetChannelUri ();
-                       }
-
                        // Gets channel data from the chain of channel providers
 
-                       channel_data = new ChannelDataStore (uris);
-                       IServerChannelSinkProvider provider = serverSinkProvider;\r
-                       while (provider != null)\r
-                       {\r
-                               provider.GetChannelData(channel_data);\r
-                               provider = provider.Next;\r
-                       }\r
+                       channel_data = new ChannelDataStore (null);
+                       IServerChannelSinkProvider provider = serverSinkProvider;
+                       while (provider != null)
+                       {
+                               provider.GetChannelData(channel_data);
+                               provider = provider.Next;
+                       }
 
                        // Creates the sink chain that will process all incoming messages
 
-                       IServerChannelSink next_sink = ChannelServices.CreateServerChannelSinkChain (serverSinkProvider, this);\r
+                       IServerChannelSink next_sink = ChannelServices.CreateServerChannelSinkChain (serverSinkProvider, this);
                        sink = new TcpServerTransportSink (next_sink);
                }
                
@@ -86,32 +107,35 @@ namespace System.Runtime.Remoting.Channels.Tcp
                                         IServerChannelSinkProvider serverSinkProvider)
                {
                        foreach(DictionaryEntry property in properties)
-                       {\r
-                               switch((string)property.Key)\r
-                               {\r
-                                       case "port":\r
+                       {
+                               switch((string)property.Key)
+                               {
+                                       case "name":
+                                               name = property.Value.ToString();
+                                               break;
+                                       case "port":
                                                port = Convert.ToInt32(property.Value);
-                                               break;\r
-                                       case "priority":\r
+                                               break;
+                                       case "priority":
                                                priority = Convert.ToInt32(property.Value);
-                                               break;\r
-                                       case "bindTo":\r
+                                               break;
+                                       case "bindTo":
                                                bindAddress = IPAddress.Parse((string)property.Value);
-                                               break;\r
-                                       case "rejectRemoteRequests":\r
+                                               break;
+                                       case "rejectRemoteRequests":
                                                if(Convert.ToBoolean(properties["rejectRemoteRequests"]))
                                                        bindAddress = IPAddress.Loopback;
                                                break;
                                        case "supressChannelData":
                                                supressChannelData = Convert.ToBoolean (property.Value);
-                                               break;\r
+                                               break;
                                        case "useIpAddress":
                                                useIpAddress = Convert.ToBoolean (property.Value);
                                                break;
                                        case "machineName":
                                                host = property.Value as string;
                                                break;
-                               }\r
+                               }
                        }                       
                        Init (serverSinkProvider);
                }
@@ -158,18 +182,18 @@ namespace System.Runtime.Remoting.Channels.Tcp
                        return "tcp://" + host + ":" + port;
                }
                
-               public string[] GetUrlsForUri (string uri)\r
-               {\r
+               public virtual string[] GetUrlsForUri (string uri)
+               {
                        if (!uri.StartsWith ("/")) uri = "/" + uri;
-\r
-                       string [] chnl_uris = channel_data.ChannelUris;\r
-                       string [] result = new String [chnl_uris.Length];\r
-\r
-                       for (int i = 0; i < chnl_uris.Length; i++) \r
-                               result [i] = chnl_uris [i] + uri;\r
-                       \r
-                       return result;\r
-               }\r
+
+                       string [] chnl_uris = channel_data.ChannelUris;
+                       string [] result = new String [chnl_uris.Length];
+
+                       for (int i = 0; i < chnl_uris.Length; i++) 
+                               result [i] = chnl_uris [i] + uri;
+                       
+                       return result;
+               }
 
                public string Parse (string url, out string objectURI)
                {
@@ -180,53 +204,51 @@ namespace System.Runtime.Remoting.Channels.Tcp
                {
                        try
                        {
-                               while (true) 
+#if !TARGET_JVM
+                               while(true)
+#else
+                               while(!stopped)
+#endif
                                {
-                                       TcpClient client = listener.AcceptTcpClient ();
-                                       CreateListenerConnection (client);
+                                       Socket socket = listener.AcceptSocket ();
+                                       ClientConnection reader = new ClientConnection (this, socket, sink);
+                                       try {
+                                               if (!threadPool.RunThread (new ThreadStart (reader.ProcessMessages)))
+                                                       socket.Close ();
+                                       } catch (Exception e) 
+                                       {
+#if DEBUG
+                                               Console.WriteLine("Exception caught in TcpServerChannel.WaitForConnections during start process message: {0} {1}", e.GetType(), e.Message);
+#endif
+                                       }
                                }
                        }
-                       catch
-                       {}
-               }
-
-               internal void CreateListenerConnection (TcpClient client)
-               {
-                       lock (_activeConnections)
+                       catch (Exception e) 
                        {
-                               if (_activeConnections.Count >= _maxConcurrentConnections)
-                                       Monitor.Wait (_activeConnections);
-
-                               if (server_thread == null) return;      // Server was stopped while waiting
-
-                               ClientConnection reader = new ClientConnection (this, client, sink);
-                               Thread thread = new Thread (new ThreadStart (reader.ProcessMessages));
-                               thread.Start();
-                               thread.IsBackground = true;
-                               _activeConnections.Add (thread);
+#if DEBUG
+                               Console.WriteLine("Exception caught in TcpServerChannel.WaitForConnections, stop channel's thread : {0} {1}", e.GetType(), e.Message);
+#endif
                        }
                }
 
-               internal void ReleaseConnection (Thread thread)
-               {
-                       lock (_activeConnections)
-                       {
-                               _activeConnections.Remove (thread);
-                               Monitor.Pulse (_activeConnections);
-                       }
-               }
-               
                public void StartListening (object data)
                {
+#if TARGET_JVM
+                       stopped = false;
+#endif 
                        listener = new TcpListener (bindAddress, port);
-                       if (server_thread == null) \r
+                       if (server_thread == null) 
                        {
+                               threadPool = RemotingThreadPool.GetSharedPool ();
                                listener.Start ();
-                               if (port == 0) {
+                               
+                               if (port == 0)
                                        port = ((IPEndPoint)listener.LocalEndpoint).Port;
-                                       channel_data.ChannelUris = new String [1];
-                                       channel_data.ChannelUris [0] = GetChannelUri ();
-                               }
+
+                               string[] uris = new String [1];
+                               uris = new String [1];
+                               uris [0] = GetChannelUri ();
+                               channel_data.ChannelUris = uris;
 
                                server_thread = new Thread (new ThreadStart (WaitForConnections));
                                server_thread.IsBackground = true;
@@ -236,42 +258,42 @@ namespace System.Runtime.Remoting.Channels.Tcp
 
                public void StopListening (object data)
                {
+#if TARGET_JVM
+                       stopped = true;
+#endif 
                        if (server_thread == null) return;
-
-                       lock (_activeConnections)
-                       {
-                               server_thread.Abort ();
-                               server_thread = null;
-                               listener.Stop ();
-
-                               foreach (Thread thread in _activeConnections)
-                                       thread.Abort();
-
-                               _activeConnections.Clear();
-                               Monitor.PulseAll (_activeConnections);
-                       }
+                       
+#if !TARGET_JVM
+                       server_thread.Abort ();
+#else
+                       server_thread.Interrupt ();
+#endif
+                       listener.Stop ();
+                       threadPool.Free ();
+                       server_thread.Join ();
+                       server_thread = null;                   
                }
        }
 
        class ClientConnection
        {
-               TcpClient _client;
+               static int _count;
+               int _id;
+               Socket _socket;
                TcpServerTransportSink _sink;
                Stream _stream;
-               TcpServerChannel _serverChannel;
 
                byte[] _buffer = new byte[TcpMessageIO.DefaultStreamBufferSize];
 
-               public ClientConnection (TcpServerChannel serverChannel, TcpClient client, TcpServerTransportSink sink)
+               public ClientConnection (TcpServerChannel serverChannel, Socket socket, TcpServerTransportSink sink)
                {
-                       _serverChannel = serverChannel;
-                       _client = client;
+                       _socket = socket;
                        _sink = sink;
+                       _id = _count++;
                }
 
-               public Stream Stream
-               {
-                       get { return _stream; }
+               public Socket Socket {
+                       get { return _socket; }
                }
 
                public byte[] Buffer
@@ -281,34 +303,59 @@ namespace System.Runtime.Remoting.Channels.Tcp
 
                public void ProcessMessages()
                {
-                       _stream = _client.GetStream();
+                       byte[] buffer = new byte[256];
+                       NetworkStream ns = new NetworkStream (_socket);
+                       _stream = new BufferedStream (ns);
 
                        try
                        {
                                bool end = false;
                                while (!end)
                                {
-                                       MessageStatus type = TcpMessageIO.ReceiveMessageStatus (_stream);
+                                       MessageStatus type = TcpMessageIO.ReceiveMessageStatus (_stream, buffer);
 
                                        switch (type)
                                        {
                                                case MessageStatus.MethodMessage:
-                                                       _sink.InternalProcessMessage (this);
+                                                       _sink.InternalProcessMessage (this, _stream);
                                                        break;
 
+                                               case MessageStatus.Unknown:
                                                case MessageStatus.CancelSignal:
                                                        end = true;
                                                        break;
                                        }
+                                       _stream.Flush ();
                                }
                        }
                        catch (Exception ex)
                        {
-                               Console.WriteLine (ex);
+#if DEBUG
+                               Console.WriteLine ("The exception was caught during TcpServerChannel.ProcessMessages: {0}, {1}", ex.GetType(), ex.Message);
+#endif
+                       }
+                       finally
+                       {
+                               try {
+                                       _stream.Close();
+                                       _socket.Close ();
+                               }
+                               catch { }
+                       }
+               }
+               
+               public int Id
+               {
+                       get { return _id; }
+               }
+               
+               public IPAddress ClientAddress
+               {
+                       get {
+                               IPEndPoint ep = _socket.RemoteEndPoint as IPEndPoint;
+                               if (ep != null) return ep.Address;
+                               else return null;
                        }
-
-                       _stream.Close();
-                       _serverChannel.ReleaseConnection (Thread.CurrentThread);
                }
        }
 }