1 //------------------------------------------------------------ 2 // Copyright (c) Microsoft Corporation. All rights reserved. 3 //------------------------------------------------------------ 4 5 namespace System.ServiceModel.Channels 6 { 7 using System.Collections.Generic; 8 using System.Diagnostics; 9 using System.Net; 10 using System.Net.Sockets; 11 using System.Runtime; 12 using System.Runtime.Diagnostics; 13 using System.Runtime.InteropServices; 14 using System.Security; 15 using System.Security.Permissions; 16 using System.ServiceModel; 17 using System.ServiceModel.Diagnostics; 18 using System.ServiceModel.Diagnostics.Application; 19 using System.Text; 20 using System.Threading; 21 22 class SocketConnection : IConnection 23 { 24 static AsyncCallback onReceiveCompleted; 25 static EventHandler<SocketAsyncEventArgs> onReceiveAsyncCompleted; 26 static EventHandler<SocketAsyncEventArgs> onSocketSendCompleted; 27 28 // common state 29 Socket socket; 30 TimeSpan asyncSendTimeout; 31 TimeSpan readFinTimeout; 32 TimeSpan asyncReceiveTimeout; 33 34 // Socket.SendTimeout/Socket.ReceiveTimeout only work with the synchronous API calls and therefore they 35 // do not get updated when asynchronous Send/Read operations are performed. In order to make sure we 36 // Set the proper timeouts on the Socket itself we need to keep these two additional fields. 37 TimeSpan socketSyncSendTimeout; 38 TimeSpan socketSyncReceiveTimeout; 39 40 CloseState closeState; 41 bool isShutdown; 42 bool noDelay = false; 43 bool aborted; 44 TraceEventType exceptionEventType; 45 46 // close state 47 TimeoutHelper closeTimeoutHelper; 48 static WaitCallback onWaitForFinComplete = new WaitCallback(OnWaitForFinComplete); 49 50 // read state 51 int asyncReadSize; 52 SocketAsyncEventArgs asyncReadEventArgs; 53 byte[] readBuffer; 54 int asyncReadBufferSize; 55 object asyncReadState; 56 WaitCallback asyncReadCallback; 57 Exception asyncReadException; 58 bool asyncReadPending; 59 60 // write state 61 SocketAsyncEventArgs asyncWriteEventArgs; 62 object asyncWriteState; 63 WaitCallback asyncWriteCallback; 64 Exception asyncWriteException; 65 bool asyncWritePending; 66 67 IOThreadTimer receiveTimer; 68 static Action<object> onReceiveTimeout; 69 IOThreadTimer sendTimer; 70 static Action<object> onSendTimeout; 71 string timeoutErrorString; 72 TransferOperation timeoutErrorTransferOperation; 73 IPEndPoint remoteEndpoint; 74 ConnectionBufferPool connectionBufferPool; 75 string remoteEndpointAddress; 76 SocketConnection(Socket socket, ConnectionBufferPool connectionBufferPool, bool autoBindToCompletionPort)77 public SocketConnection(Socket socket, ConnectionBufferPool connectionBufferPool, bool autoBindToCompletionPort) 78 { 79 if (socket == null) 80 { 81 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("socket"); 82 } 83 84 Fx.Assert(connectionBufferPool != null, "Argument connectionBufferPool cannot be null"); 85 86 this.closeState = CloseState.Open; 87 this.exceptionEventType = TraceEventType.Error; 88 this.socket = socket; 89 this.connectionBufferPool = connectionBufferPool; 90 this.readBuffer = this.connectionBufferPool.Take(); 91 this.asyncReadBufferSize = this.readBuffer.Length; 92 this.socket.SendBufferSize = this.socket.ReceiveBufferSize = this.asyncReadBufferSize; 93 this.asyncSendTimeout = this.asyncReceiveTimeout = TimeSpan.MaxValue; 94 this.socketSyncSendTimeout = this.socketSyncReceiveTimeout = TimeSpan.MaxValue; 95 96 this.remoteEndpoint = null; 97 98 if (autoBindToCompletionPort) 99 { 100 this.socket.UseOnlyOverlappedIO = false; 101 } 102 103 // In SMSvcHost, sockets must be duplicated to the target process. Binding a handle to a completion port 104 // prevents any duplicated handle from ever binding to a completion port. The target process is where we 105 // want to use completion ports for performance. This means that in SMSvcHost, socket.UseOnlyOverlappedIO 106 // must be set to true to prevent completion port use. 107 if (this.socket.UseOnlyOverlappedIO) 108 { 109 // Init BeginRead state 110 if (onReceiveCompleted == null) 111 { 112 onReceiveCompleted = Fx.ThunkCallback(new AsyncCallback(OnReceiveCompleted)); 113 } 114 } 115 116 this.TraceSocketInfo(socket, TraceCode.SocketConnectionCreate, SR.TraceCodeSocketConnectionCreate, null); 117 } 118 public int AsyncReadBufferSize 119 { 120 get { return asyncReadBufferSize; } 121 } 122 123 public byte[] AsyncReadBuffer 124 { 125 get 126 { 127 return readBuffer; 128 } 129 } 130 131 object ThisLock 132 { 133 get { return this; } 134 } 135 136 public TraceEventType ExceptionEventType 137 { 138 get { return this.exceptionEventType; } 139 set { this.exceptionEventType = value; } 140 } 141 142 public IPEndPoint RemoteIPEndPoint 143 { 144 get 145 { 146 // this property should only be called on the receive path 147 if (remoteEndpoint == null && this.closeState == CloseState.Open) 148 { 149 try 150 { 151 remoteEndpoint = (IPEndPoint)socket.RemoteEndPoint; 152 } 153 catch (SocketException socketException) 154 { 155 // will never be a timeout error, so TimeSpan.Zero is ok 156 #pragma warning suppress 56503 // Called from Receive path, SocketConnection cannot allow a SocketException to escape. 157 throw DiagnosticUtility.ExceptionUtility.ThrowHelper( 158 ConvertReceiveException(socketException, TimeSpan.Zero, TimeSpan.Zero), ExceptionEventType); 159 } 160 catch (ObjectDisposedException objectDisposedException) 161 { 162 Exception exceptionToThrow = ConvertObjectDisposedException(objectDisposedException, TransferOperation.Undefined); 163 if (object.ReferenceEquals(exceptionToThrow, objectDisposedException)) 164 { 165 #pragma warning suppress 56503 // rethrow 166 throw; 167 } 168 else 169 { 170 #pragma warning suppress 56503 // Called from Receive path, SocketConnection must convert ObjectDisposedException properly. 171 throw DiagnosticUtility.ExceptionUtility.ThrowHelper(exceptionToThrow, ExceptionEventType); 172 } 173 } 174 } 175 176 return remoteEndpoint; 177 } 178 } 179 180 IOThreadTimer SendTimer 181 { 182 get 183 { 184 if (this.sendTimer == null) 185 { 186 if (onSendTimeout == null) 187 { 188 onSendTimeout = new Action<object>(OnSendTimeout); 189 } 190 191 this.sendTimer = new IOThreadTimer(onSendTimeout, this, false); 192 } 193 194 return this.sendTimer; 195 } 196 } 197 198 IOThreadTimer ReceiveTimer 199 { 200 get 201 { 202 if (this.receiveTimer == null) 203 { 204 if (onReceiveTimeout == null) 205 { 206 onReceiveTimeout = new Action<object>(OnReceiveTimeout); 207 } 208 209 this.receiveTimer = new IOThreadTimer(onReceiveTimeout, this, false); 210 } 211 212 return this.receiveTimer; 213 } 214 } 215 216 217 string RemoteEndpointAddress 218 { 219 get 220 { 221 if (remoteEndpointAddress == null) 222 { 223 try 224 { 225 IPEndPoint local, remote; 226 if (TryGetEndpoints(out local, out remote)) 227 { 228 this.remoteEndpointAddress = TraceUtility.GetRemoteEndpointAddressPort(remote); 229 } 230 else 231 { 232 //null indicates not initialized. 233 remoteEndpointAddress = string.Empty; 234 } 235 } 236 catch (Exception exception) 237 { 238 if (Fx.IsFatal(exception)) 239 { 240 throw; 241 } 242 243 } 244 } 245 return remoteEndpointAddress; 246 } 247 } 248 OnReceiveTimeout(object state)249 static void OnReceiveTimeout(object state) 250 { 251 SocketConnection thisPtr = (SocketConnection)state; 252 thisPtr.Abort(SR.GetString(SR.SocketAbortedReceiveTimedOut, thisPtr.asyncReceiveTimeout), TransferOperation.Read); 253 } 254 OnSendTimeout(object state)255 static void OnSendTimeout(object state) 256 { 257 SocketConnection thisPtr = (SocketConnection)state; 258 thisPtr.Abort(TraceEventType.Warning, 259 SR.GetString(SR.SocketAbortedSendTimedOut, thisPtr.asyncSendTimeout), TransferOperation.Write); 260 } 261 OnReceiveCompleted(IAsyncResult result)262 static void OnReceiveCompleted(IAsyncResult result) 263 { 264 ((SocketConnection)result.AsyncState).OnReceive(result); 265 } 266 OnReceiveAsyncCompleted(object sender, SocketAsyncEventArgs e)267 static void OnReceiveAsyncCompleted(object sender, SocketAsyncEventArgs e) 268 { 269 ((SocketConnection)e.UserToken).OnReceiveAsync(sender, e); 270 } 271 OnSendAsyncCompleted(object sender, SocketAsyncEventArgs e)272 static void OnSendAsyncCompleted(object sender, SocketAsyncEventArgs e) 273 { 274 ((SocketConnection)e.UserToken).OnSendAsync(sender, e); 275 } 276 Abort()277 public void Abort() 278 { 279 Abort(null, TransferOperation.Undefined); 280 } 281 Abort(string timeoutErrorString, TransferOperation transferOperation)282 void Abort(string timeoutErrorString, TransferOperation transferOperation) 283 { 284 TraceEventType traceEventType = TraceEventType.Warning; 285 286 // we could be timing out a cached connection 287 if (this.ExceptionEventType == TraceEventType.Information) 288 { 289 traceEventType = this.ExceptionEventType; 290 } 291 292 Abort(traceEventType, timeoutErrorString, transferOperation); 293 } 294 Abort(TraceEventType traceEventType)295 void Abort(TraceEventType traceEventType) 296 { 297 Abort(traceEventType, null, TransferOperation.Undefined); 298 } 299 Abort(TraceEventType traceEventType, string timeoutErrorString, TransferOperation transferOperation)300 void Abort(TraceEventType traceEventType, string timeoutErrorString, TransferOperation transferOperation) 301 { 302 if (TD.SocketConnectionAbortIsEnabled()) 303 { 304 TD.SocketConnectionAbort(this.socket.GetHashCode()); 305 } 306 lock (ThisLock) 307 { 308 if (closeState == CloseState.Closed) 309 { 310 return; 311 } 312 313 this.timeoutErrorString = timeoutErrorString; 314 this.timeoutErrorTransferOperation = transferOperation; 315 aborted = true; 316 closeState = CloseState.Closed; 317 318 if (this.asyncReadPending) 319 { 320 CancelReceiveTimer(); 321 } 322 else 323 { 324 this.DisposeReadEventArgs(); 325 } 326 327 if (this.asyncWritePending) 328 { 329 CancelSendTimer(); 330 } 331 else 332 { 333 this.DisposeWriteEventArgs(); 334 } 335 } 336 337 if (DiagnosticUtility.ShouldTrace(traceEventType)) 338 { 339 TraceUtility.TraceEvent(traceEventType, TraceCode.SocketConnectionAbort, 340 SR.GetString(SR.TraceCodeSocketConnectionAbort), this); 341 } 342 343 socket.Close(0); 344 } 345 AbortRead()346 void AbortRead() 347 { 348 lock (ThisLock) 349 { 350 if (this.asyncReadPending) 351 { 352 if (closeState != CloseState.Closed) 353 { 354 this.SetUserToken(this.asyncReadEventArgs, null); 355 this.asyncReadPending = false; 356 CancelReceiveTimer(); 357 } 358 else 359 { 360 this.DisposeReadEventArgs(); 361 } 362 } 363 } 364 } 365 CancelReceiveTimer()366 void CancelReceiveTimer() 367 { 368 // CSDMain 34539: Snapshot the timer so that we don't null ref if there is a ---- 369 // between calls to CancelReceiveTimer (e.g., Abort, AsyncReadCallback) 370 371 IOThreadTimer receiveTimerSnapshot = this.receiveTimer; 372 this.receiveTimer = null; 373 374 if (receiveTimerSnapshot != null) 375 { 376 receiveTimerSnapshot.Cancel(); 377 } 378 } 379 CancelSendTimer()380 void CancelSendTimer() 381 { 382 IOThreadTimer sendTimerSnapshot = this.sendTimer; 383 this.sendTimer = null; 384 385 if (sendTimerSnapshot != null) 386 { 387 sendTimerSnapshot.Cancel(); 388 } 389 } 390 CloseAsyncAndLinger()391 void CloseAsyncAndLinger() 392 { 393 readFinTimeout = closeTimeoutHelper.RemainingTime(); 394 395 try 396 { 397 if (BeginReadCore(0, 1, readFinTimeout, onWaitForFinComplete, this) == AsyncCompletionResult.Queued) 398 { 399 return; 400 } 401 402 int bytesRead = EndRead(); 403 404 if (bytesRead > 0) 405 { 406 throw DiagnosticUtility.ExceptionUtility.ThrowHelper( 407 new CommunicationException(SR.GetString(SR.SocketCloseReadReceivedData, socket.RemoteEndPoint)), 408 ExceptionEventType); 409 } 410 } 411 catch (TimeoutException timeoutException) 412 { 413 throw DiagnosticUtility.ExceptionUtility.ThrowHelper(new TimeoutException( 414 SR.GetString(SR.SocketCloseReadTimeout, socket.RemoteEndPoint, readFinTimeout), timeoutException), 415 ExceptionEventType); 416 } 417 418 ContinueClose(closeTimeoutHelper.RemainingTime()); 419 } 420 OnWaitForFinComplete(object state)421 static void OnWaitForFinComplete(object state) 422 { 423 SocketConnection thisPtr = (SocketConnection)state; 424 425 try 426 { 427 int bytesRead; 428 429 try 430 { 431 bytesRead = thisPtr.EndRead(); 432 433 if (bytesRead > 0) 434 { 435 throw DiagnosticUtility.ExceptionUtility.ThrowHelper( 436 new CommunicationException(SR.GetString(SR.SocketCloseReadReceivedData, thisPtr.socket.RemoteEndPoint)), 437 thisPtr.ExceptionEventType); 438 } 439 } 440 catch (TimeoutException timeoutException) 441 { 442 throw DiagnosticUtility.ExceptionUtility.ThrowHelper(new TimeoutException( 443 SR.GetString(SR.SocketCloseReadTimeout, thisPtr.socket.RemoteEndPoint, thisPtr.readFinTimeout), 444 timeoutException), thisPtr.ExceptionEventType); 445 } 446 447 thisPtr.ContinueClose(thisPtr.closeTimeoutHelper.RemainingTime()); 448 } 449 catch (Exception e) 450 { 451 if (Fx.IsFatal(e)) 452 { 453 throw; 454 } 455 456 DiagnosticUtility.TraceHandledException(e, TraceEventType.Warning); 457 458 // The user has no opportunity to clean up the connection in the async and linger 459 // code path, ensure cleanup finishes. 460 thisPtr.Abort(); 461 } 462 } 463 Close(TimeSpan timeout, bool asyncAndLinger)464 public void Close(TimeSpan timeout, bool asyncAndLinger) 465 { 466 lock (ThisLock) 467 { 468 if (closeState == CloseState.Closing || closeState == CloseState.Closed) 469 { 470 // already closing or closed, so just return 471 return; 472 } 473 this.TraceSocketInfo(this.socket, TraceCode.SocketConnectionClose, SR.TraceCodeSocketConnectionClose, timeout.ToString()); 474 closeState = CloseState.Closing; 475 } 476 477 // first we shutdown our send-side 478 closeTimeoutHelper = new TimeoutHelper(timeout); 479 Shutdown(closeTimeoutHelper.RemainingTime()); 480 481 if (asyncAndLinger) 482 { 483 CloseAsyncAndLinger(); 484 } 485 else 486 { 487 CloseSync(); 488 } 489 } 490 CloseSync()491 void CloseSync() 492 { 493 byte[] dummy = new byte[1]; 494 495 // then we check for a FIN from the other side (i.e. read zero) 496 int bytesRead; 497 readFinTimeout = closeTimeoutHelper.RemainingTime(); 498 499 try 500 { 501 bytesRead = ReadCore(dummy, 0, 1, readFinTimeout, true); 502 503 if (bytesRead > 0) 504 { 505 throw DiagnosticUtility.ExceptionUtility.ThrowHelper( 506 new CommunicationException(SR.GetString(SR.SocketCloseReadReceivedData, socket.RemoteEndPoint)), ExceptionEventType); 507 } 508 } 509 catch (TimeoutException timeoutException) 510 { 511 throw DiagnosticUtility.ExceptionUtility.ThrowHelper(new TimeoutException( 512 SR.GetString(SR.SocketCloseReadTimeout, socket.RemoteEndPoint, readFinTimeout), timeoutException), ExceptionEventType); 513 } 514 515 // finally we call Close with whatever time is remaining 516 ContinueClose(closeTimeoutHelper.RemainingTime()); 517 } 518 ContinueClose(TimeSpan timeout)519 public void ContinueClose(TimeSpan timeout) 520 { 521 // trace if we're effectively aborting 522 if (timeout <= TimeSpan.Zero && DiagnosticUtility.ShouldTraceWarning) 523 { 524 TraceUtility.TraceEvent(TraceEventType.Warning, TraceCode.SocketConnectionAbortClose, 525 SR.GetString(SR.TraceCodeSocketConnectionAbortClose), this); 526 } 527 528 socket.Close(TimeoutHelper.ToMilliseconds(timeout)); 529 530 lock (ThisLock) 531 { 532 // Abort could have been called on a separate thread and cleaned up 533 // our buffers/completion here 534 if (this.closeState != CloseState.Closed) 535 { 536 if (!this.asyncReadPending) 537 { 538 this.DisposeReadEventArgs(); 539 } 540 541 if (!this.asyncWritePending) 542 { 543 this.DisposeWriteEventArgs(); 544 } 545 } 546 547 closeState = CloseState.Closed; 548 } 549 } 550 Shutdown(TimeSpan timeout)551 public void Shutdown(TimeSpan timeout) 552 { 553 lock (ThisLock) 554 { 555 if (isShutdown) 556 { 557 return; 558 } 559 560 isShutdown = true; 561 } 562 563 try 564 { 565 socket.Shutdown(SocketShutdown.Send); 566 } 567 catch (SocketException socketException) 568 { 569 throw DiagnosticUtility.ExceptionUtility.ThrowHelper( 570 ConvertSendException(socketException, TimeSpan.MaxValue, this.socketSyncSendTimeout), ExceptionEventType); 571 } 572 catch (ObjectDisposedException objectDisposedException) 573 { 574 Exception exceptionToThrow = ConvertObjectDisposedException(objectDisposedException, TransferOperation.Undefined); 575 if (object.ReferenceEquals(exceptionToThrow, objectDisposedException)) 576 { 577 throw; 578 } 579 else 580 { 581 throw DiagnosticUtility.ExceptionUtility.ThrowHelper(exceptionToThrow, ExceptionEventType); 582 } 583 } 584 } 585 ThrowIfNotOpen()586 void ThrowIfNotOpen() 587 { 588 if (closeState == CloseState.Closing || closeState == CloseState.Closed) 589 { 590 throw DiagnosticUtility.ExceptionUtility.ThrowHelper( 591 ConvertObjectDisposedException(new ObjectDisposedException( 592 this.GetType().ToString(), SR.GetString(SR.SocketConnectionDisposed)), TransferOperation.Undefined), ExceptionEventType); 593 } 594 } 595 ThrowIfClosed()596 void ThrowIfClosed() 597 { 598 if (closeState == CloseState.Closed) 599 { 600 throw DiagnosticUtility.ExceptionUtility.ThrowHelper( 601 ConvertObjectDisposedException(new ObjectDisposedException( 602 this.GetType().ToString(), SR.GetString(SR.SocketConnectionDisposed)), TransferOperation.Undefined), ExceptionEventType); 603 } 604 } 605 TraceSocketInfo(Socket socket, int traceCode, string srString, string timeoutString)606 void TraceSocketInfo(Socket socket, int traceCode, string srString, string timeoutString) 607 { 608 if (DiagnosticUtility.ShouldTraceInformation) 609 { 610 Dictionary<string, string> values = new Dictionary<string, string>(4); 611 values["State"] = this.closeState.ToString(); 612 613 if (timeoutString != null) 614 { 615 values["Timeout"] = timeoutString; 616 } 617 618 if (socket != null && this.closeState != CloseState.Closing) 619 { 620 if (socket.LocalEndPoint != null) 621 { 622 values["LocalEndpoint"] = socket.LocalEndPoint.ToString(); 623 } 624 if (socket.RemoteEndPoint != null) 625 { 626 values["RemoteEndPoint"] = socket.RemoteEndPoint.ToString(); 627 } 628 } 629 TraceUtility.TraceEvent(TraceEventType.Information, traceCode, SR.GetString(srString), new DictionaryTraceRecord(values), this, null); 630 } 631 } 632 TryGetEndpoints(out IPEndPoint localIPEndpoint, out IPEndPoint remoteIPEndpoint)633 bool TryGetEndpoints(out IPEndPoint localIPEndpoint, out IPEndPoint remoteIPEndpoint) 634 { 635 localIPEndpoint = null; 636 remoteIPEndpoint = null; 637 638 if (this.closeState == CloseState.Open) 639 { 640 try 641 { 642 remoteIPEndpoint = this.remoteEndpoint ?? (IPEndPoint)this.socket.RemoteEndPoint; 643 localIPEndpoint = (IPEndPoint)this.socket.LocalEndPoint; 644 } 645 catch (Exception exception) 646 { 647 if (Fx.IsFatal(exception)) 648 { 649 throw; 650 } 651 652 DiagnosticUtility.TraceHandledException(exception, TraceEventType.Warning); 653 } 654 } 655 656 return localIPEndpoint != null && remoteIPEndpoint != null; 657 } 658 DuplicateAndClose(int targetProcessId)659 public object DuplicateAndClose(int targetProcessId) 660 { 661 object result = socket.DuplicateAndClose(targetProcessId); 662 this.Abort(TraceEventType.Information); 663 return result; 664 } 665 GetCoreTransport()666 public object GetCoreTransport() 667 { 668 return socket; 669 } 670 BeginValidate(Uri uri, AsyncCallback callback, object state)671 public IAsyncResult BeginValidate(Uri uri, AsyncCallback callback, object state) 672 { 673 return new CompletedAsyncResult<bool>(true, callback, state); 674 } 675 EndValidate(IAsyncResult result)676 public bool EndValidate(IAsyncResult result) 677 { 678 return CompletedAsyncResult<bool>.End(result); 679 } 680 ConvertSendException(SocketException socketException, TimeSpan remainingTime, TimeSpan timeout)681 Exception ConvertSendException(SocketException socketException, TimeSpan remainingTime, TimeSpan timeout) 682 { 683 return ConvertTransferException(socketException, timeout, socketException, 684 TransferOperation.Write, this.aborted, this.timeoutErrorString, this.timeoutErrorTransferOperation, this, remainingTime); 685 } 686 ConvertReceiveException(SocketException socketException, TimeSpan remainingTime, TimeSpan timeout)687 Exception ConvertReceiveException(SocketException socketException, TimeSpan remainingTime, TimeSpan timeout) 688 { 689 return ConvertTransferException(socketException, timeout, socketException, 690 TransferOperation.Read, this.aborted, this.timeoutErrorString, this.timeoutErrorTransferOperation, this, remainingTime); 691 } 692 ConvertTransferException(SocketException socketException, TimeSpan timeout, Exception originalException)693 internal static Exception ConvertTransferException(SocketException socketException, TimeSpan timeout, Exception originalException) 694 { 695 return ConvertTransferException(socketException, timeout, originalException, 696 TransferOperation.Undefined, false, null, TransferOperation.Undefined, null, TimeSpan.MaxValue); 697 } 698 ConvertObjectDisposedException(ObjectDisposedException originalException, TransferOperation transferOperation)699 Exception ConvertObjectDisposedException(ObjectDisposedException originalException, TransferOperation transferOperation) 700 { 701 if (this.timeoutErrorString != null) 702 { 703 return ConvertTimeoutErrorException(originalException, transferOperation, this.timeoutErrorString, this.timeoutErrorTransferOperation); 704 } 705 else if (this.aborted) 706 { 707 return new CommunicationObjectAbortedException(SR.GetString(SR.SocketConnectionDisposed), originalException); 708 } 709 else 710 { 711 return originalException; 712 } 713 } 714 ConvertTransferException(SocketException socketException, TimeSpan timeout, Exception originalException, TransferOperation transferOperation, bool aborted, string timeoutErrorString, TransferOperation timeoutErrorTransferOperation, SocketConnection socketConnection, TimeSpan remainingTime)715 static Exception ConvertTransferException(SocketException socketException, TimeSpan timeout, Exception originalException, 716 TransferOperation transferOperation, bool aborted, string timeoutErrorString, TransferOperation timeoutErrorTransferOperation, 717 SocketConnection socketConnection, TimeSpan remainingTime) 718 { 719 if (socketException.ErrorCode == UnsafeNativeMethods.ERROR_INVALID_HANDLE) 720 { 721 return new CommunicationObjectAbortedException(socketException.Message, socketException); 722 } 723 724 if (timeoutErrorString != null) 725 { 726 return ConvertTimeoutErrorException(originalException, transferOperation, timeoutErrorString, timeoutErrorTransferOperation); 727 } 728 729 TraceEventType exceptionEventType = socketConnection == null ? TraceEventType.Error : socketConnection.ExceptionEventType; 730 731 // 10053 can occur due to our timeout sockopt firing, so map to TimeoutException in that case 732 if (socketException.ErrorCode == UnsafeNativeMethods.WSAECONNABORTED && 733 remainingTime <= TimeSpan.Zero) 734 { 735 TimeoutException timeoutException = new TimeoutException(SR.GetString(SR.TcpConnectionTimedOut, timeout), originalException); 736 if (TD.TcpConnectionTimedOutIsEnabled()) 737 { 738 if (socketConnection != null) 739 { 740 int socketid = (socketConnection != null && socketConnection.socket != null) ? socketConnection.socket.GetHashCode() : -1; 741 TD.TcpConnectionTimedOut(socketid, socketConnection.RemoteEndpointAddress); 742 } 743 } 744 if (DiagnosticUtility.ShouldTrace(exceptionEventType)) 745 { 746 TraceUtility.TraceEvent(exceptionEventType, TraceCode.TcpConnectionTimedOut, GetEndpointString(SR.TcpConnectionTimedOut, timeout, null, socketConnection), timeoutException, null); 747 } 748 return timeoutException; 749 } 750 751 if (socketException.ErrorCode == UnsafeNativeMethods.WSAENETRESET || 752 socketException.ErrorCode == UnsafeNativeMethods.WSAECONNABORTED || 753 socketException.ErrorCode == UnsafeNativeMethods.WSAECONNRESET) 754 { 755 if (aborted) 756 { 757 return new CommunicationObjectAbortedException(SR.GetString(SR.TcpLocalConnectionAborted), originalException); 758 } 759 else 760 { 761 CommunicationException communicationException = new CommunicationException(SR.GetString(SR.TcpConnectionResetError, timeout), originalException); 762 if (TD.TcpConnectionResetErrorIsEnabled()) 763 { 764 if (socketConnection != null) 765 { 766 int socketId = (socketConnection.socket != null) ? socketConnection.socket.GetHashCode() : -1; 767 TD.TcpConnectionResetError(socketId, socketConnection.RemoteEndpointAddress); 768 } 769 } 770 if (DiagnosticUtility.ShouldTrace(exceptionEventType)) 771 { 772 TraceUtility.TraceEvent(exceptionEventType, TraceCode.TcpConnectionResetError, GetEndpointString(SR.TcpConnectionResetError, timeout, null, socketConnection), communicationException, null); 773 } 774 return communicationException; 775 } 776 } 777 else if (socketException.ErrorCode == UnsafeNativeMethods.WSAETIMEDOUT) 778 { 779 TimeoutException timeoutException = new TimeoutException(SR.GetString(SR.TcpConnectionTimedOut, timeout), originalException); 780 if (DiagnosticUtility.ShouldTrace(exceptionEventType)) 781 { 782 TraceUtility.TraceEvent(exceptionEventType, TraceCode.TcpConnectionTimedOut, GetEndpointString(SR.TcpConnectionTimedOut, timeout, null, socketConnection), timeoutException, null); 783 } 784 return timeoutException; 785 } 786 else 787 { 788 if (aborted) 789 { 790 return new CommunicationObjectAbortedException(SR.GetString(SR.TcpTransferError, socketException.ErrorCode, socketException.Message), originalException); 791 } 792 else 793 { 794 CommunicationException communicationException = new CommunicationException(SR.GetString(SR.TcpTransferError, socketException.ErrorCode, socketException.Message), originalException); 795 if (DiagnosticUtility.ShouldTrace(exceptionEventType)) 796 { 797 TraceUtility.TraceEvent(exceptionEventType, TraceCode.TcpTransferError, GetEndpointString(SR.TcpTransferError, TimeSpan.MinValue, socketException, socketConnection), communicationException, null); 798 } 799 return communicationException; 800 } 801 } 802 } 803 ConvertTimeoutErrorException(Exception originalException, TransferOperation transferOperation, string timeoutErrorString, TransferOperation timeoutErrorTransferOperation)804 static Exception ConvertTimeoutErrorException(Exception originalException, 805 TransferOperation transferOperation, string timeoutErrorString, TransferOperation timeoutErrorTransferOperation) 806 { 807 if (timeoutErrorString == null) 808 { 809 Fx.Assert("Argument timeoutErrorString must not be null."); 810 } 811 812 if (transferOperation == timeoutErrorTransferOperation) 813 { 814 return new TimeoutException(timeoutErrorString, originalException); 815 } 816 else 817 { 818 return new CommunicationException(timeoutErrorString, originalException); 819 } 820 } 821 GetEndpointString(string sr, TimeSpan timeout, SocketException socketException, SocketConnection socketConnection)822 static string GetEndpointString(string sr, TimeSpan timeout, SocketException socketException, SocketConnection socketConnection) 823 { 824 IPEndPoint remoteEndpoint = null; 825 IPEndPoint localEndpoint = null; 826 bool haveEndpoints = socketConnection != null && socketConnection.TryGetEndpoints(out localEndpoint, out remoteEndpoint); 827 828 if (string.Compare(sr, SR.TcpConnectionTimedOut, StringComparison.OrdinalIgnoreCase) == 0) 829 { 830 return haveEndpoints 831 ? SR.GetString(SR.TcpConnectionTimedOutWithIP, timeout, localEndpoint, remoteEndpoint) 832 : SR.GetString(SR.TcpConnectionTimedOut, timeout); 833 } 834 else if (string.Compare(sr, SR.TcpConnectionResetError, StringComparison.OrdinalIgnoreCase) == 0) 835 { 836 return haveEndpoints 837 ? SR.GetString(SR.TcpConnectionResetErrorWithIP, timeout, localEndpoint, remoteEndpoint) 838 : SR.GetString(SR.TcpConnectionResetError, timeout); 839 } 840 else 841 { 842 // sr == SR.TcpTransferError 843 return haveEndpoints 844 ? SR.GetString(SR.TcpTransferErrorWithIP, socketException.ErrorCode, socketException.Message, localEndpoint, remoteEndpoint) 845 : SR.GetString(SR.TcpTransferError, socketException.ErrorCode, socketException.Message); 846 } 847 } 848 BeginWrite(byte[] buffer, int offset, int size, bool immediate, TimeSpan timeout, WaitCallback callback, object state)849 public AsyncCompletionResult BeginWrite(byte[] buffer, int offset, int size, bool immediate, TimeSpan timeout, 850 WaitCallback callback, object state) 851 { 852 ConnectionUtilities.ValidateBufferBounds(buffer, offset, size); 853 bool abortWrite = true; 854 855 try 856 { 857 if (TD.SocketAsyncWriteStartIsEnabled()) 858 { 859 TraceWriteStart(size, true); 860 } 861 862 lock (ThisLock) 863 { 864 Fx.Assert(!this.asyncWritePending, "Called BeginWrite twice."); 865 this.ThrowIfClosed(); 866 this.EnsureWriteEventArgs(); 867 SetImmediate(immediate); 868 SetWriteTimeout(timeout, false); 869 this.SetUserToken(this.asyncWriteEventArgs, this); 870 this.asyncWritePending = true; 871 this.asyncWriteCallback = callback; 872 this.asyncWriteState = state; 873 } 874 875 this.asyncWriteEventArgs.SetBuffer(buffer, offset, size); 876 877 if (socket.SendAsync(this.asyncWriteEventArgs)) 878 { 879 abortWrite = false; 880 return AsyncCompletionResult.Queued; 881 } 882 883 this.HandleSendAsyncCompleted(); 884 abortWrite = false; 885 return AsyncCompletionResult.Completed; 886 } 887 catch (SocketException socketException) 888 { 889 throw DiagnosticUtility.ExceptionUtility.ThrowHelper( 890 ConvertSendException(socketException, TimeSpan.MaxValue, this.asyncSendTimeout), ExceptionEventType); 891 } 892 catch (ObjectDisposedException objectDisposedException) 893 { 894 Exception exceptionToThrow = ConvertObjectDisposedException(objectDisposedException, TransferOperation.Write); 895 if (object.ReferenceEquals(exceptionToThrow, objectDisposedException)) 896 { 897 throw; 898 } 899 else 900 { 901 throw DiagnosticUtility.ExceptionUtility.ThrowHelper(exceptionToThrow, ExceptionEventType); 902 } 903 } 904 finally 905 { 906 if (abortWrite) 907 { 908 this.AbortWrite(); 909 } 910 } 911 } 912 EndWrite()913 public void EndWrite() 914 { 915 if (this.asyncWriteException != null) 916 { 917 this.AbortWrite(); 918 throw DiagnosticUtility.ExceptionUtility.ThrowHelper(this.asyncWriteException, ExceptionEventType); 919 } 920 921 lock (ThisLock) 922 { 923 if (!this.asyncWritePending) 924 { 925 throw Fx.AssertAndThrow("SocketConnection.EndWrite called with no write pending."); 926 } 927 928 this.SetUserToken(this.asyncWriteEventArgs, null); 929 this.asyncWritePending = false; 930 931 if (this.closeState == CloseState.Closed) 932 { 933 this.DisposeWriteEventArgs(); 934 } 935 } 936 } 937 OnSendAsync(object sender, SocketAsyncEventArgs eventArgs)938 void OnSendAsync(object sender, SocketAsyncEventArgs eventArgs) 939 { 940 Fx.Assert(eventArgs != null, "Argument 'eventArgs' cannot be NULL."); 941 this.CancelSendTimer(); 942 943 try 944 { 945 this.HandleSendAsyncCompleted(); 946 Fx.Assert(eventArgs.BytesTransferred == this.asyncWriteEventArgs.Count, "The socket SendAsync did not send all the bytes."); 947 } 948 catch (SocketException socketException) 949 { 950 this.asyncWriteException = ConvertSendException(socketException, TimeSpan.MaxValue, this.asyncSendTimeout); 951 } 952 #pragma warning suppress 56500 // Microsoft, transferring exception to caller 953 catch (Exception exception) 954 { 955 if (Fx.IsFatal(exception)) 956 { 957 throw; 958 } 959 960 this.asyncWriteException = exception; 961 } 962 963 this.FinishWrite(); 964 } 965 HandleSendAsyncCompleted()966 void HandleSendAsyncCompleted() 967 { 968 if (this.asyncWriteEventArgs.SocketError == SocketError.Success) 969 { 970 return; 971 } 972 973 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SocketException((int)this.asyncWriteEventArgs.SocketError)); 974 } 975 976 // This method should be called inside ThisLock DisposeWriteEventArgs()977 void DisposeWriteEventArgs() 978 { 979 if (this.asyncWriteEventArgs != null) 980 { 981 this.asyncWriteEventArgs.Completed -= onSocketSendCompleted; 982 this.asyncWriteEventArgs.Dispose(); 983 } 984 } 985 AbortWrite()986 void AbortWrite() 987 { 988 lock (ThisLock) 989 { 990 if (this.asyncWritePending) 991 { 992 if (this.closeState != CloseState.Closed) 993 { 994 this.SetUserToken(this.asyncWriteEventArgs, null); 995 this.asyncWritePending = false; 996 this.CancelSendTimer(); 997 } 998 else 999 { 1000 this.DisposeWriteEventArgs(); 1001 } 1002 } 1003 } 1004 } 1005 FinishWrite()1006 void FinishWrite() 1007 { 1008 WaitCallback asyncWriteCallback = this.asyncWriteCallback; 1009 object asyncWriteState = this.asyncWriteState; 1010 1011 this.asyncWriteState = null; 1012 this.asyncWriteCallback = null; 1013 1014 asyncWriteCallback(asyncWriteState); 1015 } 1016 Write(byte[] buffer, int offset, int size, bool immediate, TimeSpan timeout)1017 public void Write(byte[] buffer, int offset, int size, bool immediate, TimeSpan timeout) 1018 { 1019 // as per http://support.microsoft.com/default.aspx?scid=kb%3ben-us%3b201213 1020 // we shouldn't write more than 64K synchronously to a socket 1021 const int maxSocketWrite = 64 * 1024; 1022 1023 ConnectionUtilities.ValidateBufferBounds(buffer, offset, size); 1024 1025 TimeoutHelper timeoutHelper = new TimeoutHelper(timeout); 1026 try 1027 { 1028 if (TD.SocketWriteStartIsEnabled()) 1029 { 1030 TraceWriteStart(size, false); 1031 } 1032 1033 SetImmediate(immediate); 1034 int bytesToWrite = size; 1035 1036 while (bytesToWrite > 0) 1037 { 1038 SetWriteTimeout(timeoutHelper.RemainingTime(), true); 1039 size = Math.Min(bytesToWrite, maxSocketWrite); 1040 socket.Send(buffer, offset, size, SocketFlags.None); 1041 bytesToWrite -= size; 1042 offset += size; 1043 timeout = timeoutHelper.RemainingTime(); 1044 } 1045 } 1046 catch (SocketException socketException) 1047 { 1048 throw DiagnosticUtility.ExceptionUtility.ThrowHelper( 1049 ConvertSendException(socketException, timeoutHelper.RemainingTime(), this.socketSyncSendTimeout), ExceptionEventType); 1050 } 1051 catch (ObjectDisposedException objectDisposedException) 1052 { 1053 Exception exceptionToThrow = ConvertObjectDisposedException(objectDisposedException, TransferOperation.Write); 1054 if (object.ReferenceEquals(exceptionToThrow, objectDisposedException)) 1055 { 1056 throw; 1057 } 1058 else 1059 { 1060 throw DiagnosticUtility.ExceptionUtility.ThrowHelper(exceptionToThrow, ExceptionEventType); 1061 } 1062 } 1063 } 1064 TraceWriteStart(int size, bool async)1065 void TraceWriteStart(int size, bool async) 1066 { 1067 if (!async) 1068 { 1069 TD.SocketWriteStart(this.socket.GetHashCode(), size, this.RemoteEndpointAddress); 1070 } 1071 else 1072 { 1073 TD.SocketAsyncWriteStart(this.socket.GetHashCode(), size, this.RemoteEndpointAddress); 1074 } 1075 } 1076 Write(byte[] buffer, int offset, int size, bool immediate, TimeSpan timeout, BufferManager bufferManager)1077 public void Write(byte[] buffer, int offset, int size, bool immediate, TimeSpan timeout, BufferManager bufferManager) 1078 { 1079 try 1080 { 1081 Write(buffer, offset, size, immediate, timeout); 1082 } 1083 finally 1084 { 1085 bufferManager.ReturnBuffer(buffer); 1086 } 1087 } 1088 Read(byte[] buffer, int offset, int size, TimeSpan timeout)1089 public int Read(byte[] buffer, int offset, int size, TimeSpan timeout) 1090 { 1091 ConnectionUtilities.ValidateBufferBounds(buffer, offset, size); 1092 ThrowIfNotOpen(); 1093 return ReadCore(buffer, offset, size, timeout, false); 1094 } 1095 ReadCore(byte[] buffer, int offset, int size, TimeSpan timeout, bool closing)1096 int ReadCore(byte[] buffer, int offset, int size, TimeSpan timeout, bool closing) 1097 { 1098 int bytesRead = 0; 1099 TimeoutHelper timeoutHelper = new TimeoutHelper(timeout); 1100 try 1101 { 1102 SetReadTimeout(timeoutHelper.RemainingTime(), true, closing); 1103 bytesRead = socket.Receive(buffer, offset, size, SocketFlags.None); 1104 1105 if (TD.SocketReadStopIsEnabled()) 1106 { 1107 TraceSocketReadStop(bytesRead, false); 1108 } 1109 } 1110 catch (SocketException socketException) 1111 { 1112 throw DiagnosticUtility.ExceptionUtility.ThrowHelper( 1113 ConvertReceiveException(socketException, timeoutHelper.RemainingTime(), this.socketSyncReceiveTimeout), ExceptionEventType); 1114 } 1115 catch (ObjectDisposedException objectDisposedException) 1116 { 1117 Exception exceptionToThrow = ConvertObjectDisposedException(objectDisposedException, TransferOperation.Read); 1118 if (object.ReferenceEquals(exceptionToThrow, objectDisposedException)) 1119 { 1120 throw; 1121 } 1122 else 1123 { 1124 throw DiagnosticUtility.ExceptionUtility.ThrowHelper(exceptionToThrow, ExceptionEventType); 1125 } 1126 } 1127 1128 return bytesRead; 1129 } 1130 TraceSocketReadStop(int bytesRead, bool async)1131 private void TraceSocketReadStop(int bytesRead, bool async) 1132 { 1133 if (!async) 1134 { 1135 TD.SocketReadStop((this.socket != null) ? this.socket.GetHashCode() : -1, bytesRead, this.RemoteEndpointAddress); 1136 } 1137 else 1138 { 1139 TD.SocketAsyncReadStop((this.socket != null) ? this.socket.GetHashCode() : -1, bytesRead, this.RemoteEndpointAddress); 1140 } 1141 } 1142 BeginRead(int offset, int size, TimeSpan timeout, WaitCallback callback, object state)1143 public virtual AsyncCompletionResult BeginRead(int offset, int size, TimeSpan timeout, 1144 WaitCallback callback, object state) 1145 { 1146 ConnectionUtilities.ValidateBufferBounds(AsyncReadBufferSize, offset, size); 1147 this.ThrowIfNotOpen(); 1148 return this.BeginReadCore(offset, size, timeout, callback, state); 1149 } 1150 BeginReadCore(int offset, int size, TimeSpan timeout, WaitCallback callback, object state)1151 AsyncCompletionResult BeginReadCore(int offset, int size, TimeSpan timeout, 1152 WaitCallback callback, object state) 1153 { 1154 bool abortRead = true; 1155 1156 lock (ThisLock) 1157 { 1158 this.ThrowIfClosed(); 1159 this.EnsureReadEventArgs(); 1160 this.asyncReadState = state; 1161 this.asyncReadCallback = callback; 1162 this.SetUserToken(this.asyncReadEventArgs, this); 1163 this.asyncReadPending = true; 1164 this.SetReadTimeout(timeout, false, false); 1165 } 1166 1167 try 1168 { 1169 if (socket.UseOnlyOverlappedIO) 1170 { 1171 // ReceiveAsync does not respect UseOnlyOverlappedIO but BeginReceive does. 1172 IAsyncResult result = socket.BeginReceive(AsyncReadBuffer, offset, size, SocketFlags.None, onReceiveCompleted, this); 1173 1174 if (!result.CompletedSynchronously) 1175 { 1176 abortRead = false; 1177 return AsyncCompletionResult.Queued; 1178 } 1179 1180 asyncReadSize = socket.EndReceive(result); 1181 } 1182 else 1183 { 1184 if (offset != this.asyncReadEventArgs.Offset || 1185 size != this.asyncReadEventArgs.Count) 1186 { 1187 this.asyncReadEventArgs.SetBuffer(offset, size); 1188 } 1189 1190 if (this.ReceiveAsync()) 1191 { 1192 abortRead = false; 1193 return AsyncCompletionResult.Queued; 1194 } 1195 1196 this.HandleReceiveAsyncCompleted(); 1197 this.asyncReadSize = this.asyncReadEventArgs.BytesTransferred; 1198 } 1199 1200 if (TD.SocketReadStopIsEnabled()) 1201 { 1202 TraceSocketReadStop(asyncReadSize, true); 1203 } 1204 1205 abortRead = false; 1206 return AsyncCompletionResult.Completed; 1207 } 1208 catch (SocketException socketException) 1209 { 1210 throw DiagnosticUtility.ExceptionUtility.ThrowHelper(ConvertReceiveException(socketException, TimeSpan.MaxValue, this.asyncReceiveTimeout), ExceptionEventType); 1211 } 1212 catch (ObjectDisposedException objectDisposedException) 1213 { 1214 Exception exceptionToThrow = ConvertObjectDisposedException(objectDisposedException, TransferOperation.Read); 1215 if (object.ReferenceEquals(exceptionToThrow, objectDisposedException)) 1216 { 1217 throw; 1218 } 1219 else 1220 { 1221 throw DiagnosticUtility.ExceptionUtility.ThrowHelper(exceptionToThrow, ExceptionEventType); 1222 } 1223 } 1224 finally 1225 { 1226 if (abortRead) 1227 { 1228 AbortRead(); 1229 } 1230 } 1231 } 1232 1233 [Fx.Tag.SecurityNote(Critical = "Uses a SecurityCritical method to suppress ExecutionContext flow when running in fullTrust.", 1234 Safe = "Safe because we're only suppressing the ExecutionContext if we're already in full trust.")] 1235 [SecuritySafeCritical] ReceiveAsync()1236 bool ReceiveAsync() 1237 { 1238 if (!PartialTrustHelpers.ShouldFlowSecurityContext) 1239 { 1240 if (!ExecutionContext.IsFlowSuppressed()) 1241 { 1242 return ReceiveAsyncNoFlow(); 1243 } 1244 } 1245 1246 return this.socket.ReceiveAsync(this.asyncReadEventArgs); 1247 } 1248 1249 [Fx.Tag.SecurityNote(Critical = "Suppresses execution context flow and restores it after invocation. Fulltrust async callbacks " + 1250 "will not have an ExecutionContext, LogicalCallcontext or SecurityContext and should not take dependency on them.")] 1251 [SecurityCritical] ReceiveAsyncNoFlow()1252 bool ReceiveAsyncNoFlow() 1253 { 1254 using (ExecutionContext.SuppressFlow()) 1255 { 1256 return this.socket.ReceiveAsync(this.asyncReadEventArgs); 1257 } 1258 } 1259 OnReceive(IAsyncResult result)1260 void OnReceive(IAsyncResult result) 1261 { 1262 this.CancelReceiveTimer(); 1263 if (result.CompletedSynchronously) 1264 { 1265 return; 1266 } 1267 1268 try 1269 { 1270 this.asyncReadSize = socket.EndReceive(result); 1271 1272 if (TD.SocketReadStopIsEnabled()) 1273 { 1274 TraceSocketReadStop(this.asyncReadSize, true); 1275 } 1276 } 1277 catch (SocketException socketException) 1278 { 1279 this.asyncReadException = ConvertReceiveException(socketException, TimeSpan.MaxValue, this.asyncReceiveTimeout); 1280 } 1281 catch (ObjectDisposedException objectDisposedException) 1282 { 1283 this.asyncReadException = ConvertObjectDisposedException(objectDisposedException, TransferOperation.Read); 1284 } 1285 #pragma warning suppress 56500 // Microsoft, transferring exception to caller 1286 catch (Exception exception) 1287 { 1288 if (Fx.IsFatal(exception)) 1289 { 1290 throw; 1291 } 1292 this.asyncReadException = exception; 1293 } 1294 1295 this.FinishRead(); 1296 } 1297 OnReceiveAsync(object sender, SocketAsyncEventArgs eventArgs)1298 void OnReceiveAsync(object sender, SocketAsyncEventArgs eventArgs) 1299 { 1300 Fx.Assert(eventArgs != null, "Argument 'eventArgs' cannot be NULL."); 1301 this.CancelReceiveTimer(); 1302 1303 try 1304 { 1305 this.HandleReceiveAsyncCompleted(); 1306 this.asyncReadSize = eventArgs.BytesTransferred; 1307 1308 if (TD.SocketReadStopIsEnabled()) 1309 { 1310 TraceSocketReadStop(asyncReadSize, true); 1311 } 1312 } 1313 catch (SocketException socketException) 1314 { 1315 asyncReadException = ConvertReceiveException(socketException, TimeSpan.MaxValue, this.asyncReceiveTimeout); 1316 } 1317 #pragma warning suppress 56500 // Microsoft, transferring exception to caller 1318 catch (Exception exception) 1319 { 1320 if (Fx.IsFatal(exception)) 1321 { 1322 throw; 1323 } 1324 asyncReadException = exception; 1325 } 1326 1327 FinishRead(); 1328 } 1329 HandleReceiveAsyncCompleted()1330 void HandleReceiveAsyncCompleted() 1331 { 1332 if (this.asyncReadEventArgs.SocketError == SocketError.Success) 1333 { 1334 return; 1335 } 1336 1337 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SocketException((int)this.asyncReadEventArgs.SocketError)); 1338 } 1339 FinishRead()1340 void FinishRead() 1341 { 1342 WaitCallback asyncReadCallback = this.asyncReadCallback; 1343 object asyncReadState = this.asyncReadState; 1344 1345 this.asyncReadState = null; 1346 this.asyncReadCallback = null; 1347 1348 asyncReadCallback(asyncReadState); 1349 } 1350 1351 // Both BeginRead/ReadAsync paths completed themselves. EndRead's only job is to deliver the result. EndRead()1352 public int EndRead() 1353 { 1354 if (this.asyncReadException != null) 1355 { 1356 AbortRead(); 1357 throw DiagnosticUtility.ExceptionUtility.ThrowHelper(this.asyncReadException, ExceptionEventType); 1358 } 1359 1360 lock (ThisLock) 1361 { 1362 if (!this.asyncReadPending) 1363 { 1364 throw Fx.AssertAndThrow("SocketConnection.EndRead called with no read pending."); 1365 } 1366 1367 this.SetUserToken(this.asyncReadEventArgs, null); 1368 this.asyncReadPending = false; 1369 1370 if (closeState == CloseState.Closed) 1371 { 1372 this.DisposeReadEventArgs(); 1373 } 1374 } 1375 1376 return this.asyncReadSize; 1377 } 1378 1379 // This method should be called inside ThisLock DisposeReadEventArgs()1380 void DisposeReadEventArgs() 1381 { 1382 if (this.asyncReadEventArgs != null) 1383 { 1384 this.asyncReadEventArgs.Completed -= onReceiveAsyncCompleted; 1385 this.asyncReadEventArgs.Dispose(); 1386 } 1387 1388 // We release the buffer only if there is no outstanding I/O 1389 this.TryReturnReadBuffer(); 1390 } 1391 TryReturnReadBuffer()1392 void TryReturnReadBuffer() 1393 { 1394 // The buffer must not be returned and nulled when an abort occurs. Since the buffer 1395 // is also accessed by higher layers, code that has not yet realized the stack is 1396 // aborted may be attempting to read from the buffer. 1397 if (this.readBuffer != null && !this.aborted) 1398 { 1399 this.connectionBufferPool.Return(this.readBuffer); 1400 this.readBuffer = null; 1401 } 1402 } 1403 SetUserToken(SocketAsyncEventArgs args, object userToken)1404 void SetUserToken(SocketAsyncEventArgs args, object userToken) 1405 { 1406 // The socket args can be pinned by the overlapped callback. Ensure SocketConnection is 1407 // only pinned when there is outstanding IO. 1408 if (args != null) 1409 { 1410 args.UserToken = userToken; 1411 } 1412 } 1413 SetImmediate(bool immediate)1414 void SetImmediate(bool immediate) 1415 { 1416 if (immediate != this.noDelay) 1417 { 1418 lock (ThisLock) 1419 { 1420 ThrowIfNotOpen(); 1421 socket.NoDelay = immediate; 1422 } 1423 this.noDelay = immediate; 1424 } 1425 } 1426 SetReadTimeout(TimeSpan timeout, bool synchronous, bool closing)1427 void SetReadTimeout(TimeSpan timeout, bool synchronous, bool closing) 1428 { 1429 if (synchronous) 1430 { 1431 CancelReceiveTimer(); 1432 1433 // 0 == infinite for winsock timeouts, so we should preempt and throw 1434 if (timeout <= TimeSpan.Zero) 1435 { 1436 throw DiagnosticUtility.ExceptionUtility.ThrowHelper( 1437 new TimeoutException(SR.GetString(SR.TcpConnectionTimedOut, timeout)), ExceptionEventType); 1438 } 1439 1440 if (ShouldUpdateTimeout(this.socketSyncReceiveTimeout, timeout)) 1441 { 1442 lock (ThisLock) 1443 { 1444 if (!closing || this.closeState != CloseState.Closing) 1445 { 1446 ThrowIfNotOpen(); 1447 } 1448 this.socket.ReceiveTimeout = TimeoutHelper.ToMilliseconds(timeout); 1449 } 1450 this.socketSyncReceiveTimeout = timeout; 1451 } 1452 } 1453 else 1454 { 1455 this.asyncReceiveTimeout = timeout; 1456 if (timeout == TimeSpan.MaxValue) 1457 { 1458 CancelReceiveTimer(); 1459 } 1460 else 1461 { 1462 ReceiveTimer.Set(timeout); 1463 } 1464 } 1465 } 1466 SetWriteTimeout(TimeSpan timeout, bool synchronous)1467 void SetWriteTimeout(TimeSpan timeout, bool synchronous) 1468 { 1469 if (synchronous) 1470 { 1471 CancelSendTimer(); 1472 1473 // 0 == infinite for winsock timeouts, so we should preempt and throw 1474 if (timeout <= TimeSpan.Zero) 1475 { 1476 throw DiagnosticUtility.ExceptionUtility.ThrowHelper( 1477 new TimeoutException(SR.GetString(SR.TcpConnectionTimedOut, timeout)), ExceptionEventType); 1478 } 1479 1480 if (ShouldUpdateTimeout(this.socketSyncSendTimeout, timeout)) 1481 { 1482 lock (ThisLock) 1483 { 1484 ThrowIfNotOpen(); 1485 this.socket.SendTimeout = TimeoutHelper.ToMilliseconds(timeout); 1486 } 1487 this.socketSyncSendTimeout = timeout; 1488 } 1489 } 1490 else 1491 { 1492 this.asyncSendTimeout = timeout; 1493 if (timeout == TimeSpan.MaxValue) 1494 { 1495 CancelSendTimer(); 1496 } 1497 else 1498 { 1499 SendTimer.Set(timeout); 1500 } 1501 } 1502 } 1503 ShouldUpdateTimeout(TimeSpan oldTimeout, TimeSpan newTimeout)1504 bool ShouldUpdateTimeout(TimeSpan oldTimeout, TimeSpan newTimeout) 1505 { 1506 if (oldTimeout == newTimeout) 1507 { 1508 return false; 1509 } 1510 1511 long threshold = oldTimeout.Ticks / 10; 1512 long delta = Math.Max(oldTimeout.Ticks, newTimeout.Ticks) - Math.Min(oldTimeout.Ticks, newTimeout.Ticks); 1513 1514 return delta > threshold; 1515 } 1516 1517 // This method should be called inside ThisLock EnsureReadEventArgs()1518 void EnsureReadEventArgs() 1519 { 1520 if (this.asyncReadEventArgs == null) 1521 { 1522 // Init ReadAsync state 1523 if (onReceiveAsyncCompleted == null) 1524 { 1525 onReceiveAsyncCompleted = new EventHandler<SocketAsyncEventArgs>(OnReceiveAsyncCompleted); 1526 } 1527 1528 this.asyncReadEventArgs = new SocketAsyncEventArgs(); 1529 this.asyncReadEventArgs.SetBuffer(this.readBuffer, 0, this.readBuffer.Length); 1530 this.asyncReadEventArgs.Completed += onReceiveAsyncCompleted; 1531 } 1532 } 1533 1534 // This method should be called inside ThisLock EnsureWriteEventArgs()1535 void EnsureWriteEventArgs() 1536 { 1537 if (this.asyncWriteEventArgs == null) 1538 { 1539 // Init SendAsync state 1540 if (onSocketSendCompleted == null) 1541 { 1542 onSocketSendCompleted = new EventHandler<SocketAsyncEventArgs>(OnSendAsyncCompleted); 1543 } 1544 1545 this.asyncWriteEventArgs = new SocketAsyncEventArgs(); 1546 this.asyncWriteEventArgs.Completed += onSocketSendCompleted; 1547 } 1548 } 1549 1550 enum CloseState 1551 { 1552 Open, 1553 Closing, 1554 Closed, 1555 } 1556 1557 enum TransferOperation 1558 { 1559 Write, 1560 Read, 1561 Undefined, 1562 } 1563 } 1564 1565 class SocketConnectionInitiator : IConnectionInitiator 1566 { 1567 int bufferSize; 1568 ConnectionBufferPool connectionBufferPool; 1569 SocketConnectionInitiator(int bufferSize)1570 public SocketConnectionInitiator(int bufferSize) 1571 { 1572 this.bufferSize = bufferSize; 1573 this.connectionBufferPool = new ConnectionBufferPool(bufferSize); 1574 } 1575 CreateConnection(Socket socket)1576 IConnection CreateConnection(Socket socket) 1577 { 1578 return new SocketConnection(socket, this.connectionBufferPool, false); 1579 } 1580 ConvertConnectException(SocketException socketException, Uri remoteUri, TimeSpan timeSpent, Exception innerException)1581 public static Exception ConvertConnectException(SocketException socketException, Uri remoteUri, TimeSpan timeSpent, Exception innerException) 1582 { 1583 if (socketException.ErrorCode == UnsafeNativeMethods.ERROR_INVALID_HANDLE) 1584 { 1585 return new CommunicationObjectAbortedException(socketException.Message, socketException); 1586 } 1587 1588 if (socketException.ErrorCode == UnsafeNativeMethods.WSAEADDRNOTAVAIL || 1589 socketException.ErrorCode == UnsafeNativeMethods.WSAECONNREFUSED || 1590 socketException.ErrorCode == UnsafeNativeMethods.WSAENETDOWN || 1591 socketException.ErrorCode == UnsafeNativeMethods.WSAENETUNREACH || 1592 socketException.ErrorCode == UnsafeNativeMethods.WSAEHOSTDOWN || 1593 socketException.ErrorCode == UnsafeNativeMethods.WSAEHOSTUNREACH || 1594 socketException.ErrorCode == UnsafeNativeMethods.WSAETIMEDOUT) 1595 { 1596 if (timeSpent == TimeSpan.MaxValue) 1597 { 1598 return new EndpointNotFoundException(SR.GetString(SR.TcpConnectError, remoteUri.AbsoluteUri, socketException.ErrorCode, socketException.Message), innerException); 1599 } 1600 else 1601 { 1602 return new EndpointNotFoundException(SR.GetString(SR.TcpConnectErrorWithTimeSpan, remoteUri.AbsoluteUri, socketException.ErrorCode, socketException.Message, timeSpent), innerException); 1603 } 1604 } 1605 else if (socketException.ErrorCode == UnsafeNativeMethods.WSAENOBUFS) 1606 { 1607 return new InsufficientMemoryException(SR.GetString(SR.TcpConnectNoBufs), innerException); 1608 } 1609 else if (socketException.ErrorCode == UnsafeNativeMethods.ERROR_NOT_ENOUGH_MEMORY || 1610 socketException.ErrorCode == UnsafeNativeMethods.ERROR_NO_SYSTEM_RESOURCES || 1611 socketException.ErrorCode == UnsafeNativeMethods.ERROR_OUTOFMEMORY) 1612 { 1613 return new InsufficientMemoryException(SR.GetString(SR.InsufficentMemory), socketException); 1614 } 1615 else 1616 { 1617 if (timeSpent == TimeSpan.MaxValue) 1618 { 1619 return new CommunicationException(SR.GetString(SR.TcpConnectError, remoteUri.AbsoluteUri, socketException.ErrorCode, socketException.Message), innerException); 1620 } 1621 else 1622 { 1623 return new CommunicationException(SR.GetString(SR.TcpConnectErrorWithTimeSpan, remoteUri.AbsoluteUri, socketException.ErrorCode, socketException.Message, timeSpent), innerException); 1624 } 1625 } 1626 } 1627 GetIPAddresses(Uri uri)1628 static IPAddress[] GetIPAddresses(Uri uri) 1629 { 1630 if (uri.HostNameType == UriHostNameType.IPv4 || 1631 uri.HostNameType == UriHostNameType.IPv6) 1632 { 1633 IPAddress ipAddress = IPAddress.Parse(uri.DnsSafeHost); 1634 return new IPAddress[] { ipAddress }; 1635 } 1636 1637 IPHostEntry hostEntry = null; 1638 1639 try 1640 { 1641 hostEntry = DnsCache.Resolve(uri); 1642 } 1643 catch (SocketException socketException) 1644 { 1645 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 1646 new EndpointNotFoundException(SR.GetString(SR.UnableToResolveHost, uri.Host), socketException)); 1647 } 1648 1649 if (hostEntry.AddressList.Length == 0) 1650 { 1651 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 1652 new EndpointNotFoundException(SR.GetString(SR.UnableToResolveHost, uri.Host))); 1653 } 1654 1655 return hostEntry.AddressList; 1656 } 1657 CreateTimeoutException(Uri uri, TimeSpan timeout, IPAddress[] addresses, int invalidAddressCount, SocketException innerException)1658 static TimeoutException CreateTimeoutException(Uri uri, TimeSpan timeout, IPAddress[] addresses, int invalidAddressCount, 1659 SocketException innerException) 1660 { 1661 StringBuilder addressStringBuilder = new StringBuilder(); 1662 for (int i = 0; i < invalidAddressCount; i++) 1663 { 1664 if (addresses[i] == null) 1665 { 1666 continue; 1667 } 1668 1669 if (addressStringBuilder.Length > 0) 1670 { 1671 addressStringBuilder.Append(", "); 1672 } 1673 addressStringBuilder.Append(addresses[i].ToString()); 1674 } 1675 1676 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new TimeoutException( 1677 SR.GetString(SR.TcpConnectingToViaTimedOut, uri.AbsoluteUri, timeout.ToString(), 1678 invalidAddressCount, addresses.Length, addressStringBuilder.ToString()), innerException)); 1679 } 1680 Connect(Uri uri, TimeSpan timeout)1681 public IConnection Connect(Uri uri, TimeSpan timeout) 1682 { 1683 if (DiagnosticUtility.ShouldTraceInformation) 1684 { 1685 TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.InitiatingTcpConnection, 1686 SR.GetString(SR.TraceCodeInitiatingTcpConnection), 1687 new StringTraceRecord("Uri", uri.ToString()), this, null); 1688 } 1689 1690 int port = uri.Port; 1691 IPAddress[] addresses = SocketConnectionInitiator.GetIPAddresses(uri); 1692 Socket socket = null; 1693 SocketException lastException = null; 1694 1695 if (port == -1) 1696 { 1697 port = TcpUri.DefaultPort; 1698 } 1699 1700 int invalidAddressCount = 0; 1701 TimeoutHelper timeoutHelper = new TimeoutHelper(timeout); 1702 for (int i = 0; i < addresses.Length; i++) 1703 { 1704 if (timeoutHelper.RemainingTime() == TimeSpan.Zero) 1705 { 1706 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 1707 CreateTimeoutException(uri, timeoutHelper.OriginalTimeout, addresses, invalidAddressCount, lastException)); 1708 } 1709 1710 AddressFamily addressFamily = addresses[i].AddressFamily; 1711 1712 if (addressFamily == AddressFamily.InterNetworkV6 && !Socket.OSSupportsIPv6) 1713 { 1714 addresses[i] = null; // disregard for exception attempt purposes 1715 continue; 1716 } 1717 1718 DateTime connectStartTime = DateTime.UtcNow; 1719 try 1720 { 1721 socket = new Socket(addressFamily, SocketType.Stream, ProtocolType.Tcp); 1722 socket.Connect(new IPEndPoint(addresses[i], port)); 1723 lastException = null; 1724 break; 1725 } 1726 catch (SocketException socketException) 1727 { 1728 invalidAddressCount++; 1729 SocketConnectionInitiator.TraceConnectFailure(socket, socketException, uri, DateTime.UtcNow - connectStartTime); 1730 lastException = socketException; 1731 socket.Close(); 1732 } 1733 } 1734 1735 if (socket == null) 1736 { 1737 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 1738 new EndpointNotFoundException(SR.GetString(SR.NoIPEndpointsFoundForHost, uri.Host))); 1739 } 1740 1741 if (lastException != null) 1742 { 1743 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 1744 SocketConnectionInitiator.ConvertConnectException(lastException, uri, 1745 timeoutHelper.ElapsedTime(), lastException)); 1746 } 1747 1748 return CreateConnection(socket); 1749 } 1750 BeginConnect(Uri uri, TimeSpan timeout, AsyncCallback callback, object state)1751 public IAsyncResult BeginConnect(Uri uri, TimeSpan timeout, AsyncCallback callback, object state) 1752 { 1753 if (DiagnosticUtility.ShouldTraceInformation) 1754 { 1755 TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.InitiatingTcpConnection, 1756 SR.GetString(SR.TraceCodeInitiatingTcpConnection), 1757 new StringTraceRecord("Uri", uri.ToString()), this, null); 1758 } 1759 return new ConnectAsyncResult(uri, timeout, callback, state); 1760 } 1761 EndConnect(IAsyncResult result)1762 public IConnection EndConnect(IAsyncResult result) 1763 { 1764 Socket socket = ConnectAsyncResult.End(result); 1765 return CreateConnection(socket); 1766 } 1767 TraceConnectFailure(Socket socket, SocketException socketException, Uri remoteUri, TimeSpan timeSpentInConnect)1768 public static void TraceConnectFailure(Socket socket, SocketException socketException, Uri remoteUri, 1769 TimeSpan timeSpentInConnect) 1770 { 1771 if (DiagnosticUtility.ShouldTraceWarning) 1772 { 1773 Exception traceException = ConvertConnectException(socketException, remoteUri, timeSpentInConnect, socketException); 1774 TraceUtility.TraceEvent(TraceEventType.Warning, TraceCode.TcpConnectError, 1775 SR.GetString(SR.TraceCodeTcpConnectError), socket, traceException); 1776 } 1777 } 1778 1779 class ConnectAsyncResult : AsyncResult 1780 { 1781 IPAddress[] addresses; 1782 int currentIndex; 1783 int port; 1784 SocketException lastException; 1785 TimeSpan timeout; 1786 TimeoutHelper timeoutHelper; 1787 int invalidAddressCount; 1788 DateTime connectStartTime; 1789 Socket socket; 1790 Uri uri; 1791 static Action<object> startConnectCallback; 1792 static AsyncCallback onConnect = Fx.ThunkCallback(new AsyncCallback(OnConnect)); 1793 ConnectAsyncResult(Uri uri, TimeSpan timeout, AsyncCallback callback, object state)1794 public ConnectAsyncResult(Uri uri, TimeSpan timeout, AsyncCallback callback, object state) 1795 : base(callback, state) 1796 { 1797 this.uri = uri; 1798 addresses = SocketConnectionInitiator.GetIPAddresses(uri); 1799 port = uri.Port; 1800 if (port == -1) 1801 { 1802 port = TcpUri.DefaultPort; 1803 } 1804 1805 currentIndex = 0; 1806 this.timeout = timeout; 1807 this.timeoutHelper = new TimeoutHelper(timeout); 1808 1809 if (Thread.CurrentThread.IsThreadPoolThread) 1810 { 1811 if (StartConnect()) 1812 { 1813 base.Complete(true); 1814 } 1815 } 1816 else 1817 { 1818 // If we're not on a threadpool thread, then we need to post a callback to start our accepting loop 1819 // Otherwise if the calling thread aborts then the async I/O will get inadvertantly cancelled 1820 if (startConnectCallback == null) 1821 { 1822 startConnectCallback = StartConnectCallback; 1823 } 1824 1825 ActionItem.Schedule(startConnectCallback, this); 1826 } 1827 } 1828 StartConnectCallback(object state)1829 static void StartConnectCallback(object state) 1830 { 1831 ConnectAsyncResult connectAsyncResult = (ConnectAsyncResult)state; 1832 bool completeSelf = false; 1833 Exception completionException = null; 1834 try 1835 { 1836 completeSelf = connectAsyncResult.StartConnect(); 1837 } 1838 #pragma warning suppress 56500 // covered by FxCOP 1839 catch (Exception e) 1840 { 1841 if (Fx.IsFatal(e)) 1842 { 1843 throw; 1844 } 1845 completeSelf = true; 1846 completionException = e; 1847 } 1848 1849 if (completeSelf) 1850 { 1851 connectAsyncResult.Complete(false, completionException); 1852 } 1853 } 1854 StartConnect()1855 bool StartConnect() 1856 { 1857 while (currentIndex < addresses.Length) 1858 { 1859 if (timeoutHelper.RemainingTime() == TimeSpan.Zero) 1860 { 1861 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 1862 CreateTimeoutException(uri, timeoutHelper.OriginalTimeout, addresses, invalidAddressCount, lastException)); 1863 } 1864 1865 AddressFamily addressFamily = addresses[currentIndex].AddressFamily; 1866 1867 if (addressFamily == AddressFamily.InterNetworkV6 && !Socket.OSSupportsIPv6) 1868 { 1869 addresses[currentIndex++] = null; // disregard for exception attempt purposes 1870 continue; 1871 } 1872 1873 this.connectStartTime = DateTime.UtcNow; 1874 try 1875 { 1876 IPEndPoint ipEndPoint = new IPEndPoint(addresses[currentIndex], port); 1877 this.socket = new Socket(addressFamily, SocketType.Stream, ProtocolType.Tcp); 1878 IAsyncResult result = socket.BeginConnect(ipEndPoint, onConnect, this); 1879 if (!result.CompletedSynchronously) 1880 { 1881 return false; 1882 } 1883 1884 socket.EndConnect(result); 1885 return true; 1886 } 1887 catch (SocketException socketException) 1888 { 1889 invalidAddressCount++; 1890 this.TraceConnectFailure(socketException); 1891 lastException = socketException; 1892 currentIndex++; 1893 } 1894 } 1895 1896 if (socket == null) 1897 { 1898 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 1899 new EndpointNotFoundException(SR.GetString(SR.NoIPEndpointsFoundForHost, uri.Host))); 1900 } 1901 1902 Fx.Assert(lastException != null, "StartConnect: Can't get here without an exception."); 1903 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 1904 SocketConnectionInitiator.ConvertConnectException(lastException, uri, 1905 timeoutHelper.ElapsedTime(), lastException)); 1906 } 1907 TraceConnectFailure(SocketException exception)1908 void TraceConnectFailure(SocketException exception) 1909 { 1910 SocketConnectionInitiator.TraceConnectFailure(this.socket, exception, uri, DateTime.UtcNow - connectStartTime); 1911 this.socket.Close(); 1912 } 1913 OnConnect(IAsyncResult result)1914 static void OnConnect(IAsyncResult result) 1915 { 1916 if (result.CompletedSynchronously) 1917 { 1918 return; 1919 } 1920 1921 bool completeSelf = false; 1922 Exception completionException = null; 1923 ConnectAsyncResult thisPtr = (ConnectAsyncResult)result.AsyncState; 1924 try 1925 { 1926 thisPtr.socket.EndConnect(result); 1927 completeSelf = true; 1928 } 1929 catch (SocketException socketException) 1930 { 1931 thisPtr.TraceConnectFailure(socketException); 1932 thisPtr.lastException = socketException; 1933 thisPtr.currentIndex++; 1934 try 1935 { 1936 completeSelf = thisPtr.StartConnect(); 1937 } 1938 #pragma warning suppress 56500 // Microsoft, transferring exception to another thread 1939 catch (Exception e) 1940 { 1941 if (Fx.IsFatal(e)) 1942 { 1943 throw; 1944 } 1945 completeSelf = true; 1946 completionException = e; 1947 } 1948 } 1949 1950 if (completeSelf) 1951 { 1952 thisPtr.Complete(false, completionException); 1953 } 1954 } 1955 End(IAsyncResult result)1956 public static Socket End(IAsyncResult result) 1957 { 1958 ConnectAsyncResult thisPtr = AsyncResult.End<ConnectAsyncResult>(result); 1959 return thisPtr.socket; 1960 } 1961 } 1962 } 1963 1964 internal interface ISocketListenerSettings 1965 { 1966 int BufferSize { get; } 1967 bool TeredoEnabled { get; } 1968 int ListenBacklog { get; } 1969 } 1970 1971 class SocketConnectionListener : IConnectionListener 1972 { 1973 IPEndPoint localEndpoint; 1974 bool isDisposed; 1975 bool isListening; 1976 Socket listenSocket; 1977 ISocketListenerSettings settings; 1978 bool useOnlyOverlappedIO; 1979 ConnectionBufferPool connectionBufferPool; 1980 SocketAsyncEventArgsPool socketAsyncEventArgsPool; 1981 SocketConnectionListener(Socket listenSocket, ISocketListenerSettings settings, bool useOnlyOverlappedIO)1982 public SocketConnectionListener(Socket listenSocket, ISocketListenerSettings settings, bool useOnlyOverlappedIO) 1983 : this(settings, useOnlyOverlappedIO) 1984 { 1985 this.listenSocket = listenSocket; 1986 } 1987 SocketConnectionListener(IPEndPoint localEndpoint, ISocketListenerSettings settings, bool useOnlyOverlappedIO)1988 public SocketConnectionListener(IPEndPoint localEndpoint, ISocketListenerSettings settings, bool useOnlyOverlappedIO) 1989 : this(settings, useOnlyOverlappedIO) 1990 { 1991 this.localEndpoint = localEndpoint; 1992 } 1993 SocketConnectionListener(ISocketListenerSettings settings, bool useOnlyOverlappedIO)1994 SocketConnectionListener(ISocketListenerSettings settings, bool useOnlyOverlappedIO) 1995 { 1996 Fx.Assert(settings != null, "Input settings should not be null"); 1997 this.settings = settings; 1998 this.useOnlyOverlappedIO = useOnlyOverlappedIO; 1999 this.connectionBufferPool = new ConnectionBufferPool(settings.BufferSize); 2000 } 2001 2002 object ThisLock 2003 { 2004 get { return this; } 2005 } 2006 BeginAccept(AsyncCallback callback, object state)2007 public IAsyncResult BeginAccept(AsyncCallback callback, object state) 2008 { 2009 return new AcceptAsyncResult(this, callback, state); 2010 } 2011 TakeSocketAsyncEventArgs()2012 SocketAsyncEventArgs TakeSocketAsyncEventArgs() 2013 { 2014 return this.socketAsyncEventArgsPool.Take(); 2015 } 2016 ReturnSocketAsyncEventArgs(SocketAsyncEventArgs socketAsyncEventArgs)2017 void ReturnSocketAsyncEventArgs(SocketAsyncEventArgs socketAsyncEventArgs) 2018 { 2019 Fx.Assert(socketAsyncEventArgsPool != null, "The socketAsyncEventArgsPool should not be null"); 2020 this.socketAsyncEventArgsPool.Return(socketAsyncEventArgs); 2021 } 2022 2023 // This is the buffer size that is used by the System.Net for accepting new connections GetAcceptBufferSize(Socket listenSocket)2024 static int GetAcceptBufferSize(Socket listenSocket) 2025 { 2026 return (listenSocket.LocalEndPoint.Serialize().Size + 16) * 2; 2027 } 2028 InternalBeginAccept(Func<Socket, bool> acceptAsyncFunc)2029 bool InternalBeginAccept(Func<Socket, bool> acceptAsyncFunc) 2030 { 2031 lock (ThisLock) 2032 { 2033 if (isDisposed) 2034 { 2035 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ObjectDisposedException(this.GetType().ToString(), SR.GetString(SR.SocketListenerDisposed))); 2036 } 2037 2038 if (!isListening) 2039 { 2040 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SocketListenerNotListening))); 2041 } 2042 2043 return acceptAsyncFunc(listenSocket); 2044 } 2045 } 2046 EndAccept(IAsyncResult result)2047 public IConnection EndAccept(IAsyncResult result) 2048 { 2049 Socket socket = AcceptAsyncResult.End(result); 2050 2051 if (socket == null) 2052 return null; 2053 2054 if (useOnlyOverlappedIO) 2055 { 2056 socket.UseOnlyOverlappedIO = true; 2057 } 2058 return new SocketConnection(socket, this.connectionBufferPool, false); 2059 } 2060 Dispose()2061 public void Dispose() 2062 { 2063 lock (ThisLock) 2064 { 2065 if (!isDisposed) 2066 { 2067 if (listenSocket != null) 2068 { 2069 listenSocket.Close(); 2070 } 2071 2072 if (this.socketAsyncEventArgsPool != null) 2073 { 2074 this.socketAsyncEventArgsPool.Close(); 2075 } 2076 2077 isDisposed = true; 2078 } 2079 } 2080 } 2081 2082 Listen()2083 public void Listen() 2084 { 2085 // If you call listen() on a port, then kill the process, then immediately start a new process and 2086 // try to listen() on the same port, you sometimes get WSAEADDRINUSE. Even if nothing was accepted. 2087 // Ports don't immediately free themselves on process shutdown. We call listen() in a loop on a delay 2088 // for a few iterations for this reason. 2089 // 2090 TimeSpan listenTimeout = TimeSpan.FromSeconds(1); 2091 BackoffTimeoutHelper backoffHelper = new BackoffTimeoutHelper(listenTimeout); 2092 2093 lock (ThisLock) 2094 { 2095 if (this.listenSocket != null) 2096 { 2097 this.listenSocket.Listen(settings.ListenBacklog); 2098 isListening = true; 2099 } 2100 2101 while (!isListening) 2102 { 2103 try 2104 { 2105 this.listenSocket = new Socket(localEndpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); 2106 2107 if (localEndpoint.AddressFamily == AddressFamily.InterNetworkV6 && settings.TeredoEnabled) 2108 { 2109 this.listenSocket.SetSocketOption(SocketOptionLevel.IPv6, (SocketOptionName)23, 10); 2110 } 2111 2112 this.listenSocket.Bind(localEndpoint); 2113 this.listenSocket.Listen(settings.ListenBacklog); 2114 isListening = true; 2115 } 2116 catch (SocketException socketException) 2117 { 2118 bool retry = false; 2119 2120 if (socketException.ErrorCode == UnsafeNativeMethods.WSAEADDRINUSE) 2121 { 2122 if (!backoffHelper.IsExpired()) 2123 { 2124 backoffHelper.WaitAndBackoff(); 2125 retry = true; 2126 } 2127 } 2128 2129 if (!retry) 2130 { 2131 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 2132 SocketConnectionListener.ConvertListenException(socketException, this.localEndpoint)); 2133 } 2134 } 2135 } 2136 2137 this.socketAsyncEventArgsPool = new SocketAsyncEventArgsPool(GetAcceptBufferSize(this.listenSocket)); 2138 } 2139 } 2140 ConvertListenException(SocketException socketException, IPEndPoint localEndpoint)2141 public static Exception ConvertListenException(SocketException socketException, IPEndPoint localEndpoint) 2142 { 2143 if (socketException.ErrorCode == UnsafeNativeMethods.ERROR_INVALID_HANDLE) 2144 { 2145 return new CommunicationObjectAbortedException(socketException.Message, socketException); 2146 } 2147 if (socketException.ErrorCode == UnsafeNativeMethods.WSAEADDRINUSE) 2148 { 2149 return new AddressAlreadyInUseException(SR.GetString(SR.TcpAddressInUse, localEndpoint.ToString()), socketException); 2150 } 2151 else 2152 { 2153 return new CommunicationException( 2154 SR.GetString(SR.TcpListenError, socketException.ErrorCode, socketException.Message, localEndpoint.ToString()), 2155 socketException); 2156 } 2157 } 2158 2159 class AcceptAsyncResult : AsyncResult 2160 { 2161 SocketConnectionListener listener; 2162 Socket socket; 2163 SocketAsyncEventArgs socketAsyncEventArgs; 2164 static Action<object> startAccept; 2165 EventTraceActivity eventTraceActivity; 2166 2167 // 2168 static EventHandler<SocketAsyncEventArgs> acceptAsyncCompleted = new EventHandler<SocketAsyncEventArgs>(AcceptAsyncCompleted); 2169 static Action<AsyncResult, Exception> onCompleting = new Action<AsyncResult, Exception>(OnInternalCompleting); 2170 AcceptAsyncResult(SocketConnectionListener listener, AsyncCallback callback, object state)2171 public AcceptAsyncResult(SocketConnectionListener listener, AsyncCallback callback, object state) 2172 : base(callback, state) 2173 { 2174 2175 if (TD.SocketAcceptEnqueuedIsEnabled()) 2176 { 2177 TD.SocketAcceptEnqueued(this.EventTraceActivity); 2178 } 2179 2180 Fx.Assert(listener != null, "listener should not be null"); 2181 this.listener = listener; 2182 this.socketAsyncEventArgs = listener.TakeSocketAsyncEventArgs(); 2183 this.socketAsyncEventArgs.UserToken = this; 2184 this.socketAsyncEventArgs.Completed += acceptAsyncCompleted; 2185 this.OnCompleting = onCompleting; 2186 2187 // If we're going to start up the thread pool eventually anyway, avoid using RegisterWaitForSingleObject 2188 if (!Thread.CurrentThread.IsThreadPoolThread) 2189 { 2190 if (startAccept == null) 2191 { 2192 startAccept = new Action<object>(StartAccept); 2193 } 2194 2195 ActionItem.Schedule(startAccept, this); 2196 } 2197 else 2198 { 2199 bool completeSelf; 2200 bool success = false; 2201 try 2202 { 2203 completeSelf = StartAccept(); 2204 success = true; 2205 } 2206 finally 2207 { 2208 if (!success) 2209 { 2210 // Return the args when an exception is thrown 2211 ReturnSocketAsyncEventArgs(); 2212 } 2213 } 2214 2215 if (completeSelf) 2216 { 2217 base.Complete(true); 2218 } 2219 } 2220 } 2221 2222 public EventTraceActivity EventTraceActivity 2223 { 2224 get 2225 { 2226 if (this.eventTraceActivity == null) 2227 { 2228 this.eventTraceActivity = new EventTraceActivity(); 2229 } 2230 2231 return this.eventTraceActivity; 2232 } 2233 } 2234 StartAccept(object state)2235 static void StartAccept(object state) 2236 { 2237 AcceptAsyncResult thisPtr = (AcceptAsyncResult)state; 2238 2239 Exception completionException = null; 2240 bool completeSelf; 2241 try 2242 { 2243 completeSelf = thisPtr.StartAccept(); 2244 } 2245 #pragma warning suppress 56500 // Microsoft, transferring exception to another thread 2246 catch (Exception e) 2247 { 2248 if (Fx.IsFatal(e)) 2249 { 2250 throw; 2251 } 2252 completeSelf = true; 2253 completionException = e; 2254 } 2255 if (completeSelf) 2256 { 2257 thisPtr.Complete(false, completionException); 2258 } 2259 } 2260 StartAccept()2261 bool StartAccept() 2262 { 2263 while (true) 2264 { 2265 try 2266 { 2267 return listener.InternalBeginAccept(DoAcceptAsync); 2268 } 2269 catch (SocketException socketException) 2270 { 2271 if (ShouldAcceptRecover(socketException)) 2272 { 2273 continue; 2274 } 2275 else 2276 { 2277 throw; 2278 } 2279 } 2280 } 2281 } 2282 ShouldAcceptRecover(SocketException exception)2283 static bool ShouldAcceptRecover(SocketException exception) 2284 { 2285 return ( 2286 (exception.ErrorCode == UnsafeNativeMethods.WSAECONNRESET) || 2287 (exception.ErrorCode == UnsafeNativeMethods.WSAEMFILE) || 2288 (exception.ErrorCode == UnsafeNativeMethods.WSAENOBUFS) || 2289 (exception.ErrorCode == UnsafeNativeMethods.WSAETIMEDOUT) 2290 ); 2291 } 2292 2293 // Return true means completed synchronously DoAcceptAsync(Socket listenSocket)2294 bool DoAcceptAsync(Socket listenSocket) 2295 { 2296 SocketAsyncEventArgsPool.CleanupAcceptSocket(this.socketAsyncEventArgs); 2297 2298 if (listenSocket.AcceptAsync(this.socketAsyncEventArgs)) 2299 { 2300 // AcceptAsync returns true to indicate that the I/O operation is pending (asynchronous) 2301 return false; 2302 } 2303 2304 Exception exception = HandleAcceptAsyncCompleted(); 2305 if (exception != null) 2306 { 2307 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(exception); 2308 } 2309 2310 return true; 2311 } 2312 AcceptAsyncCompleted(object sender, SocketAsyncEventArgs e)2313 static void AcceptAsyncCompleted(object sender, SocketAsyncEventArgs e) 2314 { 2315 AcceptAsyncResult thisPtr = (AcceptAsyncResult)e.UserToken; 2316 Fx.Assert(thisPtr.socketAsyncEventArgs == e, "Got wrong socketAsyncEventArgs"); 2317 Exception completionException = thisPtr.HandleAcceptAsyncCompleted(); 2318 if (completionException != null && ShouldAcceptRecover((SocketException)completionException)) 2319 { 2320 DiagnosticUtility.TraceHandledException(completionException, TraceEventType.Warning); 2321 2322 StartAccept(thisPtr); 2323 return; 2324 } 2325 2326 thisPtr.Complete(false, completionException); 2327 } 2328 OnInternalCompleting(AsyncResult result, Exception exception)2329 static void OnInternalCompleting(AsyncResult result, Exception exception) 2330 { 2331 AcceptAsyncResult thisPtr = result as AcceptAsyncResult; 2332 2333 if (TD.SocketAcceptedIsEnabled()) 2334 { 2335 int hashCode = thisPtr.socket != null ? thisPtr.socket.GetHashCode() : -1; 2336 if (hashCode != -1) 2337 { 2338 TD.SocketAccepted( 2339 thisPtr.EventTraceActivity, 2340 thisPtr.listener != null ? thisPtr.listener.GetHashCode() : -1, 2341 hashCode); 2342 } 2343 else 2344 { 2345 TD.SocketAcceptClosed(thisPtr.EventTraceActivity); 2346 } 2347 } 2348 2349 Fx.Assert(result != null, "Wrong async result has been passed in to OnInternalCompleting"); 2350 thisPtr.ReturnSocketAsyncEventArgs(); 2351 } 2352 ReturnSocketAsyncEventArgs()2353 void ReturnSocketAsyncEventArgs() 2354 { 2355 if (this.socketAsyncEventArgs != null) 2356 { 2357 this.socketAsyncEventArgs.UserToken = null; 2358 this.socketAsyncEventArgs.Completed -= acceptAsyncCompleted; 2359 this.listener.ReturnSocketAsyncEventArgs(this.socketAsyncEventArgs); 2360 this.socketAsyncEventArgs = null; 2361 } 2362 } 2363 HandleAcceptAsyncCompleted()2364 Exception HandleAcceptAsyncCompleted() 2365 { 2366 Exception completionException = null; 2367 if (this.socketAsyncEventArgs.SocketError == SocketError.Success) 2368 { 2369 this.socket = this.socketAsyncEventArgs.AcceptSocket; 2370 this.socketAsyncEventArgs.AcceptSocket = null; 2371 } 2372 else 2373 { 2374 completionException = new SocketException((int)this.socketAsyncEventArgs.SocketError); 2375 } 2376 2377 return completionException; 2378 } 2379 End(IAsyncResult result)2380 public static Socket End(IAsyncResult result) 2381 { 2382 AcceptAsyncResult thisPtr = AsyncResult.End<AcceptAsyncResult>(result); 2383 return thisPtr.socket; 2384 } 2385 } 2386 } 2387 } 2388