You've already forked linux-packaging-mono
Imported Upstream version 4.6.0.125
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
This commit is contained in:
parent
a569aebcfd
commit
e79aa3c0ed
@ -0,0 +1,333 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="ServerProtocol.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace System.Web.Services.Protocols {
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Collections;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Xml.Serialization;
|
||||
using System.Web.Caching;
|
||||
using System.ComponentModel;
|
||||
using System.Text;
|
||||
using System.Net;
|
||||
using System.Web.Services;
|
||||
using System.Threading;
|
||||
using System.Security.Permissions;
|
||||
using System.Web.Services.Diagnostics;
|
||||
|
||||
[PermissionSet(SecurityAction.InheritanceDemand, Name = "FullTrust")]
|
||||
[PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")]
|
||||
public abstract class ServerProtocol {
|
||||
Type type;
|
||||
HttpRequest request;
|
||||
HttpResponse response;
|
||||
HttpContext context;
|
||||
object target;
|
||||
WebMethodAttribute methodAttr;
|
||||
|
||||
private static Object s_InternalSyncObject;
|
||||
internal static Object InternalSyncObject {
|
||||
get {
|
||||
if (s_InternalSyncObject == null) {
|
||||
Object o = new Object();
|
||||
Interlocked.CompareExchange(ref s_InternalSyncObject, o, null);
|
||||
}
|
||||
return s_InternalSyncObject;
|
||||
}
|
||||
}
|
||||
|
||||
internal void SetContext(Type type, HttpContext context, HttpRequest request, HttpResponse response) {
|
||||
PartialTrustHelpers.FailIfInPartialTrustOutsideAspNet();
|
||||
this.type = type;
|
||||
this.context = context;
|
||||
this.request = request;
|
||||
this.response = response;
|
||||
Initialize();
|
||||
}
|
||||
|
||||
internal virtual void CreateServerInstance() {
|
||||
target = Activator.CreateInstance(ServerType.Type);
|
||||
WebService service = target as WebService;
|
||||
if (service != null)
|
||||
service.SetContext(context);
|
||||
}
|
||||
|
||||
internal virtual void DisposeServerInstance() {
|
||||
if (target == null) return;
|
||||
IDisposable disposable = target as IDisposable;
|
||||
if (disposable != null)
|
||||
disposable.Dispose();
|
||||
target = null;
|
||||
}
|
||||
|
||||
protected internal HttpContext Context {
|
||||
get { return context; }
|
||||
}
|
||||
|
||||
protected internal HttpRequest Request {
|
||||
get { return request; }
|
||||
}
|
||||
|
||||
protected internal HttpResponse Response {
|
||||
get { return response; }
|
||||
}
|
||||
|
||||
internal Type Type {
|
||||
get { return type; }
|
||||
}
|
||||
|
||||
protected virtual internal object Target {
|
||||
get { return target; }
|
||||
}
|
||||
|
||||
internal virtual bool WriteException(Exception e, Stream outputStream) {
|
||||
// return true if exception should not be re-thrown to ASP.NET
|
||||
return false;
|
||||
}
|
||||
|
||||
internal abstract bool Initialize();
|
||||
internal abstract object[] ReadParameters();
|
||||
internal abstract void WriteReturns(object[] returns, Stream outputStream);
|
||||
internal abstract LogicalMethodInfo MethodInfo { get; }
|
||||
internal abstract ServerType ServerType { get; }
|
||||
internal abstract bool IsOneWay { get; }
|
||||
internal virtual Exception OnewayInitException { get { return null; } }
|
||||
|
||||
internal WebMethodAttribute MethodAttribute {
|
||||
get {
|
||||
if (methodAttr == null)
|
||||
methodAttr = MethodInfo.MethodAttribute;
|
||||
return methodAttr;
|
||||
}
|
||||
}
|
||||
|
||||
internal string GenerateFaultString(Exception e) {
|
||||
return GenerateFaultString(e, false);
|
||||
}
|
||||
|
||||
internal static void SetHttpResponseStatusCode(HttpResponse httpResponse, int statusCode) {
|
||||
// We skip IIS custom errors for HTTP requests.
|
||||
httpResponse.TrySkipIisCustomErrors = true;
|
||||
httpResponse.StatusCode = statusCode;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
internal string GenerateFaultString(Exception e, bool htmlEscapeMessage) {
|
||||
bool isDevelopmentServer = Context != null && !Context.IsCustomErrorEnabled;
|
||||
if (isDevelopmentServer && !htmlEscapeMessage) {
|
||||
//If the user has specified it's a development server (versus a production server) in ASP.NET config,
|
||||
//then we should just return e.ToString instead of extracting the list of messages.
|
||||
return e.ToString();
|
||||
}
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if (isDevelopmentServer) {
|
||||
// we are dumping the ecseption directly to IE, need to encode
|
||||
GenerateFaultString(e, builder);
|
||||
}
|
||||
else {
|
||||
for (Exception inner = e; inner != null; inner = inner.InnerException) {
|
||||
string text = htmlEscapeMessage ? HttpUtility.HtmlEncode(inner.Message) : inner.Message;
|
||||
if (text.Length == 0) text = e.GetType().Name;
|
||||
builder.Append(text);
|
||||
if (inner.InnerException != null) builder.Append(" ---> ");
|
||||
}
|
||||
}
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
static void GenerateFaultString(Exception e, StringBuilder builder) {
|
||||
builder.Append(e.GetType().FullName);
|
||||
if (e.Message != null && e.Message.Length > 0) {
|
||||
builder.Append(": ");
|
||||
builder.Append(HttpUtility.HtmlEncode(e.Message));
|
||||
}
|
||||
if (e.InnerException != null) {
|
||||
builder.Append(" ---> ");
|
||||
GenerateFaultString(e.InnerException, builder);
|
||||
builder.Append(Environment.NewLine);
|
||||
builder.Append(" ");
|
||||
builder.Append(Res.GetString(Res.StackTraceEnd));
|
||||
}
|
||||
if (e.StackTrace != null) {
|
||||
builder.Append(Environment.NewLine);
|
||||
builder.Append(e.StackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
internal void WriteOneWayResponse() {
|
||||
context.Response.ContentType = null;
|
||||
Response.StatusCode = (int)HttpStatusCode.Accepted;
|
||||
}
|
||||
|
||||
delegate string CreateCustomKeyForAspNetWebServiceMetadataCache(Type protocolType, Type serverType, string originalKey);
|
||||
|
||||
static string DefaultCreateCustomKeyForAspNetWebServiceMetadataCache(Type protocolType, Type serverType, string originalKey) {
|
||||
return originalKey;
|
||||
}
|
||||
|
||||
static CreateCustomKeyForAspNetWebServiceMetadataCache GetCreateCustomKeyForAspNetWebServiceMetadataCacheDelegate(Type serverType) {
|
||||
PartialTrustHelpers.FailIfInPartialTrustOutsideAspNet();
|
||||
string key = "CreateCustomKeyForAspNetWebServiceMetadataCache-" + serverType.FullName;
|
||||
CreateCustomKeyForAspNetWebServiceMetadataCache result = (CreateCustomKeyForAspNetWebServiceMetadataCache)HttpRuntime.Cache.Get(key);
|
||||
if (result == null) {
|
||||
MethodInfo createKeyMethod = serverType.GetMethod(
|
||||
"CreateCustomKeyForAspNetWebServiceMetadataCache",
|
||||
BindingFlags.Public | BindingFlags.Static | BindingFlags.ExactBinding | BindingFlags.FlattenHierarchy,
|
||||
null,
|
||||
new Type[] { typeof(Type), typeof(Type), typeof(string) },
|
||||
null);
|
||||
|
||||
if (createKeyMethod == null) {
|
||||
result = ServerProtocol.DefaultCreateCustomKeyForAspNetWebServiceMetadataCache;
|
||||
|
||||
} else {
|
||||
result = delegate(Type pt, Type st, string originalString)
|
||||
{
|
||||
return (string)createKeyMethod.Invoke(null, new object[] { pt, st, originalString });
|
||||
};
|
||||
}
|
||||
|
||||
HttpRuntime.Cache.Add(key, result, null, Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration, CacheItemPriority.NotRemovable, null);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
string CreateKey(Type protocolType, Type serverType, bool excludeSchemeHostPort = false, string keySuffix = null) {
|
||||
//
|
||||
// we want to use the hostname to cache since for documentation, WSDL
|
||||
// contains the cache hostname, but we definitely don't want to cache the query string!
|
||||
//
|
||||
string protocolTypeName = protocolType.FullName;
|
||||
string serverTypeName = serverType.FullName;
|
||||
string typeHandleString = serverType.TypeHandle.Value.ToString();
|
||||
string url = excludeSchemeHostPort ? Request.Url.AbsolutePath : Request.Url.GetLeftPart(UriPartial.Path);
|
||||
int length = protocolTypeName.Length + url.Length + serverTypeName.Length + typeHandleString.Length;
|
||||
StringBuilder sb = new StringBuilder(length);
|
||||
sb.Append(protocolTypeName);
|
||||
sb.Append(url);
|
||||
sb.Append(serverTypeName);
|
||||
sb.Append(typeHandleString);
|
||||
if (keySuffix != null) {
|
||||
sb.Append(keySuffix);
|
||||
}
|
||||
|
||||
CreateCustomKeyForAspNetWebServiceMetadataCache createKey = ServerProtocol.GetCreateCustomKeyForAspNetWebServiceMetadataCacheDelegate(serverType);
|
||||
|
||||
return createKey(protocolType, serverType, sb.ToString());
|
||||
}
|
||||
|
||||
protected void AddToCache(Type protocolType, Type serverType, object value) {
|
||||
this.AddToCache(protocolType, serverType, value, false);
|
||||
}
|
||||
|
||||
// See comment on the ServerProtocol.IsCacheUnderPressure method for explanation of the excludeSchemeHostPort logic.
|
||||
internal void AddToCache(Type protocolType, Type serverType, object value, bool excludeSchemeHostPort) {
|
||||
PartialTrustHelpers.FailIfInPartialTrustOutsideAspNet();
|
||||
HttpRuntime.Cache.Insert(CreateKey(protocolType, serverType, excludeSchemeHostPort),
|
||||
value,
|
||||
null,
|
||||
Cache.NoAbsoluteExpiration,
|
||||
Cache.NoSlidingExpiration,
|
||||
CacheItemPriority.NotRemovable,
|
||||
null);
|
||||
}
|
||||
|
||||
protected object GetFromCache(Type protocolType, Type serverType) {
|
||||
return this.GetFromCache(protocolType, serverType, false);
|
||||
}
|
||||
|
||||
internal object GetFromCache(Type protocolType, Type serverType, bool excludeSchemeHostPort) {
|
||||
PartialTrustHelpers.FailIfInPartialTrustOutsideAspNet();
|
||||
return HttpRuntime.Cache.Get(CreateKey(protocolType, serverType, excludeSchemeHostPort));
|
||||
}
|
||||
|
||||
// IsCacheUnderPressure is part of a DOS mitigation mechanism addressing CSDMain#195148. Original problem: when a large number of
|
||||
// HTTP requests for WSDL or the ASMX documentation page is made, each request with a unique value of the HOST header, a unique response
|
||||
// is generated and cached for each of the requests (responses contain the scheme/host/port of the request).
|
||||
// This leads to ever growing memory consumption and eventual crash of the process.
|
||||
// The mitigation for this DOS attack uses the following mechanism:
|
||||
// 1. The behavior of the system remains unchanged for the first 10 requests for WSDL of a given ASMX service that have differing
|
||||
// scheme/host/port combination of the request URI. This is to avoid breaking behavioral changes in the 99.99% case,
|
||||
// since the DOS attack cannot be generically fixed without breaking behavioral changes. The value of 10 is baked in,
|
||||
// and we consider it a reasonable default based on the assumption that ASMX services in most circumstances cannot be
|
||||
// reached using more than 10 different values of the scheme/host/port.
|
||||
// 2. For any requests for WSDL going beyond the 10 limit of scheme/host/port combination, we go into a <20>DOS mitigation mode<64>.
|
||||
// The mode prevents the eventual process crash while introducing marginal breaking behavioral changes:
|
||||
// a. We create a single service description and cache it using the AbsolutePath of the request URI alone
|
||||
// (as opposed to scheme/host/port + AbsolutePath).
|
||||
// b. For every request for WSDL/disco/documentation document, we fix up the URLs in the returned document to match the
|
||||
// scheme/host/port of the actual request for WSDL/disco. This fixup only applies to the WSDL extensions we have shipped in .NET
|
||||
// and does not apply to custom extensions implemented externally, hence the breaking behavioral change.
|
||||
// This mechamism affects the DiscoveryServerProtocol and DocumentationServerProtocol.
|
||||
internal bool IsCacheUnderPressure(Type protocolType, Type serverType) {
|
||||
PartialTrustHelpers.FailIfInPartialTrustOutsideAspNet();
|
||||
|
||||
const int threshold = 10;
|
||||
string key = this.CreateKey(protocolType, serverType, true, "CachePressure");
|
||||
ServerProtocolCachePressure item = (ServerProtocolCachePressure)HttpRuntime.Cache.Get(key);
|
||||
|
||||
// There is a potential race condition in creating a new entry or increasing the value of an existing entry,
|
||||
// but it is acceptable since DOS threshold enforcement need not be exact.
|
||||
|
||||
if (item != null) {
|
||||
return item.Pressure < threshold ? Interlocked.Increment(ref item.Pressure) >= threshold : false;
|
||||
}
|
||||
else {
|
||||
HttpRuntime.Cache.Insert(
|
||||
key,
|
||||
new ServerProtocolCachePressure { Pressure = 1 },
|
||||
null,
|
||||
Cache.NoAbsoluteExpiration,
|
||||
Cache.NoSlidingExpiration,
|
||||
CacheItemPriority.NotRemovable,
|
||||
null);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class ServerProtocolCachePressure {
|
||||
public int Pressure;
|
||||
}
|
||||
}
|
||||
|
||||
[PermissionSet(SecurityAction.InheritanceDemand, Name = "FullTrust")]
|
||||
[PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")]
|
||||
public abstract class ServerProtocolFactory {
|
||||
internal ServerProtocol Create(Type type, HttpContext context, HttpRequest request, HttpResponse response, out bool abortProcessing) {
|
||||
ServerProtocol serverProtocol = null;
|
||||
abortProcessing = false;
|
||||
serverProtocol = CreateIfRequestCompatible(request);
|
||||
try {
|
||||
if (serverProtocol != null)
|
||||
serverProtocol.SetContext(type, context, request, response);
|
||||
return serverProtocol;
|
||||
}
|
||||
catch (Exception e) {
|
||||
abortProcessing = true;
|
||||
if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) {
|
||||
throw;
|
||||
}
|
||||
if (Tracing.On) Tracing.ExceptionCatch(TraceEventType.Warning, this, "Create", e);
|
||||
if (serverProtocol != null) {
|
||||
// give the protocol a shot at handling the error in a custom way
|
||||
if (!serverProtocol.WriteException(e, serverProtocol.Response.OutputStream))
|
||||
throw new InvalidOperationException(Res.GetString(Res.UnableToHandleRequest0), e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected abstract ServerProtocol CreateIfRequestCompatible(HttpRequest request);
|
||||
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user