416 lines
14 KiB
C#
416 lines
14 KiB
C#
|
//----------------------------------------------------------------------------
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
//----------------------------------------------------------------------------
|
||
|
namespace System.ServiceModel.Channels
|
||
|
{
|
||
|
using System;
|
||
|
using System.Collections.Generic;
|
||
|
using System.Net;
|
||
|
using System.Net.Http;
|
||
|
using System.Net.Http.Headers;
|
||
|
using System.Runtime;
|
||
|
|
||
|
public sealed class HttpResponseMessageProperty : IMessageProperty, IMergeEnabledMessageProperty
|
||
|
{
|
||
|
TraditionalHttpResponseMessageProperty traditionalProperty;
|
||
|
HttpResponseMessageBackedProperty httpBackedProperty;
|
||
|
bool useHttpBackedProperty;
|
||
|
bool initialCopyPerformed;
|
||
|
|
||
|
public HttpResponseMessageProperty()
|
||
|
: this((WebHeaderCollection)null)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
internal HttpResponseMessageProperty(WebHeaderCollection originalHeaders)
|
||
|
{
|
||
|
this.traditionalProperty = new TraditionalHttpResponseMessageProperty(originalHeaders);
|
||
|
this.useHttpBackedProperty = false;
|
||
|
}
|
||
|
|
||
|
internal HttpResponseMessageProperty(HttpResponseMessage httpResponseMessage)
|
||
|
{
|
||
|
this.httpBackedProperty = new HttpResponseMessageBackedProperty(httpResponseMessage);
|
||
|
this.useHttpBackedProperty = true;
|
||
|
}
|
||
|
|
||
|
public static string Name
|
||
|
{
|
||
|
get { return "httpResponse"; }
|
||
|
}
|
||
|
|
||
|
public WebHeaderCollection Headers
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return this.useHttpBackedProperty ?
|
||
|
this.httpBackedProperty.Headers :
|
||
|
this.traditionalProperty.Headers;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public HttpStatusCode StatusCode
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return this.useHttpBackedProperty ?
|
||
|
this.httpBackedProperty.StatusCode :
|
||
|
this.traditionalProperty.StatusCode;
|
||
|
}
|
||
|
|
||
|
set
|
||
|
{
|
||
|
int valueInt = (int)value;
|
||
|
if (valueInt < 100 || valueInt > 599)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value,
|
||
|
SR.GetString(SR.ValueMustBeInRange, 100, 599)));
|
||
|
}
|
||
|
|
||
|
if (this.useHttpBackedProperty)
|
||
|
{
|
||
|
this.httpBackedProperty.StatusCode = value;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
this.traditionalProperty.StatusCode = value;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal bool HasStatusCodeBeenSet
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return this.useHttpBackedProperty ?
|
||
|
true :
|
||
|
this.traditionalProperty.HasStatusCodeBeenSet;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public string StatusDescription
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return this.useHttpBackedProperty ?
|
||
|
this.httpBackedProperty.StatusDescription :
|
||
|
this.traditionalProperty.StatusDescription;
|
||
|
}
|
||
|
|
||
|
set
|
||
|
{
|
||
|
if (this.useHttpBackedProperty)
|
||
|
{
|
||
|
this.httpBackedProperty.StatusDescription = value;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
this.traditionalProperty.StatusDescription = value;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool SuppressEntityBody
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return this.useHttpBackedProperty ?
|
||
|
this.httpBackedProperty.SuppressEntityBody :
|
||
|
this.traditionalProperty.SuppressEntityBody;
|
||
|
}
|
||
|
|
||
|
set
|
||
|
{
|
||
|
if (this.useHttpBackedProperty)
|
||
|
{
|
||
|
this.httpBackedProperty.SuppressEntityBody = value;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
this.traditionalProperty.SuppressEntityBody = value;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool SuppressPreamble
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return this.useHttpBackedProperty ?
|
||
|
false :
|
||
|
this.traditionalProperty.SuppressPreamble;
|
||
|
}
|
||
|
|
||
|
set
|
||
|
{
|
||
|
if (!this.useHttpBackedProperty)
|
||
|
{
|
||
|
this.traditionalProperty.SuppressPreamble = value;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private HttpResponseMessage HttpResponseMessage
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
if (this.useHttpBackedProperty)
|
||
|
{
|
||
|
return this.httpBackedProperty.HttpResponseMessage;
|
||
|
}
|
||
|
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal static HttpResponseMessage GetHttpResponseMessageFromMessage(Message message)
|
||
|
{
|
||
|
HttpResponseMessage httpResponseMessage = null;
|
||
|
|
||
|
HttpResponseMessageProperty property = message.Properties.GetValue<HttpResponseMessageProperty>(HttpResponseMessageProperty.Name);
|
||
|
if (property != null)
|
||
|
{
|
||
|
httpResponseMessage = property.HttpResponseMessage;
|
||
|
if (httpResponseMessage != null)
|
||
|
{
|
||
|
httpResponseMessage.CopyPropertiesFromMessage(message);
|
||
|
message.EnsureReadMessageState();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return httpResponseMessage;
|
||
|
}
|
||
|
|
||
|
IMessageProperty IMessageProperty.CreateCopy()
|
||
|
{
|
||
|
if (!this.useHttpBackedProperty ||
|
||
|
!this.initialCopyPerformed)
|
||
|
{
|
||
|
this.initialCopyPerformed = true;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
return this.httpBackedProperty.CreateTraditionalResponseMessageProperty();
|
||
|
}
|
||
|
|
||
|
bool IMergeEnabledMessageProperty.TryMergeWithProperty(object propertyToMerge)
|
||
|
{
|
||
|
// The ImmutableDispatchRuntime will merge MessageProperty instances from the
|
||
|
// OperationContext (that were created before the response message was created) with
|
||
|
// MessageProperty instances on the message itself. The message's version of the
|
||
|
// HttpResponseMessageProperty may hold a reference to an HttpResponseMessage, and this
|
||
|
// cannot be discarded, so values from the OperationContext's property must be set on
|
||
|
// the message's version without completely replacing the message's property.
|
||
|
if (this.useHttpBackedProperty)
|
||
|
{
|
||
|
HttpResponseMessageProperty responseProperty = propertyToMerge as HttpResponseMessageProperty;
|
||
|
if (responseProperty != null)
|
||
|
{
|
||
|
if (!responseProperty.useHttpBackedProperty)
|
||
|
{
|
||
|
this.httpBackedProperty.MergeWithTraditionalProperty(responseProperty.traditionalProperty);
|
||
|
responseProperty.traditionalProperty = null;
|
||
|
responseProperty.httpBackedProperty = this.httpBackedProperty;
|
||
|
responseProperty.useHttpBackedProperty = true;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
private class TraditionalHttpResponseMessageProperty
|
||
|
{
|
||
|
public const HttpStatusCode DefaultStatusCode = HttpStatusCode.OK;
|
||
|
public const string DefaultStatusDescription = null; // null means use description from status code
|
||
|
|
||
|
WebHeaderCollection headers;
|
||
|
WebHeaderCollection originalHeaders;
|
||
|
HttpStatusCode statusCode;
|
||
|
|
||
|
public TraditionalHttpResponseMessageProperty(WebHeaderCollection originalHeaders)
|
||
|
{
|
||
|
this.originalHeaders = originalHeaders;
|
||
|
this.statusCode = DefaultStatusCode;
|
||
|
this.StatusDescription = DefaultStatusDescription;
|
||
|
}
|
||
|
|
||
|
public WebHeaderCollection Headers
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
if (this.headers == null)
|
||
|
{
|
||
|
this.headers = new WebHeaderCollection();
|
||
|
if (this.originalHeaders != null)
|
||
|
{
|
||
|
this.headers.Add(originalHeaders);
|
||
|
this.originalHeaders = null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return this.headers;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public HttpStatusCode StatusCode
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return this.statusCode;
|
||
|
}
|
||
|
|
||
|
set
|
||
|
{
|
||
|
this.statusCode = value;
|
||
|
this.HasStatusCodeBeenSet = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool HasStatusCodeBeenSet { get; private set; }
|
||
|
|
||
|
public string StatusDescription { get; set; }
|
||
|
|
||
|
public bool SuppressEntityBody { get; set; }
|
||
|
|
||
|
public bool SuppressPreamble { get; set; }
|
||
|
}
|
||
|
|
||
|
private class HttpResponseMessageBackedProperty
|
||
|
{
|
||
|
private HttpHeadersWebHeaderCollection headers;
|
||
|
|
||
|
public HttpResponseMessageBackedProperty(HttpResponseMessage httpResponseMessage)
|
||
|
{
|
||
|
Fx.Assert(httpResponseMessage != null, "The 'httpResponseMessage' property should never be null.");
|
||
|
|
||
|
this.HttpResponseMessage = httpResponseMessage;
|
||
|
}
|
||
|
|
||
|
public HttpResponseMessage HttpResponseMessage { get; private set; }
|
||
|
|
||
|
public WebHeaderCollection Headers
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
if (this.headers == null)
|
||
|
{
|
||
|
this.headers = new HttpHeadersWebHeaderCollection(this.HttpResponseMessage);
|
||
|
}
|
||
|
|
||
|
return this.headers;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public HttpStatusCode StatusCode
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return this.HttpResponseMessage.StatusCode;
|
||
|
}
|
||
|
|
||
|
set
|
||
|
{
|
||
|
this.HttpResponseMessage.StatusCode = value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public string StatusDescription
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return this.HttpResponseMessage.ReasonPhrase;
|
||
|
}
|
||
|
|
||
|
set
|
||
|
{
|
||
|
this.HttpResponseMessage.ReasonPhrase = value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool SuppressEntityBody
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
HttpContent content = this.HttpResponseMessage.Content;
|
||
|
if (content != null)
|
||
|
{
|
||
|
long? contentLength = content.Headers.ContentLength;
|
||
|
|
||
|
if (!contentLength.HasValue ||
|
||
|
(contentLength.HasValue && contentLength.Value > 0))
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
set
|
||
|
{
|
||
|
HttpContent content = this.HttpResponseMessage.Content;
|
||
|
if (value && content != null &&
|
||
|
(!content.Headers.ContentLength.HasValue ||
|
||
|
content.Headers.ContentLength.Value > 0))
|
||
|
{
|
||
|
HttpContent newContent = new ByteArrayContent(EmptyArray<byte>.Instance);
|
||
|
foreach (KeyValuePair<string, IEnumerable<string>> header in content.Headers)
|
||
|
{
|
||
|
newContent.Headers.AddHeaderWithoutValidation(header);
|
||
|
}
|
||
|
|
||
|
this.HttpResponseMessage.Content = newContent;
|
||
|
content.Dispose();
|
||
|
}
|
||
|
else if (!value && content == null)
|
||
|
{
|
||
|
this.HttpResponseMessage.Content = new ByteArrayContent(EmptyArray<byte>.Instance);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public HttpResponseMessageProperty CreateTraditionalResponseMessageProperty()
|
||
|
{
|
||
|
HttpResponseMessageProperty copiedProperty = new HttpResponseMessageProperty();
|
||
|
|
||
|
copiedProperty.Headers.Add(this.Headers);
|
||
|
|
||
|
if (this.StatusCode != TraditionalHttpResponseMessageProperty.DefaultStatusCode)
|
||
|
{
|
||
|
copiedProperty.StatusCode = this.StatusCode;
|
||
|
}
|
||
|
|
||
|
copiedProperty.StatusDescription = this.StatusDescription;
|
||
|
copiedProperty.SuppressEntityBody = this.SuppressEntityBody;
|
||
|
|
||
|
return copiedProperty;
|
||
|
}
|
||
|
|
||
|
public void MergeWithTraditionalProperty(TraditionalHttpResponseMessageProperty propertyToMerge)
|
||
|
{
|
||
|
if (propertyToMerge.HasStatusCodeBeenSet)
|
||
|
{
|
||
|
this.StatusCode = propertyToMerge.StatusCode;
|
||
|
}
|
||
|
|
||
|
if (propertyToMerge.StatusDescription != TraditionalHttpResponseMessageProperty.DefaultStatusDescription)
|
||
|
{
|
||
|
this.StatusDescription = propertyToMerge.StatusDescription;
|
||
|
}
|
||
|
|
||
|
this.SuppressEntityBody = propertyToMerge.SuppressEntityBody;
|
||
|
|
||
|
WebHeaderCollection headersToMerge = propertyToMerge.Headers;
|
||
|
foreach (string headerKey in headersToMerge.AllKeys)
|
||
|
{
|
||
|
this.Headers[headerKey] = headersToMerge[headerKey];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|