// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. using System.Diagnostics.CodeAnalysis; using System.IdentityModel.Selectors; using System.ServiceModel; using System.ServiceModel.Channels; using System.ServiceModel.Description; using System.ServiceModel.Security; using System.Web.Http.SelfHost.Channels; using System.Web.Http.SelfHost.Properties; using System.Web.Http.SelfHost.ServiceModel; using System.Web.Http.SelfHost.ServiceModel.Channels; namespace System.Web.Http.SelfHost { /// /// The configuration class for Http Services /// public class HttpSelfHostConfiguration : HttpConfiguration { private const int DefaultMaxConcurrentRequests = 100; private const int DefaultMaxBufferSize = 64 * 1024; private const int DefaultReceivedMessageSize = 64 * 1024; private const int PendingContextFactor = 100; private const int MinConcurrentRequests = 1; private const int MinBufferSize = 1; private const int MinReceivedMessageSize = 1; private Uri _baseAddress; private int _maxConcurrentRequests; private ServiceCredentials _credentials = new ServiceCredentials(); private bool _useWindowsAuth; private TransferMode _transferMode; private int _maxBufferSize = DefaultMaxBufferSize; private bool _maxBufferSizeIsInitialized; private long _maxReceivedMessageSize = DefaultReceivedMessageSize; private HostNameComparisonMode _hostNameComparisonMode; /// /// Initializes a new instance of the class. /// /// The base address. public HttpSelfHostConfiguration(string baseAddress) : this(CreateBaseAddress(baseAddress)) { } /// /// Initializes a new instance of the class. /// /// The base address. [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "caller owns object")] public HttpSelfHostConfiguration(Uri baseAddress) : base(new HttpRouteCollection(ValidateBaseAddress(baseAddress).AbsolutePath)) { _baseAddress = ValidateBaseAddress(baseAddress); _maxConcurrentRequests = GetDefaultMaxConcurrentRequests(); _maxBufferSize = TransportDefaults.MaxBufferSize; _maxReceivedMessageSize = TransportDefaults.MaxReceivedMessageSize; } /// /// Gets the base address. /// /// /// The base address. /// public Uri BaseAddress { get { return _baseAddress; } } /// /// Gets or sets the upper limit of how many concurrent instances /// can be processed at any given time. The default is 100 times the number of CPU cores. /// /// /// The maximum concurrent instances processed at any given time. /// public int MaxConcurrentRequests { get { return _maxConcurrentRequests; } set { if (value < MinConcurrentRequests) { throw Error.ArgumentGreaterThanOrEqualTo("value", value, MinConcurrentRequests); } _maxConcurrentRequests = value; } } /// /// Gets or sets the transfer mode. /// /// /// The transfer mode. /// public TransferMode TransferMode { get { return _transferMode; } set { TransferModeHelper.Validate(value); _transferMode = value; } } /// /// Specifies how the host name should be used in URI comparisons when dispatching an incoming message. /// public HostNameComparisonMode HostNameComparisonMode { get { return _hostNameComparisonMode; } set { HostNameComparisonModeHelper.Validate(value); _hostNameComparisonMode = value; } } /// /// Gets or sets the size of the max buffer. /// /// /// The size of the max buffer. /// public int MaxBufferSize { get { if (_maxBufferSizeIsInitialized || TransferMode != TransferMode.Buffered) { return _maxBufferSize; } long maxReceivedMessageSize = MaxReceivedMessageSize; if (maxReceivedMessageSize > Int32.MaxValue) { return Int32.MaxValue; } return (int)maxReceivedMessageSize; } set { if (value < MinBufferSize) { throw Error.ArgumentGreaterThanOrEqualTo("value", value, MinBufferSize); } _maxBufferSizeIsInitialized = true; _maxBufferSize = value; } } /// /// Gets or sets the size of the max received message. /// /// /// The size of the max received message. /// public long MaxReceivedMessageSize { get { return _maxReceivedMessageSize; } set { if (value < MinReceivedMessageSize) { throw Error.ArgumentGreaterThanOrEqualTo("value", value, MinReceivedMessageSize); } _maxReceivedMessageSize = value; } } /// /// Gets or sets UserNamePasswordValidator so that it can be used to validate the username and password /// sent over HTTP or HTTPS /// /// /// The server certificate. /// public UserNamePasswordValidator UserNamePasswordValidator { get { return _credentials.UserNameAuthentication.CustomUserNamePasswordValidator; } set { _credentials.UserNameAuthentication.CustomUserNamePasswordValidator = value; } } /// /// Use this flag to indicate that you want to use windows authentication. This flag can /// not be used together with UserNamePasswordValidator property since you can either use /// Windows or Username Password as client credential. /// /// /// set it true if you want to use windows authentication /// public bool UseWindowsAuthentication { get { return _useWindowsAuth; } set { _useWindowsAuth = value; } } /// /// Internal method called to configure settings. /// /// Http binding. /// The to use when building the or null if no binding parameters are present. internal BindingParameterCollection ConfigureBinding(HttpBinding httpBinding) { return OnConfigureBinding(httpBinding); } /// /// Called to apply the configuration on the endpoint level. /// /// Http endpoint. /// The to use when building the or null if no binding parameters are present. protected virtual BindingParameterCollection OnConfigureBinding(HttpBinding httpBinding) { if (httpBinding == null) { throw Error.ArgumentNull("httpBinding"); } if (_useWindowsAuth && _credentials.UserNameAuthentication.CustomUserNamePasswordValidator != null) { throw Error.InvalidOperation(SRResources.CannotUseWindowsAuthWithUserNamePasswordValidator); } httpBinding.MaxBufferSize = MaxBufferSize; httpBinding.MaxReceivedMessageSize = MaxReceivedMessageSize; httpBinding.TransferMode = TransferMode; httpBinding.HostNameComparisonMode = HostNameComparisonMode; if (_baseAddress.Scheme == Uri.UriSchemeHttps) { // we need to use SSL httpBinding.Security = new HttpBindingSecurity() { Mode = HttpBindingSecurityMode.Transport, }; } // Set up binding parameters if (_credentials.UserNameAuthentication.CustomUserNamePasswordValidator != null) { _credentials.UserNameAuthentication.UserNamePasswordValidationMode = UserNamePasswordValidationMode.Custom; if (httpBinding.Security == null || httpBinding.Security.Mode == HttpBindingSecurityMode.None) { // Basic over HTTP case httpBinding.Security = new HttpBindingSecurity() { Mode = HttpBindingSecurityMode.TransportCredentialOnly, }; } // We have validator, so we can set the client credential type to be basic httpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic; return AddCredentialsToBindingParameters(); } else if (_useWindowsAuth) { if (httpBinding.Security == null || httpBinding.Security.Mode == HttpBindingSecurityMode.None) { // Basic over HTTP case, should we even allow this? httpBinding.Security = new HttpBindingSecurity() { Mode = HttpBindingSecurityMode.TransportCredentialOnly, }; } // We have validator, so we can set the client credential type to be windows httpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows; return AddCredentialsToBindingParameters(); } return null; } private BindingParameterCollection AddCredentialsToBindingParameters() { BindingParameterCollection bindingParameters = new BindingParameterCollection(); bindingParameters.Add(_credentials); return bindingParameters; } private static Uri CreateBaseAddress(string baseAddress) { if (baseAddress == null) { throw Error.ArgumentNull("baseAddress"); } return new Uri(baseAddress, UriKind.RelativeOrAbsolute); } private static Uri ValidateBaseAddress(Uri baseAddress) { if (baseAddress == null) { throw Error.ArgumentNull("baseAddress"); } if (!baseAddress.IsAbsoluteUri) { throw Error.ArgumentUriNotAbsolute("baseAddress", baseAddress); } if (!String.IsNullOrEmpty(baseAddress.Query) || !String.IsNullOrEmpty(baseAddress.Fragment)) { throw Error.ArgumentUriHasQueryOrFragment("baseAddress", baseAddress); } if (!ReferenceEquals(baseAddress.Scheme, Uri.UriSchemeHttp) && !ReferenceEquals(baseAddress.Scheme, Uri.UriSchemeHttps)) { throw Error.ArgumentUriNotHttpOrHttpsScheme("baseAddress", baseAddress); } return baseAddress; } [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "We never want to fail here so we have to catch all exceptions.")] private static int GetDefaultMaxConcurrentRequests() { try { return Math.Max(Environment.ProcessorCount * DefaultMaxConcurrentRequests, DefaultMaxConcurrentRequests); } catch { return DefaultMaxConcurrentRequests; } } } }