public static WebSocket CreateClientWebSocket(Stream innerStream,
                                                      string subProtocol,
                                                      int receiveBufferSize,
                                                      int sendBufferSize,
                                                      TimeSpan keepAliveInterval,
                                                      bool useZeroMaskingKey,
                                                      ArraySegment <byte> internalBuffer)
        {
            if (!WebSocketProtocolComponent.IsSupported)
            {
                WebSocketHelpers.ThrowPlatformNotSupportedException_WSPC();
            }

            WebSocketHelpers.ValidateInnerStream(innerStream);
            WebSocketHelpers.ValidateOptions(subProtocol, receiveBufferSize, sendBufferSize, keepAliveInterval);
            WebSocketHelpers.ValidateArraySegment <byte>(internalBuffer, "internalBuffer");
            WebSocketBuffer.Validate(internalBuffer.Count, receiveBufferSize, sendBufferSize, false);

            return(new InternalClientWebSocket(innerStream,
                                               subProtocol,
                                               receiveBufferSize,
                                               sendBufferSize,
                                               keepAliveInterval,
                                               useZeroMaskingKey,
                                               internalBuffer));
        }
Exemple #2
0
        public void SetBuffer(int receiveBufferSize, int sendBufferSize, ArraySegment <byte> buffer)
        {
            ThrowIfReadOnly();
            WebSocketHelpers.ValidateBufferSizes(receiveBufferSize, sendBufferSize);
            WebSocketHelpers.ValidateArraySegment(buffer, "buffer");
            WebSocketBuffer.Validate(buffer.Count, receiveBufferSize, sendBufferSize, false);

            this.receiveBufferSize = receiveBufferSize;
            this.sendBufferSize    = sendBufferSize;

            // Only full-trust applications can specify their own buffer to be used as the
            // internal buffer for the WebSocket object.  This is because the contents of the
            // buffer are used internally by the WebSocket as it marshals data with embedded
            // pointers to native code.  A malicious application could use this to corrupt
            // native memory.
            if (AppDomain.CurrentDomain.IsFullyTrusted)
            {
                this.buffer = buffer;
            }
            else
            {
                // We silently ignore the passed in buffer and will create an internal
                // buffer later.
                this.buffer = null;
            }
        }
        internal static string GetSupportedVersion()
        {
            if (s_WebSocketDllHandle.IsInvalid)
            {
                WebSocketHelpers.ThrowPlatformNotSupportedException_WSPC();
            }

            SafeWebSocketHandle webSocketHandle = null;

            try
            {
                int errorCode = WebSocketCreateClientHandle_Raw(null, 0, out webSocketHandle);
                ThrowOnError(errorCode);

                if (webSocketHandle == null ||
                    webSocketHandle.IsInvalid)
                {
                    WebSocketHelpers.ThrowPlatformNotSupportedException_WSPC();
                }

                IntPtr additionalHeadersPtr;
                uint   additionalHeaderCount;

                errorCode = WebSocketBeginClientHandshake_Raw(webSocketHandle,
                                                              IntPtr.Zero,
                                                              0,
                                                              IntPtr.Zero,
                                                              0,
                                                              s_InitialClientRequestHeaders,
                                                              (uint)s_InitialClientRequestHeaders.Length,
                                                              out additionalHeadersPtr,
                                                              out additionalHeaderCount);
                ThrowOnError(errorCode);

                HttpHeader[] additionalHeaders = MarshalHttpHeaders(additionalHeadersPtr, (int)additionalHeaderCount);

                string version = null;
                foreach (HttpHeader header in additionalHeaders)
                {
                    if (string.Compare(header.Name,
                                       HttpKnownHeaderNames.SecWebSocketVersion,
                                       StringComparison.OrdinalIgnoreCase) == 0)
                    {
                        version = header.Value;
                        break;
                    }
                }
                Contract.Assert(version != null, "'version' MUST NOT be NULL.");

                return(version);
            }
            finally
            {
                if (webSocketHandle != null)
                {
                    webSocketHandle.Dispose();
                }
            }
        }
        private async Task WriteAsyncCore(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
        {
            if (WebSocketBase.LoggingEnabled)
            {
                Logging.Enter(Logging.WebSockets, this, Methods.WriteAsyncCore,
                              WebSocketHelpers.GetTraceMsgForParameters(offset, count, cancellationToken));
            }

            CancellationTokenRegistration cancellationTokenRegistration = new CancellationTokenRegistration();

            try
            {
                if (cancellationToken.CanBeCanceled)
                {
                    cancellationTokenRegistration = cancellationToken.Register(s_OnCancel, this, false);
                }

                if (!m_InOpaqueMode)
                {
                    await m_OutputStream.WriteAsync(buffer, offset, count, cancellationToken).SuppressContextFlow();
                }
                else
                {
#if DEBUG
                    // When using fast path only one outstanding read is permitted. By switching into opaque mode
                    // via IWebSocketStream.SwitchToOpaqueMode (see more detailed comments in interface definition)
                    // caller takes responsibility for enforcing this constraint.
                    Contract.Assert(Interlocked.Increment(ref m_OutstandingOperations.m_Writes) == 1,
                                    "Only one outstanding write allowed at any given time.");
#endif
                    m_WriteTaskCompletionSource = new TaskCompletionSource <object>();
                    m_WriteEventArgs.BufferList = null;
                    m_WriteEventArgs.SetBuffer(buffer, offset, count);
                    if (WriteAsyncFast(m_WriteEventArgs))
                    {
                        await m_WriteTaskCompletionSource.Task.SuppressContextFlow();
                    }
                }
            }
            catch (Exception error)
            {
                if (s_CanHandleException(error))
                {
                    cancellationToken.ThrowIfCancellationRequested();
                }

                throw;
            }
            finally
            {
                cancellationTokenRegistration.Dispose();

                if (WebSocketBase.LoggingEnabled)
                {
                    Logging.Exit(Logging.WebSockets, this, Methods.WriteAsyncCore, string.Empty);
                }
            }
        }
Exemple #5
0
        public void SetBuffer(int receiveBufferSize, int sendBufferSize)
        {
            ThrowIfReadOnly();
            WebSocketHelpers.ValidateBufferSizes(receiveBufferSize, sendBufferSize);

            this.buffer            = null;
            this.receiveBufferSize = receiveBufferSize;
            this.sendBufferSize    = sendBufferSize;
        }
            public Task MultipleWriteAsync(IList <ArraySegment <byte> > sendBuffers,
                                           CancellationToken cancellationToken)
            {
                Contract.Assert(this.SupportsMultipleWrite, "This method MUST NOT be used for custom NetworkStream implementations.");

                if (!m_InOpaqueMode)
                {
                    // We can't use fast path over SSL
                    return(Task.Factory.FromAsync <IList <ArraySegment <byte> > >(s_BeginMultipleWrite, s_EndMultipleWrite,
                                                                                  sendBuffers, this));
                }

                if (WebSocketBase.LoggingEnabled)
                {
                    Logging.Enter(Logging.WebSockets, this, Methods.MultipleWriteAsync, string.Empty);
                }

                bool completedAsynchronously = false;

                try
                {
                    cancellationToken.ThrowIfCancellationRequested();
#if DEBUG
                    // When using fast path only one outstanding read is permitted. By switching into opaque mode
                    // via IWebSocketStream.SwitchToOpaqueMode (see more detailed comments in interface definition)
                    // caller takes responsibility for enforcing this constraint.
                    Contract.Assert(Interlocked.Increment(ref m_OutstandingOperations.m_Writes) == 1,
                                    "Only one outstanding write allowed at any given time.");
#endif
                    WebSocketHelpers.ThrowIfConnectionAborted(m_InnerStream, false);
                    m_WriteTaskCompletionSource = new TaskCompletionSource <object>();
                    m_WriteEventArgs.SetBuffer(null, 0, 0);
                    m_WriteEventArgs.BufferList = sendBuffers;
                    completedAsynchronously     = InnerSocket.SendAsync(m_WriteEventArgs);
                    if (!completedAsynchronously)
                    {
                        if (m_WriteEventArgs.SocketError != SocketError.Success)
                        {
                            throw new SocketException(m_WriteEventArgs.SocketError);
                        }

                        return(Task.CompletedTask);
                    }

                    return(m_WriteTaskCompletionSource.Task);
                }
                finally
                {
                    if (WebSocketBase.LoggingEnabled)
                    {
                        Logging.Exit(Logging.WebSockets, this, Methods.MultipleWriteAsync, completedAsynchronously);
                    }
                }
            }
            public override Task <int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
            {
                WebSocketHelpers.ValidateBuffer(buffer, offset, count);

                if (!m_InOpaqueMode)
                {
                    return(base.ReadAsync(buffer, offset, count, cancellationToken));
                }

                return(ReadAsyncCore(buffer, offset, count, cancellationToken, false));
            }
        public void SetBuffer(int receiveBufferSize, int sendBufferSize, ArraySegment <byte> buffer)
        {
            ThrowIfReadOnly();
            WebSocketHelpers.ValidateBufferSizes(receiveBufferSize, sendBufferSize);
            WebSocketHelpers.ValidateArraySegment(buffer, "buffer");
            WebSocketBuffer.Validate(buffer.Count, receiveBufferSize, sendBufferSize, false);

            this.receiveBufferSize = receiveBufferSize;
            this.sendBufferSize    = sendBufferSize;
            this.buffer            = buffer;
        }
        internal static void WebSocketCreateServerHandle(Property[] properties,
                                                         int propertyCount,
                                                         out SafeWebSocketHandle webSocketHandle)
        {
            Contract.Assert(propertyCount >= 0, "'propertyCount' MUST NOT be negative.");
            Contract.Assert((properties == null && propertyCount == 0) ||
                            (properties != null && propertyCount == properties.Length),
                            "'propertyCount' MUST MATCH 'properties.Length'.");

            if (s_WebSocketDllHandle.IsInvalid)
            {
                WebSocketHelpers.ThrowPlatformNotSupportedException_WSPC();
            }

            int errorCode = WebSocketCreateServerHandle_Raw(properties, (uint)propertyCount, out webSocketHandle);

            ThrowOnError(errorCode);

            if (webSocketHandle == null ||
                webSocketHandle.IsInvalid)
            {
                WebSocketHelpers.ThrowPlatformNotSupportedException_WSPC();
            }

            IntPtr responseHeadersPtr;
            uint   responseHeaderCount;

            // Currently the WSPC doesn't allow to initiate a data session
            // without also being involved in the http handshake
            // There is no information whatsoever, which is needed by the
            // WSPC for parsing WebSocket frames from the HTTP handshake
            // In the managed implementation the HTTP header handling
            // will be done using the managed HTTP stack and we will
            // just fake an HTTP handshake for the WSPC calling
            // WebSocketBeginServerHandshake and WebSocketEndServerHandshake
            // with statically defined dummy headers.
            errorCode = WebSocketBeginServerHandshake_Raw(webSocketHandle,
                                                          IntPtr.Zero,
                                                          IntPtr.Zero,
                                                          0,
                                                          s_ServerFakeRequestHeaders,
                                                          (uint)s_ServerFakeRequestHeaders.Length,
                                                          out responseHeadersPtr,
                                                          out responseHeaderCount);

            ThrowOnError(errorCode);

            HttpHeader[] responseHeaders = MarshalHttpHeaders(responseHeadersPtr, (int)responseHeaderCount);
            errorCode = WebSocketEndServerHandshake_Raw(webSocketHandle);

            ThrowOnError(errorCode);

            Contract.Assert(webSocketHandle != null, "'webSocketHandle' MUST NOT be NULL at this point.");
        }
            private static void EndMultipleWrite(IAsyncResult asyncResult)
            {
                Contract.Assert(asyncResult != null, "'asyncResult' MUST NOT be NULL.");
                Contract.Assert(asyncResult.AsyncState != null, "'asyncResult.AsyncState' MUST NOT be NULL.");
                WebSocketConnection connection = asyncResult.AsyncState as WebSocketConnection;

                Contract.Assert(connection != null, "'connection' MUST NOT be NULL.");

                WebSocketHelpers.ThrowIfConnectionAborted(connection.m_InnerStream, false);
                connection.m_InnerStream.NetworkStream.EndMultipleWrite(asyncResult);
            }
        internal static Task <HttpListenerWebSocketContext> AcceptWebSocketAsync(HttpListenerContext context,
                                                                                 string subProtocol,
                                                                                 int receiveBufferSize,
                                                                                 TimeSpan keepAliveInterval,
                                                                                 ArraySegment <byte> internalBuffer)
        {
            WebSocketHelpers.ValidateOptions(subProtocol, receiveBufferSize, WebSocketBuffer.MinSendBufferSize, keepAliveInterval);
            WebSocketHelpers.ValidateArraySegment <byte>(internalBuffer, "internalBuffer");
            WebSocketBuffer.Validate(internalBuffer.Count, receiveBufferSize, WebSocketBuffer.MinSendBufferSize, true);

            return(AcceptWebSocketAsyncCore(context, subProtocol, receiveBufferSize, keepAliveInterval, internalBuffer));
        }
            public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
            {
                WebSocketHelpers.ValidateBuffer(buffer, offset, count);

                if (!m_InOpaqueMode)
                {
                    return(base.WriteAsync(buffer, offset, count, cancellationToken));
                }

                if (WebSocketBase.LoggingEnabled)
                {
                    Logging.Enter(Logging.WebSockets, this, Methods.WriteAsync,
                                  WebSocketHelpers.GetTraceMsgForParameters(offset, count, cancellationToken));
                }

                bool completedAsynchronously = false;

                try
                {
                    cancellationToken.ThrowIfCancellationRequested();
#if DEBUG
                    // When using fast path only one outstanding read is permitted. By switching into opaque mode
                    // via IWebSocketStream.SwitchToOpaqueMode (see more detailed comments in interface definition)
                    // caller takes responsibility for enforcing this constraint.
                    Contract.Assert(Interlocked.Increment(ref m_OutstandingOperations.m_Writes) == 1,
                                    "Only one outstanding write allowed at any given time.");
#endif
                    WebSocketHelpers.ThrowIfConnectionAborted(m_InnerStream, false);
                    m_WriteTaskCompletionSource = new TaskCompletionSource <object>();
                    m_WriteEventArgs.BufferList = null;
                    m_WriteEventArgs.SetBuffer(buffer, offset, count);
                    completedAsynchronously = InnerSocket.SendAsync(m_WriteEventArgs);
                    if (!completedAsynchronously)
                    {
                        if (m_WriteEventArgs.SocketError != SocketError.Success)
                        {
                            throw new SocketException(m_WriteEventArgs.SocketError);
                        }

                        return(Task.CompletedTask);
                    }

                    return(m_WriteTaskCompletionSource.Task);
                }
                finally
                {
                    if (WebSocketBase.LoggingEnabled)
                    {
                        Logging.Exit(Logging.WebSockets, this, Methods.WriteAsync, completedAsynchronously);
                    }
                }
            }
Exemple #13
0
 public void AddSubProtocol(string subProtocol)
 {
     ThrowIfReadOnly();
     WebSocketHelpers.ValidateSubprotocol(subProtocol);
     // Duplicates not allowed.
     foreach (string item in requestedSubProtocols)
     {
         if (string.Equals(item, subProtocol, StringComparison.OrdinalIgnoreCase))
         {
             throw new ArgumentException(SR.GetString(SR.net_WebSockets_NoDuplicateProtocol, subProtocol),
                                         "subProtocol");
         }
     }
     requestedSubProtocols.Add(subProtocol);
 }
Exemple #14
0
        public InternalClientWebSocket(Stream innerStream, string subProtocol, int receiveBufferSize, int sendBufferSize,
                                       TimeSpan keepAliveInterval, bool useZeroMaskingKey, ArraySegment <byte> internalBuffer)
            : base(innerStream, subProtocol, keepAliveInterval,
                   WebSocketBuffer.CreateClientBuffer(internalBuffer, receiveBufferSize, sendBufferSize))
        {
            m_Properties    = this.InternalBuffer.CreateProperties(useZeroMaskingKey);
            m_SessionHandle = this.CreateWebSocketHandle();

            if (m_SessionHandle == null || m_SessionHandle.IsInvalid)
            {
                WebSocketHelpers.ThrowPlatformNotSupportedException_WSPC();
            }

            StartKeepAliveTimer();
        }
        public async override Task <int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
        {
            if (WebSocketBase.LoggingEnabled)
            {
                Logging.Enter(Logging.WebSockets, this, Methods.ReadAsync,
                              WebSocketHelpers.GetTraceMsgForParameters(offset, count, cancellationToken));
            }

            CancellationTokenRegistration cancellationTokenRegistration = new CancellationTokenRegistration();

            int bytesRead = 0;

            try
            {
                if (cancellationToken.CanBeCanceled)
                {
                    cancellationTokenRegistration = cancellationToken.Register(s_OnCancel, this, false);
                }

                WebSocketHelpers.ThrowIfConnectionAborted(m_ConnectStream.Connection, true);
                bytesRead = await base.ReadAsync(buffer, offset, count, cancellationToken).SuppressContextFlow <int>();

                if (WebSocketBase.LoggingEnabled)
                {
                    Logging.Dump(Logging.WebSockets, this, Methods.ReadAsync, buffer, offset, bytesRead);
                }
            }
            catch (Exception error)
            {
                if (s_CanHandleException(error))
                {
                    cancellationToken.ThrowIfCancellationRequested();
                }

                throw;
            }
            finally
            {
                cancellationTokenRegistration.Dispose();

                if (WebSocketBase.LoggingEnabled)
                {
                    Logging.Exit(Logging.WebSockets, this, Methods.ReadAsync, bytesRead);
                }
            }

            return(bytesRead);
        }
        public async Task MultipleWriteAsync(IList <ArraySegment <byte> > sendBuffers, CancellationToken cancellationToken)
        {
            Contract.Assert(this.SupportsMultipleWrite, "This method MUST NOT be used for custom NetworkStream implementations.");

            if (WebSocketBase.LoggingEnabled)
            {
                Logging.Enter(Logging.WebSockets, this, Methods.MultipleWriteAsync, string.Empty);
            }
            CancellationTokenRegistration cancellationTokenRegistration = new CancellationTokenRegistration();

            try
            {
                if (cancellationToken.CanBeCanceled)
                {
                    cancellationTokenRegistration = cancellationToken.Register(s_OnCancel, this, false);
                }

                WebSocketHelpers.ThrowIfConnectionAborted(m_ConnectStream.Connection, false);
                await((WebSocketBase.IWebSocketStream)base.BaseStream).MultipleWriteAsync(sendBuffers, cancellationToken).SuppressContextFlow();

                if (WebSocketBase.LoggingEnabled)
                {
                    foreach (ArraySegment <byte> buffer in sendBuffers)
                    {
                        Logging.Dump(Logging.WebSockets, this, Methods.MultipleWriteAsync, buffer.Array, buffer.Offset, buffer.Count);
                    }
                }
            }
            catch (Exception error)
            {
                if (s_CanHandleException(error))
                {
                    cancellationToken.ThrowIfCancellationRequested();
                }

                throw;
            }
            finally
            {
                cancellationTokenRegistration.Dispose();

                if (WebSocketBase.LoggingEnabled)
                {
                    Logging.Exit(Logging.WebSockets, this, Methods.MultipleWriteAsync, string.Empty);
                }
            }
        }
            private static IAsyncResult BeginMultipleWrite(IList <ArraySegment <byte> > sendBuffers, AsyncCallback callback, object asyncState)
            {
                Contract.Assert(sendBuffers != null, "'sendBuffers' MUST NOT be NULL.");
                Contract.Assert(asyncState != null, "'asyncState' MUST NOT be NULL.");
                WebSocketConnection connection = asyncState as WebSocketConnection;

                Contract.Assert(connection != null, "'connection' MUST NOT be NULL.");

                BufferOffsetSize[] buffers = new BufferOffsetSize[sendBuffers.Count];

                for (int index = 0; index < sendBuffers.Count; index++)
                {
                    ArraySegment <byte> sendBuffer = sendBuffers[index];
                    buffers[index] = new BufferOffsetSize(sendBuffer.Array, sendBuffer.Offset, sendBuffer.Count, false);
                }

                WebSocketHelpers.ThrowIfConnectionAborted(connection.m_InnerStream, false);
                return(connection.m_InnerStream.NetworkStream.BeginMultipleWrite(buffers, callback, asyncState));
            }
Exemple #18
0
        // This method is not thread safe. It must only be called after enforcing at most 1 outstanding send operation
        internal void PinSendBuffer(ArraySegment <byte> payload, out bool bufferHasBeenPinned)
        {
            bufferHasBeenPinned = false;
            WebSocketHelpers.ValidateBuffer(payload.Array, payload.Offset, payload.Count);
            int previousState = Interlocked.Exchange(ref m_SendBufferState, SendBufferState.SendPayloadSpecified);

            if (previousState != SendBufferState.None)
            {
                Contract.Assert(false, "'m_SendBufferState' MUST BE 'None' at this point.");
                // Indicates a violation in the API contract that could indicate
                // memory corruption because the pinned sendbuffer is shared between managed and native code
                throw new AccessViolationException();
            }
            m_PinnedSendBuffer             = payload;
            m_PinnedSendBufferHandle       = GCHandle.Alloc(m_PinnedSendBuffer.Array, GCHandleType.Pinned);
            bufferHasBeenPinned            = true;
            m_PinnedSendBufferStartAddress =
                Marshal.UnsafeAddrOfPinnedArrayElement(m_PinnedSendBuffer.Array, m_PinnedSendBuffer.Offset).ToInt64();
            m_PinnedSendBufferEndAddress = m_PinnedSendBufferStartAddress + m_PinnedSendBuffer.Count;
        }
Exemple #19
0
        public ClientWebSocket()
        {
            if (Logging.On)
            {
                Logging.Enter(Logging.WebSockets, this, ".ctor", null);
            }

            if (!WebSocketProtocolComponent.IsSupported)
            {
                WebSocketHelpers.ThrowPlatformNotSupportedException_WSPC();
            }

            state   = created;
            options = new ClientWebSocketOptions();
            cts     = new CancellationTokenSource();

            if (Logging.On)
            {
                Logging.Exit(Logging.WebSockets, this, ".ctor", null);
            }
        }
        internal static WebSocket CreateServerWebSocket(Stream innerStream,
                                                        string subProtocol,
                                                        int receiveBufferSize,
                                                        TimeSpan keepAliveInterval,
                                                        ArraySegment <byte> internalBuffer)
        {
            if (!WebSocketProtocolComponent.IsSupported)
            {
                WebSocketHelpers.ThrowPlatformNotSupportedException_WSPC();
            }

            WebSocketHelpers.ValidateInnerStream(innerStream);
            WebSocketHelpers.ValidateOptions(subProtocol, receiveBufferSize, WebSocketBuffer.MinSendBufferSize, keepAliveInterval);
            WebSocketHelpers.ValidateArraySegment <byte>(internalBuffer, "internalBuffer");
            WebSocketBuffer.Validate(internalBuffer.Count, receiveBufferSize, WebSocketBuffer.MinSendBufferSize, true);

            return(new ServerWebSocket(innerStream,
                                       subProtocol,
                                       receiveBufferSize,
                                       keepAliveInterval,
                                       internalBuffer));
        }
        internal static void WebSocketCreateClientHandle(Property[] properties,
                                                         out SafeWebSocketHandle webSocketHandle)
        {
            uint propertyCount = properties == null ? 0 : (uint)properties.Length;

            if (s_WebSocketDllHandle.IsInvalid)
            {
                WebSocketHelpers.ThrowPlatformNotSupportedException_WSPC();
            }

            int errorCode = WebSocketCreateClientHandle_Raw(properties, propertyCount, out webSocketHandle);

            ThrowOnError(errorCode);

            if (webSocketHandle == null ||
                webSocketHandle.IsInvalid)
            {
                WebSocketHelpers.ThrowPlatformNotSupportedException_WSPC();
            }

            IntPtr additionalHeadersPtr;
            uint   additionalHeaderCount;

            // Currently the WSPC doesn't allow to initiate a data session
            // without also being involved in the http handshake
            // There is no information whatsoever, which is needed by the
            // WSPC for parsing WebSocket frames from the HTTP handshake
            // In the managed implementation the HTTP header handling
            // will be done using the managed HTTP stack and we will
            // just fake an HTTP handshake for the WSPC  calling
            // WebSocketBeginClientHandshake and WebSocketEndClientHandshake
            // with statically defined dummy headers.
            errorCode = WebSocketBeginClientHandshake_Raw(webSocketHandle,
                                                          IntPtr.Zero,
                                                          0,
                                                          IntPtr.Zero,
                                                          0,
                                                          s_InitialClientRequestHeaders,
                                                          (uint)s_InitialClientRequestHeaders.Length,
                                                          out additionalHeadersPtr,
                                                          out additionalHeaderCount);

            ThrowOnError(errorCode);

            HttpHeader[] additionalHeaders = MarshalHttpHeaders(additionalHeadersPtr, (int)additionalHeaderCount);

            string key = null;

            foreach (HttpHeader header in additionalHeaders)
            {
                if (string.Compare(header.Name,
                                   HttpKnownHeaderNames.SecWebSocketKey,
                                   StringComparison.OrdinalIgnoreCase) == 0)
                {
                    key = header.Value;
                    break;
                }
            }
            Contract.Assert(key != null, "'key' MUST NOT be NULL.");

            string acceptValue = WebSocketHelpers.GetSecWebSocketAcceptString(key);

            HttpHeader[] responseHeaders = new HttpHeader[]
            {
                new HttpHeader()
                {
                    Name        = HttpKnownHeaderNames.Connection,
                    NameLength  = (uint)HttpKnownHeaderNames.Connection.Length,
                    Value       = HttpKnownHeaderNames.Upgrade,
                    ValueLength = (uint)HttpKnownHeaderNames.Upgrade.Length
                },
                new HttpHeader()
                {
                    Name        = HttpKnownHeaderNames.Upgrade,
                    NameLength  = (uint)HttpKnownHeaderNames.Upgrade.Length,
                    Value       = WebSocketHelpers.WebSocketUpgradeToken,
                    ValueLength = (uint)WebSocketHelpers.WebSocketUpgradeToken.Length
                },
                new HttpHeader()
                {
                    Name        = HttpKnownHeaderNames.SecWebSocketAccept,
                    NameLength  = (uint)HttpKnownHeaderNames.SecWebSocketAccept.Length,
                    Value       = acceptValue,
                    ValueLength = (uint)acceptValue.Length
                }
            };

            errorCode = WebSocketEndClientHandshake_Raw(webSocketHandle,
                                                        responseHeaders,
                                                        (uint)responseHeaders.Length,
                                                        IntPtr.Zero,
                                                        IntPtr.Zero,
                                                        IntPtr.Zero);

            ThrowOnError(errorCode);

            Contract.Assert(webSocketHandle != null, "'webSocketHandle' MUST NOT be NULL at this point.");
        }
        public static ArraySegment <byte> CreateClientBuffer(int receiveBufferSize, int sendBufferSize)
        {
            WebSocketHelpers.ValidateBufferSizes(receiveBufferSize, sendBufferSize);

            return(WebSocketBuffer.CreateInternalBufferArraySegment(receiveBufferSize, sendBufferSize, false));
        }
Exemple #23
0
        // Validate the response headers and return the sub-protocol.
        private string ValidateResponse(HttpWebRequest request, HttpWebResponse response)
        {
            // 101
            if (response.StatusCode != HttpStatusCode.SwitchingProtocols)
            {
                throw new WebSocketException(SR.GetString(SR.net_WebSockets_Connect101Expected,
                                                          (int)response.StatusCode));
            }

            // Upgrade: websocket
            string upgradeHeader = response.Headers[HttpKnownHeaderNames.Upgrade];

            if (!string.Equals(upgradeHeader, WebSocketHelpers.WebSocketUpgradeToken,
                               StringComparison.OrdinalIgnoreCase))
            {
                throw new WebSocketException(SR.GetString(SR.net_WebSockets_InvalidResponseHeader,
                                                          HttpKnownHeaderNames.Upgrade, upgradeHeader));
            }

            // Connection: Upgrade
            string connectionHeader = response.Headers[HttpKnownHeaderNames.Connection];

            if (!string.Equals(connectionHeader, HttpKnownHeaderNames.Upgrade,
                               StringComparison.OrdinalIgnoreCase))
            {
                throw new WebSocketException(SR.GetString(SR.net_WebSockets_InvalidResponseHeader,
                                                          HttpKnownHeaderNames.Connection, connectionHeader));
            }

            // Sec-WebSocket-Accept derived from request Sec-WebSocket-Key
            string websocketAcceptHeader = response.Headers[HttpKnownHeaderNames.SecWebSocketAccept];
            string expectedAcceptHeader  = WebSocketHelpers.GetSecWebSocketAcceptString(
                request.Headers[HttpKnownHeaderNames.SecWebSocketKey]);

            if (!string.Equals(websocketAcceptHeader, expectedAcceptHeader, StringComparison.OrdinalIgnoreCase))
            {
                throw new WebSocketException(SR.GetString(SR.net_WebSockets_InvalidResponseHeader,
                                                          HttpKnownHeaderNames.SecWebSocketAccept, websocketAcceptHeader));
            }

            // Sec-WebSocket-Protocol matches one from request
            // A missing header is ok.  It's also ok if the client didn't specify any.
            string subProtocol = response.Headers[HttpKnownHeaderNames.SecWebSocketProtocol];

            if (!string.IsNullOrWhiteSpace(subProtocol) && options.RequestedSubProtocols.Count > 0)
            {
                bool foundMatch = false;
                foreach (string requestedSubProtocol in options.RequestedSubProtocols)
                {
                    if (string.Equals(requestedSubProtocol, subProtocol, StringComparison.OrdinalIgnoreCase))
                    {
                        foundMatch = true;
                        break;
                    }
                }
                if (!foundMatch)
                {
                    throw new WebSocketException(SR.GetString(SR.net_WebSockets_AcceptUnsupportedProtocol,
                                                              string.Join(", ", options.RequestedSubProtocols), subProtocol));
                }
            }

            return(string.IsNullOrWhiteSpace(subProtocol) ? null : subProtocol); // May be null or valid.
        }
        public static ArraySegment <byte> CreateServerBuffer(int receiveBufferSize)
        {
            WebSocketHelpers.ValidateBufferSizes(receiveBufferSize, WebSocketBuffer.MinSendBufferSize);

            return(WebSocketBuffer.CreateInternalBufferArraySegment(receiveBufferSize, WebSocketBuffer.MinSendBufferSize, true));
        }
        public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
        {
            WebSocketHelpers.ValidateBuffer(buffer, offset, count);

            return(WriteAsyncCore(buffer, offset, count, cancellationToken));
        }
        public async Task CloseNetworkConnectionAsync(CancellationToken cancellationToken)
        {
            // need to yield here to make sure that we don't get any exception synchronously
            await Task.Yield();

            if (WebSocketBase.LoggingEnabled)
            {
                Logging.Enter(Logging.WebSockets, this, Methods.CloseNetworkConnectionAsync, string.Empty);
            }

            CancellationTokenSource reasonableTimeoutCancellationTokenSource = null;
            CancellationTokenSource linkedCancellationTokenSource            = null;
            CancellationToken       linkedCancellationToken = CancellationToken.None;

            CancellationTokenRegistration cancellationTokenRegistration = new CancellationTokenRegistration();

            int bytesRead = 0;

            try
            {
                reasonableTimeoutCancellationTokenSource =
                    new CancellationTokenSource(WebSocketHelpers.ClientTcpCloseTimeout);
                linkedCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(
                    reasonableTimeoutCancellationTokenSource.Token,
                    cancellationToken);
                linkedCancellationToken       = linkedCancellationTokenSource.Token;
                cancellationTokenRegistration = linkedCancellationToken.Register(s_OnCancel, this, false);

                WebSocketHelpers.ThrowIfConnectionAborted(m_ConnectStream.Connection, true);
                byte[] buffer = new byte[1];
                if (m_WebSocketConnection != null && m_InOpaqueMode)
                {
                    bytesRead = await m_WebSocketConnection.ReadAsyncCore(buffer, 0, 1, linkedCancellationToken, true).SuppressContextFlow <int>();
                }
                else
                {
                    bytesRead = await base.ReadAsync(buffer, 0, 1, linkedCancellationToken).SuppressContextFlow <int>();
                }

                if (bytesRead != 0)
                {
                    Contract.Assert(false, "'bytesRead' MUST be '0' at this point. Instead more payload was received ('" + buffer[0].ToString() + "')");

                    if (WebSocketBase.LoggingEnabled)
                    {
                        Logging.Dump(Logging.WebSockets, this, Methods.CloseNetworkConnectionAsync, buffer, 0, bytesRead);
                    }

                    throw new WebSocketException(WebSocketError.NotAWebSocket);
                }
            }
            catch (Exception error)
            {
                if (!s_CanHandleException(error))
                {
                    throw;
                }

                // throw OperationCancelledException when canceled by the caller
                // ignore cancellation due to the timeout
                cancellationToken.ThrowIfCancellationRequested();
            }
            finally
            {
                cancellationTokenRegistration.Dispose();
                if (linkedCancellationTokenSource != null)
                {
                    linkedCancellationTokenSource.Dispose();
                }

                if (reasonableTimeoutCancellationTokenSource != null)
                {
                    reasonableTimeoutCancellationTokenSource.Dispose();
                }

                if (WebSocketBase.LoggingEnabled)
                {
                    Logging.Exit(Logging.WebSockets, this, Methods.CloseNetworkConnectionAsync, bytesRead);
                }
            }
        }
            internal Task <int> ReadAsyncCore(byte[] buffer, int offset, int count, CancellationToken cancellationToken,
                                              bool ignoreReadError)
            {
                if (WebSocketBase.LoggingEnabled)
                {
                    Logging.Enter(Logging.WebSockets, this, Methods.ReadAsyncCore,
                                  WebSocketHelpers.GetTraceMsgForParameters(offset, count, cancellationToken));
                }

                bool completedAsynchronously = false;

                m_IgnoreReadError = ignoreReadError;
                try
                {
                    cancellationToken.ThrowIfCancellationRequested();
#if DEBUG
                    // When using fast path only one outstanding read is permitted. By switching into opaque mode
                    // via IWebSocketStream.SwitchToOpaqueMode (see more detailed comments in interface definition)
                    // caller takes responsibility for enforcing this constraint.
                    Contract.Assert(Interlocked.Increment(ref m_OutstandingOperations.m_Reads) == 1,
                                    "Only one outstanding read allowed at any given time.");
#endif
                    WebSocketHelpers.ThrowIfConnectionAborted(m_InnerStream, true);
                    m_ReadTaskCompletionSource = new TaskCompletionSource <int>();
                    Contract.Assert(m_ReadEventArgs != null, "'m_ReadEventArgs' MUST NOT be NULL.");
                    m_ReadEventArgs.SetBuffer(buffer, offset, count);
                    Socket innerSocket;
                    if (ignoreReadError)
                    {
                        // The State of the WebSocket instance is already Closed at this point
                        // Skipping call to WebSocketBase.ThrowIfClosedOrAborted
                        innerSocket = GetInnerSocket(true);
                    }
                    else
                    {
                        innerSocket = InnerSocket;
                    }
                    completedAsynchronously = innerSocket.ReceiveAsync(m_ReadEventArgs);
                    if (!completedAsynchronously)
                    {
                        if (m_ReadEventArgs.SocketError != SocketError.Success)
                        {
                            if (!m_IgnoreReadError)
                            {
                                throw new SocketException(m_ReadEventArgs.SocketError);
                            }
                            else
                            {
                                return(Task.FromResult <int>(0));
                            }
                        }

                        return(Task.FromResult <int>(m_ReadEventArgs.BytesTransferred));
                    }

                    return(m_ReadTaskCompletionSource.Task);
                }
                finally
                {
                    if (WebSocketBase.LoggingEnabled)
                    {
                        Logging.Exit(Logging.WebSockets, this, Methods.ReadAsyncCore, completedAsynchronously);
                    }
                }
            }
        private static async Task <HttpListenerWebSocketContext> AcceptWebSocketAsyncCore(HttpListenerContext context,
                                                                                          string subProtocol,
                                                                                          int receiveBufferSize,
                                                                                          TimeSpan keepAliveInterval,
                                                                                          ArraySegment <byte> internalBuffer)
        {
            HttpListenerWebSocketContext webSocketContext = null;

            if (Logging.On)
            {
                Logging.Enter(Logging.WebSockets, context, "AcceptWebSocketAsync", "");
            }

            try
            {
                // get property will create a new response if one doesn't exist.
                HttpListenerResponse response = context.Response;
                HttpListenerRequest  request  = context.Request;
                ValidateWebSocketHeaders(context);

                string secWebSocketVersion = request.Headers[HttpKnownHeaderNames.SecWebSocketVersion];

                // Optional for non-browser client
                string origin = request.Headers[HttpKnownHeaderNames.Origin];

                List <string> secWebSocketProtocols = new List <string>();
                string        outgoingSecWebSocketProtocolString;
                bool          shouldSendSecWebSocketProtocolHeader =
                    WebSocketHelpers.ProcessWebSocketProtocolHeader(
                        request.Headers[HttpKnownHeaderNames.SecWebSocketProtocol],
                        subProtocol,
                        out outgoingSecWebSocketProtocolString);

                if (shouldSendSecWebSocketProtocolHeader)
                {
                    secWebSocketProtocols.Add(outgoingSecWebSocketProtocolString);
                    response.Headers.Add(HttpKnownHeaderNames.SecWebSocketProtocol,
                                         outgoingSecWebSocketProtocolString);
                }

                // negotiate the websocket key return value
                string secWebSocketKey    = request.Headers[HttpKnownHeaderNames.SecWebSocketKey];
                string secWebSocketAccept = WebSocketHelpers.GetSecWebSocketAcceptString(secWebSocketKey);

                response.Headers.Add(HttpKnownHeaderNames.Connection, HttpKnownHeaderNames.Upgrade);
                response.Headers.Add(HttpKnownHeaderNames.Upgrade, WebSocketHelpers.WebSocketUpgradeToken);
                response.Headers.Add(HttpKnownHeaderNames.SecWebSocketAccept, secWebSocketAccept);

                response.StatusCode = (int)HttpStatusCode.SwitchingProtocols; // HTTP 101
                response.ComputeCoreHeaders();
                ulong hresult = SendWebSocketHeaders(response);
                if (hresult != 0)
                {
                    throw new WebSocketException((int)hresult,
                                                 SR.GetString(SR.net_WebSockets_NativeSendResponseHeaders,
                                                              WebSocketHelpers.MethodNames.AcceptWebSocketAsync,
                                                              hresult));
                }

                if (Logging.On)
                {
                    Logging.PrintInfo(Logging.WebSockets, string.Format("{0} = {1}",
                                                                        HttpKnownHeaderNames.Origin, origin));
                    Logging.PrintInfo(Logging.WebSockets, string.Format("{0} = {1}",
                                                                        HttpKnownHeaderNames.SecWebSocketVersion, secWebSocketVersion));
                    Logging.PrintInfo(Logging.WebSockets, string.Format("{0} = {1}",
                                                                        HttpKnownHeaderNames.SecWebSocketKey, secWebSocketKey));
                    Logging.PrintInfo(Logging.WebSockets, string.Format("{0} = {1}",
                                                                        HttpKnownHeaderNames.SecWebSocketAccept, secWebSocketAccept));
                    Logging.PrintInfo(Logging.WebSockets, string.Format("Request  {0} = {1}",
                                                                        HttpKnownHeaderNames.SecWebSocketProtocol,
                                                                        request.Headers[HttpKnownHeaderNames.SecWebSocketProtocol]));
                    Logging.PrintInfo(Logging.WebSockets, string.Format("Response {0} = {1}",
                                                                        HttpKnownHeaderNames.SecWebSocketProtocol, outgoingSecWebSocketProtocolString));
                }

                await response.OutputStream.FlushAsync().SuppressContextFlow();

                HttpResponseStream responseStream = response.OutputStream as HttpResponseStream;
                Contract.Assert(responseStream != null, "'responseStream' MUST be castable to System.Net.HttpResponseStream.");
                ((HttpResponseStream)response.OutputStream).SwitchToOpaqueMode();
                HttpRequestStream requestStream = new HttpRequestStream(context);
                requestStream.SwitchToOpaqueMode();
                WebSocketHttpListenerDuplexStream webSocketStream =
                    new WebSocketHttpListenerDuplexStream(requestStream, responseStream, context);
                WebSocket webSocket = WebSocket.CreateServerWebSocket(webSocketStream,
                                                                      subProtocol,
                                                                      receiveBufferSize,
                                                                      keepAliveInterval,
                                                                      internalBuffer);

                webSocketContext = new HttpListenerWebSocketContext(
                    request.Url,
                    request.Headers,
                    request.Cookies,
                    context.User,
                    request.IsAuthenticated,
                    request.IsLocal,
                    request.IsSecureConnection,
                    origin,
                    secWebSocketProtocols.AsReadOnly(),
                    secWebSocketVersion,
                    secWebSocketKey,
                    webSocket);

                if (Logging.On)
                {
                    Logging.Associate(Logging.WebSockets, context, webSocketContext);
                    Logging.Associate(Logging.WebSockets, webSocketContext, webSocket);
                }
            }
            catch (Exception ex)
            {
                if (Logging.On)
                {
                    Logging.Exception(Logging.WebSockets, context, "AcceptWebSocketAsync", ex);
                }
                throw;
            }
            finally
            {
                if (Logging.On)
                {
                    Logging.Exit(Logging.WebSockets, context, "AcceptWebSocketAsync", "");
                }
            }

            return(webSocketContext);
        }