Esempio n. 1
0
        private async Task ConnectAsyncJavaScript(Uri uri, CancellationToken cancellationToken)
        {
            var tcsConnect = new TaskCompletionSource <bool> ();

            // For Abort/Dispose.  Calling Abort on the request at any point will close the connection.
            cts.Token.Register(AbortRequest);

            // Wrap the cancellationToken in a using so that it can be disposed of whether
            // we successfully connected or failed trying.
            // Otherwise any timeout/cancellation would apply to the full session.
            // In the failure case we need to release the references and dispose of the objects.
            using (cancellationToken.Register(() => tcsConnect.TrySetCanceled())) {
                try {
                    Core.Array subProtocols = null;
                    if (Options.RequestedSubProtocols.Count > 0)
                    {
                        subProtocols = new Core.Array();
                        foreach (var item in Options.RequestedSubProtocols)
                        {
                            subProtocols.Push(item);
                        }
                    }
                    innerWebSocket = new HostObject("WebSocket", uri.ToString(), subProtocols);

                    subProtocols?.Dispose();

                    // Setup the onError callback
                    onError = new Action <JSObject> ((errorEvt) => {
                        errorEvt.Dispose();
                    });

                    // Attach the onError callback
                    innerWebSocket.SetObjectProperty("onerror", onError);

                    // Setup the onClose callback
                    onClose = new Action <JSObject> ((closeEvt) => {
                        innerWebSocketCloseStatus            = (WebSocketCloseStatus)closeEvt.GetObjectProperty("code");
                        innerWebSocketCloseStatusDescription = closeEvt.GetObjectProperty("reason")?.ToString();
                        var mess = new ReceivePayload(WebSocketHelpers.EmptyPayload, WebSocketMessageType.Close);
                        receiveMessageQueue.BufferPayload(mess);

                        if (!tcsConnect.Task.IsCanceled && !tcsConnect.Task.IsCompleted && !tcsConnect.Task.IsFaulted)
                        {
                            tcsConnect.SetException(new WebSocketException(WebSocketError.NativeError));
                        }
                        else
                        {
                            tcsClose?.SetResult(true);
                        }

                        closeEvt.Dispose();
                    });

                    // Attach the onClose callback
                    innerWebSocket.SetObjectProperty("onclose", onClose);

                    // Setup the onOpen callback
                    onOpen = new Action <JSObject> ((evt) => {
                        if (!cancellationToken.IsCancellationRequested)
                        {
                            // Change internal state to 'connected' to enable the other methods
                            if (Interlocked.CompareExchange(ref state, connected, connecting) != connecting)
                            {
                                // Aborted/Disposed during connect.
                                throw new ObjectDisposedException(GetType().FullName);
                            }

                            tcsConnect.SetResult(true);
                        }

                        evt.Dispose();
                    });

                    // Attach the onOpen callback
                    innerWebSocket.SetObjectProperty("onopen", onOpen);

                    // Setup the onMessage callback
                    onMessage = new Action <JSObject> ((messageEvent) => {
                        ThrowIfNotConnected();

                        // get the events "data"
                        var eventData = messageEvent.GetObjectProperty("data");

                        // If the messageEvent's data property is marshalled as a JSObject then we are dealing with
                        // binary data
                        if (eventData is JSObject)
                        {
                            // TODO: Handle ArrayBuffer binary type but have only seen 'blob' so far without
                            // changing the default websocket binary type manually.
                            if (innerWebSocket.GetObjectProperty("binaryType").ToString() == "blob")
                            {
                                Action <JSObject> loadend = null;
                                // Create a new "FileReader" object
                                using (var reader = new HostObject("FileReader")) {
                                    loadend = new Action <JSObject> ((loadEvent) => {
                                        using (var target = (JSObject)loadEvent.GetObjectProperty("target")) {
                                            if ((int)target.GetObjectProperty("readyState") == 2)
                                            {
                                                using (var binResult = (ArrayBuffer)target.GetObjectProperty("result")) {
                                                    var mess = new ReceivePayload(binResult, WebSocketMessageType.Binary);
                                                    receiveMessageQueue.BufferPayload(mess);
                                                    Runtime.FreeObject(loadend);
                                                }
                                            }
                                        }
                                        loadEvent.Dispose();
                                    });

                                    reader.Invoke("addEventListener", "loadend", loadend);

                                    using (var blobData = (JSObject)messageEvent.GetObjectProperty("data"))
                                        reader.Invoke("readAsArrayBuffer", blobData);
                                }
                            }
                            else
                            {
                                throw new NotImplementedException($"WebSocket bynary type '{innerWebSocket.GetObjectProperty ("binaryType").ToString ()}' not supported.");
                            }
                        }
                        else if (eventData is string)
                        {
                            var mess = new ReceivePayload(Encoding.UTF8.GetBytes(((string)eventData).ToString()), WebSocketMessageType.Text);
                            receiveMessageQueue.BufferPayload(mess);
                        }
                        messageEvent.Dispose();
                    });

                    // Attach the onMessage callaback
                    innerWebSocket.SetObjectProperty("onmessage", onMessage);

                    await tcsConnect.Task;
                } catch (Exception wse) {
                    ConnectExceptionCleanup();
                    WebSocketException wex = new WebSocketException("WebSocket connection failure.", wse);
                    throw wex;
                }
            }
        }