* TcpServerChannel.cs: If useIpAddress and bindAddress are both specified,
[mono.git] / mcs / class / System.Runtime.Remoting / System.Runtime.Remoting.Channels.Tcp / TcpServerChannel.cs
1 //
2 // System.Runtime.Remoting.Channels.Tcp.TcpServerChannel.cs
3 //
4 // Author: Rodrigo Moya (rodrigo@ximian.com)
5 //         Lluis Sanchez Gual (lluis@ideary.com)
6 //
7 // 2002 (C) Copyright, Ximian, Inc.
8 //
9
10 using System.Collections;
11 using System.Runtime.Remoting.Messaging;
12 using System.Text.RegularExpressions;
13 using System.Net.Sockets;
14 using System.Net;
15 using System.Threading;
16 using System.IO;
17
18 namespace System.Runtime.Remoting.Channels.Tcp
19 {
20         public class TcpServerChannel : IChannelReceiver, IChannel
21         {
22                 int port = 0;
23                 string name = "tcp";
24                 string host = null;
25                 int priority = 1;
26                 bool supressChannelData = false;
27                 bool useIpAddress = false;
28                 
29                 IPAddress bindAddress = IPAddress.Any;
30                 Thread server_thread = null;
31                 TcpListener listener;
32                 TcpServerTransportSink sink;
33                 ChannelDataStore channel_data;
34                 int _maxConcurrentConnections = 100;
35                 ArrayList _activeConnections = new ArrayList();
36                 
37                 
38                 void Init (IServerChannelSinkProvider serverSinkProvider) 
39                 {
40                         if (serverSinkProvider == null) \r
41                         {
42                                 serverSinkProvider = new BinaryServerFormatterSinkProvider ();
43                         }
44
45                         if (host == null)
46                         {
47                                 if (useIpAddress) {
48                                         if (!bindAddress.Equals(IPAddress.Any)) host = bindAddress.ToString ();
49                                         else {
50                                                 IPHostEntry he = Dns.Resolve (Dns.GetHostName());
51                                                 if (he.AddressList.Length == 0) throw new RemotingException ("IP address could not be determined for this host");
52                                                 host = he.AddressList [0].ToString ();
53                                         }
54                                 }
55                                 else
56                                         host = Dns.GetHostByName(Dns.GetHostName()).HostName;
57                         }
58                         
59                         // Gets channel data from the chain of channel providers
60
61                         channel_data = new ChannelDataStore (null);
62                         IServerChannelSinkProvider provider = serverSinkProvider;\r
63                         while (provider != null)\r
64                         {\r
65                                 provider.GetChannelData(channel_data);\r
66                                 provider = provider.Next;\r
67                         }\r
68
69                         // Creates the sink chain that will process all incoming messages
70
71                         IServerChannelSink next_sink = ChannelServices.CreateServerChannelSinkChain (serverSinkProvider, this);\r
72                         sink = new TcpServerTransportSink (next_sink);
73                 }
74                 
75                 public TcpServerChannel (int port)
76                 {
77                         this.port = port;
78                         Init (null);
79                 }
80
81                 public TcpServerChannel (IDictionary properties,
82                                          IServerChannelSinkProvider serverSinkProvider)
83                 {
84                         foreach(DictionaryEntry property in properties)
85                         {\r
86                                 switch((string)property.Key)\r
87                                 {\r
88                                         case "port":\r
89                                                 port = Convert.ToInt32(property.Value);
90                                                 break;\r
91                                         case "priority":\r
92                                                 priority = Convert.ToInt32(property.Value);
93                                                 break;\r
94                                         case "bindTo":\r
95                                                 bindAddress = IPAddress.Parse((string)property.Value);
96                                                 break;\r
97                                         case "rejectRemoteRequests":\r
98                                                 if(Convert.ToBoolean(properties["rejectRemoteRequests"]))
99                                                         bindAddress = IPAddress.Loopback;
100                                                 break;
101                                         case "supressChannelData":
102                                                 supressChannelData = Convert.ToBoolean (property.Value);
103                                                 break;\r
104                                         case "useIpAddress":
105                                                 useIpAddress = Convert.ToBoolean (property.Value);
106                                                 break;
107                                         case "machineName":
108                                                 host = property.Value as string;
109                                                 break;
110                                 }\r
111                         }                       
112                         Init (serverSinkProvider);
113                 }
114
115                 public TcpServerChannel (string name, int port,
116                                          IServerChannelSinkProvider serverSinkProvider)
117                 {
118                         this.name = name;
119                         this.port = port;
120                         Init (serverSinkProvider);
121                 }
122                 
123                 public TcpServerChannel (string name, int port)
124                 {
125                         this.name = name;
126                         this.port = port;
127                         Init (null);
128                 }
129                 
130                 public object ChannelData
131                 {
132                         get {
133                                 if (supressChannelData) return null;
134                                 else return channel_data;
135                         }
136                 }
137
138                 public string ChannelName
139                 {
140                         get {
141                                 return name;
142                         }
143                 }
144
145                 public int ChannelPriority
146                 {
147                         get {
148                                 return priority;
149                         }
150                 }
151
152                 public string GetChannelUri ()
153                 {
154                         return "tcp://" + host + ":" + port;
155                 }
156                 
157                 public string[] GetUrlsForUri (string uri)\r
158                 {\r
159                         if (!uri.StartsWith ("/")) uri = "/" + uri;
160 \r
161                         string [] chnl_uris = channel_data.ChannelUris;\r
162                         string [] result = new String [chnl_uris.Length];\r
163 \r
164                         for (int i = 0; i < chnl_uris.Length; i++) \r
165                                 result [i] = chnl_uris [i] + uri;\r
166                         \r
167                         return result;\r
168                 }\r
169
170                 public string Parse (string url, out string objectURI)
171                 {
172                         return TcpChannel.ParseChannelUrl (url, out objectURI);
173                 }
174
175                 void WaitForConnections ()
176                 {
177                         try
178                         {
179                                 while (true) 
180                                 {
181                                         TcpClient client = listener.AcceptTcpClient ();
182                                         CreateListenerConnection (client);
183                                 }
184                         }
185                         catch
186                         {}
187                 }
188
189                 internal void CreateListenerConnection (TcpClient client)
190                 {
191                         lock (_activeConnections)
192                         {
193                                 if (_activeConnections.Count >= _maxConcurrentConnections)
194                                         Monitor.Wait (_activeConnections);
195
196                                 if (server_thread == null) return;      // Server was stopped while waiting
197
198                                 ClientConnection reader = new ClientConnection (this, client, sink);
199                                 Thread thread = new Thread (new ThreadStart (reader.ProcessMessages));
200                                 thread.Start();
201                                 thread.IsBackground = true;
202                                 _activeConnections.Add (thread);
203                         }
204                 }
205
206                 internal void ReleaseConnection (Thread thread)
207                 {
208                         lock (_activeConnections)
209                         {
210                                 _activeConnections.Remove (thread);
211                                 Monitor.Pulse (_activeConnections);
212                         }
213                 }
214                 
215                 public void StartListening (object data)
216                 {
217                         listener = new TcpListener (bindAddress, port);
218                         if (server_thread == null) \r
219                         {
220                                 listener.Start ();
221                                 
222                                 if (port == 0)
223                                         port = ((IPEndPoint)listener.LocalEndpoint).Port;
224
225                                 string[] uris = new String [1];
226                                 uris = new String [1];
227                                 uris [0] = GetChannelUri ();
228                                 channel_data.ChannelUris = uris;
229
230                                 server_thread = new Thread (new ThreadStart (WaitForConnections));
231                                 server_thread.IsBackground = true;
232                                 server_thread.Start ();
233                         }
234                 }
235
236                 public void StopListening (object data)
237                 {
238                         if (server_thread == null) return;
239
240                         lock (_activeConnections)
241                         {
242                                 server_thread.Abort ();
243                                 server_thread = null;
244                                 listener.Stop ();
245
246                                 foreach (Thread thread in _activeConnections)
247                                         thread.Abort();
248
249                                 _activeConnections.Clear();
250                                 Monitor.PulseAll (_activeConnections);
251                         }
252                 }
253         }
254
255         class ClientConnection
256         {
257                 TcpClient _client;
258                 TcpServerTransportSink _sink;
259                 Stream _stream;
260                 TcpServerChannel _serverChannel;
261
262                 byte[] _buffer = new byte[TcpMessageIO.DefaultStreamBufferSize];
263
264                 public ClientConnection (TcpServerChannel serverChannel, TcpClient client, TcpServerTransportSink sink)
265                 {
266                         _serverChannel = serverChannel;
267                         _client = client;
268                         _sink = sink;
269                 }
270
271                 public Stream Stream
272                 {
273                         get { return _stream; }
274                 }
275
276                 public byte[] Buffer
277                 {
278                         get { return _buffer; }
279                 }
280
281                 public void ProcessMessages()
282                 {
283                         _stream = _client.GetStream();
284
285                         try
286                         {
287                                 bool end = false;
288                                 while (!end)
289                                 {
290                                         MessageStatus type = TcpMessageIO.ReceiveMessageStatus (_stream);
291
292                                         switch (type)
293                                         {
294                                                 case MessageStatus.MethodMessage:
295                                                         _sink.InternalProcessMessage (this);
296                                                         break;
297
298                                                 case MessageStatus.CancelSignal:
299                                                         end = true;
300                                                         break;
301                                         }
302                                 }
303                         }
304                         catch (Exception ex)
305                         {
306 //                              Console.WriteLine (ex);
307                         }
308
309                         _stream.Close();
310                         _serverChannel.ReleaseConnection (Thread.CurrentThread);
311                 }
312         }
313 }