Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Web.Services / System / Web / Services / Protocols / ClientProtocol.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="ClientProtocol.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.Diagnostics;
11     using System.ComponentModel;
12     using System.IO;
13     using System.Reflection;
14     using System.Xml.Serialization;
15     using System.Net;
16     using System.Net.Cache;
17     using System.Threading;
18     using System.Text;
19     using System.Security.Cryptography.X509Certificates;
20     using System.Security.Permissions;
21     using System.Runtime.InteropServices;
22     using System.Web.Services.Diagnostics;
23
24     internal class ClientTypeCache {
25         Hashtable cache = new Hashtable();
26
27         internal object this[Type key] {
28             get { return cache[key]; }
29         }
30
31         internal void Add(Type key, object value) {
32             lock (this) {
33                 if (cache[key] == value) return;
34                 Hashtable clone = new Hashtable();
35                 foreach (object k in cache.Keys) {
36                     clone.Add(k, cache[k]);
37                 }
38                 cache = clone;
39                 cache[key] = value;
40             }
41         }
42     }
43
44     /// <include file='doc\ClientProtocol.uex' path='docs/doc[@for="WebClientProtocol"]/*' />
45     /// <devdoc>
46     ///    <para>
47     ///       Specifies the base class for all web service protocol client proxies that
48     ///       use the HTTP protocol.
49     ///    </para>
50     /// </devdoc>
51     [ComVisible(true)]
52     public abstract class WebClientProtocol : Component {
53         static AsyncCallback getRequestStreamAsyncCallback;
54         static AsyncCallback getResponseAsyncCallback;
55
56         // Double-checked locking pattern requires volatile for read/write synchronization
57         static volatile AsyncCallback readResponseAsyncCallback;
58         private static ClientTypeCache cache;
59         private static RequestCachePolicy bypassCache;
60         private ICredentials credentials;
61         private bool preAuthenticate;
62         private Uri uri;
63         private int timeout;
64         private string connectionGroupName;
65         private Encoding requestEncoding;
66 #if !MONO
67         private RemoteDebugger debugger;
68 #endif
69         private WebRequest pendingSyncRequest;
70         object nullToken = new object();
71         Hashtable asyncInvokes = Hashtable.Synchronized(new Hashtable());
72
73         private static Object s_InternalSyncObject;
74         internal static Object InternalSyncObject {
75             get {
76                 if (s_InternalSyncObject == null) {
77                     Object o = new Object();
78                     Interlocked.CompareExchange(ref s_InternalSyncObject, o, null);
79                 }
80                 return s_InternalSyncObject;
81             }
82         }
83
84         static WebClientProtocol() {
85             cache = new ClientTypeCache();
86         }
87
88         /// <include file='doc\ClientProtocol.uex' path='docs/doc[@for="WebClientProtocol.WebClientProtocol"]/*' />
89         /// <devdoc>
90         ///    <para>[To be supplied.]</para>
91         /// </devdoc>
92         protected WebClientProtocol() {
93             this.timeout = 100000; // should be kept in sync with HttpWebRequest.Timeout default (see private WebRequest.DefaultTimeout)
94         }
95
96         internal WebClientProtocol(WebClientProtocol protocol) {
97             this.credentials = protocol.credentials;
98             this.uri = protocol.uri;
99             this.timeout = protocol.timeout;
100             this.connectionGroupName = protocol.connectionGroupName;
101             this.requestEncoding = protocol.requestEncoding;
102         }
103
104         internal static RequestCachePolicy BypassCache {
105             get {
106                 if (bypassCache == null) {
107                     bypassCache = new RequestCachePolicy(RequestCacheLevel.BypassCache);
108                 }
109                 return bypassCache;
110             }
111         }
112
113         /// <include file='doc\ClientProtocol.uex' path='docs/doc[@for="WebClientProtocol.Credentials"]/*' />
114         /// <devdoc>
115         ///    <para>[To be supplied.]</para>
116         /// </devdoc>
117         public ICredentials Credentials {
118             get {
119                 return credentials;
120             }
121             set {
122                 credentials = value;
123             }
124         }
125
126         /// <include file='doc\ClientProtocol.uex' path='docs/doc[@for="WebClientProtocol.UseDefaultCredentials"]/*' />
127         /// <devdoc>
128         ///    <para>Sets Credentials to CredentialCache.DefaultCredentials</para>
129         /// </devdoc>
130         public bool UseDefaultCredentials {
131             get {
132                 return (credentials == CredentialCache.DefaultCredentials) ? true : false;
133             }
134             set {
135                 credentials = value ? CredentialCache.DefaultCredentials : null;
136             }
137         }
138
139         /// <include file='doc\ClientProtocol.uex' path='docs/doc[@for="WebClientProtocol.ConnectionGroupName"]/*' />
140         /// <devdoc>
141         ///    <para>
142         ///       Gets or sets a value indicating the name of the connection group to use when making a request.
143         ///    </para>
144         /// </devdoc>
145         [DefaultValue("")]
146         public string ConnectionGroupName {
147             get { return (connectionGroupName == null) ? string.Empty : connectionGroupName; }
148             set { connectionGroupName = value; }
149         }
150
151         internal WebRequest PendingSyncRequest {
152             get { return pendingSyncRequest; }
153             set { pendingSyncRequest = value; }
154         }
155
156         /// <include file='doc\ClientProtocol.uex' path='docs/doc[@for="WebClientProtocol.PreAuthenticate"]/*' />
157         /// <devdoc>
158         ///    <para>
159         ///       Gets or sets a value indicating whether pre-authentication is enabled.
160         ///    </para>
161         /// </devdoc>
162         [DefaultValue(false), WebServicesDescription(Res.ClientProtocolPreAuthenticate)]
163         public bool PreAuthenticate {
164             get { return preAuthenticate; }
165             set { preAuthenticate = value; }
166         }
167
168         /// <include file='doc\ClientProtocol.uex' path='docs/doc[@for="WebClientProtocol.Url"]/*' />
169         /// <devdoc>
170         ///    <para>
171         ///       Gets or sets the base Uri to the server to use for requests.
172         ///    </para>
173         /// </devdoc>
174         [DefaultValue(""), SettingsBindable(true), WebServicesDescription(Res.ClientProtocolUrl)]
175         public string Url {
176             get { return uri == null ? string.Empty : uri.ToString(); }
177             set { uri = new Uri(value); }
178         }
179
180         internal Hashtable AsyncInvokes {
181             get { return asyncInvokes; }
182         }
183
184         internal object NullToken {
185             get { return nullToken; }
186         }
187
188         internal Uri Uri {
189             get { return uri; }
190             set { uri = value; }
191         }
192
193         /// <include file='doc\ClientProtocol.uex' path='docs/doc[@for="WebClientProtocol.RequestEncoding"]/*' />
194         /// <devdoc>
195         ///    <para>
196         ///       Gets or sets the encoding used for making the request.
197         ///    </para>
198         /// </devdoc>
199         [DefaultValue(null), SettingsBindable(true), WebServicesDescription(Res.ClientProtocolEncoding)]
200         public Encoding RequestEncoding {
201             get { return requestEncoding; }
202             set { requestEncoding = value; }
203         }
204
205         /// <include file='doc\ClientProtocol.uex' path='docs/doc[@for="WebClientProtocol.Timeout"]/*' />
206         /// <devdoc>
207         ///    <para>
208         ///       Gets or sets the timeout (in milliseconds) used for synchronous calls.
209         ///    </para>
210         /// </devdoc>
211         [DefaultValue(100000), SettingsBindable(true), WebServicesDescription(Res.ClientProtocolTimeout)]
212         public int Timeout {
213             get { return timeout; }
214             set { timeout = (value < Threading.Timeout.Infinite) ? Threading.Timeout.Infinite : value; }
215         }
216
217         /// <include file='doc\ClientProtocol.uex' path='docs/doc[@for="WebClientProtocol.Abort"]/*' />
218         public virtual void Abort() {
219             WebRequest request = PendingSyncRequest;
220             if (request != null)
221                 request.Abort();
222         }
223
224         /// <devdoc>
225         ///    <para>
226         ///     Starts async request processing including async retrieval of the request stream and response.
227         ///     Derived classes can use BeginSend
228         ///     to help implement their own higher level async methods like BeginInvoke. Derived
229         ///     classes can add custom behavior by overriding GetWebRequest, GetWebResponse,
230         ///     InitializeAsyncRequest and WriteAsyncRequest methods.
231         ///    </para>
232         /// </devdoc>
233         internal IAsyncResult BeginSend(Uri requestUri, WebClientAsyncResult asyncResult, bool callWriteAsyncRequest) {
234             if (readResponseAsyncCallback == null) {
235                 lock (InternalSyncObject) {
236                     if (readResponseAsyncCallback == null) {
237                         getRequestStreamAsyncCallback = new AsyncCallback(GetRequestStreamAsyncCallback);
238                         getResponseAsyncCallback = new AsyncCallback(GetResponseAsyncCallback);
239                         readResponseAsyncCallback = new AsyncCallback(ReadResponseAsyncCallback);
240                     }
241                 }
242             }
243             Debug.Assert(asyncResult.Request == null, "calling GetWebRequest twice for the same WebClientAsyncResult");
244             WebRequest request = GetWebRequest(requestUri);
245             asyncResult.Request = request;
246             InitializeAsyncRequest(request, asyncResult.InternalAsyncState);
247             if (callWriteAsyncRequest)
248                 request.BeginGetRequestStream(getRequestStreamAsyncCallback, asyncResult);
249             else
250                 request.BeginGetResponse(getResponseAsyncCallback, asyncResult);
251
252             if (!asyncResult.IsCompleted)
253                 asyncResult.CombineCompletedSynchronously(false);
254             return asyncResult;
255         }
256
257         static private void ProcessAsyncException(WebClientAsyncResult client, Exception e, string method) {
258             if (Tracing.On) Tracing.ExceptionCatch(TraceEventType.Error, typeof(WebClientProtocol), method, e);
259             WebException webException = e as WebException;
260             if (webException != null && webException.Response != null) {
261                 client.Response = webException.Response;
262             }
263             else {
264                 // If we've already completed the call then the exception must have come
265                 // out of the user callback in which case we need to rethrow it here
266                 // so that it bubbles up to the AppDomain unhandled exception event.
267                 if (client.IsCompleted)
268                     throw new InvalidOperationException(Res.GetString(Res.ThereWasAnErrorDuringAsyncProcessing), e);
269                 else
270                     client.Complete(e);
271             }
272         }
273
274         static private void GetRequestStreamAsyncCallback(IAsyncResult asyncResult) {
275             WebClientAsyncResult client = (WebClientAsyncResult)asyncResult.AsyncState;
276             client.CombineCompletedSynchronously(asyncResult.CompletedSynchronously);
277             bool processingRequest = true;
278             try {
279                 Stream requestStream = client.Request.EndGetRequestStream(asyncResult);
280                 processingRequest = false;
281                 try {
282                     client.ClientProtocol.AsyncBufferedSerialize(client.Request, requestStream, client.InternalAsyncState);
283                 }
284                 finally {
285                     requestStream.Close();
286                 }
287                 client.Request.BeginGetResponse(getResponseAsyncCallback, client);
288             }
289             catch (Exception e) {
290                 if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) {
291                     throw;
292                 }
293                 ProcessAsyncException(client, e, "GetRequestStreamAsyncCallback");
294                 if (processingRequest)
295                 {
296                     WebException we = e as WebException;
297                     if (we != null && we.Response != null)
298                     {
299                         // ProcessAsyncExcption doesn't call client.Complete() if there's a response,
300                         // because it expects us to read the response. However, in certain cases
301                         // (e.g. 502 errors), the exception thrown from Request can have a response.
302                         // We don't process it, so call Complete() now.
303                         client.Complete(e);
304                     }
305                 }
306             }
307         }
308
309         static private void GetResponseAsyncCallback(IAsyncResult asyncResult) {
310             WebClientAsyncResult client = (WebClientAsyncResult)asyncResult.AsyncState;
311             client.CombineCompletedSynchronously(asyncResult.CompletedSynchronously);
312             try {
313                 client.Response = client.ClientProtocol.GetWebResponse(client.Request, asyncResult);
314             }
315             catch (Exception e) {
316                 if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) {
317                     throw;
318                 }
319                 ProcessAsyncException(client, e, "GetResponseAsyncCallback");
320                 if (client.Response == null)
321                     return;
322             }
323
324             ReadAsyncResponse(client);
325         }
326
327         static private void ReadAsyncResponse(WebClientAsyncResult client) {
328             if (client.Response.ContentLength == 0) {
329                 client.Complete();
330                 return;
331             }
332             try {
333                 client.ResponseStream = client.Response.GetResponseStream();
334                 ReadAsyncResponseStream(client);
335             }
336             catch (Exception e) {
337                 if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) {
338                     throw;
339                 }
340                 ProcessAsyncException(client, e, "ReadAsyncResponse");
341             }
342         }
343
344         static private void ReadAsyncResponseStream(WebClientAsyncResult client) {
345             IAsyncResult asyncResult;
346             do {
347                 byte[] buffer = client.Buffer;
348                 long contentLength = client.Response.ContentLength;
349                 if (buffer == null)
350                     buffer = client.Buffer = new byte[(contentLength == -1) ? 1024 : contentLength];
351                 else if (contentLength != -1 && contentLength > buffer.Length)
352                     buffer = client.Buffer = new byte[contentLength];
353                 asyncResult = client.ResponseStream.BeginRead(buffer, 0, buffer.Length, readResponseAsyncCallback, client);
354                 if (!asyncResult.CompletedSynchronously)
355                     return;
356             }
357             while (!ProcessAsyncResponseStreamResult(client, asyncResult));
358         }
359
360         static private bool ProcessAsyncResponseStreamResult(WebClientAsyncResult client, IAsyncResult asyncResult) {
361             bool complete;
362             int bytesRead = client.ResponseStream.EndRead(asyncResult);
363             long contentLength = client.Response.ContentLength;
364             if (contentLength > 0 && bytesRead == contentLength) {
365                 // the non-chunked response finished in a single read
366                 client.ResponseBufferedStream = new MemoryStream(client.Buffer);
367                 complete = true;
368             }
369             else if (bytesRead > 0) {
370                 if (client.ResponseBufferedStream == null) {
371                     int capacity = (int)((contentLength == -1) ? client.Buffer.Length : contentLength);
372                     client.ResponseBufferedStream = new MemoryStream(capacity);
373                 }
374                 client.ResponseBufferedStream.Write(client.Buffer, 0, bytesRead);
375                 complete = false;
376             }
377             else
378                 complete = true;
379
380             if (complete)
381                 client.Complete();
382             return complete;
383         }
384
385         static private void ReadResponseAsyncCallback(IAsyncResult asyncResult) {
386             WebClientAsyncResult client = (WebClientAsyncResult)asyncResult.AsyncState;
387             client.CombineCompletedSynchronously(asyncResult.CompletedSynchronously);
388             if (asyncResult.CompletedSynchronously)
389                 return;
390             try {
391                 bool complete = ProcessAsyncResponseStreamResult(client, asyncResult);
392                 if (!complete)
393                     ReadAsyncResponseStream(client);
394             }
395             catch (Exception e) {
396                 if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) {
397                     throw;
398                 }
399                 ProcessAsyncException(client, e, "ReadResponseAsyncCallback");
400             }
401         }
402
403         internal void NotifyClientCallOut(WebRequest request) {
404 #if !MONO
405             if (RemoteDebugger.IsClientCallOutEnabled()) {
406                 debugger = new RemoteDebugger();
407                 debugger.NotifyClientCallOut(request);
408             }
409             else {
410                 debugger = null;
411             }
412 #endif
413         }
414
415         /// <include file='doc\ClientProtocol.uex' path='docs/doc[@for="WebClientProtocol.GetWebRequest"]/*' />
416         /// <devdoc>
417         ///    <para>
418         ///     Creates a new <see cref='System.Net.WebRequest'/> instance for the given url. The base implementation creates a new
419         ///     instance using the WebRequest.Create() and then sets request related properties from
420         ///     the WebClientProtocol instance. Derived classes can override this method if additional
421         ///     properties need to be set on the web request instance.
422         ///    </para>
423         /// </devdoc>
424         protected virtual WebRequest GetWebRequest(Uri uri) {
425             if (uri == null)
426                 throw new InvalidOperationException(Res.GetString(Res.WebMissingPath));
427             WebRequest request = (WebRequest)WebRequest.Create(uri);
428             PendingSyncRequest = request;
429             request.Timeout = this.timeout;
430             request.ConnectionGroupName = connectionGroupName;
431             request.Credentials = Credentials;
432             request.PreAuthenticate = PreAuthenticate;
433             request.CachePolicy = BypassCache;
434             return request;
435         }
436
437         /// <include file='doc\ClientProtocol.uex' path='docs/doc[@for="WebClientProtocol.GetWebResponse"]/*' />
438         /// <devdoc>
439         ///    <para>
440         ///     Gets the <see cref='System.Net.WebResponse'/> from the given request by calling
441         ///     GetResponse(). Derived classes can override this method to do additional
442         ///     processing on the response instance.
443         ///    </para>
444         /// </devdoc>
445         protected virtual WebResponse GetWebResponse(WebRequest request) {
446             TraceMethod caller = Tracing.On ? new TraceMethod(this, "GetWebResponse") : null;
447             WebResponse response = null;
448             try {
449                 if (Tracing.On) Tracing.Enter("WebRequest.GetResponse", caller, new TraceMethod(request, "GetResponse"));
450                 response = request.GetResponse();
451                 if (Tracing.On) Tracing.Exit("WebRequest.GetResponse", caller);
452             }
453             catch (WebException e) {
454                 if (e.Response == null)
455                     throw e;
456                 else {
457                     if (Tracing.On) Tracing.ExceptionCatch(TraceEventType.Error, this, "GetWebResponse", e);
458                     response = e.Response;
459                 }
460             }
461             finally {
462 #if !MONO
463                 if (debugger != null)
464                     debugger.NotifyClientCallReturn(response);
465 #endif
466             }
467             return response;
468         }
469
470         /// <include file='doc\ClientProtocol.uex' path='docs/doc[@for="WebClientProtocol.GetWebResponse1"]/*' />
471         /// <devdoc>
472         ///    <para>
473         ///     Gets the <see cref='System.Net.WebResponse'/> from the given request by calling
474         ///     EndGetResponse(). Derived classes can override this method to do additional
475         ///     processing on the response instance. This method is only called during
476         ///     async request processing.
477         ///    </para>
478         /// </devdoc>
479         protected virtual WebResponse GetWebResponse(WebRequest request, IAsyncResult result) {
480             WebResponse response = request.EndGetResponse(result);
481 #if !MONO
482             if (response != null && debugger != null)
483                 debugger.NotifyClientCallReturn(response);
484 #endif
485             return response;
486         }
487
488         /// <devdoc>
489         ///    <para>
490         ///     Called during async request processing to give the derived class an opportunity
491         ///     to modify the web request instance before the request stream is retrieved at which
492         ///     point the request headers are sent and can no longer be modified. The base implementation
493         ///     does nothing.
494         ///    </para>
495         /// </devdoc>
496         [PermissionSet(SecurityAction.InheritanceDemand, Name = "FullTrust")]
497         internal virtual void InitializeAsyncRequest(WebRequest request, object internalAsyncState) {
498             return;
499         }
500
501         [PermissionSet(SecurityAction.InheritanceDemand, Name = "FullTrust")]
502         internal virtual void AsyncBufferedSerialize(WebRequest request, Stream requestStream, object internalAsyncState) {
503             throw new NotSupportedException(Res.GetString(Res.ProtocolDoesNotAsyncSerialize));
504         }
505
506         internal WebResponse EndSend(IAsyncResult asyncResult, ref object internalAsyncState, ref Stream responseStream) {
507             if (asyncResult == null) throw new ArgumentNullException(Res.GetString(Res.WebNullAsyncResultInEnd));
508
509             WebClientAsyncResult client = (WebClientAsyncResult)asyncResult;
510             if (client.EndSendCalled)
511                 throw new InvalidOperationException(Res.GetString(Res.CanTCallTheEndMethodOfAnAsyncCallMoreThan));
512             client.EndSendCalled = true;
513             WebResponse response = client.WaitForResponse();
514             internalAsyncState = client.InternalAsyncState;
515             responseStream = client.ResponseBufferedStream;
516             return response;
517         }
518
519         /// <include file='doc\ClientProtocol.uex' path='docs/doc[@for="WebClientProtocol.GetFromCache"]/*' />
520         /// <devdoc>
521         /// Returns an instance of a client protocol handler from the cache.
522         /// </devdoc>
523         protected static object GetFromCache(Type type) {
524             return cache[type];
525         }
526
527         /// <include file='doc\ClientProtocol.uex' path='docs/doc[@for="WebClientProtocol.AddToCache"]/*' />
528         /// <devdoc>
529         ///    <para>
530         ///       Add an instance of the client protocol handler to the cache.
531         ///    </para>
532         /// </devdoc>
533         protected static void AddToCache(Type type, object value) {
534             cache.Add(type, value);
535         }
536
537     }
538
539     /// <include file='doc\ClientProtocol.uex' path='docs/doc[@for="WebClientAsyncResult"]/*' />
540     [PermissionSet(SecurityAction.InheritanceDemand, Name = "FullTrust")]
541     public class WebClientAsyncResult : IAsyncResult {
542         private object userAsyncState;
543         private bool completedSynchronously;
544         private bool isCompleted;
545
546         // Double-checked locking pattern requires volatile for read/write synchronization
547         private volatile ManualResetEvent manualResetEvent;
548         private AsyncCallback userCallback;
549
550         internal WebClientProtocol ClientProtocol;
551         internal object InternalAsyncState;
552         internal Exception Exception;
553         internal WebResponse Response;
554         internal WebRequest Request;
555         internal Stream ResponseStream;
556         internal Stream ResponseBufferedStream;
557         internal byte[] Buffer;
558         internal bool EndSendCalled;
559
560         internal WebClientAsyncResult(WebClientProtocol clientProtocol,
561             object internalAsyncState,
562             WebRequest request,
563             AsyncCallback userCallback,
564             object userAsyncState) 
565         {
566             this.ClientProtocol = clientProtocol;
567             this.InternalAsyncState = internalAsyncState;
568             this.userAsyncState = userAsyncState;
569             this.userCallback = userCallback;
570             this.Request = request;
571             this.completedSynchronously = true;
572         }
573
574         /// <include file='doc\ClientProtocol.uex' path='docs/doc[@for="WebClientAsyncResult.AsyncState"]/*' />
575         public object AsyncState { get { return userAsyncState; } }
576
577         /// <include file='doc\ClientProtocol.uex' path='docs/doc[@for="WebClientAsyncResult.AsyncWaitHandle"]/*' />
578         public WaitHandle AsyncWaitHandle {
579             get {
580                 bool savedIsCompleted = isCompleted;
581                 if (manualResetEvent == null) {
582                     lock (this) {
583                         if (manualResetEvent == null)
584                             manualResetEvent = new ManualResetEvent(savedIsCompleted);
585                     }
586                 }
587                 if (!savedIsCompleted && isCompleted)
588                     manualResetEvent.Set();
589                 return (WaitHandle)manualResetEvent;
590             }
591         }
592
593         /// <include file='doc\ClientProtocol.uex' path='docs/doc[@for="WebClientAsyncResult.CompletedSynchronously"]/*' />
594         public bool CompletedSynchronously {
595             get { return completedSynchronously; }
596         }
597
598         /// <include file='doc\ClientProtocol.uex' path='docs/doc[@for="WebClientAsyncResult.IsCompleted"]/*' />
599         public bool IsCompleted { get { return isCompleted; } }
600
601         /// <include file='doc\ClientProtocol.uex' path='docs/doc[@for="WebClientAsyncResult.Abort"]/*' />
602         public void Abort() {
603             WebRequest req = Request;
604             if (req != null)
605                 req.Abort();
606         }
607
608
609         internal void Complete() {
610             Debug.Assert(!isCompleted, "Complete called more than once.");
611
612             try {
613                 if (ResponseStream != null) {
614                     ResponseStream.Close();
615                     ResponseStream = null;
616                 }
617
618                 if (ResponseBufferedStream != null)
619                     ResponseBufferedStream.Position = 0;
620             }
621             catch (Exception e) {
622                 if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) {
623                     throw;
624                 }
625                 if (this.Exception == null)
626                     this.Exception = e;
627                 if (Tracing.On) Tracing.ExceptionCatch(TraceEventType.Error, this, "Complete", e);
628             }
629
630             isCompleted = true;
631
632             try {
633                 if (manualResetEvent != null)
634                     manualResetEvent.Set();
635             }
636             catch (Exception e) {
637                 if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) {
638                     throw;
639                 }
640                 if (this.Exception == null)
641                     this.Exception = e;
642                 if (Tracing.On) Tracing.ExceptionCatch(TraceEventType.Error, this, "Complete", e);
643             }
644
645             // We want to let exceptions in user callback to bubble up to
646             // threadpool so that AppDomain.UnhandledExceptionEventHandler
647             // will get it if one is registered
648             if (userCallback != null)
649                 userCallback(this);
650         }
651
652         internal void Complete(Exception e) {
653             this.Exception = e;
654             Complete();
655         }
656
657         internal WebResponse WaitForResponse() {
658             if (!isCompleted)
659                 AsyncWaitHandle.WaitOne();
660
661             if (this.Exception != null)
662                 throw this.Exception;
663
664             return Response;
665         }
666
667         internal void CombineCompletedSynchronously(bool innerCompletedSynchronously) {
668             completedSynchronously = completedSynchronously && innerCompletedSynchronously;
669         }
670     }
671
672     /// <include file='doc\ClientProtocol.uex' path='docs/doc[@for="InvokeCompletedEventHandler"]/*' />
673     /// <devdoc>
674     ///    <para>[To be supplied.]</para>
675     /// </devdoc>
676     public delegate void InvokeCompletedEventHandler(object sender, InvokeCompletedEventArgs e);
677
678     /// <include file='doc\ClientProtocol.uex' path='docs/doc[@for="InvokeCompletedEventArgs"]/*' />
679     /// <devdoc>
680     ///    <para>[To be supplied.]</para>
681     /// </devdoc>
682     public class InvokeCompletedEventArgs : AsyncCompletedEventArgs {
683         object[] results;
684
685         internal InvokeCompletedEventArgs(object[] results, Exception exception, bool cancelled, object userState) :
686             base(exception, cancelled, userState) {
687             this.results = results;
688         }
689
690         /// <include file='doc\ClientProtocol.uex' path='docs/doc[@for="InvokeCompletedEventArgs.Results"]/*' />
691         /// <devdoc>
692         ///    <para>
693         ///       Gets or sets a value indicating whether the client should automatically follow server redirects.
694         ///    </para>
695         /// </devdoc>
696         public object[] Results {
697             get {
698                 return results;
699             }
700         }
701     }
702
703
704     internal class UserToken {
705         SendOrPostCallback callback;
706         object userState;
707
708         internal UserToken(SendOrPostCallback callback, object userState) {
709             this.callback = callback;
710             this.userState = userState;
711         }
712         internal SendOrPostCallback Callback { get { return callback; } }
713         internal object UserState { get { return userState; } }
714     }
715
716     /// <include file='doc\ClientProtocol.uex' path='docs/doc[@for="HttpWebClientProtocol"]/*' />
717     /// <devdoc>
718     ///    <para>[To be supplied.]</para>
719     /// </devdoc>
720     [ComVisible(true)]
721     public abstract class HttpWebClientProtocol : WebClientProtocol {
722         private bool allowAutoRedirect;
723         private bool enableDecompression = false;
724         private CookieContainer cookieJar = null;
725         private X509CertificateCollection clientCertificates;
726         private IWebProxy proxy;
727         private static string UserAgentDefault = "Mozilla/4.0 (compatible; MSIE 6.0; MS Web Services Client Protocol " + System.Environment.Version.ToString() + ")";
728         private string userAgent;
729         private bool unsafeAuthenticatedConnectionSharing;
730
731         /// <include file='doc\ClientProtocol.uex' path='docs/doc[@for="HttpWebClientProtocol.HttpWebClientProtocol"]/*' />
732         /// <devdoc>
733         ///    <para>[To be supplied.]</para>
734         /// </devdoc>
735         protected HttpWebClientProtocol() : base() {
736             this.allowAutoRedirect = false;
737             this.userAgent = UserAgentDefault;
738             // the right thing to do, for NetClasses to pick up the default
739             // GlobalProxySelection settings, is to leave proxy to null
740             // (which is the default initialization value)
741             // rather than picking up GlobalProxySelection.Select
742             // which will never change.
743         }
744
745         // used by SoapHttpClientProtocol.Discover
746         internal HttpWebClientProtocol(HttpWebClientProtocol protocol)
747             : base(protocol) {
748             this.allowAutoRedirect  = protocol.allowAutoRedirect;
749             this.enableDecompression  = protocol.enableDecompression;
750             this.cookieJar          = protocol.cookieJar;
751             this.clientCertificates = protocol.clientCertificates;
752             this.proxy              = protocol.proxy;
753             this.userAgent          = protocol.userAgent;
754         }
755
756         /// <include file='doc\ClientProtocol.uex' path='docs/doc[@for="HttpWebClientProtocol.AllowAutoRedirect"]/*' />
757         /// <devdoc>
758         ///    <para>
759         ///       Gets or sets a value indicating whether the client should automatically follow server redirects.
760         ///    </para>
761         /// </devdoc>
762         [DefaultValue(false), WebServicesDescription(Res.ClientProtocolAllowAutoRedirect)]
763         public bool AllowAutoRedirect {
764             get {
765                 return allowAutoRedirect;
766             }
767
768             set {
769                 allowAutoRedirect = value;
770             }
771         }
772
773
774         /// <include file='doc\ClientProtocol.uex' path='docs/doc[@for="HttpWebClientProtocol.CookieContainer"]/*' />
775         [DefaultValue(null), WebServicesDescription(Res.ClientProtocolCookieContainer)]
776         public CookieContainer CookieContainer {
777             get {
778                 return cookieJar;
779             }
780             set {
781                 cookieJar = value;
782             }
783         }
784
785         /// <include file='doc\ClientProtocol.uex' path='docs/doc[@for="HttpWebClientProtocol.ClientCertificates"]/*' />
786         [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), WebServicesDescription(Res.ClientProtocolClientCertificates)]
787         public X509CertificateCollection ClientCertificates {
788             get {
789                 if (clientCertificates == null) {
790                     clientCertificates = new X509CertificateCollection();
791                 }
792                 return clientCertificates;
793             }
794         }
795
796         /// <include file='doc\ClientProtocol.uex' path='docs/doc[@for="HttpWebClientProtocol.EnableDecompression"]/*' />
797         /// <devdoc>
798         ///    <para>
799         ///       Gets or sets a value indicating whether the client should automatically follow server redirects.
800         ///    </para>
801         /// </devdoc>
802         [DefaultValue(false), WebServicesDescription(Res.ClientProtocolEnableDecompression)]
803         public bool EnableDecompression {
804             get {
805                 return enableDecompression;
806             }
807
808             set {
809                 enableDecompression = value;
810             }
811         }
812
813          /// <include file='doc\ClientProtocol.uex' path='docs/doc[@for="HttpWebClientProtocol.UserAgent"]/*' />
814          /// <devdoc>
815          ///    <para>
816          ///       Gets or sets the value for the user agent header that is
817          ///       sent with each request.
818          ///    </para>
819          /// </devdoc>
820         [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), WebServicesDescription(Res.ClientProtocolUserAgent)]
821         public string UserAgent {
822             get { return (userAgent == null) ? string.Empty : userAgent; }
823             set { userAgent = value; }
824         }
825
826         /// <include file='doc\ClientProtocol.uex' path='docs/doc[@for="HttpWebClientProtocol.Proxy"]/*' />
827         /// <devdoc>
828         ///    <para>
829         ///       Gets or sets the name of the proxy server to use for requests.
830         ///    </para>
831         /// </devdoc>
832         [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
833         public IWebProxy Proxy {
834             get { return proxy; }
835             set { proxy = value; }
836         }
837
838         /// <include file='doc\ClientProtocol.uex' path='docs/doc[@for="HttpWebClientProtocol.GetWebRequest"]/*' />
839         /// <devdoc>
840         ///    <para>[To be supplied.]</para>
841         /// </devdoc>
842         protected override WebRequest GetWebRequest(Uri uri) {
843             WebRequest request = base.GetWebRequest(uri);
844             HttpWebRequest httpRequest = request as HttpWebRequest;
845             if (httpRequest != null) {
846                 httpRequest.UserAgent = UserAgent;
847                 httpRequest.AllowAutoRedirect = allowAutoRedirect;
848                 httpRequest.AutomaticDecompression = enableDecompression ? DecompressionMethods.GZip : DecompressionMethods.None;
849                 httpRequest.AllowWriteStreamBuffering = true;
850                 httpRequest.SendChunked = false;
851                 if (unsafeAuthenticatedConnectionSharing != httpRequest.UnsafeAuthenticatedConnectionSharing)
852                     httpRequest.UnsafeAuthenticatedConnectionSharing = unsafeAuthenticatedConnectionSharing;
853                 // if the user has set a proxy explictly then we need to
854                 // propagate that to the WebRequest, otherwise we'll let NetClasses
855                 // use their global setting (GlobalProxySelection.Select).
856                 if (proxy != null) {
857                     httpRequest.Proxy = proxy;
858                 }
859                 if (clientCertificates != null && clientCertificates.Count > 0) {
860                     httpRequest.ClientCertificates.AddRange(clientCertificates);
861                 }
862                 httpRequest.CookieContainer = cookieJar;
863             }
864             return request;
865         }
866
867         /// <include file='doc\ClientProtocol.uex' path='docs/doc[@for="HttpWebClientProtocol.GetWebResponse"]/*' />
868         /// <devdoc>
869         ///    <para>[To be supplied.]</para>
870         /// </devdoc>
871         protected override WebResponse GetWebResponse(WebRequest request) {
872             WebResponse response = base.GetWebResponse(request);
873             return response;
874         }
875
876         /// <include file='doc\ClientProtocol.uex' path='docs/doc[@for="HttpWebClientProtocol.GetWebResponse1"]/*' />
877         /// <devdoc>
878         ///    <para>[To be supplied.]</para>
879         /// </devdoc>
880         protected override WebResponse GetWebResponse(WebRequest request, IAsyncResult result) {
881             WebResponse response = base.GetWebResponse(request, result);
882             return response;
883         }
884
885         /// <include file='doc\ClientProtocol.uex' path='docs/doc[@for="HttpWebClientProtocol.UnsafeAuthenticatedConnectionSharing"]/*' />
886         [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
887         public bool UnsafeAuthenticatedConnectionSharing {
888             get { return unsafeAuthenticatedConnectionSharing; }
889             set { unsafeAuthenticatedConnectionSharing = value; }
890         }
891
892         /// <include file='doc\ClientProtocol.uex' path='docs/doc[@for="ClientProtocol.CancelInvokeAsync"]/*' />
893         /// <devdoc>
894         ///    <para>[To be supplied.]</para>
895         /// </devdoc>
896         protected void CancelAsync(object userState) {
897             if (userState == null)
898                 userState = NullToken;
899             WebClientAsyncResult result = OperationCompleted(userState, new object[] { null }, null, true);
900             if (result != null) {
901                 result.Abort();
902             }
903         }
904
905         internal WebClientAsyncResult OperationCompleted(object userState, object[] parameters, Exception e, bool canceled) {
906             Debug.Assert(userState != null, "We should not call OperationCompleted with null user token.");
907             WebClientAsyncResult result = (WebClientAsyncResult)AsyncInvokes[userState];
908             if (result != null) {
909                 AsyncOperation asyncOp = (AsyncOperation)result.AsyncState;
910                 UserToken token = (UserToken)asyncOp.UserSuppliedState;
911                 InvokeCompletedEventArgs eventArgs = new InvokeCompletedEventArgs(parameters, e, canceled, userState);
912                 AsyncInvokes.Remove(userState);
913                 asyncOp.PostOperationCompleted(token.Callback, eventArgs);
914             }
915             return result;
916         }
917
918         /// <include file='doc\ClientProtocol.uex' path='docs/doc[@for="HttpWebClientProtocol.GenerateXmlMappings"]/*' />
919         /// <devdoc>
920         ///    <para>[To be supplied.]</para>
921         /// </devdoc>
922         public static bool GenerateXmlMappings(Type type, ArrayList mappings) {
923             if (typeof(SoapHttpClientProtocol).IsAssignableFrom(type)) {
924                 WebServiceBindingAttribute binding = WebServiceBindingReflector.GetAttribute(type);
925                 if (binding == null)
926                     throw new InvalidOperationException(Res.GetString(Res.WebClientBindingAttributeRequired));
927                 // Note: Service namespace is taken from WebserviceBindingAttribute and not WebserviceAttribute because
928                 // the generated proxy does not have a WebServiceAttribute; however all have a WebServiceBindingAttribute. 
929                 string serviceNamespace = binding.Namespace;
930                 bool serviceDefaultIsEncoded = SoapReflector.ServiceDefaultIsEncoded(type);
931                 ArrayList soapMethodList = new ArrayList();
932                 SoapClientType.GenerateXmlMappings(type, soapMethodList, serviceNamespace, serviceDefaultIsEncoded, mappings);
933                 return true;
934             }
935             return false;
936         }
937
938         /// <include file='doc\ClientProtocol.uex' path='docs/doc[@for="HttpWebClientProtocol.GenerateXmlMappings1"]/*' />
939         /// <devdoc>
940         ///    <para>[To be supplied.]</para>
941         /// </devdoc>
942         public static Hashtable GenerateXmlMappings(Type[] types, ArrayList mappings) {
943             if (types == null)
944                 throw new ArgumentNullException("types");
945
946             Hashtable mappedTypes = new Hashtable();
947             foreach (Type type in types) {
948                 ArrayList typeMappings = new ArrayList();
949                 if (GenerateXmlMappings(type, mappings)) {
950                     mappedTypes.Add(type, typeMappings);
951                     mappings.Add(typeMappings);
952                 }
953             }
954             return mappedTypes;
955         }
956     }
957 }