public override Task SendAsync( ArraySegment <byte> buffer, WebSocketMessageType messageType, bool endOfMessage, CancellationToken cancellationToken) { _operation.InterlockedCheckValidStates(s_validSendStates); using (CancellationTokenRegistration ctr = ThrowOrRegisterCancellation(cancellationToken)) { var bufferType = WebSocketMessageTypeAdapter.GetWinHttpMessageType(messageType, endOfMessage); _operation.PinSendBuffer(buffer); bool sendOperationAlreadyPending = false; if (_operation.PendingWriteOperation == false) { lock (_operation.Lock) { _operation.CheckValidState(s_validSendStates); if (_operation.PendingWriteOperation == false) { _operation.PendingWriteOperation = true; _operation.TcsSend = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously); uint ret = Interop.WinHttp.WinHttpWebSocketSend( _operation.WebSocketHandle, bufferType, buffer.Count > 0 ? Marshal.UnsafeAddrOfPinnedArrayElement(buffer.Array, buffer.Offset) : IntPtr.Zero, (uint)buffer.Count); if (Interop.WinHttp.ERROR_SUCCESS != ret) { throw WinHttpException.CreateExceptionUsingError((int)ret); } } else { sendOperationAlreadyPending = true; } } } else { sendOperationAlreadyPending = true; } if (sendOperationAlreadyPending) { var exception = new InvalidOperationException( SR.Format(SR.net_Websockets_AlreadyOneOutstandingOperation, "SendAsync")); _operation.TcsSend.TrySetException(exception); Abort(); } return(_operation.TcsSend.Task); } }
private void UpdateServerCloseStatus() { uint ret; ushort serverStatus; var closeDescription = new byte[WebSocketValidate.MaxControlFramePayloadLength]; uint closeDescriptionConsumed; lock (_operation.Lock) { ret = Interop.WinHttp.WinHttpWebSocketQueryCloseStatus( _operation.WebSocketHandle, out serverStatus, closeDescription, (uint)closeDescription.Length, out closeDescriptionConsumed); if (ret != Interop.WinHttp.ERROR_SUCCESS) { throw WinHttpException.CreateExceptionUsingError((int)ret); } _closeStatus = (WebSocketCloseStatus)serverStatus; _closeStatusDescription = Encoding.UTF8.GetString(closeDescription, 0, (int)closeDescriptionConsumed); } }
private static void OnWebSocketError( WinHttpWebSocketState state, Interop.WinHttp.WINHTTP_WEB_SOCKET_ASYNC_RESULT asyncResult) { Debug.Assert(state != null, "OnWebSocketError: state is null"); var innerException = WinHttpException.CreateExceptionUsingError((int)(asyncResult.AsyncResult.dwError)); if (asyncResult.AsyncResult.dwError == Interop.WinHttp.ERROR_WINHTTP_OPERATION_CANCELLED) { var exception = new WebSocketException( WebSocketError.InvalidState, SR.Format( SR.net_WebSockets_InvalidState_ClosedOrAborted, "System.Net.WebSockets.InternalClientWebSocket", "Aborted"), innerException); state.UpdateState(WebSocketState.Aborted); if (state.TcsReceive != null) { state.TcsReceive.TrySetException(exception); } if (state.TcsSend != null) { state.TcsSend.TrySetException(exception); } return; } switch (asyncResult.Operation) { case Interop.WinHttp.WINHTTP_WEB_SOCKET_OPERATION.WINHTTP_WEB_SOCKET_SEND_OPERATION: state.PendingWriteOperation = false; state.TcsSend.TrySetException(innerException); break; case Interop.WinHttp.WINHTTP_WEB_SOCKET_OPERATION.WINHTTP_WEB_SOCKET_RECEIVE_OPERATION: state.PendingReadOperation = false; state.TcsReceive.TrySetException(innerException); break; case Interop.WinHttp.WINHTTP_WEB_SOCKET_OPERATION.WINHTTP_WEB_SOCKET_CLOSE_OPERATION: state.TcsClose.TrySetException(innerException); break; case Interop.WinHttp.WINHTTP_WEB_SOCKET_OPERATION.WINHTTP_WEB_SOCKET_SHUTDOWN_OPERATION: state.TcsCloseOutput.TrySetException(innerException); break; default: Debug.Fail( "OnWebSocketError: Operation (" + asyncResult.Operation + ") is not expected.", "Error code: " + asyncResult.AsyncResult.dwError + " (" + innerException.Message + ")"); break; } }
private static void OnRequestError( WinHttpWebSocketState state, Interop.WinHttp.WINHTTP_ASYNC_RESULT asyncResult) { Debug.Assert(state != null, "OnRequestError: state is null"); var innerException = WinHttpException.CreateExceptionUsingError((int)asyncResult.dwError); switch ((uint)asyncResult.dwResult.ToInt32()) { case Interop.WinHttp.API_SEND_REQUEST: case Interop.WinHttp.API_RECEIVE_RESPONSE: { var exception = new WebSocketException(SR.net_webstatus_ConnectFailure, innerException); state.UpdateState(WebSocketState.Closed); state.TcsUpgrade.TrySetException(exception); } break; default: { Debug.Fail( "OnRequestError: Result (" + asyncResult.dwResult + ") is not expected.", "Error code: " + asyncResult.dwError + " (" + innerException.Message + ")"); } break; } }
private Task <bool> InternalReceiveAsync(ArraySegment <byte> buffer) { bool receiveOperationAlreadyPending = false; if (_operation.PendingReadOperation == false) { lock (_operation.Lock) { if (_operation.PendingReadOperation == false) { _operation.CheckValidState(s_validReceiveStates); // Prevent continuations from running on the same thread as the callback to prevent re-entrance deadlocks _operation.TcsReceive = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously); _operation.PendingReadOperation = true; uint bytesRead = 0; Interop.WinHttp.WINHTTP_WEB_SOCKET_BUFFER_TYPE winHttpBufferType = 0; uint status = Interop.WinHttp.WinHttpWebSocketReceive( _operation.WebSocketHandle, Marshal.UnsafeAddrOfPinnedArrayElement(buffer.Array, buffer.Offset), (uint)buffer.Count, out bytesRead, // Unused in async mode: ignore. out winHttpBufferType); // Unused in async mode: ignore. if (Interop.WinHttp.ERROR_SUCCESS != status) { throw WinHttpException.CreateExceptionUsingError((int)status); } } else { receiveOperationAlreadyPending = true; } } } else { receiveOperationAlreadyPending = true; } if (receiveOperationAlreadyPending) { var exception = new InvalidOperationException( SR.Format(SR.net_Websockets_AlreadyOneOutstandingOperation, "ReceiveAsync")); _operation.TcsReceive.TrySetException(exception); Abort(); } return(_operation.TcsReceive.Task); }
private static void OnWebSocketSecureFailure(WinHttpWebSocketState state, uint flags) { Debug.Assert(state != null, "OnWebSocketSecureFailure: state is null"); var innerException = WinHttpException.CreateExceptionUsingError((int)Interop.WinHttp.ERROR_WINHTTP_SECURE_FAILURE); var exception = new WebSocketException(WebSocketError.ConnectionClosedPrematurely, innerException); // TODO (Issue 2509): handle SSL related exceptions. state.UpdateState(WebSocketState.Aborted); // TODO (Issue 2509): Create exception from WINHTTP_CALLBACK_STATUS_SECURE_FAILURE flags. state.TcsUpgrade.TrySetException(exception); }
private static void OnRequestSecureFailure(WinHttpWebSocketState state, uint flags) { Debug.Assert(state != null, "OnRequestSecureFailure: state is null"); var innerException = WinHttpException.CreateExceptionUsingError((int)Interop.WinHttp.ERROR_WINHTTP_SECURE_FAILURE); var exception = new WebSocketException( WebSocketError.Success, SR.net_webstatus_ConnectFailure, innerException); // TODO: handle SSL related exceptions. state.UpdateState(WebSocketState.Closed); // TODO: Create exception from WINHTTP_CALLBACK_STATUS_SECURE_FAILURE flags. state.TcsUpgrade.TrySetException(exception); }
private unsafe string GetResponseHeader(string headerName, char[] buffer = null) { const int StackLimit = 128; Debug.Assert(buffer == null || (buffer != null && buffer.Length > StackLimit)); int bufferLength; if (buffer == null) { bufferLength = StackLimit; char *pBuffer = stackalloc char[bufferLength]; if (QueryHeaders(headerName, pBuffer, ref bufferLength)) { return(new string(pBuffer, 0, bufferLength)); } } else { bufferLength = buffer.Length; fixed(char *pBuffer = buffer) { if (QueryHeaders(headerName, pBuffer, ref bufferLength)) { return(new string(pBuffer, 0, bufferLength)); } } } int lastError = Marshal.GetLastWin32Error(); if (lastError == Interop.WinHttp.ERROR_WINHTTP_HEADER_NOT_FOUND) { return(null); } if (lastError == Interop.WinHttp.ERROR_INSUFFICIENT_BUFFER) { buffer = new char[bufferLength]; return(GetResponseHeader(headerName, buffer)); } throw WinHttpException.CreateExceptionUsingError(lastError); }
public override Task CloseOutputAsync( WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken) { _operation.InterlockedCheckAndUpdateState(WebSocketState.CloseSent, s_validCloseOutputStates); using (CancellationTokenRegistration ctr = ThrowOrRegisterCancellation(cancellationToken)) { lock (_operation.Lock) { _operation.CheckValidState(s_validCloseOutputStatesAfterUpdate); uint ret; _operation.TcsCloseOutput = new TaskCompletionSource <bool>(); if (!string.IsNullOrEmpty(statusDescription)) { byte[] statusDescriptionBuffer = Encoding.UTF8.GetBytes(statusDescription); ret = Interop.WinHttp.WinHttpWebSocketShutdown( _operation.WebSocketHandle, (ushort)closeStatus, statusDescriptionBuffer, (uint)statusDescriptionBuffer.Length); } else { ret = Interop.WinHttp.WinHttpWebSocketShutdown( _operation.WebSocketHandle, (ushort)closeStatus, IntPtr.Zero, 0); } if (ret != Interop.WinHttp.ERROR_SUCCESS) { throw WinHttpException.CreateExceptionUsingError((int)ret); } } return(_operation.TcsCloseOutput.Task); } }
private string GetResponseHeaderStringInfo(string headerName) { uint bytesNeeded = 0; bool results = false; // Call WinHttpQueryHeaders once to obtain the size of the buffer needed. The size is returned in // bytes but the API actually returns Unicode characters. if (!Interop.WinHttp.WinHttpQueryHeaders( _operation.RequestHandle, Interop.WinHttp.WINHTTP_QUERY_CUSTOM, headerName, null, ref bytesNeeded, IntPtr.Zero)) { int lastError = Marshal.GetLastWin32Error(); if (lastError == Interop.WinHttp.ERROR_WINHTTP_HEADER_NOT_FOUND) { return(null); } if (lastError != Interop.WinHttp.ERROR_INSUFFICIENT_BUFFER) { throw WinHttpException.CreateExceptionUsingError(lastError); } } int charsNeeded = (int)bytesNeeded / sizeof(char); var buffer = new StringBuilder(charsNeeded, charsNeeded); results = Interop.WinHttp.WinHttpQueryHeaders( _operation.RequestHandle, Interop.WinHttp.WINHTTP_QUERY_CUSTOM, headerName, buffer, ref bytesNeeded, IntPtr.Zero); if (!results) { WinHttpException.ThrowExceptionUsingLastError(); } return(buffer.ToString()); }
private Task <bool> InternalCloseAsync(WebSocketCloseStatus closeStatus, string statusDescription) { uint ret; _operation.TcsClose = new TaskCompletionSource <bool>(); lock (_operation.Lock) { _operation.CheckValidState(s_validCloseStates); if (!string.IsNullOrEmpty(statusDescription)) { byte[] statusDescriptionBuffer = Encoding.UTF8.GetBytes(statusDescription); ret = Interop.WinHttp.WinHttpWebSocketClose( _operation.WebSocketHandle, (ushort)closeStatus, statusDescriptionBuffer, (uint)statusDescriptionBuffer.Length); } else { ret = Interop.WinHttp.WinHttpWebSocketClose( _operation.WebSocketHandle, (ushort)closeStatus, IntPtr.Zero, 0); } if (ret != Interop.WinHttp.ERROR_SUCCESS) { throw WinHttpException.CreateExceptionUsingError((int)ret); } } return(_operation.TcsClose.Task); }
public override Task SendAsync( ArraySegment <byte> buffer, WebSocketMessageType messageType, bool endOfMessage, CancellationToken cancellationToken) { _operation.InterlockedCheckValidStates(s_validSendStates); using (CancellationTokenRegistration ctr = ThrowOrRegisterCancellation(cancellationToken)) { var bufferType = WebSocketMessageTypeAdapter.GetWinHttpMessageType(messageType, endOfMessage); // TODO (Issue 2505): replace with PinnableBufferCache. if (!_cachedSendPinnedBuffer.IsAllocated || _cachedSendPinnedBuffer.Target != buffer.Array) { if (_cachedSendPinnedBuffer.IsAllocated) { _cachedSendPinnedBuffer.Free(); } _cachedSendPinnedBuffer = GCHandle.Alloc(buffer.Array, GCHandleType.Pinned); } bool sendOperationAlreadyPending = false; if (_operation.PendingWriteOperation == false) { lock (_operation.Lock) { _operation.CheckValidState(s_validSendStates); if (_operation.PendingWriteOperation == false) { _operation.PendingWriteOperation = true; _operation.TcsSend = new TaskCompletionSource <bool>(); uint ret = Interop.WinHttp.WinHttpWebSocketSend( _operation.WebSocketHandle, bufferType, buffer.Count > 0 ? Marshal.UnsafeAddrOfPinnedArrayElement(buffer.Array, buffer.Offset) : IntPtr.Zero, (uint)buffer.Count); if (Interop.WinHttp.ERROR_SUCCESS != ret) { throw WinHttpException.CreateExceptionUsingError((int)ret); } } else { sendOperationAlreadyPending = true; } } } else { sendOperationAlreadyPending = true; } if (sendOperationAlreadyPending) { var exception = new InvalidOperationException( SR.Format(SR.net_Websockets_AlreadyOneOutstandingOperation, "SendAsync")); _operation.TcsSend.TrySetException(exception); Abort(); } return(_operation.TcsSend.Task); } }
private Task <bool> InternalReceiveAsync(Memory <byte> pinnedBuffer) { bool receiveOperationAlreadyPending = false; if (_operation.PendingReadOperation == false) { lock (_operation.Lock) { if (_operation.PendingReadOperation == false) { _operation.CheckValidState(s_validReceiveStates); // Prevent continuations from running on the same thread as the callback to prevent re-entrance deadlocks _operation.TcsReceive = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously); _operation.PendingReadOperation = true; uint bytesRead = 0; Interop.WinHttp.WINHTTP_WEB_SOCKET_BUFFER_TYPE winHttpBufferType = 0; IntPtr pinnedBufferPtr; unsafe { fixed(byte *p = &pinnedBuffer.Span.DangerousGetPinnableReference()) { pinnedBufferPtr = (IntPtr)p; } } uint status = Interop.WinHttp.WinHttpWebSocketReceive( _operation.WebSocketHandle, pinnedBufferPtr, (uint)pinnedBuffer.Length, out bytesRead, // Unused in async mode: ignore. out winHttpBufferType); // Unused in async mode: ignore. if (Interop.WinHttp.ERROR_SUCCESS != status) { throw WinHttpException.CreateExceptionUsingError((int)status); } } else { receiveOperationAlreadyPending = true; } } } else { receiveOperationAlreadyPending = true; } if (receiveOperationAlreadyPending) { var exception = new InvalidOperationException( SR.Format(SR.net_Websockets_AlreadyOneOutstandingOperation, "ReceiveAsync")); _operation.TcsReceive.TrySetException(exception); Abort(); } return(_operation.TcsReceive.Task); }