Merge pull request #2394 from Mailaender/patch-1
[mono.git] / mcs / class / System.Runtime.Remoting / System.Runtime.Remoting.Channels.Http / HttpServerChannel.cs
1 //
2 // HttpServerChannel.cs
3 // 
4 // Author:
5 //   Michael Hutchinson <mhutchinson@novell.com>
6 // 
7 // Copyright (C) 2008 Novell, Inc (http://www.novell.com)
8 // 
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28
29 using System.Net;
30 using System.Collections;
31 using System.Globalization;
32 using System.Threading;
33 using System.Runtime.Remoting.MetadataServices;
34 using System.Runtime.Remoting.Messaging;
35
36 namespace System.Runtime.Remoting.Channels.Http
37 {
38
39         public class HttpServerChannel : BaseChannelWithProperties,
40                 IChannel, IChannelReceiver, IChannelReceiverHook
41         {
42                 string name = "http server";
43                 int priority = 1;
44                 
45                 string machineName = null;
46                 IPAddress bindAddress = IPAddress.Any;
47                 int port = -1; // querying GetChannelUri () on .NET indicates this is the default value
48                 bool suppressChannelData = false;
49                 bool useIPAddress = true;
50                 bool wantsToListen = true;
51
52                 HttpServerTransportSink sink;
53                 ChannelDataStore channelData;
54                 RemotingHttpListener listener;
55
56                 #region Constructors
57
58                 public HttpServerChannel ()
59                 {
60                         //DONT START SERVER, EVEN THOUGH ALL OTHER CONSTRUCTORS DO
61                         BuildSink (null);
62                 }
63
64                 public HttpServerChannel (int port)
65                 {
66                         this.port = port;
67                         BuildSink (null);
68                 }
69                 
70                 [MonoTODO ("Handle the listen property")]
71                 public HttpServerChannel (IDictionary properties, IServerChannelSinkProvider sinkProvider)
72                 {
73
74                         if (properties != null) {
75                                 foreach (DictionaryEntry property in properties) {
76                                         switch ((string)property.Key) {
77                                         case "name":
78                                                 //NOTE: matching MS behaviour: throws InvalidCastException, allows null
79                                                 this.name = (string)property.Value;
80                                                 break;
81                                         case "priority":
82                                                 this.priority = Convert.ToInt32 (property.Value);
83                                                 break;
84                                         case "port":
85                                                 this.port = Convert.ToInt32 (property.Value);
86                                                 break;
87                                         case "suppressChannelData":
88                                                 this.suppressChannelData = Convert.ToBoolean (property.Value);
89                                                 break;
90                                         case "bindTo":
91                                                 bindAddress = IPAddress.Parse ((string)property.Value);
92                                                 break;
93                                         case "useIpAddress":
94                                                 this.useIPAddress = Convert.ToBoolean (property.Value);
95                                                 break;
96                                         case "machineName":
97                                                 this.machineName = (string)property.Value;
98                                                 break;
99                                         case "listen":
100                                                 this.wantsToListen = Convert.ToBoolean (property.Value);
101                                                 break;
102                                         }
103                                 }
104                         }
105
106                         BuildSink (sinkProvider);
107                 }
108
109                 public HttpServerChannel (string name, int port)
110                         : this (name, port, null)
111                 {
112                 }
113
114                 public HttpServerChannel (string name, int port, IServerChannelSinkProvider sinkProvider)
115                 {
116                         this.name = name;
117                         this.port = port;
118                         BuildSink (sinkProvider);
119                 }
120
121                 void BuildSink (IServerChannelSinkProvider sinkProvider)
122                 {
123                         //resolve names (modified from TcpChannel)
124                         if (machineName == null) {
125                                 if (useIPAddress) {
126                                         if (!bindAddress.Equals (IPAddress.Any)) {
127                                                 machineName = bindAddress.ToString ();
128                                         } else {
129                                                 IPHostEntry hostEntry = Dns.Resolve (Dns.GetHostName ());
130                                                 if (hostEntry.AddressList.Length == 0)
131                                                         throw new RemotingException ("IP address could not be determined for this host");
132                                                 // We DON'T want to take the resolved address from the hostEntry, since the socket
133                                                 // should still bind to IPAddress.Any, so that we get the loopback too
134                                                 machineName = hostEntry.AddressList[0].ToString ();
135                                         }
136                                 } else {
137                                         IPHostEntry hostEntry = Dns.GetHostByName (Dns.GetHostName ());
138                                         bindAddress = hostEntry.AddressList[0];
139                                         machineName = hostEntry.HostName;
140                                 }
141                         }
142
143                         if (sinkProvider == null) {
144                                 //build a default chain that can handle wsdl, soap, binary
145                                 sinkProvider = new SdlChannelSinkProvider (); //for wsdl
146                                 sinkProvider.Next = new SoapServerFormatterSinkProvider ();
147                                 sinkProvider.Next.Next = new BinaryServerFormatterSinkProvider ();
148                         }
149                         
150                         //MS compat: channelData is null when port < 0
151                         if (port >= 0) {
152                                 channelData = new ChannelDataStore (null);
153                                 IServerChannelSinkProvider provider = sinkProvider;
154                                 while (provider != null) {
155                                         provider.GetChannelData (channelData);
156                                         provider = provider.Next;
157                                 }
158                         }
159                         
160                         //create the sink chain and add an HTTP sink
161                         IServerChannelSink nextSink = ChannelServices.CreateServerChannelSinkChain (sinkProvider, this);
162                         sink = new HttpServerTransportSink (nextSink);
163
164                         // BaseChannelWithProperties wants this to be set with the chain
165                         base.SinksWithProperties = nextSink;
166
167                         StartListening (null);
168                 }
169
170                 #endregion
171
172                 #region IChannel
173
174                 public string ChannelName
175                 {
176                         get { return name; }
177                 }
178
179                 public int ChannelPriority
180                 {
181                         get { return priority; }
182                 }
183
184                 public string Parse (string url, out string objectURI)
185                 {
186                         return HttpChannel.ParseInternal (url, out objectURI);
187                 }
188
189                 #endregion
190
191                 public string GetChannelUri ()
192                 {
193                         return "http://" + machineName + ":" + port;
194                 }
195
196                 #region IChannelReceiver (: IChannel)
197
198                 public object ChannelData
199                 {
200                         get
201                         {
202                                 return suppressChannelData ? null
203                                         : channelData;
204                         }
205                 }
206
207                 //from TcpServerChannel
208                 public virtual string[] GetUrlsForUri (string objectUri)
209                 {
210                         if (!objectUri.StartsWith ("/"))
211                                 objectUri = "/" + objectUri;
212
213                         if (channelData == null || channelData.ChannelUris == null || channelData.ChannelUris.Length < 1) {
214                                 return new string[] { GetChannelUri () + objectUri };
215                         }
216
217                         string[] channelUris = channelData.ChannelUris;
218                         string[] result = new string[channelUris.Length];
219
220                         for (int i = 0; i < channelUris.Length; i++)
221                                 result[i] = channelUris[i] + objectUri;
222
223                         return result;
224                 }
225
226                 public void StartListening (object data)
227                 {
228                         if (listener != null)
229                                 return;
230
231                         if (port < 0)
232                                 return;
233
234                         try {
235                                 listener = new RemotingHttpListener (bindAddress, port, sink);
236                         } catch (Exception) {
237                                 if (listener != null) {
238                                         listener.Dispose ();
239                                         listener = null;
240                                 }
241                                 throw;
242                         }
243                         
244                         if (port == 0)
245                                 port = listener.AssignedPort;
246                         
247                         channelData.ChannelUris = new string [] { GetChannelUri () };
248                         wantsToListen = false;
249                 }
250
251                 public void StopListening (object data)
252                 {
253                         if (listener != null) {
254                                 listener.Dispose ();
255                                 listener = null;
256                         }
257                 }
258
259                 #endregion
260
261                 #region BaseChannelWithProperties overrides
262
263                 public override object this[object key]
264                 {
265                         get { return base[key]; }
266                         set { base[key] = value; }
267                 }
268
269                 public override ICollection Keys
270                 {
271                         get { return new object[0]; }
272                 }
273
274                 #endregion
275
276                 #region IChannelReceiverHook
277
278                 public void AddHookChannelUri (string channelUri)
279                 {
280                         string [] newUris = new string[1] { channelUri };
281                         if (channelData == null)
282                                 channelData = new ChannelDataStore (newUris);
283                         else
284                                 channelData.ChannelUris = newUris;
285                         wantsToListen = false;
286                 }
287
288                 public string ChannelScheme
289                 {
290                         get { return "http"; }
291                 }
292
293                 public IServerChannelSink ChannelSinkChain
294                 {
295                         get { return (IServerChannelSink)base.SinksWithProperties; }
296                 }
297
298                 public bool WantsToListen
299                 {
300                         get { return wantsToListen; }
301                         set {
302                                 throw new NotImplementedException ("Behaviour not yet determined");
303                         }
304                 }
305
306                 #endregion
307         }
308 }