New tests, updates.
[mono.git] / mcs / class / System.Runtime.Remoting / System.Runtime.Remoting.Channels.Http / HttpClientChannel.cs
1 //
2 // HttpClientChannel.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;
30 using System.Net;
31 using System.Collections;
32 using System.Globalization;
33 using System.Runtime.Remoting.Messaging;
34
35 namespace System.Runtime.Remoting.Channels.Http
36 {
37
38         public class HttpClientChannel : BaseChannelWithProperties,
39                 IChannel, IChannelSender
40 #if NET_2_0
41                 , ISecurableChannel
42 #endif
43         {
44                 string name = "http client";
45                 int priority = 1;
46
47
48                 // names and (some) default values fromhttp://msdn.microsoft.com/en-us/library/bb187435(VS.85).aspx
49                 // other values guessed from defaults of HttpWebResponse
50                 string machineName = null;
51                 bool allowAutoRedirect = true; //FIXME: what's the default? true/false?
52                 int clientConnectionLimit = 2;
53                 string connectionGroupName = null;
54                 ICredentials credentials = null;
55                 string domain = null;
56                 string password = null;
57                 string proxyName = null;
58                 int proxyPort = -1;
59                 Uri proxyUri = null;
60                 string servicePrincipalName = null;
61                 int timeout = -1;
62                 bool unsafeAuthenticatedConnectionSharing = false;
63                 // according to docs, should be true if useDefaultCredentials true or 
64                 // credentials is CredentialCache.DefaultCredentials
65                 bool useAuthenticatedConnectionSharing = false;
66                 bool useDefaultCredentials = false;
67                 string username = null;
68                 
69                 bool isSecured = false;
70
71                 IClientChannelSinkProvider sinkProvider;
72
73                 #region Constructors
74
75                 public HttpClientChannel ()
76                 {
77                         BuildSink (null);
78                 }
79                 
80                 [MonoTODO ("Handle the machineName, proxyName, proxyPort, servicePrincipalName, " + 
81                         "useAuthenticatedConnectionSharing properties")]
82                 public HttpClientChannel (IDictionary properties, IClientChannelSinkProvider sinkProvider)
83                 {
84                         if (properties != null) {
85                                 foreach (DictionaryEntry property in properties) {
86                                         switch ((string)property.Key) {
87                                         case "name":
88                                                 //NOTE: matching MS behaviour: throws InvalidCastException, allows null
89                                                 this.name = (string)property.Value;
90                                                 break;
91                                         case "priority":
92                                                 this.priority = Convert.ToInt32 (property.Value);
93                                                 break;
94                                         case "machineName":
95                                                 this.machineName = (string)property.Value;
96                                                 break;
97                                         case "allowAutoRedirect":
98                                                 this.allowAutoRedirect = Convert.ToBoolean (property.Value);
99                                                 break;
100                                         case "clientConnectionLimit":
101                                                 this.clientConnectionLimit = Convert.ToInt32 (property.Value);
102                                                 break;
103                                         case "connectionGroupName":
104                                                 this.connectionGroupName = (string)property.Value;
105                                                 break;
106                                         case "credentials":
107                                                 this.credentials = (ICredentials)property.Value;
108                                                 if (this.credentials == CredentialCache.DefaultCredentials)
109                                                         useAuthenticatedConnectionSharing = true;
110                                                 break;
111                                         case "domain":
112                                                 this.domain = (string)property.Value;
113                                                 break;
114                                         case "password":
115                                                 this.password = (string)property.Value;
116                                                 break;
117                                         case "proxyName":
118                                                 this.proxyName = (string)property.Value;
119                                                 break;
120                                         case "proxyPort":
121                                                 this.proxyPort = Convert.ToInt32 (property.Value);
122                                                 break;
123                                         case "servicePrincipalName":
124                                                 this.servicePrincipalName = (string)property.Value;
125                                                 break;
126                                         case "timeout":
127                                                 this.timeout = Convert.ToInt32 (property.Value);
128                                                 break;
129                                         case "unsafeAuthenticatedConnectionSharing":
130                                                 this.unsafeAuthenticatedConnectionSharing = Convert.ToBoolean (property.Value);
131                                                 break;
132                                         case "useAuthenticatedConnectionSharing":
133                                                 this.useAuthenticatedConnectionSharing = Convert.ToBoolean (property.Value);
134                                                 break;
135                                         case "useDefaultCredentials":
136                                                 this.useDefaultCredentials = Convert.ToBoolean (property.Value);
137                                                 if (useDefaultCredentials)
138                                                         useAuthenticatedConnectionSharing = true;
139                                                 break;
140                                         case "username":
141                                                 this.username = (string)property.Value;
142                                                 break;
143                                         }
144                                 }
145                         }
146
147                         BuildSink (sinkProvider);
148                 }
149
150                 public HttpClientChannel (string name, IClientChannelSinkProvider sinkProvider)
151                 {
152                         this.name = name;
153                         BuildSink (sinkProvider);
154                 }
155
156                 void BuildSink (IClientChannelSinkProvider sinkProvider)
157                 {
158                         if (sinkProvider == null) {
159                                 //according to docs, defaults to SOAP if no other sink provided
160                                 sinkProvider = new SoapClientFormatterSinkProvider ();
161                         }
162
163                         this.sinkProvider = sinkProvider;
164
165                         //add HTTP sink at the end of the chain
166                         while (sinkProvider.Next != null) sinkProvider = sinkProvider.Next;
167                         sinkProvider.Next = new HttpClientTransportSinkProvider ();
168
169                         // LAMESPEC: BaseChannelWithProperties wants SinksWithProperties to be set with the sink chain
170                         // BUT MS' HttpClientChannel does not set it (inspected from HttpClientChannel subclass)
171                 }
172
173                 #endregion
174
175                 #region BaseChannelWithProperties overrides
176
177                 public override object this[object key]
178                 {
179                         get
180                         {
181                                 switch (key as string) {
182                                 case "proxyport":
183                                         return proxyPort;
184                                 case "proxyname":
185                                         return proxyName;
186                                 }
187                                 return null;
188                         }
189                         set
190                         {
191                                 switch (key as string) {
192                                 case "proxyport":
193                                         proxyPort = Convert.ToInt32 (value);
194                                         ConstructProxy ();
195                                         return;
196                                 case "proxyname":
197                                         proxyName = (string)value;
198                                         ConstructProxy ();
199                                         return;
200                                 }
201                                 //ignore other values, MS does so
202                         }
203                 }
204
205                 public override ICollection Keys
206                 {
207                         get
208                         {
209                                 return new string[] {
210                                         "proxyname",
211                                         "proxyport"
212                                 };
213                         }
214                 }
215
216                 void ConstructProxy ()
217                 {
218                         if (proxyName != null && proxyPort > 0)
219                                 proxyUri = new Uri (proxyName + ":" + proxyPort);
220                 }
221
222                 #endregion
223
224                 #region IChannel
225
226                 public string ChannelName
227                 {
228                         get { return name; }
229                 }
230
231                 public int ChannelPriority
232                 {
233                         get { return priority; }
234                 }
235
236                 public string Parse (string url, out string objectURI)
237                 {
238                         return HttpChannel.ParseInternal (url, out objectURI);
239                 }
240
241                 #endregion
242
243                 #region IChannelSender (: IChannel)
244
245                 public virtual IMessageSink CreateMessageSink (string url, object remoteChannelData, out string objectURI)
246                 {
247                         //Mostly copied from TcpClientChannel
248                         if (url == null || Parse (url, out objectURI) == null) {
249                                 if (remoteChannelData != null) {
250                                         IChannelDataStore ds = remoteChannelData as IChannelDataStore;
251                                         if (ds != null && ds.ChannelUris.Length > 0)
252                                                 url = ds.ChannelUris[0];
253                                         else {
254                                                 objectURI = null;
255                                                 return null;
256                                         }
257                                 }
258
259                                 if (Parse (url, out objectURI) == null)
260                                         return null;
261                         }
262                         
263                         object newSink = sinkProvider.CreateSink (this, url, remoteChannelData);
264                         if (newSink is IMessageSink) {
265                                 return (IMessageSink) newSink;
266                         } else {
267                                 throw new RemotingException ("First channel sink must implement IMessageSink");
268                         }
269                 }
270
271                 #endregion
272                 
273 #if NET_2_0
274                 #region ISecurableChannel
275                 
276                 public bool IsSecured
277                 {
278                         get { return isSecured; }
279                         set {
280                                 throw new NotImplementedException ("Unable to determine expected behaviour yet.");
281                         }
282                 }
283                 
284                 #endregion
285 #endif
286                 
287                 #region Internal properties
288                 
289                 internal string MachineName {
290                         get { return machineName; }
291                 }
292                 internal bool AllowAutoRedirect {
293                         get { return allowAutoRedirect; }
294                 }
295                 internal int ClientConnectionLimit {
296                         get { return clientConnectionLimit; }
297                 }
298                 internal string ConnectionGroupName {
299                         get { return connectionGroupName; }
300                 }
301                 internal ICredentials Credentials {
302                         get { return credentials; }
303                 }
304                 internal string Domain {
305                         get { return domain; }
306                 }
307                 internal string Password {
308                         get { return password; }
309                 }
310                 internal string Username {
311                         get { return username; }
312                 }
313                 internal string ProxyName {
314                         get { return proxyName; }
315                 }
316                 internal int ProxyPort {
317                         get { return proxyPort; }
318                 }
319                 internal Uri ProxyUri {
320                         get { return proxyUri; }
321                 }
322                 internal string ServicePrincipalName {
323                         get { return servicePrincipalName; }
324                 }
325                 internal int Timeout {
326                         get { return timeout; }
327                 }
328                 internal bool UnsafeAuthenticatedConnectionSharing {
329                         get { return unsafeAuthenticatedConnectionSharing; }
330                 }
331                 internal bool UseAuthenticatedConnectionSharing {
332                         get { return useAuthenticatedConnectionSharing; }
333                 }
334                 internal bool UseDefaultCredentials {
335                         get { return useDefaultCredentials; }
336                 }
337                 
338                 #endregion
339
340         }
341 }