// // HttpClientChannel.cs // // Author: // Michael Hutchinson // // Copyright (C) 2008 Novell, Inc (http://www.novell.com) // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System; using System.Net; using System.Collections; using System.Globalization; using System.Runtime.Remoting.Messaging; namespace System.Runtime.Remoting.Channels.Http { public class HttpClientChannel : BaseChannelWithProperties, IChannel, IChannelSender #if NET_2_0 , ISecurableChannel #endif { string name = "http client"; int priority = 1; // names and (some) default values fromhttp://msdn.microsoft.com/en-us/library/bb187435(VS.85).aspx // other values guessed from defaults of HttpWebResponse string machineName = null; bool allowAutoRedirect = true; //FIXME: what's the default? true/false? int clientConnectionLimit = 2; string connectionGroupName = null; ICredentials credentials = null; string domain = null; string password = null; string proxyName = null; int proxyPort = -1; Uri proxyUri = null; string servicePrincipalName = null; int timeout = -1; bool unsafeAuthenticatedConnectionSharing = false; // according to docs, should be true if useDefaultCredentials true or // credentials is CredentialCache.DefaultCredentials bool useAuthenticatedConnectionSharing = false; bool useDefaultCredentials = false; string username = null; bool isSecured = false; IClientChannelSinkProvider sinkProvider; #region Constructors public HttpClientChannel () { BuildSink (null); } [MonoTODO ("Handle the machineName, proxyName, proxyPort, servicePrincipalName, " + "useAuthenticatedConnectionSharing properties")] public HttpClientChannel (IDictionary properties, IClientChannelSinkProvider sinkProvider) { if (properties != null) { foreach (DictionaryEntry property in properties) { switch ((string)property.Key) { case "name": //NOTE: matching MS behaviour: throws InvalidCastException, allows null this.name = (string)property.Value; break; case "priority": this.priority = Convert.ToInt32 (property.Value); break; case "machineName": this.machineName = (string)property.Value; break; case "allowAutoRedirect": this.allowAutoRedirect = Convert.ToBoolean (property.Value); break; case "clientConnectionLimit": this.clientConnectionLimit = Convert.ToInt32 (property.Value); break; case "connectionGroupName": this.connectionGroupName = (string)property.Value; break; case "credentials": this.credentials = (ICredentials)property.Value; if (this.credentials == CredentialCache.DefaultCredentials) useAuthenticatedConnectionSharing = true; break; case "domain": this.domain = (string)property.Value; break; case "password": this.password = (string)property.Value; break; case "proxyName": this.proxyName = (string)property.Value; break; case "proxyPort": this.proxyPort = Convert.ToInt32 (property.Value); break; case "servicePrincipalName": this.servicePrincipalName = (string)property.Value; break; case "timeout": this.timeout = Convert.ToInt32 (property.Value); break; case "unsafeAuthenticatedConnectionSharing": this.unsafeAuthenticatedConnectionSharing = Convert.ToBoolean (property.Value); break; case "useAuthenticatedConnectionSharing": this.useAuthenticatedConnectionSharing = Convert.ToBoolean (property.Value); break; case "useDefaultCredentials": this.useDefaultCredentials = Convert.ToBoolean (property.Value); if (useDefaultCredentials) useAuthenticatedConnectionSharing = true; break; case "username": this.username = (string)property.Value; break; } } } BuildSink (sinkProvider); } public HttpClientChannel (string name, IClientChannelSinkProvider sinkProvider) { this.name = name; BuildSink (sinkProvider); } void BuildSink (IClientChannelSinkProvider sinkProvider) { if (sinkProvider == null) { //according to docs, defaults to SOAP if no other sink provided sinkProvider = new SoapClientFormatterSinkProvider (); } this.sinkProvider = sinkProvider; //add HTTP sink at the end of the chain while (sinkProvider.Next != null) sinkProvider = sinkProvider.Next; sinkProvider.Next = new HttpClientTransportSinkProvider (); // LAMESPEC: BaseChannelWithProperties wants SinksWithProperties to be set with the sink chain // BUT MS' HttpClientChannel does not set it (inspected from HttpClientChannel subclass) } #endregion #region BaseChannelWithProperties overrides public override object this[object key] { get { switch (key as string) { case "proxyport": return proxyPort; case "proxyname": return proxyName; } return null; } set { switch (key as string) { case "proxyport": proxyPort = Convert.ToInt32 (value); ConstructProxy (); return; case "proxyname": proxyName = (string)value; ConstructProxy (); return; } //ignore other values, MS does so } } public override ICollection Keys { get { return new string[] { "proxyname", "proxyport" }; } } void ConstructProxy () { if (proxyName != null && proxyPort > 0) proxyUri = new Uri (proxyName + ":" + proxyPort); } #endregion #region IChannel public string ChannelName { get { return name; } } public int ChannelPriority { get { return priority; } } public string Parse (string url, out string objectURI) { return HttpChannel.ParseInternal (url, out objectURI); } #endregion #region IChannelSender (: IChannel) public virtual IMessageSink CreateMessageSink (string url, object remoteChannelData, out string objectURI) { //Mostly copied from TcpClientChannel if (url == null || Parse (url, out objectURI) == null) { if (remoteChannelData != null) { IChannelDataStore ds = remoteChannelData as IChannelDataStore; if (ds != null && ds.ChannelUris.Length > 0) url = ds.ChannelUris[0]; else { objectURI = null; return null; } } if (Parse (url, out objectURI) == null) return null; } object newSink = sinkProvider.CreateSink (this, url, remoteChannelData); if (newSink is IMessageSink) { return (IMessageSink) newSink; } else { throw new RemotingException ("First channel sink must implement IMessageSink"); } } #endregion #if NET_2_0 #region ISecurableChannel public bool IsSecured { get { return isSecured; } set { throw new NotImplementedException ("Unable to determine expected behaviour yet."); } } #endregion #endif #region Internal properties internal string MachineName { get { return machineName; } } internal bool AllowAutoRedirect { get { return allowAutoRedirect; } } internal int ClientConnectionLimit { get { return clientConnectionLimit; } } internal string ConnectionGroupName { get { return connectionGroupName; } } internal ICredentials Credentials { get { return credentials; } } internal string Domain { get { return domain; } } internal string Password { get { return password; } } internal string Username { get { return username; } } internal string ProxyName { get { return proxyName; } } internal int ProxyPort { get { return proxyPort; } } internal Uri ProxyUri { get { return proxyUri; } } internal string ServicePrincipalName { get { return servicePrincipalName; } } internal int Timeout { get { return timeout; } } internal bool UnsafeAuthenticatedConnectionSharing { get { return unsafeAuthenticatedConnectionSharing; } } internal bool UseAuthenticatedConnectionSharing { get { return useAuthenticatedConnectionSharing; } } internal bool UseDefaultCredentials { get { return useDefaultCredentials; } } #endregion } }