Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System / net / System / Net / _TLSstream.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="_TLSstream.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6
7 namespace System.Net {
8     using System.IO;
9     using System.Text;
10     using System.Net.Sockets;
11     using System.Threading;
12     using System.Security.Cryptography.X509Certificates;
13     using System.ComponentModel;
14     using System.Collections;
15     using System.Net.Security;
16     using System.Globalization;
17     using System.Security.Authentication.ExtendedProtection;
18     using System.Net.Configuration;
19
20     internal class TlsStream : NetworkStream, IDisposable {
21         private SslState m_Worker;
22         private WebExceptionStatus m_ExceptionStatus;
23         private string m_DestinationHost;
24         private X509CertificateCollection m_ClientCertificates;
25         private static AsyncCallback _CompleteIOCallback = new AsyncCallback(CompleteIOCallback);
26
27         private ExecutionContext _ExecutionContext;
28         private ChannelBinding m_CachedChannelBinding;
29
30         //
31         // This version of an Ssl Stream is for internal HttpWebrequest use.
32         // This Ssl client owns the underlined socket
33         // The TlsStream will own secured read/write and disposal of the passed "networkStream" stream.
34         //
35         public TlsStream(string destinationHost, NetworkStream networkStream, X509CertificateCollection clientCertificates, ServicePoint servicePoint, object initiatingRequest, ExecutionContext executionContext)
36                :base(networkStream, true) {
37
38         // WebRequest manages the execution context manually so we have to ensure we get one for SSL client certificate demand
39         _ExecutionContext = executionContext;
40         if (_ExecutionContext == null)
41         {
42             _ExecutionContext = ExecutionContext.Capture();
43         }
44
45         // 
46
47
48          GlobalLog.Enter("TlsStream::TlsStream", "host="+destinationHost+", #certs="+((clientCertificates == null) ? "none" : clientCertificates.Count.ToString(NumberFormatInfo.InvariantInfo)));
49          if (Logging.On) Logging.PrintInfo(Logging.Web, this, ".ctor", "host="+destinationHost+", #certs="+((clientCertificates == null) ? "null" : clientCertificates.Count.ToString(NumberFormatInfo.InvariantInfo)));
50
51          m_ExceptionStatus = WebExceptionStatus.SecureChannelFailure;
52          m_Worker = new SslState(networkStream, initiatingRequest is HttpWebRequest, SettingsSectionInternal.Section.EncryptionPolicy);
53
54          m_DestinationHost = destinationHost;
55          m_ClientCertificates = clientCertificates;
56
57          RemoteCertValidationCallback certValidationCallback = servicePoint.SetupHandshakeDoneProcedure(this, initiatingRequest);
58          m_Worker.SetCertValidationDelegate(certValidationCallback);
59
60          // The Handshake is NOT done at this point
61          GlobalLog.Leave("TlsStream::TlsStream (Handshake is not done)");
62         }
63
64         //
65         // HttpWebRequest as a consumer of this class will ignore any write error, by relying on the read side exception.
66         // We want to keep the right failure status for a user application.
67         //
68         internal WebExceptionStatus ExceptionStatus {
69             get {
70                 return m_ExceptionStatus;
71             }
72         }
73
74         // This implements the IDisposable contract from the NetworkStream base class
75         // Note that finalizer on the base class WILL call us unless there was explicit disposal.
76         int m_ShutDown = 0;
77         protected override void Dispose(bool disposing) {
78             GlobalLog.Print("TlsStream::Dispose()");
79             if ( Interlocked.Exchange( ref m_ShutDown,  1) == 1 ) {
80                 return;
81             }
82             try {
83                 if (disposing) {
84                     // When KeepAlive is turned off, the TlsStream will be closed before the auth headers for the next request
85                     // are computed.  We cannot retrieve the ChannelBinding from the TlsStream after closing it, so we need to
86                     // cache it now.
87                     m_CachedChannelBinding = GetChannelBinding(ChannelBindingKind.Endpoint);
88
89                     // Note this will not close the underlined socket, only security context
90                     m_Worker.Close();
91                 }
92                 else {
93                     m_Worker = null;
94                 }
95             }
96             finally {
97                 //This will close the underlined socket
98                 base.Dispose(disposing);
99             }
100         }
101
102         public override bool DataAvailable {
103             get {
104                  return m_Worker.DataAvailable || base.DataAvailable;
105             }
106         }
107
108         //
109         // Sync Read version
110         //
111         public override int Read(byte[] buffer, int offset, int size) {
112             GlobalLog.Print("TlsStream#" + ValidationHelper.HashString(this) + "::Read() SecureWorker#" + ValidationHelper.HashString(m_Worker) + " offset:" + offset.ToString() + " size:" + size.ToString());
113
114             if (!m_Worker.IsAuthenticated)
115                 ProcessAuthentication(null);
116
117             try {
118                 return m_Worker.SecureStream.Read(buffer, offset, size);
119
120             }
121             catch {
122                 if (m_Worker.IsCertValidationFailed) {
123                     m_ExceptionStatus = WebExceptionStatus.TrustFailure;
124                 }
125                 else if (m_Worker.LastSecurityStatus != SecurityStatus.OK) {
126                     m_ExceptionStatus = WebExceptionStatus.SecureChannelFailure;
127                 }
128                 else {
129                     m_ExceptionStatus = WebExceptionStatus.ReceiveFailure;
130                 }
131                 throw;
132             }
133         }
134         //
135         // Async Read version
136         //
137         public override IAsyncResult BeginRead(byte[] buffer, int offset, int size, AsyncCallback asyncCallback, object asyncState) {
138             GlobalLog.Print("TlsStream#" + ValidationHelper.HashString(this) + "::BeginRead() SecureWorker#" + ValidationHelper.HashString(m_Worker) + " offset:" + offset.ToString() + " size:" + size.ToString());
139
140             if (!m_Worker.IsAuthenticated)
141             {
142                 BufferAsyncResult result = new BufferAsyncResult(this, buffer, offset, size, false, asyncState, asyncCallback);
143                 if (ProcessAuthentication(result))
144                     return result;
145             }
146
147             try {
148                 return m_Worker.SecureStream.BeginRead(buffer, offset, size, asyncCallback, asyncState);
149             }
150             catch {
151                 if (m_Worker.IsCertValidationFailed) {
152                     m_ExceptionStatus = WebExceptionStatus.TrustFailure;
153                 }
154                 else if (m_Worker.LastSecurityStatus != SecurityStatus.OK) {
155                     m_ExceptionStatus = WebExceptionStatus.SecureChannelFailure;
156                 }
157                 else {
158                     m_ExceptionStatus = WebExceptionStatus.ReceiveFailure;
159                 }
160                 throw;
161             }
162         }
163
164         // 
165         internal override IAsyncResult UnsafeBeginRead(byte[] buffer, int offset, int size, AsyncCallback asyncCallback, object asyncState)
166         {
167             return BeginRead(buffer, offset, size, asyncCallback, asyncState);
168         }
169
170         //
171         public override int EndRead(IAsyncResult asyncResult) {
172             GlobalLog.Print("TlsStream#" + ValidationHelper.HashString(this) + "::EndRead() IAsyncResult#" + ValidationHelper.HashString(asyncResult));
173             try {
174
175                 BufferAsyncResult bufferResult = asyncResult as BufferAsyncResult;
176
177                 if (bufferResult == null || (object)bufferResult.AsyncObject != this)
178                     return m_Worker.SecureStream.EndRead(asyncResult);
179
180                 // we have wrapped user IO in case when handshake was not done as the Begin call
181                 bufferResult.InternalWaitForCompletion();
182                 Exception e = bufferResult.Result as Exception;
183                 if (e != null)
184                     throw e;
185
186                 return (int) bufferResult.Result;
187             }
188             catch {
189                 if (m_Worker.IsCertValidationFailed) {
190                     m_ExceptionStatus = WebExceptionStatus.TrustFailure;
191                 }
192                 else if (m_Worker.LastSecurityStatus != SecurityStatus.OK) {
193                     m_ExceptionStatus = WebExceptionStatus.SecureChannelFailure;
194                 }
195                 else {
196                     m_ExceptionStatus = WebExceptionStatus.ReceiveFailure;
197                 }
198                 throw;
199             }
200         }
201
202
203         //
204         // Write, all flavours: synchrnous and asynchrnous
205         //
206         public override void Write(byte[] buffer, int offset, int size) {
207             GlobalLog.Print("TlsStream#" + ValidationHelper.HashString(this) + "::Write() SecureWorker#" + ValidationHelper.HashString(m_Worker) + " offset:" + offset.ToString() + " size:" + size.ToString());
208
209             if (!m_Worker.IsAuthenticated)
210                 ProcessAuthentication(null);
211
212             try {
213                 m_Worker.SecureStream.Write(buffer, offset, size);
214             }
215             catch {
216                 // We preserve the original status of a failure because the read
217                 // side will now fail with object dispose error.
218                 if (m_Worker.IsCertValidationFailed) {
219                     m_ExceptionStatus = WebExceptionStatus.TrustFailure;
220                 }
221                 else if (m_Worker.LastSecurityStatus != SecurityStatus.OK) {
222                     m_ExceptionStatus = WebExceptionStatus.SecureChannelFailure;
223                 }
224                 else {
225                     m_ExceptionStatus = WebExceptionStatus.SendFailure;
226                 }
227                 //HttpWbeRequest depends on the phyical stream to be dropped on a write error.
228                 Socket chkSocket = this.Socket;
229                 if(chkSocket != null) {
230                     chkSocket.InternalShutdown(SocketShutdown.Both);
231                 }
232                 throw;
233             }
234         }
235
236         //
237         // BeginWrite -
238         //
239         // Write the bytes to the write - while encrypting
240         //
241         // copy plain text data to a temporary buffer
242         // encrypt the data
243         // once the data is encrypted clear the plain text for security
244         //
245         public override IAsyncResult BeginWrite( byte[] buffer, int offset, int size, AsyncCallback asyncCallback, object asyncState) {
246             GlobalLog.Print("TlsStream#" + ValidationHelper.HashString(this) + "::BeginWrite() SecureWorker#" + ValidationHelper.HashString(m_Worker) + " offset:" + offset.ToString() + " size:" + size.ToString());
247             if (!m_Worker.IsAuthenticated)
248             {
249                 BufferAsyncResult result = new BufferAsyncResult(this, buffer, offset, size, true, asyncState, asyncCallback);
250                 if (ProcessAuthentication(result))
251                     return result;
252             }
253
254             try {
255                 return m_Worker.SecureStream.BeginWrite(buffer, offset, size, asyncCallback, asyncState);
256             }
257             catch {
258                 if (m_Worker.IsCertValidationFailed) {
259                     m_ExceptionStatus = WebExceptionStatus.TrustFailure;
260                 }
261                 else if (m_Worker.LastSecurityStatus != SecurityStatus.OK) {
262                     m_ExceptionStatus = WebExceptionStatus.SecureChannelFailure;
263                 }
264                 else {
265                     m_ExceptionStatus = WebExceptionStatus.SendFailure;
266                 }
267                 throw;
268             }
269         }
270
271
272         internal override IAsyncResult UnsafeBeginWrite( byte[] buffer, int offset, int size, AsyncCallback asyncCallback, object asyncState) {
273             return BeginWrite(buffer,offset,size,asyncCallback,asyncState);
274         }
275
276
277
278         public override void EndWrite(IAsyncResult asyncResult) {
279             GlobalLog.Print("TlsStream#" + ValidationHelper.HashString(this) + "::EndWrite() IAsyncResult#" + ValidationHelper.HashString(asyncResult));
280
281             try {
282                 BufferAsyncResult bufferResult = asyncResult as BufferAsyncResult;
283
284                 if (bufferResult == null || (object)bufferResult.AsyncObject != this)
285                 {
286                     m_Worker.SecureStream.EndWrite(asyncResult);
287                 }
288                 else
289                 {
290                     // we have wrapped user IO in case when handshake was not done as the Begin call
291                     bufferResult.InternalWaitForCompletion();
292                     Exception e = bufferResult.Result as Exception;
293                     if (e != null)
294                         throw e;
295                 }
296             }
297             catch {
298                 //HttpWebRequest depends on the stream to be dropped on a write error.
299                 Socket chkSocket = this.Socket;
300                 if(chkSocket != null) {
301                     chkSocket.InternalShutdown(SocketShutdown.Both);
302                 }
303                 // We also preserve the original status of a failure because the read
304                 // side will now fail with object dispose error.
305                 if (m_Worker.IsCertValidationFailed) {
306                     m_ExceptionStatus = WebExceptionStatus.TrustFailure;
307                 }
308                 else if (m_Worker.LastSecurityStatus != SecurityStatus.OK) {
309                     m_ExceptionStatus = WebExceptionStatus.SecureChannelFailure;
310                 }
311                 else {
312                     m_ExceptionStatus = WebExceptionStatus.SendFailure;
313                 }
314                 throw;
315             }
316         }
317
318         internal override void MultipleWrite(BufferOffsetSize[] buffers) {
319             GlobalLog.Print("TlsStream#" + ValidationHelper.HashString(this) + "::MultipleWrite() SecureWorker#" + ValidationHelper.HashString(m_Worker) + " buffers.Length:" + buffers.Length.ToString());
320
321             if (!m_Worker.IsAuthenticated)
322                 ProcessAuthentication(null);
323
324             try {
325                 m_Worker.SecureStream.Write(buffers);
326             }
327             catch {
328                 //HttpWbeRequest depends on the physical stream to be dropped on a write error.
329                 Socket chkSocket = this.Socket;
330                 if(chkSocket != null) {
331                     chkSocket.InternalShutdown(SocketShutdown.Both);
332                 }
333                 // We preserve the original status of a failure because the read
334                 // side will now fail with object dispose error.
335                 if (m_Worker.IsCertValidationFailed) {
336                     m_ExceptionStatus = WebExceptionStatus.TrustFailure;
337                 }
338                 else if (m_Worker.LastSecurityStatus != SecurityStatus.OK) {
339                     m_ExceptionStatus = WebExceptionStatus.SecureChannelFailure;
340                 }
341                 else {
342                     m_ExceptionStatus = WebExceptionStatus.SendFailure;
343                 }
344                 throw;
345             }
346         }
347
348         internal override IAsyncResult BeginMultipleWrite(BufferOffsetSize[] buffers, AsyncCallback callback, object state) {
349             GlobalLog.Print("TlsStream#" + ValidationHelper.HashString(this) + "::BeginMultipleWrite() SecureWorker#" + ValidationHelper.HashString(m_Worker) + " buffers.Length:" + buffers.Length.ToString());
350             if (!m_Worker.IsAuthenticated)
351             {
352                 BufferAsyncResult result = new BufferAsyncResult(this, buffers, state, callback);
353                 if (ProcessAuthentication(result))
354                     return result;
355             }
356
357             try {
358                 return m_Worker.SecureStream.BeginWrite(buffers, callback, state);
359             }
360             catch {
361                 if (m_Worker.IsCertValidationFailed) {
362                     m_ExceptionStatus = WebExceptionStatus.TrustFailure;
363                 }
364                 else if (m_Worker.LastSecurityStatus != SecurityStatus.OK) {
365                     m_ExceptionStatus = WebExceptionStatus.SecureChannelFailure;
366                 }
367                 else {
368                     m_ExceptionStatus = WebExceptionStatus.SendFailure;
369                 }
370                 throw;
371             }
372         }
373
374         internal override IAsyncResult UnsafeBeginMultipleWrite(BufferOffsetSize[] buffers, AsyncCallback callback, object state) {
375             return BeginMultipleWrite(buffers,callback,state);
376         }
377
378         // overrides base.EndMultipeWrite
379         internal override void EndMultipleWrite(IAsyncResult asyncResult)
380         {
381             GlobalLog.Print("TlsStream#" + ValidationHelper.HashString(this) + "::EndMultipleWrite() IAsyncResult#" + ValidationHelper.HashString(asyncResult));
382             EndWrite(asyncResult);
383         }
384
385
386         public X509Certificate ClientCertificate {
387             get {
388                 return m_Worker.InternalLocalCertificate;
389             }
390         }
391
392         internal ChannelBinding GetChannelBinding(ChannelBindingKind kind)
393         {
394             if (kind == ChannelBindingKind.Endpoint && m_CachedChannelBinding != null)
395             {
396                 return m_CachedChannelBinding;
397             }
398
399             return m_Worker.GetChannelBinding(kind);
400         }
401
402         //
403         // This methods ensures that IO is only issued when the handshake is completed in ether way
404         // The very first coming IO will initiate the handshake and define it's flavor (sync/async).
405         //
406         // Returns false if the handshake was already done.
407         // Returns true  if the user IO is queued and the handshake is started.
408         // Return value is not applicable in sync case.
409         //
410         private ArrayList m_PendingIO = new ArrayList();
411         internal bool ProcessAuthentication(LazyAsyncResult result)
412         {
413             bool doHandshake = false;
414             bool isSyncCall = result == null;
415
416             lock (m_PendingIO)
417             {
418                 // do we have handshake as already done before we grabbed a lock?
419                 if (m_Worker.IsAuthenticated)
420                     return false;
421
422                 if (m_PendingIO.Count == 0)
423                 {
424                     doHandshake = true;
425                 }
426
427                 if (isSyncCall)
428                 {
429                     // we will wait on this guy in this method for the handshake to complete
430                     result = new LazyAsyncResult(this, null, null);
431                 }
432
433                 m_PendingIO.Add(result);
434             }
435
436             try {
437                 if (doHandshake)
438                 {
439                     bool success = true;
440                     LazyAsyncResult handshakeResult = null;
441                     try
442                     {
443                         m_Worker.ValidateCreateContext(false,
444                                                        m_DestinationHost,
445                                                        (System.Security.Authentication.SslProtocols)ServicePointManager.SecurityProtocol,
446                                                        null, m_ClientCertificates,
447                                                        true,
448                                                        ServicePointManager.CheckCertificateRevocationList,
449                                                        ServicePointManager.CheckCertificateName);
450
451
452                         if (!isSyncCall)
453                         {
454                             // wrap a user async IO/Handshake request into auth request
455                             handshakeResult = new LazyAsyncResult(m_Worker, null, new AsyncCallback(WakeupPendingIO));
456 #if DEBUG
457                             result._DebugAsyncChain = handshakeResult;
458 #endif
459                         }
460
461                         //
462                         // TlsStream is used by classes that manually control ExecutionContext, so set it here if we need to.
463                         //
464                         if (_ExecutionContext != null)
465                         {
466                             ExecutionContext.Run(_ExecutionContext.CreateCopy(), new ContextCallback(CallProcessAuthentication), handshakeResult);
467                         }
468                         else
469                         {
470                             m_Worker.ProcessAuthentication(handshakeResult);
471                         }
472                     }
473                     catch
474                     {
475                         success = false;
476                         throw;
477                     }
478                     finally
479                     {
480                         if (isSyncCall || !success)
481                         {
482                             lock (m_PendingIO)
483                             {
484                                 if(m_PendingIO.Count > 1)
485                                 {
486                                     // It was a real sync handshake (now completed) and another IO came in.
487                                     // It's now waiting on us so resume.
488                                     ThreadPool.QueueUserWorkItem(new WaitCallback(StartWakeupPendingIO), null);
489                                 }
490                                 else {
491                                     m_PendingIO.Clear();
492                                 }
493                             }
494                         }
495                     }
496                 }
497                 else if (isSyncCall)
498                 {
499                     GlobalLog.Assert(result != null, "TlsStream::ProcessAuthentication() this is a Sync call and it did not started the handshake hence null result must be wrapped into LazyAsyncResult");
500                     Exception e = result.InternalWaitForCompletion() as Exception;
501                     if (e != null)
502                         throw e;
503                 }
504             }
505             catch {
506                 if (m_Worker.IsCertValidationFailed) {
507                     m_ExceptionStatus = WebExceptionStatus.TrustFailure;
508                 }
509                 else if (m_Worker.LastSecurityStatus != SecurityStatus.OK) {
510                     m_ExceptionStatus = WebExceptionStatus.SecureChannelFailure;
511                 }
512                 else {
513                     m_ExceptionStatus = WebExceptionStatus.ReceiveFailure;
514                 }
515                 throw;
516             }
517
518             // Here in the async case a user IO has been queued (and may be already completed)
519             // For sync case it does not matter since the caller will resume IO upon return
520             return true;
521         }
522         //
523         void CallProcessAuthentication(object state)
524         {
525             m_Worker.ProcessAuthentication((LazyAsyncResult)state);
526         }
527
528         //
529         private void StartWakeupPendingIO(object nullState)
530         {
531             // state must be is  null here
532             GlobalLog.Assert(nullState == null, "TlsStream::StartWakeupPendingIO|Expected null state but got {0}.", nullState == null ? "null" : (nullState.GetType().FullName));
533             WakeupPendingIO(null);
534         }
535         //
536         // This is proven to be called without any user stack or it was just ONE IO queued.
537         //
538         private void WakeupPendingIO(IAsyncResult ar)
539         {
540             Exception exception = null;
541             try {
542                 if (ar != null)
543                     m_Worker.EndProcessAuthentication(ar);
544             }
545             catch (Exception e) {
546                 // This method does not throw because it job is to notify everyon waiting on the result
547                 // NOTE: SSL engine remembers the exception and will rethrow it on any access for SecureStream
548                 // property means on  any IO attempt.
549                 exception = e;
550
551                 if (m_Worker.IsCertValidationFailed) {
552                     m_ExceptionStatus = WebExceptionStatus.TrustFailure;
553                 }
554                 else if (m_Worker.LastSecurityStatus != SecurityStatus.OK) {
555                     m_ExceptionStatus = WebExceptionStatus.SecureChannelFailure;
556                 }
557                 else {
558                     m_ExceptionStatus = WebExceptionStatus.ReceiveFailure;
559                 }
560             }
561
562             lock (m_PendingIO)
563             {
564                 while(m_PendingIO.Count != 0)
565                 {
566                     LazyAsyncResult lazyResult = (LazyAsyncResult )m_PendingIO[m_PendingIO.Count-1];
567
568                     m_PendingIO.RemoveAt(m_PendingIO.Count-1);
569
570                     if (lazyResult is BufferAsyncResult)
571                     {
572                         if (m_PendingIO.Count == 0)
573                         {
574                             // Resume the LAST IO on that thread and offload other IOs on worker threads
575                             ResumeIOWorker(lazyResult);
576                         }
577                         else
578                         {
579                             ThreadPool.QueueUserWorkItem(new WaitCallback(ResumeIOWorker), lazyResult);
580                         }
581                     }
582                     else
583                     {
584                         //resume sync IO waiting on other thread or signal waiting async handshake result.
585                         try {
586                             lazyResult.InvokeCallback(exception);
587                         }
588                         catch {
589                             // this method never throws unles the failure is catastrophic
590                         }
591                     }
592                 }
593             }
594         }
595
596         private void ResumeIOWorker(object result)
597         {
598             BufferAsyncResult bufferResult = (BufferAsyncResult) result;
599             try
600             {
601                 ResumeIO(bufferResult);
602             }
603             catch (Exception exception)
604             {
605                 if (exception is OutOfMemoryException || exception is StackOverflowException || exception is ThreadAbortException)
606                 {
607                     throw;
608                 }
609                 if (bufferResult.InternalPeekCompleted)
610                     throw;
611                 bufferResult.InvokeCallback(exception);
612             }
613         }
614
615         //
616         // Resumes async Read or Write after the handshake is done
617         //
618         private void ResumeIO(BufferAsyncResult bufferResult)
619         {
620             IAsyncResult result;
621             if (bufferResult.IsWrite)
622             {
623                 if (bufferResult.Buffers != null)
624                     result = m_Worker.SecureStream.BeginWrite(bufferResult.Buffers, _CompleteIOCallback, bufferResult);
625                 else
626                     result = m_Worker.SecureStream.BeginWrite(bufferResult.Buffer, bufferResult.Offset, bufferResult.Count, _CompleteIOCallback, bufferResult);
627             }
628             else
629             {
630                 result = m_Worker.SecureStream.BeginRead(bufferResult.Buffer, bufferResult.Offset, bufferResult.Count, _CompleteIOCallback, bufferResult);
631             }
632
633             if (result.CompletedSynchronously)
634             {
635                 CompleteIO(result);
636             }
637         }
638
639         //
640         //
641         //
642         private static void CompleteIOCallback(IAsyncResult result)
643         {
644             if (result.CompletedSynchronously)
645             {
646                 return;
647             }
648
649             try
650             {
651                 CompleteIO(result);
652             }
653             catch (Exception exception)
654             {
655                 if (exception is OutOfMemoryException || exception is StackOverflowException || exception is ThreadAbortException)
656                 {
657                     throw;
658                 }
659
660                 if (((LazyAsyncResult) result.AsyncState).InternalPeekCompleted)
661                     throw;
662                 ((LazyAsyncResult) result.AsyncState).InvokeCallback(exception);
663             }
664         }
665
666         private static void CompleteIO(IAsyncResult result)
667         {
668             BufferAsyncResult bufferResult = (BufferAsyncResult) result.AsyncState;
669
670             object readBytes = null;
671             if (bufferResult.IsWrite)
672                 ((TlsStream)bufferResult.AsyncObject).m_Worker.SecureStream.EndWrite(result);
673             else
674                 readBytes = ((TlsStream)bufferResult.AsyncObject).m_Worker.SecureStream.EndRead(result);
675
676             bufferResult.InvokeCallback(readBytes);
677         }
678
679         //IT should be virtual but we won't keep internal virtual even in debug version
680 #if TRAVE
681         [System.Diagnostics.Conditional("TRAVE")]
682         internal new void DebugMembers() {
683             GlobalLog.Print("m_ExceptionStatus: " + m_ExceptionStatus);
684             GlobalLog.Print("m_DestinationHost: " + m_DestinationHost);
685             GlobalLog.Print("m_Worker:");
686             m_Worker.DebugMembers();
687             base.DebugMembers();
688         }
689 #endif
690
691
692     }; // class TlsStream
693
694 } // namespace System.Net