The Labs \ Source Viewer \ SSCLI \ System.Runtime.Remoting.Channels.Http \ HttpClientTransportSink

  1. // ==++==
  2. //
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. //
  14. // ==--==
  15. //==========================================================================
  16. // File: HttpClientChannel.cs
  17. //
  18. // Summary: Implements a client channel that transmits method calls over HTTP.
  19. //
  20. // Classes: public HttpClientChannel
  21. // internal HttpClientTransportSink
  22. //
  23. //==========================================================================
  24. using System;
  25. using System.Collections;
  26. using System.IO;
  27. using System.Net;
  28. using System.Net.Cache;
  29. using System.Text;
  30. using System.Runtime.InteropServices;
  31. using System.Security.Principal;
  32. using System.ComponentModel;
  33. using System.Runtime.Remoting;
  34. using System.Runtime.Remoting.Channels;
  35. using System.Runtime.Remoting.Messaging;
  36. using System.Threading;
  37. using System.Globalization;
  38. using System.Security.Permissions;
  39. namespace System.Runtime.Remoting.Channels.Http
  40. {
  41.    
  42.    
  43.    
  44.     public class HttpClientChannel : BaseChannelWithProperties, IChannelSender, ISecurableChannel
  45.     {
  46.         // Property Keys (purposely all lower-case)
  47.         private const string ProxyNameKey = "proxyname";
  48.         private const string ProxyPortKey = "proxyport";
  49.        
  50.         // If above keys get modified be sure to modify, the KeySet property on this
  51.         // class.
  52.         private static ICollection s_keySet = null;
  53.        
  54.        
  55.         // Settings
  56.         private int _channelPriority = 1;
  57.         // channel priority
  58.         private string _channelName = "http client";
  59.         // channel name
  60.         // Proxy settings (_proxyObject gets recreated when _proxyName and _proxyPort are updated)
  61.         private IWebProxy _proxyObject = null;
  62.         // proxy object for request, can be overridden in transport sink
  63.         private string _proxyName = null;
  64.         private int _proxyPort = -1;
  65.         private int _timeout = System.Threading.Timeout.Infinite;
  66.         // default timeout is infinite
  67.         private int _clientConnectionLimit = 0;
  68.         // bump connection limit to at least this number (only meaningful if > 0)
  69.         private bool _bUseDefaultCredentials = false;
  70.         // should default credentials be used?
  71.         private bool _bAuthenticatedConnectionSharing = true;
  72.         private bool _secure = false;
  73.        
  74.         private IClientChannelSinkProvider _sinkProvider = null;
  75.         // sink chain provider
  76.        
  77.         public HttpClientChannel()
  78.         {
  79.             SetupChannel();
  80.         }
  81.         // HttpClientChannel()
  82.        
  83.         public HttpClientChannel(string name, IClientChannelSinkProvider sinkProvider)
  84.         {
  85.             _channelName = name;
  86.             _sinkProvider = sinkProvider;
  87.            
  88.             SetupChannel();
  89.         }
  90.         // HttpClientChannel(IClientChannelSinkProvider sinkProvider)
  91.        
  92.         // constructor used by config file
  93.         public HttpClientChannel(IDictionary properties, IClientChannelSinkProvider sinkProvider)
  94.         {
  95.             if (properties != null) {
  96.                 foreach (DictionaryEntry entry in properties) {
  97.                     switch ((string)entry.Key) {
  98.                         case "name":
  99.                             _channelName = (string)entry.Value;
  100.                             break;
  101.                         case "priority":
  102.                             _channelPriority = Convert.ToInt32(entry.Value, CultureInfo.InvariantCulture);
  103.                             break;
  104.                         case "proxyName":
  105.                            
  106.                             this["proxyName"] = entry.Value;
  107.                             break;
  108.                         case "proxyPort":
  109.                             this["proxyPort"] = entry.Value;
  110.                             break;
  111.                         case "timeout":
  112.                             _timeout = Convert.ToInt32(entry.Value, CultureInfo.InvariantCulture);
  113.                             break;
  114.                         case "clientConnectionLimit":
  115.                            
  116.                            
  117.                             {
  118.                                 _clientConnectionLimit = Convert.ToInt32(entry.Value, CultureInfo.InvariantCulture);
  119.                                 break;
  120.                             }
  121.                             break;
  122.                         case "useDefaultCredentials":
  123.                            
  124.                            
  125.                             {
  126.                                 _bUseDefaultCredentials = Convert.ToBoolean(entry.Value, CultureInfo.InvariantCulture);
  127.                                 break;
  128.                             }
  129.                             break;
  130.                         case "useAuthenticatedConnectionSharing":
  131.                            
  132.                            
  133.                             {
  134.                                 _bAuthenticatedConnectionSharing = Convert.ToBoolean(entry.Value, CultureInfo.InvariantCulture);
  135.                                 break;
  136.                             }
  137.                             break;
  138.                         default:
  139.                            
  140.                             break;
  141.                     }
  142.                 }
  143.             }
  144.            
  145.             _sinkProvider = sinkProvider;
  146.            
  147.             SetupChannel();
  148.         }
  149.         // HttpClientChannel
  150.        
  151.         private void SetupChannel()
  152.         {
  153.             if (_sinkProvider != null) {
  154.                 CoreChannel.AppendProviderToClientProviderChain(_sinkProvider, new HttpClientTransportSinkProvider(_timeout));
  155.             }
  156.             else
  157.                 _sinkProvider = CreateDefaultClientProviderChain();
  158.            
  159.         }
  160.         // SetupChannel()
  161.        
  162.        
  163.         //
  164.         // ISecurableChannel implementation
  165.         //
  166.         public bool IsSecured {
  167.             [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure, Infrastructure = true)]
  168.             get { return _secure; }
  169.             [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure, Infrastructure = true)]
  170.             set { _secure = value; }
  171.         }
  172.        
  173.         //
  174.         // IChannel implementation
  175.         //
  176.        
  177.         public int ChannelPriority {
  178.             [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure, Infrastructure = true)]
  179.             get { return _channelPriority; }
  180.         }
  181.        
  182.         public string ChannelName {
  183.             [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure, Infrastructure = true)]
  184.             get { return _channelName; }
  185.         }
  186.        
  187.         // returns channelURI and places object uri into out parameter
  188.         [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure, Infrastructure = true)]
  189.         public string Parse(string url, out string objectURI)
  190.         {
  191.             return HttpChannelHelper.ParseURL(url, out objectURI);
  192.         }
  193.         // Parse
  194.         //
  195.         // end of IChannel implementation
  196.         //
  197.        
  198.        
  199.        
  200.         //
  201.         // IChannelSender implementation
  202.         //
  203.        
  204.         [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure, Infrastructure = true)]
  205.         public virtual IMessageSink CreateMessageSink(string url, object remoteChannelData, out string objectURI)
  206.         {
  207.             // Set the out parameters
  208.             objectURI = null;
  209.             string channelURI = null;
  210.            
  211.            
  212.             // Is this a well known object?
  213.             if (url != null) {
  214.                 // Parse returns null if this is not one of our url's
  215.                 channelURI = Parse(url, out objectURI);
  216.             }
  217.             // determine if we want to connect based on the channel data
  218.             else {
  219.                 if (remoteChannelData != null) {
  220.                     if (remoteChannelData is IChannelDataStore) {
  221.                         IChannelDataStore cds = (IChannelDataStore)remoteChannelData;
  222.                        
  223.                         // see if this is an http uri
  224.                         string simpleChannelUri = Parse(cds.ChannelUris[0], out objectURI);
  225.                         if (simpleChannelUri != null)
  226.                             channelURI = cds.ChannelUris[0];
  227.                     }
  228.                 }
  229.             }
  230.            
  231.             if (channelURI != null) {
  232.                 if (url == null)
  233.                     url = channelURI;
  234.                
  235.                 if (_clientConnectionLimit > 0) {
  236.                     ServicePoint sp = ServicePointManager.FindServicePoint(new Uri(channelURI));
  237.                     if (sp.ConnectionLimit < _clientConnectionLimit)
  238.                         sp.ConnectionLimit = _clientConnectionLimit;
  239.                 }
  240.                
  241.                 // This will return null if one of the sink providers decides it doesn't
  242.                 // want to allow (or can't provide) a connection through this channel.
  243.                 IClientChannelSink sink = _sinkProvider.CreateSink(this, url, remoteChannelData);
  244.                
  245.                 // return sink after making sure that it implements IMessageSink
  246.                 IMessageSink msgSink = sink as IMessageSink;
  247.                 if ((sink != null) && (msgSink == null)) {
  248.                     throw new RemotingException(CoreChannel.GetResourceString("Remoting_Channels_ChannelSinkNotMsgSink"));
  249.                 }
  250.                
  251.                 return msgSink;
  252.             }
  253.            
  254.             return null;
  255.         }
  256.         // CreateMessageSink
  257.        
  258.         //
  259.         // end of IChannelSender implementation
  260.         //
  261.        
  262.        
  263.         private IClientChannelSinkProvider CreateDefaultClientProviderChain()
  264.         {
  265.             IClientChannelSinkProvider chain = new SoapClientFormatterSinkProvider();
  266.             IClientChannelSinkProvider sink = chain;
  267.            
  268.             sink.Next = new HttpClientTransportSinkProvider(_timeout);
  269.            
  270.             return chain;
  271.         }
  272.         // CreateDefaultClientProviderChain
  273.        
  274.        
  275.         //
  276.         // Support for properties (through BaseChannelSinkWithProperties)
  277.         //
  278.        
  279.         public override object this[object key]
  280.         {
  281.             get {
  282.                 string keyStr = key as string;
  283.                 if (keyStr == null)
  284.                     return null;
  285.                
  286.                 switch (keyStr.ToLower(CultureInfo.InvariantCulture)) {
  287.                     case ProxyNameKey:
  288.                         return _proxyName;
  289.                     case ProxyPortKey:
  290.                         return _proxyPort;
  291.                 }
  292.                 // switch (keyStr.ToLower(CultureInfo.InvariantCulture))
  293.                 return null;
  294.             }
  295.            
  296.             set {
  297.                 string keyStr = key as string;
  298.                 if (keyStr == null)
  299.                     return;
  300.                
  301.                 switch (keyStr.ToLower(CultureInfo.InvariantCulture)) {
  302.                     case ProxyNameKey:
  303.                         _proxyName = (string)value;
  304.                         UpdateProxy();
  305.                         break;
  306.                     case ProxyPortKey:
  307.                         _proxyPort = Convert.ToInt32(value, CultureInfo.InvariantCulture);
  308.                         UpdateProxy();
  309.                         break;
  310.                 }
  311.                 // switch (keyStr.ToLower(CultureInfo.InvariantCulture))
  312.             }
  313.         }
  314.         // this[]
  315.        
  316.         public override ICollection Keys {
  317.             get {
  318.                 if (s_keySet == null) {
  319.                     // Don't need to synchronize. Doesn't matter if the list gets
  320.                     // generated twice.
  321.                     ArrayList keys = new ArrayList(2);
  322.                     keys.Add(ProxyNameKey);
  323.                     keys.Add(ProxyPortKey);
  324.                    
  325.                     s_keySet = keys;
  326.                 }
  327.                
  328.                 return s_keySet;
  329.             }
  330.         }
  331.         // Keys
  332.        
  333.         //
  334.         // end of Support for properties
  335.         //
  336.        
  337.        
  338.         //
  339.         // Helper functions for processing settings and properties
  340.         //
  341.        
  342.         // Called to recreate proxy object whenever the proxy name or port is changed.
  343.         private void UpdateProxy()
  344.         {
  345.             if ((_proxyName != null) && (_proxyName.Length > 0) && (_proxyPort > 0)) {
  346.                 WebProxy proxy = new WebProxy(_proxyName, _proxyPort);
  347.                
  348.                 // disable proxy use when the host is local. i.e. without periods
  349.                 proxy.BypassProxyOnLocal = true;
  350.                
  351.                 // setup bypasslist to include local ip address
  352.                 string[] bypassList = new string[] {CoreChannel.GetMachineIp()};
  353.                 proxy.BypassList = bypassList;
  354.                
  355.                 _proxyObject = proxy;
  356.             }
  357.             else {
  358.                 _proxyObject = new WebProxy();
  359.             }
  360.         }
  361.         // UpdateProxy
  362.         //
  363.         // end of Helper functions for processing settings and properties
  364.         //
  365.        
  366.         //
  367.         // Methods to access properties (internals are for use by the transport sink)
  368.         //
  369.        
  370.         internal IWebProxy ProxyObject {
  371.             get { return _proxyObject; }
  372.         }
  373.         internal bool UseDefaultCredentials {
  374.             get { return _secure || _bUseDefaultCredentials; }
  375.         }
  376.         internal bool UseAuthenticatedConnectionSharing {
  377.             get { return _bAuthenticatedConnectionSharing; }
  378.         }
  379.        
  380.         //
  381.         // end of Methods to access properties
  382.         //
  383.        
  384.     }
  385.     // class HttpClientChannel
  386.    
  387.    
  388.    
  389.     internal class HttpClientTransportSinkProvider : IClientChannelSinkProvider
  390.     {
  391.         int _timeout;
  392.        
  393.         internal HttpClientTransportSinkProvider(int timeout)
  394.         {
  395.             _timeout = timeout;
  396.         }
  397.        
  398.         public IClientChannelSink CreateSink(IChannelSender channel, string url, object remoteChannelData)
  399.         {
  400.             // url is set to the channel uri in CreateMessageSink
  401.             HttpClientTransportSink sink = new HttpClientTransportSink((HttpClientChannel)channel, url);
  402.             sink["timeout"] = _timeout;
  403.             return sink;
  404.         }
  405.        
  406.         public IClientChannelSinkProvider Next {
  407.             get { return null; }
  408.             set {
  409.                 throw new NotSupportedException();
  410.             }
  411.         }
  412.     }
  413.     // class HttpClientTransportSinkProvider
  414.    
  415.    
  416.    
  417.     // transport sender sink used by HttpClientChannel
  418.     internal class HttpClientTransportSink : BaseChannelSinkWithProperties, IClientChannelSink
  419.     {
  420.         private const string s_defaultVerb = "POST";
  421.        
  422.         private static string s_userAgent = "Mozilla/4.0+(compatible; MSIE 6.0; Windows " + "; MS .NET Remoting; MS .NET CLR " + System.Environment.Version.ToString() + " )";
  423.        
  424.         // Property keys (purposely all lower-case)
  425.         private const string UserNameKey = "username";
  426.         private const string PasswordKey = "password";
  427.         private const string DomainKey = "domain";
  428.         private const string PreAuthenticateKey = "preauthenticate";
  429.         private const string CredentialsKey = "credentials";
  430.         private const string ClientCertificatesKey = "clientcertificates";
  431.         private const string ProxyNameKey = "proxyname";
  432.         private const string ProxyPortKey = "proxyport";
  433.         private const string TimeoutKey = "timeout";
  434.         private const string AllowAutoRedirectKey = "allowautoredirect";
  435.         private const string UnsafeAuthenticatedConnectionSharingKey = "unsafeauthenticatedconnectionsharing";
  436.         private const string ConnectionGroupNameKey = "connectiongroupname";
  437.        
  438.         // If above keys get modified be sure to modify, the KeySet property on this
  439.         // class.
  440.         private static ICollection s_keySet = null;
  441.        
  442.         // Property values
  443.         private string _securityUserName = null;
  444.         private string _securityPassword = null;
  445.         private string _securityDomain = null;
  446.         private bool _bSecurityPreAuthenticate = false;
  447.         private bool _bUnsafeAuthenticatedConnectionSharing = false;
  448.         private string _connectionGroupName = null;
  449.         private ICredentials _credentials = null;
  450.         // this overrides all of the other security settings
  451.         private int _timeout = System.Threading.Timeout.Infinite;
  452.         // timeout value in milliseconds (only used if greater than 0)
  453.         private bool _bAllowAutoRedirect = false;
  454.        
  455.         // Proxy settings (_proxyObject gets recreated when _proxyName and _proxyPort are updated)
  456.         private IWebProxy _proxyObject = null;
  457.         // overrides channel proxy object if non-null
  458.         private string _proxyName = null;
  459.         private int _proxyPort = -1;
  460.         private static RequestCachePolicy s_requestCachePolicy = new RequestCachePolicy(RequestCacheLevel.BypassCache);
  461.        
  462.         // Other members
  463.         private HttpClientChannel _channel;
  464.         // channel that created this sink
  465.         private string _channelURI;
  466.         // complete url to remote object
  467.         // settings
  468.         private bool _useChunked = false;
  469.         private bool _useKeepAlive = true;
  470.        
  471.         internal HttpClientTransportSink(HttpClientChannel channel, string channelURI) : base()
  472.         {
  473.             _channel = channel;
  474.            
  475.             _channelURI = channelURI;
  476.            
  477.             // make sure channel uri doesn't end with a slash.
  478.             if (_channelURI.EndsWith("/", StringComparison.Ordinal))
  479.                 _channelURI = _channelURI.Substring(0, _channelURI.Length - 1);
  480.            
  481.         }
  482.         // HttpClientTransportSink
  483.        
  484.         public void ProcessMessage(IMessage msg, ITransportHeaders requestHeaders, Stream requestStream, out ITransportHeaders responseHeaders, out Stream responseStream)
  485.         {
  486.            
  487.             InternalRemotingServices.RemotingTrace("HttpTransportSenderSink::ProcessMessage");
  488.            
  489.             HttpWebRequest httpWebRequest = ProcessAndSend(msg, requestHeaders, requestStream);
  490.            
  491.             // receive server response
  492.             HttpWebResponse response = null;
  493.             try {
  494.                 response = (HttpWebResponse)httpWebRequest.GetResponse();
  495.             }
  496.             catch (WebException webException) {
  497.                 ProcessResponseException(webException, out response);
  498.             }
  499.            
  500.             ReceiveAndProcess(response, out responseHeaders, out responseStream);
  501.         }
  502.         // ProcessMessage
  503.         public void AsyncProcessRequest(IClientChannelSinkStack sinkStack, IMessage msg, ITransportHeaders headers, Stream stream)
  504.         {
  505.             // Send the webrequest, headers, request stream, and retry count.
  506.             AsyncHttpClientRequestState asyncRequestState = new AsyncHttpClientRequestState(this, sinkStack, msg, headers, stream, 1);
  507.            
  508.             asyncRequestState.StartRequest();
  509.         }
  510.         // AsyncProcessRequest
  511.        
  512.         private static void ProcessResponseException(WebException webException, out HttpWebResponse response)
  513.         {
  514.             // if a timeout occurred throw a RemotingTimeoutException
  515.             if (webException.Status == WebExceptionStatus.Timeout)
  516.                 throw new RemotingTimeoutException(CoreChannel.GetResourceString("Remoting_Channels_RequestTimedOut"), webException);
  517.            
  518.             response = webException.Response as HttpWebResponse;
  519.             if ((response == null))
  520.                 throw webException;
  521.            
  522.             // if server error (500-599 continue with processing the soap fault);
  523.             // otherwise, rethrow the exception.
  524.            
  525.             int statusCode = (int)(response.StatusCode);
  526.             if ((statusCode < 500) || (statusCode > 599)) {
  527.                 throw webException;
  528.             }
  529.         }
  530.         // ProcessResponseException
  531.        
  532.         public void AsyncProcessResponse(IClientResponseChannelSinkStack sinkStack, object state, ITransportHeaders headers, Stream stream)
  533.         {
  534.             // We don't have to implement this since we are always last in the chain.
  535.         }
  536.         // AsyncProcessRequest
  537.        
  538.        
  539.         public Stream GetRequestStream(IMessage msg, ITransportHeaders headers)
  540.         {
  541.             return null;
  542.         }
  543.         // GetRequestStream
  544.        
  545.         public IClientChannelSink NextChannelSink {
  546.             get { return null; }
  547.         }
  548.        
  549.        
  550.        
  551.         private HttpWebRequest SetupWebRequest(IMessage msg, ITransportHeaders headers)
  552.         {
  553.             IMethodCallMessage mcMsg = msg as IMethodCallMessage;
  554.            
  555.             string msgUri = (string)headers[CommonTransportKeys.RequestUri];
  556.             InternalRemotingServices.RemotingTrace("HttpClientChannel::SetupWebRequest Message uri is " + msgUri);
  557.            
  558.             if (msgUri == null) {
  559.                 if (mcMsg != null)
  560.                     msgUri = mcMsg.Uri;
  561.                 else
  562.                     msgUri = (string)msg.Properties["__Uri"];
  563.             }
  564.            
  565.             string fullPath;
  566.             if (HttpChannelHelper.StartsWithHttp(msgUri) != -1) {
  567.                 // this is the full path
  568.                 fullPath = msgUri;
  569.             }
  570.             else {
  571.                 // this is not the full path (_channelURI never has trailing slash)
  572.                 if (!msgUri.StartsWith("/", StringComparison.Ordinal))
  573.                     msgUri = "/" + msgUri;
  574.                
  575.                 fullPath = _channelURI + msgUri;
  576.             }
  577.             InternalRemotingServices.RemotingTrace("HttpClientChannel::SetupWebRequest FullPath " + fullPath);
  578.            
  579.             // based on headers, initialize the network stream
  580.            
  581.             string verb = (string)headers["__RequestVerb"];
  582.             if (verb == null)
  583.                 verb = s_defaultVerb;
  584.            
  585.             HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(fullPath);
  586.             httpWebRequest.AllowAutoRedirect = _bAllowAutoRedirect;
  587.             httpWebRequest.Method = verb;
  588.             httpWebRequest.SendChunked = _useChunked;
  589.             httpWebRequest.KeepAlive = _useKeepAlive;
  590.             httpWebRequest.Pipelined = false;
  591.             httpWebRequest.UserAgent = s_userAgent;
  592.             httpWebRequest.Timeout = _timeout;
  593.             httpWebRequest.CachePolicy = s_requestCachePolicy;
  594.            
  595.             // see if we should use a proxy object
  596.             IWebProxy proxy = _proxyObject;
  597.             if (proxy == null)
  598.                 // use channel proxy if one hasn't been explicity set for this sink
  599.                 proxy = _channel.ProxyObject;
  600.             if (proxy != null)
  601.                 httpWebRequest.Proxy = proxy;
  602.            
  603.             // see if security should be used
  604.             // order of applying credentials is:
  605.             // 1. check for explicitly set credentials
  606.             // 2. else check for explicitly set username, password, domain
  607.             // 3. else use default credentials if channel is configured to do so.
  608.             if (_credentials != null) {
  609.                 httpWebRequest.Credentials = _credentials;
  610.                 httpWebRequest.PreAuthenticate = _bSecurityPreAuthenticate;
  611.                 httpWebRequest.UnsafeAuthenticatedConnectionSharing = _bUnsafeAuthenticatedConnectionSharing;
  612.                 if (_connectionGroupName != null)
  613.                     httpWebRequest.ConnectionGroupName = _connectionGroupName;
  614.             }
  615.             else if (_securityUserName != null) {
  616.                 if (_securityDomain == null)
  617.                     httpWebRequest.Credentials = new NetworkCredential(_securityUserName, _securityPassword);
  618.                 else
  619.                     httpWebRequest.Credentials = new NetworkCredential(_securityUserName, _securityPassword, _securityDomain);
  620.                
  621.                 httpWebRequest.PreAuthenticate = _bSecurityPreAuthenticate;
  622.                 httpWebRequest.UnsafeAuthenticatedConnectionSharing = _bUnsafeAuthenticatedConnectionSharing;
  623.                 if (_connectionGroupName != null)
  624.                     httpWebRequest.ConnectionGroupName = _connectionGroupName;
  625.                
  626.             }
  627.             else if (_channel.UseDefaultCredentials) {
  628.                 if (_channel.UseAuthenticatedConnectionSharing) {
  629.                     httpWebRequest.UnsafeAuthenticatedConnectionSharing = true;
  630.                 }
  631.                
  632.                 httpWebRequest.PreAuthenticate = _bSecurityPreAuthenticate;
  633.             }
  634.            
  635.            
  636.             InternalRemotingServices.RemotingTrace("HttpClientTransportSink::SetupWebRequest - Get Http Request Headers");
  637.            
  638.             // add headers
  639.             foreach (DictionaryEntry header in headers) {
  640.                 string key = header.Key as string;
  641.                
  642.                 // if header name starts with "__", it is a special value that shouldn't be
  643.                 // actually sent out.
  644.                 if ((key != null) && !key.StartsWith("__", StringComparison.Ordinal)) {
  645.                     if (key.Equals("Content-Type"))
  646.                         httpWebRequest.ContentType = header.Value.ToString();
  647.                     else
  648.                         httpWebRequest.Headers[key] = header.Value.ToString();
  649.                 }
  650.             }
  651.            
  652.             return httpWebRequest;
  653.         }
  654.         // SetupWebRequest
  655.        
  656.         private HttpWebRequest ProcessAndSend(IMessage msg, ITransportHeaders headers, Stream inputStream)
  657.         {
  658.             // If the stream is seekable, we can retry once on a failure to write.
  659.             long initialPosition = 0;
  660.             bool bCanSeek = false;
  661.             if (inputStream != null) {
  662.                 bCanSeek = inputStream.CanSeek;
  663.                 if (bCanSeek)
  664.                     initialPosition = inputStream.Position;
  665.             }
  666.            
  667.             HttpWebRequest httpWebRequest = null;
  668.             Stream writeStream = null;
  669.             try {
  670.                 httpWebRequest = SetupWebRequest(msg, headers);
  671.                
  672.                 if (inputStream != null) {
  673.                     if (!_useChunked)
  674.                         httpWebRequest.ContentLength = (int)inputStream.Length;
  675.                    
  676.                     writeStream = httpWebRequest.GetRequestStream();
  677.                     StreamHelper.CopyStream(inputStream, writeStream);
  678.                 }
  679.             }
  680.             catch {
  681.                 // try to send one more time if possible
  682.                 if (bCanSeek) {
  683.                     httpWebRequest = SetupWebRequest(msg, headers);
  684.                    
  685.                     if (inputStream != null) {
  686.                         inputStream.Position = initialPosition;
  687.                        
  688.                         if (!_useChunked)
  689.                             httpWebRequest.ContentLength = (int)inputStream.Length;
  690.                        
  691.                         writeStream = httpWebRequest.GetRequestStream();
  692.                         StreamHelper.CopyStream(inputStream, writeStream);
  693.                     }
  694.                 }
  695.                 // end of "try to send one more time"
  696.             }
  697.            
  698.             if (inputStream != null)
  699.                 inputStream.Close();
  700.            
  701.             if (writeStream != null)
  702.                 writeStream.Close();
  703.            
  704.             return httpWebRequest;
  705.         }
  706.         // ProcessAndSend
  707.         private void ReceiveAndProcess(HttpWebResponse response, out ITransportHeaders returnHeaders, out Stream returnStream)
  708.         {
  709.             //
  710.             // Read Response Message
  711.            
  712.             // Just hand back the network stream
  713.             // (NOTE: The channel sinks are responsible for calling Close() on a stream
  714.             // once they are done with it).
  715.             int bufferSize;
  716.             if (response == null)
  717.                 bufferSize = 4096;
  718.             else {
  719.                 int contentLength = (int)response.ContentLength;
  720.                 if (contentLength == -1 || contentLength == 0)
  721.                     bufferSize = 4096;
  722.                 else if (contentLength <= 16000)
  723.                     bufferSize = contentLength;
  724.                 else
  725.                     bufferSize = 16000;
  726.             }
  727.             returnStream = new BufferedStream(response.GetResponseStream(), bufferSize);
  728.            
  729.             // collect headers
  730.             returnHeaders = CollectResponseHeaders(response);
  731.         }
  732.         // ReceiveAndProcess
  733.         private static ITransportHeaders CollectResponseHeaders(HttpWebResponse response)
  734.         {
  735.             TransportHeaders responseHeaders = new TransportHeaders();
  736.             foreach (object key in response.Headers) {
  737.                 string keyString = key.ToString();
  738.                 responseHeaders[keyString] = response.Headers[keyString];
  739.             }
  740.            
  741.             return responseHeaders;
  742.         }
  743.         // CollectResponseHeaders
  744.         //
  745.         // Support for properties (through BaseChannelSinkWithProperties)
  746.         //
  747.        
  748.         public override object this[object key]
  749.         {
  750.             get {
  751.                 string keyStr = key as string;
  752.                 if (keyStr == null)
  753.                     return null;
  754.                
  755.                 switch (keyStr.ToLower(CultureInfo.InvariantCulture)) {
  756.                     case UserNameKey:
  757.                         return _securityUserName;
  758.                     case PasswordKey:
  759.                         return null;
  760.                     case DomainKey:
  761.                         // Intentionally refuse to return password.
  762.                         return _securityDomain;
  763.                     case PreAuthenticateKey:
  764.                         return _bSecurityPreAuthenticate;
  765.                     case CredentialsKey:
  766.                         return _credentials;
  767.                     case ClientCertificatesKey:
  768.                         return null;
  769.                     case ProxyNameKey:
  770.                         // Intentionally refuse to return certificates
  771.                         return _proxyName;
  772.                     case ProxyPortKey:
  773.                         return _proxyPort;
  774.                     case TimeoutKey:
  775.                         return _timeout;
  776.                     case AllowAutoRedirectKey:
  777.                         return _bAllowAutoRedirect;
  778.                     case UnsafeAuthenticatedConnectionSharingKey:
  779.                         return _bUnsafeAuthenticatedConnectionSharing;
  780.                     case ConnectionGroupNameKey:
  781.                         return _connectionGroupName;
  782.                 }
  783.                 // switch (keyStr.ToLower(CultureInfo.InvariantCulture))
  784.                 return null;
  785.             }
  786.            
  787.             set {
  788.                 string keyStr = key as string;
  789.                 if (keyStr == null)
  790.                     return;
  791.                
  792.                 switch (keyStr.ToLower(CultureInfo.InvariantCulture)) {
  793.                     case UserNameKey:
  794.                         _securityUserName = (string)value;
  795.                         break;
  796.                     case PasswordKey:
  797.                         _securityPassword = (string)value;
  798.                         break;
  799.                     case DomainKey:
  800.                         _securityDomain = (string)value;
  801.                         break;
  802.                     case PreAuthenticateKey:
  803.                         _bSecurityPreAuthenticate = Convert.ToBoolean(value, CultureInfo.InvariantCulture);
  804.                         break;
  805.                     case CredentialsKey:
  806.                         _credentials = (ICredentials)value;
  807.                         break;
  808.                     case ProxyNameKey:
  809.                         _proxyName = (string)value;
  810.                         UpdateProxy();
  811.                         break;
  812.                     case ProxyPortKey:
  813.                         _proxyPort = Convert.ToInt32(value, CultureInfo.InvariantCulture);
  814.                         UpdateProxy();
  815.                         break;
  816.                     case TimeoutKey:
  817.                        
  818.                        
  819.                         {
  820.                             if (value is TimeSpan)
  821.                                 _timeout = (int)((TimeSpan)value).TotalMilliseconds;
  822.                             else
  823.                                 _timeout = Convert.ToInt32(value, CultureInfo.InvariantCulture);
  824.                             break;
  825.                         }
  826.                         break;
  827.                     case AllowAutoRedirectKey:
  828.                         // case TimeoutKey
  829.                         _bAllowAutoRedirect = Convert.ToBoolean(value, CultureInfo.InvariantCulture);
  830.                         break;
  831.                     case UnsafeAuthenticatedConnectionSharingKey:
  832.                         _bUnsafeAuthenticatedConnectionSharing = Convert.ToBoolean(value, CultureInfo.InvariantCulture);
  833.                         break;
  834.                     case ConnectionGroupNameKey:
  835.                         _connectionGroupName = (string)value;
  836.                         break;
  837.                    
  838.                 }
  839.                 // switch (keyStr.ToLower(CultureInfo.InvariantCulturey))
  840.             }
  841.         }
  842.         // this[]
  843.        
  844.         public override ICollection Keys {
  845.             get {
  846.                 if (s_keySet == null) {
  847.                     // Don't need to synchronize. Doesn't matter if the list gets
  848.                     // generated twice.
  849.                     ArrayList keys = new ArrayList(6);
  850.                     keys.Add(UserNameKey);
  851.                     keys.Add(PasswordKey);
  852.                     keys.Add(DomainKey);
  853.                     keys.Add(PreAuthenticateKey);
  854.                     keys.Add(CredentialsKey);
  855.                     keys.Add(ClientCertificatesKey);
  856.                     keys.Add(ProxyNameKey);
  857.                     keys.Add(ProxyPortKey);
  858.                     keys.Add(TimeoutKey);
  859.                     keys.Add(AllowAutoRedirectKey);
  860.                     keys.Add(UnsafeAuthenticatedConnectionSharingKey);
  861.                     keys.Add(ConnectionGroupNameKey);
  862.                    
  863.                     s_keySet = keys;
  864.                 }
  865.                
  866.                 return s_keySet;
  867.             }
  868.         }
  869.         // Keys
  870.        
  871.         //
  872.         // end of Support for properties
  873.         //
  874.        
  875.        
  876.         //
  877.         // Helper functions for processing settings and properties
  878.         //
  879.        
  880.         // Called to recreate proxy object whenever the proxy name or port is changed.
  881.         private void UpdateProxy()
  882.         {
  883.             if ((_proxyName != null) && (_proxyPort > 0)) {
  884.                 WebProxy proxy = new WebProxy(_proxyName, _proxyPort);
  885.                
  886.                 // disable proxy use when the host is local. i.e. without periods
  887.                 proxy.BypassProxyOnLocal = true;
  888.                
  889.                 _proxyObject = proxy;
  890.             }
  891.         }
  892.         // UpdateProxy
  893.         //
  894.         // end of Helper functions for processing settings and properties
  895.         //
  896.        
  897.        
  898.         static internal string UserAgent {
  899.             get { return s_userAgent; }
  900.         }
  901.        
  902.         // Used for maintaining async request state
  903.         private class AsyncHttpClientRequestState
  904.         {
  905.             private static AsyncCallback s_processGetRequestStreamCompletionCallback = new AsyncCallback(ProcessGetRequestStreamCompletion);
  906.             private static AsyncCallback s_processAsyncCopyRequestStreamCompletionCallback = new AsyncCallback(ProcessAsyncCopyRequestStreamCompletion);
  907.             private static AsyncCallback s_processGetResponseCompletionCallback = new AsyncCallback(ProcessGetResponseCompletion);
  908.             private static AsyncCallback s_processAsyncCopyRequestStreamCompletion = new AsyncCallback(ProcessAsyncCopyResponseStreamCompletion);
  909.            
  910.            
  911.             internal HttpWebRequest WebRequest;
  912.             internal HttpWebResponse WebResponse;
  913.             internal IClientChannelSinkStack SinkStack;
  914.             internal Stream RequestStream;
  915.             internal Stream ActualResponseStream;
  916.             // stream that will be passed to channel sinks
  917.             private HttpClientTransportSink _transportSink;
  918.             private int _retryCount;
  919.             private long _initialStreamPosition;
  920.             private IMessage _msg;
  921.             private ITransportHeaders _requestHeaders;
  922.            
  923.             internal AsyncHttpClientRequestState(HttpClientTransportSink transportSink, IClientChannelSinkStack sinkStack, IMessage msg, ITransportHeaders headers, Stream stream, int retryCount)
  924.             {
  925.                 _transportSink = transportSink;
  926.                 SinkStack = sinkStack;
  927.                 _msg = msg;
  928.                 _requestHeaders = headers;
  929.                 RequestStream = stream;
  930.                 _retryCount = retryCount;
  931.                
  932.                 if (RequestStream.CanSeek)
  933.                     _initialStreamPosition = RequestStream.Position;
  934.             }
  935.             // AsyncHttpClientRequestState
  936.             internal void StartRequest()
  937.             {
  938.                 WebRequest = _transportSink.SetupWebRequest(_msg, _requestHeaders);
  939.                 if (!_transportSink._useChunked) {
  940.                     try {
  941.                         WebRequest.ContentLength = (int)RequestStream.Length;
  942.                     }
  943.                     catch {
  944.                         // swallow exception if RequestStream.Length throws; just
  945.                         // means that WebRequest will have to buffer the stream.
  946.                     }
  947.                 }
  948.                
  949.                 // Chain of methods called is as follows:
  950.                 // 1. StartRequest (this one)
  951.                 // 2. ProcessGetRequestStreamCompletion
  952.                 // 3. ProcessAsyncCopyRequestStreamCompletion
  953.                 // 2. ProcessGetResponseCompletion
  954.                 // 3. ProcessAsyncCopyResponseStreamCompletion
  955.                
  956.                 WebRequest.BeginGetRequestStream(s_processGetRequestStreamCompletionCallback, this);
  957.             }
  958.             // StartRequest
  959.             // This should only be done when the send fails.
  960.             internal void RetryOrDispatchException(Exception e)
  961.             {
  962.                 bool bRetry = false;
  963.                 try {
  964.                     if (_retryCount > 0) {
  965.                         _retryCount--;
  966.                        
  967.                         if (RequestStream.CanSeek) {
  968.                             RequestStream.Position = _initialStreamPosition;
  969.                            
  970.                             StartRequest();
  971.                             bRetry = true;
  972.                         }
  973.                     }
  974.                 }
  975.                 catch {
  976.                 }
  977.                
  978.                 if (!bRetry) {
  979.                     RequestStream.Close();
  980.                     SinkStack.DispatchException(e);
  981.                 }
  982.             }
  983.             // DispatchExceptionOrRetry
  984.            
  985.             // called from StartRequest
  986.             private static void ProcessGetRequestStreamCompletion(IAsyncResult iar)
  987.             {
  988.                 // We've just received a request stream.
  989.                
  990.                 AsyncHttpClientRequestState asyncRequestState = (AsyncHttpClientRequestState)iar.AsyncState;
  991.                
  992.                 try {
  993.                     HttpWebRequest httpWebRequest = asyncRequestState.WebRequest;
  994.                     Stream sourceRequestStream = asyncRequestState.RequestStream;
  995.                    
  996.                     Stream webRequestStream = httpWebRequest.EndGetRequestStream(iar);
  997.                    
  998.                         // sync read, async write
  999.                         // leave source open, close target
  1000.                     StreamHelper.BeginAsyncCopyStream(sourceRequestStream, webRequestStream, false, true, false, true, s_processAsyncCopyRequestStreamCompletionCallback, asyncRequestState);
  1001.                 }
  1002.                 catch (Exception e) {
  1003.                     asyncRequestState.RetryOrDispatchException(e);
  1004.                 }
  1005.                 catch {
  1006.                     asyncRequestState.RetryOrDispatchException(new Exception(CoreChannel.GetResourceString("Remoting_nonClsCompliantException")));
  1007.                 }
  1008.             }
  1009.             // ProcessGetRequestStreamCompletion
  1010.            
  1011.             // called from ProcessGetRequestStreamCompletion
  1012.             private static void ProcessAsyncCopyRequestStreamCompletion(IAsyncResult iar)
  1013.             {
  1014.                 // We've just finished copying the original request stream into the network stream.
  1015.                
  1016.                 AsyncHttpClientRequestState asyncRequestState = (AsyncHttpClientRequestState)iar.AsyncState;
  1017.                
  1018.                 try {
  1019.                     StreamHelper.EndAsyncCopyStream(iar);
  1020.                    
  1021.                     asyncRequestState.WebRequest.BeginGetResponse(s_processGetResponseCompletionCallback, asyncRequestState);
  1022.                 }
  1023.                 catch (Exception e) {
  1024.                     // This is the last point where we should retry.
  1025.                     asyncRequestState.RetryOrDispatchException(e);
  1026.                 }
  1027.                 catch {
  1028.                     // This is the last point where we should retry.
  1029.                     asyncRequestState.RetryOrDispatchException(new Exception(CoreChannel.GetResourceString("Remoting_nonClsCompliantException")));
  1030.                 }
  1031.             }
  1032.             // ProcessAsyncCopyRequestStreamCompletion
  1033.            
  1034.             // called from ProcessAsyncCopyRequestStreamCompletion
  1035.             private static void ProcessGetResponseCompletion(IAsyncResult iar)
  1036.             {
  1037.                 // We've just received a response.
  1038.                
  1039.                 AsyncHttpClientRequestState asyncRequestState = (AsyncHttpClientRequestState)iar.AsyncState;
  1040.                
  1041.                 try {
  1042.                     // close the request stream since we are done with it.
  1043.                     asyncRequestState.RequestStream.Close();
  1044.                    
  1045.                     HttpWebResponse httpWebResponse = null;
  1046.                     HttpWebRequest httpWebRequest = asyncRequestState.WebRequest;
  1047.                     try {
  1048.                         httpWebResponse = (HttpWebResponse)httpWebRequest.EndGetResponse(iar);
  1049.                     }
  1050.                     catch (WebException webException) {
  1051.                         ProcessResponseException(webException, out httpWebResponse);
  1052.                     }
  1053.                    
  1054.                     asyncRequestState.WebResponse = httpWebResponse;
  1055.                    
  1056.                     // Asynchronously pump the web response stream into a memory stream.
  1057.                     ChunkedMemoryStream responseStream = new ChunkedMemoryStream(CoreChannel.BufferPool);
  1058.                     asyncRequestState.ActualResponseStream = responseStream;
  1059.                    
  1060.                         // async read, sync write
  1061.                         // close source, leave target open
  1062.                     StreamHelper.BeginAsyncCopyStream(httpWebResponse.GetResponseStream(), responseStream, true, false, true, false, s_processAsyncCopyRequestStreamCompletion, asyncRequestState);
  1063.                 }
  1064.                 catch (Exception e) {
  1065.                     asyncRequestState.SinkStack.DispatchException(e);
  1066.                 }
  1067.                 catch {
  1068.                     asyncRequestState.SinkStack.DispatchException(new Exception(CoreChannel.GetResourceString("Remoting_nonClsCompliantException")));
  1069.                 }
  1070.             }
  1071.             // ProcessGetResponseCompletion
  1072.            
  1073.             // called from ProcessGetResponseCompletion
  1074.             private static void ProcessAsyncCopyResponseStreamCompletion(IAsyncResult iar)
  1075.             {
  1076.                 // We've just finished copying the network response stream into a memory stream.
  1077.                
  1078.                 AsyncHttpClientRequestState asyncRequestState = (AsyncHttpClientRequestState)iar.AsyncState;
  1079.                
  1080.                 try {
  1081.                     StreamHelper.EndAsyncCopyStream(iar);
  1082.                    
  1083.                     HttpWebResponse webResponse = asyncRequestState.WebResponse;
  1084.                     Stream responseStream = asyncRequestState.ActualResponseStream;
  1085.                    
  1086.                     ITransportHeaders responseHeaders = CollectResponseHeaders(webResponse);
  1087.                    
  1088.                     // call down the sink chain
  1089.                     asyncRequestState.SinkStack.AsyncProcessResponse(responseHeaders, responseStream);
  1090.                 }
  1091.                 catch (Exception e) {
  1092.                     asyncRequestState.SinkStack.DispatchException(e);
  1093.                 }
  1094.                 catch {
  1095.                     asyncRequestState.SinkStack.DispatchException(new Exception(CoreChannel.GetResourceString("Remoting_nonClsCompliantException")));
  1096.                 }
  1097.             }
  1098.             // ProcessAsyncResponseStreamCompletion
  1099.            
  1100.         }
  1101.         // class AsyncHttpClientRequest
  1102.        
  1103.     }
  1104.     // class HttpClientTransportSink
  1105. }
  1106. // namespace System.Runtime.Remoting.Channels.Http

Developer Fusion