1 //------------------------------------------------------------------------------ 2 // <copyright file="HttpClientProtocol.cs" company="Microsoft"> 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // </copyright> 5 //------------------------------------------------------------------------------ 6 7 namespace System.Web.Services.Protocols { 8 using System; 9 using System.Collections; 10 using System.IO; 11 using System.Reflection; 12 using System.Xml.Serialization; 13 using System.Net; 14 using System.Text; 15 using System.Threading; 16 using System.ComponentModel; 17 using System.Runtime.InteropServices; 18 using System.Diagnostics; 19 using System.Web.Services.Diagnostics; 20 21 internal class HttpClientMethod { 22 internal Type readerType; 23 internal object readerInitializer; 24 internal Type writerType; 25 internal object writerInitializer; 26 internal LogicalMethodInfo methodInfo; 27 } 28 29 internal class HttpClientType { 30 Hashtable methods = new Hashtable(); 31 HttpClientType(Type type)32 internal HttpClientType(Type type) { 33 LogicalMethodInfo[] methodInfos = LogicalMethodInfo.Create(type.GetMethods(), LogicalMethodTypes.Sync); 34 35 Hashtable formatterTypes = new Hashtable(); 36 for (int i = 0; i < methodInfos.Length; i++) { 37 LogicalMethodInfo methodInfo = methodInfos[i]; 38 try { 39 object[] attributes = methodInfo.GetCustomAttributes(typeof(HttpMethodAttribute)); 40 if (attributes.Length == 0) continue; 41 HttpMethodAttribute attribute = (HttpMethodAttribute)attributes[0]; 42 HttpClientMethod method = new HttpClientMethod(); 43 method.readerType = attribute.ReturnFormatter; 44 method.writerType = attribute.ParameterFormatter; 45 method.methodInfo = methodInfo; 46 AddFormatter(formatterTypes, method.readerType, method); 47 AddFormatter(formatterTypes, method.writerType, method); 48 methods.Add(methodInfo.Name, method); 49 } 50 catch (Exception e) { 51 if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) { 52 throw; 53 } 54 throw new InvalidOperationException(Res.GetString(Res.WebReflectionError, methodInfo.DeclaringType.FullName, methodInfo.Name), e); 55 } 56 } 57 58 foreach (Type t in formatterTypes.Keys) { 59 ArrayList list = (ArrayList)formatterTypes[t]; 60 LogicalMethodInfo[] m = new LogicalMethodInfo[list.Count]; 61 for (int j = 0; j < list.Count; j++) 62 m[j] = ((HttpClientMethod)list[j]).methodInfo; 63 object[] initializers = MimeFormatter.GetInitializers(t, m); 64 bool isWriter = typeof(MimeParameterWriter).IsAssignableFrom(t); 65 for (int j = 0; j < list.Count; j++) { 66 if (isWriter) { 67 ((HttpClientMethod)list[j]).writerInitializer = initializers[j]; 68 } 69 else { 70 ((HttpClientMethod)list[j]).readerInitializer = initializers[j]; 71 } 72 } 73 } 74 } 75 AddFormatter(Hashtable formatterTypes, Type formatterType, HttpClientMethod method)76 static void AddFormatter(Hashtable formatterTypes, Type formatterType, HttpClientMethod method) { 77 if (formatterType == null) return; 78 ArrayList list = (ArrayList)formatterTypes[formatterType]; 79 if (list == null) { 80 list = new ArrayList(); 81 formatterTypes.Add(formatterType, list); 82 } 83 list.Add(method); 84 } 85 GetMethod(string name)86 internal HttpClientMethod GetMethod(string name) { 87 return (HttpClientMethod)methods[name]; 88 } 89 } 90 91 /// <include file='doc\HttpClientProtocol.uex' path='docs/doc[@for="HttpSimpleClientProtocol"]/*' /> 92 /// <devdoc> 93 /// <para> 94 /// Specifies 95 /// most of the implementation for communicating with an HTTP web service over HTTP. 96 /// </para> 97 /// </devdoc> 98 [ComVisible(true)] 99 public abstract class HttpSimpleClientProtocol : HttpWebClientProtocol { 100 HttpClientType clientType; 101 102 /// <include file='doc\HttpClientProtocol.uex' path='docs/doc[@for="HttpSimpleClientProtocol.HttpSimpleClientProtocol"]/*' /> 103 /// <devdoc> 104 /// <para> 105 /// Initializes a new instance of the <see cref='System.Web.Services.Protocols.HttpSimpleClientProtocol'/> class. 106 /// </para> 107 /// </devdoc> HttpSimpleClientProtocol()108 protected HttpSimpleClientProtocol() 109 : base() { 110 Type type = this.GetType(); 111 clientType = (HttpClientType)GetFromCache(type); 112 if (clientType == null) { 113 lock (InternalSyncObject) { 114 clientType = (HttpClientType)GetFromCache(type); 115 if (clientType == null) { 116 clientType = new HttpClientType(type); 117 AddToCache(type, clientType); 118 } 119 } 120 } 121 } 122 123 /// <include file='doc\HttpClientProtocol.uex' path='docs/doc[@for="HttpSimpleClientProtocol.Invoke"]/*' /> 124 /// <devdoc> 125 /// <para> 126 /// Invokes a method of a HTTP web service. 127 /// </para> 128 /// </devdoc> Invoke(string methodName, string requestUrl, object[] parameters)129 protected object Invoke(string methodName, string requestUrl, object[] parameters) { 130 WebResponse response = null; 131 HttpClientMethod method = GetClientMethod(methodName); 132 MimeParameterWriter paramWriter = GetParameterWriter(method); 133 Uri requestUri = new Uri(requestUrl); 134 if (paramWriter != null) { 135 paramWriter.RequestEncoding = RequestEncoding; 136 requestUrl = paramWriter.GetRequestUrl(requestUri.AbsoluteUri, parameters); 137 requestUri = new Uri(requestUrl, true); 138 } 139 WebRequest request = null; 140 try { 141 request = GetWebRequest(requestUri); 142 NotifyClientCallOut(request); 143 PendingSyncRequest = request; 144 if (paramWriter != null) { 145 paramWriter.InitializeRequest(request, parameters); 146 // 147 148 149 if (paramWriter.UsesWriteRequest) { 150 if (parameters.Length == 0) 151 request.ContentLength = 0; 152 else { 153 Stream requestStream = null; 154 try { 155 requestStream = request.GetRequestStream(); 156 paramWriter.WriteRequest(requestStream, parameters); 157 } 158 finally { 159 if (requestStream != null) requestStream.Close(); 160 } 161 } 162 } 163 } 164 response = GetWebResponse(request); 165 Stream responseStream = null; 166 if (response.ContentLength != 0) 167 responseStream = response.GetResponseStream(); 168 169 return ReadResponse(method, response, responseStream); 170 } 171 finally { 172 if (request == PendingSyncRequest) 173 PendingSyncRequest = null; 174 } 175 } 176 177 178 /// <include file='doc\HttpClientProtocol.uex' path='docs/doc[@for="HttpSimpleClientProtocol.BeginInvoke"]/*' /> 179 /// <devdoc> 180 /// <para> 181 /// Starts an asynchronous invocation of a method of a HTTP web service. 182 /// </para> 183 /// </devdoc> BeginInvoke(string methodName, string requestUrl, object[] parameters, AsyncCallback callback, object asyncState)184 protected IAsyncResult BeginInvoke(string methodName, string requestUrl, object[] parameters, AsyncCallback callback, object asyncState) { 185 HttpClientMethod method = GetClientMethod(methodName); 186 MimeParameterWriter paramWriter = GetParameterWriter(method); 187 Uri requestUri = new Uri(requestUrl); 188 if (paramWriter != null) { 189 paramWriter.RequestEncoding = RequestEncoding; 190 requestUrl = paramWriter.GetRequestUrl(requestUri.AbsoluteUri, parameters); 191 requestUri = new Uri(requestUrl, true); 192 } 193 InvokeAsyncState invokeState = new InvokeAsyncState(method, paramWriter, parameters); 194 WebClientAsyncResult asyncResult = new WebClientAsyncResult(this, invokeState, null, callback, asyncState); 195 return BeginSend(requestUri, asyncResult, paramWriter.UsesWriteRequest); 196 } 197 198 199 /// <include file='doc\HttpClientProtocol.uex' path='docs/doc[@for="HttpSimpleClientProtocol.InitializeAsyncRequest"]/*' /> 200 /// <devdoc> 201 /// <para>[To be supplied.]</para> 202 /// </devdoc> InitializeAsyncRequest(WebRequest request, object internalAsyncState)203 internal override void InitializeAsyncRequest(WebRequest request, object internalAsyncState) { 204 InvokeAsyncState invokeState = (InvokeAsyncState)internalAsyncState; 205 if (invokeState.ParamWriter.UsesWriteRequest && invokeState.Parameters.Length == 0) 206 request.ContentLength = 0; 207 } 208 AsyncBufferedSerialize(WebRequest request, Stream requestStream, object internalAsyncState)209 internal override void AsyncBufferedSerialize(WebRequest request, Stream requestStream, object internalAsyncState) { 210 InvokeAsyncState invokeState = (InvokeAsyncState)internalAsyncState; 211 if (invokeState.ParamWriter != null) { 212 invokeState.ParamWriter.InitializeRequest(request, invokeState.Parameters); 213 if (invokeState.ParamWriter.UsesWriteRequest && invokeState.Parameters.Length > 0) 214 invokeState.ParamWriter.WriteRequest(requestStream, invokeState.Parameters); 215 } 216 } 217 218 class InvokeAsyncState { 219 internal object[] Parameters; 220 internal MimeParameterWriter ParamWriter; 221 internal HttpClientMethod Method; 222 InvokeAsyncState(HttpClientMethod method, MimeParameterWriter paramWriter, object[] parameters)223 internal InvokeAsyncState(HttpClientMethod method, MimeParameterWriter paramWriter, object[] parameters) { 224 this.Method = method; 225 this.ParamWriter = paramWriter; 226 this.Parameters = parameters; 227 } 228 } 229 230 /// <include file='doc\HttpClientProtocol.uex' path='docs/doc[@for="HttpSimpleClientProtocol.EndInvoke"]/*' /> 231 /// <devdoc> 232 /// <para> 233 /// Ends an asynchronous invocation of a method of a HTTP web service. 234 /// </para> 235 /// </devdoc> EndInvoke(IAsyncResult asyncResult)236 protected object EndInvoke(IAsyncResult asyncResult) { 237 object o = null; 238 Stream responseStream = null; 239 WebResponse response = EndSend(asyncResult, ref o, ref responseStream); 240 InvokeAsyncState invokeState = (InvokeAsyncState) o; 241 return ReadResponse(invokeState.Method, response, responseStream); 242 } 243 InvokeAsyncCallback(IAsyncResult result)244 private void InvokeAsyncCallback(IAsyncResult result) { 245 object parameter = null; 246 Exception exception = null; 247 WebClientAsyncResult asyncResult = (WebClientAsyncResult)result; 248 if (asyncResult.Request != null) { 249 try { 250 object o = null; 251 Stream responseStream = null; 252 WebResponse response = EndSend(asyncResult, ref o, ref responseStream); 253 InvokeAsyncState invokeState = (InvokeAsyncState) o; 254 parameter = ReadResponse(invokeState.Method, response, responseStream); 255 } 256 catch (Exception e) { 257 if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) 258 throw; 259 exception = e; 260 if (Tracing.On) Tracing.ExceptionCatch(TraceEventType.Error, this, "InvokeAsyncCallback", e); 261 } 262 } 263 AsyncOperation asyncOp = (AsyncOperation)result.AsyncState; 264 UserToken token = (UserToken)asyncOp.UserSuppliedState; 265 OperationCompleted(token.UserState, new object[] { parameter }, exception, false); 266 } 267 /// <include file='doc\HttpClientProtocol.uex' path='docs/doc[@for="HttpSimpleClientProtocol.InvokeAsync"]/*' /> 268 /// <devdoc> 269 /// <para>[To be supplied.]</para> 270 /// </devdoc> InvokeAsync(string methodName, string requestUrl, object[] parameters, SendOrPostCallback callback)271 protected void InvokeAsync(string methodName, string requestUrl, object[] parameters, SendOrPostCallback callback) { 272 InvokeAsync(methodName, requestUrl, parameters, callback, null); 273 } 274 275 /// <include file='doc\HttpClientProtocol.uex' path='docs/doc[@for="HttpSimpleClientProtocol.InvokeAsync1"]/*' /> 276 /// <devdoc> 277 /// <para>[To be supplied.]</para> 278 /// </devdoc> InvokeAsync(string methodName, string requestUrl, object[] parameters, SendOrPostCallback callback, object userState)279 protected void InvokeAsync(string methodName, string requestUrl, object[] parameters, SendOrPostCallback callback, object userState) { 280 if (userState == null) 281 userState = NullToken; 282 AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(new UserToken(callback, userState)); 283 WebClientAsyncResult asyncResult = new WebClientAsyncResult(this, null, null, new AsyncCallback(InvokeAsyncCallback), asyncOp); 284 try { 285 AsyncInvokes.Add(userState, asyncResult); 286 } 287 catch (Exception e) { 288 if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) 289 throw; 290 if (Tracing.On) Tracing.ExceptionCatch(TraceEventType.Error, this, "InvokeAsync", e); 291 Exception exception = new ArgumentException(Res.GetString(Res.AsyncDuplicateUserState), e); 292 InvokeCompletedEventArgs eventArgs = new InvokeCompletedEventArgs(new object[] { null }, exception, false, userState); 293 asyncOp.PostOperationCompleted(callback, eventArgs); 294 return; 295 } 296 try { 297 HttpClientMethod method = GetClientMethod(methodName); 298 MimeParameterWriter paramWriter = GetParameterWriter(method); 299 Uri requestUri = new Uri(requestUrl); 300 if (paramWriter != null) { 301 paramWriter.RequestEncoding = RequestEncoding; 302 requestUrl = paramWriter.GetRequestUrl(requestUri.AbsoluteUri, parameters); 303 requestUri = new Uri(requestUrl, true); 304 } 305 asyncResult.InternalAsyncState = new InvokeAsyncState(method, paramWriter, parameters); 306 BeginSend(requestUri, asyncResult, paramWriter.UsesWriteRequest); 307 } 308 catch (Exception e) { 309 if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) 310 throw; 311 if (Tracing.On) Tracing.ExceptionCatch(TraceEventType.Error, this, "InvokeAsync", e); 312 OperationCompleted(userState, new object[] { null }, e, false); 313 } 314 } 315 GetParameterWriter(HttpClientMethod method)316 MimeParameterWriter GetParameterWriter(HttpClientMethod method) { 317 if (method.writerType == null) 318 return null; 319 return (MimeParameterWriter)MimeFormatter.CreateInstance(method.writerType, method.writerInitializer); 320 } 321 GetClientMethod(string methodName)322 HttpClientMethod GetClientMethod(string methodName) { 323 HttpClientMethod method = clientType.GetMethod(methodName); 324 if (method == null) throw new ArgumentException(Res.GetString(Res.WebInvalidMethodName, methodName), "methodName"); 325 return method; 326 } 327 ReadResponse(HttpClientMethod method, WebResponse response, Stream responseStream)328 object ReadResponse(HttpClientMethod method, WebResponse response, Stream responseStream) { 329 HttpWebResponse httpResponse = response as HttpWebResponse; 330 if (httpResponse != null && (int)httpResponse.StatusCode >= 300) 331 throw new WebException(RequestResponseUtils.CreateResponseExceptionString(httpResponse, responseStream), null, 332 WebExceptionStatus.ProtocolError, httpResponse); 333 334 if (method.readerType == null) 335 return null; 336 337 // 338 339 340 if (responseStream != null) { 341 MimeReturnReader reader = (MimeReturnReader)MimeFormatter.CreateInstance(method.readerType, method.readerInitializer); 342 return reader.Read(response, responseStream); 343 } 344 else 345 return null; 346 } 347 } 348 } 349