Imported Upstream version 3.6.0

Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
This commit is contained in:
Jo Shields
2014-08-13 10:39:27 +01:00
commit a575963da9
50588 changed files with 8155799 additions and 0 deletions

View File

@@ -0,0 +1,222 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Web.Http.SelfHost.ServiceModel;
using System.Web.Http.SelfHost.ServiceModel.Channels;
namespace System.Web.Http.SelfHost.Channels
{
/// <summary>
/// A binding used with endpoints for web services that use strongly-type HTTP request
/// and response messages.
/// </summary>
public class HttpBinding : Binding, IBindingRuntimePreferences
{
internal const string CollectionElementName = "httpBinding";
internal const TransferMode DefaultTransferMode = System.ServiceModel.TransferMode.Buffered;
private HttpsTransportBindingElement _httpsTransportBindingElement;
private HttpTransportBindingElement _httpTransportBindingElement;
private HttpBindingSecurity _security;
private HttpMessageEncodingBindingElement _httpMessageEncodingBindingElement;
/// <summary>
/// Initializes a new instance of the <see cref="HttpBinding"/> class.
/// </summary>
public HttpBinding()
{
Initialize();
}
/// <summary>
/// Initializes a new instance of the <see cref="HttpBinding"/> class with the
/// type of security used by the binding explicitly specified.
/// </summary>
/// <param name="securityMode">The value of <see cref="HttpBindingSecurityMode"/> that
/// specifies the type of security that is used to configure a service endpoint using the
/// <see cref="HttpBinding"/> binding.
/// </param>
public HttpBinding(HttpBindingSecurityMode securityMode)
: this()
{
_security.Mode = securityMode;
}
/// <summary>
/// Gets the envelope version that is used by endpoints that are configured to use an
/// <see cref="HttpBinding"/> binding. Always returns <see cref="System.ServiceModel.EnvelopeVersion.None"/>.
/// </summary>
[SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "This is existing public API")]
public EnvelopeVersion EnvelopeVersion
{
get { return EnvelopeVersion.None; }
}
/// <summary>
/// Gets or sets a value that indicates whether the hostname is used to reach the
/// service when matching the URI.
/// </summary>
[DefaultValue(HttpTransportDefaults.HostNameComparisonMode)]
public HostNameComparisonMode HostNameComparisonMode
{
get { return _httpTransportBindingElement.HostNameComparisonMode; }
set
{
_httpTransportBindingElement.HostNameComparisonMode = value;
_httpsTransportBindingElement.HostNameComparisonMode = value;
}
}
/// <summary>
/// Gets or sets the maximum amount of memory allocated for the buffer manager that manages the buffers
/// required by endpoints that use this binding.
/// </summary>
[DefaultValue(TransportDefaults.MaxBufferPoolSize)]
public long MaxBufferPoolSize
{
get { return _httpTransportBindingElement.MaxBufferPoolSize; }
set
{
_httpTransportBindingElement.MaxBufferPoolSize = value;
_httpsTransportBindingElement.MaxBufferPoolSize = value;
}
}
/// <summary>
/// Gets or sets the maximum amount of memory that is allocated for use by the manager of the message
/// buffers that receive messages from the channel.
/// </summary>
[DefaultValue(TransportDefaults.MaxBufferSize)]
public int MaxBufferSize
{
get { return _httpTransportBindingElement.MaxBufferSize; }
set
{
_httpTransportBindingElement.MaxBufferSize = value;
_httpsTransportBindingElement.MaxBufferSize = value;
}
}
/// <summary>
/// Gets or sets the maximum size for a message that can be processed by the binding.
/// </summary>
[DefaultValue(TransportDefaults.MaxReceivedMessageSize)]
public long MaxReceivedMessageSize
{
get { return _httpTransportBindingElement.MaxReceivedMessageSize; }
set
{
_httpTransportBindingElement.MaxReceivedMessageSize = value;
_httpsTransportBindingElement.MaxReceivedMessageSize = value;
}
}
/// <summary>
/// Gets the URI transport scheme for the channels and listeners that are configured
/// with this binding. (Overrides <see cref="System.ServiceModel.Channels.Binding.Scheme">
/// Binding.Scheme</see>.)
/// </summary>
public override string Scheme
{
get { return GetTransport().Scheme; }
}
/// <summary>
/// Gets or sets the security settings used with this binding.
/// </summary>
public HttpBindingSecurity Security
{
get { return _security; }
set
{
if (value == null)
{
throw Error.ArgumentNull("value");
}
_security = value;
}
}
/// <summary>
/// Gets or sets a value that indicates whether the service configured with the
/// binding uses streamed or buffered (or both) modes of message transfer.
/// </summary>
[DefaultValue(HttpTransportDefaults.TransferMode)]
public TransferMode TransferMode
{
get { return _httpTransportBindingElement.TransferMode; }
set
{
_httpTransportBindingElement.TransferMode = value;
_httpsTransportBindingElement.TransferMode = value;
}
}
/// <summary>
/// Gets a value indicating whether incoming requests can be handled more efficiently synchronously or asynchronously.
/// </summary>
[SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Justification = "This is the pattern used by all standard bindings.")]
bool IBindingRuntimePreferences.ReceiveSynchronously
{
get { return false; }
}
/// <summary>
/// Returns an ordered collection of binding elements contained in the current binding.
/// (Overrides <see cref="System.ServiceModel.Channels.Binding.CreateBindingElements">
/// Binding.CreateBindingElements</see>.)
/// </summary>
/// <returns>
/// An ordered collection of binding elements contained in the current binding.
/// </returns>
public override BindingElementCollection CreateBindingElements()
{
BindingElementCollection bindingElements = new BindingElementCollection();
bindingElements.Add(_httpMessageEncodingBindingElement);
bindingElements.Add(GetTransport());
return bindingElements.Clone();
}
private TransportBindingElement GetTransport()
{
if (_security.Mode == HttpBindingSecurityMode.Transport)
{
_security.Transport.ConfigureTransportProtectionAndAuthentication(_httpsTransportBindingElement);
return _httpsTransportBindingElement;
}
else if (_security.Mode == HttpBindingSecurityMode.TransportCredentialOnly)
{
_security.Transport.ConfigureTransportAuthentication(_httpTransportBindingElement);
return _httpTransportBindingElement;
}
_security.Transport.DisableTransportAuthentication(_httpTransportBindingElement);
return _httpTransportBindingElement;
}
private void Initialize()
{
_security = new HttpBindingSecurity();
_httpTransportBindingElement = new HttpTransportBindingElement();
_httpTransportBindingElement.ManualAddressing = true;
_httpsTransportBindingElement = new HttpsTransportBindingElement();
_httpsTransportBindingElement.ManualAddressing = true;
_httpMessageEncodingBindingElement = new HttpMessageEncodingBindingElement();
}
}
}

View File

@@ -0,0 +1,56 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.ServiceModel;
namespace System.Web.Http.SelfHost.Channels
{
/// <summary>
/// Specifies the types of security available to a service endpoint configured to use an
/// <see cref="HttpBinding"/> binding.
/// </summary>
public sealed class HttpBindingSecurity
{
internal const HttpBindingSecurityMode DefaultMode = HttpBindingSecurityMode.None;
private HttpBindingSecurityMode _mode;
private HttpTransportSecurity _transportSecurity;
/// <summary>
/// Creates a new instance of the <see cref="HttpBindingSecurity"/> class.
/// </summary>
public HttpBindingSecurity()
{
_mode = DefaultMode;
_transportSecurity = new HttpTransportSecurity();
}
/// <summary>
/// Gets or sets the mode of security that is used by an endpoint configured to use an
/// <see cref="HttpBinding"/> binding.
/// </summary>
public HttpBindingSecurityMode Mode
{
get { return _mode; }
set
{
HttpBindingSecurityModeHelper.Validate(value, "value");
IsModeSet = true;
_mode = value;
}
}
/// <summary>
/// Gets or sets an object that contains the transport-level security settings for the
/// <see cref="HttpBinding"/> binding.
/// </summary>
public HttpTransportSecurity Transport
{
get { return _transportSecurity; }
set { _transportSecurity = value ?? new HttpTransportSecurity(); }
}
internal bool IsModeSet { get; private set; }
}
}

View File

@@ -0,0 +1,26 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
namespace System.Web.Http.SelfHost.Channels
{
/// <summary>
/// Defines the modes of security that can be used to configure a service endpoint that uses the
/// <see cref="HttpBinding"/>.
/// </summary>
public enum HttpBindingSecurityMode
{
/// <summary>
/// Indicates no security is used with HTTP requests.
/// </summary>
None,
/// <summary>
/// Indicates that transport-level security is used with HTTP requests.
/// </summary>
Transport,
/// <summary>
/// Indicates that only HTTP-based client authentication is provided.
/// </summary>
TransportCredentialOnly,
}
}

View File

@@ -0,0 +1,41 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.ComponentModel;
namespace System.Web.Http.SelfHost.Channels
{
/// <summary>
/// Internal helper class to validate <see cref="HttpBindingSecurityMode"/> enum values.
/// </summary>
internal static class HttpBindingSecurityModeHelper
{
private static readonly Type _httpBindingSecurityMode = typeof(HttpBindingSecurityMode);
/// <summary>
/// Determines whether the specified <paramref name="value"/> is defined by the <see cref="HttpBindingSecurityMode"/>
/// enumeration.
/// </summary>
/// <param name="value">The value to test.</param>
/// <returns><c>true</c> if <paramref name="value"/> is a valid <see cref="HttpBindingSecurityMode"/> value; otherwise<c> false</c>.</returns>
public static bool IsDefined(HttpBindingSecurityMode value)
{
return value == HttpBindingSecurityMode.None ||
value == HttpBindingSecurityMode.Transport ||
value == HttpBindingSecurityMode.TransportCredentialOnly;
}
/// <summary>
/// Validates the specified <paramref name="value"/> and throws an <see cref="InvalidEnumArgumentException"/>
/// exception if not valid.
/// </summary>
/// <param name="value">The value to validate.</param>
/// <param name="parameterName">Name of the parameter to use if throwing exception.</param>
public static void Validate(HttpBindingSecurityMode value, string parameterName)
{
if (!IsDefined(value))
{
throw Error.InvalidEnumArgument(parameterName, (int)value, _httpBindingSecurityMode);
}
}
}
}

View File

@@ -0,0 +1,229 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Diagnostics.Contracts;
using System.Net.Http;
using System.ServiceModel.Channels;
using System.Web.Http.SelfHost.Properties;
using System.Xml;
namespace System.Web.Http.SelfHost.Channels
{
internal sealed class HttpMessage : Message
{
private HttpRequestMessage _request;
private HttpResponseMessage _response;
private MessageHeaders _headers;
private MessageProperties _properties;
public HttpMessage(HttpRequestMessage request)
{
Contract.Assert(request != null, "The 'request' parameter should not be null.");
_request = request;
Headers.To = request.RequestUri;
IsRequest = true;
}
public HttpMessage(HttpResponseMessage response)
{
Contract.Assert(response != null, "The 'response' parameter should not be null.");
_response = response;
IsRequest = false;
}
public override MessageVersion Version
{
get
{
EnsureNotDisposed();
return MessageVersion.None;
}
}
public override MessageHeaders Headers
{
get
{
EnsureNotDisposed();
if (_headers == null)
{
_headers = new MessageHeaders(MessageVersion.None);
}
return _headers;
}
}
public override MessageProperties Properties
{
get
{
EnsureNotDisposed();
if (_properties == null)
{
_properties = new MessageProperties();
_properties.AllowOutputBatching = false;
}
return _properties;
}
}
public override bool IsEmpty
{
get
{
long? contentLength = GetHttpContentLength();
return contentLength.HasValue && contentLength.Value == 0;
}
}
public override bool IsFault
{
get { return false; }
}
public bool IsRequest { get; private set; }
public HttpRequestMessage GetHttpRequestMessage(bool extract)
{
EnsureNotDisposed();
Contract.Assert(IsRequest, "This method should only be called when IsRequest is true.");
if (extract)
{
HttpRequestMessage req = _request;
_request = null;
return req;
}
return _request;
}
public HttpResponseMessage GetHttpResponseMessage(bool extract)
{
EnsureNotDisposed();
Contract.Assert(!IsRequest, "This method should only be called when IsRequest is false.");
if (extract)
{
HttpResponseMessage res = _response;
_response = null;
return res;
}
return _response;
}
protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
{
throw Error.NotSupported(GetNotSupportedMessage());
}
protected override MessageBuffer OnCreateBufferedCopy(int maxBufferSize)
{
throw Error.NotSupported(GetNotSupportedMessage());
}
protected override XmlDictionaryReader OnGetReaderAtBodyContents()
{
throw Error.NotSupported(GetNotSupportedMessage());
}
protected override string OnGetBodyAttribute(string localName, string ns)
{
throw Error.NotSupported(GetNotSupportedMessage());
}
protected override void OnWriteStartBody(XmlDictionaryWriter writer)
{
throw Error.NotSupported(GetNotSupportedMessage());
}
protected override void OnWriteStartEnvelope(XmlDictionaryWriter writer)
{
throw Error.NotSupported(GetNotSupportedMessage());
}
protected override void OnBodyToString(XmlDictionaryWriter writer)
{
if (writer == null)
{
throw Error.ArgumentNull("writer");
}
long? contentLength = GetHttpContentLength();
string contentString = null;
if (IsRequest)
{
contentString = contentLength.HasValue
? Error.Format(SRResources.MessageBodyIsHttpRequestMessageWithKnownContentLength, contentLength.Value)
: SRResources.MessageBodyIsHttpRequestMessageWithUnknownContentLength;
}
else
{
contentString = contentLength.HasValue
? Error.Format(SRResources.MessageBodyIsHttpResponseMessageWithKnownContentLength, contentLength.Value)
: SRResources.MessageBodyIsHttpResponseMessageWithUnknownContentLength;
}
writer.WriteString(contentString);
}
protected override void OnWriteMessage(XmlDictionaryWriter writer)
{
throw Error.NotSupported(GetNotSupportedMessage());
}
protected override void OnWriteStartHeaders(XmlDictionaryWriter writer)
{
throw Error.NotSupported(GetNotSupportedMessage());
}
protected override void OnClose()
{
base.OnClose();
if (_request != null)
{
_request.DisposeRequestResources();
_request.Dispose();
_request = null;
}
if (_response != null)
{
_response.Dispose();
_response = null;
}
}
private static string GetNotSupportedMessage()
{
return Error.Format(
SRResources.MessageReadWriteCopyNotSupported,
HttpMessageExtensions.ToHttpRequestMessageMethodName,
HttpMessageExtensions.ToHttpResponseMessageMethodName,
typeof(HttpMessage).Name);
}
private void EnsureNotDisposed()
{
if (IsDisposed)
{
throw Error.ObjectDisposed(SRResources.MessageClosed, typeof(Message).Name);
}
}
private long? GetHttpContentLength()
{
HttpContent content = IsRequest
? GetHttpRequestMessage(false).Content
: GetHttpResponseMessage(false).Content;
if (content == null)
{
return 0;
}
return content.Headers.ContentLength;
}
}
}

View File

@@ -0,0 +1,270 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.IO;
using System.Net.Http;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http.SelfHost.Properties;
using System.Web.Http.SelfHost.ServiceModel.Channels;
namespace System.Web.Http.SelfHost.Channels
{
internal class HttpMessageEncoderFactory : MessageEncoderFactory
{
private HttpMessageEncoder _encoder;
public HttpMessageEncoderFactory()
{
_encoder = new HttpMessageEncoder();
}
public override MessageEncoder Encoder
{
get { return _encoder; }
}
public override MessageVersion MessageVersion
{
get { return MessageVersion.None; }
}
public override MessageEncoder CreateSessionEncoder()
{
throw Error.NotSupported(SRResources.HttpMessageEncoderFactoryDoesNotSupportSessionEncoder, typeof(HttpMessageEncoderFactory).Name);
}
private class HttpMessageEncoder : MessageEncoder
{
private const string ContentTypeHeaderName = "Content-Type";
private const string MaxSentMessageSizeExceededResourceStringName = "MaxSentMessageSizeExceeded";
private static readonly string _httpBindingClassName = typeof(HttpBinding).FullName;
private static readonly string _httpResponseMessageClassName = typeof(HttpResponseMessage).FullName;
public override string ContentType
{
get { return String.Empty; }
}
public override string MediaType
{
get { return String.Empty; }
}
public override MessageVersion MessageVersion
{
get { return MessageVersion.None; }
}
public override bool IsContentTypeSupported(string contentType)
{
if (contentType == null)
{
throw Error.ArgumentNull("contentType");
}
return true;
}
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "disposed later.")]
public override Message ReadMessage(ArraySegment<byte> buffer, BufferManager bufferManager, string contentType)
{
if (bufferManager == null)
{
throw Error.ArgumentNull("bufferManager");
}
HttpRequestMessage request = new HttpRequestMessage();
request.Content = new ByteArrayBufferManagerContent(bufferManager, buffer.Array, buffer.Offset, buffer.Count);
if (!String.IsNullOrEmpty(contentType))
{
request.Content.Headers.Add(ContentTypeHeaderName, contentType);
}
Message message = request.ToMessage();
message.Properties.Encoder = this;
return message;
}
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "disposed later.")]
public override Message ReadMessage(Stream stream, int maxSizeOfHeaders, string contentType)
{
if (stream == null)
{
throw Error.ArgumentNull("stream");
}
HttpRequestMessage request = new HttpRequestMessage();
request.Content = new StreamContent(stream);
if (!String.IsNullOrEmpty(contentType))
{
request.Content.Headers.Add(ContentTypeHeaderName, contentType);
}
Message message = request.ToMessage();
message.Properties.Encoder = this;
return message;
}
public override ArraySegment<byte> WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset)
{
if (message == null)
{
throw Error.ArgumentNull("message");
}
if (bufferManager == null)
{
throw Error.ArgumentNull("bufferManager");
}
if (maxMessageSize < 0)
{
throw Error.ArgumentOutOfRange("maxMessageSize", maxMessageSize, SRResources.NonnegativeNumberRequired);
}
if (messageOffset < 0)
{
throw Error.ArgumentOutOfRange("messageOffset", messageOffset, SRResources.NonnegativeNumberRequired);
}
if (messageOffset > maxMessageSize)
{
throw Error.Argument(String.Empty, SRResources.ParameterMustBeLessThanOrEqualSecondParameter, "messageOffset", "maxMessageSize");
}
// TODO: DevDiv2 bug #378887 -- find out how to eliminate this middle buffer
using (BufferManagerOutputStream stream = new BufferManagerOutputStream(MaxSentMessageSizeExceededResourceStringName, 0, maxMessageSize, bufferManager))
{
int num;
stream.Skip(messageOffset);
WriteMessage(message, stream);
byte[] buffer = stream.ToArray(out num);
ArraySegment<byte> messageData = new ArraySegment<byte>(buffer, 0, num - messageOffset);
// ToArray transfers full ownership of buffer to us, meaning we are responsible for returning it to BufferManager.
// But we must delay that release until WCF has finished with the buffer we are returning from this method.
HttpMessageEncodingRequestContext requestContext = HttpMessageEncodingRequestContext.GetContextFromMessage(message);
Contract.Assert(requestContext != null);
requestContext.BufferManager = bufferManager;
requestContext.BufferToReturn = buffer;
return messageData;
}
}
[SuppressMessage("Microsoft.WebAPI", "CR4001:DoNotCallProblematicMethodsOnTask", Justification = "The WriteMessage() API is synchronous, and Wait() won't deadlock in self-host.")]
public override void WriteMessage(Message message, Stream stream)
{
if (message == null)
{
throw Error.ArgumentNull("message");
}
if (stream == null)
{
throw Error.ArgumentNull("stream");
}
ThrowIfMismatchedMessageVersion(message);
message.Properties.Encoder = this;
HttpResponseMessage response = GetHttpResponseMessageOrThrow(message);
if (response.Content != null)
{
HttpMessageEncodingRequestContext requestContext =
HttpMessageEncodingRequestContext.GetContextFromMessage(message);
try
{
response.Content.CopyToAsync(stream)
.Catch((info) =>
{
if (requestContext != null)
{
requestContext.Exception = info.Exception;
}
return info.Throw();
})
.Wait();
}
catch (Exception ex)
{
if (requestContext != null)
{
requestContext.Exception = ex;
}
throw;
}
}
}
internal void ThrowIfMismatchedMessageVersion(Message message)
{
if (message.Version != MessageVersion)
{
throw new ProtocolException(Error.Format(SRResources.EncoderMessageVersionMismatch, message.Version, MessageVersion));
}
}
private static HttpResponseMessage GetHttpResponseMessageOrThrow(Message message)
{
HttpResponseMessage response = message.ToHttpResponseMessage();
if (response == null)
{
throw Error.InvalidOperation(
SRResources.MessageInvalidForHttpMessageEncoder,
_httpBindingClassName,
HttpMessageExtensions.ToMessageMethodName,
_httpResponseMessageClassName);
}
return response;
}
private class ByteArrayBufferManagerContent : ByteArrayContent
{
private BufferManager _bufferManager;
private byte[] _content;
public ByteArrayBufferManagerContent(BufferManager bufferManager, byte[] content, int offset, int count)
: base(content, offset, count)
{
Contract.Assert(bufferManager != null, "The 'bufferManager' parameter should never be null.");
_bufferManager = bufferManager;
_content = content;
}
protected override void Dispose(bool disposing)
{
try
{
if (disposing)
{
BufferManager oldBufferManager = Interlocked.Exchange(ref _bufferManager, null);
if (oldBufferManager != null)
{
oldBufferManager.ReturnBuffer(_content);
_content = null;
}
}
}
finally
{
base.Dispose(disposing);
}
}
}
}
}
}

View File

@@ -0,0 +1,140 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Diagnostics.CodeAnalysis;
using System.ServiceModel.Channels;
using System.Web.Http.SelfHost.Properties;
namespace System.Web.Http.SelfHost.Channels
{
/// <summary>
/// Provides an <see cref="HttpMessageEncoderFactory"/> that returns a <see cref="MessageEncoder"/>
/// that is able to produce and consume <see cref="HttpMessage"/> instances.
/// </summary>
internal sealed class HttpMessageEncodingBindingElement : MessageEncodingBindingElement
{
private static readonly Type _replyChannelType = typeof(IReplyChannel);
/// <summary>
/// Gets or sets the message version that can be handled by the message encoders produced by the message encoder factory.
/// </summary>
/// <returns>The <see cref="MessageVersion"/> used by the encoders produced by the message encoder factory.</returns>
public override MessageVersion MessageVersion
{
get { return MessageVersion.None; }
set
{
if (value == null)
{
throw Error.ArgumentNull("value");
}
if (value != MessageVersion.None)
{
throw Error.NotSupported(SRResources.OnlyMessageVersionNoneSupportedOnHttpMessageEncodingBindingElement, typeof(HttpMessageEncodingBindingElement).Name);
}
}
}
/// <summary>
/// Returns a value that indicates whether the binding element can build a listener for a specific type of channel.
/// </summary>
/// <typeparam name="TChannel">The type of channel the listener accepts.</typeparam>
/// <param name="context">The <see cref="BindingContext"/> that provides context for the binding element</param>
/// <returns>true if the <see cref="IChannelListener{TChannel}"/> of type <see cref="IChannel"/> can be built by the binding element; otherwise, false.</returns>
[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "Existing public API")]
public override bool CanBuildChannelFactory<TChannel>(BindingContext context)
{
return false;
}
/// <summary>
/// Returns a value that indicates whether the binding element can build a channel factory for a specific type of channel.
/// </summary>
/// <typeparam name="TChannel">The type of channel the channel factory produces.</typeparam>
/// <param name="context">The <see cref="BindingContext"/> that provides context for the binding element</param>
/// <returns>ALways false.</returns>
[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "Existing public API")]
public override IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingContext context)
{
throw Error.NotSupported(SRResources.ChannelFactoryNotSupported, typeof(HttpMessageEncodingBindingElement).Name, typeof(IChannelFactory).Name);
}
/// <summary>
/// Returns a value that indicates whether the binding element can build a channel factory for a specific type of channel.
/// </summary>
/// <typeparam name="TChannel">The type of channel the channel factory produces.</typeparam>
/// <param name="context">The <see cref="BindingContext"/> that provides context for the binding element</param>
/// <returns>ALways false.</returns>
[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "Existing public API")]
public override bool CanBuildChannelListener<TChannel>(BindingContext context)
{
if (context == null)
{
throw Error.ArgumentNull("context");
}
context.BindingParameters.Add(this);
return IsChannelShapeSupported<TChannel>() && context.CanBuildInnerChannelListener<TChannel>();
}
/// <summary>
/// Initializes a channel listener to accept channels of a specified type from the binding context.
/// </summary>
/// <typeparam name="TChannel">The type of channel the listener is built to accept.</typeparam>
/// <param name="context">The <see cref="BindingContext"/> that provides context for the binding element</param>
/// <returns>The <see cref="IChannelListener{TChannel}"/> of type <see cref="IChannel"/> initialized from the context.</returns>
[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "Existing public API")]
public override IChannelListener<TChannel> BuildChannelListener<TChannel>(BindingContext context)
{
if (context == null)
{
throw Error.ArgumentNull("context");
}
if (!IsChannelShapeSupported<TChannel>())
{
throw Error.NotSupported(SRResources.ChannelShapeNotSupported, typeof(HttpMessageEncodingBindingElement).Name, typeof(IReplyChannel).Name);
}
context.BindingParameters.Add(this);
IChannelListener<IReplyChannel> innerListener = context.BuildInnerChannelListener<IReplyChannel>();
if (innerListener == null)
{
return null;
}
return (IChannelListener<TChannel>)new HttpMessageEncodingChannelListener(context.Binding, innerListener);
}
/// <summary>
/// Returns a copy of the binding element object.
/// </summary>
/// <returns>A <see cref="BindingElement"/> object that is a deep clone of the original.</returns>
public override BindingElement Clone()
{
return new HttpMessageEncodingBindingElement();
}
/// <summary>
/// Creates a factory for producing message encoders that are able to
/// produce and consume <see cref="HttpMessage"/> instances.
/// </summary>
/// <returns>
/// The <see cref="MessageEncoderFactory"/> used to produce message encoders that are able to
/// produce and consume <see cref="HttpMessage"/> instances.
/// </returns>
public override MessageEncoderFactory CreateMessageEncoderFactory()
{
return new HttpMessageEncoderFactory();
}
private static bool IsChannelShapeSupported<TChannel>()
{
return typeof(TChannel) == _replyChannelType;
}
}
}

View File

@@ -0,0 +1,62 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.ServiceModel.Channels;
using System.Web.Http.SelfHost.ServiceModel.Channels;
namespace System.Web.Http.SelfHost.Channels
{
internal class HttpMessageEncodingChannelListener : LayeredChannelListener<IReplyChannel>
{
private IChannelListener<IReplyChannel> _innerChannelListener;
public HttpMessageEncodingChannelListener(Binding binding, IChannelListener<IReplyChannel> innerListener) :
base(binding, innerListener)
{
}
protected override void OnOpening()
{
_innerChannelListener = (IChannelListener<IReplyChannel>)InnerChannelListener;
base.OnOpening();
}
protected override IReplyChannel OnAcceptChannel(TimeSpan timeout)
{
IReplyChannel innerChannel = _innerChannelListener.AcceptChannel(timeout);
return WrapInnerChannel(innerChannel);
}
protected override IAsyncResult OnBeginAcceptChannel(TimeSpan timeout, AsyncCallback callback, object state)
{
return _innerChannelListener.BeginAcceptChannel(timeout, callback, state);
}
protected override IReplyChannel OnEndAcceptChannel(IAsyncResult result)
{
IReplyChannel innerChannel = _innerChannelListener.EndAcceptChannel(result);
return WrapInnerChannel(innerChannel);
}
protected override IAsyncResult OnBeginWaitForChannel(TimeSpan timeout, AsyncCallback callback, object state)
{
return _innerChannelListener.BeginWaitForChannel(timeout, callback, state);
}
protected override bool OnEndWaitForChannel(IAsyncResult result)
{
return _innerChannelListener.EndWaitForChannel(result);
}
protected override bool OnWaitForChannel(TimeSpan timeout)
{
return _innerChannelListener.WaitForChannel(timeout);
}
private IReplyChannel WrapInnerChannel(IReplyChannel innerChannel)
{
return (innerChannel != null)
? new HttpMessageEncodingReplyChannel(this, innerChannel)
: (IReplyChannel)null;
}
}
}

View File

@@ -0,0 +1,104 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Diagnostics.CodeAnalysis;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Web.Http.SelfHost.ServiceModel.Channels;
namespace System.Web.Http.SelfHost.Channels
{
internal class HttpMessageEncodingReplyChannel : LayeredChannel<IReplyChannel>, IReplyChannel, IChannel, ICommunicationObject
{
public HttpMessageEncodingReplyChannel(ChannelManagerBase channelManager, IReplyChannel innerChannel)
: base(channelManager, innerChannel)
{
}
public EndpointAddress LocalAddress
{
get { return InnerChannel.LocalAddress; }
}
public IAsyncResult BeginReceiveRequest(AsyncCallback callback, object state)
{
return InnerChannel.BeginReceiveRequest(callback, state);
}
public IAsyncResult BeginReceiveRequest(TimeSpan timeout, AsyncCallback callback, object state)
{
return InnerChannel.BeginReceiveRequest(timeout, callback, state);
}
public IAsyncResult BeginTryReceiveRequest(TimeSpan timeout, AsyncCallback callback, object state)
{
return InnerChannel.BeginTryReceiveRequest(timeout, callback, state);
}
public IAsyncResult BeginWaitForRequest(TimeSpan timeout, AsyncCallback callback, object state)
{
return InnerChannel.BeginWaitForRequest(timeout, callback, state);
}
public RequestContext EndReceiveRequest(IAsyncResult result)
{
RequestContext innerContext = InnerChannel.EndReceiveRequest(result);
return WrapRequestContext(innerContext);
}
public bool EndTryReceiveRequest(IAsyncResult result, out RequestContext context)
{
RequestContext innerContext;
context = null;
if (!InnerChannel.EndTryReceiveRequest(result, out innerContext))
{
return false;
}
context = WrapRequestContext(innerContext);
return true;
}
public bool EndWaitForRequest(IAsyncResult result)
{
return InnerChannel.EndWaitForRequest(result);
}
public RequestContext ReceiveRequest()
{
RequestContext innerContext = InnerChannel.ReceiveRequest();
return WrapRequestContext(innerContext);
}
public RequestContext ReceiveRequest(TimeSpan timeout)
{
RequestContext innerContext = InnerChannel.ReceiveRequest(timeout);
return WrapRequestContext(innerContext);
}
public bool TryReceiveRequest(TimeSpan timeout, out RequestContext context)
{
RequestContext innerContext;
if (InnerChannel.TryReceiveRequest(timeout, out innerContext))
{
context = WrapRequestContext(innerContext);
return true;
}
context = null;
return false;
}
public bool WaitForRequest(TimeSpan timeout)
{
return InnerChannel.WaitForRequest(timeout);
}
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "disposed later.")]
private static RequestContext WrapRequestContext(RequestContext innerContext)
{
return (innerContext != null)
? new HttpMessageEncodingRequestContext(innerContext)
: (RequestContext)null;
}
}
}

View File

@@ -0,0 +1,275 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.ServiceModel.Channels;
using System.Web.Http.SelfHost.Properties;
namespace System.Web.Http.SelfHost.Channels
{
internal class HttpMessageEncodingRequestContext : RequestContext
{
private const string HttpMessageEncodingRequestContextPropertyName = "MS_HttpMessageEncodingRequestContextKey";
private const string DefaultReasonPhrase = "OK";
private RequestContext _innerContext;
private Message _configuredRequestMessage;
public HttpMessageEncodingRequestContext(RequestContext innerContext)
{
Contract.Assert(innerContext != null, "The 'innerContext' parameter should not be null.");
_innerContext = innerContext;
}
internal Exception Exception { get; set; }
internal BufferManager BufferManager { get; set; }
internal byte[] BufferToReturn { get; set; }
public override Message RequestMessage
{
get
{
if (_configuredRequestMessage == null)
{
_configuredRequestMessage = ConfigureRequestMessage(_innerContext.RequestMessage);
}
return _configuredRequestMessage;
}
}
public override void Abort()
{
Cleanup();
_innerContext.Abort();
}
public override IAsyncResult BeginReply(Message message, TimeSpan timeout, AsyncCallback callback, object state)
{
ConfigureResponseMessage(message);
return _innerContext.BeginReply(message, timeout, callback, state);
}
public override IAsyncResult BeginReply(Message message, AsyncCallback callback, object state)
{
ConfigureResponseMessage(message);
return _innerContext.BeginReply(message, callback, state);
}
public override void Close(TimeSpan timeout)
{
Cleanup();
_innerContext.Close(timeout);
}
public override void Close()
{
Cleanup();
_innerContext.Close();
}
public override void EndReply(IAsyncResult result)
{
try
{
_innerContext.EndReply(result);
}
catch (Exception ex)
{
Exception = ex;
throw;
}
}
public override void Reply(Message message, TimeSpan timeout)
{
ConfigureResponseMessage(message);
_innerContext.Reply(message, timeout);
}
public override void Reply(Message message)
{
ConfigureResponseMessage(message);
_innerContext.Reply(message);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
// To avoid double buffering, we use the BufferedOutputStream's own buffer
// that it created through the BufferManager passed to WriteStream.
// We are responsible for returning that buffer to the BufferManager
// because we used BufferedOutputStream.ToArray to take ownership
// of its buffers.
if (BufferManager != null && BufferToReturn != null)
{
BufferManager.ReturnBuffer(BufferToReturn);
BufferToReturn = null;
}
}
base.Dispose(disposing);
}
internal static HttpMessageEncodingRequestContext GetContextFromMessage(Message message)
{
HttpMessageEncodingRequestContext context = null;
message.Properties.TryGetValue<HttpMessageEncodingRequestContext>(HttpMessageEncodingRequestContextPropertyName, out context);
return context;
}
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Cleanup exceptions are ignored as they are not fatal to the host.")]
private void Cleanup()
{
if (_configuredRequestMessage != null)
{
try
{
_configuredRequestMessage.Close();
}
catch
{
}
}
}
private static void CopyHeadersToNameValueCollection(HttpHeaders headers, NameValueCollection nameValueCollection)
{
foreach (KeyValuePair<string, IEnumerable<string>> header in headers)
{
foreach (string value in header.Value)
{
nameValueCollection.Add(header.Key, value);
}
}
}
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "disposed later.")]
private static Message ConfigureRequestMessage(Message message)
{
if (message == null)
{
return null;
}
HttpRequestMessageProperty requestProperty;
if (!message.Properties.TryGetValue(HttpRequestMessageProperty.Name, out requestProperty))
{
throw Error.InvalidOperation(
SRResources.RequestMissingHttpRequestMessageProperty,
HttpRequestMessageProperty.Name,
typeof(HttpRequestMessageProperty).Name);
}
Uri uri = message.Headers.To;
if (uri == null)
{
throw Error.InvalidOperation(SRResources.RequestMissingToHeader);
}
HttpRequestMessage httpRequestMessage = message.ToHttpRequestMessage();
if (httpRequestMessage == null)
{
if (!message.IsEmpty)
{
throw Error.InvalidOperation(SRResources.NonHttpMessageMustBeEmpty, HttpMessageExtensions.ToHttpRequestMessageMethodName, typeof(HttpMessage).Name);
}
httpRequestMessage = new HttpRequestMessage();
Message oldMessage = message;
message = httpRequestMessage.ToMessage();
message.Properties.CopyProperties(oldMessage.Properties);
oldMessage.Close();
}
else
{
// Clear headers but not properties.
message.Headers.Clear();
}
// Copy message properties to HttpRequestMessage. While it does have the
// risk of allowing properties to get out of sync they in virtually all cases are
// read-only so the risk is low. The downside to not doing it is that it isn't
// possible to access anything from HttpRequestMessage (or OperationContent.Current)
// which is worse.
foreach (KeyValuePair<string, object> kv in message.Properties)
{
httpRequestMessage.Properties.Add(kv.Key, kv.Value);
}
if (httpRequestMessage.Content == null)
{
httpRequestMessage.Content = new ByteArrayContent(new byte[0]);
}
else
{
httpRequestMessage.Content.Headers.Clear();
}
message.Headers.To = uri;
httpRequestMessage.RequestUri = uri;
httpRequestMessage.Method = HttpMethodHelper.GetHttpMethod(requestProperty.Method);
foreach (var headerName in requestProperty.Headers.AllKeys)
{
string headerValue = requestProperty.Headers[headerName];
if (!httpRequestMessage.Headers.TryAddWithoutValidation(headerName, headerValue))
{
httpRequestMessage.Content.Headers.TryAddWithoutValidation(headerName, headerValue);
}
}
return message;
}
private void ConfigureResponseMessage(Message message)
{
Contract.Assert(message != null);
HttpResponseMessageProperty responseProperty = new HttpResponseMessageProperty();
HttpResponseMessage httpResponseMessage = message.ToHttpResponseMessage();
if (httpResponseMessage == null)
{
responseProperty.StatusCode = HttpStatusCode.InternalServerError;
responseProperty.SuppressEntityBody = true;
}
else
{
responseProperty.StatusCode = httpResponseMessage.StatusCode;
if (httpResponseMessage.ReasonPhrase != null &&
httpResponseMessage.ReasonPhrase != DefaultReasonPhrase)
{
responseProperty.StatusDescription = httpResponseMessage.ReasonPhrase;
}
CopyHeadersToNameValueCollection(httpResponseMessage.Headers, responseProperty.Headers);
HttpContent content = httpResponseMessage.Content;
if (content != null)
{
CopyHeadersToNameValueCollection(httpResponseMessage.Content.Headers, responseProperty.Headers);
}
else
{
responseProperty.SuppressEntityBody = true;
}
}
message.Properties.Clear();
message.Headers.Clear();
message.Properties.Add(HttpResponseMessageProperty.Name, responseProperty);
// The current request context flows with the Message for later use
// by HttpMessageEncoder.WriteMessage
message.Properties.Add(HttpMessageEncodingRequestContextPropertyName, this);
}
}
}

View File

@@ -0,0 +1,189 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Net.Http;
using System.ServiceModel.Channels;
namespace System.Web.Http.SelfHost.Channels
{
/// <summary>
/// Provides extension methods for getting an <see cref="HttpRequestMessage"/> instance or
/// an <see cref="HttpResponseMessage"/> instance from a <see cref="Message"/> instance and
/// provides extension methods for creating a <see cref="Message"/> instance from either an
/// <see cref="HttpRequestMessage"/> instance or an
/// <see cref="HttpResponseMessage"/> instance.
/// </summary>
internal static class HttpMessageExtensions
{
internal const string ToHttpRequestMessageMethodName = "ToHttpRequestMessage";
internal const string ToHttpResponseMessageMethodName = "ToHttpResponseMessage";
internal const string ToMessageMethodName = "ToMessage";
/// <summary>
/// Returns a reference to the <see cref="HttpRequestMessage"/>
/// instance held by the given <see cref="Message"/> or null if the <see cref="Message"/> does not
/// hold a reference to an <see cref="HttpRequestMessage"/>
/// instance.
/// </summary>
/// <param name="message">The given <see cref="Message"/> that holds a reference to an
/// <see cref="HttpRequestMessage"/> instance.
/// </param>
/// <returns>
/// A reference to the <see cref="HttpRequestMessage"/>
/// instance held by the given <see cref="Message"/> or null if the <see cref="Message"/> does not
/// hold a reference to an <see cref="HttpRequestMessage"/>
/// instance.
/// </returns>
public static HttpRequestMessage ToHttpRequestMessage(this Message message)
{
if (message == null)
{
throw Error.ArgumentNull("message");
}
HttpMessage httpMessage = message as HttpMessage;
if (httpMessage != null && httpMessage.IsRequest)
{
return httpMessage.GetHttpRequestMessage(false);
}
return null;
}
/// <summary>
/// Returns a reference to the <see cref="HttpRequestMessage"/>
/// instance held by the given <see cref="Message"/> or null if the <see cref="Message"/> does not
/// hold a reference to an <see cref="HttpRequestMessage"/>
/// instance.
/// </summary>
/// <remarks>The caller takes over the ownership of the associated <see cref="HttpRequestMessage"/> and is responsible for its disposal.</remarks>
/// <param name="message">The given <see cref="Message"/> that holds a reference to an
/// <see cref="HttpRequestMessage"/> instance.
/// </param>
/// <returns>
/// A reference to the <see cref="HttpRequestMessage"/>
/// instance held by the given <see cref="Message"/> or null if the <see cref="Message"/> does not
/// hold a reference to an <see cref="HttpRequestMessage"/>
/// instance.
/// The caller is responsible for disposing any <see cref="HttpRequestMessage"/> instance returned.
/// </returns>
public static HttpRequestMessage ExtractHttpRequestMessage(this Message message)
{
if (message == null)
{
throw Error.ArgumentNull("message");
}
HttpMessage httpMessage = message as HttpMessage;
if (httpMessage != null && httpMessage.IsRequest)
{
return httpMessage.GetHttpRequestMessage(true);
}
return null;
}
/// <summary>
/// Returns a reference to the <see cref="HttpResponseMessage"/>
/// instance held by the given <see cref="Message"/> or null if the <see cref="Message"/> does not
/// hold a reference to an <see cref="HttpResponseMessage"/>
/// instance.
/// </summary>
/// <param name="message">The given <see cref="Message"/> that holds a reference to an
/// <see cref="HttpResponseMessage"/> instance.
/// </param>
/// <returns>
/// A reference to the <see cref="HttpResponseMessage"/>
/// instance held by the given <see cref="Message"/> or null if the <see cref="Message"/> does not
/// hold a reference to an <see cref="HttpResponseMessage"/>
/// instance.
/// </returns>
public static HttpResponseMessage ToHttpResponseMessage(this Message message)
{
if (message == null)
{
throw Error.ArgumentNull("message");
}
HttpMessage httpMessage = message as HttpMessage;
if (httpMessage != null && !httpMessage.IsRequest)
{
return httpMessage.GetHttpResponseMessage(false);
}
return null;
}
/// <summary>
/// Returns a reference to the <see cref="HttpResponseMessage"/>
/// instance held by the given <see cref="Message"/> or null if the <see cref="Message"/> does not
/// hold a reference to an <see cref="HttpResponseMessage"/>
/// instance.
/// </summary>
/// <remarks>The caller takes over the ownership of the associated <see cref="HttpRequestMessage"/> and is responsible for its disposal.</remarks>
/// <param name="message">The given <see cref="Message"/> that holds a reference to an
/// <see cref="HttpResponseMessage"/> instance.
/// </param>
/// <returns>
/// A reference to the <see cref="HttpResponseMessage"/>
/// instance held by the given <see cref="Message"/> or null if the <see cref="Message"/> does not
/// hold a reference to an <see cref="HttpResponseMessage"/>
/// instance.
/// The caller is responsible for disposing any <see cref="HttpResponseMessage"/> instance returned.
/// </returns>
public static HttpResponseMessage ExtractHttpResponseMessage(this Message message)
{
if (message == null)
{
throw Error.ArgumentNull("message");
}
HttpMessage httpMessage = message as HttpMessage;
if (httpMessage != null && !httpMessage.IsRequest)
{
return httpMessage.GetHttpResponseMessage(true);
}
return null;
}
/// <summary>
/// Creates a new <see cref="Message"/> that holds a reference to the given
/// <see cref="HttpRequestMessage"/> instance.
/// </summary>
/// <param name="request">The <see cref="HttpRequestMessage"/>
/// instance to which the new <see cref="Message"/> should hold a reference.
/// </param>
/// <returns>A <see cref="Message"/> that holds a reference to the given
/// <see cref="HttpRequestMessage"/> instance.
/// </returns>
public static Message ToMessage(this HttpRequestMessage request)
{
if (request == null)
{
throw Error.ArgumentNull("request");
}
return new HttpMessage(request);
}
/// <summary>
/// Creates a new <see cref="Message"/> that holds a reference to the given
/// <see cref="HttpResponseMessage"/> instance.
/// </summary>
/// <param name="response">The <see cref="HttpResponseMessage"/>
/// instance to which the new <see cref="Message"/> should hold a reference.
/// </param>
/// <returns>A <see cref="Message"/> that holds a reference to the given
/// <see cref="HttpResponseMessage"/> instance.
/// </returns>
public static Message ToMessage(this HttpResponseMessage response)
{
if (response == null)
{
throw Error.ArgumentNull("response");
}
return new HttpMessage(response);
}
}
}