b51a3198eaaccec1fb526f13acb72403443fa64d
[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 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30
31 using System.Collections;
32 using System.Runtime.Remoting.Messaging;
33 using System.Text.RegularExpressions;
34 using System.Net.Sockets;
35 using System.Net;
36 using System.Threading;
37 using System.IO;
38 using System.Runtime.Remoting.Channels;
39
40 namespace System.Runtime.Remoting.Channels.Tcp
41 {
42         public class TcpServerChannel : IChannelReceiver, IChannel
43         {
44                 int port = 0;
45                 string name = "tcp";
46                 string host = null;
47                 int priority = 1;
48                 bool supressChannelData = false;
49                 bool useIpAddress = true;
50                 
51                 IPAddress bindAddress = IPAddress.Any;
52                 Thread server_thread = null;
53                 TcpListener listener;
54                 TcpServerTransportSink sink;
55                 ChannelDataStore channel_data;
56                 
57                 RemotingThreadPool threadPool;
58                 
59 #if TARGET_JVM
60                 private volatile bool stopped = false;
61 #endif
62
63                 void Init (IServerChannelSinkProvider serverSinkProvider) 
64                 {
65                         if (serverSinkProvider == null) 
66                         {
67                                 serverSinkProvider = new BinaryServerFormatterSinkProvider ();
68                         }
69
70                         if (host == null)
71                         {
72                                 if (useIpAddress) {
73                                         if (!bindAddress.Equals(IPAddress.Any)) host = bindAddress.ToString ();
74                                         else {
75                                                 IPHostEntry he = Dns.Resolve (Dns.GetHostName());
76                                                 if (he.AddressList.Length == 0) throw new RemotingException ("IP address could not be determined for this host");
77                                                 host = he.AddressList [0].ToString ();
78                                         }
79                                 }
80                                 else
81                                         host = Dns.GetHostByName(Dns.GetHostName()).HostName;
82                         }
83                         
84                         // Gets channel data from the chain of channel providers
85
86                         channel_data = new ChannelDataStore (null);
87                         IServerChannelSinkProvider provider = serverSinkProvider;
88                         while (provider != null)
89                         {
90                                 provider.GetChannelData(channel_data);
91                                 provider = provider.Next;
92                         }
93
94                         // Creates the sink chain that will process all incoming messages
95
96                         IServerChannelSink next_sink = ChannelServices.CreateServerChannelSinkChain (serverSinkProvider, this);
97                         sink = new TcpServerTransportSink (next_sink);
98                 }
99                 
100                 public TcpServerChannel (int port)
101                 {
102                         this.port = port;
103                         Init (null);
104                 }
105
106                 public TcpServerChannel (IDictionary properties,
107                                          IServerChannelSinkProvider serverSinkProvider)
108                 {
109                         foreach(DictionaryEntry property in properties)
110                         {
111                                 switch((string)property.Key)
112                                 {
113                                         case "name":
114                                                 name = property.Value.ToString();
115                                                 break;
116                                         case "port":
117                                                 port = Convert.ToInt32(property.Value);
118                                                 break;
119                                         case "priority":
120                                                 priority = Convert.ToInt32(property.Value);
121                                                 break;
122                                         case "bindTo":
123                                                 bindAddress = IPAddress.Parse((string)property.Value);
124                                                 break;
125                                         case "rejectRemoteRequests":
126                                                 if(Convert.ToBoolean(properties["rejectRemoteRequests"]))
127                                                         bindAddress = IPAddress.Loopback;
128                                                 break;
129                                         case "supressChannelData":
130                                                 supressChannelData = Convert.ToBoolean (property.Value);
131                                                 break;
132                                         case "useIpAddress":
133                                                 useIpAddress = Convert.ToBoolean (property.Value);
134                                                 break;
135                                         case "machineName":
136                                                 host = property.Value as string;
137                                                 break;
138                                 }
139                         }                       
140                         Init (serverSinkProvider);
141                 }
142
143                 public TcpServerChannel (string name, int port,
144                                          IServerChannelSinkProvider serverSinkProvider)
145                 {
146                         this.name = name;
147                         this.port = port;
148                         Init (serverSinkProvider);
149                 }
150                 
151                 public TcpServerChannel (string name, int port)
152                 {
153                         this.name = name;
154                         this.port = port;
155                         Init (null);
156                 }
157                 
158                 public object ChannelData
159                 {
160                         get {
161                                 if (supressChannelData) return null;
162                                 else return channel_data;
163                         }
164                 }
165
166                 public string ChannelName
167                 {
168                         get {
169                                 return name;
170                         }
171                 }
172
173                 public int ChannelPriority
174                 {
175                         get {
176                                 return priority;
177                         }
178                 }
179
180                 public string GetChannelUri ()
181                 {
182                         return "tcp://" + host + ":" + port;
183                 }
184                 
185                 public string[] GetUrlsForUri (string uri)
186                 {
187                         if (!uri.StartsWith ("/")) uri = "/" + uri;
188
189                         string [] chnl_uris = channel_data.ChannelUris;
190                         string [] result = new String [chnl_uris.Length];
191
192                         for (int i = 0; i < chnl_uris.Length; i++) 
193                                 result [i] = chnl_uris [i] + uri;
194                         
195                         return result;
196                 }
197
198                 public string Parse (string url, out string objectURI)
199                 {
200                         return TcpChannel.ParseChannelUrl (url, out objectURI);
201                 }
202
203                 void WaitForConnections ()
204                 {
205                         try
206                         {
207 #if !TARGET_JVM
208                                 while(true)
209 #else
210                                 while(!stopped)
211 #endif
212                                 {
213                                         Socket socket = listener.AcceptSocket ();
214                                         ClientConnection reader = new ClientConnection (this, socket, sink);
215                                         try {
216                                                 if (!threadPool.RunThread (new ThreadStart (reader.ProcessMessages)))
217                                                         socket.Close ();
218                                         } catch (Exception e) 
219                                         {
220 #if DEBUG
221                                                 Console.WriteLine("Exception caught in TcpServerChannel.WaitForConnections during start process message: {0} {1}", e.GetType(), e.Message);
222 #endif
223                                         }
224                                 }
225                         }
226                         catch (Exception e) 
227                         {
228 #if DEBUG
229                                 Console.WriteLine("Exception caught in TcpServerChannel.WaitForConnections, stop channel's thread : {0} {1}", e.GetType(), e.Message);
230 #endif
231                         }
232                 }
233
234                 public void StartListening (object data)
235                 {
236 #if TARGET_JVM
237                         stopped = false;
238 #endif 
239                         listener = new TcpListener (bindAddress, port);
240                         if (server_thread == null) 
241                         {
242                                 threadPool = RemotingThreadPool.GetSharedPool ();
243                                 listener.Start ();
244                                 
245                                 if (port == 0)
246                                         port = ((IPEndPoint)listener.LocalEndpoint).Port;
247
248                                 string[] uris = new String [1];
249                                 uris = new String [1];
250                                 uris [0] = GetChannelUri ();
251                                 channel_data.ChannelUris = uris;
252
253                                 server_thread = new Thread (new ThreadStart (WaitForConnections));
254                                 server_thread.IsBackground = true;
255                                 server_thread.Start ();
256                         }
257                 }
258
259                 public void StopListening (object data)
260                 {
261 #if TARGET_JVM
262                         stopped = true;
263 #endif 
264                         if (server_thread == null) return;
265                         
266 #if !TARGET_JVM
267                         server_thread.Abort ();
268 #else
269                         server_thread.Interrupt ();
270 #endif
271                         listener.Stop ();
272                         threadPool.Free ();
273                         server_thread.Join ();
274                         server_thread = null;                   
275                 }
276         }
277
278         class ClientConnection
279         {
280                 static int _count;
281                 int _id;
282                 Socket _socket;
283                 TcpServerTransportSink _sink;
284                 Stream _stream;
285
286                 byte[] _buffer = new byte[TcpMessageIO.DefaultStreamBufferSize];
287
288                 public ClientConnection (TcpServerChannel serverChannel, Socket socket, TcpServerTransportSink sink)
289                 {
290                         _socket = socket;
291                         _sink = sink;
292                         _id = _count++;
293                 }
294
295                 public Stream Stream
296                 {
297                         get { return _stream; }
298                 }
299
300                 public byte[] Buffer
301                 {
302                         get { return _buffer; }
303                 }
304
305                 public void ProcessMessages()
306                 {
307                         byte[] buffer = new byte[256];
308                         NetworkStream ns = new NetworkStream (_socket);
309                         _stream = new BufferedStream (ns);
310
311                         try
312                         {
313                                 bool end = false;
314                                 while (!end)
315                                 {
316                                         MessageStatus type = TcpMessageIO.ReceiveMessageStatus (_stream, buffer);
317
318                                         switch (type)
319                                         {
320                                                 case MessageStatus.MethodMessage:
321                                                         _sink.InternalProcessMessage (this);
322                                                         break;
323
324                                                 case MessageStatus.Unknown:
325                                                 case MessageStatus.CancelSignal:
326                                                         end = true;
327                                                         break;
328                                         }
329                                         _stream.Flush ();
330                                 }
331                         }
332                         catch (Exception ex)
333                         {
334 #if DEBUG
335                                 Console.WriteLine ("The exception was caught during TcpServerChannel.ProcessMessages: {0}, {1}", ex.GetType(), ex.Message);
336 #endif
337                         }
338                         finally
339                         {
340                                 try {
341                                         _stream.Close();
342                                         _socket.Close ();
343                                 }
344                                 catch { }
345                         }
346                 }
347                 
348                 public int Id
349                 {
350                         get { return _id; }
351                 }
352                 
353                 public IPAddress ClientAddress
354                 {
355                         get {
356                                 IPEndPoint ep = _socket.RemoteEndPoint as IPEndPoint;
357                                 if (ep != null) return ep.Address;
358                                 else return null;
359                         }
360                 }
361         }
362 }