7a4081d611bf30b2c5323c588efcccf34617a0d4
[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) \r
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;\r
88                         while (provider != null)\r
89                         {\r
90                                 provider.GetChannelData(channel_data);\r
91                                 provider = provider.Next;\r
92                         }\r
93
94                         // Creates the sink chain that will process all incoming messages
95
96                         IServerChannelSink next_sink = ChannelServices.CreateServerChannelSinkChain (serverSinkProvider, this);\r
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                         {\r
111                                 switch((string)property.Key)\r
112                                 {\r
113                                         case "name":
114                                                 name = property.Value.ToString();
115                                                 break;
116                                         case "port":\r
117                                                 port = Convert.ToInt32(property.Value);
118                                                 break;\r
119                                         case "priority":\r
120                                                 priority = Convert.ToInt32(property.Value);
121                                                 break;\r
122                                         case "bindTo":\r
123                                                 bindAddress = IPAddress.Parse((string)property.Value);
124                                                 break;\r
125                                         case "rejectRemoteRequests":\r
126                                                 if(Convert.ToBoolean(properties["rejectRemoteRequests"]))
127                                                         bindAddress = IPAddress.Loopback;
128                                                 break;
129                                         case "supressChannelData":
130                                                 supressChannelData = Convert.ToBoolean (property.Value);
131                                                 break;\r
132                                         case "useIpAddress":
133                                                 useIpAddress = Convert.ToBoolean (property.Value);
134                                                 break;
135                                         case "machineName":
136                                                 host = property.Value as string;
137                                                 break;
138                                 }\r
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)\r
186                 {\r
187                         if (!uri.StartsWith ("/")) uri = "/" + uri;
188 \r
189                         string [] chnl_uris = channel_data.ChannelUris;\r
190                         string [] result = new String [chnl_uris.Length];\r
191 \r
192                         for (int i = 0; i < chnl_uris.Length; i++) \r
193                                 result [i] = chnl_uris [i] + uri;\r
194                         \r
195                         return result;\r
196                 }\r
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) \r
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                         server_thread = null;
272                         listener.Stop ();
273                         threadPool.Free ();
274                         server_thread.Join ();\r
275                         server_thread = null;                   \r
276                 }
277         }
278
279         class ClientConnection
280         {
281                 static int _count;
282                 int _id;
283                 Socket _socket;
284                 TcpServerTransportSink _sink;
285                 Stream _stream;
286
287                 byte[] _buffer = new byte[TcpMessageIO.DefaultStreamBufferSize];
288
289                 public ClientConnection (TcpServerChannel serverChannel, Socket socket, TcpServerTransportSink sink)
290                 {
291                         _socket = socket;
292                         _sink = sink;
293                         _id = _count++;
294                 }
295
296                 public Stream Stream
297                 {
298                         get { return _stream; }
299                 }
300
301                 public byte[] Buffer
302                 {
303                         get { return _buffer; }
304                 }
305
306                 public void ProcessMessages()
307                 {
308                         byte[] buffer = new byte[256];
309                         NetworkStream ns = new NetworkStream (_socket);
310                         _stream = new BufferedStream (ns);
311
312                         try
313                         {
314                                 bool end = false;
315                                 while (!end)
316                                 {
317                                         MessageStatus type = TcpMessageIO.ReceiveMessageStatus (_stream, buffer);
318
319                                         switch (type)
320                                         {
321                                                 case MessageStatus.MethodMessage:
322                                                         _sink.InternalProcessMessage (this);
323                                                         break;
324
325                                                 case MessageStatus.Unknown:
326                                                 case MessageStatus.CancelSignal:
327                                                         end = true;
328                                                         break;
329                                         }
330                                         _stream.Flush ();
331                                 }
332                         }
333                         catch (Exception ex)
334                         {
335 #if DEBUG
336                                 Console.WriteLine ("The exception was caught during TcpServerChannel.ProcessMessages: {0}, {1}", ex.GetType(), ex.Message);
337 #endif
338                         }
339                         finally
340                         {
341                                 try {
342                                         _stream.Close();
343                                         _socket.Close ();
344                                 }
345                                 catch { }
346                         }
347                 }
348                 
349                 public int Id
350                 {
351                         get { return _id; }
352                 }
353                 
354                 public IPAddress ClientAddress
355                 {
356                         get {
357                                 IPEndPoint ep = _socket.RemoteEndPoint as IPEndPoint;
358                                 if (ep != null) return ep.Address;
359                                 else return null;
360                         }
361                 }
362         }
363 }