File: WebServiceHost.cs

package info (click to toggle)
mono 6.12.0.199%2Bdfsg-6
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 1,296,836 kB
  • sloc: cs: 11,181,803; xml: 2,850,076; ansic: 699,709; cpp: 123,344; perl: 59,361; javascript: 30,841; asm: 21,853; makefile: 20,405; sh: 15,009; python: 4,839; pascal: 925; sql: 859; sed: 16; php: 1
file content (377 lines) | stat: -rw-r--r-- 17,861 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
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------

namespace System.ServiceModel.Web
{
    using System.Collections.Generic;
    using System.IO;
    using System.Net;
    using System.Runtime;
    using System.ServiceModel;
    using System.ServiceModel.Activation;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Configuration;
    using System.ServiceModel.Description;
    using System.ServiceModel.Dispatcher;
    using System.ServiceModel.Web.Configuration;

    public class WebServiceHost : ServiceHost
    {
        static readonly Type WebHttpBindingType = typeof(WebHttpBinding);
        static readonly string WebHttpEndpointKind = "webHttpEndpoint";

        public WebServiceHost()
            : base()
        {
        }

        public WebServiceHost(object singletonInstance, params Uri[] baseAddresses)
            : base(singletonInstance, baseAddresses)
        {
        }

        public WebServiceHost(Type serviceType, params Uri[] baseAddresses) :
            base(serviceType, baseAddresses)
        {
        }

        // This method adds automatic endpoints at the base addresses, 1 per site binding (http or https). It only configures
        // the security on the binding. It does not add any behaviors.
        // If there are no base addresses, or if endpoints have been configured explicitly, it does not add any
        // automatic endpoints.
        // If it adds automatic endpoints, it validates that the service implements a single contract
        internal static void AddAutomaticWebHttpBindingEndpoints(ServiceHost host, IDictionary<string, ContractDescription> implementedContracts,  string multipleContractsErrorMessage, string noContractErrorMessage, string standardEndpointKind)
        {
            bool enableAutoEndpointCompat = AppSettings.EnableAutomaticEndpointsCompatibility;
            // We do not add an automatic endpoint if an explicit endpoint has been configured unless
            // the user has specifically opted into compat mode.  See CSDMain bugs 176157 & 262728 for history
            if (host.Description.Endpoints != null 
                && host.Description.Endpoints.Count > 0
                && !enableAutoEndpointCompat)
            {
                return;
            }

            AuthenticationSchemes supportedSchemes = AuthenticationSchemes.None;
            if (host.BaseAddresses.Count > 0)
            {
                supportedSchemes = AspNetEnvironment.Current.GetAuthenticationSchemes(host.BaseAddresses[0]);

                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;
                        }
                    }
                }
            }
            Type contractType = null;
            // add an endpoint with the contract at each base address
            foreach (Uri baseAddress in host.BaseAddresses)
            {
                string uriScheme = baseAddress.Scheme;
                
                // HTTP and HTTPs are only supported schemes
                if (Object.ReferenceEquals(uriScheme, Uri.UriSchemeHttp) || Object.ReferenceEquals(uriScheme, Uri.UriSchemeHttps))
                {
                    // bypass adding the automatic endpoint if there's already one at the base address
                    bool isExplicitEndpointConfigured = false;
                    foreach (ServiceEndpoint endpoint in host.Description.Endpoints)
                    {
                        if (endpoint.Address != null && EndpointAddress.UriEquals(endpoint.Address.Uri, baseAddress, true, false))
                        {
                            isExplicitEndpointConfigured = true;
                            break;
                        }
                    }
                    if (isExplicitEndpointConfigured)
                    {
                        continue;
                    }

                    if (contractType == null)
                    {
                        if (implementedContracts.Count > 1)
                        {
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(multipleContractsErrorMessage));
                        }
                        else if (implementedContracts.Count == 0)
                        {
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(noContractErrorMessage));
                        }
                        foreach (ContractDescription contract in implementedContracts.Values)
                        {
                            contractType = contract.ContractType;
                            break;
                        }
                    }
                    
                    // Get the default web endpoint
                    ConfigLoader configLoader = new ConfigLoader(host.GetContractResolver(implementedContracts));
                    ServiceEndpointElement serviceEndpointElement = new ServiceEndpointElement();
                    serviceEndpointElement.Contract = contractType.FullName;
                    // Check for a protocol mapping
                    ProtocolMappingItem protocolMappingItem = ConfigLoader.LookupProtocolMapping(baseAddress.Scheme);
                    if (protocolMappingItem != null &&
                        string.Equals(protocolMappingItem.Binding, WebHttpBinding.WebHttpBindingConfigurationStrings.WebHttpBindingCollectionElementName, StringComparison.Ordinal))
                    {
                        serviceEndpointElement.BindingConfiguration = protocolMappingItem.BindingConfiguration;
                    }
                    serviceEndpointElement.Kind = standardEndpointKind;

                    // LookupEndpoint will not set the Endpoint address and listenUri
                    // because omitSettingEndpointAddress is set to true.
                    // We will set them after setting the binding security
                    ServiceEndpoint automaticEndpoint = configLoader.LookupEndpoint(serviceEndpointElement, null, host, host.Description, true /*omitSettingEndpointAddress*/);
                    WebHttpBinding binding = automaticEndpoint.Binding as WebHttpBinding;
                                    
                    bool automaticallyConfigureSecurity = !binding.Security.IsModeSet;
                    if (automaticallyConfigureSecurity)
                    {
                        if (Object.ReferenceEquals(uriScheme, Uri.UriSchemeHttps))
                        {
                            binding.Security.Mode = WebHttpSecurityMode.Transport;
                        }
                        else if (supportedSchemes != AuthenticationSchemes.None && supportedSchemes != AuthenticationSchemes.Anonymous)
                        {
                            binding.Security.Mode = WebHttpSecurityMode.TransportCredentialOnly;
                        }
                        else
                        {
                            binding.Security.Mode = WebHttpSecurityMode.None;
                        }
                    }
                    
                    if (automaticallyConfigureSecurity && AspNetEnvironment.Enabled)
                    {
                        SetBindingCredentialBasedOnHostedEnvironment(automaticEndpoint, supportedSchemes);
                    }

                    // Setting the Endpoint address and listenUri now that we've set the binding security
                    ConfigLoader.ConfigureEndpointAddress(serviceEndpointElement, host, automaticEndpoint);
                    ConfigLoader.ConfigureEndpointListenUri(serviceEndpointElement, host, automaticEndpoint);

                    host.AddServiceEndpoint(automaticEndpoint);
                }
            }
        }

        internal static void SetRawContentTypeMapperIfNecessary(ServiceEndpoint endpoint, bool isDispatch)
        {
            Binding binding = endpoint.Binding;
            ContractDescription contract = endpoint.Contract;
            if (binding == null)
            {
                return;
            }
            CustomBinding customBinding = new CustomBinding(binding);
            BindingElementCollection bec = customBinding.Elements;
            WebMessageEncodingBindingElement encodingElement = bec.Find<WebMessageEncodingBindingElement>();
            if (encodingElement == null || encodingElement.ContentTypeMapper != null)
            {
                return;
            }
            bool areAllOperationsRawMapperCompatible = true;
            int numStreamOperations = 0;
            foreach (OperationDescription operation in contract.Operations)
            {
                bool isCompatible = (isDispatch) ? IsRawContentMapperCompatibleDispatchOperation(operation, ref numStreamOperations) : IsRawContentMapperCompatibleClientOperation(operation, ref numStreamOperations);
                if (!isCompatible)
                {
                    areAllOperationsRawMapperCompatible = false;
                    break;
                }
            }
            if (areAllOperationsRawMapperCompatible && numStreamOperations > 0)
            {
                encodingElement.ContentTypeMapper = RawContentTypeMapper.Instance;
                endpoint.Binding = customBinding;
            }
        }

        protected override void OnOpening()
        {
            if (this.Description == null)
            {
                return;
            }
            
            // disable other things that listen for GET at base address and may conflict with auto-endpoints
            ServiceDebugBehavior sdb = this.Description.Behaviors.Find<ServiceDebugBehavior>();
            if (sdb != null)
            {
                sdb.HttpHelpPageEnabled = false;
                sdb.HttpsHelpPageEnabled = false;
            }
            ServiceMetadataBehavior smb = this.Description.Behaviors.Find<ServiceMetadataBehavior>();
            if (smb != null)
            {
                smb.HttpGetEnabled = false;
                smb.HttpsGetEnabled = false;
            }

            AddAutomaticWebHttpBindingEndpoints(this, this.ImplementedContracts, SR2.GetString(SR2.HttpTransferServiceHostMultipleContracts, this.Description.Name), SR2.GetString(SR2.HttpTransferServiceHostNoContract, this.Description.Name), WebHttpEndpointKind);

            // for both user-defined and automatic endpoints, ensure they have the right behavior and content type mapper added
            foreach (ServiceEndpoint serviceEndpoint in this.Description.Endpoints)
            {
                if (serviceEndpoint.Binding != null && serviceEndpoint.Binding.CreateBindingElements().Find<WebMessageEncodingBindingElement>() != null)
                {
                    SetRawContentTypeMapperIfNecessary(serviceEndpoint, true);
                    if (serviceEndpoint.Behaviors.Find<WebHttpBehavior>() == null)
                    {
                        ConfigLoader.LoadDefaultEndpointBehaviors(serviceEndpoint);
                        if (serviceEndpoint.Behaviors.Find<WebHttpBehavior>() == null)
                        {
                            serviceEndpoint.Behaviors.Add(new WebHttpBehavior());
                        }
                    }
                }
            }

            base.OnOpening();
        }

        static bool IsRawContentMapperCompatibleClientOperation(OperationDescription operation, ref int numStreamOperations)
        {
            // An operation is raw encoder compatible on the client side iff the response is a Stream or void
            // The request is driven by the format property on the message and not by the content type 
            if (operation.Messages.Count > 1 & !IsResponseStreamOrVoid(operation, ref numStreamOperations))
            {
                return false;
            }
            return true;

        }

        static bool IsRawContentMapperCompatibleDispatchOperation(OperationDescription operation, ref int numStreamOperations)
        {
            // An operation is raw encoder compatible on the dispatch side iff the request body is a Stream or void
            // The response is driven by the format property on the message and not by the content type 
            UriTemplateDispatchFormatter throwAway = new UriTemplateDispatchFormatter(operation, null, new QueryStringConverter(), operation.DeclaringContract.Name, new Uri("http://localhost"));
            int numUriVariables = throwAway.pathMapping.Count + throwAway.queryMapping.Count;
            bool isRequestCompatible = false;
            if (numUriVariables > 0)
            {
                // we need the local variable tmp because ref parameters are not allowed to be passed into
                // anonymous methods by the compiler.
                int tmp = 0;
                WebHttpBehavior.HideRequestUriTemplateParameters(operation, throwAway, delegate()
                {
                    isRequestCompatible = IsRequestStreamOrVoid(operation, ref tmp);
                });
                numStreamOperations += tmp;
            }
            else
            {
                isRequestCompatible = IsRequestStreamOrVoid(operation, ref numStreamOperations);
            }
            return isRequestCompatible;
        }

        static bool IsRequestStreamOrVoid(OperationDescription operation, ref int numStreamOperations)
        {
            MessageDescription message = operation.Messages[0];
            if (WebHttpBehavior.IsTypedMessage(message) || WebHttpBehavior.IsUntypedMessage(message))
            {
                return false;
            }
            if (message.Body.Parts.Count == 0)
            {
                return true;
            }
            else if (message.Body.Parts.Count == 1)
            {
                if (IsStreamPart(message.Body.Parts[0].Type))
                {
                    ++numStreamOperations;
                    return true;
                }
                else if (IsVoidPart(message.Body.Parts[0].Type))
                {
                    return true;
                }
            }
            return false;
        }

        static bool IsResponseStreamOrVoid(OperationDescription operation, ref int numStreamOperations)
        {
            if (operation.Messages.Count <= 1)
            {
                return true;
            }
            MessageDescription message = operation.Messages[1];
            if (WebHttpBehavior.IsTypedMessage(message) || WebHttpBehavior.IsUntypedMessage(message))
            {
                return false;
            }
            if (message.Body.Parts.Count == 0)
            {
                if (message.Body.ReturnValue == null || IsVoidPart(message.Body.ReturnValue.Type))
                {
                    return true;
                }
                else if (IsStreamPart(message.Body.ReturnValue.Type))
                {
                    ++numStreamOperations;
                    return true;
                }
            }
            return false;
        }

        static bool IsStreamPart(Type type)
        {
            return (type == typeof(Stream));
        }

        static bool IsVoidPart(Type type)
        {
            return (type == null || type == typeof(void));
        }

        // For automatic endpoints, in the hosted case we configure a credential type based on the vdir settings.
        // For IIS, in IntegratedWindowsAuth mode we pick Negotiate.
        static void SetBindingCredentialBasedOnHostedEnvironment(ServiceEndpoint serviceEndpoint, AuthenticationSchemes supportedSchemes)
        {
            WebHttpBinding whb = serviceEndpoint.Binding as WebHttpBinding;
            Fx.Assert(whb != null, "Automatic endpoint must be WebHttpBinding");
            
            switch (supportedSchemes)
            {
                case AuthenticationSchemes.Digest:
                    whb.Security.Transport.ClientCredentialType = HttpClientCredentialType.Digest;
                    break;
                case AuthenticationSchemes.IntegratedWindowsAuthentication:
                // fall through to Negotiate
                case AuthenticationSchemes.Negotiate:
                    whb.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
                    break;
                case AuthenticationSchemes.Ntlm:
                    whb.Security.Transport.ClientCredentialType = HttpClientCredentialType.Ntlm;
                    break;
                case AuthenticationSchemes.Basic:
                    whb.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
                    break;
                case AuthenticationSchemes.Anonymous:
                    whb.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
                    break;
                default:
                    whb.Security.Transport.ClientCredentialType = HttpClientCredentialType.InheritedFromHost;
                    break;
            }
            
        }
    }
}