1 //------------------------------------------------------------------------------
2 // <copyright file="HttpProtocolImporter.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6 namespace System.Web.Services.Description {
7 
8     using System.Web.Services;
9     using System.Web.Services.Protocols;
10     using System.Xml;
11     using System.Xml.Serialization;
12     using System.Xml.Schema;
13     using System.Collections;
14     using System;
15     using System.Reflection;
16     using System.CodeDom;
17     using System.CodeDom.Compiler;
18     using System.Web.Services.Configuration;
19     using System.Diagnostics;
20     using System.ComponentModel;
21     using System.Threading;
22     using System.EnterpriseServices;
23 
24     //
25     internal class HttpMethodInfo {
26         internal MimeParameterCollection UrlParameters;
27         internal MimeParameterCollection MimeParameters;
28         internal MimeReturn MimeReturn;
29         internal string Name;
30         internal string Href;
31     }
32 
33     internal abstract class HttpProtocolImporter : ProtocolImporter {
34         MimeImporter[] importers;
35         ArrayList[] importedParameters;
36         ArrayList[] importedReturns;
37         bool hasInputPayload;
38         ArrayList codeClasses = new ArrayList();
39 
HttpProtocolImporter(bool hasInputPayload)40         protected HttpProtocolImporter(bool hasInputPayload) {
41             Type[] importerTypes = WebServicesSection.Current.MimeImporterTypes;
42             importers = new MimeImporter[importerTypes.Length];
43             importedParameters = new ArrayList[importerTypes.Length];
44             importedReturns = new ArrayList[importerTypes.Length];
45             for (int i = 0; i < importers.Length; i++) {
46                 MimeImporter importer = (MimeImporter)Activator.CreateInstance(importerTypes[i]);
47                 importer.ImportContext = this;
48                 importedParameters[i] = new ArrayList();
49                 importedReturns[i] = new ArrayList();
50                 importers[i] = importer;
51             }
52             this.hasInputPayload = hasInputPayload;
53         }
54 
55         //
ImportMimeParameters()56         MimeParameterCollection ImportMimeParameters() {
57             for (int i = 0; i < importers.Length; i++) {
58                 MimeParameterCollection importedParameters = importers[i].ImportParameters();
59                 if (importedParameters != null) {
60                     this.importedParameters[i].Add(importedParameters);
61                     return importedParameters;
62                 }
63             }
64             return null;
65         }
66 
ImportMimeReturn()67         MimeReturn ImportMimeReturn() {
68             MimeReturn importedReturn;
69             if (OperationBinding.Output.Extensions.Count == 0) {
70                 importedReturn = new MimeReturn();
71                 importedReturn.TypeName = typeof(void).FullName;
72                 return importedReturn;
73             }
74             for (int i = 0; i < importers.Length; i++) {
75                 importedReturn = importers[i].ImportReturn();
76                 if (importedReturn != null) {
77                     this.importedReturns[i].Add(importedReturn);
78                     return importedReturn;
79                 }
80             }
81             return null;
82         }
83 
ImportUrlParameters()84         MimeParameterCollection ImportUrlParameters() {
85             //
86             HttpUrlEncodedBinding httpUrlEncodedBinding = (HttpUrlEncodedBinding)OperationBinding.Input.Extensions.Find(typeof(HttpUrlEncodedBinding));
87             if (httpUrlEncodedBinding == null) return new MimeParameterCollection();
88             return ImportStringParametersMessage();
89         }
90 
ImportStringParametersMessage()91         internal MimeParameterCollection ImportStringParametersMessage() {
92             MimeParameterCollection parameters = new MimeParameterCollection();
93             foreach (MessagePart part in InputMessage.Parts) {
94                 MimeParameter parameter = ImportUrlParameter(part);
95                 if (parameter == null) return null;
96                 parameters.Add(parameter);
97             }
98             return parameters;
99         }
100 
ImportUrlParameter(MessagePart part)101         MimeParameter ImportUrlParameter(MessagePart part) {
102             //
103             MimeParameter parameter = new MimeParameter();
104             parameter.Name = CodeIdentifier.MakeValid(XmlConvert.DecodeName(part.Name));
105             parameter.TypeName = IsRepeatingParameter(part) ? typeof(string[]).FullName : typeof(string).FullName;
106             return parameter;
107         }
108 
IsRepeatingParameter(MessagePart part)109         bool IsRepeatingParameter(MessagePart part) {
110             XmlSchemaComplexType type = (XmlSchemaComplexType)Schemas.Find(part.Type, typeof(XmlSchemaComplexType));
111             if (type == null) return false;
112             if (type.ContentModel == null) return false;
113             if (type.ContentModel.Content == null) throw new ArgumentException(Res.GetString(Res.Missing2, type.Name, type.ContentModel.GetType().Name), "part");
114             if (type.ContentModel.Content is XmlSchemaComplexContentExtension) {
115                 return ((XmlSchemaComplexContentExtension)type.ContentModel.Content).BaseTypeName == new XmlQualifiedName(Soap.ArrayType, Soap.Encoding);
116             }
117             else if (type.ContentModel.Content is XmlSchemaComplexContentRestriction) {
118                 return ((XmlSchemaComplexContentRestriction)type.ContentModel.Content).BaseTypeName == new XmlQualifiedName(Soap.ArrayType, Soap.Encoding);
119             }
120             return false;
121         }
122 
AppendMetadata(CodeAttributeDeclarationCollection from, CodeAttributeDeclarationCollection to)123         static void AppendMetadata(CodeAttributeDeclarationCollection from, CodeAttributeDeclarationCollection to) {
124             foreach (CodeAttributeDeclaration attr in from) to.Add(attr);
125         }
126 
GenerateMethod(HttpMethodInfo method)127         CodeMemberMethod GenerateMethod(HttpMethodInfo method) {
128             MimeParameterCollection parameters = method.MimeParameters != null ? method.MimeParameters : method.UrlParameters;
129 
130             string[] parameterTypeNames = new string[parameters.Count];
131             string[] parameterNames = new string[parameters.Count];
132 
133             for (int i = 0; i < parameters.Count; i++) {
134                 MimeParameter param = parameters[i];
135                 parameterNames[i] = param.Name;
136                 parameterTypeNames[i] = param.TypeName;
137             }
138 
139             CodeAttributeDeclarationCollection metadata = new CodeAttributeDeclarationCollection();
140 
141             CodeExpression[] formatterTypes = new CodeExpression[2];
142 
143             if (method.MimeReturn.ReaderType == null) {
144                 formatterTypes[0] = new CodeTypeOfExpression(typeof(NopReturnReader).FullName);
145             }
146             else {
147                 formatterTypes[0] = new CodeTypeOfExpression(method.MimeReturn.ReaderType.FullName);
148             }
149 
150             if (method.MimeParameters != null)
151                 formatterTypes[1] = new CodeTypeOfExpression(method.MimeParameters.WriterType.FullName);
152             else
153                 formatterTypes[1] = new CodeTypeOfExpression(typeof(UrlParameterWriter).FullName);
154 
155             WebCodeGenerator.AddCustomAttribute(metadata, typeof(HttpMethodAttribute), formatterTypes, new string[0], new CodeExpression[0]);
156 
157 
158             CodeMemberMethod mainCodeMethod = WebCodeGenerator.AddMethod(this.CodeTypeDeclaration, method.Name, new CodeFlags[parameterTypeNames.Length], parameterTypeNames, parameterNames,
159                                         method.MimeReturn.TypeName, metadata,
160                                         CodeFlags.IsPublic | (Style == ServiceDescriptionImportStyle.Client ? 0 : CodeFlags.IsAbstract));
161 
162             AppendMetadata(method.MimeReturn.Attributes, mainCodeMethod.ReturnTypeCustomAttributes);
163 
164             mainCodeMethod.Comments.Add(new CodeCommentStatement(Res.GetString(Res.CodeRemarks), true));
165 
166             for (int i = 0; i < parameters.Count; i++) {
167                 AppendMetadata(parameters[i].Attributes, mainCodeMethod.Parameters[i].CustomAttributes);
168             }
169 
170             if (Style == ServiceDescriptionImportStyle.Client) {
171                 bool oldAsync = (ServiceImporter.CodeGenerationOptions & CodeGenerationOptions.GenerateOldAsync) != 0;
172                 bool newAsync = (ServiceImporter.CodeGenerationOptions & CodeGenerationOptions.GenerateNewAsync) != 0 &&
173                     ServiceImporter.CodeGenerator.Supports(GeneratorSupport.DeclareEvents) &&
174                     ServiceImporter.CodeGenerator.Supports(GeneratorSupport.DeclareDelegates);
175 
176                 CodeExpression[] invokeParams = new CodeExpression[3];
177                 CreateInvokeParams(invokeParams, method, parameterNames);
178                 CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), "Invoke", invokeParams);
179                 if (method.MimeReturn.ReaderType != null) {
180                     mainCodeMethod.Statements.Add(new CodeMethodReturnStatement(new CodeCastExpression(method.MimeReturn.TypeName, invoke)));
181                 }
182                 else {
183                     mainCodeMethod.Statements.Add(new CodeExpressionStatement(invoke));
184                 }
185 
186                 metadata = new CodeAttributeDeclarationCollection();
187 
188                 string[] asyncParameterTypeNames = new string[parameterTypeNames.Length + 2];
189                 parameterTypeNames.CopyTo(asyncParameterTypeNames, 0);
190                 asyncParameterTypeNames[parameterTypeNames.Length] = typeof(AsyncCallback).FullName;
191                 asyncParameterTypeNames[parameterTypeNames.Length + 1] = typeof(object).FullName;
192 
193                 string[] asyncParameterNames = new string[parameterNames.Length + 2];
194                 parameterNames.CopyTo(asyncParameterNames, 0);
195                 asyncParameterNames[parameterNames.Length] = "callback";
196                 asyncParameterNames[parameterNames.Length + 1] = "asyncState";
197 
198                 if (oldAsync) {
199                     CodeMemberMethod beginCodeMethod = WebCodeGenerator.AddMethod(this.CodeTypeDeclaration, "Begin" + method.Name, new CodeFlags[asyncParameterTypeNames.Length],
200                         asyncParameterTypeNames, asyncParameterNames,
201                         typeof(IAsyncResult).FullName, metadata, CodeFlags.IsPublic);
202                     beginCodeMethod.Comments.Add(new CodeCommentStatement(Res.GetString(Res.CodeRemarks), true));
203 
204                     invokeParams = new CodeExpression[5];
205                     CreateInvokeParams(invokeParams, method, parameterNames);
206 
207                     invokeParams[3] = new CodeArgumentReferenceExpression( "callback");
208                     invokeParams[4] = new CodeArgumentReferenceExpression( "asyncState");
209 
210                     invoke = new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), "BeginInvoke", invokeParams);
211                     beginCodeMethod.Statements.Add(new CodeMethodReturnStatement(invoke));
212 
213                     CodeMemberMethod endCodeMethod = WebCodeGenerator.AddMethod(this.CodeTypeDeclaration, "End" + method.Name, new CodeFlags[1],
214                         new string[] { typeof(IAsyncResult).FullName },
215                         new string[] { "asyncResult" },
216                         method.MimeReturn.TypeName, metadata, CodeFlags.IsPublic);
217                     endCodeMethod.Comments.Add(new CodeCommentStatement(Res.GetString(Res.CodeRemarks), true));
218 
219                     CodeExpression expr = new CodeArgumentReferenceExpression( "asyncResult");
220                     invoke = new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), "EndInvoke", new CodeExpression[] { expr });
221                     if (method.MimeReturn.ReaderType != null) {
222                         endCodeMethod.Statements.Add(new CodeMethodReturnStatement(new CodeCastExpression(method.MimeReturn.TypeName, invoke)));
223                     }
224                     else {
225                         endCodeMethod.Statements.Add(new CodeExpressionStatement(invoke));
226                     }
227                 }
228                 if (newAsync) {
229                     metadata = new CodeAttributeDeclarationCollection();
230                     string uniqueMethodName = method.Name;
231                     string methodKey = MethodSignature(uniqueMethodName, method.MimeReturn.TypeName, new CodeFlags[parameterTypeNames.Length], parameterTypeNames);
232                     DelegateInfo delegateInfo = (DelegateInfo)ExportContext[methodKey];
233                     if (delegateInfo == null) {
234                         string handlerType = ClassNames.AddUnique(uniqueMethodName + "CompletedEventHandler", uniqueMethodName);
235                         string handlerArgs = ClassNames.AddUnique(uniqueMethodName + "CompletedEventArgs", uniqueMethodName);
236                         delegateInfo = new DelegateInfo(handlerType, handlerArgs);
237                     }
238                     string handlerName = MethodNames.AddUnique(uniqueMethodName + "Completed", uniqueMethodName);
239                     string asyncName = MethodNames.AddUnique(uniqueMethodName + "Async", uniqueMethodName);
240                     string callbackMember = MethodNames.AddUnique(uniqueMethodName + "OperationCompleted", uniqueMethodName);
241                     string callbackName = MethodNames.AddUnique("On" + uniqueMethodName + "OperationCompleted", uniqueMethodName);
242 
243                     // public event xxxCompletedEventHandler xxxCompleted;
244                     WebCodeGenerator.AddEvent(this.CodeTypeDeclaration.Members, delegateInfo.handlerType, handlerName);
245 
246                     // private SendOrPostCallback xxxOperationCompleted;
247                     WebCodeGenerator.AddCallbackDeclaration(this.CodeTypeDeclaration.Members, callbackMember);
248 
249                     // create the pair of xxxAsync methods
250                     string userState = UniqueName("userState", parameterNames);
251                     CodeMemberMethod asyncCodeMethod = WebCodeGenerator.AddAsyncMethod(this.CodeTypeDeclaration, asyncName,
252                         parameterTypeNames, parameterNames, callbackMember, callbackName, userState);
253 
254                     // Generate InvokeAsync call
255                     invokeParams = new CodeExpression[5];
256                     CreateInvokeParams(invokeParams, method, parameterNames);
257                     invokeParams[3] = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), callbackMember);
258                     invokeParams[4] = new CodeArgumentReferenceExpression(userState);
259 
260                     invoke = new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), "InvokeAsync", invokeParams);
261                     asyncCodeMethod.Statements.Add(invoke);
262 
263                     //  private void On_xxx_OperationCompleted(object arg) {..}
264                     bool methodHasReturn = method.MimeReturn.ReaderType != null;
265                     WebCodeGenerator.AddCallbackImplementation(this.CodeTypeDeclaration, callbackName, handlerName, delegateInfo.handlerArgs, methodHasReturn);
266                     if (ExportContext[methodKey] == null) {
267                         // public delegate void xxxCompletedEventHandler(object sender, System.ComponentModel.AsyncCompletedEventArgs args);
268                         WebCodeGenerator.AddDelegate(ExtraCodeClasses, delegateInfo.handlerType, methodHasReturn ? delegateInfo.handlerArgs : typeof(AsyncCompletedEventArgs).FullName);
269 
270                         if (methodHasReturn) {
271                             ExtraCodeClasses.Add(WebCodeGenerator.CreateArgsClass(delegateInfo.handlerArgs, new string[] { method.MimeReturn.TypeName }, new string[] { "Result" },
272                                 ServiceImporter.CodeGenerator.Supports(GeneratorSupport.PartialTypes)));
273                         }
274                         ExportContext[methodKey] = delegateInfo;
275                     }
276                 }
277             }
278             return mainCodeMethod;
279         }
280 
CreateInvokeParams(CodeExpression[] invokeParams, HttpMethodInfo method, string[] parameterNames)281         void CreateInvokeParams(CodeExpression[] invokeParams, HttpMethodInfo method, string[] parameterNames) {
282             invokeParams[0] = new CodePrimitiveExpression(method.Name);
283 
284             CodeExpression left = new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), "Url");
285             CodeExpression right = new CodePrimitiveExpression(method.Href);
286             invokeParams[1] = new CodeBinaryOperatorExpression(left, CodeBinaryOperatorType.Add, right);
287 
288             CodeExpression[] values = new CodeExpression[parameterNames.Length];
289             for (int i = 0; i < parameterNames.Length; i++) {
290                 values[i] = new CodeArgumentReferenceExpression( parameterNames[i]);
291             }
292             invokeParams[2] = new CodeArrayCreateExpression(typeof(object).FullName, values);
293         }
294 
IsOperationFlowSupported(OperationFlow flow)295         protected override bool IsOperationFlowSupported(OperationFlow flow) {
296             return flow == OperationFlow.RequestResponse;
297         }
298 
299         //
300 
GenerateMethod()301         protected override CodeMemberMethod GenerateMethod() {
302             HttpOperationBinding httpOperationBinding = (HttpOperationBinding)OperationBinding.Extensions.Find(typeof(HttpOperationBinding));
303             if (httpOperationBinding == null) throw OperationBindingSyntaxException(Res.GetString(Res.MissingHttpOperationElement0));
304 
305             HttpMethodInfo method = new HttpMethodInfo();
306 
307             if (hasInputPayload) {
308                 method.MimeParameters = ImportMimeParameters();
309                 if (method.MimeParameters == null) {
310                     UnsupportedOperationWarning(Res.GetString(Res.NoInputMIMEFormatsWereRecognized0));
311                     return null;
312                 }
313             }
314             else {
315                 method.UrlParameters = ImportUrlParameters();
316                 if (method.UrlParameters == null) {
317                     UnsupportedOperationWarning(Res.GetString(Res.NoInputHTTPFormatsWereRecognized0));
318                     return null;
319                 }
320             }
321             method.MimeReturn = ImportMimeReturn();
322             if (method.MimeReturn == null) {
323                 UnsupportedOperationWarning(Res.GetString(Res.NoOutputMIMEFormatsWereRecognized0));
324                 return null;
325             }
326             method.Name = MethodNames.AddUnique(MethodName, method);
327             method.Href = httpOperationBinding.Location;
328             return GenerateMethod(method);
329         }
330 
BeginClass()331         protected override CodeTypeDeclaration BeginClass() {
332             MethodNames.Clear();
333             ExtraCodeClasses.Clear();
334             CodeAttributeDeclarationCollection metadata = new CodeAttributeDeclarationCollection();
335             if (Style == ServiceDescriptionImportStyle.Client) {
336                 WebCodeGenerator.AddCustomAttribute(metadata, typeof(DebuggerStepThroughAttribute), new CodeExpression[0]);
337                 WebCodeGenerator.AddCustomAttribute(metadata, typeof(DesignerCategoryAttribute), new CodeExpression[] { new CodePrimitiveExpression("code") });
338             }
339 
340             Type[] requiredTypes = new Type[] {
341                 typeof(SoapDocumentMethodAttribute),
342                 typeof(XmlAttributeAttribute),
343                 typeof(WebService),
344                 typeof(Object),
345                 typeof(DebuggerStepThroughAttribute),
346                 typeof(DesignerCategoryAttribute),
347                 typeof(TransactionOption),
348             };
349             WebCodeGenerator.AddImports(this.CodeNamespace, WebCodeGenerator.GetNamespacesForTypes(requiredTypes));
350             CodeFlags flags = 0;
351             if (Style == ServiceDescriptionImportStyle.Server)
352                 flags = CodeFlags.IsAbstract;
353             else if (Style == ServiceDescriptionImportStyle.ServerInterface)
354                 flags = CodeFlags.IsInterface;
355             CodeTypeDeclaration codeClass = WebCodeGenerator.CreateClass(this.ClassName, BaseClass.FullName,
356                 new string[0], metadata, CodeFlags.IsPublic | flags,
357                 ServiceImporter.CodeGenerator.Supports(GeneratorSupport.PartialTypes));
358 
359             codeClass.Comments.Add(new CodeCommentStatement(Res.GetString(Res.CodeRemarks), true));
360 
361             CodeConstructor ctor = WebCodeGenerator.AddConstructor(codeClass, new string[0], new string[0], null, CodeFlags.IsPublic);
362             ctor.Comments.Add(new CodeCommentStatement(Res.GetString(Res.CodeRemarks), true));
363 
364             HttpAddressBinding httpAddressBinding = Port == null ? null : (HttpAddressBinding)Port.Extensions.Find(typeof(HttpAddressBinding));
365             string url = (httpAddressBinding != null) ? httpAddressBinding.Location : null;
366             ServiceDescription serviceDescription = Binding.ServiceDescription;
367             ProtocolImporterUtil.GenerateConstructorStatements(ctor, url, serviceDescription.AppSettingUrlKey, serviceDescription.AppSettingBaseUrl, false);
368 
369             codeClasses.Add(codeClass);
370             return codeClass;
371         }
372 
EndNamespace()373         protected override void EndNamespace() {
374             for (int i = 0; i < importers.Length; i++) {
375                 importers[i].GenerateCode((MimeReturn[])importedReturns[i].ToArray(typeof(MimeReturn)),
376                                           (MimeParameterCollection[])importedParameters[i].ToArray(typeof(MimeParameterCollection)));
377             }
378 
379             foreach (CodeTypeDeclaration codeClass in codeClasses) {
380                 if (codeClass.CustomAttributes == null)
381                     codeClass.CustomAttributes = new CodeAttributeDeclarationCollection();
382 
383                 for (int i = 0; i < importers.Length; i++) {
384                     importers[i].AddClassMetadata(codeClass);
385                 }
386             }
387             foreach (CodeTypeDeclaration declaration in ExtraCodeClasses) {
388                 this.CodeNamespace.Types.Add(declaration);
389             }
390             CodeGenerator.ValidateIdentifiers(CodeNamespace);
391         }
392 
393         internal abstract Type BaseClass { get; }
394     }
395 }
396