Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.ServiceModel / System / ServiceModel / Channels / HttpChannelFactory.cs
blob323b25415036cc194c261f3816596e1df5ef4d89
1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //------------------------------------------------------------
4 namespace System.ServiceModel.Channels
6 using System.Collections.ObjectModel;
7 using System.Diagnostics;
8 using System.IdentityModel.Policy;
9 using System.IdentityModel.Selectors;
10 using System.IdentityModel.Tokens;
11 using System.IO;
12 using System.Net;
13 using System.Net.Cache;
14 using System.Net.Security;
15 using System.Runtime;
16 using System.Runtime.CompilerServices;
17 using System.Runtime.Diagnostics;
18 using System.Security;
19 using System.Security.Authentication.ExtendedProtection;
20 using System.Security.Cryptography;
21 using System.Security.Permissions;
22 using System.Security.Principal;
23 using System.ServiceModel;
24 using System.ServiceModel.Description;
25 using System.ServiceModel.Diagnostics;
26 using System.ServiceModel.Diagnostics.Application;
27 using System.ServiceModel.Security;
28 using System.ServiceModel.Security.Tokens;
29 using System.Text;
30 using System.Threading;
32 class HttpChannelFactory<TChannel>
33 : TransportChannelFactory<TChannel>,
34 IHttpTransportFactorySettings
36 static bool httpWebRequestWebPermissionDenied = false;
37 static RequestCachePolicy requestCachePolicy = new RequestCachePolicy(RequestCacheLevel.BypassCache);
38 static long connectionGroupNamePrefix = 0;
40 readonly ClientWebSocketFactory clientWebSocketFactory;
42 bool allowCookies;
43 AuthenticationSchemes authenticationScheme;
44 HttpCookieContainerManager httpCookieContainerManager;
46 // Double-checked locking pattern requires volatile for read/write synchronization
47 volatile MruCache<Uri, Uri> credentialCacheUriPrefixCache;
48 bool decompressionEnabled;
50 // Double-checked locking pattern requires volatile for read/write synchronization
51 [Fx.Tag.SecurityNote(Critical = "This cache stores strings that contain domain/user name/password. Must not be settable from PT code.")]
52 [SecurityCritical]
53 volatile MruCache<string, string> credentialHashCache;
55 [Fx.Tag.SecurityNote(Critical = "This hash algorithm takes strings that contain domain/user name/password. Must not be settable from PT code.")]
56 [SecurityCritical]
57 HashAlgorithm hashAlgorithm;
58 bool keepAliveEnabled;
59 int maxBufferSize;
60 IWebProxy proxy;
61 WebProxyFactory proxyFactory;
62 SecurityCredentialsManager channelCredentials;
63 SecurityTokenManager securityTokenManager;
64 TransferMode transferMode;
65 ISecurityCapabilities securityCapabilities;
66 WebSocketTransportSettings webSocketSettings;
67 ConnectionBufferPool bufferPool;
68 Lazy<string> webSocketSoapContentType;
69 string uniqueConnectionGroupNamePrefix;
71 internal HttpChannelFactory(HttpTransportBindingElement bindingElement, BindingContext context)
72 : base(bindingElement, context, HttpTransportDefaults.GetDefaultMessageEncoderFactory())
74 // validate setting interactions
75 if (bindingElement.TransferMode == TransferMode.Buffered)
77 if (bindingElement.MaxReceivedMessageSize > int.MaxValue)
79 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
80 new ArgumentOutOfRangeException("bindingElement.MaxReceivedMessageSize",
81 SR.GetString(SR.MaxReceivedMessageSizeMustBeInIntegerRange)));
84 if (bindingElement.MaxBufferSize != bindingElement.MaxReceivedMessageSize)
86 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("bindingElement",
87 SR.GetString(SR.MaxBufferSizeMustMatchMaxReceivedMessageSize));
90 else
92 if (bindingElement.MaxBufferSize > bindingElement.MaxReceivedMessageSize)
94 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("bindingElement",
95 SR.GetString(SR.MaxBufferSizeMustNotExceedMaxReceivedMessageSize));
99 if (TransferModeHelper.IsRequestStreamed(bindingElement.TransferMode) &&
100 bindingElement.AuthenticationScheme != AuthenticationSchemes.Anonymous)
102 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("bindingElement", SR.GetString(
103 SR.HttpAuthDoesNotSupportRequestStreaming));
106 this.allowCookies = bindingElement.AllowCookies;
107 #pragma warning disable 618
108 if (!this.allowCookies)
110 Collection<HttpCookieContainerBindingElement> httpCookieContainerBindingElements = context.BindingParameters.FindAll<HttpCookieContainerBindingElement>();
111 if (httpCookieContainerBindingElements.Count > 1)
113 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.MultipleCCbesInParameters, typeof(HttpCookieContainerBindingElement))));
115 if (httpCookieContainerBindingElements.Count == 1)
117 this.allowCookies = true;
118 context.BindingParameters.Remove<HttpCookieContainerBindingElement>();
121 #pragma warning restore 618
123 if (this.allowCookies)
125 this.httpCookieContainerManager = new HttpCookieContainerManager();
128 if (!bindingElement.AuthenticationScheme.IsSingleton())
130 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("value", SR.GetString(SR.HttpRequiresSingleAuthScheme,
131 bindingElement.AuthenticationScheme));
134 this.authenticationScheme = bindingElement.AuthenticationScheme;
135 this.decompressionEnabled = bindingElement.DecompressionEnabled;
136 this.keepAliveEnabled = bindingElement.KeepAliveEnabled;
137 this.maxBufferSize = bindingElement.MaxBufferSize;
138 this.transferMode = bindingElement.TransferMode;
140 if (bindingElement.Proxy != null)
142 this.proxy = bindingElement.Proxy;
144 else if (bindingElement.ProxyAddress != null)
146 if (bindingElement.UseDefaultWebProxy)
148 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.UseDefaultWebProxyCantBeUsedWithExplicitProxyAddress)));
151 if (bindingElement.ProxyAuthenticationScheme == AuthenticationSchemes.Anonymous)
153 this.proxy = new WebProxy(bindingElement.ProxyAddress, bindingElement.BypassProxyOnLocal);
155 else
157 this.proxy = null;
158 this.proxyFactory =
159 new WebProxyFactory(bindingElement.ProxyAddress, bindingElement.BypassProxyOnLocal,
160 bindingElement.ProxyAuthenticationScheme);
163 else if (!bindingElement.UseDefaultWebProxy)
165 this.proxy = new WebProxy();
168 this.channelCredentials = context.BindingParameters.Find<SecurityCredentialsManager>();
169 this.securityCapabilities = bindingElement.GetProperty<ISecurityCapabilities>(context);
170 this.webSocketSettings = WebSocketHelper.GetRuntimeWebSocketSettings(bindingElement.WebSocketSettings);
172 int webSocketBufferSize = WebSocketHelper.ComputeClientBufferSize(this.MaxReceivedMessageSize);
173 this.bufferPool = new ConnectionBufferPool(webSocketBufferSize);
175 Collection<ClientWebSocketFactory> clientWebSocketFactories = context.BindingParameters.FindAll<ClientWebSocketFactory>();
176 if (clientWebSocketFactories.Count > 1)
178 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(
179 "context",
180 SR.GetString(SR.MultipleClientWebSocketFactoriesSpecified, typeof(BindingContext).Name, typeof(ClientWebSocketFactory).Name));
182 else
184 this.clientWebSocketFactory = clientWebSocketFactories.Count == 0 ? null : clientWebSocketFactories[0];
187 this.webSocketSoapContentType = new Lazy<string>(() => { return this.MessageEncoderFactory.CreateSessionEncoder().ContentType; }, LazyThreadSafetyMode.ExecutionAndPublication);
189 if (ServiceModelAppSettings.HttpTransportPerFactoryConnectionPool)
191 this.uniqueConnectionGroupNamePrefix = Interlocked.Increment(ref connectionGroupNamePrefix).ToString();
193 else
195 this.uniqueConnectionGroupNamePrefix = string.Empty;
199 public bool AllowCookies
203 return this.allowCookies;
207 public AuthenticationSchemes AuthenticationScheme
211 return this.authenticationScheme;
215 public bool DecompressionEnabled
219 return this.decompressionEnabled;
223 public virtual bool IsChannelBindingSupportEnabled
227 return false;
231 public bool KeepAliveEnabled
235 return this.keepAliveEnabled;
239 public SecurityTokenManager SecurityTokenManager
243 return this.securityTokenManager;
247 public int MaxBufferSize
251 return maxBufferSize;
255 public IWebProxy Proxy
259 return this.proxy;
263 public TransferMode TransferMode
267 return transferMode;
271 public override string Scheme
275 return Uri.UriSchemeHttp;
279 public WebSocketTransportSettings WebSocketSettings
281 get { return this.webSocketSettings; }
284 internal string WebSocketSoapContentType
288 return this.webSocketSoapContentType.Value;
292 protected ConnectionBufferPool WebSocketBufferPool
294 get { return this.bufferPool; }
297 // must be called under lock (this.credentialHashCache)
298 HashAlgorithm HashAlgorithm
300 [SecurityCritical]
303 if (this.hashAlgorithm == null)
305 this.hashAlgorithm = CryptoHelper.CreateHashAlgorithm(SecurityAlgorithms.Sha1Digest);
307 else
309 this.hashAlgorithm.Initialize();
312 return this.hashAlgorithm;
316 int IHttpTransportFactorySettings.MaxBufferSize
318 get { return MaxBufferSize; }
321 TransferMode IHttpTransportFactorySettings.TransferMode
323 get { return TransferMode; }
326 protected ClientWebSocketFactory ClientWebSocketFactory
330 return this.clientWebSocketFactory;
334 public override T GetProperty<T>()
336 if (typeof(T) == typeof(ISecurityCapabilities))
338 return (T)(object)this.securityCapabilities;
340 if (typeof(T) == typeof(IHttpCookieContainerManager))
342 return (T)(object)this.GetHttpCookieContainerManager();
345 return base.GetProperty<T>();
348 [PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
349 [MethodImpl(MethodImplOptions.NoInlining)]
350 private HttpCookieContainerManager GetHttpCookieContainerManager()
352 return this.httpCookieContainerManager;
355 internal virtual SecurityMessageProperty CreateReplySecurityProperty(HttpWebRequest request,
356 HttpWebResponse response)
358 // Don't pull in System.Authorization if we don't need to!
359 if (!response.IsMutuallyAuthenticated)
361 return null;
364 return CreateMutuallyAuthenticatedReplySecurityProperty(response);
367 internal Exception CreateToMustEqualViaException(Uri to, Uri via)
369 return new ArgumentException(SR.GetString(SR.HttpToMustEqualVia, to, via));
372 [MethodImpl(MethodImplOptions.NoInlining)]
373 SecurityMessageProperty CreateMutuallyAuthenticatedReplySecurityProperty(HttpWebResponse response)
375 string spn = AuthenticationManager.CustomTargetNameDictionary[response.ResponseUri.AbsoluteUri];
376 if (spn == null)
378 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.HttpSpnNotFound,
379 response.ResponseUri)));
381 ReadOnlyCollection<IAuthorizationPolicy> spnPolicies = SecurityUtils.CreatePrincipalNameAuthorizationPolicies(spn);
382 SecurityMessageProperty remoteSecurity = new SecurityMessageProperty();
383 remoteSecurity.TransportToken = new SecurityTokenSpecification(null, spnPolicies);
384 remoteSecurity.ServiceSecurityContext = new ServiceSecurityContext(spnPolicies);
385 return remoteSecurity;
388 internal override int GetMaxBufferSize()
390 return MaxBufferSize;
393 SecurityTokenProviderContainer CreateAndOpenTokenProvider(TimeSpan timeout, AuthenticationSchemes authenticationScheme,
394 EndpointAddress target, Uri via, ChannelParameterCollection channelParameters)
396 SecurityTokenProvider tokenProvider = null;
397 switch (authenticationScheme)
399 case AuthenticationSchemes.Anonymous:
400 break;
401 case AuthenticationSchemes.Basic:
402 tokenProvider = TransportSecurityHelpers.GetUserNameTokenProvider(this.SecurityTokenManager, target, via, this.Scheme, authenticationScheme, channelParameters);
403 break;
404 case AuthenticationSchemes.Negotiate:
405 case AuthenticationSchemes.Ntlm:
406 tokenProvider = TransportSecurityHelpers.GetSspiTokenProvider(this.SecurityTokenManager, target, via, this.Scheme, authenticationScheme, channelParameters);
407 break;
408 case AuthenticationSchemes.Digest:
409 tokenProvider = TransportSecurityHelpers.GetDigestTokenProvider(this.SecurityTokenManager, target, via, this.Scheme, authenticationScheme, channelParameters);
410 break;
411 default:
412 // The setter for this property should prevent this.
413 throw Fx.AssertAndThrow("CreateAndOpenTokenProvider: Invalid authentication scheme");
415 SecurityTokenProviderContainer result;
416 if (tokenProvider != null)
418 result = new SecurityTokenProviderContainer(tokenProvider);
419 result.Open(timeout);
421 else
423 result = null;
425 return result;
428 protected virtual void ValidateCreateChannelParameters(EndpointAddress remoteAddress, Uri via)
430 base.ValidateScheme(via);
432 if (this.MessageVersion.Addressing == AddressingVersion.None && remoteAddress.Uri != via)
434 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateToMustEqualViaException(remoteAddress.Uri, via));
438 protected override TChannel OnCreateChannel(EndpointAddress remoteAddress, Uri via)
440 EndpointAddress httpRemoteAddress = remoteAddress != null && WebSocketHelper.IsWebSocketUri(remoteAddress.Uri) ?
441 new EndpointAddress(WebSocketHelper.NormalizeWsSchemeWithHttpScheme(remoteAddress.Uri), remoteAddress) :
442 remoteAddress;
444 Uri httpVia = WebSocketHelper.IsWebSocketUri(via) ? WebSocketHelper.NormalizeWsSchemeWithHttpScheme(via) : via;
445 return this.OnCreateChannelCore(httpRemoteAddress, httpVia);
448 protected virtual TChannel OnCreateChannelCore(EndpointAddress remoteAddress, Uri via)
450 ValidateCreateChannelParameters(remoteAddress, via);
451 this.ValidateWebSocketTransportUsage();
453 if (typeof(TChannel) == typeof(IRequestChannel))
455 return (TChannel)(object)new HttpRequestChannel((HttpChannelFactory<IRequestChannel>)(object)this, remoteAddress, via, ManualAddressing);
457 else
459 return (TChannel)(object)new ClientWebSocketTransportDuplexSessionChannel((HttpChannelFactory<IDuplexSessionChannel>)(object)this, this.clientWebSocketFactory, remoteAddress, via, this.WebSocketBufferPool);
463 protected void ValidateWebSocketTransportUsage()
465 Type channelType = typeof(TChannel);
466 if (channelType == typeof(IRequestChannel) && this.WebSocketSettings.TransportUsage == WebSocketTransportUsage.Always)
468 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.GetString(
469 SR.WebSocketCannotCreateRequestClientChannelWithCertainWebSocketTransportUsage,
470 typeof(TChannel),
471 WebSocketTransportSettings.TransportUsageMethodName,
472 typeof(WebSocketTransportSettings).Name,
473 this.WebSocketSettings.TransportUsage)));
476 else if (channelType == typeof(IDuplexSessionChannel))
478 if (this.WebSocketSettings.TransportUsage == WebSocketTransportUsage.Never)
480 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.GetString(
481 SR.WebSocketCannotCreateRequestClientChannelWithCertainWebSocketTransportUsage,
482 typeof(TChannel),
483 WebSocketTransportSettings.TransportUsageMethodName,
484 typeof(WebSocketTransportSettings).Name,
485 this.WebSocketSettings.TransportUsage)));
487 else if (!WebSocketHelper.OSSupportsWebSockets() && this.ClientWebSocketFactory == null)
489 throw FxTrace.Exception.AsError(new PlatformNotSupportedException(SR.GetString(SR.WebSocketsClientSideNotSupported, typeof(ClientWebSocketFactory).FullName)));
494 [MethodImpl(MethodImplOptions.NoInlining)]
495 void InitializeSecurityTokenManager()
497 if (this.channelCredentials == null)
499 this.channelCredentials = ClientCredentials.CreateDefaultCredentials();
501 this.securityTokenManager = this.channelCredentials.CreateSecurityTokenManager();
504 protected virtual bool IsSecurityTokenManagerRequired()
506 if (this.AuthenticationScheme != AuthenticationSchemes.Anonymous)
508 return true;
510 if (this.proxyFactory != null && this.proxyFactory.AuthenticationScheme != AuthenticationSchemes.Anonymous)
512 return true;
514 else
516 return false;
520 protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
522 this.OnOpen(timeout);
523 return new CompletedAsyncResult(callback, state);
526 protected override void OnEndOpen(IAsyncResult result)
528 CompletedAsyncResult.End(result);
531 protected override void OnOpen(TimeSpan timeout)
533 if (IsSecurityTokenManagerRequired())
535 this.InitializeSecurityTokenManager();
538 if (this.AllowCookies &&
539 !this.httpCookieContainerManager.IsInitialized) // We don't want to overwrite the CookieContainer if someone has set it already.
541 this.httpCookieContainerManager.CookieContainer = new CookieContainer();
544 // we need to make sure System.Net will buffer faults (sent as 500 requests) up to our allowed size
545 // Their value is in Kbytes and ours is in bytes. We round up so that the KB value is large enough to
546 // encompass our MaxReceivedMessageSize. See MB#20860 and related for details
548 if (!httpWebRequestWebPermissionDenied && HttpWebRequest.DefaultMaximumErrorResponseLength != -1)
550 int MaxReceivedMessageSizeKbytes;
551 if (MaxBufferSize >= (int.MaxValue - 1024)) // make sure NCL doesn't overflow
553 MaxReceivedMessageSizeKbytes = -1;
555 else
557 MaxReceivedMessageSizeKbytes = (int)(MaxBufferSize / 1024);
558 if (MaxReceivedMessageSizeKbytes * 1024 < MaxBufferSize)
560 MaxReceivedMessageSizeKbytes++;
564 if (MaxReceivedMessageSizeKbytes == -1
565 || MaxReceivedMessageSizeKbytes > HttpWebRequest.DefaultMaximumErrorResponseLength)
569 HttpWebRequest.DefaultMaximumErrorResponseLength = MaxReceivedMessageSizeKbytes;
571 catch (SecurityException exception)
573 // CSDMain\33725 - setting DefaultMaximumErrorResponseLength should not fail HttpChannelFactory.OnOpen
574 // if the user does not have the permission to do so.
575 httpWebRequestWebPermissionDenied = true;
577 DiagnosticUtility.TraceHandledException(exception, TraceEventType.Warning);
583 protected override void OnClosed()
585 base.OnClosed();
586 if (this.bufferPool != null)
588 this.bufferPool.Close();
592 static internal void TraceResponseReceived(HttpWebResponse response, Message message, object receiver)
594 if (DiagnosticUtility.ShouldTraceVerbose)
596 if (response != null && response.ResponseUri != null)
598 TraceUtility.TraceEvent(TraceEventType.Verbose, TraceCode.HttpResponseReceived, SR.GetString(SR.TraceCodeHttpResponseReceived), new StringTraceRecord("ResponseUri", response.ResponseUri.ToString()), receiver, null, message);
600 else
602 TraceUtility.TraceEvent(TraceEventType.Verbose, TraceCode.HttpResponseReceived, SR.GetString(SR.TraceCodeHttpResponseReceived), receiver, message);
607 [Fx.Tag.SecurityNote(Critical = "Uses unsafe critical method AppendWindowsAuthenticationInfo to access the credential domain/user name/password.")]
608 [SecurityCritical]
609 [MethodImpl(MethodImplOptions.NoInlining)]
610 string AppendWindowsAuthenticationInfo(string inputString, NetworkCredential credential,
611 AuthenticationLevel authenticationLevel, TokenImpersonationLevel impersonationLevel)
613 return SecurityUtils.AppendWindowsAuthenticationInfo(inputString, credential, authenticationLevel, impersonationLevel);
616 protected virtual string OnGetConnectionGroupPrefix(HttpWebRequest httpWebRequest, SecurityTokenContainer clientCertificateToken)
618 return string.Empty;
621 internal static bool IsWindowsAuth(AuthenticationSchemes authScheme)
623 Fx.Assert(authScheme.IsSingleton(), "authenticationScheme used in an Http(s)ChannelFactory must be a singleton value.");
625 return authScheme == AuthenticationSchemes.Negotiate ||
626 authScheme == AuthenticationSchemes.Ntlm;
629 [Fx.Tag.SecurityNote(Critical = "Uses unsafe critical method AppendWindowsAuthenticationInfo to access the credential domain/user name/password.",
630 Safe = "Uses the domain/user name/password to store and compute a hash. The store is SecurityCritical. The hash leaks but" +
631 "the hash cannot be reversed to the domain/user name/password.")]
632 [SecuritySafeCritical]
633 string GetConnectionGroupName(HttpWebRequest httpWebRequest, NetworkCredential credential, AuthenticationLevel authenticationLevel,
634 TokenImpersonationLevel impersonationLevel, SecurityTokenContainer clientCertificateToken)
636 if (this.credentialHashCache == null)
638 lock (ThisLock)
640 if (this.credentialHashCache == null)
642 this.credentialHashCache = new MruCache<string, string>(5);
647 // The following line is a work-around for VSWhidbey 558605. In particular, we need to isolate our
648 // connection groups based on whether we are streaming the request.
649 string inputString = TransferModeHelper.IsRequestStreamed(this.TransferMode) ? "streamed" : string.Empty;
651 if (IsWindowsAuth(this.AuthenticationScheme))
653 // for NTLM & Negotiate, System.Net doesn't pool connections by default. This is because
654 // IIS doesn't re-authenticate NTLM connections (made for a perf reason), and HttpWebRequest
655 // shared connections among multiple callers.
656 // This causes Indigo a performance problem in turn. We mitigate this by (1) enabling
657 // connection sharing for NTLM connections on our pool, and (2) scoping the pool we use
658 // to be based on the NetworkCredential that is being used to authenticate the connection.
659 // Therefore we're only sharing connections among the same Credential.
661 // Setting this will fail in partial trust, and that's ok since this is an optimization.
662 if (!httpWebRequestWebPermissionDenied)
666 httpWebRequest.UnsafeAuthenticatedConnectionSharing = true;
668 catch (SecurityException e)
670 DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
671 httpWebRequestWebPermissionDenied = true;
675 inputString = AppendWindowsAuthenticationInfo(inputString, credential, authenticationLevel, impersonationLevel);
678 string prefix = this.OnGetConnectionGroupPrefix(httpWebRequest, clientCertificateToken);
679 inputString = string.Concat(this.uniqueConnectionGroupNamePrefix, prefix, inputString);
681 string credentialHash = null;
683 // we have to lock around each call to TryGetValue since the MruCache modifies the
684 // contents of it's mruList in a single-threaded manner underneath TryGetValue
686 if (!string.IsNullOrEmpty(inputString))
688 lock (this.credentialHashCache)
690 if (!this.credentialHashCache.TryGetValue(inputString, out credentialHash))
692 byte[] inputBytes = new UTF8Encoding().GetBytes(inputString);
693 byte[] digestBytes = this.HashAlgorithm.ComputeHash(inputBytes);
694 credentialHash = Convert.ToBase64String(digestBytes);
695 this.credentialHashCache.Add(inputString, credentialHash);
700 return credentialHash;
703 Uri GetCredentialCacheUriPrefix(Uri via)
705 Uri result;
707 if (this.credentialCacheUriPrefixCache == null)
709 lock (ThisLock)
711 if (this.credentialCacheUriPrefixCache == null)
713 this.credentialCacheUriPrefixCache = new MruCache<Uri, Uri>(10);
718 lock (this.credentialCacheUriPrefixCache)
720 if (!this.credentialCacheUriPrefixCache.TryGetValue(via, out result))
722 result = new UriBuilder(via.Scheme, via.Host, via.Port).Uri;
723 this.credentialCacheUriPrefixCache.Add(via, result);
727 return result;
730 // core code for creating an HttpWebRequest
731 HttpWebRequest GetWebRequest(EndpointAddress to, Uri via, NetworkCredential credential,
732 TokenImpersonationLevel impersonationLevel, AuthenticationLevel authenticationLevel,
733 SecurityTokenProviderContainer proxyTokenProvider, SecurityTokenContainer clientCertificateToken, TimeSpan timeout, bool isWebSocketRequest)
735 Uri httpWebRequestUri = isWebSocketRequest ? WebSocketHelper.GetWebSocketUri(via) : via;
736 HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(httpWebRequestUri);
737 Fx.Assert(httpWebRequest.Method.Equals("GET", StringComparison.OrdinalIgnoreCase), "the default HTTP method of HttpWebRequest should be 'Get'.");
739 if (!isWebSocketRequest)
741 httpWebRequest.Method = "POST";
743 if (TransferModeHelper.IsRequestStreamed(TransferMode))
745 httpWebRequest.SendChunked = true;
746 httpWebRequest.AllowWriteStreamBuffering = false;
748 else
750 httpWebRequest.AllowWriteStreamBuffering = true;
754 httpWebRequest.CachePolicy = requestCachePolicy;
755 httpWebRequest.KeepAlive = this.keepAliveEnabled;
757 if (this.decompressionEnabled)
759 httpWebRequest.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
761 else
763 httpWebRequest.AutomaticDecompression = DecompressionMethods.None;
766 if (credential != null)
768 CredentialCache credentials = new CredentialCache();
769 credentials.Add(this.GetCredentialCacheUriPrefix(via),
770 AuthenticationSchemesHelper.ToString(this.authenticationScheme), credential);
771 httpWebRequest.Credentials = credentials;
773 httpWebRequest.AuthenticationLevel = authenticationLevel;
774 httpWebRequest.ImpersonationLevel = impersonationLevel;
776 string connectionGroupName = GetConnectionGroupName(httpWebRequest, credential, authenticationLevel, impersonationLevel, clientCertificateToken);
778 X509CertificateEndpointIdentity remoteCertificateIdentity = to.Identity as X509CertificateEndpointIdentity;
779 if (remoteCertificateIdentity != null)
781 connectionGroupName = string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}[{1}]", connectionGroupName, remoteCertificateIdentity.Certificates[0].Thumbprint);
784 if (!string.IsNullOrEmpty(connectionGroupName))
786 httpWebRequest.ConnectionGroupName = connectionGroupName;
789 if (AuthenticationScheme == AuthenticationSchemes.Basic)
791 httpWebRequest.PreAuthenticate = true;
794 if (this.proxy != null)
796 httpWebRequest.Proxy = this.proxy;
798 else if (this.proxyFactory != null)
800 httpWebRequest.Proxy = this.proxyFactory.CreateWebProxy(httpWebRequest, proxyTokenProvider, timeout);
803 if (this.AllowCookies)
805 httpWebRequest.CookieContainer = this.httpCookieContainerManager.CookieContainer;
808 // we do this at the end so that we access the correct ServicePoint
809 httpWebRequest.ServicePoint.UseNagleAlgorithm = false;
811 return httpWebRequest;
814 void ApplyManualAddressing(ref EndpointAddress to, ref Uri via, Message message)
816 if (ManualAddressing)
818 Uri toHeader = message.Headers.To;
819 if (toHeader == null)
821 throw TraceUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ManualAddressingRequiresAddressedMessages)), message);
824 to = new EndpointAddress(toHeader);
826 if (this.MessageVersion.Addressing == AddressingVersion.None)
828 via = toHeader;
832 // now apply query string property
833 object property;
834 if (message.Properties.TryGetValue(HttpRequestMessageProperty.Name, out property))
836 HttpRequestMessageProperty requestProperty = (HttpRequestMessageProperty)property;
837 if (!string.IsNullOrEmpty(requestProperty.QueryString))
839 UriBuilder uriBuilder = new UriBuilder(via);
841 if (requestProperty.QueryString.StartsWith("?", StringComparison.Ordinal))
843 uriBuilder.Query = requestProperty.QueryString.Substring(1);
845 else
847 uriBuilder.Query = requestProperty.QueryString;
850 via = uriBuilder.Uri;
855 [MethodImpl(MethodImplOptions.NoInlining)]
856 void CreateAndOpenTokenProvidersCore(EndpointAddress to, Uri via, ChannelParameterCollection channelParameters, TimeSpan timeout, out SecurityTokenProviderContainer tokenProvider, out SecurityTokenProviderContainer proxyTokenProvider)
858 TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
859 tokenProvider = CreateAndOpenTokenProvider(timeoutHelper.RemainingTime(), this.AuthenticationScheme, to, via, channelParameters);
860 if (this.proxyFactory != null)
862 proxyTokenProvider = CreateAndOpenTokenProvider(timeoutHelper.RemainingTime(), this.proxyFactory.AuthenticationScheme, to, via, channelParameters);
864 else
866 proxyTokenProvider = null;
870 internal void CreateAndOpenTokenProviders(EndpointAddress to, Uri via, ChannelParameterCollection channelParameters, TimeSpan timeout, out SecurityTokenProviderContainer tokenProvider, out SecurityTokenProviderContainer proxyTokenProvider)
872 if (!IsSecurityTokenManagerRequired())
874 tokenProvider = null;
875 proxyTokenProvider = null;
877 else
879 CreateAndOpenTokenProvidersCore(to, via, channelParameters, timeout, out tokenProvider, out proxyTokenProvider);
883 internal HttpWebRequest GetWebRequest(EndpointAddress to, Uri via, SecurityTokenProviderContainer tokenProvider,
884 SecurityTokenProviderContainer proxyTokenProvider, SecurityTokenContainer clientCertificateToken, TimeSpan timeout, bool isWebSocketRequest)
886 TokenImpersonationLevel impersonationLevel;
887 AuthenticationLevel authenticationLevel;
888 TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
889 NetworkCredential credential = HttpChannelUtilities.GetCredential(this.authenticationScheme,
890 tokenProvider, timeoutHelper.RemainingTime(), out impersonationLevel, out authenticationLevel);
892 return GetWebRequest(to, via, credential, impersonationLevel, authenticationLevel, proxyTokenProvider, clientCertificateToken, timeoutHelper.RemainingTime(), isWebSocketRequest);
895 internal static bool MapIdentity(EndpointAddress target, AuthenticationSchemes authenticationScheme)
897 if ((target.Identity == null) || (target.Identity is X509CertificateEndpointIdentity))
899 return false;
902 return IsWindowsAuth(authenticationScheme);
905 bool MapIdentity(EndpointAddress target)
907 return MapIdentity(target, this.AuthenticationScheme);
910 protected class HttpRequestChannel : RequestChannel
912 // Double-checked locking pattern requires volatile for read/write synchronization
913 volatile bool cleanupIdentity;
914 HttpChannelFactory<IRequestChannel> factory;
915 SecurityTokenProviderContainer tokenProvider;
916 SecurityTokenProviderContainer proxyTokenProvider;
917 ServiceModelActivity activity = null;
918 ChannelParameterCollection channelParameters;
920 public HttpRequestChannel(HttpChannelFactory<IRequestChannel> factory, EndpointAddress to, Uri via, bool manualAddressing)
921 : base(factory, to, via, manualAddressing)
923 this.factory = factory;
926 public HttpChannelFactory<IRequestChannel> Factory
928 get { return this.factory; }
931 internal ServiceModelActivity Activity
933 get { return this.activity; }
936 protected ChannelParameterCollection ChannelParameters
940 return this.channelParameters;
944 public override T GetProperty<T>()
946 if (typeof(T) == typeof(ChannelParameterCollection))
948 if (this.State == CommunicationState.Created)
950 lock (ThisLock)
952 if (this.channelParameters == null)
954 this.channelParameters = new ChannelParameterCollection();
958 return (T)(object)this.channelParameters;
960 else
962 return base.GetProperty<T>();
966 void PrepareOpen()
968 if (Factory.MapIdentity(RemoteAddress))
970 lock (ThisLock)
972 cleanupIdentity = HttpTransportSecurityHelpers.AddIdentityMapping(Via, RemoteAddress);
977 void CreateAndOpenTokenProviders(TimeSpan timeout)
979 TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
980 if (!ManualAddressing)
982 Factory.CreateAndOpenTokenProviders(this.RemoteAddress, this.Via, this.channelParameters, timeoutHelper.RemainingTime(), out this.tokenProvider, out this.proxyTokenProvider);
986 void CloseTokenProviders(TimeSpan timeout)
988 TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
989 if (this.tokenProvider != null)
991 tokenProvider.Close(timeoutHelper.RemainingTime());
993 if (this.proxyTokenProvider != null)
995 proxyTokenProvider.Close(timeoutHelper.RemainingTime());
999 void AbortTokenProviders()
1001 if (this.tokenProvider != null)
1003 tokenProvider.Abort();
1005 if (this.proxyTokenProvider != null)
1007 proxyTokenProvider.Abort();
1011 protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
1013 PrepareOpen();
1014 TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
1015 CreateAndOpenTokenProviders(timeoutHelper.RemainingTime());
1016 return new CompletedAsyncResult(callback, state);
1019 protected override void OnOpen(TimeSpan timeout)
1021 PrepareOpen();
1022 CreateAndOpenTokenProviders(timeout);
1025 protected override void OnEndOpen(IAsyncResult result)
1027 CompletedAsyncResult.End(result);
1030 void PrepareClose(bool aborting)
1032 if (cleanupIdentity)
1034 lock (ThisLock)
1036 if (cleanupIdentity)
1038 cleanupIdentity = false;
1039 HttpTransportSecurityHelpers.RemoveIdentityMapping(Via, RemoteAddress, !aborting);
1045 protected override void OnAbort()
1047 PrepareClose(true);
1048 AbortTokenProviders();
1049 base.OnAbort();
1052 protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
1054 IAsyncResult retval = null;
1055 using (ServiceModelActivity.BoundOperation(this.activity))
1057 PrepareClose(false);
1058 TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
1059 CloseTokenProviders(timeoutHelper.RemainingTime());
1060 retval = base.BeginWaitForPendingRequests(timeoutHelper.RemainingTime(), callback, state);
1062 ServiceModelActivity.Stop(this.activity);
1063 return retval;
1066 protected override void OnEndClose(IAsyncResult result)
1068 using (ServiceModelActivity.BoundOperation(this.activity))
1070 base.EndWaitForPendingRequests(result);
1072 ServiceModelActivity.Stop(this.activity);
1075 protected override void OnClose(TimeSpan timeout)
1077 using (ServiceModelActivity.BoundOperation(this.activity))
1079 PrepareClose(false);
1080 TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
1081 CloseTokenProviders(timeoutHelper.RemainingTime());
1082 base.WaitForPendingRequests(timeoutHelper.RemainingTime());
1084 ServiceModelActivity.Stop(this.activity);
1087 protected override IAsyncRequest CreateAsyncRequest(Message message, AsyncCallback callback, object state)
1089 if (DiagnosticUtility.ShouldUseActivity && this.activity == null)
1091 this.activity = ServiceModelActivity.CreateActivity();
1092 if (null != FxTrace.Trace)
1094 FxTrace.Trace.TraceTransfer(this.activity.Id);
1096 ServiceModelActivity.Start(this.activity, SR.GetString(SR.ActivityReceiveBytes, this.RemoteAddress.Uri.ToString()), ActivityType.ReceiveBytes);
1099 return new HttpChannelAsyncRequest(this, callback, state);
1102 protected override IRequest CreateRequest(Message message)
1104 return new HttpChannelRequest(this, Factory);
1107 public virtual HttpWebRequest GetWebRequest(EndpointAddress to, Uri via, ref TimeoutHelper timeoutHelper)
1109 return GetWebRequest(to, via, null, ref timeoutHelper);
1112 protected HttpWebRequest GetWebRequest(EndpointAddress to, Uri via, SecurityTokenContainer clientCertificateToken, ref TimeoutHelper timeoutHelper)
1114 SecurityTokenProviderContainer webRequestTokenProvider;
1115 SecurityTokenProviderContainer webRequestProxyTokenProvider;
1116 if (this.ManualAddressing)
1118 this.Factory.CreateAndOpenTokenProviders(to, via, this.channelParameters, timeoutHelper.RemainingTime(),
1119 out webRequestTokenProvider, out webRequestProxyTokenProvider);
1121 else
1123 webRequestTokenProvider = this.tokenProvider;
1124 webRequestProxyTokenProvider = this.proxyTokenProvider;
1128 return this.Factory.GetWebRequest(to, via, webRequestTokenProvider, webRequestProxyTokenProvider, clientCertificateToken, timeoutHelper.RemainingTime(), false);
1130 finally
1132 if (this.ManualAddressing)
1134 if (webRequestTokenProvider != null)
1136 webRequestTokenProvider.Abort();
1138 if (webRequestProxyTokenProvider != null)
1140 webRequestProxyTokenProvider.Abort();
1146 protected IAsyncResult BeginGetWebRequest(
1147 EndpointAddress to, Uri via, SecurityTokenContainer clientCertificateToken, ref TimeoutHelper timeoutHelper, AsyncCallback callback, object state)
1149 return new GetWebRequestAsyncResult(this, to, via, clientCertificateToken, ref timeoutHelper, callback, state);
1152 public virtual IAsyncResult BeginGetWebRequest(
1153 EndpointAddress to, Uri via, ref TimeoutHelper timeoutHelper, AsyncCallback callback, object state)
1155 return BeginGetWebRequest(to, via, null, ref timeoutHelper, callback, state);
1158 public virtual HttpWebRequest EndGetWebRequest(IAsyncResult result)
1160 return GetWebRequestAsyncResult.End(result);
1163 public virtual bool WillGetWebRequestCompleteSynchronously()
1165 return ((this.tokenProvider == null) && !Factory.ManualAddressing);
1168 internal virtual void OnWebRequestCompleted(HttpWebRequest request)
1170 // empty
1173 class HttpChannelRequest : IRequest
1175 HttpRequestChannel channel;
1176 HttpChannelFactory<IRequestChannel> factory;
1177 EndpointAddress to;
1178 Uri via;
1179 HttpWebRequest webRequest;
1180 HttpAbortReason abortReason;
1181 ChannelBinding channelBinding;
1182 int webRequestCompleted;
1183 EventTraceActivity eventTraceActivity;
1184 const string ConnectionGroupPrefixMessagePropertyName = "HttpTransportConnectionGroupNamePrefix";
1186 public HttpChannelRequest(HttpRequestChannel channel, HttpChannelFactory<IRequestChannel> factory)
1188 this.channel = channel;
1189 this.to = channel.RemoteAddress;
1190 this.via = channel.Via;
1191 this.factory = factory;
1194 private string GetConnectionGroupPrefix(Message message)
1196 object property;
1197 if (message.Properties.TryGetValue(ConnectionGroupPrefixMessagePropertyName, out property))
1199 string prefix = property as string;
1200 if (prefix != null)
1202 return prefix;
1206 return string.Empty;
1209 public void SendRequest(Message message, TimeSpan timeout)
1211 TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
1212 factory.ApplyManualAddressing(ref this.to, ref this.via, message);
1213 this.webRequest = channel.GetWebRequest(this.to, this.via, ref timeoutHelper);
1214 this.webRequest.ConnectionGroupName = GetConnectionGroupPrefix(message) + this.webRequest.ConnectionGroupName;
1216 Message request = message;
1220 if (channel.State != CommunicationState.Opened)
1222 // if we were aborted while getting our request or doing correlation,
1223 // we need to abort the web request and bail
1224 Cleanup();
1225 channel.ThrowIfDisposedOrNotOpen();
1228 HttpChannelUtilities.SetRequestTimeout(this.webRequest, timeoutHelper.RemainingTime());
1229 HttpOutput httpOutput = HttpOutput.CreateHttpOutput(this.webRequest, this.factory, request, this.factory.IsChannelBindingSupportEnabled);
1231 bool success = false;
1235 httpOutput.Send(timeoutHelper.RemainingTime());
1237 this.channelBinding = httpOutput.TakeChannelBinding();
1238 httpOutput.Close();
1239 success = true;
1241 if (FxTrace.Trace.IsEnd2EndActivityTracingEnabled)
1243 this.eventTraceActivity = EventTraceActivityHelper.TryExtractActivity(message);
1244 if (TD.MessageSentByTransportIsEnabled())
1246 TD.MessageSentByTransport(eventTraceActivity, this.to.Uri.AbsoluteUri);
1250 finally
1252 if (!success)
1254 httpOutput.Abort(HttpAbortReason.Aborted);
1258 finally
1260 if (!object.ReferenceEquals(request, message))
1262 request.Close();
1267 void Cleanup()
1269 if (this.webRequest != null)
1271 HttpChannelUtilities.AbortRequest(this.webRequest);
1272 this.TryCompleteWebRequest(this.webRequest);
1275 ChannelBindingUtility.Dispose(ref this.channelBinding);
1278 public void Abort(RequestChannel channel)
1280 Cleanup();
1281 abortReason = HttpAbortReason.Aborted;
1284 public void Fault(RequestChannel channel)
1286 Cleanup();
1289 [System.Diagnostics.CodeAnalysis.SuppressMessage(FxCop.Category.ReliabilityBasic, "Reliability104",
1290 Justification = "This is an old method from previous release.")]
1291 public Message WaitForReply(TimeSpan timeout)
1293 if (TD.HttpResponseReceiveStartIsEnabled())
1295 TD.HttpResponseReceiveStart(this.eventTraceActivity);
1298 HttpWebResponse response = null;
1299 WebException responseException = null;
1304 response = (HttpWebResponse)webRequest.GetResponse();
1306 catch (NullReferenceException nullReferenceException)
1308 // workaround for Whidbey bug #558605 - only happens in streamed case.
1309 if (TransferModeHelper.IsRequestStreamed(this.factory.transferMode))
1311 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
1312 HttpChannelUtilities.CreateNullReferenceResponseException(nullReferenceException));
1314 throw;
1317 if (TD.MessageReceivedByTransportIsEnabled())
1319 TD.MessageReceivedByTransport(this.eventTraceActivity ?? EventTraceActivity.Empty,
1320 response.ResponseUri != null ? response.ResponseUri.AbsoluteUri : string.Empty,
1321 EventTraceActivity.GetActivityIdFromThread());
1324 if (DiagnosticUtility.ShouldTraceVerbose)
1326 HttpChannelFactory<TChannel>.TraceResponseReceived(response, null, this);
1329 catch (WebException webException)
1331 responseException = webException;
1332 response = HttpChannelUtilities.ProcessGetResponseWebException(webException, this.webRequest,
1333 abortReason);
1336 HttpInput httpInput = HttpChannelUtilities.ValidateRequestReplyResponse(this.webRequest, response,
1337 this.factory, responseException, this.channelBinding);
1338 this.channelBinding = null;
1340 Message replyMessage = null;
1341 if (httpInput != null)
1343 Exception exception = null;
1344 replyMessage = httpInput.ParseIncomingMessage(out exception);
1345 Fx.Assert(exception == null, "ParseIncomingMessage should not set an exception after parsing a response message.");
1347 if (replyMessage != null)
1349 HttpChannelUtilities.AddReplySecurityProperty(this.factory, this.webRequest, response,
1350 replyMessage);
1352 if (FxTrace.Trace.IsEnd2EndActivityTracingEnabled && (eventTraceActivity != null))
1354 EventTraceActivityHelper.TryAttachActivity(replyMessage, eventTraceActivity);
1359 this.TryCompleteWebRequest(this.webRequest);
1360 return replyMessage;
1363 public void OnReleaseRequest()
1365 this.TryCompleteWebRequest(this.webRequest);
1368 void TryCompleteWebRequest(HttpWebRequest request)
1370 if (request == null)
1372 return;
1375 if (Interlocked.CompareExchange(ref this.webRequestCompleted, 1, 0) == 0)
1377 this.channel.OnWebRequestCompleted(request);
1382 class HttpChannelAsyncRequest : TraceAsyncResult, IAsyncRequest
1384 static AsyncCallback onProcessIncomingMessage = Fx.ThunkCallback(new AsyncCallback(OnParseIncomingMessage));
1385 static AsyncCallback onGetResponse = Fx.ThunkCallback(new AsyncCallback(OnGetResponse));
1386 static AsyncCallback onGetWebRequestCompleted;
1387 static AsyncCallback onSend = Fx.ThunkCallback(new AsyncCallback(OnSend));
1388 static Action<object> onSendTimeout;
1389 ChannelBinding channelBinding;
1391 HttpChannelFactory<IRequestChannel> factory;
1392 HttpRequestChannel channel;
1393 HttpOutput httpOutput;
1394 HttpInput httpInput;
1395 Message message;
1396 Message requestMessage;
1397 Message replyMessage;
1398 HttpWebResponse response;
1399 HttpWebRequest request;
1400 object sendLock = new object();
1401 IOThreadTimer sendTimer;
1402 TimeoutHelper timeoutHelper;
1403 EndpointAddress to;
1404 Uri via;
1405 HttpAbortReason abortReason;
1406 int webRequestCompleted;
1407 EventTraceActivity eventTraceActivity;
1409 public HttpChannelAsyncRequest(HttpRequestChannel channel, AsyncCallback callback, object state)
1410 : base(callback, state)
1412 this.channel = channel;
1413 this.to = channel.RemoteAddress;
1414 this.via = channel.Via;
1415 this.factory = channel.Factory;
1418 IOThreadTimer SendTimer
1422 if (this.sendTimer == null)
1424 if (onSendTimeout == null)
1426 onSendTimeout = new Action<object>(OnSendTimeout);
1429 this.sendTimer = new IOThreadTimer(onSendTimeout, this, false);
1432 return this.sendTimer;
1436 public static void End(IAsyncResult result)
1438 AsyncResult.End<HttpChannelAsyncRequest>(result);
1441 public void BeginSendRequest(Message message, TimeSpan timeout)
1443 this.message = this.requestMessage = message;
1444 this.timeoutHelper = new TimeoutHelper(timeout);
1446 if (FxTrace.Trace.IsEnd2EndActivityTracingEnabled)
1448 this.eventTraceActivity = EventTraceActivityHelper.TryExtractActivity(message);
1451 factory.ApplyManualAddressing(ref this.to, ref this.via, this.requestMessage);
1452 if (this.channel.WillGetWebRequestCompleteSynchronously())
1454 SetWebRequest(channel.GetWebRequest(this.to, this.via, ref this.timeoutHelper));
1455 if (this.SendWebRequest())
1457 base.Complete(true);
1460 else
1462 if (onGetWebRequestCompleted == null)
1464 onGetWebRequestCompleted = Fx.ThunkCallback(
1465 new AsyncCallback(OnGetWebRequestCompletedCallback));
1468 IAsyncResult result = channel.BeginGetWebRequest(
1469 to, via, ref this.timeoutHelper, onGetWebRequestCompleted, this);
1471 if (result.CompletedSynchronously)
1473 if (TD.MessageSentByTransportIsEnabled())
1475 TD.MessageSentByTransport(this.eventTraceActivity, this.to.Uri.AbsoluteUri);
1477 if (this.OnGetWebRequestCompleted(result))
1479 base.Complete(true);
1485 static void OnGetWebRequestCompletedCallback(IAsyncResult result)
1487 if (result.CompletedSynchronously)
1489 return;
1492 HttpChannelAsyncRequest thisPtr = (HttpChannelAsyncRequest)result.AsyncState;
1493 Exception completionException = null;
1494 bool completeSelf;
1497 completeSelf = thisPtr.OnGetWebRequestCompleted(result);
1499 #pragma warning suppress 56500 // Microsoft, transferring exception to another thread
1500 catch (Exception e)
1502 if (Fx.IsFatal(e))
1504 throw;
1506 completeSelf = true;
1507 completionException = e;
1509 if (completeSelf)
1511 thisPtr.Complete(false, completionException);
1515 void AbortSend()
1517 CancelSendTimer();
1518 if (this.request != null)
1520 this.TryCompleteWebRequest(this.request);
1521 this.abortReason = HttpAbortReason.TimedOut;
1522 httpOutput.Abort(this.abortReason);
1526 void CancelSendTimer()
1528 lock (sendLock)
1530 if (this.sendTimer != null)
1532 this.sendTimer.Cancel();
1533 this.sendTimer = null;
1538 bool OnGetWebRequestCompleted(IAsyncResult result)
1540 SetWebRequest(this.channel.EndGetWebRequest(result));
1541 return this.SendWebRequest();
1544 bool SendWebRequest()
1546 this.httpOutput = HttpOutput.CreateHttpOutput(this.request, this.factory, this.requestMessage, this.factory.IsChannelBindingSupportEnabled);
1548 bool success = false;
1551 bool result = false;
1552 SetSendTimeout(timeoutHelper.RemainingTime());
1553 IAsyncResult asyncResult = httpOutput.BeginSend(timeoutHelper.RemainingTime(), onSend, this);
1554 success = true;
1556 if (asyncResult.CompletedSynchronously)
1558 result = CompleteSend(asyncResult);
1561 return result;
1563 finally
1565 if (!success)
1567 this.httpOutput.Abort(HttpAbortReason.Aborted);
1569 if (!object.ReferenceEquals(this.message, this.requestMessage))
1571 this.requestMessage.Close();
1577 bool CompleteSend(IAsyncResult result)
1579 bool success = false;
1582 httpOutput.EndSend(result);
1583 this.channelBinding = httpOutput.TakeChannelBinding();
1584 httpOutput.Close();
1585 success = true;
1586 if (TD.MessageSentByTransportIsEnabled())
1588 TD.MessageSentByTransport(this.eventTraceActivity, this.to.Uri.AbsoluteUri);
1591 finally
1593 if (!success)
1595 httpOutput.Abort(HttpAbortReason.Aborted);
1598 if (!object.ReferenceEquals(this.message, this.requestMessage))
1600 this.requestMessage.Close();
1606 IAsyncResult getResponseResult;
1609 getResponseResult = request.BeginGetResponse(onGetResponse, this);
1611 catch (NullReferenceException nullReferenceException)
1613 // workaround for Whidbey bug #558605 - only happens in streamed case.
1614 if (TransferModeHelper.IsRequestStreamed(this.factory.transferMode))
1616 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
1617 HttpChannelUtilities.CreateNullReferenceResponseException(nullReferenceException));
1619 throw;
1623 if (getResponseResult.CompletedSynchronously)
1625 return CompleteGetResponse(getResponseResult);
1628 return false;
1630 catch (IOException ioException)
1632 throw TraceUtility.ThrowHelperError(new CommunicationException(ioException.Message,
1633 ioException), this.requestMessage);
1635 catch (WebException webException)
1637 throw TraceUtility.ThrowHelperError(new CommunicationException(webException.Message,
1638 webException), this.requestMessage);
1640 catch (ObjectDisposedException objectDisposedException)
1642 if (abortReason == HttpAbortReason.Aborted)
1644 throw TraceUtility.ThrowHelperError(new CommunicationObjectAbortedException(SR.GetString(SR.HttpRequestAborted, to.Uri),
1645 objectDisposedException), this.requestMessage);
1648 throw TraceUtility.ThrowHelperError(new TimeoutException(SR.GetString(SR.HttpRequestTimedOut,
1649 to.Uri, this.timeoutHelper.OriginalTimeout), objectDisposedException), this.requestMessage);
1653 [System.Diagnostics.CodeAnalysis.SuppressMessage(FxCop.Category.ReliabilityBasic, "Reliability104",
1654 Justification = "This is an old method from previous release.")]
1655 bool CompleteGetResponse(IAsyncResult result)
1657 using (ServiceModelActivity.BoundOperation(this.channel.Activity))
1659 HttpWebResponse response = null;
1660 WebException responseException = null;
1665 CancelSendTimer();
1666 response = (HttpWebResponse)request.EndGetResponse(result);
1668 catch (NullReferenceException nullReferenceException)
1670 // workaround for Whidbey bug #558605 - only happens in streamed case.
1671 if (TransferModeHelper.IsRequestStreamed(this.factory.transferMode))
1673 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
1674 HttpChannelUtilities.CreateNullReferenceResponseException(nullReferenceException));
1676 throw;
1679 if (TD.MessageReceivedByTransportIsEnabled())
1681 TD.MessageReceivedByTransport(
1682 this.eventTraceActivity ?? EventTraceActivity.Empty,
1683 this.to.Uri.AbsoluteUri,
1684 EventTraceActivity.GetActivityIdFromThread());
1687 if (DiagnosticUtility.ShouldTraceVerbose)
1689 HttpChannelFactory<TChannel>.TraceResponseReceived(response, this.message, this);
1692 catch (WebException webException)
1694 responseException = webException;
1695 response = HttpChannelUtilities.ProcessGetResponseWebException(webException, request,
1696 abortReason);
1699 return ProcessResponse(response, responseException);
1703 void Cleanup()
1705 if (this.request != null)
1707 HttpChannelUtilities.AbortRequest(this.request);
1708 this.TryCompleteWebRequest(this.request);
1711 ChannelBindingUtility.Dispose(ref this.channelBinding);
1714 void SetSendTimeout(TimeSpan timeout)
1716 // We also set the timeout on the HttpWebRequest so that we can subsequently use it in the
1717 // exception message in the event of a timeout.
1718 HttpChannelUtilities.SetRequestTimeout(this.request, timeout);
1720 if (timeout == TimeSpan.MaxValue)
1722 CancelSendTimer();
1724 else
1726 SendTimer.Set(timeout);
1730 public void Abort(RequestChannel channel)
1732 Cleanup();
1733 abortReason = HttpAbortReason.Aborted;
1736 public void Fault(RequestChannel channel)
1738 Cleanup();
1741 void SetWebRequest(HttpWebRequest webRequest)
1743 this.request = webRequest;
1745 if (channel.State != CommunicationState.Opened)
1747 // if we were aborted while getting our request, we need to abort the web request and bail
1748 Cleanup();
1749 channel.ThrowIfDisposedOrNotOpen();
1753 public Message End()
1755 HttpChannelAsyncRequest.End(this);
1756 return replyMessage;
1759 bool ProcessResponse(HttpWebResponse response, WebException responseException)
1761 this.httpInput = HttpChannelUtilities.ValidateRequestReplyResponse(this.request, response,
1762 this.factory, responseException, this.channelBinding);
1763 this.channelBinding = null;
1765 if (httpInput != null)
1767 this.response = response;
1768 IAsyncResult result =
1769 httpInput.BeginParseIncomingMessage(onProcessIncomingMessage, this);
1770 if (!result.CompletedSynchronously)
1772 return false;
1775 CompleteParseIncomingMessage(result);
1777 else
1779 this.replyMessage = null;
1782 this.TryCompleteWebRequest(this.request);
1783 return true;
1786 void CompleteParseIncomingMessage(IAsyncResult result)
1788 Exception exception = null;
1789 this.replyMessage = this.httpInput.EndParseIncomingMessage(result, out exception);
1790 Fx.Assert(exception == null, "ParseIncomingMessage should not set an exception after parsing a response message.");
1792 if (this.replyMessage != null)
1794 HttpChannelUtilities.AddReplySecurityProperty(this.factory, this.request, this.response,
1795 this.replyMessage);
1799 static void OnParseIncomingMessage(IAsyncResult result)
1801 if (result.CompletedSynchronously)
1803 return;
1806 HttpChannelAsyncRequest thisPtr = (HttpChannelAsyncRequest)result.AsyncState;
1808 Exception completionException = null;
1811 thisPtr.CompleteParseIncomingMessage(result);
1813 #pragma warning suppress 56500 // Microsoft, transferring exception to another thread
1814 catch (Exception e)
1816 if (Fx.IsFatal(e))
1818 throw;
1820 completionException = e;
1822 thisPtr.Complete(false, completionException);
1825 static void OnSend(IAsyncResult result)
1827 if (result.CompletedSynchronously)
1829 return;
1832 HttpChannelAsyncRequest thisPtr = (HttpChannelAsyncRequest)result.AsyncState;
1834 Exception completionException = null;
1835 bool completeSelf;
1838 completeSelf = thisPtr.CompleteSend(result);
1840 #pragma warning suppress 56500 // Microsoft, transferring exception to another thread
1841 catch (Exception e)
1843 if (Fx.IsFatal(e))
1845 throw;
1848 completeSelf = true;
1849 completionException = e;
1851 if (completeSelf)
1853 thisPtr.Complete(false, completionException);
1857 static void OnSendTimeout(object state)
1859 HttpChannelAsyncRequest thisPtr = (HttpChannelAsyncRequest)state;
1860 thisPtr.AbortSend();
1863 [System.Diagnostics.CodeAnalysis.SuppressMessage(FxCop.Category.ReliabilityBasic, "Reliability104",
1864 Justification = "This is an old method from previous release.")]
1865 static void OnGetResponse(IAsyncResult result)
1867 if (result.CompletedSynchronously)
1869 return;
1872 HttpChannelAsyncRequest thisPtr = (HttpChannelAsyncRequest)result.AsyncState;
1874 Exception completionException = null;
1875 bool completeSelf;
1878 completeSelf = thisPtr.CompleteGetResponse(result);
1880 catch (WebException webException)
1882 completeSelf = true;
1883 completionException = new CommunicationException(webException.Message, webException);
1885 #pragma warning suppress 56500 // Microsoft, transferring exception to another thread
1886 catch (Exception e)
1888 if (Fx.IsFatal(e))
1890 throw;
1892 completeSelf = true;
1893 completionException = e;
1895 if (completeSelf)
1897 thisPtr.Complete(false, completionException);
1901 public void OnReleaseRequest()
1903 this.TryCompleteWebRequest(this.request);
1906 void TryCompleteWebRequest(HttpWebRequest request)
1908 if (request == null)
1910 return;
1913 if (Interlocked.CompareExchange(ref this.webRequestCompleted, 1, 0) == 0)
1915 this.channel.OnWebRequestCompleted(request);
1920 class GetWebRequestAsyncResult : AsyncResult
1922 static AsyncCallback onGetSspiCredential;
1923 static AsyncCallback onGetUserNameCredential;
1925 SecurityTokenContainer clientCertificateToken;
1926 HttpChannelFactory<IRequestChannel> factory;
1927 SecurityTokenProviderContainer proxyTokenProvider;
1928 HttpWebRequest request;
1929 EndpointAddress to;
1930 TimeoutHelper timeoutHelper;
1931 SecurityTokenProviderContainer tokenProvider;
1932 Uri via;
1934 public GetWebRequestAsyncResult(HttpRequestChannel channel,
1935 EndpointAddress to, Uri via, SecurityTokenContainer clientCertificateToken, ref TimeoutHelper timeoutHelper,
1936 AsyncCallback callback, object state)
1937 : base(callback, state)
1939 this.to = to;
1940 this.via = via;
1941 this.clientCertificateToken = clientCertificateToken;
1942 this.timeoutHelper = timeoutHelper;
1943 this.factory = channel.Factory;
1944 this.tokenProvider = channel.tokenProvider;
1945 this.proxyTokenProvider = channel.proxyTokenProvider;
1946 if (factory.ManualAddressing)
1948 this.factory.CreateAndOpenTokenProviders(to, via, channel.channelParameters, timeoutHelper.RemainingTime(),
1949 out this.tokenProvider, out this.proxyTokenProvider);
1952 bool completeSelf = false;
1953 IAsyncResult result = null;
1954 if (factory.AuthenticationScheme == AuthenticationSchemes.Anonymous)
1956 SetupWebRequest(AuthenticationLevel.None, TokenImpersonationLevel.None, null);
1957 completeSelf = true;
1959 else if (factory.AuthenticationScheme == AuthenticationSchemes.Basic)
1961 if (onGetUserNameCredential == null)
1963 onGetUserNameCredential = Fx.ThunkCallback(new AsyncCallback(OnGetUserNameCredential));
1966 result = TransportSecurityHelpers.BeginGetUserNameCredential(
1967 tokenProvider, timeoutHelper.RemainingTime(), onGetUserNameCredential, this);
1969 if (result.CompletedSynchronously)
1971 CompleteGetUserNameCredential(result);
1972 completeSelf = true;
1975 else
1977 if (onGetSspiCredential == null)
1979 onGetSspiCredential = Fx.ThunkCallback(new AsyncCallback(OnGetSspiCredential));
1982 result = TransportSecurityHelpers.BeginGetSspiCredential(
1983 tokenProvider, timeoutHelper.RemainingTime(), onGetSspiCredential, this);
1985 if (result.CompletedSynchronously)
1987 CompleteGetSspiCredential(result);
1988 completeSelf = true;
1992 if (completeSelf)
1994 CloseTokenProvidersIfRequired();
1995 base.Complete(true);
1999 public static HttpWebRequest End(IAsyncResult result)
2001 GetWebRequestAsyncResult thisPtr = AsyncResult.End<GetWebRequestAsyncResult>(result);
2002 return thisPtr.request;
2005 void CompleteGetUserNameCredential(IAsyncResult result)
2007 NetworkCredential credential =
2008 TransportSecurityHelpers.EndGetUserNameCredential(result);
2009 SetupWebRequest(AuthenticationLevel.None, TokenImpersonationLevel.None, credential);
2012 void CompleteGetSspiCredential(IAsyncResult result)
2014 AuthenticationLevel authenticationLevel;
2015 TokenImpersonationLevel impersonationLevel;
2016 NetworkCredential credential =
2017 TransportSecurityHelpers.EndGetSspiCredential(result, out impersonationLevel, out authenticationLevel);
2019 if (factory.AuthenticationScheme == AuthenticationSchemes.Digest)
2021 HttpChannelUtilities.ValidateDigestCredential(ref credential, impersonationLevel);
2023 else if (factory.AuthenticationScheme == AuthenticationSchemes.Ntlm)
2025 if (authenticationLevel == AuthenticationLevel.MutualAuthRequired)
2027 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
2028 SR.GetString(SR.CredentialDisallowsNtlm)));
2032 SetupWebRequest(authenticationLevel, impersonationLevel, credential);
2035 void SetupWebRequest(AuthenticationLevel authenticationLevel, TokenImpersonationLevel impersonationLevel, NetworkCredential credential)
2037 this.request = factory.GetWebRequest(to, via, credential, impersonationLevel,
2038 authenticationLevel, this.proxyTokenProvider, this.clientCertificateToken, timeoutHelper.RemainingTime(), false);
2041 void CloseTokenProvidersIfRequired()
2043 if (this.factory.ManualAddressing)
2045 if (this.tokenProvider != null)
2047 tokenProvider.Abort();
2049 if (this.proxyTokenProvider != null)
2051 proxyTokenProvider.Abort();
2056 static void OnGetSspiCredential(IAsyncResult result)
2058 if (result.CompletedSynchronously)
2060 return;
2063 GetWebRequestAsyncResult thisPtr = (GetWebRequestAsyncResult)result.AsyncState;
2065 Exception completionException = null;
2068 thisPtr.CompleteGetSspiCredential(result);
2069 thisPtr.CloseTokenProvidersIfRequired();
2071 #pragma warning suppress 56500 // Microsoft, transferring exception to another thread
2072 catch (Exception e)
2074 if (Fx.IsFatal(e))
2076 throw;
2079 completionException = e;
2081 thisPtr.Complete(false, completionException);
2084 static void OnGetUserNameCredential(IAsyncResult result)
2086 if (result.CompletedSynchronously)
2088 return;
2091 GetWebRequestAsyncResult thisPtr = (GetWebRequestAsyncResult)result.AsyncState;
2093 Exception completionException = null;
2096 thisPtr.CompleteGetUserNameCredential(result);
2097 thisPtr.CloseTokenProvidersIfRequired();
2099 #pragma warning suppress 56500 // Microsoft, transferring exception to another thread
2100 catch (Exception e)
2102 if (Fx.IsFatal(e))
2104 throw;
2107 completionException = e;
2109 thisPtr.Complete(false, completionException);
2114 class WebProxyFactory
2116 Uri address;
2117 bool bypassOnLocal;
2118 AuthenticationSchemes authenticationScheme;
2120 public WebProxyFactory(Uri address, bool bypassOnLocal, AuthenticationSchemes authenticationScheme)
2122 this.address = address;
2123 this.bypassOnLocal = bypassOnLocal;
2125 if (!authenticationScheme.IsSingleton())
2127 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("value", SR.GetString(SR.HttpRequiresSingleAuthScheme,
2128 authenticationScheme));
2131 this.authenticationScheme = authenticationScheme;
2134 internal AuthenticationSchemes AuthenticationScheme
2138 return authenticationScheme;
2142 public IWebProxy CreateWebProxy(HttpWebRequest request, SecurityTokenProviderContainer tokenProvider, TimeSpan timeout)
2144 WebProxy result = new WebProxy(this.address, this.bypassOnLocal);
2146 if (this.authenticationScheme != AuthenticationSchemes.Anonymous)
2148 TokenImpersonationLevel impersonationLevel;
2149 AuthenticationLevel authenticationLevel;
2150 NetworkCredential credential = HttpChannelUtilities.GetCredential(this.authenticationScheme,
2151 tokenProvider, timeout, out impersonationLevel, out authenticationLevel);
2153 // The impersonation level for target auth is also used for proxy auth (by System.Net). Therefore,
2154 // fail if the level stipulated for proxy auth is more restrictive than that for target auth.
2155 if (!TokenImpersonationLevelHelper.IsGreaterOrEqual(impersonationLevel, request.ImpersonationLevel))
2157 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(
2158 SR.ProxyImpersonationLevelMismatch, impersonationLevel, request.ImpersonationLevel)));
2161 // The authentication level for target auth is also used for proxy auth (by System.Net).
2162 // Therefore, fail if proxy auth requires mutual authentication but target auth does not.
2163 if ((authenticationLevel == AuthenticationLevel.MutualAuthRequired) &&
2164 (request.AuthenticationLevel != AuthenticationLevel.MutualAuthRequired))
2166 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(
2167 SR.ProxyAuthenticationLevelMismatch, authenticationLevel, request.AuthenticationLevel)));
2170 CredentialCache credentials = new CredentialCache();
2171 credentials.Add(this.address, AuthenticationSchemesHelper.ToString(this.authenticationScheme),
2172 credential);
2173 result.Credentials = credentials;
2176 return result;