UNPKG

@es-react/react

Version:

Hippy react framework

225 lines (199 loc) 7.54 kB
/* * Tencent is pleased to support the open source community by making * Hippy available. * * Copyright (C) 2017-2019 THL A29 Limited, a Tencent company. * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import HippyEventListener from '../event/listener'; import { Bridge } from '../global'; import { warn } from '../utils'; import {HippyTypes} from '../types' const enum ReadyState { CONNECTING, OPEN, CLOSING, CLOSED, } const WEB_SOCKET_MODULE_NAME = 'websocket'; let websocketEventHub: HippyEventListener; /** * The WebSocket API is an advanced technology that makes it possible to open a two-way * interactive communication session between the user's browser and a server. With this API, * you can send messages to a server and receive event-driven responses without having to * poll the server for a reply. */ class WebSocket implements HippyTypes.WebSocket { public protocol = ''; public url: string; public readyState: number; public webSocketCallbackId: number; public webSocketId: number | undefined; public readonly webSocketCallbacks: { onOpen?: (...args: any[]) => void; onClose?: (...args: any[]) => void; onError?: (...args: any[]) => void; onMessage?: (...args: any[]) => void; }; /** * Returns a newly created WebSocket object. * * @param {string} url - The URL to which to connect; this should be the URL to which the * WebSocket server will respond. * @param {string | string[]} [protocols] - Either a single protocol string or an array * of protocol strings. These strings are used to * indicate sub-protocols, so that a single server * can implement multiple WebSocket sub-protocols * (for example, you might want one server to be able * to handle different types of interactions depending * on the specified protocol). * If you don't specify a protocol string, an empty * string is assumed. * @param {Object} extrasHeaders - Http headers will append to connection. */ public constructor(url: any, protocols: string[] | string, extrasHeaders: {[key: string]: string}) { this.onWebSocketEvent = this.onWebSocketEvent.bind(this); if (!websocketEventHub) { websocketEventHub = new HippyEventListener('hippyWebsocketEvents'); } this.readyState = ReadyState.CONNECTING; this.webSocketCallbacks = {}; if (!url || typeof url !== 'string') { throw new TypeError('Invalid WebSocket url'); } const headers: { [key: string]: string; } = { ...extrasHeaders, }; if (protocols !== undefined) { if (Array.isArray(protocols) && protocols.length > 0) { headers['Sec-WebSocket-Protocol'] = protocols.join(','); } else if (typeof protocols === 'string') { headers['Sec-WebSocket-Protocol'] = protocols; } else { throw new TypeError('Invalid WebSocket protocols'); } } const params = { headers, url, }; this.url = url; this.webSocketCallbackId = websocketEventHub.addCallback(this.onWebSocketEvent); Bridge.callNativeWithPromise(WEB_SOCKET_MODULE_NAME, 'connect', params) .then((resp: { code: number, id: number | undefined } | any) => { if (!resp || resp.code !== 0 || typeof resp.id !== 'number') { warn('Fail to create websocket connection', resp); return; } this.webSocketId = resp.id; }); } /** * Closes the WebSocket connection or connection attempt, if any. * If the connection is already CLOSED, this method does nothing. * * @param {number} [code] - A numeric value indicating the status code explaining * why the connection is being closed. If this parameter * is not specified, a default value of 1005 is assumed. * See the list of status codes of CloseEvent for permitted values. * @param {string} [reason] - A human-readable string explaining why the connection * is closing. This string must be no longer than 123 bytes * of UTF-8 text (not characters). */ public close(code: number, reason: string) { if (this.readyState !== ReadyState.OPEN) { warn('WebSocket is not connected'); return; } this.readyState = ReadyState.CLOSING; Bridge.callNative(WEB_SOCKET_MODULE_NAME, 'close', { id: this.webSocketId, code, reason, }); } /** * Enqueues the specified data to be transmitted to the server over the WebSocket connection. * * @param {string} data - The data to send to the server. Hippy supports string type only. */ public send(data: string | undefined) { if (this.readyState !== ReadyState.OPEN) { warn('WebSocket is not connected'); return; } if (typeof data !== 'string') { throw new TypeError(`Unsupported websocket data type: ${typeof data}`); } Bridge.callNative(WEB_SOCKET_MODULE_NAME, 'send', { id: this.webSocketId, data, }); } /** * Set an EventHandler that is called when the WebSocket connection's readyState changes to OPEN; */ public set onopen(callback: () => void) { this.webSocketCallbacks.onOpen = callback; } /** * Set an EventHandler that is called when the WebSocket connection's readyState * changes to CLOSED. */ public set onclose(callback: () => void) { this.webSocketCallbacks.onClose = callback; } /** * Set an EventHandler that is called when a message is received from the server. */ public set onerror(callback: () => void) { this.webSocketCallbacks.onError = callback; } /** * Set an event handler property is a function which gets called when an error * occurs on the WebSocket. */ public set onmessage(callback: (data: any) => void) { this.webSocketCallbacks.onMessage = callback; } /** * WebSocket events handler from Native. * * @param {Object} param - Native response. */ private onWebSocketEvent<T>(param: { id: number; type: 'onOpen' | 'onClose'; data: T; }) { if (typeof param !== 'object' || param.id !== this.webSocketId) { return; } const { type: eventType } = param; if (eventType === 'onOpen') { this.readyState = ReadyState.OPEN; } else if (eventType === 'onClose') { this.readyState = ReadyState.CLOSED; websocketEventHub.removeCallback(this.webSocketCallbackId); } const callback = this.webSocketCallbacks[eventType]; if (typeof callback === 'function') { callback(param.data); } } } export default WebSocket;