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