Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / clr / src / ManagedLibraries / Remoting / Channels / HTTP / HttpClientChannel.cs / 1305376 / HttpClientChannel.cs
// ==++== // // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== //========================================================================== // File: HttpClientChannel.cs // // Summary: Implements a client channel that transmits method calls over HTTP. // // Classes: public HttpClientChannel // internal HttpClientTransportSink // //========================================================================= using System; using System.Collections; using System.IO; using System.Net; using System.Net.Cache; using System.Text; using System.Runtime.InteropServices; using System.Security.Principal; using System.ComponentModel; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Messaging; #if !FEATURE_PAL using System.Security.Cryptography.X509Certificates; #endif using System.Threading; using System.Globalization; using System.Security.Permissions; namespace System.Runtime.Remoting.Channels.Http { public class HttpClientChannel : BaseChannelWithProperties, IChannelSender, ISecurableChannel { // Property Keys (purposely all lower-case) private const String ProxyNameKey = "proxyname"; private const String ProxyPortKey = "proxyport"; // If above keys get modified be sure to modify, the KeySet property on this // class. private static ICollection s_keySet = null; // Settings private int _channelPriority = 1; // channel priority private String _channelName = "http client"; // channel name // Proxy settings (_proxyObject gets recreated when _proxyName and _proxyPort are updated) private IWebProxy _proxyObject = null; // proxy object for request, can be overridden in transport sink private String _proxyName = null; private int _proxyPort = -1; private int _timeout = System.Threading.Timeout.Infinite; // default timeout is infinite private int _clientConnectionLimit = 0; // bump connection limit to at least this number (only meaningful if > 0) private bool _bUseDefaultCredentials = false; // should default credentials be used? private bool _bAuthenticatedConnectionSharing = true; private bool _secure = false; private IClientChannelSinkProvider _sinkProvider = null; // sink chain provider public HttpClientChannel() { SetupChannel(); } // HttpClientChannel() public HttpClientChannel(String name, IClientChannelSinkProvider sinkProvider) { _channelName = name; _sinkProvider = sinkProvider; SetupChannel(); } // HttpClientChannel(IClientChannelSinkProvider sinkProvider) // constructor used by config file public HttpClientChannel(IDictionary properties, IClientChannelSinkProvider sinkProvider) { if (properties != null) { foreach (DictionaryEntry entry in properties) { switch ((String)entry.Key) { case "name": _channelName = (String)entry.Value; break; case "priority": _channelPriority = Convert.ToInt32(entry.Value, CultureInfo.InvariantCulture); break; case "proxyName": this["proxyName"] = entry.Value; break; case "proxyPort": this["proxyPort"] = entry.Value; break; case "timeout": _timeout = Convert.ToInt32(entry.Value, CultureInfo.InvariantCulture); break; case "clientConnectionLimit": { _clientConnectionLimit = Convert.ToInt32(entry.Value, CultureInfo.InvariantCulture); break; } case "useDefaultCredentials": { _bUseDefaultCredentials = Convert.ToBoolean(entry.Value, CultureInfo.InvariantCulture); break; } case "useAuthenticatedConnectionSharing": { _bAuthenticatedConnectionSharing = Convert.ToBoolean(entry.Value, CultureInfo.InvariantCulture); break; } default: break; } } } _sinkProvider = sinkProvider; SetupChannel(); } // HttpClientChannel private void SetupChannel() { if (_sinkProvider != null) { CoreChannel.AppendProviderToClientProviderChain( _sinkProvider, new HttpClientTransportSinkProvider(_timeout)); } else _sinkProvider = CreateDefaultClientProviderChain(); } // SetupChannel() // // ISecurableChannel implementation // public bool IsSecured { [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.Infrastructure, Infrastructure=true)] get { return _secure; } [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.Infrastructure, Infrastructure=true)] set { _secure = value; } } // // IChannel implementation // public int ChannelPriority { [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.Infrastructure, Infrastructure=true)] get { return _channelPriority; } } public String ChannelName { [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.Infrastructure, Infrastructure=true)] get { return _channelName; } } // returns channelURI and places object uri into out parameter [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.Infrastructure, Infrastructure=true)] public String Parse(String url, out String objectURI) { return HttpChannelHelper.ParseURL(url, out objectURI); } // Parse // // end of IChannel implementation // // // IChannelSender implementation // [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.Infrastructure, Infrastructure=true)] public virtual IMessageSink CreateMessageSink(String url, Object remoteChannelData, out String objectURI) { // Set the out parameters objectURI = null; String channelURI = null; if (url != null) // Is this a well known object? { // Parse returns null if this is not one of our url's channelURI = Parse(url, out objectURI); } else // determine if we want to connect based on the channel data { if (remoteChannelData != null) { if (remoteChannelData is IChannelDataStore) { IChannelDataStore cds = (IChannelDataStore)remoteChannelData; // see if this is an http uri String simpleChannelUri = Parse(cds.ChannelUris[0], out objectURI); if (simpleChannelUri != null) channelURI = cds.ChannelUris[0]; } } } if (channelURI != null) { if (url == null) url = channelURI; if (_clientConnectionLimit > 0) { ServicePoint sp = ServicePointManager.FindServicePoint(new Uri(channelURI)); if (sp.ConnectionLimit < _clientConnectionLimit) sp.ConnectionLimit = _clientConnectionLimit; } // This will return null if one of the sink providers decides it doesn't // want to allow (or can't provide) a connection through this channel. IClientChannelSink sink = _sinkProvider.CreateSink(this, url, remoteChannelData); // return sink after making sure that it implements IMessageSink IMessageSink msgSink = sink as IMessageSink; if ((sink != null) && (msgSink == null)) { throw new RemotingException( CoreChannel.GetResourceString("Remoting_Channels_ChannelSinkNotMsgSink")); } return msgSink; } return null; } // CreateMessageSink // // end of IChannelSender implementation // private IClientChannelSinkProvider CreateDefaultClientProviderChain() { IClientChannelSinkProvider chain = new SoapClientFormatterSinkProvider(); IClientChannelSinkProvider sink = chain; sink.Next = new HttpClientTransportSinkProvider(_timeout); return chain; } // CreateDefaultClientProviderChain // // Support for properties (through BaseChannelSinkWithProperties) // public override Object this[Object key] { get { String keyStr = key as String; if (keyStr == null) return null; switch (keyStr.ToLower(CultureInfo.InvariantCulture)) { case ProxyNameKey: return _proxyName; case ProxyPortKey: return _proxyPort; } // switch (keyStr.ToLower(CultureInfo.InvariantCulture)) return null; } set { String keyStr = key as String; if (keyStr == null) return; switch (keyStr.ToLower(CultureInfo.InvariantCulture)) { case ProxyNameKey: _proxyName = (String)value; UpdateProxy(); break; case ProxyPortKey: _proxyPort = Convert.ToInt32(value, CultureInfo.InvariantCulture); UpdateProxy(); break; } // switch (keyStr.ToLower(CultureInfo.InvariantCulture)) } } // this[] public override ICollection Keys { get { if (s_keySet == null) { // Don't need to synchronize. Doesn't matter if the list gets // generated twice. ArrayList keys = new ArrayList(2); keys.Add(ProxyNameKey); keys.Add(ProxyPortKey); s_keySet = keys; } return s_keySet; } } // Keys // // end of Support for properties // // // Helper functions for processing settings and properties // // Called to recreate proxy object whenever the proxy name or port is changed. private void UpdateProxy() { if ((_proxyName != null) && (_proxyName.Length > 0) && (_proxyPort > 0)) { WebProxy proxy = new WebProxy(_proxyName, _proxyPort); // disable proxy use when the host is local. i.e. without periods proxy.BypassProxyOnLocal = true; // setup bypasslist to include local ip address String[] bypassList = new String[]{ CoreChannel.GetMachineIp() }; proxy.BypassList = bypassList; _proxyObject = proxy; } else { _proxyObject = new WebProxy(); } } // UpdateProxy // // end of Helper functions for processing settings and properties // // // Methods to access properties (internals are for use by the transport sink) // internal IWebProxy ProxyObject { get { return _proxyObject; } } internal bool UseDefaultCredentials { get { return _secure || _bUseDefaultCredentials; } } internal bool UseAuthenticatedConnectionSharing { get { return _bAuthenticatedConnectionSharing; } } // // end of Methods to access properties // } // class HttpClientChannel internal class HttpClientTransportSinkProvider : IClientChannelSinkProvider { int _timeout; internal HttpClientTransportSinkProvider(int timeout) { _timeout = timeout; } public IClientChannelSink CreateSink(IChannelSender channel, String url, Object remoteChannelData) { // url is set to the channel uri in CreateMessageSink HttpClientTransportSink sink = new HttpClientTransportSink((HttpClientChannel)channel, url); sink["timeout"] = _timeout; return sink; } public IClientChannelSinkProvider Next { get { return null; } set { throw new NotSupportedException(); } } } // class HttpClientTransportSinkProvider // transport sender sink used by HttpClientChannel internal class HttpClientTransportSink : BaseChannelSinkWithProperties, IClientChannelSink { private const String s_defaultVerb = "POST"; private static String s_userAgent = "Mozilla/4.0+(compatible; MSIE 6.0; Windows " + #if !FEATURE_PAL System.Environment.OSVersion.Version + #endif "; MS .NET Remoting; MS .NET CLR " + System.Environment.Version.ToString() + " )"; // Property keys (purposely all lower-case) private const String UserNameKey = "username"; private const String PasswordKey = "password"; private const String DomainKey = "domain"; private const String PreAuthenticateKey = "preauthenticate"; private const String CredentialsKey = "credentials"; private const String ClientCertificatesKey = "clientcertificates"; private const String ProxyNameKey = "proxyname"; private const String ProxyPortKey = "proxyport"; private const String TimeoutKey = "timeout"; private const String AllowAutoRedirectKey = "allowautoredirect"; private const String UnsafeAuthenticatedConnectionSharingKey = "unsafeauthenticatedconnectionsharing"; private const String ConnectionGroupNameKey = "connectiongroupname"; // If above keys get modified be sure to modify, the KeySet property on this // class. private static ICollection s_keySet = null; // Property values private String _securityUserName = null; private String _securityPassword = null; private String _securityDomain = null; private bool _bSecurityPreAuthenticate = false; private bool _bUnsafeAuthenticatedConnectionSharing = false; private String _connectionGroupName = null; private ICredentials _credentials = null; // this overrides all of the other security settings #if !FEATURE_PAL private X509CertificateCollection _certificates = null; #endif private int _timeout = System.Threading.Timeout.Infinite; // timeout value in milliseconds (only used if greater than 0) private bool _bAllowAutoRedirect = false; // Proxy settings (_proxyObject gets recreated when _proxyName and _proxyPort are updated) private IWebProxy _proxyObject = null; // overrides channel proxy object if non-null private String _proxyName = null; private int _proxyPort = -1; private static RequestCachePolicy s_requestCachePolicy = new RequestCachePolicy(RequestCacheLevel.BypassCache); // Other members private HttpClientChannel _channel; // channel that created this sink private String _channelURI; // complete url to remote object // settings private bool _useChunked = false; // private bool _useKeepAlive = true; internal HttpClientTransportSink(HttpClientChannel channel, String channelURI) : base() { _channel = channel; _channelURI = channelURI; // make sure channel uri doesn't end with a slash. if (_channelURI.EndsWith("/", StringComparison.Ordinal)) _channelURI = _channelURI.Substring(0, _channelURI.Length - 1); } // HttpClientTransportSink public void ProcessMessage(IMessage msg, ITransportHeaders requestHeaders, Stream requestStream, out ITransportHeaders responseHeaders, out Stream responseStream) { InternalRemotingServices.RemotingTrace("HttpTransportSenderSink::ProcessMessage"); HttpWebRequest httpWebRequest = ProcessAndSend(msg, requestHeaders, requestStream); // receive server response HttpWebResponse response = null; try { response = (HttpWebResponse)httpWebRequest.GetResponse(); } catch (WebException webException) { ProcessResponseException(webException, out response); } ReceiveAndProcess(response, out responseHeaders, out responseStream); } // ProcessMessage public void AsyncProcessRequest(IClientChannelSinkStack sinkStack, IMessage msg, ITransportHeaders headers, Stream stream) { // Send the webrequest, headers, request stream, and retry count. AsyncHttpClientRequestState asyncRequestState = new AsyncHttpClientRequestState(this, sinkStack, msg, headers, stream, 1); asyncRequestState.StartRequest(); } // AsyncProcessRequest private static void ProcessResponseException(WebException webException, out HttpWebResponse response) { // if a timeout occurred throw a RemotingTimeoutException if (webException.Status == WebExceptionStatus.Timeout) throw new RemotingTimeoutException( CoreChannel.GetResourceString( "Remoting_Channels_RequestTimedOut"), webException); response = webException.Response as HttpWebResponse; if ((response == null)) throw webException; // if server error (500-599 continue with processing the soap fault); // otherwise, rethrow the exception. int statusCode = (int)(response.StatusCode); if ((statusCode < 500) || (statusCode > 599)) { throw webException; } } // ProcessResponseException public void AsyncProcessResponse(IClientResponseChannelSinkStack sinkStack, Object state, ITransportHeaders headers, Stream stream) { // We don't have to implement this since we are always last in the chain. } // AsyncProcessRequest public Stream GetRequestStream(IMessage msg, ITransportHeaders headers) { // < return null; } // GetRequestStream public IClientChannelSink NextChannelSink { get { return null; } } private HttpWebRequest SetupWebRequest(IMessage msg, ITransportHeaders headers) { IMethodCallMessage mcMsg = msg as IMethodCallMessage; String msgUri = (String)headers[CommonTransportKeys.RequestUri]; InternalRemotingServices.RemotingTrace("HttpClientChannel::SetupWebRequest Message uri is " + msgUri); if (msgUri == null) { if (mcMsg != null) msgUri = mcMsg.Uri; else msgUri = (String)msg.Properties["__Uri"]; } String fullPath; if (HttpChannelHelper.StartsWithHttp(msgUri) != -1) { // this is the full path fullPath = msgUri; } else { // this is not the full path (_channelURI never has trailing slash) if (!msgUri.StartsWith("/", StringComparison.Ordinal)) msgUri = "/" + msgUri; fullPath = _channelURI + msgUri; } InternalRemotingServices.RemotingTrace("HttpClientChannel::SetupWebRequest FullPath " + fullPath); // based on headers, initialize the network stream String verb = (String)headers["__RequestVerb"]; if (verb == null) verb = s_defaultVerb; HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(fullPath); httpWebRequest.AllowAutoRedirect = _bAllowAutoRedirect; httpWebRequest.Method = verb; httpWebRequest.SendChunked = _useChunked; httpWebRequest.KeepAlive = _useKeepAlive; httpWebRequest.Pipelined = false; httpWebRequest.UserAgent = s_userAgent; httpWebRequest.Timeout = _timeout; httpWebRequest.CachePolicy = s_requestCachePolicy; // see if we should use a proxy object IWebProxy proxy = _proxyObject; if (proxy == null) // use channel proxy if one hasn't been explicity set for this sink proxy = _channel.ProxyObject; if (proxy != null) httpWebRequest.Proxy = proxy; // see if security should be used // order of applying credentials is: // 1. check for explicitly set credentials // 2. else check for explicitly set username, password, domain // 3. else use default credentials if channel is configured to do so. if (_credentials != null) { httpWebRequest.Credentials = _credentials; httpWebRequest.PreAuthenticate = _bSecurityPreAuthenticate; httpWebRequest.UnsafeAuthenticatedConnectionSharing = _bUnsafeAuthenticatedConnectionSharing; if (_connectionGroupName != null) httpWebRequest.ConnectionGroupName = _connectionGroupName; } else if (_securityUserName != null) { if (_securityDomain == null) httpWebRequest.Credentials = new NetworkCredential(_securityUserName, _securityPassword); else httpWebRequest.Credentials = new NetworkCredential(_securityUserName, _securityPassword, _securityDomain); httpWebRequest.PreAuthenticate = _bSecurityPreAuthenticate; httpWebRequest.UnsafeAuthenticatedConnectionSharing = _bUnsafeAuthenticatedConnectionSharing; if (_connectionGroupName != null) httpWebRequest.ConnectionGroupName = _connectionGroupName; } else if (_channel.UseDefaultCredentials) { if (_channel.UseAuthenticatedConnectionSharing) { #if !FEATURE_PAL httpWebRequest.ConnectionGroupName = CoreChannel.GetCurrentSidString(); #endif httpWebRequest.UnsafeAuthenticatedConnectionSharing = true; } #if !FEATURE_PAL httpWebRequest.Credentials = CredentialCache.DefaultCredentials; #endif // !FEATURE_PAL httpWebRequest.PreAuthenticate = _bSecurityPreAuthenticate; } #if !FEATURE_PAL if (_certificates != null) { // attach certificates to the outgoing web request foreach (X509Certificate certificate in _certificates) { httpWebRequest.ClientCertificates.Add(certificate); } httpWebRequest.PreAuthenticate = _bSecurityPreAuthenticate; } #endif InternalRemotingServices.RemotingTrace("HttpClientTransportSink::SetupWebRequest - Get Http Request Headers"); // add headers foreach (DictionaryEntry header in headers) { String key = header.Key as String; // if header name starts with "__", it is a special value that shouldn't be // actually sent out. if ((key != null) && !key.StartsWith("__", StringComparison.Ordinal)) { if (key.Equals("Content-Type")) httpWebRequest.ContentType = header.Value.ToString(); else httpWebRequest.Headers[key] = header.Value.ToString(); } } return httpWebRequest; } // SetupWebRequest private HttpWebRequest ProcessAndSend(IMessage msg, ITransportHeaders headers, Stream inputStream) { // If the stream is seekable, we can retry once on a failure to write. long initialPosition = 0; bool bCanSeek = false; if (inputStream != null) { bCanSeek = inputStream.CanSeek; if (bCanSeek) initialPosition = inputStream.Position; } HttpWebRequest httpWebRequest = null; Stream writeStream = null; try { httpWebRequest = SetupWebRequest(msg, headers); if (inputStream != null) { if (!_useChunked) httpWebRequest.ContentLength = (int)inputStream.Length; writeStream = httpWebRequest.GetRequestStream(); StreamHelper.CopyStream(inputStream, writeStream); } } catch { // try to send one more time if possible if (bCanSeek) { httpWebRequest = SetupWebRequest(msg, headers); if (inputStream != null) { inputStream.Position = initialPosition; if (!_useChunked) httpWebRequest.ContentLength = (int)inputStream.Length; writeStream = httpWebRequest.GetRequestStream(); StreamHelper.CopyStream(inputStream, writeStream); } } // end of "try to send one more time" } if (inputStream != null) inputStream.Close(); if (writeStream != null) writeStream.Close(); return httpWebRequest; } // ProcessAndSend private void ReceiveAndProcess(HttpWebResponse response, out ITransportHeaders returnHeaders, out Stream returnStream) { // // Read Response Message // Just hand back the network stream // (NOTE: The channel sinks are responsible for calling Close() on a stream // once they are done with it). int bufferSize; if (response == null) bufferSize = 4096; else { int contentLength = (int)response.ContentLength; if (contentLength == -1 || contentLength == 0) bufferSize = 4096; else if (contentLength <= 16000) bufferSize = contentLength; else bufferSize = 16000; } returnStream = new BufferedStream(response.GetResponseStream(), bufferSize); // collect headers returnHeaders = CollectResponseHeaders(response); } // ReceiveAndProcess private static ITransportHeaders CollectResponseHeaders(HttpWebResponse response) { TransportHeaders responseHeaders = new TransportHeaders(); foreach (Object key in response.Headers) { String keyString = key.ToString(); responseHeaders[keyString] = response.Headers[keyString]; } return responseHeaders; } // CollectResponseHeaders // // Support for properties (through BaseChannelSinkWithProperties) // public override Object this[Object key] { get { String keyStr = key as String; if (keyStr == null) return null; switch (keyStr.ToLower(CultureInfo.InvariantCulture)) { case UserNameKey: return _securityUserName; case PasswordKey: return null; // Intentionally refuse to return password. case DomainKey: return _securityDomain; case PreAuthenticateKey: return _bSecurityPreAuthenticate; case CredentialsKey: return _credentials; case ClientCertificatesKey: return null; // Intentionally refuse to return certificates case ProxyNameKey: return _proxyName; case ProxyPortKey: return _proxyPort; case TimeoutKey: return _timeout; case AllowAutoRedirectKey: return _bAllowAutoRedirect; case UnsafeAuthenticatedConnectionSharingKey: return _bUnsafeAuthenticatedConnectionSharing; case ConnectionGroupNameKey: return _connectionGroupName; } // switch (keyStr.ToLower(CultureInfo.InvariantCulture)) return null; } set { String keyStr = key as String; if (keyStr == null) return; switch (keyStr.ToLower(CultureInfo.InvariantCulture)) { case UserNameKey: _securityUserName = (String)value; break; case PasswordKey: _securityPassword = (String)value; break; case DomainKey: _securityDomain = (String)value; break; case PreAuthenticateKey: _bSecurityPreAuthenticate = Convert.ToBoolean(value, CultureInfo.InvariantCulture); break; case CredentialsKey: _credentials = (ICredentials)value; break; #if !FEATURE_PAL case ClientCertificatesKey: _certificates = (X509CertificateCollection)value; break; #endif case ProxyNameKey: _proxyName = (String)value; UpdateProxy(); break; case ProxyPortKey: _proxyPort = Convert.ToInt32(value, CultureInfo.InvariantCulture); UpdateProxy(); break; case TimeoutKey: { if (value is TimeSpan) _timeout = (int)((TimeSpan)value).TotalMilliseconds; else _timeout = Convert.ToInt32(value, CultureInfo.InvariantCulture); break; } // case TimeoutKey case AllowAutoRedirectKey: _bAllowAutoRedirect = Convert.ToBoolean(value, CultureInfo.InvariantCulture); break; case UnsafeAuthenticatedConnectionSharingKey: _bUnsafeAuthenticatedConnectionSharing = Convert.ToBoolean(value, CultureInfo.InvariantCulture); break; case ConnectionGroupNameKey: _connectionGroupName = (String)value; break; } // switch (keyStr.ToLower(CultureInfo.InvariantCulturey)) } } // this[] public override ICollection Keys { get { if (s_keySet == null) { // Don't need to synchronize. Doesn't matter if the list gets // generated twice. ArrayList keys = new ArrayList(6); keys.Add(UserNameKey); keys.Add(PasswordKey); keys.Add(DomainKey); keys.Add(PreAuthenticateKey); keys.Add(CredentialsKey); keys.Add(ClientCertificatesKey); keys.Add(ProxyNameKey); keys.Add(ProxyPortKey); keys.Add(TimeoutKey); keys.Add(AllowAutoRedirectKey); keys.Add(UnsafeAuthenticatedConnectionSharingKey); keys.Add(ConnectionGroupNameKey); s_keySet = keys; } return s_keySet; } } // Keys // // end of Support for properties // // // Helper functions for processing settings and properties // // Called to recreate proxy object whenever the proxy name or port is changed. private void UpdateProxy() { if ((_proxyName != null) && (_proxyPort > 0)) { WebProxy proxy = new WebProxy(_proxyName, _proxyPort); // disable proxy use when the host is local. i.e. without periods proxy.BypassProxyOnLocal = true; _proxyObject = proxy; } } // UpdateProxy // // end of Helper functions for processing settings and properties // internal static String UserAgent { get { return s_userAgent; } } // Used for maintaining async request state private class AsyncHttpClientRequestState { private static AsyncCallback s_processGetRequestStreamCompletionCallback = new AsyncCallback(ProcessGetRequestStreamCompletion); private static AsyncCallback s_processAsyncCopyRequestStreamCompletionCallback = new AsyncCallback(ProcessAsyncCopyRequestStreamCompletion); private static AsyncCallback s_processGetResponseCompletionCallback = new AsyncCallback(ProcessGetResponseCompletion); private static AsyncCallback s_processAsyncCopyRequestStreamCompletion = new AsyncCallback(ProcessAsyncCopyResponseStreamCompletion); internal HttpWebRequest WebRequest; internal HttpWebResponse WebResponse; internal IClientChannelSinkStack SinkStack; internal Stream RequestStream; internal Stream ActualResponseStream; // stream that will be passed to channel sinks private HttpClientTransportSink _transportSink; private int _retryCount; private long _initialStreamPosition; private IMessage _msg; private ITransportHeaders _requestHeaders; internal AsyncHttpClientRequestState( HttpClientTransportSink transportSink, IClientChannelSinkStack sinkStack, IMessage msg, ITransportHeaders headers, Stream stream, int retryCount) { _transportSink = transportSink; SinkStack = sinkStack; _msg = msg; _requestHeaders = headers; RequestStream = stream; _retryCount = retryCount; if (RequestStream.CanSeek) _initialStreamPosition = RequestStream.Position; } // AsyncHttpClientRequestState internal void StartRequest() { WebRequest = _transportSink.SetupWebRequest(_msg, _requestHeaders); if (!_transportSink._useChunked) { try { WebRequest.ContentLength = (int)RequestStream.Length; } catch { // ---- exception if RequestStream.Length throws; just // means that WebRequest will have to buffer the stream. } } // Chain of methods called is as follows: // 1. StartRequest (this one) // 2. ProcessGetRequestStreamCompletion // 3. ProcessAsyncCopyRequestStreamCompletion // 2. ProcessGetResponseCompletion // 3. ProcessAsyncCopyResponseStreamCompletion WebRequest.BeginGetRequestStream(s_processGetRequestStreamCompletionCallback, this); } // StartRequest // This should only be done when the send fails. internal void RetryOrDispatchException(Exception e) { bool bRetry = false; try { if (_retryCount > 0) { _retryCount--; if (RequestStream.CanSeek) { RequestStream.Position = _initialStreamPosition; StartRequest(); bRetry = true; } } } catch { } if (!bRetry) { RequestStream.Close(); SinkStack.DispatchException(e); } } // DispatchExceptionOrRetry // called from StartRequest private static void ProcessGetRequestStreamCompletion(IAsyncResult iar) { // We've just received a request stream. AsyncHttpClientRequestState asyncRequestState = (AsyncHttpClientRequestState)iar.AsyncState; try { HttpWebRequest httpWebRequest = asyncRequestState.WebRequest; Stream sourceRequestStream = asyncRequestState.RequestStream; Stream webRequestStream = httpWebRequest.EndGetRequestStream(iar); StreamHelper.BeginAsyncCopyStream( sourceRequestStream, webRequestStream, false, true, // [....] read, async write false, true, // leave source open, close target s_processAsyncCopyRequestStreamCompletionCallback, asyncRequestState); } catch (Exception e) { asyncRequestState.RetryOrDispatchException(e); } } // ProcessGetRequestStreamCompletion // called from ProcessGetRequestStreamCompletion private static void ProcessAsyncCopyRequestStreamCompletion(IAsyncResult iar) { // We've just finished copying the original request stream into the network stream. AsyncHttpClientRequestState asyncRequestState = (AsyncHttpClientRequestState)iar.AsyncState; try { StreamHelper.EndAsyncCopyStream(iar); asyncRequestState.WebRequest.BeginGetResponse( s_processGetResponseCompletionCallback, asyncRequestState); } catch (Exception e) { // This is the last point where we should retry. asyncRequestState.RetryOrDispatchException(e); } } // ProcessAsyncCopyRequestStreamCompletion // called from ProcessAsyncCopyRequestStreamCompletion private static void ProcessGetResponseCompletion(IAsyncResult iar) { // We've just received a response. AsyncHttpClientRequestState asyncRequestState = (AsyncHttpClientRequestState)iar.AsyncState; try { // close the request stream since we are done with it. asyncRequestState.RequestStream.Close(); HttpWebResponse httpWebResponse = null; HttpWebRequest httpWebRequest = asyncRequestState.WebRequest; try { httpWebResponse = (HttpWebResponse)httpWebRequest.EndGetResponse(iar); } catch (WebException webException) { ProcessResponseException(webException, out httpWebResponse); } asyncRequestState.WebResponse = httpWebResponse; // Asynchronously pump the web response stream into a memory stream. ChunkedMemoryStream responseStream = new ChunkedMemoryStream(CoreChannel.BufferPool); asyncRequestState.ActualResponseStream = responseStream; StreamHelper.BeginAsyncCopyStream( httpWebResponse.GetResponseStream(), responseStream, true, false, // async read, [....] write true, false, // close source, leave target open s_processAsyncCopyRequestStreamCompletion, asyncRequestState); } catch (Exception e) { asyncRequestState.SinkStack.DispatchException(e); } } // ProcessGetResponseCompletion // called from ProcessGetResponseCompletion private static void ProcessAsyncCopyResponseStreamCompletion(IAsyncResult iar) { // We've just finished copying the network response stream into a memory stream. AsyncHttpClientRequestState asyncRequestState = (AsyncHttpClientRequestState)iar.AsyncState; try { StreamHelper.EndAsyncCopyStream(iar); HttpWebResponse webResponse = asyncRequestState.WebResponse; Stream responseStream = asyncRequestState.ActualResponseStream; ITransportHeaders responseHeaders = CollectResponseHeaders(webResponse); // call down the sink chain asyncRequestState.SinkStack.AsyncProcessResponse(responseHeaders, responseStream); } catch (Exception e) { asyncRequestState.SinkStack.DispatchException(e); } } // ProcessAsyncResponseStreamCompletion } // class AsyncHttpClientRequest } // class HttpClientTransportSink } // namespace System.Runtime.Remoting.Channels.Http // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // ==++== // // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== //========================================================================== // File: HttpClientChannel.cs // // Summary: Implements a client channel that transmits method calls over HTTP. // // Classes: public HttpClientChannel // internal HttpClientTransportSink // //========================================================================= using System; using System.Collections; using System.IO; using System.Net; using System.Net.Cache; using System.Text; using System.Runtime.InteropServices; using System.Security.Principal; using System.ComponentModel; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Messaging; #if !FEATURE_PAL using System.Security.Cryptography.X509Certificates; #endif using System.Threading; using System.Globalization; using System.Security.Permissions; namespace System.Runtime.Remoting.Channels.Http { public class HttpClientChannel : BaseChannelWithProperties, IChannelSender, ISecurableChannel { // Property Keys (purposely all lower-case) private const String ProxyNameKey = "proxyname"; private const String ProxyPortKey = "proxyport"; // If above keys get modified be sure to modify, the KeySet property on this // class. private static ICollection s_keySet = null; // Settings private int _channelPriority = 1; // channel priority private String _channelName = "http client"; // channel name // Proxy settings (_proxyObject gets recreated when _proxyName and _proxyPort are updated) private IWebProxy _proxyObject = null; // proxy object for request, can be overridden in transport sink private String _proxyName = null; private int _proxyPort = -1; private int _timeout = System.Threading.Timeout.Infinite; // default timeout is infinite private int _clientConnectionLimit = 0; // bump connection limit to at least this number (only meaningful if > 0) private bool _bUseDefaultCredentials = false; // should default credentials be used? private bool _bAuthenticatedConnectionSharing = true; private bool _secure = false; private IClientChannelSinkProvider _sinkProvider = null; // sink chain provider public HttpClientChannel() { SetupChannel(); } // HttpClientChannel() public HttpClientChannel(String name, IClientChannelSinkProvider sinkProvider) { _channelName = name; _sinkProvider = sinkProvider; SetupChannel(); } // HttpClientChannel(IClientChannelSinkProvider sinkProvider) // constructor used by config file public HttpClientChannel(IDictionary properties, IClientChannelSinkProvider sinkProvider) { if (properties != null) { foreach (DictionaryEntry entry in properties) { switch ((String)entry.Key) { case "name": _channelName = (String)entry.Value; break; case "priority": _channelPriority = Convert.ToInt32(entry.Value, CultureInfo.InvariantCulture); break; case "proxyName": this["proxyName"] = entry.Value; break; case "proxyPort": this["proxyPort"] = entry.Value; break; case "timeout": _timeout = Convert.ToInt32(entry.Value, CultureInfo.InvariantCulture); break; case "clientConnectionLimit": { _clientConnectionLimit = Convert.ToInt32(entry.Value, CultureInfo.InvariantCulture); break; } case "useDefaultCredentials": { _bUseDefaultCredentials = Convert.ToBoolean(entry.Value, CultureInfo.InvariantCulture); break; } case "useAuthenticatedConnectionSharing": { _bAuthenticatedConnectionSharing = Convert.ToBoolean(entry.Value, CultureInfo.InvariantCulture); break; } default: break; } } } _sinkProvider = sinkProvider; SetupChannel(); } // HttpClientChannel private void SetupChannel() { if (_sinkProvider != null) { CoreChannel.AppendProviderToClientProviderChain( _sinkProvider, new HttpClientTransportSinkProvider(_timeout)); } else _sinkProvider = CreateDefaultClientProviderChain(); } // SetupChannel() // // ISecurableChannel implementation // public bool IsSecured { [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.Infrastructure, Infrastructure=true)] get { return _secure; } [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.Infrastructure, Infrastructure=true)] set { _secure = value; } } // // IChannel implementation // public int ChannelPriority { [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.Infrastructure, Infrastructure=true)] get { return _channelPriority; } } public String ChannelName { [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.Infrastructure, Infrastructure=true)] get { return _channelName; } } // returns channelURI and places object uri into out parameter [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.Infrastructure, Infrastructure=true)] public String Parse(String url, out String objectURI) { return HttpChannelHelper.ParseURL(url, out objectURI); } // Parse // // end of IChannel implementation // // // IChannelSender implementation // [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.Infrastructure, Infrastructure=true)] public virtual IMessageSink CreateMessageSink(String url, Object remoteChannelData, out String objectURI) { // Set the out parameters objectURI = null; String channelURI = null; if (url != null) // Is this a well known object? { // Parse returns null if this is not one of our url's channelURI = Parse(url, out objectURI); } else // determine if we want to connect based on the channel data { if (remoteChannelData != null) { if (remoteChannelData is IChannelDataStore) { IChannelDataStore cds = (IChannelDataStore)remoteChannelData; // see if this is an http uri String simpleChannelUri = Parse(cds.ChannelUris[0], out objectURI); if (simpleChannelUri != null) channelURI = cds.ChannelUris[0]; } } } if (channelURI != null) { if (url == null) url = channelURI; if (_clientConnectionLimit > 0) { ServicePoint sp = ServicePointManager.FindServicePoint(new Uri(channelURI)); if (sp.ConnectionLimit < _clientConnectionLimit) sp.ConnectionLimit = _clientConnectionLimit; } // This will return null if one of the sink providers decides it doesn't // want to allow (or can't provide) a connection through this channel. IClientChannelSink sink = _sinkProvider.CreateSink(this, url, remoteChannelData); // return sink after making sure that it implements IMessageSink IMessageSink msgSink = sink as IMessageSink; if ((sink != null) && (msgSink == null)) { throw new RemotingException( CoreChannel.GetResourceString("Remoting_Channels_ChannelSinkNotMsgSink")); } return msgSink; } return null; } // CreateMessageSink // // end of IChannelSender implementation // private IClientChannelSinkProvider CreateDefaultClientProviderChain() { IClientChannelSinkProvider chain = new SoapClientFormatterSinkProvider(); IClientChannelSinkProvider sink = chain; sink.Next = new HttpClientTransportSinkProvider(_timeout); return chain; } // CreateDefaultClientProviderChain // // Support for properties (through BaseChannelSinkWithProperties) // public override Object this[Object key] { get { String keyStr = key as String; if (keyStr == null) return null; switch (keyStr.ToLower(CultureInfo.InvariantCulture)) { case ProxyNameKey: return _proxyName; case ProxyPortKey: return _proxyPort; } // switch (keyStr.ToLower(CultureInfo.InvariantCulture)) return null; } set { String keyStr = key as String; if (keyStr == null) return; switch (keyStr.ToLower(CultureInfo.InvariantCulture)) { case ProxyNameKey: _proxyName = (String)value; UpdateProxy(); break; case ProxyPortKey: _proxyPort = Convert.ToInt32(value, CultureInfo.InvariantCulture); UpdateProxy(); break; } // switch (keyStr.ToLower(CultureInfo.InvariantCulture)) } } // this[] public override ICollection Keys { get { if (s_keySet == null) { // Don't need to synchronize. Doesn't matter if the list gets // generated twice. ArrayList keys = new ArrayList(2); keys.Add(ProxyNameKey); keys.Add(ProxyPortKey); s_keySet = keys; } return s_keySet; } } // Keys // // end of Support for properties // // // Helper functions for processing settings and properties // // Called to recreate proxy object whenever the proxy name or port is changed. private void UpdateProxy() { if ((_proxyName != null) && (_proxyName.Length > 0) && (_proxyPort > 0)) { WebProxy proxy = new WebProxy(_proxyName, _proxyPort); // disable proxy use when the host is local. i.e. without periods proxy.BypassProxyOnLocal = true; // setup bypasslist to include local ip address String[] bypassList = new String[]{ CoreChannel.GetMachineIp() }; proxy.BypassList = bypassList; _proxyObject = proxy; } else { _proxyObject = new WebProxy(); } } // UpdateProxy // // end of Helper functions for processing settings and properties // // // Methods to access properties (internals are for use by the transport sink) // internal IWebProxy ProxyObject { get { return _proxyObject; } } internal bool UseDefaultCredentials { get { return _secure || _bUseDefaultCredentials; } } internal bool UseAuthenticatedConnectionSharing { get { return _bAuthenticatedConnectionSharing; } } // // end of Methods to access properties // } // class HttpClientChannel internal class HttpClientTransportSinkProvider : IClientChannelSinkProvider { int _timeout; internal HttpClientTransportSinkProvider(int timeout) { _timeout = timeout; } public IClientChannelSink CreateSink(IChannelSender channel, String url, Object remoteChannelData) { // url is set to the channel uri in CreateMessageSink HttpClientTransportSink sink = new HttpClientTransportSink((HttpClientChannel)channel, url); sink["timeout"] = _timeout; return sink; } public IClientChannelSinkProvider Next { get { return null; } set { throw new NotSupportedException(); } } } // class HttpClientTransportSinkProvider // transport sender sink used by HttpClientChannel internal class HttpClientTransportSink : BaseChannelSinkWithProperties, IClientChannelSink { private const String s_defaultVerb = "POST"; private static String s_userAgent = "Mozilla/4.0+(compatible; MSIE 6.0; Windows " + #if !FEATURE_PAL System.Environment.OSVersion.Version + #endif "; MS .NET Remoting; MS .NET CLR " + System.Environment.Version.ToString() + " )"; // Property keys (purposely all lower-case) private const String UserNameKey = "username"; private const String PasswordKey = "password"; private const String DomainKey = "domain"; private const String PreAuthenticateKey = "preauthenticate"; private const String CredentialsKey = "credentials"; private const String ClientCertificatesKey = "clientcertificates"; private const String ProxyNameKey = "proxyname"; private const String ProxyPortKey = "proxyport"; private const String TimeoutKey = "timeout"; private const String AllowAutoRedirectKey = "allowautoredirect"; private const String UnsafeAuthenticatedConnectionSharingKey = "unsafeauthenticatedconnectionsharing"; private const String ConnectionGroupNameKey = "connectiongroupname"; // If above keys get modified be sure to modify, the KeySet property on this // class. private static ICollection s_keySet = null; // Property values private String _securityUserName = null; private String _securityPassword = null; private String _securityDomain = null; private bool _bSecurityPreAuthenticate = false; private bool _bUnsafeAuthenticatedConnectionSharing = false; private String _connectionGroupName = null; private ICredentials _credentials = null; // this overrides all of the other security settings #if !FEATURE_PAL private X509CertificateCollection _certificates = null; #endif private int _timeout = System.Threading.Timeout.Infinite; // timeout value in milliseconds (only used if greater than 0) private bool _bAllowAutoRedirect = false; // Proxy settings (_proxyObject gets recreated when _proxyName and _proxyPort are updated) private IWebProxy _proxyObject = null; // overrides channel proxy object if non-null private String _proxyName = null; private int _proxyPort = -1; private static RequestCachePolicy s_requestCachePolicy = new RequestCachePolicy(RequestCacheLevel.BypassCache); // Other members private HttpClientChannel _channel; // channel that created this sink private String _channelURI; // complete url to remote object // settings private bool _useChunked = false; // private bool _useKeepAlive = true; internal HttpClientTransportSink(HttpClientChannel channel, String channelURI) : base() { _channel = channel; _channelURI = channelURI; // make sure channel uri doesn't end with a slash. if (_channelURI.EndsWith("/", StringComparison.Ordinal)) _channelURI = _channelURI.Substring(0, _channelURI.Length - 1); } // HttpClientTransportSink public void ProcessMessage(IMessage msg, ITransportHeaders requestHeaders, Stream requestStream, out ITransportHeaders responseHeaders, out Stream responseStream) { InternalRemotingServices.RemotingTrace("HttpTransportSenderSink::ProcessMessage"); HttpWebRequest httpWebRequest = ProcessAndSend(msg, requestHeaders, requestStream); // receive server response HttpWebResponse response = null; try { response = (HttpWebResponse)httpWebRequest.GetResponse(); } catch (WebException webException) { ProcessResponseException(webException, out response); } ReceiveAndProcess(response, out responseHeaders, out responseStream); } // ProcessMessage public void AsyncProcessRequest(IClientChannelSinkStack sinkStack, IMessage msg, ITransportHeaders headers, Stream stream) { // Send the webrequest, headers, request stream, and retry count. AsyncHttpClientRequestState asyncRequestState = new AsyncHttpClientRequestState(this, sinkStack, msg, headers, stream, 1); asyncRequestState.StartRequest(); } // AsyncProcessRequest private static void ProcessResponseException(WebException webException, out HttpWebResponse response) { // if a timeout occurred throw a RemotingTimeoutException if (webException.Status == WebExceptionStatus.Timeout) throw new RemotingTimeoutException( CoreChannel.GetResourceString( "Remoting_Channels_RequestTimedOut"), webException); response = webException.Response as HttpWebResponse; if ((response == null)) throw webException; // if server error (500-599 continue with processing the soap fault); // otherwise, rethrow the exception. int statusCode = (int)(response.StatusCode); if ((statusCode < 500) || (statusCode > 599)) { throw webException; } } // ProcessResponseException public void AsyncProcessResponse(IClientResponseChannelSinkStack sinkStack, Object state, ITransportHeaders headers, Stream stream) { // We don't have to implement this since we are always last in the chain. } // AsyncProcessRequest public Stream GetRequestStream(IMessage msg, ITransportHeaders headers) { // < return null; } // GetRequestStream public IClientChannelSink NextChannelSink { get { return null; } } private HttpWebRequest SetupWebRequest(IMessage msg, ITransportHeaders headers) { IMethodCallMessage mcMsg = msg as IMethodCallMessage; String msgUri = (String)headers[CommonTransportKeys.RequestUri]; InternalRemotingServices.RemotingTrace("HttpClientChannel::SetupWebRequest Message uri is " + msgUri); if (msgUri == null) { if (mcMsg != null) msgUri = mcMsg.Uri; else msgUri = (String)msg.Properties["__Uri"]; } String fullPath; if (HttpChannelHelper.StartsWithHttp(msgUri) != -1) { // this is the full path fullPath = msgUri; } else { // this is not the full path (_channelURI never has trailing slash) if (!msgUri.StartsWith("/", StringComparison.Ordinal)) msgUri = "/" + msgUri; fullPath = _channelURI + msgUri; } InternalRemotingServices.RemotingTrace("HttpClientChannel::SetupWebRequest FullPath " + fullPath); // based on headers, initialize the network stream String verb = (String)headers["__RequestVerb"]; if (verb == null) verb = s_defaultVerb; HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(fullPath); httpWebRequest.AllowAutoRedirect = _bAllowAutoRedirect; httpWebRequest.Method = verb; httpWebRequest.SendChunked = _useChunked; httpWebRequest.KeepAlive = _useKeepAlive; httpWebRequest.Pipelined = false; httpWebRequest.UserAgent = s_userAgent; httpWebRequest.Timeout = _timeout; httpWebRequest.CachePolicy = s_requestCachePolicy; // see if we should use a proxy object IWebProxy proxy = _proxyObject; if (proxy == null) // use channel proxy if one hasn't been explicity set for this sink proxy = _channel.ProxyObject; if (proxy != null) httpWebRequest.Proxy = proxy; // see if security should be used // order of applying credentials is: // 1. check for explicitly set credentials // 2. else check for explicitly set username, password, domain // 3. else use default credentials if channel is configured to do so. if (_credentials != null) { httpWebRequest.Credentials = _credentials; httpWebRequest.PreAuthenticate = _bSecurityPreAuthenticate; httpWebRequest.UnsafeAuthenticatedConnectionSharing = _bUnsafeAuthenticatedConnectionSharing; if (_connectionGroupName != null) httpWebRequest.ConnectionGroupName = _connectionGroupName; } else if (_securityUserName != null) { if (_securityDomain == null) httpWebRequest.Credentials = new NetworkCredential(_securityUserName, _securityPassword); else httpWebRequest.Credentials = new NetworkCredential(_securityUserName, _securityPassword, _securityDomain); httpWebRequest.PreAuthenticate = _bSecurityPreAuthenticate; httpWebRequest.UnsafeAuthenticatedConnectionSharing = _bUnsafeAuthenticatedConnectionSharing; if (_connectionGroupName != null) httpWebRequest.ConnectionGroupName = _connectionGroupName; } else if (_channel.UseDefaultCredentials) { if (_channel.UseAuthenticatedConnectionSharing) { #if !FEATURE_PAL httpWebRequest.ConnectionGroupName = CoreChannel.GetCurrentSidString(); #endif httpWebRequest.UnsafeAuthenticatedConnectionSharing = true; } #if !FEATURE_PAL httpWebRequest.Credentials = CredentialCache.DefaultCredentials; #endif // !FEATURE_PAL httpWebRequest.PreAuthenticate = _bSecurityPreAuthenticate; } #if !FEATURE_PAL if (_certificates != null) { // attach certificates to the outgoing web request foreach (X509Certificate certificate in _certificates) { httpWebRequest.ClientCertificates.Add(certificate); } httpWebRequest.PreAuthenticate = _bSecurityPreAuthenticate; } #endif InternalRemotingServices.RemotingTrace("HttpClientTransportSink::SetupWebRequest - Get Http Request Headers"); // add headers foreach (DictionaryEntry header in headers) { String key = header.Key as String; // if header name starts with "__", it is a special value that shouldn't be // actually sent out. if ((key != null) && !key.StartsWith("__", StringComparison.Ordinal)) { if (key.Equals("Content-Type")) httpWebRequest.ContentType = header.Value.ToString(); else httpWebRequest.Headers[key] = header.Value.ToString(); } } return httpWebRequest; } // SetupWebRequest private HttpWebRequest ProcessAndSend(IMessage msg, ITransportHeaders headers, Stream inputStream) { // If the stream is seekable, we can retry once on a failure to write. long initialPosition = 0; bool bCanSeek = false; if (inputStream != null) { bCanSeek = inputStream.CanSeek; if (bCanSeek) initialPosition = inputStream.Position; } HttpWebRequest httpWebRequest = null; Stream writeStream = null; try { httpWebRequest = SetupWebRequest(msg, headers); if (inputStream != null) { if (!_useChunked) httpWebRequest.ContentLength = (int)inputStream.Length; writeStream = httpWebRequest.GetRequestStream(); StreamHelper.CopyStream(inputStream, writeStream); } } catch { // try to send one more time if possible if (bCanSeek) { httpWebRequest = SetupWebRequest(msg, headers); if (inputStream != null) { inputStream.Position = initialPosition; if (!_useChunked) httpWebRequest.ContentLength = (int)inputStream.Length; writeStream = httpWebRequest.GetRequestStream(); StreamHelper.CopyStream(inputStream, writeStream); } } // end of "try to send one more time" } if (inputStream != null) inputStream.Close(); if (writeStream != null) writeStream.Close(); return httpWebRequest; } // ProcessAndSend private void ReceiveAndProcess(HttpWebResponse response, out ITransportHeaders returnHeaders, out Stream returnStream) { // // Read Response Message // Just hand back the network stream // (NOTE: The channel sinks are responsible for calling Close() on a stream // once they are done with it). int bufferSize; if (response == null) bufferSize = 4096; else { int contentLength = (int)response.ContentLength; if (contentLength == -1 || contentLength == 0) bufferSize = 4096; else if (contentLength <= 16000) bufferSize = contentLength; else bufferSize = 16000; } returnStream = new BufferedStream(response.GetResponseStream(), bufferSize); // collect headers returnHeaders = CollectResponseHeaders(response); } // ReceiveAndProcess private static ITransportHeaders CollectResponseHeaders(HttpWebResponse response) { TransportHeaders responseHeaders = new TransportHeaders(); foreach (Object key in response.Headers) { String keyString = key.ToString(); responseHeaders[keyString] = response.Headers[keyString]; } return responseHeaders; } // CollectResponseHeaders // // Support for properties (through BaseChannelSinkWithProperties) // public override Object this[Object key] { get { String keyStr = key as String; if (keyStr == null) return null; switch (keyStr.ToLower(CultureInfo.InvariantCulture)) { case UserNameKey: return _securityUserName; case PasswordKey: return null; // Intentionally refuse to return password. case DomainKey: return _securityDomain; case PreAuthenticateKey: return _bSecurityPreAuthenticate; case CredentialsKey: return _credentials; case ClientCertificatesKey: return null; // Intentionally refuse to return certificates case ProxyNameKey: return _proxyName; case ProxyPortKey: return _proxyPort; case TimeoutKey: return _timeout; case AllowAutoRedirectKey: return _bAllowAutoRedirect; case UnsafeAuthenticatedConnectionSharingKey: return _bUnsafeAuthenticatedConnectionSharing; case ConnectionGroupNameKey: return _connectionGroupName; } // switch (keyStr.ToLower(CultureInfo.InvariantCulture)) return null; } set { String keyStr = key as String; if (keyStr == null) return; switch (keyStr.ToLower(CultureInfo.InvariantCulture)) { case UserNameKey: _securityUserName = (String)value; break; case PasswordKey: _securityPassword = (String)value; break; case DomainKey: _securityDomain = (String)value; break; case PreAuthenticateKey: _bSecurityPreAuthenticate = Convert.ToBoolean(value, CultureInfo.InvariantCulture); break; case CredentialsKey: _credentials = (ICredentials)value; break; #if !FEATURE_PAL case ClientCertificatesKey: _certificates = (X509CertificateCollection)value; break; #endif case ProxyNameKey: _proxyName = (String)value; UpdateProxy(); break; case ProxyPortKey: _proxyPort = Convert.ToInt32(value, CultureInfo.InvariantCulture); UpdateProxy(); break; case TimeoutKey: { if (value is TimeSpan) _timeout = (int)((TimeSpan)value).TotalMilliseconds; else _timeout = Convert.ToInt32(value, CultureInfo.InvariantCulture); break; } // case TimeoutKey case AllowAutoRedirectKey: _bAllowAutoRedirect = Convert.ToBoolean(value, CultureInfo.InvariantCulture); break; case UnsafeAuthenticatedConnectionSharingKey: _bUnsafeAuthenticatedConnectionSharing = Convert.ToBoolean(value, CultureInfo.InvariantCulture); break; case ConnectionGroupNameKey: _connectionGroupName = (String)value; break; } // switch (keyStr.ToLower(CultureInfo.InvariantCulturey)) } } // this[] public override ICollection Keys { get { if (s_keySet == null) { // Don't need to synchronize. Doesn't matter if the list gets // generated twice. ArrayList keys = new ArrayList(6); keys.Add(UserNameKey); keys.Add(PasswordKey); keys.Add(DomainKey); keys.Add(PreAuthenticateKey); keys.Add(CredentialsKey); keys.Add(ClientCertificatesKey); keys.Add(ProxyNameKey); keys.Add(ProxyPortKey); keys.Add(TimeoutKey); keys.Add(AllowAutoRedirectKey); keys.Add(UnsafeAuthenticatedConnectionSharingKey); keys.Add(ConnectionGroupNameKey); s_keySet = keys; } return s_keySet; } } // Keys // // end of Support for properties // // // Helper functions for processing settings and properties // // Called to recreate proxy object whenever the proxy name or port is changed. private void UpdateProxy() { if ((_proxyName != null) && (_proxyPort > 0)) { WebProxy proxy = new WebProxy(_proxyName, _proxyPort); // disable proxy use when the host is local. i.e. without periods proxy.BypassProxyOnLocal = true; _proxyObject = proxy; } } // UpdateProxy // // end of Helper functions for processing settings and properties // internal static String UserAgent { get { return s_userAgent; } } // Used for maintaining async request state private class AsyncHttpClientRequestState { private static AsyncCallback s_processGetRequestStreamCompletionCallback = new AsyncCallback(ProcessGetRequestStreamCompletion); private static AsyncCallback s_processAsyncCopyRequestStreamCompletionCallback = new AsyncCallback(ProcessAsyncCopyRequestStreamCompletion); private static AsyncCallback s_processGetResponseCompletionCallback = new AsyncCallback(ProcessGetResponseCompletion); private static AsyncCallback s_processAsyncCopyRequestStreamCompletion = new AsyncCallback(ProcessAsyncCopyResponseStreamCompletion); internal HttpWebRequest WebRequest; internal HttpWebResponse WebResponse; internal IClientChannelSinkStack SinkStack; internal Stream RequestStream; internal Stream ActualResponseStream; // stream that will be passed to channel sinks private HttpClientTransportSink _transportSink; private int _retryCount; private long _initialStreamPosition; private IMessage _msg; private ITransportHeaders _requestHeaders; internal AsyncHttpClientRequestState( HttpClientTransportSink transportSink, IClientChannelSinkStack sinkStack, IMessage msg, ITransportHeaders headers, Stream stream, int retryCount) { _transportSink = transportSink; SinkStack = sinkStack; _msg = msg; _requestHeaders = headers; RequestStream = stream; _retryCount = retryCount; if (RequestStream.CanSeek) _initialStreamPosition = RequestStream.Position; } // AsyncHttpClientRequestState internal void StartRequest() { WebRequest = _transportSink.SetupWebRequest(_msg, _requestHeaders); if (!_transportSink._useChunked) { try { WebRequest.ContentLength = (int)RequestStream.Length; } catch { // ---- exception if RequestStream.Length throws; just // means that WebRequest will have to buffer the stream. } } // Chain of methods called is as follows: // 1. StartRequest (this one) // 2. ProcessGetRequestStreamCompletion // 3. ProcessAsyncCopyRequestStreamCompletion // 2. ProcessGetResponseCompletion // 3. ProcessAsyncCopyResponseStreamCompletion WebRequest.BeginGetRequestStream(s_processGetRequestStreamCompletionCallback, this); } // StartRequest // This should only be done when the send fails. internal void RetryOrDispatchException(Exception e) { bool bRetry = false; try { if (_retryCount > 0) { _retryCount--; if (RequestStream.CanSeek) { RequestStream.Position = _initialStreamPosition; StartRequest(); bRetry = true; } } } catch { } if (!bRetry) { RequestStream.Close(); SinkStack.DispatchException(e); } } // DispatchExceptionOrRetry // called from StartRequest private static void ProcessGetRequestStreamCompletion(IAsyncResult iar) { // We've just received a request stream. AsyncHttpClientRequestState asyncRequestState = (AsyncHttpClientRequestState)iar.AsyncState; try { HttpWebRequest httpWebRequest = asyncRequestState.WebRequest; Stream sourceRequestStream = asyncRequestState.RequestStream; Stream webRequestStream = httpWebRequest.EndGetRequestStream(iar); StreamHelper.BeginAsyncCopyStream( sourceRequestStream, webRequestStream, false, true, // [....] read, async write false, true, // leave source open, close target s_processAsyncCopyRequestStreamCompletionCallback, asyncRequestState); } catch (Exception e) { asyncRequestState.RetryOrDispatchException(e); } } // ProcessGetRequestStreamCompletion // called from ProcessGetRequestStreamCompletion private static void ProcessAsyncCopyRequestStreamCompletion(IAsyncResult iar) { // We've just finished copying the original request stream into the network stream. AsyncHttpClientRequestState asyncRequestState = (AsyncHttpClientRequestState)iar.AsyncState; try { StreamHelper.EndAsyncCopyStream(iar); asyncRequestState.WebRequest.BeginGetResponse( s_processGetResponseCompletionCallback, asyncRequestState); } catch (Exception e) { // This is the last point where we should retry. asyncRequestState.RetryOrDispatchException(e); } } // ProcessAsyncCopyRequestStreamCompletion // called from ProcessAsyncCopyRequestStreamCompletion private static void ProcessGetResponseCompletion(IAsyncResult iar) { // We've just received a response. AsyncHttpClientRequestState asyncRequestState = (AsyncHttpClientRequestState)iar.AsyncState; try { // close the request stream since we are done with it. asyncRequestState.RequestStream.Close(); HttpWebResponse httpWebResponse = null; HttpWebRequest httpWebRequest = asyncRequestState.WebRequest; try { httpWebResponse = (HttpWebResponse)httpWebRequest.EndGetResponse(iar); } catch (WebException webException) { ProcessResponseException(webException, out httpWebResponse); } asyncRequestState.WebResponse = httpWebResponse; // Asynchronously pump the web response stream into a memory stream. ChunkedMemoryStream responseStream = new ChunkedMemoryStream(CoreChannel.BufferPool); asyncRequestState.ActualResponseStream = responseStream; StreamHelper.BeginAsyncCopyStream( httpWebResponse.GetResponseStream(), responseStream, true, false, // async read, [....] write true, false, // close source, leave target open s_processAsyncCopyRequestStreamCompletion, asyncRequestState); } catch (Exception e) { asyncRequestState.SinkStack.DispatchException(e); } } // ProcessGetResponseCompletion // called from ProcessGetResponseCompletion private static void ProcessAsyncCopyResponseStreamCompletion(IAsyncResult iar) { // We've just finished copying the network response stream into a memory stream. AsyncHttpClientRequestState asyncRequestState = (AsyncHttpClientRequestState)iar.AsyncState; try { StreamHelper.EndAsyncCopyStream(iar); HttpWebResponse webResponse = asyncRequestState.WebResponse; Stream responseStream = asyncRequestState.ActualResponseStream; ITransportHeaders responseHeaders = CollectResponseHeaders(webResponse); // call down the sink chain asyncRequestState.SinkStack.AsyncProcessResponse(responseHeaders, responseStream); } catch (Exception e) { asyncRequestState.SinkStack.DispatchException(e); } } // ProcessAsyncResponseStreamCompletion } // class AsyncHttpClientRequest } // class HttpClientTransportSink } // namespace System.Runtime.Remoting.Channels.Http // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- JsonByteArrayDataContract.cs
- Assign.cs
- SplashScreenNativeMethods.cs
- ResourceAttributes.cs
- InputMethod.cs
- Tool.cs
- HtmlInputCheckBox.cs
- ElementInit.cs
- FunctionNode.cs
- TextTrailingWordEllipsis.cs
- ContextMenuStrip.cs
- PrinterResolution.cs
- BaseParagraph.cs
- TextPointer.cs
- SetStateEventArgs.cs
- OracleDataReader.cs
- ErrorFormatterPage.cs
- SignatureHelper.cs
- InkCanvas.cs
- ComplusTypeValidator.cs
- TextEvent.cs
- IList.cs
- DataGridViewBindingCompleteEventArgs.cs
- hresults.cs
- ResourceDescriptionAttribute.cs
- FixedSchema.cs
- ItemsPresenter.cs
- DateTimeParse.cs
- PageEventArgs.cs
- SimpleRecyclingCache.cs
- ToolboxComponentsCreatingEventArgs.cs
- CommonObjectSecurity.cs
- SuppressMessageAttribute.cs
- MessagePartSpecification.cs
- IApplicationTrustManager.cs
- ListViewGroup.cs
- BrushMappingModeValidation.cs
- WorkflowPageSetupDialog.cs
- documentsequencetextpointer.cs
- GridViewDeleteEventArgs.cs
- UnknownWrapper.cs
- Bezier.cs
- ReferenceConverter.cs
- TextEditorMouse.cs
- InlineObject.cs
- DrawListViewItemEventArgs.cs
- DispatcherExceptionEventArgs.cs
- ConstraintManager.cs
- NameNode.cs
- PackageProperties.cs
- ToolStripRenderer.cs
- PropertyIDSet.cs
- IISUnsafeMethods.cs
- ImageInfo.cs
- DocComment.cs
- HttpDictionary.cs
- SqlInternalConnectionSmi.cs
- EditBehavior.cs
- Highlights.cs
- StoryFragments.cs
- HttpHandlerActionCollection.cs
- FileSystemInfo.cs
- MSAANativeProvider.cs
- SingleSelectRootGridEntry.cs
- TextDecoration.cs
- SkewTransform.cs
- SingleKeyFrameCollection.cs
- WizardStepBase.cs
- XmlCollation.cs
- SettingsPropertyValueCollection.cs
- ParameterCollectionEditor.cs
- BitStack.cs
- MessageQueuePermission.cs
- ResourceContainer.cs
- ArrayListCollectionBase.cs
- DbProviderManifest.cs
- DataGridViewCellStyleChangedEventArgs.cs
- CharUnicodeInfo.cs
- WebPermission.cs
- ProtocolViolationException.cs
- DesignerActionListCollection.cs
- FlowDecision.cs
- Funcletizer.cs
- PolyBezierSegment.cs
- ADConnectionHelper.cs
- TransformGroup.cs
- TaskScheduler.cs
- ImageField.cs
- RenamedEventArgs.cs
- LocationSectionRecord.cs
- RawStylusInputReport.cs
- ObjectTag.cs
- ColorInterpolationModeValidation.cs
- StringUtil.cs
- SequenceQuery.cs
- ObjectStateManagerMetadata.cs
- VisualBrush.cs
- HyperLinkField.cs
- NativeMethods.cs
- SimpleBitVector32.cs