File: ServerProtocol.cs

package info (click to toggle)
mono 6.8.0.105%2Bdfsg-3.3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 1,284,512 kB
  • sloc: cs: 11,172,132; xml: 2,850,069; ansic: 671,653; cpp: 122,091; perl: 59,366; javascript: 30,841; asm: 22,168; makefile: 20,093; sh: 15,020; python: 4,827; pascal: 925; sql: 859; sed: 16; php: 1
file content (333 lines) | stat: -rw-r--r-- 15,655 bytes parent folder | download | duplicates (7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
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 DOS mitigation mode. 
        //    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);

    }

}