You've already forked linux-packaging-mono
Imported Upstream version 4.0.0~alpha1
Former-commit-id: 806294f5ded97629b74c85c09952f2a74fe182d9
This commit is contained in:
@ -0,0 +1,54 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
namespace System.ServiceModel.Web
|
||||
{
|
||||
using System.ServiceModel.Activation;
|
||||
using System.ServiceModel.Channels;
|
||||
using System.ServiceModel.Description;
|
||||
using System.ServiceModel.Dispatcher;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public sealed class AspNetCacheProfileAttribute : Attribute, IOperationBehavior
|
||||
{
|
||||
string cacheProfileName;
|
||||
|
||||
public AspNetCacheProfileAttribute(string cacheProfileName)
|
||||
{
|
||||
this.cacheProfileName = cacheProfileName;
|
||||
}
|
||||
|
||||
public string CacheProfileName
|
||||
{
|
||||
get { return this.cacheProfileName; }
|
||||
}
|
||||
|
||||
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
|
||||
{
|
||||
} // do nothing
|
||||
|
||||
public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
|
||||
{
|
||||
} // do nothing
|
||||
|
||||
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
|
||||
{
|
||||
if (!AspNetEnvironment.Current.AspNetCompatibilityEnabled)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR2.CacheProfileOnlySupportedInAspNetCompatibilityMode));
|
||||
}
|
||||
|
||||
if (operationDescription.Behaviors.Find<WebGetAttribute>() == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR2.CacheProfileAttributeOnlyWithGet));
|
||||
}
|
||||
dispatchOperation.ParameterInspectors.Add(new CachingParameterInspector(this.cacheProfileName));
|
||||
}
|
||||
|
||||
public void Validate(OperationDescription operationDescription)
|
||||
{
|
||||
// validation happens in ApplyDispatchBehavior because it is dispatcher specific
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,130 @@
|
||||
// <copyright>
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace System.ServiceModel.Web
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Runtime;
|
||||
using System.ServiceModel.Activation;
|
||||
using System.ServiceModel.Channels;
|
||||
using System.ServiceModel.Configuration;
|
||||
using System.ServiceModel.Description;
|
||||
|
||||
internal abstract class AutomaticEndpointGenerator
|
||||
{
|
||||
private IDictionary<string, ContractDescription> implementedContracts;
|
||||
private string multipleContractsErrorMessage;
|
||||
private string noContractErrorMessage;
|
||||
private string standardEndpointKind;
|
||||
private Type singleImplementedContract;
|
||||
|
||||
protected AutomaticEndpointGenerator(IDictionary<string, ContractDescription> implementedContracts, string multipleContractsErrorMessage, string noContractErrorMessage, string standardEndpointKind)
|
||||
{
|
||||
Fx.Assert(implementedContracts != null, "The 'implementedContracts' parameter should not be null.");
|
||||
Fx.Assert(multipleContractsErrorMessage != null, "The 'multipleContractsErrorMessage' parameter should not be null.");
|
||||
Fx.Assert(noContractErrorMessage != null, "The 'noContractErrorMessage' parameter should not be null.");
|
||||
Fx.Assert(standardEndpointKind != null, "The 'standardEndpointKind' parameter should not be null.");
|
||||
|
||||
this.implementedContracts = implementedContracts;
|
||||
this.multipleContractsErrorMessage = multipleContractsErrorMessage;
|
||||
this.noContractErrorMessage = noContractErrorMessage;
|
||||
this.standardEndpointKind = standardEndpointKind;
|
||||
}
|
||||
|
||||
protected abstract string BindingCollectionElementName { get; }
|
||||
|
||||
public ServiceEndpoint GenerateServiceEndpoint(ServiceHostBase serviceHost, Uri baseAddress)
|
||||
{
|
||||
Fx.Assert(serviceHost != null, "The 'serviceHost' parameter should not be null.");
|
||||
Fx.Assert(baseAddress != null, "The 'baseAddress' parameter should not be null.");
|
||||
|
||||
AuthenticationSchemes supportedSchemes = GetAuthenticationSchemes(baseAddress);
|
||||
Type contractType = this.GetSingleImplementedContract();
|
||||
ConfigLoader configLoader = new ConfigLoader(serviceHost.GetContractResolver(this.implementedContracts));
|
||||
ServiceEndpointElement serviceEndpointElement = new ServiceEndpointElement();
|
||||
|
||||
serviceEndpointElement.Contract = contractType.FullName;
|
||||
this.SetBindingConfiguration(baseAddress.Scheme, serviceEndpointElement);
|
||||
serviceEndpointElement.Kind = this.standardEndpointKind;
|
||||
|
||||
ServiceEndpoint serviceEndpoint = configLoader.LookupEndpoint(serviceEndpointElement, null, serviceHost, serviceHost.Description, true);
|
||||
this.ConfigureBinding(serviceEndpoint.Binding, baseAddress.Scheme, supportedSchemes, AspNetEnvironment.Enabled);
|
||||
|
||||
// Setting the Endpoint address and listenUri now that we've set the binding security
|
||||
ConfigLoader.ConfigureEndpointAddress(serviceEndpointElement, serviceHost, serviceEndpoint);
|
||||
ConfigLoader.ConfigureEndpointListenUri(serviceEndpointElement, serviceHost, serviceEndpoint);
|
||||
|
||||
return serviceEndpoint;
|
||||
}
|
||||
|
||||
protected abstract void ConfigureBinding(Binding binding, string uriScheme, AuthenticationSchemes supportedAuthenticationSchemes, bool hostedEnvironment);
|
||||
|
||||
private static AuthenticationSchemes GetAuthenticationSchemes(Uri baseAddress)
|
||||
{
|
||||
AuthenticationSchemes supportedSchemes = AspNetEnvironment.Current.GetAuthenticationSchemes(baseAddress);
|
||||
|
||||
if (AspNetEnvironment.Current.IsSimpleApplicationHost)
|
||||
{
|
||||
// Cassini always reports the auth scheme as anonymous or Ntlm. Map this to Ntlm, except when forms auth
|
||||
// is requested
|
||||
if (supportedSchemes == (AuthenticationSchemes.Anonymous | AuthenticationSchemes.Ntlm))
|
||||
{
|
||||
if (AspNetEnvironment.Current.IsWindowsAuthenticationConfigured())
|
||||
{
|
||||
supportedSchemes = AuthenticationSchemes.Ntlm;
|
||||
}
|
||||
else
|
||||
{
|
||||
supportedSchemes = AuthenticationSchemes.Anonymous;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return supportedSchemes;
|
||||
}
|
||||
|
||||
private Type GetSingleImplementedContract()
|
||||
{
|
||||
if (this.singleImplementedContract == null)
|
||||
{
|
||||
Fx.Assert(this.implementedContracts != null, "The 'implementedContracts' field should not be null.");
|
||||
Fx.Assert(this.multipleContractsErrorMessage != null, "The 'multipleContractsErrorMessage' field should not be null.");
|
||||
Fx.Assert(this.noContractErrorMessage != null, "The 'noContractErrorMessage' field should not be null.");
|
||||
|
||||
if (this.implementedContracts.Count > 1)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(this.multipleContractsErrorMessage));
|
||||
}
|
||||
else if (this.implementedContracts.Count == 0)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(this.noContractErrorMessage));
|
||||
}
|
||||
|
||||
foreach (ContractDescription contract in this.implementedContracts.Values)
|
||||
{
|
||||
this.singleImplementedContract = contract.ContractType;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return this.singleImplementedContract;
|
||||
}
|
||||
|
||||
private void SetBindingConfiguration(string uriScheme, ServiceEndpointElement serviceEndpointElement)
|
||||
{
|
||||
Fx.Assert(uriScheme != null, "The 'uriScheme' parameter should not be null.");
|
||||
Fx.Assert(serviceEndpointElement != null, "The 'serviceEndpointElement' parameter should not be null.");
|
||||
Fx.Assert(this.BindingCollectionElementName != null, "The 'this.BindingCollectionElementName' property should not be null.");
|
||||
|
||||
ProtocolMappingItem protocolMappingItem = ConfigLoader.LookupProtocolMapping(uriScheme);
|
||||
if (protocolMappingItem != null &&
|
||||
string.Equals(protocolMappingItem.Binding, this.BindingCollectionElementName, StringComparison.Ordinal))
|
||||
{
|
||||
serviceEndpointElement.BindingConfiguration = protocolMappingItem.BindingConfiguration;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,366 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Security;
|
||||
using System.ServiceModel;
|
||||
using System.ServiceModel.Channels;
|
||||
using System.ServiceModel.Diagnostics;
|
||||
using System.ServiceModel.Dispatcher;
|
||||
using System.Web;
|
||||
using System.Web.Caching;
|
||||
using System.Web.Configuration;
|
||||
using System.Web.UI;
|
||||
using System.ServiceModel.Activation;
|
||||
|
||||
namespace System.ServiceModel.Web
|
||||
{
|
||||
class CachingParameterInspector : IParameterInspector
|
||||
{
|
||||
const char seperatorChar = ';';
|
||||
const char escapeChar = '\\';
|
||||
const char tableDbSeperatorChar = ':';
|
||||
const string invalidSqlDependencyString = "Invalid Sql dependency string.";
|
||||
|
||||
[Fx.Tag.SecurityNote(Critical = "A config object, which should not be leaked.")]
|
||||
[SecurityCritical]
|
||||
OutputCacheProfile cacheProfile;
|
||||
|
||||
SqlDependencyInfo[] cacheDependencyInfoArray;
|
||||
|
||||
[Fx.Tag.SecurityNote(Critical = "Handles config objects, which should not be leaked.",
|
||||
Safe = "The config object never leaves the CachingParameterInspector.")]
|
||||
[SecuritySafeCritical]
|
||||
public CachingParameterInspector(string cacheProfileName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(cacheProfileName))
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR2.CacheProfileNameNullOrEmpty));
|
||||
}
|
||||
|
||||
OutputCacheSettingsSection cacheSettings = AspNetEnvironment.Current.UnsafeGetConfigurationSection("system.web/caching/outputCacheSettings") as OutputCacheSettingsSection;
|
||||
if (cacheSettings == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR2.GetString(SR2.CacheProfileNotConfigured, cacheProfileName)));
|
||||
}
|
||||
|
||||
this.cacheProfile = cacheSettings.OutputCacheProfiles[cacheProfileName];
|
||||
if (this.cacheProfile == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR2.GetString(SR2.CacheProfileNotConfigured, cacheProfileName)));
|
||||
}
|
||||
|
||||
// Validate the cacheProfile
|
||||
if (this.cacheProfile.Location != OutputCacheLocation.None)
|
||||
{
|
||||
// Duration must be set; Duration default value is -1
|
||||
if (this.cacheProfile.Duration == -1)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR2.GetString(SR2.CacheProfileValueMissing, this.cacheProfile.Name, "Duration")));
|
||||
}
|
||||
if (this.cacheProfile.VaryByParam == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR2.GetString(SR2.CacheProfileValueMissing, this.cacheProfile.Name, "VaryByParam")));
|
||||
}
|
||||
}
|
||||
|
||||
if (string.Equals(this.cacheProfile.SqlDependency, "CommandNotification", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR2.CommandNotificationSqlDependencyNotSupported));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(this.cacheProfile.SqlDependency))
|
||||
{
|
||||
ParseSqlDependencyString(cacheProfile.SqlDependency);
|
||||
}
|
||||
}
|
||||
|
||||
[Fx.Tag.SecurityNote(Critical = "Handles config objects, which should not be leaked.",
|
||||
Safe = "The config object never leaves the CachingParameterInspector.")]
|
||||
[SecuritySafeCritical]
|
||||
public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState)
|
||||
{
|
||||
if (this.cacheProfile != null &&
|
||||
this.cacheProfile.Enabled &&
|
||||
OperationContext.Current.IncomingMessage.Version == MessageVersion.None)
|
||||
{
|
||||
if (DiagnosticUtility.ShouldTraceWarning && !IsAnonymous())
|
||||
{
|
||||
TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.AddingAuthenticatedResponseToOutputCache, SR2.GetString(SR2.TraceCodeAddingAuthenticatedResponseToOutputCache, operationName));
|
||||
}
|
||||
else if (DiagnosticUtility.ShouldTraceInformation)
|
||||
{
|
||||
TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.AddingResponseToOutputCache, SR2.GetString(SR2.TraceCodeAddingResponseToOutputCache, operationName));
|
||||
}
|
||||
|
||||
SetCacheFromCacheProfile();
|
||||
}
|
||||
}
|
||||
|
||||
public object BeforeCall(string operationName, object[] inputs)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
bool IsAnonymous()
|
||||
{
|
||||
if (HttpContext.Current.User.Identity.IsAuthenticated)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (OperationContext.Current.ServiceSecurityContext == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return OperationContext.Current.ServiceSecurityContext.IsAnonymous;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static SqlDependencyInfo[] ParseSqlDependencyString(string sqlDependencyString)
|
||||
{
|
||||
// The code for this method was taken from private code in
|
||||
// System.Web.SqlCacheDependency.ParseSql7OutputCacheDependency.
|
||||
// Alter if only absolutely necessary since we want to reproduce the same ASP.NET caching behavior.
|
||||
|
||||
List<SqlDependencyInfo> dependencyList = new List<SqlDependencyInfo>();
|
||||
bool escapeSequenceFlag = false;
|
||||
int startIndexForDatabaseName = 0;
|
||||
int startIndexForTableName = -1;
|
||||
string databaseName = null;
|
||||
|
||||
try
|
||||
{
|
||||
for (int currentIndex = 0; currentIndex < (sqlDependencyString.Length + 1); currentIndex++)
|
||||
{
|
||||
if (escapeSequenceFlag)
|
||||
{
|
||||
escapeSequenceFlag = false;
|
||||
}
|
||||
else if ((currentIndex != sqlDependencyString.Length) &&
|
||||
(sqlDependencyString[currentIndex] == escapeChar))
|
||||
{
|
||||
escapeSequenceFlag = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
int subStringLength;
|
||||
if ((currentIndex == sqlDependencyString.Length) ||
|
||||
(sqlDependencyString[currentIndex] == seperatorChar))
|
||||
{
|
||||
if (databaseName == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(invalidSqlDependencyString);
|
||||
}
|
||||
subStringLength = currentIndex - startIndexForTableName;
|
||||
if (subStringLength == 0)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(invalidSqlDependencyString);
|
||||
}
|
||||
string tableName = sqlDependencyString.Substring(startIndexForTableName, subStringLength);
|
||||
SqlDependencyInfo info = new SqlDependencyInfo();
|
||||
info.Database = VerifyAndRemoveEscapeCharacters(databaseName);
|
||||
info.Table = VerifyAndRemoveEscapeCharacters(tableName);
|
||||
dependencyList.Add(info);
|
||||
startIndexForDatabaseName = currentIndex + 1;
|
||||
databaseName = null;
|
||||
}
|
||||
if (currentIndex == sqlDependencyString.Length)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (sqlDependencyString[currentIndex] == tableDbSeperatorChar)
|
||||
{
|
||||
if (databaseName != null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(invalidSqlDependencyString);
|
||||
}
|
||||
subStringLength = currentIndex - startIndexForDatabaseName;
|
||||
if (subStringLength == 0)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(invalidSqlDependencyString);
|
||||
}
|
||||
databaseName = sqlDependencyString.Substring(startIndexForDatabaseName, subStringLength);
|
||||
startIndexForTableName = currentIndex + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR2.GetString(SR2.CacheProfileSqlDependencyIsInvalid, sqlDependencyString)));
|
||||
}
|
||||
if (dependencyList.Count == 0)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR2.GetString(SR2.CacheProfileSqlDependencyIsInvalid, sqlDependencyString)));
|
||||
}
|
||||
return dependencyList.ToArray();
|
||||
}
|
||||
|
||||
static string VerifyAndRemoveEscapeCharacters(string str)
|
||||
{
|
||||
// The code for this method was taken from private code in
|
||||
// System.Web.SqlCacheDependency.VerifyAndRemoveEscapeCharacters.
|
||||
// Alter if only absolutely necessary since we want to reproduce the same ASP.NET caching behavior.
|
||||
|
||||
bool escapeSequenceFlag = false;
|
||||
for (int currentIndex = 0; currentIndex < str.Length; currentIndex++)
|
||||
{
|
||||
if (escapeSequenceFlag)
|
||||
{
|
||||
if (((str[currentIndex] != escapeChar) &&
|
||||
(str[currentIndex] != tableDbSeperatorChar)) &&
|
||||
(str[currentIndex] != seperatorChar))
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(str);
|
||||
}
|
||||
escapeSequenceFlag = false;
|
||||
}
|
||||
else if (str[currentIndex] == escapeChar)
|
||||
{
|
||||
if ((currentIndex + 1) == str.Length)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(str);
|
||||
}
|
||||
escapeSequenceFlag = true;
|
||||
str = str.Remove(currentIndex, 1);
|
||||
currentIndex--;
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
CacheDependency CreateSingleCacheDependency(string sqlDependency)
|
||||
{
|
||||
if (this.cacheDependencyInfoArray == null)
|
||||
{
|
||||
this.cacheDependencyInfoArray = CachingParameterInspector.ParseSqlDependencyString(sqlDependency);
|
||||
}
|
||||
|
||||
// cacheDependencyInfoArray will never have length = 0
|
||||
|
||||
if (this.cacheDependencyInfoArray.Length == 1)
|
||||
{
|
||||
return new SqlCacheDependency(this.cacheDependencyInfoArray[0].Database, this.cacheDependencyInfoArray[0].Table);
|
||||
}
|
||||
|
||||
AggregateCacheDependency cacheDependency = new AggregateCacheDependency();
|
||||
foreach (SqlDependencyInfo dependencyInfo in this.cacheDependencyInfoArray)
|
||||
{
|
||||
cacheDependency.Add(new CacheDependency[] { new SqlCacheDependency(dependencyInfo.Database, dependencyInfo.Table) });
|
||||
}
|
||||
return cacheDependency;
|
||||
}
|
||||
|
||||
[Fx.Tag.SecurityNote(Critical = "Uses config object to set properties of the HttpCachePolicy.",
|
||||
Safe = "The config object itself doesn't leak.")]
|
||||
[SecuritySafeCritical]
|
||||
void SetCacheFromCacheProfile()
|
||||
{
|
||||
HttpCachePolicy cache = HttpContext.Current.Response.Cache;
|
||||
|
||||
if (this.cacheProfile.NoStore)
|
||||
{
|
||||
cache.SetNoStore();
|
||||
}
|
||||
|
||||
// Location is not required to be set in the config. The default is Any,
|
||||
// but if it is not set in the config the value will be -1. So must correct for this.
|
||||
if ((int)(this.cacheProfile.Location) == -1)
|
||||
{
|
||||
cache.SetCacheability(HttpCacheability.Public);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (this.cacheProfile.Location)
|
||||
{
|
||||
case OutputCacheLocation.Any:
|
||||
cache.SetCacheability(HttpCacheability.Public);
|
||||
break;
|
||||
case OutputCacheLocation.Client:
|
||||
cache.SetCacheability(HttpCacheability.Private);
|
||||
break;
|
||||
case OutputCacheLocation.Downstream:
|
||||
cache.SetCacheability(HttpCacheability.Public);
|
||||
cache.SetNoServerCaching();
|
||||
break;
|
||||
case OutputCacheLocation.None:
|
||||
cache.SetCacheability(HttpCacheability.NoCache);
|
||||
break;
|
||||
case OutputCacheLocation.Server:
|
||||
cache.SetCacheability(HttpCacheability.ServerAndNoCache);
|
||||
break;
|
||||
case OutputCacheLocation.ServerAndClient:
|
||||
cache.SetCacheability(HttpCacheability.ServerAndPrivate);
|
||||
break;
|
||||
default:
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR2.GetString(SR2.CacheProfileLocationNotSupported, this.cacheProfile.Location)));
|
||||
}
|
||||
}
|
||||
|
||||
if (this.cacheProfile.Location != OutputCacheLocation.None)
|
||||
{
|
||||
cache.SetExpires(HttpContext.Current.Timestamp.AddSeconds((double)this.cacheProfile.Duration));
|
||||
cache.SetMaxAge(new TimeSpan(0, 0, this.cacheProfile.Duration));
|
||||
cache.SetValidUntilExpires(true);
|
||||
cache.SetLastModified(HttpContext.Current.Timestamp);
|
||||
|
||||
if (this.cacheProfile.Location != OutputCacheLocation.Client)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(this.cacheProfile.VaryByContentEncoding))
|
||||
{
|
||||
foreach (string contentEncoding in this.cacheProfile.VaryByContentEncoding.Split(seperatorChar))
|
||||
{
|
||||
cache.VaryByContentEncodings[contentEncoding.Trim()] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(this.cacheProfile.VaryByHeader))
|
||||
{
|
||||
foreach (string header in this.cacheProfile.VaryByHeader.Split(seperatorChar))
|
||||
{
|
||||
cache.VaryByHeaders[header.Trim()] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.cacheProfile.Location != OutputCacheLocation.Downstream)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(this.cacheProfile.VaryByCustom))
|
||||
{
|
||||
cache.SetVaryByCustom(this.cacheProfile.VaryByCustom);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(this.cacheProfile.VaryByParam))
|
||||
{
|
||||
foreach (string parameter in cacheProfile.VaryByParam.Split(seperatorChar))
|
||||
{
|
||||
cache.VaryByParams[parameter.Trim()] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(this.cacheProfile.SqlDependency))
|
||||
{
|
||||
CacheDependency cacheDependency = this.CreateSingleCacheDependency(cacheProfile.SqlDependency);
|
||||
HttpContext.Current.Response.AddCacheDependency(new CacheDependency[] { cacheDependency });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct SqlDependencyInfo
|
||||
{
|
||||
public string Database;
|
||||
public string Table;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
472
external/referencesource/System.ServiceModel.Web/System/ServiceModel/Web/HttpDateParse.cs
vendored
Normal file
472
external/referencesource/System.ServiceModel.Web/System/ServiceModel/Web/HttpDateParse.cs
vendored
Normal file
@ -0,0 +1,472 @@
|
||||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
namespace System.ServiceModel.Web
|
||||
{
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
// DO NOT EDIT THIS CODE.
|
||||
//
|
||||
// All of the code from this class was taken from build 20717.00
|
||||
// of System.Net.HttpDateParse. If there is a bug with this code
|
||||
// it should be fixed in the original System.Net.HttpDateParse
|
||||
// and then ported here. [[....]]
|
||||
|
||||
internal static class HttpDateParse
|
||||
{
|
||||
private const int BASE_DEC = 10; // base 10
|
||||
|
||||
//
|
||||
// Date indicies used to figure out what each entry is.
|
||||
//
|
||||
|
||||
private const int DATE_INDEX_DAY_OF_WEEK = 0;
|
||||
|
||||
private const int DATE_1123_INDEX_DAY = 1;
|
||||
private const int DATE_1123_INDEX_MONTH = 2;
|
||||
private const int DATE_1123_INDEX_YEAR = 3;
|
||||
private const int DATE_1123_INDEX_HRS = 4;
|
||||
private const int DATE_1123_INDEX_MINS = 5;
|
||||
private const int DATE_1123_INDEX_SECS = 6;
|
||||
|
||||
private const int DATE_ANSI_INDEX_MONTH = 1;
|
||||
private const int DATE_ANSI_INDEX_DAY = 2;
|
||||
private const int DATE_ANSI_INDEX_HRS = 3;
|
||||
private const int DATE_ANSI_INDEX_MINS = 4;
|
||||
private const int DATE_ANSI_INDEX_SECS = 5;
|
||||
private const int DATE_ANSI_INDEX_YEAR = 6;
|
||||
|
||||
private const int DATE_INDEX_TZ = 7;
|
||||
|
||||
private const int DATE_INDEX_LAST = DATE_INDEX_TZ;
|
||||
private const int MAX_FIELD_DATE_ENTRIES = (DATE_INDEX_LAST + 1);
|
||||
|
||||
//
|
||||
// DATE_TOKEN's DWORD values used to determine what day/month we're on
|
||||
//
|
||||
|
||||
private const int DATE_TOKEN_JANUARY = 1;
|
||||
private const int DATE_TOKEN_FEBRUARY = 2;
|
||||
private const int DATE_TOKEN_MARCH = 3;
|
||||
private const int DATE_TOKEN_APRIL = 4;
|
||||
private const int DATE_TOKEN_MAY = 5;
|
||||
private const int DATE_TOKEN_JUNE = 6;
|
||||
private const int DATE_TOKEN_JULY = 7;
|
||||
private const int DATE_TOKEN_AUGUST = 8;
|
||||
private const int DATE_TOKEN_SEPTEMBER = 9;
|
||||
private const int DATE_TOKEN_OCTOBER = 10;
|
||||
private const int DATE_TOKEN_NOVEMBER = 11;
|
||||
private const int DATE_TOKEN_DECEMBER = 12;
|
||||
|
||||
private const int DATE_TOKEN_LAST_MONTH = (DATE_TOKEN_DECEMBER + 1);
|
||||
|
||||
private const int DATE_TOKEN_SUNDAY = 0;
|
||||
private const int DATE_TOKEN_MONDAY = 1;
|
||||
private const int DATE_TOKEN_TUESDAY = 2;
|
||||
private const int DATE_TOKEN_WEDNESDAY = 3;
|
||||
private const int DATE_TOKEN_THURSDAY = 4;
|
||||
private const int DATE_TOKEN_FRIDAY = 5;
|
||||
private const int DATE_TOKEN_SATURDAY = 6;
|
||||
|
||||
private const int DATE_TOKEN_LAST_DAY = (DATE_TOKEN_SATURDAY + 1);
|
||||
|
||||
private const int DATE_TOKEN_GMT = -1000;
|
||||
|
||||
private const int DATE_TOKEN_LAST = DATE_TOKEN_GMT;
|
||||
|
||||
private const int DATE_TOKEN_ERROR = (DATE_TOKEN_LAST + 1);
|
||||
|
||||
//
|
||||
// MakeUpper - takes an assumed lower character and bit manipulates into a upper.
|
||||
// (make sure the character is Lower case alpha char to begin,
|
||||
// otherwise it corrupts)
|
||||
//
|
||||
static char MakeUpper(char c)
|
||||
{
|
||||
return (Char.ToUpper(c, CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
//
|
||||
// Looks at the first three bytes of string to determine if we're looking
|
||||
// at a Day of the Week, or Month, or "GMT" string. Is inlined so that
|
||||
// the compiler can optimize this code into the caller FInternalParseHttpDate.
|
||||
//
|
||||
// Arguments:
|
||||
//
|
||||
// lpszDay - a string ptr to the first byte of the string in question.
|
||||
//
|
||||
// Return Value:
|
||||
//
|
||||
// DWORD
|
||||
// Success - The Correct date token, 0-6 for day of the week, 1-14 for month, etc
|
||||
//
|
||||
// Failure - DATE_TOKEN_ERROR
|
||||
static int MapDayMonthToDword(char[] lpszDay, int index)
|
||||
{
|
||||
switch (MakeUpper(lpszDay[index]))
|
||||
{ // make uppercase
|
||||
case 'A':
|
||||
switch (MakeUpper(lpszDay[index + 1]))
|
||||
{
|
||||
case 'P':
|
||||
return DATE_TOKEN_APRIL;
|
||||
case 'U':
|
||||
return DATE_TOKEN_AUGUST;
|
||||
|
||||
}
|
||||
return DATE_TOKEN_ERROR;
|
||||
|
||||
case 'D':
|
||||
return DATE_TOKEN_DECEMBER;
|
||||
|
||||
case 'F':
|
||||
switch (MakeUpper(lpszDay[index + 1]))
|
||||
{
|
||||
case 'R':
|
||||
return DATE_TOKEN_FRIDAY;
|
||||
case 'E':
|
||||
return DATE_TOKEN_FEBRUARY;
|
||||
}
|
||||
|
||||
return DATE_TOKEN_ERROR;
|
||||
|
||||
case 'G':
|
||||
return DATE_TOKEN_GMT;
|
||||
|
||||
case 'M':
|
||||
|
||||
switch (MakeUpper(lpszDay[index + 1]))
|
||||
{
|
||||
case 'O':
|
||||
return DATE_TOKEN_MONDAY;
|
||||
case 'A':
|
||||
switch (MakeUpper(lpszDay[index + 2]))
|
||||
{
|
||||
case 'R':
|
||||
return DATE_TOKEN_MARCH;
|
||||
case 'Y':
|
||||
return DATE_TOKEN_MAY;
|
||||
}
|
||||
|
||||
// fall through to error
|
||||
break;
|
||||
}
|
||||
|
||||
return DATE_TOKEN_ERROR;
|
||||
|
||||
case 'N':
|
||||
return DATE_TOKEN_NOVEMBER;
|
||||
|
||||
case 'J':
|
||||
|
||||
switch (MakeUpper(lpszDay[index + 1]))
|
||||
{
|
||||
case 'A':
|
||||
return DATE_TOKEN_JANUARY;
|
||||
|
||||
case 'U':
|
||||
switch (MakeUpper(lpszDay[index + 2]))
|
||||
{
|
||||
case 'N':
|
||||
return DATE_TOKEN_JUNE;
|
||||
case 'L':
|
||||
return DATE_TOKEN_JULY;
|
||||
}
|
||||
|
||||
// fall through to error
|
||||
break;
|
||||
}
|
||||
|
||||
return DATE_TOKEN_ERROR;
|
||||
|
||||
case 'O':
|
||||
return DATE_TOKEN_OCTOBER;
|
||||
|
||||
case 'S':
|
||||
|
||||
switch (MakeUpper(lpszDay[index + 1]))
|
||||
{
|
||||
case 'A':
|
||||
return DATE_TOKEN_SATURDAY;
|
||||
case 'U':
|
||||
return DATE_TOKEN_SUNDAY;
|
||||
case 'E':
|
||||
return DATE_TOKEN_SEPTEMBER;
|
||||
}
|
||||
|
||||
return DATE_TOKEN_ERROR;
|
||||
|
||||
|
||||
case 'T':
|
||||
switch (MakeUpper(lpszDay[index + 1]))
|
||||
{
|
||||
case 'U':
|
||||
return DATE_TOKEN_TUESDAY;
|
||||
case 'H':
|
||||
return DATE_TOKEN_THURSDAY;
|
||||
}
|
||||
|
||||
return DATE_TOKEN_ERROR;
|
||||
|
||||
case 'U':
|
||||
return DATE_TOKEN_GMT;
|
||||
|
||||
case 'W':
|
||||
return DATE_TOKEN_WEDNESDAY;
|
||||
|
||||
}
|
||||
|
||||
return DATE_TOKEN_ERROR;
|
||||
}
|
||||
|
||||
|
||||
// Routine Description:
|
||||
//
|
||||
// Parses through a ANSI, RFC850, or RFC1123 date format and covents it into
|
||||
// a FILETIME/SYSTEMTIME time format.
|
||||
//
|
||||
// Important this a time-critical function and should only be changed
|
||||
// with the intention of optimizing or a critical need work item.
|
||||
//
|
||||
// Arguments:
|
||||
//
|
||||
// lpft - Ptr to FILETIME structure. Used to store converted result.
|
||||
// Must be NULL if not intended to be used !!!
|
||||
//
|
||||
// lpSysTime - Ptr to SYSTEMTIME struture. Used to return Systime if needed.
|
||||
//
|
||||
// lpcszDateStr - Const Date string to parse.
|
||||
//
|
||||
// Return Value:
|
||||
//
|
||||
// BOOL
|
||||
// Success - TRUE
|
||||
// Failure - FALSE
|
||||
internal static bool ParseHttpDate(String DateString, out DateTime dtOut)
|
||||
{
|
||||
int index = 0;
|
||||
int i = 0, iLastLettered = -1;
|
||||
bool fIsANSIDateFormat = false;
|
||||
int[] rgdwDateParseResults = new int[MAX_FIELD_DATE_ENTRIES];
|
||||
bool fRet = true;
|
||||
char[] lpInputBuffer = DateString.ToCharArray();
|
||||
|
||||
dtOut = new DateTime();
|
||||
|
||||
//
|
||||
// Date Parsing v2 (1 more to go), and here is how it works...
|
||||
// We take a date string and churn through it once, converting
|
||||
// integers to integers, Month,Day, and GMT strings into integers,
|
||||
// and all is then placed IN order in a temp array.
|
||||
//
|
||||
// At the completetion of the parse stage, we simple look at
|
||||
// the data, and then map the results into the correct
|
||||
// places in the SYSTIME structure. Simple, No allocations, and
|
||||
// No dirting the data.
|
||||
//
|
||||
// The end of the function does something munging and pretting
|
||||
// up of the results to handle the year 2000, and TZ offsets
|
||||
// Note: do we need to fully handle TZs anymore?
|
||||
//
|
||||
|
||||
while (index < DateString.Length && i < MAX_FIELD_DATE_ENTRIES)
|
||||
{
|
||||
if (lpInputBuffer[index] >= '0' && lpInputBuffer[index] <= '9')
|
||||
{
|
||||
//
|
||||
// we have a numerical entry, scan through it and convent to DWORD
|
||||
//
|
||||
|
||||
rgdwDateParseResults[i] = 0;
|
||||
|
||||
do
|
||||
{
|
||||
rgdwDateParseResults[i] *= BASE_DEC;
|
||||
rgdwDateParseResults[i] += (lpInputBuffer[index] - '0');
|
||||
index++;
|
||||
} while (index < DateString.Length &&
|
||||
lpInputBuffer[index] >= '0' &&
|
||||
lpInputBuffer[index] <= '9');
|
||||
|
||||
i++; // next token
|
||||
}
|
||||
else if ((lpInputBuffer[index] >= 'A' && lpInputBuffer[index] <= 'Z') ||
|
||||
(lpInputBuffer[index] >= 'a' && lpInputBuffer[index] <= 'z'))
|
||||
{
|
||||
//
|
||||
// we have a string, should be a day, month, or GMT
|
||||
// lets skim to the end of the string
|
||||
//
|
||||
|
||||
rgdwDateParseResults[i] =
|
||||
MapDayMonthToDword(lpInputBuffer, index);
|
||||
|
||||
iLastLettered = i;
|
||||
|
||||
// We want to ignore the possibility of a time zone such as PST or EST in a non-standard
|
||||
// date format such as "Thu Dec 17 16:01:28 PST 1998" (Notice that the year is _after_ the time zone
|
||||
if ((rgdwDateParseResults[i] == DATE_TOKEN_ERROR)
|
||||
&&
|
||||
!(fIsANSIDateFormat && (i == DATE_ANSI_INDEX_YEAR)))
|
||||
{
|
||||
fRet = false;
|
||||
goto quit;
|
||||
}
|
||||
|
||||
//
|
||||
// At this point if we have a vaild string
|
||||
// at this index, we know for sure that we're
|
||||
// looking at a ANSI type DATE format.
|
||||
//
|
||||
|
||||
if (i == DATE_ANSI_INDEX_MONTH)
|
||||
{
|
||||
fIsANSIDateFormat = true;
|
||||
}
|
||||
|
||||
//
|
||||
// Read past the end of the current set of alpha characters,
|
||||
// as MapDayMonthToDword only peeks at a few characters
|
||||
//
|
||||
|
||||
do
|
||||
{
|
||||
index++;
|
||||
} while (index < DateString.Length &&
|
||||
((lpInputBuffer[index] >= 'A' && lpInputBuffer[index] <= 'Z') ||
|
||||
(lpInputBuffer[index] >= 'a' && lpInputBuffer[index] <= 'z')));
|
||||
|
||||
i++; // next token
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// For the generic case its either a space, comma, semi-colon, etc.
|
||||
// the point is we really don't care, nor do we need to waste time
|
||||
// worring about it (the orginal code did). The point is we
|
||||
// care about the actual date information, So we just advance to the
|
||||
// next lexume.
|
||||
//
|
||||
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// We're finished parsing the string, now take the parsed tokens
|
||||
// and turn them to the actual structured information we care about.
|
||||
// So we build lpSysTime from the Array, using a local if none is passed in.
|
||||
//
|
||||
|
||||
int year;
|
||||
int month;
|
||||
int day;
|
||||
int hour;
|
||||
int minute;
|
||||
int second;
|
||||
int millisecond;
|
||||
|
||||
millisecond = 0;
|
||||
|
||||
if (fIsANSIDateFormat)
|
||||
{
|
||||
day = rgdwDateParseResults[DATE_ANSI_INDEX_DAY];
|
||||
month = rgdwDateParseResults[DATE_ANSI_INDEX_MONTH];
|
||||
hour = rgdwDateParseResults[DATE_ANSI_INDEX_HRS];
|
||||
minute = rgdwDateParseResults[DATE_ANSI_INDEX_MINS];
|
||||
second = rgdwDateParseResults[DATE_ANSI_INDEX_SECS];
|
||||
if (iLastLettered != DATE_ANSI_INDEX_YEAR)
|
||||
{
|
||||
year = rgdwDateParseResults[DATE_ANSI_INDEX_YEAR];
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is a fix to get around toString/toGMTstring (where the timezone is
|
||||
// appended at the end. (See above)
|
||||
year = rgdwDateParseResults[DATE_INDEX_TZ];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
day = rgdwDateParseResults[DATE_1123_INDEX_DAY];
|
||||
month = rgdwDateParseResults[DATE_1123_INDEX_MONTH];
|
||||
year = rgdwDateParseResults[DATE_1123_INDEX_YEAR];
|
||||
hour = rgdwDateParseResults[DATE_1123_INDEX_HRS];
|
||||
minute = rgdwDateParseResults[DATE_1123_INDEX_MINS];
|
||||
second = rgdwDateParseResults[DATE_1123_INDEX_SECS];
|
||||
}
|
||||
|
||||
//
|
||||
// Normalize the year, 90 == 1990, handle the year 2000, 02 == 2002
|
||||
// This is Year 2000 handling folks!!! We get this wrong and
|
||||
// we all look bad.
|
||||
//
|
||||
|
||||
if (year < 100)
|
||||
{
|
||||
year += ((year < 80) ? 2000 : 1900);
|
||||
}
|
||||
|
||||
//
|
||||
// if we got misformed time, then plug in the current time
|
||||
// !lpszHrs || !lpszMins || !lpszSec
|
||||
//
|
||||
|
||||
if ((i < 4)
|
||||
|| (day > 31)
|
||||
|| (hour > 23)
|
||||
|| (minute > 59)
|
||||
|| (second > 59))
|
||||
{
|
||||
fRet = false;
|
||||
goto quit;
|
||||
}
|
||||
|
||||
//
|
||||
// Now do the DateTime conversion
|
||||
//
|
||||
|
||||
dtOut = new DateTime(year, month, day, hour, minute, second, millisecond);
|
||||
|
||||
//
|
||||
// we want the system time to be accurate. This is _suhlow_
|
||||
// The time passed in is in the local time zone; we have to convert this into GMT.
|
||||
//
|
||||
|
||||
if (iLastLettered == DATE_ANSI_INDEX_YEAR)
|
||||
{
|
||||
// this should be an unusual case.
|
||||
dtOut = dtOut.ToUniversalTime();
|
||||
}
|
||||
|
||||
//
|
||||
// If we have an Offset to another Time Zone
|
||||
// then convert to appropriate GMT time
|
||||
//
|
||||
|
||||
if ((i > DATE_INDEX_TZ &&
|
||||
rgdwDateParseResults[DATE_INDEX_TZ] != DATE_TOKEN_GMT))
|
||||
{
|
||||
|
||||
//
|
||||
// if we received +/-nnnn as offset (hhmm), modify the output FILETIME
|
||||
//
|
||||
|
||||
double offset;
|
||||
|
||||
offset = (double)rgdwDateParseResults[DATE_INDEX_TZ];
|
||||
dtOut.AddHours(offset);
|
||||
}
|
||||
|
||||
// In the end, we leave it all in LocalTime
|
||||
|
||||
dtOut = dtOut.ToLocalTime();
|
||||
|
||||
quit:
|
||||
|
||||
return fRet;
|
||||
}
|
||||
}
|
||||
}
|
16
external/referencesource/System.ServiceModel.Web/System/ServiceModel/Web/IWebFaultException.cs
vendored
Normal file
16
external/referencesource/System.ServiceModel.Web/System/ServiceModel/Web/IWebFaultException.cs
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
namespace System.ServiceModel.Web
|
||||
{
|
||||
using System;
|
||||
using System.Net;
|
||||
|
||||
interface IWebFaultException
|
||||
{
|
||||
HttpStatusCode StatusCode { get; }
|
||||
Type DetailType { get; }
|
||||
object DetailObject { get; }
|
||||
Type[] KnownTypes { get; }
|
||||
}
|
||||
}
|
@ -0,0 +1,456 @@
|
||||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
#pragma warning disable 1634, 1691
|
||||
|
||||
namespace System.ServiceModel.Web
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
using System.Net.Mime;
|
||||
using System.Runtime;
|
||||
using System.ServiceModel;
|
||||
using System.ServiceModel.Channels;
|
||||
using System.ServiceModel.Dispatcher;
|
||||
|
||||
public class IncomingWebRequestContext
|
||||
{
|
||||
static readonly string HttpGetMethod = "GET";
|
||||
static readonly string HttpHeadMethod = "HEAD";
|
||||
static readonly string HttpPutMethod = "PUT";
|
||||
static readonly string HttpPostMethod = "POST";
|
||||
static readonly string HttpDeleteMethod = "DELETE";
|
||||
|
||||
Collection<ContentType> cachedAcceptHeaderElements;
|
||||
string acceptHeaderWhenHeaderElementsCached;
|
||||
internal const string UriTemplateMatchResultsPropertyName = "UriTemplateMatchResults";
|
||||
OperationContext operationContext;
|
||||
|
||||
internal IncomingWebRequestContext(OperationContext operationContext)
|
||||
{
|
||||
Fx.Assert(operationContext != null, "operationContext is null");
|
||||
this.operationContext = operationContext;
|
||||
}
|
||||
|
||||
public string Accept
|
||||
{
|
||||
get { return EnsureMessageProperty().Headers[HttpRequestHeader.Accept]; }
|
||||
}
|
||||
|
||||
public long ContentLength
|
||||
{
|
||||
get { return long.Parse(this.EnsureMessageProperty().Headers[HttpRequestHeader.ContentLength], CultureInfo.InvariantCulture); }
|
||||
}
|
||||
|
||||
public string ContentType
|
||||
{
|
||||
get { return this.EnsureMessageProperty().Headers[HttpRequestHeader.ContentType]; }
|
||||
}
|
||||
|
||||
public IEnumerable<string> IfMatch
|
||||
{
|
||||
get
|
||||
{
|
||||
string ifMatchHeader = MessageProperty.Headers[HttpRequestHeader.IfMatch];
|
||||
return (string.IsNullOrEmpty(ifMatchHeader)) ? null : Utility.QuoteAwareStringSplit(ifMatchHeader);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<string> IfNoneMatch
|
||||
{
|
||||
get
|
||||
{
|
||||
string ifNoneMatchHeader = MessageProperty.Headers[HttpRequestHeader.IfNoneMatch];
|
||||
return (string.IsNullOrEmpty(ifNoneMatchHeader)) ? null : Utility.QuoteAwareStringSplit(ifNoneMatchHeader);
|
||||
}
|
||||
}
|
||||
|
||||
public DateTime? IfModifiedSince
|
||||
{
|
||||
get
|
||||
{
|
||||
string dateTime = this.MessageProperty.Headers[HttpRequestHeader.IfModifiedSince];
|
||||
if (!string.IsNullOrEmpty(dateTime))
|
||||
{
|
||||
DateTime parsedDateTime;
|
||||
if (HttpDateParse.ParseHttpDate(dateTime, out parsedDateTime))
|
||||
{
|
||||
return parsedDateTime;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public DateTime? IfUnmodifiedSince
|
||||
{
|
||||
get
|
||||
{
|
||||
string dateTime = this.MessageProperty.Headers[HttpRequestHeader.IfUnmodifiedSince];
|
||||
if (!string.IsNullOrEmpty(dateTime))
|
||||
{
|
||||
DateTime parsedDateTime;
|
||||
if (HttpDateParse.ParseHttpDate(dateTime, out parsedDateTime))
|
||||
{
|
||||
return parsedDateTime;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public WebHeaderCollection Headers
|
||||
{
|
||||
get { return this.EnsureMessageProperty().Headers; }
|
||||
}
|
||||
|
||||
public string Method
|
||||
{
|
||||
get { return this.EnsureMessageProperty().Method; }
|
||||
}
|
||||
|
||||
public UriTemplateMatch UriTemplateMatch
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.operationContext.IncomingMessageProperties.ContainsKey(UriTemplateMatchResultsPropertyName))
|
||||
{
|
||||
return this.operationContext.IncomingMessageProperties[UriTemplateMatchResultsPropertyName] as UriTemplateMatch;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
this.operationContext.IncomingMessageProperties[UriTemplateMatchResultsPropertyName] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public string UserAgent
|
||||
{
|
||||
get { return this.EnsureMessageProperty().Headers[HttpRequestHeader.UserAgent]; }
|
||||
}
|
||||
|
||||
HttpRequestMessageProperty MessageProperty
|
||||
{
|
||||
get
|
||||
{
|
||||
if (operationContext.IncomingMessageProperties == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (!operationContext.IncomingMessageProperties.ContainsKey(HttpRequestMessageProperty.Name))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return operationContext.IncomingMessageProperties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty;
|
||||
}
|
||||
}
|
||||
|
||||
public void CheckConditionalRetrieve(string entityTag)
|
||||
{
|
||||
string validEtag = OutgoingWebResponseContext.GenerateValidEtagFromString(entityTag);
|
||||
CheckConditionalRetrieveWithValidatedEtag(validEtag);
|
||||
}
|
||||
|
||||
public void CheckConditionalRetrieve(int entityTag)
|
||||
{
|
||||
string validEtag = OutgoingWebResponseContext.GenerateValidEtag(entityTag);
|
||||
CheckConditionalRetrieveWithValidatedEtag(validEtag);
|
||||
}
|
||||
|
||||
public void CheckConditionalRetrieve(long entityTag)
|
||||
{
|
||||
string validEtag = OutgoingWebResponseContext.GenerateValidEtag(entityTag);
|
||||
CheckConditionalRetrieveWithValidatedEtag(validEtag);
|
||||
}
|
||||
|
||||
public void CheckConditionalRetrieve(Guid entityTag)
|
||||
{
|
||||
string validEtag = OutgoingWebResponseContext.GenerateValidEtag(entityTag);
|
||||
CheckConditionalRetrieveWithValidatedEtag(validEtag);
|
||||
}
|
||||
|
||||
public void CheckConditionalRetrieve(DateTime lastModified)
|
||||
{
|
||||
if (!string.Equals(this.Method, IncomingWebRequestContext.HttpGetMethod, StringComparison.OrdinalIgnoreCase) &&
|
||||
!string.Equals(this.Method, IncomingWebRequestContext.HttpHeadMethod, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
|
||||
SR2.GetString(SR2.ConditionalRetrieveGetAndHeadOnly, this.Method)));
|
||||
}
|
||||
|
||||
DateTime? ifModifiedSince = this.IfModifiedSince;
|
||||
if (ifModifiedSince.HasValue)
|
||||
{
|
||||
long ticksDifference = lastModified.ToUniversalTime().Ticks - ifModifiedSince.Value.ToUniversalTime().Ticks;
|
||||
if (ticksDifference < TimeSpan.TicksPerSecond)
|
||||
{
|
||||
WebOperationContext.Current.OutgoingResponse.LastModified = lastModified;
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new WebFaultException(HttpStatusCode.NotModified));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void CheckConditionalUpdate(string entityTag)
|
||||
{
|
||||
string validEtag = OutgoingWebResponseContext.GenerateValidEtagFromString(entityTag);
|
||||
CheckConditionalUpdateWithValidatedEtag(validEtag);
|
||||
}
|
||||
|
||||
public void CheckConditionalUpdate(int entityTag)
|
||||
{
|
||||
string validEtag = OutgoingWebResponseContext.GenerateValidEtag(entityTag);
|
||||
CheckConditionalUpdateWithValidatedEtag(validEtag);
|
||||
}
|
||||
|
||||
public void CheckConditionalUpdate(long entityTag)
|
||||
{
|
||||
string validEtag = OutgoingWebResponseContext.GenerateValidEtag(entityTag);
|
||||
CheckConditionalUpdateWithValidatedEtag(validEtag);
|
||||
}
|
||||
|
||||
public void CheckConditionalUpdate(Guid entityTag)
|
||||
{
|
||||
string validEtag = OutgoingWebResponseContext.GenerateValidEtag(entityTag);
|
||||
CheckConditionalUpdateWithValidatedEtag(validEtag);
|
||||
}
|
||||
|
||||
public Collection<ContentType> GetAcceptHeaderElements()
|
||||
{
|
||||
string acceptHeader = this.Accept;
|
||||
if (cachedAcceptHeaderElements == null ||
|
||||
(!string.Equals(acceptHeaderWhenHeaderElementsCached, acceptHeader, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
if (string.IsNullOrEmpty(acceptHeader))
|
||||
{
|
||||
cachedAcceptHeaderElements = new Collection<ContentType>();
|
||||
acceptHeaderWhenHeaderElementsCached = acceptHeader;
|
||||
}
|
||||
else
|
||||
{
|
||||
List<ContentType> contentTypeList = new List<ContentType>();
|
||||
int offset = 0;
|
||||
while (true)
|
||||
{
|
||||
string nextItem = Utility.QuoteAwareSubString(acceptHeader, ref offset);
|
||||
if (nextItem == null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
ContentType contentType = Utility.GetContentTypeOrNull(nextItem);
|
||||
if (contentType != null)
|
||||
{
|
||||
contentTypeList.Add(contentType);
|
||||
}
|
||||
}
|
||||
|
||||
contentTypeList.Sort(new AcceptHeaderElementComparer());
|
||||
cachedAcceptHeaderElements = new Collection<ContentType>(contentTypeList);
|
||||
acceptHeaderWhenHeaderElementsCached = acceptHeader;
|
||||
}
|
||||
}
|
||||
return cachedAcceptHeaderElements;
|
||||
}
|
||||
|
||||
HttpRequestMessageProperty EnsureMessageProperty()
|
||||
{
|
||||
if (this.MessageProperty == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
|
||||
SR2.GetString(SR2.HttpContextNoIncomingMessageProperty, typeof(HttpRequestMessageProperty).Name)));
|
||||
}
|
||||
return this.MessageProperty;
|
||||
}
|
||||
|
||||
|
||||
void CheckConditionalRetrieveWithValidatedEtag(string entityTag)
|
||||
{
|
||||
if (!string.Equals(this.Method, IncomingWebRequestContext.HttpGetMethod, StringComparison.OrdinalIgnoreCase) &&
|
||||
!string.Equals(this.Method, IncomingWebRequestContext.HttpHeadMethod, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
|
||||
SR2.GetString(SR2.ConditionalRetrieveGetAndHeadOnly, this.Method)));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(entityTag))
|
||||
{
|
||||
string entityTagHeader = this.Headers[HttpRequestHeader.IfNoneMatch];
|
||||
if (!string.IsNullOrEmpty(entityTagHeader))
|
||||
{
|
||||
if (IsWildCardCharacter(entityTagHeader) ||
|
||||
DoesHeaderContainEtag(entityTagHeader, entityTag))
|
||||
{
|
||||
// set response entityTag directly because it has already been validated
|
||||
WebOperationContext.Current.OutgoingResponse.ETag = entityTag;
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new WebFaultException(HttpStatusCode.NotModified));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CheckConditionalUpdateWithValidatedEtag(string entityTag)
|
||||
{
|
||||
bool isPutMethod = string.Equals(this.Method, IncomingWebRequestContext.HttpPutMethod, StringComparison.OrdinalIgnoreCase);
|
||||
if (!isPutMethod &&
|
||||
!string.Equals(this.Method, IncomingWebRequestContext.HttpPostMethod, StringComparison.OrdinalIgnoreCase) &&
|
||||
!string.Equals(this.Method, IncomingWebRequestContext.HttpDeleteMethod, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
|
||||
SR2.GetString(SR2.ConditionalUpdatePutPostAndDeleteOnly, this.Method)));
|
||||
}
|
||||
|
||||
string headerOfInterest;
|
||||
|
||||
// if the current entityTag is null then the resource doesn't currently exist and the
|
||||
// a PUT request should only succeed if If-None-Match equals '*'.
|
||||
if (isPutMethod && string.IsNullOrEmpty(entityTag))
|
||||
{
|
||||
headerOfInterest = this.Headers[HttpRequestHeader.IfNoneMatch];
|
||||
if (string.IsNullOrEmpty(headerOfInterest) ||
|
||||
!IsWildCardCharacter(headerOfInterest))
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new WebFaultException(HttpStatusCode.PreconditionFailed));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// all remaining cases are with an If-Match header
|
||||
headerOfInterest = this.Headers[HttpRequestHeader.IfMatch];
|
||||
if (string.IsNullOrEmpty(headerOfInterest) ||
|
||||
(!IsWildCardCharacter(headerOfInterest) &&
|
||||
!DoesHeaderContainEtag(headerOfInterest, entityTag)))
|
||||
{
|
||||
// set response entityTag directly because it has already been validated
|
||||
WebOperationContext.Current.OutgoingResponse.ETag = entityTag;
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new WebFaultException(HttpStatusCode.PreconditionFailed));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static bool DoesHeaderContainEtag(string header, string entityTag)
|
||||
{
|
||||
int offset = 0;
|
||||
while (true)
|
||||
{
|
||||
string nextEntityTag = Utility.QuoteAwareSubString(header, ref offset);
|
||||
if (nextEntityTag == null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (string.Equals(nextEntityTag, entityTag, StringComparison.Ordinal))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool IsWildCardCharacter(string header)
|
||||
{
|
||||
return (header.Trim() == "*");
|
||||
}
|
||||
}
|
||||
|
||||
class AcceptHeaderElementComparer : IComparer<ContentType>
|
||||
{
|
||||
static NumberStyles numberStyles = NumberStyles.AllowDecimalPoint;
|
||||
|
||||
public int Compare(ContentType x, ContentType y)
|
||||
{
|
||||
string[] xTypeSubType = x.MediaType.Split('/');
|
||||
string[] yTypeSubType = y.MediaType.Split('/');
|
||||
|
||||
Fx.Assert(xTypeSubType.Length == 2, "The creation of the ContentType would have failed if there wasn't a type and subtype.");
|
||||
Fx.Assert(yTypeSubType.Length == 2, "The creation of the ContentType would have failed if there wasn't a type and subtype.");
|
||||
|
||||
if (string.Equals(xTypeSubType[0], yTypeSubType[0], StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (string.Equals(xTypeSubType[1], yTypeSubType[1], StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// need to check the number of parameters to determine which is more specific
|
||||
bool xHasParam = HasParameters(x);
|
||||
bool yHasParam = HasParameters(y);
|
||||
if (xHasParam && !yHasParam)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else if (!xHasParam && yHasParam)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (xTypeSubType[1][0] == '*' && xTypeSubType[1].Length == 1)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (yTypeSubType[1][0] == '*' && yTypeSubType[1].Length == 1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (xTypeSubType[0][0] == '*' && xTypeSubType[0].Length == 1)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else if (yTypeSubType[0][0] == '*' && yTypeSubType[0].Length == 1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
decimal qualityDifference = GetQualityFactor(x) - GetQualityFactor(y);
|
||||
if (qualityDifference < 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else if (qualityDifference > 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
decimal GetQualityFactor(ContentType contentType)
|
||||
{
|
||||
decimal result;
|
||||
foreach (string key in contentType.Parameters.Keys)
|
||||
{
|
||||
if (string.Equals("q", key, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (decimal.TryParse(contentType.Parameters[key], numberStyles, CultureInfo.InvariantCulture, out result) &&
|
||||
(result <= (decimal)1.0))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (decimal)1.0;
|
||||
}
|
||||
|
||||
bool HasParameters(ContentType contentType)
|
||||
{
|
||||
int number = 0;
|
||||
foreach (string param in contentType.Parameters.Keys)
|
||||
{
|
||||
if (!string.Equals("q", param, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
number++;
|
||||
}
|
||||
}
|
||||
|
||||
return (number > 0);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
#pragma warning disable 1634, 1691
|
||||
|
||||
namespace System.ServiceModel.Web
|
||||
{
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
using System.Runtime;
|
||||
using System.ServiceModel;
|
||||
using System.ServiceModel.Channels;
|
||||
|
||||
public class IncomingWebResponseContext
|
||||
{
|
||||
OperationContext operationContext;
|
||||
internal IncomingWebResponseContext(OperationContext operationContext)
|
||||
{
|
||||
Fx.Assert(operationContext != null, "operationContext is null");
|
||||
this.operationContext = operationContext;
|
||||
}
|
||||
|
||||
public long ContentLength
|
||||
{ get { return long.Parse(EnsureMessageProperty().Headers[HttpResponseHeader.ContentLength], CultureInfo.InvariantCulture); } }
|
||||
|
||||
public string ContentType
|
||||
{ get { return EnsureMessageProperty().Headers[HttpResponseHeader.ContentType]; } }
|
||||
|
||||
public string ETag
|
||||
{ get { return EnsureMessageProperty().Headers[HttpResponseHeader.ETag]; } }
|
||||
|
||||
public WebHeaderCollection Headers
|
||||
{ get { return EnsureMessageProperty().Headers; } }
|
||||
|
||||
public string Location
|
||||
{ get { return EnsureMessageProperty().Headers[HttpResponseHeader.Location]; } }
|
||||
|
||||
public HttpStatusCode StatusCode
|
||||
{ get { return this.EnsureMessageProperty().StatusCode; } }
|
||||
|
||||
public string StatusDescription
|
||||
{ get { return this.EnsureMessageProperty().StatusDescription; } }
|
||||
|
||||
HttpResponseMessageProperty MessageProperty
|
||||
{ get
|
||||
{
|
||||
if (operationContext.IncomingMessageProperties == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (!operationContext.IncomingMessageProperties.ContainsKey(HttpResponseMessageProperty.Name))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return operationContext.IncomingMessageProperties[HttpResponseMessageProperty.Name] as HttpResponseMessageProperty;
|
||||
}
|
||||
}
|
||||
|
||||
HttpResponseMessageProperty EnsureMessageProperty()
|
||||
{
|
||||
if (this.MessageProperty == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
|
||||
SR2.GetString(SR2.HttpContextNoIncomingMessageProperty, typeof(HttpResponseMessageProperty).Name)));
|
||||
}
|
||||
return this.MessageProperty;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
|
||||
namespace System.ServiceModel.Web
|
||||
{
|
||||
using System;
|
||||
using System.ServiceModel.Description;
|
||||
using System.ServiceModel.Dispatcher;
|
||||
using System.ServiceModel.Channels;
|
||||
using System.ServiceModel.Web;
|
||||
using System.ServiceModel.Administration;
|
||||
using System.Collections.Generic;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
|
||||
public sealed class JavascriptCallbackBehaviorAttribute : Attribute, IContractBehavior
|
||||
{
|
||||
string urlParameterName;
|
||||
|
||||
public string UrlParameterName
|
||||
{
|
||||
get { return urlParameterName; }
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value"));
|
||||
}
|
||||
else
|
||||
{
|
||||
urlParameterName = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public JavascriptCallbackBehaviorAttribute()
|
||||
{
|
||||
}
|
||||
|
||||
public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
|
||||
{
|
||||
}
|
||||
|
||||
public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
|
||||
{
|
||||
}
|
||||
|
||||
public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.DispatchRuntime dispatchRuntime)
|
||||
{
|
||||
}
|
||||
|
||||
public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,100 @@
|
||||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
#pragma warning disable 1634, 1691
|
||||
|
||||
namespace System.ServiceModel.Web
|
||||
{
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
using System.Runtime;
|
||||
using System.ServiceModel;
|
||||
using System.ServiceModel.Channels;
|
||||
|
||||
public class OutgoingWebRequestContext
|
||||
{
|
||||
OperationContext operationContext;
|
||||
internal OutgoingWebRequestContext(OperationContext operationContext)
|
||||
{
|
||||
Fx.Assert(operationContext != null, "operationContext is null");
|
||||
this.operationContext = operationContext;
|
||||
}
|
||||
|
||||
public string Accept
|
||||
{
|
||||
get { return this.MessageProperty.Headers[HttpRequestHeader.Accept]; }
|
||||
set { this.MessageProperty.Headers[HttpRequestHeader.Accept] = value; }
|
||||
}
|
||||
|
||||
public long ContentLength
|
||||
{
|
||||
get { return long.Parse(this.MessageProperty.Headers[HttpRequestHeader.ContentLength], CultureInfo.InvariantCulture); }
|
||||
set { this.MessageProperty.Headers[HttpRequestHeader.ContentLength] = value.ToString(CultureInfo.InvariantCulture); }
|
||||
}
|
||||
|
||||
public string ContentType
|
||||
{
|
||||
get { return this.MessageProperty.Headers[HttpRequestHeader.ContentType]; }
|
||||
set { this.MessageProperty.Headers[HttpRequestHeader.ContentType] = value; }
|
||||
}
|
||||
|
||||
public WebHeaderCollection Headers
|
||||
{
|
||||
get { return this.MessageProperty.Headers; }
|
||||
}
|
||||
|
||||
public string IfMatch
|
||||
{
|
||||
get { return this.MessageProperty.Headers[HttpRequestHeader.IfMatch]; }
|
||||
set { this.MessageProperty.Headers[HttpRequestHeader.IfMatch] = value; }
|
||||
}
|
||||
|
||||
public string IfModifiedSince
|
||||
{
|
||||
get { return this.MessageProperty.Headers[HttpRequestHeader.IfModifiedSince]; }
|
||||
set { this.MessageProperty.Headers[HttpRequestHeader.IfModifiedSince] = value; }
|
||||
}
|
||||
|
||||
public string IfNoneMatch
|
||||
{
|
||||
get { return this.MessageProperty.Headers[HttpRequestHeader.IfNoneMatch]; }
|
||||
set { this.MessageProperty.Headers[HttpRequestHeader.IfNoneMatch] = value; }
|
||||
}
|
||||
|
||||
public string IfUnmodifiedSince
|
||||
{
|
||||
get { return this.MessageProperty.Headers[HttpRequestHeader.IfUnmodifiedSince]; }
|
||||
set { this.MessageProperty.Headers[HttpRequestHeader.IfUnmodifiedSince] = value; }
|
||||
}
|
||||
|
||||
public string Method
|
||||
{
|
||||
get { return this.MessageProperty.Method; }
|
||||
set { this.MessageProperty.Method = value; }
|
||||
}
|
||||
|
||||
public bool SuppressEntityBody
|
||||
{
|
||||
get { return this.MessageProperty.SuppressEntityBody; }
|
||||
set { this.MessageProperty.SuppressEntityBody = value; }
|
||||
}
|
||||
|
||||
public string UserAgent
|
||||
{
|
||||
get { return this.MessageProperty.Headers[HttpRequestHeader.UserAgent]; }
|
||||
set { this.MessageProperty.Headers[HttpRequestHeader.UserAgent] = value; }
|
||||
}
|
||||
|
||||
HttpRequestMessageProperty MessageProperty
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!operationContext.OutgoingMessageProperties.ContainsKey(HttpRequestMessageProperty.Name))
|
||||
{
|
||||
operationContext.OutgoingMessageProperties.Add(HttpRequestMessageProperty.Name, new HttpRequestMessageProperty());
|
||||
}
|
||||
return operationContext.OutgoingMessageProperties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,320 @@
|
||||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
#pragma warning disable 1634, 1691
|
||||
|
||||
namespace System.ServiceModel.Web
|
||||
{
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
using System.Runtime;
|
||||
using System.ServiceModel;
|
||||
using System.ServiceModel.Channels;
|
||||
using System.ServiceModel.Description;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class OutgoingWebResponseContext
|
||||
{
|
||||
internal static readonly string WebResponseFormatPropertyName = "WebResponseFormatProperty";
|
||||
internal static readonly string AutomatedFormatSelectionContentTypePropertyName = "AutomatedFormatSelectionContentTypePropertyName";
|
||||
|
||||
Encoding bindingWriteEncoding = null;
|
||||
|
||||
OperationContext operationContext;
|
||||
internal OutgoingWebResponseContext(OperationContext operationContext)
|
||||
{
|
||||
Fx.Assert(operationContext != null, "operationContext is null");
|
||||
this.operationContext = operationContext;
|
||||
}
|
||||
|
||||
public long ContentLength
|
||||
{
|
||||
get { return long.Parse(this.MessageProperty.Headers[HttpResponseHeader.ContentLength], CultureInfo.InvariantCulture); }
|
||||
set { this.MessageProperty.Headers[HttpResponseHeader.ContentLength] = value.ToString(CultureInfo.InvariantCulture); }
|
||||
}
|
||||
|
||||
public string ContentType
|
||||
{
|
||||
get { return this.MessageProperty.Headers[HttpResponseHeader.ContentType]; }
|
||||
set { this.MessageProperty.Headers[HttpResponseHeader.ContentType] = value; }
|
||||
}
|
||||
|
||||
public string ETag
|
||||
{
|
||||
get { return this.MessageProperty.Headers[HttpResponseHeader.ETag]; }
|
||||
set { this.MessageProperty.Headers[HttpResponseHeader.ETag] = value; }
|
||||
}
|
||||
|
||||
public WebHeaderCollection Headers
|
||||
{
|
||||
get { return this.MessageProperty.Headers; }
|
||||
}
|
||||
|
||||
public DateTime LastModified
|
||||
{
|
||||
get
|
||||
{
|
||||
string dateTime = this.MessageProperty.Headers[HttpRequestHeader.LastModified];
|
||||
if (!string.IsNullOrEmpty(dateTime))
|
||||
{
|
||||
DateTime parsedDateTime;
|
||||
if (DateTime.TryParse(dateTime, CultureInfo.InvariantCulture, DateTimeStyles.None, out parsedDateTime))
|
||||
{
|
||||
return parsedDateTime;
|
||||
}
|
||||
}
|
||||
return DateTime.MinValue;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.MessageProperty.Headers[HttpResponseHeader.LastModified] =
|
||||
(value.Kind == DateTimeKind.Utc ?
|
||||
value.ToString("R", CultureInfo.InvariantCulture) :
|
||||
value.ToUniversalTime().ToString("R", CultureInfo.InvariantCulture));
|
||||
}
|
||||
}
|
||||
|
||||
public string Location
|
||||
{
|
||||
get { return this.MessageProperty.Headers[HttpResponseHeader.Location]; }
|
||||
set { this.MessageProperty.Headers[HttpResponseHeader.Location] = value; }
|
||||
}
|
||||
|
||||
public HttpStatusCode StatusCode
|
||||
{
|
||||
get { return this.MessageProperty.StatusCode; }
|
||||
set { this.MessageProperty.StatusCode = value; }
|
||||
}
|
||||
|
||||
public string StatusDescription
|
||||
{
|
||||
get { return this.MessageProperty.StatusDescription; }
|
||||
set { this.MessageProperty.StatusDescription = value; }
|
||||
}
|
||||
|
||||
public bool SuppressEntityBody
|
||||
{
|
||||
get { return this.MessageProperty.SuppressEntityBody; }
|
||||
set { this.MessageProperty.SuppressEntityBody = value; }
|
||||
}
|
||||
|
||||
public WebMessageFormat? Format
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!operationContext.OutgoingMessageProperties.ContainsKey(WebResponseFormatPropertyName))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return operationContext.OutgoingMessageProperties[WebResponseFormatPropertyName] as WebMessageFormat?;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value.HasValue)
|
||||
{
|
||||
if (!WebMessageFormatHelper.IsDefined(value.Value))
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value"));
|
||||
}
|
||||
else
|
||||
{
|
||||
operationContext.OutgoingMessageProperties[WebResponseFormatPropertyName] = value.Value;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
operationContext.OutgoingMessageProperties[WebResponseFormatPropertyName] = null;
|
||||
}
|
||||
this.AutomatedFormatSelectionContentType = null;
|
||||
}
|
||||
}
|
||||
|
||||
// This is an internal property because we need to carry the content-type that was selected by the FormatSelectingMessageInspector
|
||||
// forward so that the formatter has access to it. However, we dond't want to use the ContentType property on this, because then
|
||||
// developers would have to clear the ContentType property manually when overriding the format set by the
|
||||
// FormatSelectingMessageInspector
|
||||
internal string AutomatedFormatSelectionContentType
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!operationContext.OutgoingMessageProperties.ContainsKey(AutomatedFormatSelectionContentTypePropertyName))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return operationContext.OutgoingMessageProperties[AutomatedFormatSelectionContentTypePropertyName] as string;
|
||||
}
|
||||
set
|
||||
{
|
||||
operationContext.OutgoingMessageProperties[AutomatedFormatSelectionContentTypePropertyName] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public Encoding BindingWriteEncoding
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.bindingWriteEncoding == null)
|
||||
{
|
||||
string endpointId = this.operationContext.EndpointDispatcher.Id;
|
||||
Fx.Assert(endpointId != null, "There should always be an valid EndpointDispatcher.Id");
|
||||
foreach (ServiceEndpoint endpoint in this.operationContext.Host.Description.Endpoints)
|
||||
{
|
||||
if (endpoint.Id == endpointId)
|
||||
{
|
||||
WebMessageEncodingBindingElement encodingElement = endpoint.Binding.CreateBindingElements().Find<WebMessageEncodingBindingElement>() as WebMessageEncodingBindingElement;
|
||||
if (encodingElement != null)
|
||||
{
|
||||
this.bindingWriteEncoding = encodingElement.WriteEncoding;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.bindingWriteEncoding;
|
||||
}
|
||||
}
|
||||
|
||||
internal HttpResponseMessageProperty MessageProperty
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!operationContext.OutgoingMessageProperties.ContainsKey(HttpResponseMessageProperty.Name))
|
||||
{
|
||||
operationContext.OutgoingMessageProperties.Add(HttpResponseMessageProperty.Name, new HttpResponseMessageProperty());
|
||||
}
|
||||
return operationContext.OutgoingMessageProperties[HttpResponseMessageProperty.Name] as HttpResponseMessageProperty;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetETag(string entityTag)
|
||||
{
|
||||
this.ETag = GenerateValidEtagFromString(entityTag);
|
||||
}
|
||||
|
||||
public void SetETag(int entityTag)
|
||||
{
|
||||
this.ETag = GenerateValidEtag(entityTag);
|
||||
}
|
||||
|
||||
public void SetETag(long entityTag)
|
||||
{
|
||||
this.ETag = GenerateValidEtag(entityTag);
|
||||
}
|
||||
|
||||
public void SetETag(Guid entityTag)
|
||||
{
|
||||
this.ETag = GenerateValidEtag(entityTag);
|
||||
}
|
||||
|
||||
public void SetStatusAsCreated(Uri locationUri)
|
||||
{
|
||||
if (locationUri == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("locationUri");
|
||||
}
|
||||
this.StatusCode = HttpStatusCode.Created;
|
||||
this.Location = locationUri.ToString();
|
||||
}
|
||||
|
||||
public void SetStatusAsNotFound()
|
||||
{
|
||||
this.StatusCode = HttpStatusCode.NotFound;
|
||||
}
|
||||
|
||||
public void SetStatusAsNotFound(string description)
|
||||
{
|
||||
this.StatusCode = HttpStatusCode.NotFound;
|
||||
this.StatusDescription = description;
|
||||
}
|
||||
|
||||
internal static string GenerateValidEtagFromString(string entityTag)
|
||||
{
|
||||
// This method will generate a valid entityTag from a string by doing the following:
|
||||
// 1) Adding surrounding double quotes if the string doesn't already start and end with them
|
||||
// 2) Escaping any internal double quotes that aren't already escaped (preceded with a backslash)
|
||||
// 3) If a string starts with a double quote but doesn't end with one, or vice-versa, then the
|
||||
// double quote is considered internal and is escaped.
|
||||
|
||||
if (string.IsNullOrEmpty(entityTag))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (entityTag.StartsWith("W/\"", StringComparison.OrdinalIgnoreCase) &&
|
||||
entityTag.EndsWith("\"", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
|
||||
SR2.GetString(SR2.WeakEntityTagsNotSupported, entityTag)));
|
||||
}
|
||||
|
||||
List<int> escapeCharacterInsertIndices = null;
|
||||
int lastEtagIndex = entityTag.Length - 1;
|
||||
bool startsWithQuote = entityTag[0] == '\"';
|
||||
bool endsWithQuote = entityTag[lastEtagIndex] == '\"';
|
||||
|
||||
// special case where the entityTag is a single character, a double quote, '"'
|
||||
if (lastEtagIndex == 0 && startsWithQuote)
|
||||
{
|
||||
endsWithQuote = false;
|
||||
}
|
||||
|
||||
bool needsSurroundingQuotes = !startsWithQuote || !endsWithQuote;
|
||||
|
||||
if (startsWithQuote && !endsWithQuote)
|
||||
{
|
||||
if (escapeCharacterInsertIndices == null)
|
||||
{
|
||||
escapeCharacterInsertIndices = new List<int>();
|
||||
}
|
||||
escapeCharacterInsertIndices.Add(0);
|
||||
}
|
||||
|
||||
for (int x = 1; x < lastEtagIndex; x++)
|
||||
{
|
||||
if (entityTag[x] == '\"' && entityTag[x - 1] != '\\')
|
||||
{
|
||||
if (escapeCharacterInsertIndices == null)
|
||||
{
|
||||
escapeCharacterInsertIndices = new List<int>();
|
||||
}
|
||||
escapeCharacterInsertIndices.Add(x + escapeCharacterInsertIndices.Count);
|
||||
}
|
||||
}
|
||||
|
||||
// Possible that the ending internal quote is already escaped so must check the character before it
|
||||
if (!startsWithQuote && endsWithQuote && entityTag[lastEtagIndex - 1] != '\\')
|
||||
{
|
||||
if (escapeCharacterInsertIndices == null)
|
||||
{
|
||||
escapeCharacterInsertIndices = new List<int>();
|
||||
}
|
||||
escapeCharacterInsertIndices.Add(lastEtagIndex + escapeCharacterInsertIndices.Count);
|
||||
}
|
||||
|
||||
if (needsSurroundingQuotes || escapeCharacterInsertIndices != null)
|
||||
{
|
||||
int escapeCharacterInsertIndicesCount = (escapeCharacterInsertIndices == null) ? 0 : escapeCharacterInsertIndices.Count;
|
||||
StringBuilder editedEtag = new StringBuilder(entityTag, entityTag.Length + escapeCharacterInsertIndicesCount + 2);
|
||||
for (int x = 0; x < escapeCharacterInsertIndicesCount; x++)
|
||||
{
|
||||
editedEtag.Insert(escapeCharacterInsertIndices[x], '\\');
|
||||
}
|
||||
if (needsSurroundingQuotes)
|
||||
{
|
||||
editedEtag.Insert(entityTag.Length + escapeCharacterInsertIndicesCount, '\"');
|
||||
editedEtag.Insert(0, '\"');
|
||||
}
|
||||
entityTag = editedEtag.ToString();
|
||||
}
|
||||
|
||||
return entityTag;
|
||||
}
|
||||
|
||||
internal static string GenerateValidEtag(object entityTag)
|
||||
{
|
||||
return string.Format(CultureInfo.InvariantCulture, "\"{0}\"", entityTag.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
213
external/referencesource/System.ServiceModel.Web/System/ServiceModel/Web/Utility.cs
vendored
Normal file
213
external/referencesource/System.ServiceModel.Web/System/ServiceModel/Web/Utility.cs
vendored
Normal file
@ -0,0 +1,213 @@
|
||||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
|
||||
namespace System.ServiceModel.Web
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Net.Mime;
|
||||
using System.Runtime;
|
||||
using System.Text;
|
||||
using System.Globalization;
|
||||
using System.ServiceModel.Channels;
|
||||
|
||||
static class Utility
|
||||
{
|
||||
public const string applicationXml = "application/xml";
|
||||
public const string textXml = "text/xml";
|
||||
public const string applicationJson = "application/json";
|
||||
public const string textJson = "text/json";
|
||||
public const string GET = "GET";
|
||||
|
||||
|
||||
public static bool IsXmlContent(this string contentType)
|
||||
{
|
||||
if (contentType == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
string contentTypeProcessed = contentType.Trim();
|
||||
|
||||
return contentTypeProcessed.StartsWith(applicationXml, StringComparison.OrdinalIgnoreCase)
|
||||
|| contentTypeProcessed.StartsWith(textXml, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public static bool IsJsonContent(this string contentType)
|
||||
{
|
||||
if (contentType == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
string contentTypeProcessed = contentType.Trim();
|
||||
|
||||
return contentTypeProcessed.StartsWith(applicationJson, StringComparison.OrdinalIgnoreCase)
|
||||
|| contentTypeProcessed.StartsWith(textJson, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public static string CombineUri(string former, string latter)
|
||||
{
|
||||
// Appending the latter string to the form string,
|
||||
// while making sure there is a single slash char seperating the latter and the former.
|
||||
// This method behaves differently than new Uri(baseUri, relativeUri)
|
||||
// as CombineUri simply appends, whereas new Uri() actually replaces the last segment
|
||||
// of the its base path with the relative uri.
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if (former.Length > 0 && latter.Length > 0)
|
||||
{
|
||||
if (former[former.Length - 1] == '/' && latter[0] == '/')
|
||||
{
|
||||
builder.Append(former, 0, former.Length - 1);
|
||||
builder.Append(latter);
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
if (former[former.Length - 1] != '/' && latter[0] != '/')
|
||||
{
|
||||
builder.Append(former);
|
||||
builder.Append('/');
|
||||
builder.Append(latter);
|
||||
return builder.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
return former + latter;
|
||||
}
|
||||
public static List<string> QuoteAwareStringSplit(string str)
|
||||
{
|
||||
List<string> subStrings = new List<string>();
|
||||
int offset = 0;
|
||||
while (true)
|
||||
{
|
||||
string subString = QuoteAwareSubString(str, ref offset);
|
||||
if (subString == null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
subStrings.Add(subString);
|
||||
}
|
||||
|
||||
return subStrings;
|
||||
}
|
||||
|
||||
// This method extracts substrings from a string starting at the offset
|
||||
// and up until the next comma in the string. The sub string extraction is
|
||||
// quote aware such that commas inside quoted-strings are ignored. On return,
|
||||
// offset points to the next char beyond the comma of the substring returned
|
||||
// and may point beyond the length of the header.
|
||||
public static string QuoteAwareSubString(string str, ref int offset)
|
||||
{
|
||||
// this method will filter out empty-string and white-space-only items in
|
||||
// the header. For example "x,,y" and "x, ,y" would result in just "x" and "y"
|
||||
// substrings being returned.
|
||||
|
||||
if (string.IsNullOrEmpty(str) || offset >= str.Length)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
int startIndex = (offset > 0) ? offset : 0;
|
||||
|
||||
// trim whitespace and commas from the begining of the item
|
||||
while (char.IsWhiteSpace(str[startIndex]) || str[startIndex] == ',')
|
||||
{
|
||||
startIndex++;
|
||||
if (startIndex >= str.Length)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
int endIndex = startIndex;
|
||||
bool insideQuotes = false;
|
||||
|
||||
while (endIndex < str.Length)
|
||||
{
|
||||
if (str[endIndex] == '\"' &&
|
||||
(!insideQuotes || endIndex == 0 || str[endIndex - 1] != '\\'))
|
||||
{
|
||||
insideQuotes = !insideQuotes;
|
||||
}
|
||||
else if (str[endIndex] == ',' && !insideQuotes)
|
||||
{
|
||||
break;
|
||||
}
|
||||
endIndex++;
|
||||
}
|
||||
offset = endIndex + 1;
|
||||
|
||||
// trim whitespace from the end of the item; the substring is guaranteed to
|
||||
// have at least one non-whitespace character
|
||||
while (char.IsWhiteSpace(str[endIndex - 1]))
|
||||
{
|
||||
endIndex--;
|
||||
}
|
||||
|
||||
return str.Substring(startIndex, endIndex - startIndex);
|
||||
}
|
||||
|
||||
public static ContentType GetContentType(string contentType)
|
||||
{
|
||||
string contentTypeTrimmed = contentType.Trim();
|
||||
if (!string.IsNullOrEmpty(contentTypeTrimmed))
|
||||
{
|
||||
return GetContentTypeOrNull(contentTypeTrimmed);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static ContentType GetContentTypeOrNull(string contentType)
|
||||
{
|
||||
try
|
||||
{
|
||||
Fx.Assert(contentType == contentType.Trim(), "The ContentType input argument should already be trimmed.");
|
||||
Fx.Assert(!string.IsNullOrEmpty(contentType), "The ContentType input argument should not be null or empty.");
|
||||
|
||||
ContentType contentTypeToReturn = new ContentType(contentType);
|
||||
|
||||
// Need to check for "*/<Something-other-than-*>" because the ContentType constructor doesn't catch this
|
||||
string[] typeAndSubType = contentTypeToReturn.MediaType.Split('/');
|
||||
Fx.Assert(typeAndSubType.Length == 2, "The creation of the ContentType would have failed if there wasn't a type and subtype.");
|
||||
if (typeAndSubType[0][0] == '*' && typeAndSubType[0].Length == 1 &&
|
||||
!(typeAndSubType[1][0] == '*' && typeAndSubType[1].Length == 1))
|
||||
{
|
||||
//
|
||||
|
||||
|
||||
|
||||
// throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new FormatException(
|
||||
// SR2.GetString(SR2.InvalidContentType, contentType)));
|
||||
return null;
|
||||
}
|
||||
return contentTypeToReturn;
|
||||
}
|
||||
catch (FormatException e)
|
||||
{
|
||||
// Return null to indicate that the content type creation failed
|
||||
System.ServiceModel.DiagnosticUtility.TraceHandledException(e, TraceEventType.Warning);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static string IEnumerableToCommaSeparatedString(IEnumerable<string> items)
|
||||
{
|
||||
Fx.Assert(items != null, "The 'items' argument should never be null.");
|
||||
return string.Join(", ", items);
|
||||
}
|
||||
|
||||
public static void AddRange<T>(ICollection<T> list, IEnumerable<T> itemsToAdd)
|
||||
{
|
||||
Fx.Assert(list != null, "The 'list' argument should never be null.");
|
||||
Fx.Assert(itemsToAdd != null, "The 'itemsToAdd' argument should never be null.");
|
||||
|
||||
foreach (T item in itemsToAdd)
|
||||
{
|
||||
list.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
102
external/referencesource/System.ServiceModel.Web/System/ServiceModel/Web/WebChannelFactory.cs
vendored
Normal file
102
external/referencesource/System.ServiceModel.Web/System/ServiceModel/Web/WebChannelFactory.cs
vendored
Normal file
@ -0,0 +1,102 @@
|
||||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
|
||||
namespace System.ServiceModel.Web
|
||||
{
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.ServiceModel;
|
||||
using System.ServiceModel.Activation;
|
||||
using System.ServiceModel.Channels;
|
||||
using System.ServiceModel.Description;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Configuration;
|
||||
using System.Net;
|
||||
using System.Globalization;
|
||||
|
||||
public class WebChannelFactory<TChannel> : ChannelFactory<TChannel>
|
||||
where TChannel : class
|
||||
{
|
||||
public WebChannelFactory()
|
||||
: base()
|
||||
{
|
||||
}
|
||||
|
||||
public WebChannelFactory(Binding binding)
|
||||
: base(binding)
|
||||
{
|
||||
}
|
||||
|
||||
public WebChannelFactory(ServiceEndpoint endpoint) :
|
||||
base(endpoint)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
[SuppressMessage("Microsoft.Design", "CA1057:StringUriOverloadsCallSystemUriOverloads", Justification = "This is a configuration string and not a network location")]
|
||||
public WebChannelFactory(string endpointConfigurationName)
|
||||
: base(endpointConfigurationName)
|
||||
{
|
||||
}
|
||||
|
||||
public WebChannelFactory(Type channelType)
|
||||
: base(channelType)
|
||||
{
|
||||
}
|
||||
|
||||
public WebChannelFactory(Uri remoteAddress)
|
||||
: this(GetDefaultBinding(remoteAddress), remoteAddress)
|
||||
{
|
||||
}
|
||||
|
||||
public WebChannelFactory(Binding binding, Uri remoteAddress)
|
||||
: base(binding, (remoteAddress != null) ? new EndpointAddress(remoteAddress) : null)
|
||||
{
|
||||
}
|
||||
|
||||
public WebChannelFactory(string endpointConfigurationName, Uri remoteAddress)
|
||||
: base(endpointConfigurationName, (remoteAddress != null) ? new EndpointAddress(remoteAddress) : null)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnOpening()
|
||||
{
|
||||
if (this.Endpoint == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// if the binding is missing, set up a default binding
|
||||
if (this.Endpoint.Binding == null && this.Endpoint.Address != null)
|
||||
{
|
||||
this.Endpoint.Binding = GetDefaultBinding(this.Endpoint.Address.Uri);
|
||||
}
|
||||
WebServiceHost.SetRawContentTypeMapperIfNecessary(this.Endpoint, false);
|
||||
if (this.Endpoint.Behaviors.Find<WebHttpBehavior>() == null)
|
||||
{
|
||||
this.Endpoint.Behaviors.Add(new WebHttpBehavior());
|
||||
}
|
||||
base.OnOpening();
|
||||
}
|
||||
|
||||
static Binding GetDefaultBinding(Uri remoteAddress)
|
||||
{
|
||||
if (remoteAddress == null || (remoteAddress.Scheme != Uri.UriSchemeHttp && remoteAddress.Scheme != Uri.UriSchemeHttps))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (remoteAddress.Scheme == Uri.UriSchemeHttp)
|
||||
{
|
||||
return new WebHttpBinding();
|
||||
}
|
||||
else
|
||||
{
|
||||
WebHttpBinding result = new WebHttpBinding();
|
||||
result.Security.Mode = WebHttpSecurityMode.Transport;
|
||||
result.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
202
external/referencesource/System.ServiceModel.Web/System/ServiceModel/Web/WebFaultException.cs
vendored
Normal file
202
external/referencesource/System.ServiceModel.Web/System/ServiceModel/Web/WebFaultException.cs
vendored
Normal file
@ -0,0 +1,202 @@
|
||||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
namespace System.ServiceModel.Web
|
||||
{
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Runtime;
|
||||
using System.Net;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Security;
|
||||
using System.Security.Permissions;
|
||||
using System.ServiceModel;
|
||||
using System.Globalization;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
[Serializable]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1032:ImplementStandardExceptionConstructors", Justification = "REST Exceptions cannot contain InnerExceptions or messages")]
|
||||
public class WebFaultException : FaultException, IWebFaultException
|
||||
{
|
||||
internal const string WebFaultCodeNamespace = "http://schemas.microsoft.com/2009/WebFault";
|
||||
|
||||
public WebFaultException(HttpStatusCode statusCode)
|
||||
: base(GetDefaultReason(statusCode), GetFaultCode(statusCode))
|
||||
{
|
||||
this.StatusCode = statusCode;
|
||||
}
|
||||
|
||||
protected WebFaultException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{
|
||||
this.StatusCode = (HttpStatusCode)info.GetValue("statusCode", typeof(HttpStatusCode));
|
||||
}
|
||||
|
||||
public HttpStatusCode StatusCode { get; private set; }
|
||||
|
||||
[SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes",
|
||||
Justification = "The interface is used merely to be able to handled both the generic and non-generic versions and doesn't need to be exposed.")]
|
||||
Type IWebFaultException.DetailType
|
||||
{
|
||||
get { return null; }
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes",
|
||||
Justification = "The interface is used merely to be able to handled both the generic and non-generic versions and doesn't need to be exposed.")]
|
||||
object IWebFaultException.DetailObject
|
||||
{
|
||||
get { return null; }
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes",
|
||||
Justification = "The interface is used merely to be able to handled both the generic and non-generic versions and doesn't need to be exposed.")]
|
||||
Type[] IWebFaultException.KnownTypes
|
||||
{
|
||||
get { return null; }
|
||||
}
|
||||
|
||||
[Fx.Tag.SecurityNote(Critical = "Overrides the base.GetObjectData which is critical, as well as calling this method.", Safe = "Replicates the LinkDemand.")]
|
||||
[SecurityCritical]
|
||||
[SecurityPermissionAttribute(SecurityAction.LinkDemand, SerializationFormatter = true)]
|
||||
public override void GetObjectData(SerializationInfo info, StreamingContext context)
|
||||
{
|
||||
base.GetObjectData(info, context);
|
||||
info.AddValue("statusCode", this.StatusCode);
|
||||
}
|
||||
|
||||
internal static FaultCode GetFaultCode(HttpStatusCode statusCode)
|
||||
{
|
||||
if ((int) statusCode >= (int) HttpStatusCode.InternalServerError)
|
||||
{
|
||||
return FaultCode.CreateReceiverFaultCode(statusCode.ToString(), WebFaultCodeNamespace);
|
||||
}
|
||||
else
|
||||
{
|
||||
return FaultCode.CreateSenderFaultCode(statusCode.ToString(), WebFaultCodeNamespace);
|
||||
}
|
||||
}
|
||||
|
||||
// These reasons come from section 6.1.1 of http://www.ietf.org/rfc/rfc2616.txt
|
||||
internal static string GetDefaultReason(HttpStatusCode statusCode)
|
||||
{
|
||||
switch ((int)statusCode)
|
||||
{
|
||||
case 100: return "Continue";
|
||||
case 101: return "Switching Protocols";
|
||||
case 200: return "OK";
|
||||
case 201: return "Created";
|
||||
case 202: return "Accepted";
|
||||
case 203: return "Non-Authoritative Information";
|
||||
case 204: return "No Content";
|
||||
case 205: return "Reset Content";
|
||||
case 206: return "Partial Content";
|
||||
case 300: return "Multiple Choices";
|
||||
case 301: return "Moved Permanently";
|
||||
case 302: return "Found";
|
||||
case 303: return "See Other";
|
||||
case 304: return "Not Modified";
|
||||
case 305: return "Use Proxy";
|
||||
case 307: return "Temporary Redirect";
|
||||
case 400: return "Bad Request";
|
||||
case 401: return "Unauthorized";
|
||||
case 402: return "Payment Required";
|
||||
case 403: return "Forbidden";
|
||||
case 404: return "Not Found";
|
||||
case 405: return "Method Not Allowed";
|
||||
case 406: return "Not Acceptable";
|
||||
case 407: return "Proxy Authentication Required";
|
||||
case 408: return "Request Time-out";
|
||||
case 409: return "Conflict";
|
||||
case 410: return "Gone";
|
||||
case 411: return "Length Required";
|
||||
case 412: return "Precondition Failed";
|
||||
case 413: return "Request Entity Too Large";
|
||||
case 414: return "Request-URI Too Large";
|
||||
case 415: return "Unsupported Media Type";
|
||||
case 416: return "Requested range not satisfiable";
|
||||
case 417: return "Expectation Failed";
|
||||
case 500: return "Internal Server Error";
|
||||
case 501: return "Not Implemented";
|
||||
case 502: return "Bad Gateway";
|
||||
case 503: return "Service Unavailable";
|
||||
case 504: return "Gateway Time-out";
|
||||
case 505: return "HTTP Version not supported";
|
||||
default:
|
||||
{
|
||||
int errorClass = ((int)statusCode) / 100;
|
||||
switch (errorClass)
|
||||
{
|
||||
case 1: return "Informational";
|
||||
case 2: return "Success";
|
||||
case 3: return "Redirection";
|
||||
case 4: return "Client Error";
|
||||
case 5: return "Server Error";
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1032:ImplementStandardExceptionConstructors", Justification = "REST Exceptions cannot contain InnerExceptions or messages")]
|
||||
public class WebFaultException<T> : FaultException<T>, IWebFaultException
|
||||
{
|
||||
Type[] knownTypes;
|
||||
|
||||
public WebFaultException(T detail, HttpStatusCode statusCode)
|
||||
: base(detail, WebFaultException.GetDefaultReason(statusCode), WebFaultException.GetFaultCode(statusCode))
|
||||
{
|
||||
this.StatusCode = statusCode;
|
||||
}
|
||||
|
||||
public WebFaultException(T detail, HttpStatusCode statusCode, IEnumerable<Type> knownTypes)
|
||||
: base(detail, WebFaultException.GetDefaultReason(statusCode), WebFaultException.GetFaultCode(statusCode))
|
||||
{
|
||||
this.StatusCode = statusCode;
|
||||
this.knownTypes = (knownTypes == null) ? null : new List<Type>(knownTypes).ToArray();
|
||||
}
|
||||
|
||||
protected WebFaultException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{
|
||||
this.StatusCode = (HttpStatusCode)info.GetValue("statusCode", typeof(HttpStatusCode));
|
||||
this.knownTypes = (Type[])info.GetValue("knownTypes", typeof(Type[]));
|
||||
}
|
||||
|
||||
public HttpStatusCode StatusCode { get; private set; }
|
||||
|
||||
[SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes",
|
||||
Justification = "The interface is used merely to be able to handled both the generic and non-generic versions and doesn't need to be exposed.")]
|
||||
Type IWebFaultException.DetailType
|
||||
{
|
||||
get { return typeof(T); }
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes",
|
||||
Justification = "The interface is used merely to be able to handled both the generic and non-generic versions and doesn't need to be exposed.")]
|
||||
object IWebFaultException.DetailObject
|
||||
{
|
||||
get { return this.Detail; }
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes",
|
||||
Justification = "The interface is used merely to be able to handled both the generic and non-generic versions and doesn't need to be exposed.")]
|
||||
Type[] IWebFaultException.KnownTypes
|
||||
{
|
||||
get { return this.knownTypes; }
|
||||
}
|
||||
|
||||
[Fx.Tag.SecurityNote(Critical = "Overrides the base.GetObjectData which is critical, as well as calling this method.", Safe = "Replicates the LinkDemand.")]
|
||||
[SecurityCritical]
|
||||
[SecurityPermissionAttribute(SecurityAction.LinkDemand, SerializationFormatter = true)]
|
||||
public override void GetObjectData(SerializationInfo info, StreamingContext context)
|
||||
{
|
||||
base.GetObjectData(info, context);
|
||||
info.AddValue("statusCode", this.StatusCode);
|
||||
info.AddValue("knownTypes", this.knownTypes);
|
||||
}
|
||||
}
|
||||
}
|
148
external/referencesource/System.ServiceModel.Web/System/ServiceModel/Web/WebGetAttribute.cs
vendored
Normal file
148
external/referencesource/System.ServiceModel.Web/System/ServiceModel/Web/WebGetAttribute.cs
vendored
Normal file
@ -0,0 +1,148 @@
|
||||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
|
||||
namespace System.ServiceModel.Web
|
||||
{
|
||||
using System;
|
||||
using System.ServiceModel.Description;
|
||||
using System.ServiceModel.Dispatcher;
|
||||
using System.ServiceModel.Channels;
|
||||
using System.ServiceModel.Web;
|
||||
using System.ServiceModel.Administration;
|
||||
using System.Collections.Generic;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public sealed class WebGetAttribute : Attribute, IOperationContractAttributeProvider, IOperationBehavior, IWmiInstanceProvider
|
||||
{
|
||||
WebMessageBodyStyle bodyStyle;
|
||||
bool isBodyStyleDefined;
|
||||
bool isRequestMessageFormatSet;
|
||||
bool isResponseMessageFormatSet;
|
||||
WebMessageFormat requestMessageFormat;
|
||||
WebMessageFormat responseMessageFormat;
|
||||
string uriTemplate; // Note: HttpTransferEndpointBehavior interprets uriTemplate as: null means 'no opinion', whereas string.Empty means relative path of ""
|
||||
|
||||
public WebGetAttribute()
|
||||
{
|
||||
}
|
||||
|
||||
public WebMessageBodyStyle BodyStyle
|
||||
{
|
||||
get { return this.bodyStyle; }
|
||||
set
|
||||
{
|
||||
if (!WebMessageBodyStyleHelper.IsDefined(value))
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value"));
|
||||
}
|
||||
this.bodyStyle = value;
|
||||
this.isBodyStyleDefined = true;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsBodyStyleSetExplicitly
|
||||
{
|
||||
get { return this.isBodyStyleDefined; }
|
||||
}
|
||||
|
||||
public bool IsRequestFormatSetExplicitly
|
||||
{
|
||||
get { return this.isRequestMessageFormatSet; }
|
||||
}
|
||||
|
||||
public bool IsResponseFormatSetExplicitly
|
||||
{
|
||||
get { return this.isResponseMessageFormatSet; }
|
||||
}
|
||||
public WebMessageFormat RequestFormat
|
||||
{
|
||||
get
|
||||
{
|
||||
|
||||
return this.requestMessageFormat;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (!WebMessageFormatHelper.IsDefined(value))
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value"));
|
||||
}
|
||||
this.requestMessageFormat = value;
|
||||
this.isRequestMessageFormatSet = true;
|
||||
}
|
||||
}
|
||||
|
||||
public WebMessageFormat ResponseFormat
|
||||
{
|
||||
get
|
||||
{
|
||||
|
||||
return this.responseMessageFormat;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (!WebMessageFormatHelper.IsDefined(value))
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value"));
|
||||
}
|
||||
this.responseMessageFormat = value;
|
||||
this.isResponseMessageFormatSet = true;
|
||||
}
|
||||
}
|
||||
|
||||
public string UriTemplate
|
||||
{ get { return this.uriTemplate; } set { this.uriTemplate = value; } }
|
||||
|
||||
void IOperationBehavior.AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
|
||||
{
|
||||
} // do nothing
|
||||
void IOperationBehavior.ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
|
||||
{
|
||||
} // do nothing
|
||||
void IOperationBehavior.ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
|
||||
{
|
||||
} // do nothing
|
||||
void IOperationBehavior.Validate(OperationDescription operationDescription)
|
||||
{
|
||||
} // do nothing
|
||||
|
||||
void IWmiInstanceProvider.FillInstance(IWmiInstance wmiInstance)
|
||||
{
|
||||
if (wmiInstance == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("wmiInstance");
|
||||
}
|
||||
wmiInstance.SetProperty("BodyStyle", this.BodyStyle.ToString());
|
||||
wmiInstance.SetProperty("IsBodyStyleSetExplicitly", this.IsBodyStyleSetExplicitly.ToString());
|
||||
wmiInstance.SetProperty("RequestFormat", this.RequestFormat.ToString());
|
||||
wmiInstance.SetProperty("IsRequestFormatSetExplicitly", this.IsRequestFormatSetExplicitly.ToString());
|
||||
wmiInstance.SetProperty("ResponseFormat", this.ResponseFormat.ToString());
|
||||
wmiInstance.SetProperty("IsResponseFormatSetExplicitly", this.IsResponseFormatSetExplicitly.ToString());
|
||||
wmiInstance.SetProperty("UriTemplate", this.UriTemplate);
|
||||
}
|
||||
|
||||
string IWmiInstanceProvider.GetInstanceType()
|
||||
{
|
||||
return "WebGetAttribute";
|
||||
}
|
||||
|
||||
internal WebMessageBodyStyle GetBodyStyleOrDefault(WebMessageBodyStyle defaultStyle)
|
||||
{
|
||||
if (this.IsBodyStyleSetExplicitly)
|
||||
{
|
||||
return this.BodyStyle;
|
||||
}
|
||||
else
|
||||
{
|
||||
return defaultStyle;
|
||||
}
|
||||
}
|
||||
|
||||
OperationContractAttribute IOperationContractAttributeProvider.GetOperationContractAttribute()
|
||||
{
|
||||
return new OperationContractAttribute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
152
external/referencesource/System.ServiceModel.Web/System/ServiceModel/Web/WebInvokeAttribute.cs
vendored
Normal file
152
external/referencesource/System.ServiceModel.Web/System/ServiceModel/Web/WebInvokeAttribute.cs
vendored
Normal file
@ -0,0 +1,152 @@
|
||||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
|
||||
namespace System.ServiceModel.Web
|
||||
{
|
||||
using System;
|
||||
using System.ServiceModel.Description;
|
||||
using System.ServiceModel.Dispatcher;
|
||||
using System.ServiceModel.Channels;
|
||||
using System.ServiceModel.Web;
|
||||
using System.ServiceModel.Administration;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public sealed class WebInvokeAttribute : Attribute, IOperationContractAttributeProvider, IOperationBehavior, IWmiInstanceProvider
|
||||
{
|
||||
WebMessageBodyStyle bodyStyle;
|
||||
bool isBodyStyleDefined;
|
||||
bool isRequestMessageFormatSet;
|
||||
bool isResponseMessageFormatSet;
|
||||
string method; // http verb
|
||||
WebMessageFormat requestMessageFormat;
|
||||
WebMessageFormat responseMessageFormat;
|
||||
string uriTemplate; // Note: HttpTransferEndpointBehavior interprets uriTemplate as: null means 'no opinion', whereas string.Empty means relative path of ""
|
||||
|
||||
public WebInvokeAttribute()
|
||||
{
|
||||
}
|
||||
|
||||
public WebMessageBodyStyle BodyStyle
|
||||
{
|
||||
get { return this.bodyStyle; }
|
||||
set
|
||||
{
|
||||
if (!WebMessageBodyStyleHelper.IsDefined(value))
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value"));
|
||||
}
|
||||
this.bodyStyle = value;
|
||||
this.isBodyStyleDefined = true;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsBodyStyleSetExplicitly
|
||||
{
|
||||
get { return this.isBodyStyleDefined; }
|
||||
}
|
||||
|
||||
public bool IsRequestFormatSetExplicitly
|
||||
{
|
||||
get { return this.isRequestMessageFormatSet; }
|
||||
}
|
||||
|
||||
public bool IsResponseFormatSetExplicitly
|
||||
{
|
||||
get { return this.isResponseMessageFormatSet; }
|
||||
}
|
||||
|
||||
public string Method
|
||||
{ get { return this.method; } set { this.method = value; } }
|
||||
public WebMessageFormat RequestFormat
|
||||
{
|
||||
get
|
||||
{
|
||||
|
||||
return this.requestMessageFormat;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (!WebMessageFormatHelper.IsDefined(value))
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value"));
|
||||
}
|
||||
this.requestMessageFormat = value;
|
||||
this.isRequestMessageFormatSet = true;
|
||||
}
|
||||
}
|
||||
|
||||
public WebMessageFormat ResponseFormat
|
||||
{
|
||||
get
|
||||
{
|
||||
|
||||
return this.responseMessageFormat;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (!WebMessageFormatHelper.IsDefined(value))
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value"));
|
||||
}
|
||||
this.responseMessageFormat = value;
|
||||
this.isResponseMessageFormatSet = true;
|
||||
}
|
||||
}
|
||||
|
||||
public string UriTemplate
|
||||
{ get { return this.uriTemplate; } set { this.uriTemplate = value; } }
|
||||
|
||||
void IOperationBehavior.AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
|
||||
{
|
||||
} // do nothing
|
||||
void IOperationBehavior.ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
|
||||
{
|
||||
} // do nothing
|
||||
void IOperationBehavior.ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
|
||||
{
|
||||
} // do nothing
|
||||
void IOperationBehavior.Validate(OperationDescription operationDescription)
|
||||
{
|
||||
} // do nothing
|
||||
|
||||
internal WebMessageBodyStyle GetBodyStyleOrDefault(WebMessageBodyStyle defaultStyle)
|
||||
{
|
||||
if (this.IsBodyStyleSetExplicitly)
|
||||
{
|
||||
return this.BodyStyle;
|
||||
}
|
||||
else
|
||||
{
|
||||
return defaultStyle;
|
||||
}
|
||||
}
|
||||
|
||||
void IWmiInstanceProvider.FillInstance(IWmiInstance wmiInstance)
|
||||
{
|
||||
if (wmiInstance == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("wmiInstance");
|
||||
}
|
||||
wmiInstance.SetProperty("BodyStyle", this.BodyStyle.ToString());
|
||||
wmiInstance.SetProperty("IsBodyStyleSetExplicitly", this.IsBodyStyleSetExplicitly.ToString());
|
||||
wmiInstance.SetProperty("RequestFormat", this.RequestFormat.ToString());
|
||||
wmiInstance.SetProperty("IsRequestFormatSetExplicitly", this.IsRequestFormatSetExplicitly.ToString());
|
||||
wmiInstance.SetProperty("ResponseFormat", this.ResponseFormat.ToString());
|
||||
wmiInstance.SetProperty("IsResponseFormatSetExplicitly", this.IsResponseFormatSetExplicitly.ToString());
|
||||
wmiInstance.SetProperty("UriTemplate", this.UriTemplate);
|
||||
wmiInstance.SetProperty("Method", this.Method);
|
||||
}
|
||||
|
||||
string IWmiInstanceProvider.GetInstanceType()
|
||||
{
|
||||
return "WebInvokeAttribute";
|
||||
}
|
||||
|
||||
OperationContractAttribute IOperationContractAttributeProvider.GetOperationContractAttribute()
|
||||
{
|
||||
return new OperationContractAttribute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
14
external/referencesource/System.ServiceModel.Web/System/ServiceModel/Web/WebMessageBodyStyle.cs
vendored
Normal file
14
external/referencesource/System.ServiceModel.Web/System/ServiceModel/Web/WebMessageBodyStyle.cs
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
|
||||
namespace System.ServiceModel.Web
|
||||
{
|
||||
public enum WebMessageBodyStyle
|
||||
{
|
||||
Bare,
|
||||
Wrapped,
|
||||
WrappedRequest,
|
||||
WrappedResponse
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
|
||||
namespace System.ServiceModel.Web
|
||||
{
|
||||
internal static class WebMessageBodyStyleHelper
|
||||
{
|
||||
internal static bool IsDefined(WebMessageBodyStyle style)
|
||||
{
|
||||
return (style == WebMessageBodyStyle.Bare
|
||||
|| style == WebMessageBodyStyle.Wrapped
|
||||
|| style == WebMessageBodyStyle.WrappedRequest
|
||||
|| style == WebMessageBodyStyle.WrappedResponse);
|
||||
}
|
||||
}
|
||||
}
|
12
external/referencesource/System.ServiceModel.Web/System/ServiceModel/Web/WebMessageFormat.cs
vendored
Normal file
12
external/referencesource/System.ServiceModel.Web/System/ServiceModel/Web/WebMessageFormat.cs
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
|
||||
namespace System.ServiceModel.Web
|
||||
{
|
||||
public enum WebMessageFormat
|
||||
{
|
||||
Xml,
|
||||
Json
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
|
||||
namespace System.ServiceModel.Web
|
||||
{
|
||||
internal static class WebMessageFormatHelper
|
||||
{
|
||||
internal static bool IsDefined(WebMessageFormat format)
|
||||
{
|
||||
return (format == WebMessageFormat.Xml || format == WebMessageFormat.Json);
|
||||
}
|
||||
}
|
||||
}
|
393
external/referencesource/System.ServiceModel.Web/System/ServiceModel/Web/WebOperationContext.cs
vendored
Normal file
393
external/referencesource/System.ServiceModel.Web/System/ServiceModel/Web/WebOperationContext.cs
vendored
Normal file
@ -0,0 +1,393 @@
|
||||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
#pragma warning disable 1634, 1691
|
||||
|
||||
namespace System.ServiceModel.Web
|
||||
{
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Runtime.Serialization.Json;
|
||||
using System.ServiceModel;
|
||||
using System.ServiceModel.Channels;
|
||||
using System.ServiceModel.Description;
|
||||
using System.ServiceModel.Syndication;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using System.Xml.Serialization;
|
||||
using System.ServiceModel.Dispatcher;
|
||||
|
||||
public class WebOperationContext : IExtension<OperationContext>
|
||||
{
|
||||
internal static readonly string DefaultTextMediaType = "text/plain";
|
||||
internal static readonly string DefaultJsonMediaType = JsonGlobals.applicationJsonMediaType;
|
||||
internal static readonly string DefaultXmlMediaType = "application/xml";
|
||||
internal static readonly string DefaultAtomMediaType = "application/atom+xml";
|
||||
internal static readonly string DefaultStreamMediaType = WebHttpBehavior.defaultStreamContentType;
|
||||
|
||||
OperationContext operationContext;
|
||||
|
||||
public WebOperationContext(OperationContext operationContext)
|
||||
{
|
||||
if (operationContext == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("operationContext");
|
||||
}
|
||||
this.operationContext = operationContext;
|
||||
#pragma warning disable 56506 // [....], operationContext.Extensions is never null
|
||||
if (operationContext.Extensions.Find<WebOperationContext>() == null)
|
||||
{
|
||||
operationContext.Extensions.Add(this);
|
||||
}
|
||||
#pragma warning enable 56506
|
||||
}
|
||||
|
||||
public static WebOperationContext Current
|
||||
{
|
||||
get
|
||||
{
|
||||
if (OperationContext.Current == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
WebOperationContext existing = OperationContext.Current.Extensions.Find<WebOperationContext>();
|
||||
if (existing != null)
|
||||
{
|
||||
return existing;
|
||||
}
|
||||
return new WebOperationContext(OperationContext.Current);
|
||||
}
|
||||
}
|
||||
|
||||
public IncomingWebRequestContext IncomingRequest
|
||||
{
|
||||
get { return new IncomingWebRequestContext(this.operationContext); }
|
||||
}
|
||||
|
||||
public IncomingWebResponseContext IncomingResponse
|
||||
{
|
||||
get { return new IncomingWebResponseContext(this.operationContext); }
|
||||
}
|
||||
|
||||
public OutgoingWebRequestContext OutgoingRequest
|
||||
{
|
||||
get { return new OutgoingWebRequestContext(this.operationContext); }
|
||||
}
|
||||
|
||||
public OutgoingWebResponseContext OutgoingResponse
|
||||
{
|
||||
get { return new OutgoingWebResponseContext(this.operationContext); }
|
||||
}
|
||||
|
||||
public void Attach(OperationContext owner)
|
||||
{
|
||||
}
|
||||
|
||||
public void Detach(OperationContext owner)
|
||||
{
|
||||
}
|
||||
|
||||
public Message CreateJsonResponse<T>(T instance)
|
||||
{
|
||||
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
|
||||
return CreateJsonResponse<T>(instance, serializer);
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "CreateJsonMessage requires the DataContractJsonSerializer. Allowing the base type XmlObjectSerializer would let deveopers supply a non-Json Serializer.")]
|
||||
public Message CreateJsonResponse<T>(T instance, DataContractJsonSerializer serializer)
|
||||
{
|
||||
if (serializer == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("serializer");
|
||||
}
|
||||
Message message = Message.CreateMessage(MessageVersion.None, (string)null, instance, serializer);
|
||||
message.Properties.Add(WebBodyFormatMessageProperty.Name, WebBodyFormatMessageProperty.JsonProperty);
|
||||
AddContentType(WebOperationContext.DefaultJsonMediaType, this.OutgoingResponse.BindingWriteEncoding);
|
||||
return message;
|
||||
}
|
||||
|
||||
public Message CreateXmlResponse<T>(T instance)
|
||||
{
|
||||
System.Runtime.Serialization.DataContractSerializer serializer = new System.Runtime.Serialization.DataContractSerializer(typeof(T));
|
||||
return CreateXmlResponse(instance, serializer);
|
||||
}
|
||||
|
||||
public Message CreateXmlResponse<T>(T instance, System.Runtime.Serialization.XmlObjectSerializer serializer)
|
||||
{
|
||||
if (serializer == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("serializer");
|
||||
}
|
||||
Message message = Message.CreateMessage(MessageVersion.None, (string)null, instance, serializer);
|
||||
message.Properties.Add(WebBodyFormatMessageProperty.Name, WebBodyFormatMessageProperty.XmlProperty);
|
||||
AddContentType(WebOperationContext.DefaultXmlMediaType, this.OutgoingResponse.BindingWriteEncoding);
|
||||
return message;
|
||||
}
|
||||
|
||||
public Message CreateXmlResponse<T>(T instance, XmlSerializer serializer)
|
||||
{
|
||||
if (serializer == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("serializer");
|
||||
}
|
||||
Message message = Message.CreateMessage(MessageVersion.None, (string)null, new XmlSerializerBodyWriter(instance, serializer));
|
||||
message.Properties.Add(WebBodyFormatMessageProperty.Name, WebBodyFormatMessageProperty.XmlProperty);
|
||||
AddContentType(WebOperationContext.DefaultXmlMediaType, this.OutgoingResponse.BindingWriteEncoding);
|
||||
return message;
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "Other XNode derived types such as XAttribute don't make sense in this context, so we are not using the XNode base type.")]
|
||||
public Message CreateXmlResponse(XDocument document)
|
||||
{
|
||||
if (document == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("document");
|
||||
}
|
||||
Message message;
|
||||
if (document.FirstNode == null)
|
||||
{
|
||||
message = Message.CreateMessage(MessageVersion.None, (string)null);
|
||||
}
|
||||
else
|
||||
{
|
||||
message = Message.CreateMessage(MessageVersion.None, (string)null, document.CreateReader());
|
||||
}
|
||||
message.Properties.Add(WebBodyFormatMessageProperty.Name, WebBodyFormatMessageProperty.XmlProperty);
|
||||
AddContentType(WebOperationContext.DefaultXmlMediaType, this.OutgoingResponse.BindingWriteEncoding);
|
||||
return message;
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "Other XNode derived types such as XAttribute don't make sense in this context, so we are not using the XNode base type.")]
|
||||
public Message CreateXmlResponse(XElement element)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("element");
|
||||
}
|
||||
Message message = Message.CreateMessage(MessageVersion.None, (string)null, element.CreateReader());
|
||||
message.Properties.Add(WebBodyFormatMessageProperty.Name, WebBodyFormatMessageProperty.XmlProperty);
|
||||
AddContentType(WebOperationContext.DefaultXmlMediaType, this.OutgoingResponse.BindingWriteEncoding);
|
||||
return message;
|
||||
}
|
||||
|
||||
public Message CreateAtom10Response(SyndicationItem item)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("item");
|
||||
}
|
||||
Message message = Message.CreateMessage(MessageVersion.None, (string)null, item.GetAtom10Formatter());
|
||||
message.Properties.Add(WebBodyFormatMessageProperty.Name, WebBodyFormatMessageProperty.XmlProperty);
|
||||
AddContentType(WebOperationContext.DefaultAtomMediaType, this.OutgoingResponse.BindingWriteEncoding);
|
||||
return message;
|
||||
}
|
||||
|
||||
public Message CreateAtom10Response(SyndicationFeed feed)
|
||||
{
|
||||
if (feed == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("feed");
|
||||
}
|
||||
Message message = Message.CreateMessage(MessageVersion.None, (string)null, feed.GetAtom10Formatter());
|
||||
message.Properties.Add(WebBodyFormatMessageProperty.Name, WebBodyFormatMessageProperty.XmlProperty);
|
||||
AddContentType(WebOperationContext.DefaultAtomMediaType, this.OutgoingResponse.BindingWriteEncoding);
|
||||
return message;
|
||||
}
|
||||
|
||||
public Message CreateAtom10Response(ServiceDocument document)
|
||||
{
|
||||
if (document == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("document");
|
||||
}
|
||||
Message message = Message.CreateMessage(MessageVersion.None, (string)null, document.GetFormatter());
|
||||
message.Properties.Add(WebBodyFormatMessageProperty.Name, WebBodyFormatMessageProperty.XmlProperty);
|
||||
AddContentType(WebOperationContext.DefaultAtomMediaType, this.OutgoingResponse.BindingWriteEncoding);
|
||||
return message;
|
||||
}
|
||||
|
||||
public Message CreateTextResponse(string text)
|
||||
{
|
||||
return CreateTextResponse(text, WebOperationContext.DefaultTextMediaType, Encoding.UTF8);
|
||||
}
|
||||
|
||||
public Message CreateTextResponse(string text, string contentType)
|
||||
{
|
||||
return CreateTextResponse(text, contentType, Encoding.UTF8);
|
||||
}
|
||||
|
||||
public Message CreateTextResponse(string text, string contentType, Encoding encoding)
|
||||
{
|
||||
if (text == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("text");
|
||||
}
|
||||
if (contentType == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("contentType");
|
||||
}
|
||||
if (encoding == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("encoding");
|
||||
}
|
||||
|
||||
Message message = new HttpStreamMessage(StreamBodyWriter.CreateStreamBodyWriter((stream) =>
|
||||
{
|
||||
byte[] preamble = encoding.GetPreamble();
|
||||
if (preamble.Length > 0)
|
||||
{
|
||||
stream.Write(preamble, 0, preamble.Length);
|
||||
}
|
||||
byte[] bytes = encoding.GetBytes(text);
|
||||
stream.Write(bytes, 0, bytes.Length);
|
||||
stream.Flush();
|
||||
}));
|
||||
message.Properties.Add(WebBodyFormatMessageProperty.Name, WebBodyFormatMessageProperty.RawProperty);
|
||||
AddContentType(contentType, null);
|
||||
return message;
|
||||
}
|
||||
|
||||
public Message CreateTextResponse(Action<TextWriter> textWriter, string contentType)
|
||||
{
|
||||
Encoding encoding = this.OutgoingResponse.BindingWriteEncoding;
|
||||
if (encoding == null)
|
||||
{
|
||||
encoding = Encoding.UTF8;
|
||||
}
|
||||
return CreateTextResponse(textWriter, contentType, encoding);
|
||||
}
|
||||
|
||||
public Message CreateTextResponse(Action<TextWriter> textWriter, string contentType, Encoding encoding)
|
||||
{
|
||||
if (textWriter == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("textWriter");
|
||||
}
|
||||
if (contentType == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("contentType");
|
||||
}
|
||||
if (encoding == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("encoding");
|
||||
}
|
||||
|
||||
Message message = new HttpStreamMessage(StreamBodyWriter.CreateStreamBodyWriter((stream) =>
|
||||
{
|
||||
using (TextWriter writer = new StreamWriter(stream, encoding))
|
||||
{
|
||||
textWriter(writer);
|
||||
}
|
||||
}));
|
||||
message.Properties.Add(WebBodyFormatMessageProperty.Name, WebBodyFormatMessageProperty.RawProperty);
|
||||
AddContentType(contentType, null);
|
||||
return message;
|
||||
}
|
||||
|
||||
public Message CreateStreamResponse(Stream stream, string contentType)
|
||||
{
|
||||
if (stream == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("stream");
|
||||
}
|
||||
if (contentType == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("contentType");
|
||||
}
|
||||
Message message = ByteStreamMessage.CreateMessage(stream);
|
||||
message.Properties.Add(WebBodyFormatMessageProperty.Name, WebBodyFormatMessageProperty.RawProperty);
|
||||
AddContentType(contentType, null);
|
||||
return message;
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "Using the StreamBodyWriter type instead of the BodyWriter type for discoverability. The StreamBodyWriter provides a helpful overload of the OnWriteBodyContents method that takes a Stream")]
|
||||
public Message CreateStreamResponse(StreamBodyWriter bodyWriter, string contentType)
|
||||
{
|
||||
if (bodyWriter == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("bodyWriter");
|
||||
}
|
||||
if (contentType == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("contentType");
|
||||
}
|
||||
Message message = new HttpStreamMessage(bodyWriter);
|
||||
message.Properties.Add(WebBodyFormatMessageProperty.Name, WebBodyFormatMessageProperty.RawProperty);
|
||||
AddContentType(contentType, null);
|
||||
return message;
|
||||
}
|
||||
|
||||
public Message CreateStreamResponse(Action<Stream> streamWriter, string contentType)
|
||||
{
|
||||
if (streamWriter == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("streamWriter");
|
||||
}
|
||||
if (contentType == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("contentType");
|
||||
}
|
||||
Message message = new HttpStreamMessage(StreamBodyWriter.CreateStreamBodyWriter(streamWriter));
|
||||
message.Properties.Add(WebBodyFormatMessageProperty.Name, WebBodyFormatMessageProperty.RawProperty);
|
||||
AddContentType(contentType, null);
|
||||
return message;
|
||||
}
|
||||
|
||||
public UriTemplate GetUriTemplate(string operationName)
|
||||
{
|
||||
if (String.IsNullOrEmpty(operationName))
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("operationName");
|
||||
}
|
||||
|
||||
WebHttpDispatchOperationSelector selector = OperationContext.Current.EndpointDispatcher.DispatchRuntime.OperationSelector as WebHttpDispatchOperationSelector;
|
||||
if (selector == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR2.GetString(SR2.OperationSelectorNotWebSelector, typeof(WebHttpDispatchOperationSelector))));
|
||||
}
|
||||
return selector.GetUriTemplate(operationName);
|
||||
}
|
||||
|
||||
void AddContentType(string contentType, Encoding encoding)
|
||||
{
|
||||
if (string.IsNullOrEmpty(this.OutgoingResponse.ContentType))
|
||||
{
|
||||
if (encoding != null)
|
||||
{
|
||||
contentType = WebMessageEncoderFactory.GetContentType(contentType, encoding);
|
||||
}
|
||||
this.OutgoingResponse.ContentType = contentType;
|
||||
}
|
||||
}
|
||||
|
||||
class XmlSerializerBodyWriter : BodyWriter
|
||||
{
|
||||
object instance;
|
||||
XmlSerializer serializer;
|
||||
|
||||
public XmlSerializerBodyWriter(object instance, XmlSerializer serializer)
|
||||
: base(false)
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("instance");
|
||||
}
|
||||
if (serializer == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("serializer");
|
||||
}
|
||||
this.instance = instance;
|
||||
this.serializer = serializer;
|
||||
}
|
||||
|
||||
protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
|
||||
{
|
||||
serializer.Serialize(writer, instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user