// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Net;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Security.Principal;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http.Dispatcher;
using System.Web.Http.Hosting;
using System.Web.Http.Metadata;
using System.Web.Http.Tracing;
using System.Web.Http.Validation;
namespace System.Web.Http
{
///
/// Defines an implementation of an which dispatches an
/// incoming and creates an as a result.
///
public class HttpServer : DelegatingHandler
{
private static readonly Lazy _anonymousPrincipal = new Lazy(() => new GenericPrincipal(new GenericIdentity(String.Empty), new string[0]), isThreadSafe: true);
private readonly HttpConfiguration _configuration;
private readonly HttpMessageHandler _dispatcher;
private bool _disposed;
private bool _initialized = false;
private object _initializationLock = new object();
private object _initializationTarget;
///
/// Initializes a new instance of the class with default configuration and dispatcher.
///
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope",
Justification = "The configuration object is disposed as part of this class.")]
public HttpServer()
: this(new HttpConfiguration())
{
}
///
/// Initializes a new instance of the class with default dispatcher.
///
/// The used to configure this instance.
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope",
Justification = "The configuration object is disposed as part of this class.")]
public HttpServer(HttpConfiguration configuration)
: this(configuration, new HttpControllerDispatcher(configuration))
{
}
///
/// Initializes a new instance of the class with a custom dispatcher.
///
/// Http dispatcher responsible for handling incoming requests.
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope",
Justification = "The configuration object is disposed as part of this class.")]
public HttpServer(HttpControllerDispatcher dispatcher)
: this(new HttpConfiguration(), dispatcher)
{
}
///
/// Initializes a new instance of the class.
///
/// The used to configure this instance.
/// Http dispatcher responsible for handling incoming requests.
public HttpServer(HttpConfiguration configuration, HttpMessageHandler dispatcher)
{
if (configuration == null)
{
throw Error.ArgumentNull("configuration");
}
if (dispatcher == null)
{
throw Error.ArgumentNull("dispatcher");
}
_dispatcher = dispatcher;
_configuration = configuration;
}
///
/// Gets the dispatcher.
///
public HttpMessageHandler Dispatcher
{
get { return _dispatcher; }
}
///
/// Gets the .
///
public HttpConfiguration Configuration
{
get { return _configuration; }
}
///
/// Releases unmanaged and - optionally - managed resources
///
/// true to release both managed and unmanaged resources; false to release only unmanaged SRResources.
protected override void Dispose(bool disposing)
{
if (!_disposed)
{
_disposed = true;
if (disposing)
{
_configuration.Dispose();
}
}
base.Dispose(disposing);
}
///
/// Dispatches an incoming .
///
/// The request to dispatch
/// The cancellation token.
/// A representing the ongoing operation.
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Caller becomes owner.")]
protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request == null)
{
throw Error.ArgumentNull("request");
}
if (_disposed)
{
return TaskHelpers.FromResult(request.CreateResponse(HttpStatusCode.ServiceUnavailable));
}
// The first request initializes the server
EnsureInitialized();
// Capture current synchronization context and add it as a parameter to the request
SynchronizationContext context = SynchronizationContext.Current;
if (context != null)
{
request.Properties.Add(HttpPropertyKeys.SynchronizationContextKey, context);
}
// Add HttpConfiguration object as a parameter to the request
request.Properties.Add(HttpPropertyKeys.HttpConfigurationKey, _configuration);
// Ensure we have a principal, even if the host didn't give us one
IPrincipal originalPrincipal = Thread.CurrentPrincipal;
if (originalPrincipal == null)
{
Thread.CurrentPrincipal = _anonymousPrincipal.Value;
}
return base.SendAsync(request, cancellationToken)
.Finally(() => Thread.CurrentPrincipal = originalPrincipal);
}
private void EnsureInitialized()
{
LazyInitializer.EnsureInitialized(ref _initializationTarget, ref _initialized, ref _initializationLock, () =>
{
Initialize();
// Attach tracing before creating pipeline to allow injection of message handlers
ITraceManager traceManager = _configuration.Services.GetTraceManager();
Contract.Assert(traceManager != null);
traceManager.Initialize(_configuration);
// Create pipeline
InnerHandler = HttpPipelineFactory.Create(_configuration.MessageHandlers, _dispatcher);
return null;
});
}
///
/// Prepares the server for operation.
///
///
/// This method must be called after all configuration is complete
/// but before the first request is processed.
///
protected virtual void Initialize()
{
// Register the default IRequiredMemberSelector for formatters that haven't been assigned one
ModelMetadataProvider metadataProvider = _configuration.Services.GetModelMetadataProvider();
IEnumerable validatorProviders = _configuration.Services.GetModelValidatorProviders();
IRequiredMemberSelector defaultRequiredMemberSelector = new ModelValidationRequiredMemberSelector(metadataProvider, validatorProviders);
foreach (MediaTypeFormatter formatter in _configuration.Formatters)
{
if (formatter.RequiredMemberSelector == null)
{
formatter.RequiredMemberSelector = defaultRequiredMemberSelector;
}
}
}
}
}