* TcpChannel.cs: The port property may not be an string. Fixed.
[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;
25                 int priority = 1;
26                 IPAddress bindAddress = IPAddress.Any;
27                 Thread server_thread = null;
28                 TcpListener listener;
29                 TcpServerTransportSink sink;
30                 ChannelDataStore channel_data;
31                 int _maxConcurrentConnections = 100;
32                 ArrayList _activeConnections = new ArrayList();
33                 
34                 void Init (IServerChannelSinkProvider serverSinkProvider) 
35                 {
36                         if (serverSinkProvider == null) \r
37                         {
38                                 serverSinkProvider = new BinaryServerFormatterSinkProvider ();
39                         }
40
41                         host = Dns.GetHostByName(Dns.GetHostName()).HostName;
42                         
43                         string [] uris = null;
44                         
45                         if (port != 0) {
46                                 uris = new String [1];
47                                 uris [0] = GetChannelUri ();
48                         }
49
50                         // Gets channel data from the chain of channel providers
51
52                         channel_data = new ChannelDataStore (uris);
53                         IServerChannelSinkProvider provider = serverSinkProvider;\r
54                         while (provider != null)\r
55                         {\r
56                                 provider.GetChannelData(channel_data);\r
57                                 provider = provider.Next;\r
58                         }\r
59
60                         // Creates the sink chain that will process all incoming messages
61
62                         IServerChannelSink next_sink = ChannelServices.CreateServerChannelSinkChain (serverSinkProvider, this);\r
63                         sink = new TcpServerTransportSink (next_sink);
64                         
65                         listener = new TcpListener(bindAddress, port);
66                         StartListening (null);
67                 }
68                 
69                 public TcpServerChannel (int port)
70                 {
71                         this.port = port;
72                         Init (null);
73                 }
74
75                 public TcpServerChannel (IDictionary properties,
76                                          IServerChannelSinkProvider serverSinkProvider)
77                 {
78                         foreach(DictionaryEntry property in properties)
79                         {\r
80                                 switch((string)property.Key)\r
81                                 {\r
82                                         case "port":\r
83                                                 port = Convert.ToInt32(property.Value);
84                                                 break;\r
85                                         case "priority":\r
86                                                 priority = Convert.ToInt32(property.Value);
87                                                 break;\r
88                                         case "bindTo":\r
89                                                 bindAddress = IPAddress.Parse((string)property.Value);
90                                                 break;\r
91                                         case "rejectRemoteRequests":\r
92                                                 if(Convert.ToBoolean(properties["rejectRemoteRequests"]))
93                                                         bindAddress = IPAddress.Loopback;
94                                                 break;\r
95                                 }\r
96                         }                       
97                         Init (serverSinkProvider);
98                 }
99
100                 public TcpServerChannel (string name, int port,
101                                          IServerChannelSinkProvider serverSinkProvider)
102                 {
103                         this.name = name;
104                         this.port = port;
105                         Init (serverSinkProvider);
106                 }
107                 
108                 public TcpServerChannel (string name, int port)
109                 {
110                         this.name = name;
111                         this.port = port;
112                         Init (null);
113                 }
114                 
115                 public object ChannelData
116                 {
117                         get {
118                                 return channel_data;
119                         }
120                 }
121
122                 public string ChannelName
123                 {
124                         get {
125                                 return name;
126                         }
127                 }
128
129                 public int ChannelPriority
130                 {
131                         get {
132                                 return priority;
133                         }
134                 }
135
136                 public string GetChannelUri ()
137                 {
138                         return "tcp://" + host + ":" + port;
139                 }
140                 
141                 public string[] GetUrlsForUri (string uri)\r
142                 {\r
143                         if (!uri.StartsWith ("/")) uri = "/" + uri;
144 \r
145                         string [] chnl_uris = channel_data.ChannelUris;\r
146                         string [] result = new String [chnl_uris.Length];\r
147 \r
148                         for (int i = 0; i < chnl_uris.Length; i++) \r
149                                 result [i] = chnl_uris [i] + uri;\r
150                         \r
151                         return result;\r
152                 }\r
153
154                 public string Parse (string url, out string objectURI)
155                 {
156                         return TcpChannel.ParseChannelUrl (url, out objectURI);
157                 }
158
159                 void WaitForConnections ()
160                 {
161                         try
162                         {
163                                 while (true) 
164                                 {
165                                         TcpClient client = listener.AcceptTcpClient ();
166                                         CreateListenerConnection (client);
167                                 }
168                         }
169                         catch
170                         {}
171                 }
172
173                 internal void CreateListenerConnection (TcpClient client)
174                 {
175                         lock (_activeConnections)
176                         {
177                                 if (_activeConnections.Count >= _maxConcurrentConnections)
178                                         Monitor.Wait (_activeConnections);
179
180                                 if (server_thread == null) return;      // Server was stopped while waiting
181
182                                 ClientConnection reader = new ClientConnection (this, client, sink);
183                                 Thread thread = new Thread (new ThreadStart (reader.ProcessMessages));
184                                 thread.Start();
185                                 thread.IsBackground = true;
186                                 _activeConnections.Add (thread);
187                         }
188                 }
189
190                 internal void ReleaseConnection (Thread thread)
191                 {
192                         lock (_activeConnections)
193                         {
194                                 _activeConnections.Remove (thread);
195                                 Monitor.Pulse (_activeConnections);
196                         }
197                 }
198                 
199                 public void StartListening (object data)
200                 {
201                         if (server_thread == null) \r
202                         {
203                                 listener.Start ();
204                                 if (port == 0) {
205                                         port = ((IPEndPoint)listener.LocalEndpoint).Port;
206                                         channel_data.ChannelUris = new String [1];
207                                         channel_data.ChannelUris [0] = GetChannelUri ();
208                                 }
209
210                                 server_thread = new Thread (new ThreadStart (WaitForConnections));
211                                 server_thread.IsBackground = true;
212                                 server_thread.Start ();
213                         }
214                 }
215
216                 public void StopListening (object data)
217                 {
218                         if (server_thread == null) return;
219
220                         lock (_activeConnections)
221                         {
222                                 server_thread.Abort ();
223                                 server_thread = null;
224                                 listener.Stop ();
225
226                                 foreach (Thread thread in _activeConnections)
227                                         thread.Abort();
228
229                                 _activeConnections.Clear();
230                                 Monitor.PulseAll (_activeConnections);
231                         }
232                 }
233         }
234
235         class ClientConnection
236         {
237                 TcpClient _client;
238                 TcpServerTransportSink _sink;
239                 Stream _stream;
240                 TcpServerChannel _serverChannel;
241
242                 byte[] _buffer = new byte[TcpMessageIO.DefaultStreamBufferSize];
243
244                 public ClientConnection (TcpServerChannel serverChannel, TcpClient client, TcpServerTransportSink sink)
245                 {
246                         _serverChannel = serverChannel;
247                         _client = client;
248                         _sink = sink;
249                 }
250
251                 public Stream Stream
252                 {
253                         get { return _stream; }
254                 }
255
256                 public byte[] Buffer
257                 {
258                         get { return _buffer; }
259                 }
260
261                 public void ProcessMessages()
262                 {
263                         _stream = _client.GetStream();
264
265                         try
266                         {
267                                 bool end = false;
268                                 while (!end)
269                                 {
270                                         MessageStatus type = TcpMessageIO.ReceiveMessageStatus (_stream);
271
272                                         switch (type)
273                                         {
274                                                 case MessageStatus.MethodMessage:
275                                                         _sink.InternalProcessMessage (this);
276                                                         break;
277
278                                                 case MessageStatus.CancelSignal:
279                                                         end = true;
280                                                         break;
281                                         }
282                                 }
283                         }
284                         catch
285                         {}
286
287                         _stream.Close();
288                         _serverChannel.ReleaseConnection (Thread.CurrentThread);
289                 }
290         }
291 }