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
44 string name
= "http client";
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;
56 string password
= null;
57 string proxyName
= null;
60 string servicePrincipalName
= null;
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;
69 bool isSecured
= false;
71 IClientChannelSinkProvider sinkProvider
;
75 public HttpClientChannel ()
80 [MonoTODO ("Handle the machineName, proxyName, proxyPort, servicePrincipalName, " +
81 "useAuthenticatedConnectionSharing properties")]
82 public HttpClientChannel (IDictionary properties
, IClientChannelSinkProvider sinkProvider
)
84 if (properties
!= null) {
85 foreach (DictionaryEntry property
in properties
) {
86 switch ((string)property
.Key
) {
88 //NOTE: matching MS behaviour: throws InvalidCastException, allows null
89 this.name
= (string)property
.Value
;
92 this.priority
= Convert
.ToInt32 (property
.Value
);
95 this.machineName
= (string)property
.Value
;
97 case "allowAutoRedirect":
98 this.allowAutoRedirect
= Convert
.ToBoolean (property
.Value
);
100 case "clientConnectionLimit":
101 this.clientConnectionLimit
= Convert
.ToInt32 (property
.Value
);
103 case "connectionGroupName":
104 this.connectionGroupName
= (string)property
.Value
;
107 this.credentials
= (ICredentials
)property
.Value
;
108 if (this.credentials
== CredentialCache
.DefaultCredentials
)
109 useAuthenticatedConnectionSharing
= true;
112 this.domain
= (string)property
.Value
;
115 this.password
= (string)property
.Value
;
118 this.proxyName
= (string)property
.Value
;
121 this.proxyPort
= Convert
.ToInt32 (property
.Value
);
123 case "servicePrincipalName":
124 this.servicePrincipalName
= (string)property
.Value
;
127 this.timeout
= Convert
.ToInt32 (property
.Value
);
129 case "unsafeAuthenticatedConnectionSharing":
130 this.unsafeAuthenticatedConnectionSharing
= Convert
.ToBoolean (property
.Value
);
132 case "useAuthenticatedConnectionSharing":
133 this.useAuthenticatedConnectionSharing
= Convert
.ToBoolean (property
.Value
);
135 case "useDefaultCredentials":
136 this.useDefaultCredentials
= Convert
.ToBoolean (property
.Value
);
137 if (useDefaultCredentials
)
138 useAuthenticatedConnectionSharing
= true;
141 this.username
= (string)property
.Value
;
147 BuildSink (sinkProvider
);
150 public HttpClientChannel (string name
, IClientChannelSinkProvider sinkProvider
)
153 BuildSink (sinkProvider
);
156 void BuildSink (IClientChannelSinkProvider sinkProvider
)
158 if (sinkProvider
== null) {
159 //according to docs, defaults to SOAP if no other sink provided
160 sinkProvider
= new SoapClientFormatterSinkProvider ();
163 this.sinkProvider
= sinkProvider
;
165 //add HTTP sink at the end of the chain
166 while (sinkProvider
.Next
!= null) sinkProvider
= sinkProvider
.Next
;
167 sinkProvider
.Next
= new HttpClientTransportSinkProvider ();
169 // LAMESPEC: BaseChannelWithProperties wants SinksWithProperties to be set with the sink chain
170 // BUT MS' HttpClientChannel does not set it (inspected from HttpClientChannel subclass)
175 #region BaseChannelWithProperties overrides
177 public override object this[object key
]
181 switch (key
as string) {
191 switch (key
as string) {
193 proxyPort
= Convert
.ToInt32 (value);
197 proxyName
= (string)value;
201 //ignore other values, MS does so
205 public override ICollection Keys
209 return new string[] {
216 void ConstructProxy ()
218 if (proxyName
!= null && proxyPort
> 0)
219 proxyUri
= new Uri (proxyName
+ ":" + proxyPort
);
226 public string ChannelName
231 public int ChannelPriority
233 get { return priority; }
236 public string Parse (string url
, out string objectURI
)
238 return HttpChannel
.ParseInternal (url
, out objectURI
);
243 #region IChannelSender (: IChannel)
245 public virtual IMessageSink
CreateMessageSink (string url
, object remoteChannelData
, out string objectURI
)
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];
259 if (Parse (url
, out objectURI
) == null)
263 object newSink
= sinkProvider
.CreateSink (this, url
, remoteChannelData
);
264 if (newSink
is IMessageSink
) {
265 return (IMessageSink
) newSink
;
267 throw new RemotingException ("First channel sink must implement IMessageSink");
274 #region ISecurableChannel
276 public bool IsSecured
278 get { return isSecured; }
280 throw new NotImplementedException ("Unable to determine expected behaviour yet.");
287 #region Internal properties
289 internal string MachineName
{
290 get { return machineName; }
292 internal bool AllowAutoRedirect
{
293 get { return allowAutoRedirect; }
295 internal int ClientConnectionLimit
{
296 get { return clientConnectionLimit; }
298 internal string ConnectionGroupName
{
299 get { return connectionGroupName; }
301 internal ICredentials Credentials
{
302 get { return credentials; }
304 internal string Domain
{
305 get { return domain; }
307 internal string Password
{
308 get { return password; }
310 internal string Username
{
311 get { return username; }
313 internal string ProxyName
{
314 get { return proxyName; }
316 internal int ProxyPort
{
317 get { return proxyPort; }
319 internal Uri ProxyUri
{
320 get { return proxyUri; }
322 internal string ServicePrincipalName
{
323 get { return servicePrincipalName; }
325 internal int Timeout
{
326 get { return timeout; }
328 internal bool UnsafeAuthenticatedConnectionSharing
{
329 get { return unsafeAuthenticatedConnectionSharing; }
331 internal bool UseAuthenticatedConnectionSharing
{
332 get { return useAuthenticatedConnectionSharing; }
334 internal bool UseDefaultCredentials
{
335 get { return useDefaultCredentials; }