New run-jenkins tags to allow a checked build job and fix partial checked build suppo...
[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
60                 void Init (IServerChannelSinkProvider serverSinkProvider)
61                 {
62                         if (serverSinkProvider == null) 
63                         {
64                                 serverSinkProvider = new BinaryServerFormatterSinkProvider ();
65                         }
66
67                         if (host == null)
68                         {
69                                 if (useIpAddress) {
70                                         if (!bindAddress.Equals(IPAddress.Any)) host = bindAddress.ToString ();
71                                         else {
72                                                 IPHostEntry he = Dns.Resolve (Dns.GetHostName());
73                                                 if (he.AddressList.Length == 0) throw new RemotingException ("IP address could not be determined for this host");
74                                                 host = he.AddressList [0].ToString ();
75                                         }
76                                 }
77                                 else
78                                         host = Dns.GetHostByName(Dns.GetHostName()).HostName;
79                         }
80                         
81                         // Gets channel data from the chain of channel providers
82
83                         channel_data = new ChannelDataStore (null);
84                         IServerChannelSinkProvider provider = serverSinkProvider;
85                         while (provider != null)
86                         {
87                                 provider.GetChannelData(channel_data);
88                                 provider = provider.Next;
89                         }
90
91                         // Creates the sink chain that will process all incoming messages
92
93                         IServerChannelSink next_sink = ChannelServices.CreateServerChannelSinkChain (serverSinkProvider, this);
94                         sink = new TcpServerTransportSink (next_sink);
95
96                         StartListening (null);
97                 }
98                 
99                 public TcpServerChannel (int port)
100                 {
101                         this.port = port;
102                         Init (null);
103                 }
104
105                 public TcpServerChannel (IDictionary properties,
106                                          IServerChannelSinkProvider sinkProvider)
107                 {
108                         foreach(DictionaryEntry property in properties)
109                         {
110                                 switch((string)property.Key)
111                                 {
112                                         case "name":
113                                                 name = property.Value.ToString();
114                                                 break;
115                                         case "port":
116                                                 port = Convert.ToInt32(property.Value);
117                                                 break;
118                                         case "priority":
119                                                 priority = Convert.ToInt32(property.Value);
120                                                 break;
121                                         case "bindTo":
122                                                 bindAddress = IPAddress.Parse((string)property.Value);
123                                                 break;
124                                         case "rejectRemoteRequests":
125                                                 if(Convert.ToBoolean(properties["rejectRemoteRequests"]))
126                                                         bindAddress = IPAddress.Loopback;
127                                                 break;
128                                         case "supressChannelData":
129                                                 supressChannelData = Convert.ToBoolean (property.Value);
130                                                 break;
131                                         case "useIpAddress":
132                                                 useIpAddress = Convert.ToBoolean (property.Value);
133                                                 break;
134                                         case "machineName":
135                                                 host = property.Value as string;
136                                                 break;
137                                 }
138                         }
139                         Init (sinkProvider);
140                 }
141
142                 public TcpServerChannel (string name, int port,
143                                          IServerChannelSinkProvider sinkProvider)
144                 {
145                         this.name = name;
146                         this.port = port;
147                         Init (sinkProvider);
148                 }
149                 
150                 public TcpServerChannel (string name, int port)
151                 {
152                         this.name = name;
153                         this.port = port;
154                         Init (null);
155                 }
156                 
157                 public object ChannelData
158                 {
159                         get {
160                                 if (supressChannelData) return null;
161                                 else return channel_data;
162                         }
163                 }
164
165                 public string ChannelName
166                 {
167                         get {
168                                 return name;
169                         }
170                 }
171
172                 public int ChannelPriority
173                 {
174                         get {
175                                 return priority;
176                         }
177                 }
178
179                 public string GetChannelUri ()
180                 {
181                         return "tcp://" + host + ":" + port;
182                 }
183
184                 public virtual string [] GetUrlsForUri (string objectUri)
185                 {
186                         if (!objectUri.StartsWith ("/"))
187                                 objectUri = "/" + objectUri;
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] + objectUri;
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                                 while(true)
208                                 {
209                                         Socket socket = listener.AcceptSocket ();
210                                         ClientConnection reader = new ClientConnection (this, socket, sink);
211                                         try {
212                                                 if (!threadPool.RunThread (new ThreadStart (reader.ProcessMessages)))
213                                                         socket.Close ();
214                                         } catch (Exception e) 
215                                         {
216 #if DEBUG
217                                                 Console.WriteLine("Exception caught in TcpServerChannel.WaitForConnections during start process message: {0} {1}", e.GetType(), e.Message);
218 #endif
219                                         }
220                                 }
221                         }
222                         catch (Exception e)
223                         {
224 #if DEBUG
225                                 Console.WriteLine("Exception caught in TcpServerChannel.WaitForConnections, stop channel's thread : {0} {1}", e.GetType(), e.Message);
226 #endif
227                         }
228                 }
229
230                 public void StartListening (object data)
231                 {
232                         listener = new TcpListener (bindAddress, port);
233                         if (server_thread == null) 
234                         {
235                                 threadPool = RemotingThreadPool.GetSharedPool ();
236                                 listener.Start ();
237                                 
238                                 if (port == 0)
239                                         port = ((IPEndPoint)listener.LocalEndpoint).Port;
240
241                                 string[] uris = new String [1];
242                                 uris = new String [1];
243                                 uris [0] = GetChannelUri ();
244                                 channel_data.ChannelUris = uris;
245
246                                 server_thread = new Thread (new ThreadStart (WaitForConnections));
247                                 server_thread.IsBackground = true;
248                                 server_thread.Start ();
249                         }
250                 }
251
252                 public void StopListening (object data)
253                 {
254                         if (server_thread == null) return;
255                         
256                         server_thread.Abort ();
257                         listener.Stop ();
258                         threadPool.Free ();
259                         server_thread.Join ();
260                         server_thread = null;                   
261                 }
262         }
263
264         class ClientConnection
265         {
266                 static int _count;
267                 int _id;
268                 Socket _socket;
269                 TcpServerTransportSink _sink;
270                 Stream _stream;
271
272                 byte[] _buffer = new byte[TcpMessageIO.DefaultStreamBufferSize];
273
274                 public ClientConnection (TcpServerChannel serverChannel, Socket socket, TcpServerTransportSink sink)
275                 {
276                         _socket = socket;
277                         _sink = sink;
278                         _id = _count++;
279                 }
280
281                 public Socket Socket {
282                         get { return _socket; }
283                 }
284
285                 public byte[] Buffer
286                 {
287                         get { return _buffer; }
288                 }
289
290                 public void ProcessMessages()
291                 {
292                         byte[] buffer = new byte[256];
293                         NetworkStream ns = new NetworkStream (_socket);
294                         _stream = new BufferedStream (ns);
295
296                         try
297                         {
298                                 bool end = false;
299                                 while (!end)
300                                 {
301                                         MessageStatus type = TcpMessageIO.ReceiveMessageStatus (_stream, buffer);
302
303                                         switch (type)
304                                         {
305                                                 case MessageStatus.MethodMessage:
306                                                         _sink.InternalProcessMessage (this, _stream);
307                                                         break;
308
309                                                 case MessageStatus.Unknown:
310                                                 case MessageStatus.CancelSignal:
311                                                         _stream.Flush ();
312                                                         end = true;
313                                                         break;
314                                         }
315                                 }
316                         }
317 #if DEBUG
318                         catch (Exception ex)
319                         {
320                                 Console.WriteLine ("The exception was caught during TcpServerChannel.ProcessMessages: {0}, {1}", ex.GetType(), ex.Message);
321                         }
322 #endif
323                         finally
324                         {
325                                 try {
326                                         _stream.Close();
327                                         _socket.Close ();
328                                 }
329                                 catch { }
330                         }
331                 }
332                 
333                 public int Id
334                 {
335                         get { return _id; }
336                 }
337                 
338                 public IPAddress ClientAddress
339                 {
340                         get {
341                                 IPEndPoint ep = _socket.RemoteEndPoint as IPEndPoint;
342                                 if (ep != null) return ep.Address;
343                                 else return null;
344                         }
345                 }
346         }
347 }