//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // //------------------------------------------------------------------------------ namespace System.Web.Services.Protocols { using System; using System.Web.Services; using System.Reflection; using System.Collections; using System.Security.Permissions; using System.Globalization; using System.Text; using System.Security.Cryptography; /// /// /// [To be supplied.] /// public enum LogicalMethodTypes { /// /// /// [To be supplied.] /// Sync = 0x1, /// /// /// [To be supplied.] /// Async = 0x2, } /// /// /// [To be supplied.] /// public sealed class LogicalMethodInfo { MethodInfo methodInfo; MethodInfo endMethodInfo; ParameterInfo[] inParams; ParameterInfo[] outParams; ParameterInfo[] parameters; Hashtable attributes; Type retType; ParameterInfo callbackParam; ParameterInfo stateParam; ParameterInfo resultParam; string methodName; bool isVoid; static object[] emptyObjectArray = new object[0]; WebServiceBindingAttribute binding; WebMethodAttribute attribute; MethodInfo declaration; static HashAlgorithm hash; /// /// /// [To be supplied.] /// public LogicalMethodInfo(MethodInfo methodInfo) : this (methodInfo, null) { } internal LogicalMethodInfo(MethodInfo methodInfo, WebMethod webMethod) { if (methodInfo.IsStatic) throw new InvalidOperationException(Res.GetString(Res.WebMethodStatic, methodInfo.Name)); this.methodInfo = methodInfo; if (webMethod != null) { this.binding = webMethod.binding; this.attribute = webMethod.attribute; this.declaration = webMethod.declaration; } MethodInfo methodDefinition = declaration != null ? declaration : methodInfo; parameters = methodDefinition.GetParameters(); inParams = GetInParameters(methodDefinition, parameters, 0, parameters.Length, false); outParams = GetOutParameters(methodDefinition, parameters, 0, parameters.Length, false); retType = methodDefinition.ReturnType; isVoid = retType == typeof(void); methodName = methodDefinition.Name; attributes = new Hashtable(); } LogicalMethodInfo(MethodInfo beginMethodInfo, MethodInfo endMethodInfo, WebMethod webMethod) { this.methodInfo = beginMethodInfo; this.endMethodInfo = endMethodInfo; methodName = beginMethodInfo.Name.Substring(5); if (webMethod != null) { this.binding = webMethod.binding; this.attribute = webMethod.attribute; this.declaration = webMethod.declaration; } ParameterInfo[] beginParamInfos = beginMethodInfo.GetParameters(); if (beginParamInfos.Length < 2 || beginParamInfos[beginParamInfos.Length - 1].ParameterType != typeof(object) || beginParamInfos[beginParamInfos.Length - 2].ParameterType != typeof(AsyncCallback)) { throw new InvalidOperationException(Res.GetString(Res.WebMethodMissingParams, beginMethodInfo.DeclaringType.FullName, beginMethodInfo.Name, typeof(AsyncCallback).FullName, typeof(object).FullName)); } stateParam = beginParamInfos[beginParamInfos.Length - 1]; callbackParam = beginParamInfos[beginParamInfos.Length - 2]; inParams = GetInParameters(beginMethodInfo, beginParamInfos, 0, beginParamInfos.Length - 2, true); ParameterInfo[] endParamInfos = endMethodInfo.GetParameters(); resultParam = endParamInfos[0]; outParams = GetOutParameters(endMethodInfo, endParamInfos, 1, endParamInfos.Length - 1, true); parameters = new ParameterInfo[inParams.Length + outParams.Length]; inParams.CopyTo(parameters, 0); outParams.CopyTo(parameters, inParams.Length); retType = endMethodInfo.ReturnType; isVoid = retType == typeof(void); attributes = new Hashtable(); } /// /// /// [To be supplied.] /// public override string ToString() { return methodInfo.ToString(); } // This takes in parameters, and returns return value followed by out parameters in an array /// /// /// [To be supplied.] /// [PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")] public object[] Invoke(object target, object[] values) { if (outParams.Length > 0) { object[] newValues = new object[parameters.Length]; for (int i = 0; i < inParams.Length; i++) { newValues[inParams[i].Position] = values[i]; } values = newValues; } object returnValue = methodInfo.Invoke(target, values); if (outParams.Length > 0) { int count = outParams.Length; if (!isVoid) count++; object[] results = new object[count]; count = 0; if (!isVoid) results[count++] = returnValue; for (int i = 0; i < outParams.Length; i++) { results[count++] = values[outParams[i].Position]; } return results; } else if (isVoid) { return emptyObjectArray; } else { return new object[] { returnValue }; } } /// /// /// [To be supplied.] /// [PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")] public IAsyncResult BeginInvoke(object target, object[] values, AsyncCallback callback, object asyncState) { object[] asyncValues = new object[values.Length + 2]; values.CopyTo(asyncValues, 0); asyncValues[values.Length] = callback; asyncValues[values.Length + 1] = asyncState; return (IAsyncResult)methodInfo.Invoke(target, asyncValues); } /// /// /// [To be supplied.] /// [PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")] public object[] EndInvoke(object target, IAsyncResult asyncResult) { object[] values = new object[outParams.Length + 1]; values[0] = asyncResult; object returnValue = endMethodInfo.Invoke(target, values); if (!isVoid) { values[0] = returnValue; return values; } else if (outParams.Length > 0) { object[] newValues = new object[outParams.Length]; Array.Copy(values, 1, newValues, 0, newValues.Length); return newValues; } else { return emptyObjectArray; } } internal WebServiceBindingAttribute Binding { get { return binding; } } internal MethodInfo Declaration { get { return declaration; } } /// /// /// [To be supplied.] /// public Type DeclaringType { get { return methodInfo.DeclaringType; } } /// /// /// [To be supplied.] /// public string Name { get { return methodName; } } /// /// /// [To be supplied.] /// public ParameterInfo AsyncResultParameter { get { return resultParam; } } /// /// /// [To be supplied.] /// public ParameterInfo AsyncCallbackParameter { get { return callbackParam; } } /// /// /// [To be supplied.] /// public ParameterInfo AsyncStateParameter { get { return stateParam; } } /// /// /// [To be supplied.] /// public Type ReturnType { get { return retType; } } /// /// /// [To be supplied.] /// public bool IsVoid { get { return isVoid; } } /// /// /// [To be supplied.] /// public bool IsAsync { get { return endMethodInfo != null; } } /// /// /// [To be supplied.] /// public ParameterInfo[] InParameters { get { return inParams; } } /// /// /// [To be supplied.] /// public ParameterInfo[] OutParameters { get { return outParams; } } /// /// /// [To be supplied.] /// public ParameterInfo[] Parameters { get { return parameters; } } /// /// /// [To be supplied.] /// public object[] GetCustomAttributes(Type type) { object[] attrForType = null; attrForType = (object[])attributes[type]; if (attrForType != null) return attrForType; lock (attributes) { attrForType = (object[])attributes[type]; if (attrForType == null) { if (declaration != null) { object[] declAttributes = declaration.GetCustomAttributes(type, false); object[] implAttributes = methodInfo.GetCustomAttributes(type, false); if (implAttributes.Length > 0) { if (CanMerge(type)) { ArrayList all = new ArrayList(); for (int i = 0; i < declAttributes.Length; i++) { all.Add(declAttributes[i]); } for (int i = 0; i < implAttributes.Length; i++) { all.Add(implAttributes[i]); } attrForType = (object[])all.ToArray(type); } else { throw new InvalidOperationException(Res.GetString(Res.ContractOverride, methodInfo.Name, methodInfo.DeclaringType.FullName, declaration.DeclaringType.FullName, declaration.ToString(), implAttributes[0].ToString())); } } else { attrForType = declAttributes; } } else { attrForType = methodInfo.GetCustomAttributes(type, false); } attributes[type] = attrForType; } } return attrForType; } /// /// /// [To be supplied.] /// public object GetCustomAttribute(Type type) { object[] attrs = GetCustomAttributes(type); if (attrs.Length == 0) return null; return attrs[0]; } internal WebMethodAttribute MethodAttribute { get { if (attribute == null) { attribute = (WebMethodAttribute)GetCustomAttribute(typeof(WebMethodAttribute)); if (attribute == null) { attribute = new WebMethodAttribute(); } } return attribute; } } /// /// /// [To be supplied.] /// public ICustomAttributeProvider CustomAttributeProvider { // Custom attributes are always on the XXX (sync) or BeginXXX (async) method. get { return methodInfo; } } /// /// /// [To be supplied.] /// public ICustomAttributeProvider ReturnTypeCustomAttributeProvider { get { if (declaration != null) return declaration.ReturnTypeCustomAttributes; return methodInfo.ReturnTypeCustomAttributes; } } // Do not use this to property get custom attributes. Instead use the CustomAttributeProvider // property which automatically handles where the custom attributes belong for async methods // (which are actually two methods: BeginXXX and EndXXX). /// /// /// [To be supplied.] /// public MethodInfo MethodInfo { get { return endMethodInfo == null ? methodInfo : null; } } /// /// /// [To be supplied.] /// public MethodInfo BeginMethodInfo { get { return methodInfo; } } /// /// /// [To be supplied.] /// public MethodInfo EndMethodInfo { get { return endMethodInfo; } } static ParameterInfo[] GetInParameters(MethodInfo methodInfo, ParameterInfo[] paramInfos, int start, int length, bool mustBeIn) { int count = 0; for (int i = 0; i < length; i++) { ParameterInfo paramInfo = paramInfos[i + start]; if (IsInParameter(paramInfo)) { count++; } else if (mustBeIn) { throw new InvalidOperationException(Res.GetString(Res.WebBadOutParameter, paramInfo.Name, methodInfo.DeclaringType.FullName, paramInfo.Name)); } } ParameterInfo[] ins = new ParameterInfo[count]; count = 0; for (int i = 0; i < length; i++) { ParameterInfo paramInfo = paramInfos[i + start]; if (IsInParameter(paramInfo)) { ins[count++] = paramInfo; } } return ins; } static ParameterInfo[] GetOutParameters(MethodInfo methodInfo, ParameterInfo[] paramInfos, int start, int length, bool mustBeOut) { int count = 0; for (int i = 0; i < length; i++) { ParameterInfo paramInfo = paramInfos[i + start]; if (IsOutParameter(paramInfo)) { count++; } else if (mustBeOut) { throw new InvalidOperationException(Res.GetString(Res.WebInOutParameter, paramInfo.Name, methodInfo.DeclaringType.FullName, paramInfo.Name)); } } ParameterInfo[] outs = new ParameterInfo[count]; count = 0; for (int i = 0; i < length; i++) { ParameterInfo paramInfo = paramInfos[i + start]; if (IsOutParameter(paramInfo)) { outs[count++] = paramInfo; } } return outs; } static bool IsInParameter(ParameterInfo paramInfo) { return !paramInfo.IsOut; } static bool IsOutParameter(ParameterInfo paramInfo) { return paramInfo.IsOut || paramInfo.ParameterType.IsByRef; } /// /// /// [To be supplied.] /// public static bool IsBeginMethod(MethodInfo methodInfo) { return typeof(IAsyncResult).IsAssignableFrom(methodInfo.ReturnType) && methodInfo.Name.StartsWith("Begin", StringComparison.Ordinal); } /// /// /// [To be supplied.] /// public static bool IsEndMethod(MethodInfo methodInfo) { ParameterInfo[] paramInfos = methodInfo.GetParameters(); return paramInfos.Length > 0 && typeof(IAsyncResult).IsAssignableFrom(paramInfos[0].ParameterType) && methodInfo.Name.StartsWith("End", StringComparison.Ordinal); } /// /// /// [To be supplied.] /// public static LogicalMethodInfo[] Create(MethodInfo[] methodInfos) { return Create(methodInfos, LogicalMethodTypes.Async | LogicalMethodTypes.Sync, null); } /// /// /// [To be supplied.] /// public static LogicalMethodInfo[] Create(MethodInfo[] methodInfos, LogicalMethodTypes types) { return Create(methodInfos, types, null); } internal static LogicalMethodInfo[] Create(MethodInfo[] methodInfos, LogicalMethodTypes types, Hashtable declarations) { ArrayList begins = (types & LogicalMethodTypes.Async) != 0 ? new ArrayList() : null; Hashtable ends = (types & LogicalMethodTypes.Async) != 0 ? new Hashtable() : null; ArrayList syncs = (types & LogicalMethodTypes.Sync) != 0 ? new ArrayList() : null; for (int i = 0; i < methodInfos.Length; i++) { MethodInfo methodInfo = methodInfos[i]; if (IsBeginMethod(methodInfo)) { if (begins != null) begins.Add(methodInfo); } else if (IsEndMethod(methodInfo)) { if (ends != null) ends.Add(methodInfo.Name, methodInfo); } else { if (syncs != null) syncs.Add(methodInfo); } } int beginsCount = begins == null ? 0 : begins.Count; int syncsCount = syncs == null ? 0 : syncs.Count; int count = syncsCount + beginsCount; LogicalMethodInfo[] methods = new LogicalMethodInfo[count]; count = 0; for (int i = 0; i < syncsCount; i++) { MethodInfo syncMethod = (MethodInfo)syncs[i]; WebMethod webMethod = declarations == null ? null : (WebMethod)declarations[syncMethod]; methods[count] = new LogicalMethodInfo(syncMethod, webMethod); methods[count].CheckContractOverride(); count++; } for (int i = 0; i < beginsCount; i++) { MethodInfo beginMethodInfo = (MethodInfo)begins[i]; string endName = "End" + beginMethodInfo.Name.Substring(5); MethodInfo endMethodInfo = (MethodInfo)ends[endName]; if (endMethodInfo == null) { throw new InvalidOperationException(Res.GetString(Res.WebAsyncMissingEnd, beginMethodInfo.DeclaringType.FullName, beginMethodInfo.Name, endName)); } WebMethod webMethod = declarations == null ? null : (WebMethod)declarations[beginMethodInfo]; methods[count++] = new LogicalMethodInfo(beginMethodInfo, endMethodInfo, webMethod); } return methods; } internal static HashAlgorithm HashAlgorithm { get { if (hash == null) { hash = SHA1.Create(); } return hash; } } internal string GetKey() { if (methodInfo == null) return string.Empty; string key = methodInfo.DeclaringType.FullName + ":" + methodInfo.ToString(); // for very long method signatures use a hash string instead of actual method signature. if (key.Length > 1024) { byte[] bytes = HashAlgorithm.ComputeHash(Encoding.UTF8.GetBytes(key)); key = Convert.ToBase64String(bytes); } return key; } internal void CheckContractOverride() { if (declaration == null) return; methodInfo.GetParameters(); ParameterInfo[] parameters = methodInfo.GetParameters(); foreach (ParameterInfo p in parameters) { object[] attrs = p.GetCustomAttributes(false); foreach (object o in attrs) { if (o.GetType().Namespace == "System.Xml.Serialization") { throw new InvalidOperationException(Res.GetString(Res.ContractOverride, methodInfo.Name, methodInfo.DeclaringType.FullName, declaration.DeclaringType.FullName, declaration.ToString(), o.ToString())); } } } } internal static bool CanMerge(Type type) { if (type == typeof(SoapHeaderAttribute)) return true; if (typeof(SoapExtensionAttribute).IsAssignableFrom(type)) return true; return false; } } }