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