2 // HttpClientChannel.cs
5 // Michael Hutchinson <mhutchinson@novell.com>
7 // Copyright (C) 2008 Novell, Inc (http://www.novell.com)
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:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
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.
31 using System.Collections;
32 using System.Globalization;
33 using System.Runtime.Remoting.Messaging;
35 namespace System.Runtime.Remoting.Channels.Http
38 public class HttpClientChannel : BaseChannelWithProperties,
39 IChannel, IChannelSender
42 string name = "http client";
46 // names and (some) default values fromhttp://msdn.microsoft.com/en-us/library/bb187435(VS.85).aspx
47 // other values guessed from defaults of HttpWebResponse
48 string machineName = null;
49 bool allowAutoRedirect = true; //FIXME: what's the default? true/false?
50 int clientConnectionLimit = 2;
51 string connectionGroupName = null;
52 ICredentials credentials = null;
54 string password = null;
55 string proxyName = null;
58 string servicePrincipalName = null;
60 bool unsafeAuthenticatedConnectionSharing = false;
61 // according to docs, should be true if useDefaultCredentials true or
62 // credentials is CredentialCache.DefaultCredentials
63 bool useAuthenticatedConnectionSharing = false;
64 bool useDefaultCredentials = false;
65 string username = null;
67 bool isSecured = false;
69 IClientChannelSinkProvider sinkProvider;
73 public HttpClientChannel ()
78 [MonoTODO ("Handle the machineName, proxyName, proxyPort, servicePrincipalName, " +
79 "useAuthenticatedConnectionSharing properties")]
80 public HttpClientChannel (IDictionary properties, IClientChannelSinkProvider sinkProvider)
82 if (properties != null) {
83 foreach (DictionaryEntry property in properties) {
84 switch ((string)property.Key) {
86 //NOTE: matching MS behaviour: throws InvalidCastException, allows null
87 this.name = (string)property.Value;
90 this.priority = Convert.ToInt32 (property.Value);
93 this.machineName = (string)property.Value;
95 case "allowAutoRedirect":
96 this.allowAutoRedirect = Convert.ToBoolean (property.Value);
98 case "clientConnectionLimit":
99 this.clientConnectionLimit = Convert.ToInt32 (property.Value);
101 case "connectionGroupName":
102 this.connectionGroupName = (string)property.Value;
105 this.credentials = (ICredentials)property.Value;
106 if (this.credentials == CredentialCache.DefaultCredentials)
107 useAuthenticatedConnectionSharing = true;
110 this.domain = (string)property.Value;
113 this.password = (string)property.Value;
116 this.proxyName = (string)property.Value;
119 this.proxyPort = Convert.ToInt32 (property.Value);
121 case "servicePrincipalName":
122 this.servicePrincipalName = (string)property.Value;
125 this.timeout = Convert.ToInt32 (property.Value);
127 case "unsafeAuthenticatedConnectionSharing":
128 this.unsafeAuthenticatedConnectionSharing = Convert.ToBoolean (property.Value);
130 case "useAuthenticatedConnectionSharing":
131 this.useAuthenticatedConnectionSharing = Convert.ToBoolean (property.Value);
133 case "useDefaultCredentials":
134 this.useDefaultCredentials = Convert.ToBoolean (property.Value);
135 if (useDefaultCredentials)
136 useAuthenticatedConnectionSharing = true;
139 this.username = (string)property.Value;
145 BuildSink (sinkProvider);
148 public HttpClientChannel (string name, IClientChannelSinkProvider sinkProvider)
151 BuildSink (sinkProvider);
154 void BuildSink (IClientChannelSinkProvider sinkProvider)
156 if (sinkProvider == null) {
157 //according to docs, defaults to SOAP if no other sink provided
158 sinkProvider = new SoapClientFormatterSinkProvider ();
161 this.sinkProvider = sinkProvider;
163 //add HTTP sink at the end of the chain
164 while (sinkProvider.Next != null) sinkProvider = sinkProvider.Next;
165 sinkProvider.Next = new HttpClientTransportSinkProvider ();
167 // LAMESPEC: BaseChannelWithProperties wants SinksWithProperties to be set with the sink chain
168 // BUT MS' HttpClientChannel does not set it (inspected from HttpClientChannel subclass)
173 #region BaseChannelWithProperties overrides
175 public override object this[object key]
179 switch (key as string) {
189 switch (key as string) {
191 proxyPort = Convert.ToInt32 (value);
195 proxyName = (string)value;
199 //ignore other values, MS does so
203 public override ICollection Keys
207 return new string[] {
214 void ConstructProxy ()
216 if (proxyName != null && proxyPort > 0)
217 proxyUri = new Uri (proxyName + ":" + proxyPort);
224 public string ChannelName
229 public int ChannelPriority
231 get { return priority; }
234 public string Parse (string url, out string objectURI)
236 return HttpChannel.ParseInternal (url, out objectURI);
241 #region IChannelSender (: IChannel)
243 public virtual IMessageSink CreateMessageSink (string url, object remoteChannelData, out string objectURI)
245 //Mostly copied from TcpClientChannel
246 if (url == null || Parse (url, out objectURI) == null) {
247 if (remoteChannelData != null) {
248 IChannelDataStore ds = remoteChannelData as IChannelDataStore;
249 if (ds != null && ds.ChannelUris.Length > 0)
250 url = ds.ChannelUris[0];
257 if (Parse (url, out objectURI) == null)
261 object newSink = sinkProvider.CreateSink (this, url, remoteChannelData);
262 if (newSink is IMessageSink) {
263 return (IMessageSink) newSink;
265 throw new RemotingException ("First channel sink must implement IMessageSink");
271 #region ISecurableChannel
273 public bool IsSecured
275 get { return isSecured; }
277 throw new NotImplementedException ("Unable to determine expected behaviour yet.");
283 #region Internal properties
285 internal string MachineName {
286 get { return machineName; }
288 internal bool AllowAutoRedirect {
289 get { return allowAutoRedirect; }
291 internal int ClientConnectionLimit {
292 get { return clientConnectionLimit; }
294 internal string ConnectionGroupName {
295 get { return connectionGroupName; }
297 internal ICredentials Credentials {
298 get { return credentials; }
300 internal string Domain {
301 get { return domain; }
303 internal string Password {
304 get { return password; }
306 internal string Username {
307 get { return username; }
309 internal string ProxyName {
310 get { return proxyName; }
312 internal int ProxyPort {
313 get { return proxyPort; }
315 internal Uri ProxyUri {
316 get { return proxyUri; }
318 internal string ServicePrincipalName {
319 get { return servicePrincipalName; }
321 internal int Timeout {
322 get { return timeout; }
324 internal bool UnsafeAuthenticatedConnectionSharing {
325 get { return unsafeAuthenticatedConnectionSharing; }
327 internal bool UseAuthenticatedConnectionSharing {
328 get { return useAuthenticatedConnectionSharing; }
330 internal bool UseDefaultCredentials {
331 get { return useDefaultCredentials; }