mirror of
https://github.com/status-im/react-native.git
synced 2025-01-16 04:24:15 +00:00
4b6e9d3dfd
Summary: This reverts commit b9be28915cf323eb36f1d7c77821cdf994954074. Thank you for sending the PR! We appreciate you spending the time to work on these changes. Help us understand your motivation by explaining why you decided to make this change. <!-- Required: Write your motivation here. If this PR fixes an issue, type "Fixes #issueNumber" to automatically close the issue when the PR is merged. --> Fix #18696 <!-- Required: Write your test plan here. If you changed any code, please provide us with clear instructions on how you verified your changes work. Bonus points for screenshots and videos! --> Run Jest tests. Run WebSocket example from RNTester on Android. <!-- Does this PR require a documentation change? Create a PR at https://github.com/facebook/react-native-website and add a link to it here. --> <!-- Required. Help reviewers and the release process by writing your own release notes. See below for an example. --> [CATEGORY] [TYPE] [LOCATION] - Message <!-- **INTERNAL and MINOR tagged notes will not be included in the next version's final release notes.** CATEGORY [----------] TYPE [ CLI ] [-------------] LOCATION [ DOCS ] [ BREAKING ] [-------------] [ GENERAL ] [ BUGFIX ] [ {Component} ] [ INTERNAL ] [ ENHANCEMENT ] [ {Filename} ] [ IOS ] [ FEATURE ] [ {Directory} ] |-----------| [ ANDROID ] [ MINOR ] [ {Framework} ] - | {Message} | [----------] [-------------] [-------------] |-----------| EXAMPLES: [IOS] [BREAKING] [FlatList] - Change a thing that breaks other things [ANDROID] [BUGFIX] [TextInput] - Did a thing to TextInput [CLI] [FEATURE] [local-cli/info/info.js] - CLI easier to do things with [DOCS] [BUGFIX] [GettingStarted.md] - Accidentally a thing/word [GENERAL] [ENHANCEMENT] [Yoga] - Added new yoga thing/position [INTERNAL] [FEATURE] [./scripts] - Added thing to script that nobody will see --> [ANDROID][BUGFIX][WebSocketModule] - revert change that regresses WebSocketModule Closes https://github.com/facebook/react-native/pull/18733 Differential Revision: D7548850 Pulled By: hramos fbshipit-source-id: b8c79810c1cd6e5a30ec4118bd5ff8ad719f04b9
283 lines
8.3 KiB
JavaScript
283 lines
8.3 KiB
JavaScript
/**
|
|
* Copyright (c) 2015-present, Facebook, Inc.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* @providesModule WebSocket
|
|
* @flow
|
|
*/
|
|
'use strict';
|
|
|
|
const Blob = require('Blob');
|
|
const EventTarget = require('event-target-shim');
|
|
const NativeEventEmitter = require('NativeEventEmitter');
|
|
const BlobManager = require('BlobManager');
|
|
const NativeModules = require('NativeModules');
|
|
const Platform = require('Platform');
|
|
const WebSocketEvent = require('WebSocketEvent');
|
|
|
|
/* $FlowFixMe(>=0.54.0 site=react_native_oss) This comment suppresses an error
|
|
* found when Flow v0.54 was deployed. To see the error delete this comment and
|
|
* run Flow. */
|
|
const base64 = require('base64-js');
|
|
const binaryToBase64 = require('binaryToBase64');
|
|
const invariant = require('fbjs/lib/invariant');
|
|
|
|
const {WebSocketModule} = NativeModules;
|
|
|
|
import type EventSubscription from 'EventSubscription';
|
|
|
|
type ArrayBufferView =
|
|
| Int8Array
|
|
| Uint8Array
|
|
| Uint8ClampedArray
|
|
| Int16Array
|
|
| Uint16Array
|
|
| Int32Array
|
|
| Uint32Array
|
|
| Float32Array
|
|
| Float64Array
|
|
| DataView
|
|
|
|
type BinaryType = 'blob' | 'arraybuffer'
|
|
|
|
const CONNECTING = 0;
|
|
const OPEN = 1;
|
|
const CLOSING = 2;
|
|
const CLOSED = 3;
|
|
|
|
const CLOSE_NORMAL = 1000;
|
|
|
|
const WEBSOCKET_EVENTS = [
|
|
'close',
|
|
'error',
|
|
'message',
|
|
'open',
|
|
];
|
|
|
|
let nextWebSocketId = 0;
|
|
|
|
/**
|
|
* Browser-compatible WebSockets implementation.
|
|
*
|
|
* See https://developer.mozilla.org/en-US/docs/Web/API/WebSocket
|
|
* See https://github.com/websockets/ws
|
|
*/
|
|
class WebSocket extends EventTarget(...WEBSOCKET_EVENTS) {
|
|
static CONNECTING = CONNECTING;
|
|
static OPEN = OPEN;
|
|
static CLOSING = CLOSING;
|
|
static CLOSED = CLOSED;
|
|
|
|
CONNECTING: number = CONNECTING;
|
|
OPEN: number = OPEN;
|
|
CLOSING: number = CLOSING;
|
|
CLOSED: number = CLOSED;
|
|
|
|
_socketId: number;
|
|
_eventEmitter: NativeEventEmitter;
|
|
_subscriptions: Array<EventSubscription>;
|
|
_binaryType: ?BinaryType;
|
|
|
|
onclose: ?Function;
|
|
onerror: ?Function;
|
|
onmessage: ?Function;
|
|
onopen: ?Function;
|
|
|
|
bufferedAmount: number;
|
|
extension: ?string;
|
|
protocol: ?string;
|
|
readyState: number = CONNECTING;
|
|
url: ?string;
|
|
|
|
// This module depends on the native `WebSocketModule` module. If you don't include it,
|
|
// `WebSocket.isAvailable` will return `false`, and WebSocket constructor will throw an error
|
|
static isAvailable: boolean = !!WebSocketModule;
|
|
|
|
constructor(url: string, protocols: ?string | ?Array<string>, options: ?{headers?: {origin?: string}}) {
|
|
super();
|
|
if (typeof protocols === 'string') {
|
|
protocols = [protocols];
|
|
}
|
|
|
|
const {headers = {}, ...unrecognized} = options || {};
|
|
|
|
// Preserve deprecated backwards compatibility for the 'origin' option
|
|
/* $FlowFixMe(>=0.68.0 site=react_native_fb) This comment suppresses an
|
|
* error found when Flow v0.68 was deployed. To see the error delete this
|
|
* comment and run Flow. */
|
|
if (unrecognized && typeof unrecognized.origin === 'string') {
|
|
console.warn('Specifying `origin` as a WebSocket connection option is deprecated. Include it under `headers` instead.');
|
|
/* $FlowFixMe(>=0.54.0 site=react_native_fb,react_native_oss) This
|
|
* comment suppresses an error found when Flow v0.54 was deployed. To see
|
|
* the error delete this comment and run Flow. */
|
|
headers.origin = unrecognized.origin;
|
|
/* $FlowFixMe(>=0.54.0 site=react_native_fb,react_native_oss) This
|
|
* comment suppresses an error found when Flow v0.54 was deployed. To see
|
|
* the error delete this comment and run Flow. */
|
|
delete unrecognized.origin;
|
|
}
|
|
|
|
// Warn about and discard anything else
|
|
if (Object.keys(unrecognized).length > 0) {
|
|
console.warn('Unrecognized WebSocket connection option(s) `' + Object.keys(unrecognized).join('`, `') + '`. '
|
|
+ 'Did you mean to put these under `headers`?');
|
|
}
|
|
|
|
if (!Array.isArray(protocols)) {
|
|
protocols = null;
|
|
}
|
|
|
|
if (!WebSocket.isAvailable) {
|
|
throw new Error('Cannot initialize WebSocket module. ' +
|
|
'Native module WebSocketModule is missing.');
|
|
}
|
|
|
|
this._eventEmitter = new NativeEventEmitter(WebSocketModule);
|
|
this._socketId = nextWebSocketId++;
|
|
this._registerEvents();
|
|
WebSocketModule.connect(url, protocols, { headers }, this._socketId);
|
|
}
|
|
|
|
get binaryType(): ?BinaryType {
|
|
return this._binaryType;
|
|
}
|
|
|
|
set binaryType(binaryType: BinaryType): void {
|
|
if (binaryType !== 'blob' && binaryType !== 'arraybuffer') {
|
|
throw new Error('binaryType must be either \'blob\' or \'arraybuffer\'');
|
|
}
|
|
if (this._binaryType === 'blob' || binaryType === 'blob') {
|
|
invariant(BlobManager.isAvailable, 'Native module BlobModule is required for blob support');
|
|
if (binaryType === 'blob') {
|
|
BlobManager.addWebSocketHandler(this._socketId);
|
|
} else {
|
|
BlobManager.removeWebSocketHandler(this._socketId);
|
|
}
|
|
}
|
|
this._binaryType = binaryType;
|
|
}
|
|
|
|
get binaryType(): ?BinaryType {
|
|
return this._binaryType;
|
|
}
|
|
|
|
close(code?: number, reason?: string): void {
|
|
if (this.readyState === this.CLOSING ||
|
|
this.readyState === this.CLOSED) {
|
|
return;
|
|
}
|
|
|
|
this.readyState = this.CLOSING;
|
|
this._close(code, reason);
|
|
}
|
|
|
|
send(data: string | ArrayBuffer | ArrayBufferView | Blob): void {
|
|
if (this.readyState === this.CONNECTING) {
|
|
throw new Error('INVALID_STATE_ERR');
|
|
}
|
|
|
|
if (data instanceof Blob) {
|
|
invariant(BlobManager.isAvailable, 'Native module BlobModule is required for blob support');
|
|
BlobManager.sendOverSocket(data, this._socketId);
|
|
return;
|
|
}
|
|
|
|
if (typeof data === 'string') {
|
|
WebSocketModule.send(data, this._socketId);
|
|
return;
|
|
}
|
|
|
|
if (data instanceof ArrayBuffer || ArrayBuffer.isView(data)) {
|
|
WebSocketModule.sendBinary(binaryToBase64(data), this._socketId);
|
|
return;
|
|
}
|
|
|
|
throw new Error('Unsupported data type');
|
|
}
|
|
|
|
ping(): void {
|
|
if (this.readyState === this.CONNECTING) {
|
|
throw new Error('INVALID_STATE_ERR');
|
|
}
|
|
|
|
WebSocketModule.ping(this._socketId);
|
|
}
|
|
|
|
_close(code?: number, reason?: string): void {
|
|
if (Platform.OS === 'android') {
|
|
// See https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent
|
|
const statusCode = typeof code === 'number' ? code : CLOSE_NORMAL;
|
|
const closeReason = typeof reason === 'string' ? reason : '';
|
|
WebSocketModule.close(statusCode, closeReason, this._socketId);
|
|
} else {
|
|
WebSocketModule.close(this._socketId);
|
|
}
|
|
|
|
if (BlobManager.isAvailable && this._binaryType === 'blob') {
|
|
BlobManager.removeWebSocketHandler(this._socketId);
|
|
}
|
|
}
|
|
|
|
_unregisterEvents(): void {
|
|
this._subscriptions.forEach(e => e.remove());
|
|
this._subscriptions = [];
|
|
}
|
|
|
|
_registerEvents(): void {
|
|
this._subscriptions = [
|
|
this._eventEmitter.addListener('websocketMessage', ev => {
|
|
if (ev.id !== this._socketId) {
|
|
return;
|
|
}
|
|
let data = ev.data;
|
|
switch (ev.type) {
|
|
case 'binary':
|
|
data = base64.toByteArray(ev.data).buffer;
|
|
break;
|
|
case 'blob':
|
|
data = BlobManager.createFromOptions(ev.data);
|
|
break;
|
|
}
|
|
this.dispatchEvent(new WebSocketEvent('message', { data }));
|
|
}),
|
|
this._eventEmitter.addListener('websocketOpen', ev => {
|
|
if (ev.id !== this._socketId) {
|
|
return;
|
|
}
|
|
this.readyState = this.OPEN;
|
|
this.dispatchEvent(new WebSocketEvent('open'));
|
|
}),
|
|
this._eventEmitter.addListener('websocketClosed', ev => {
|
|
if (ev.id !== this._socketId) {
|
|
return;
|
|
}
|
|
this.readyState = this.CLOSED;
|
|
this.dispatchEvent(new WebSocketEvent('close', {
|
|
code: ev.code,
|
|
reason: ev.reason,
|
|
}));
|
|
this._unregisterEvents();
|
|
this.close();
|
|
}),
|
|
this._eventEmitter.addListener('websocketFailed', ev => {
|
|
if (ev.id !== this._socketId) {
|
|
return;
|
|
}
|
|
this.readyState = this.CLOSED;
|
|
this.dispatchEvent(new WebSocketEvent('error', {
|
|
message: ev.message,
|
|
}));
|
|
this.dispatchEvent(new WebSocketEvent('close', {
|
|
message: ev.message,
|
|
}));
|
|
this._unregisterEvents();
|
|
this.close();
|
|
})
|
|
];
|
|
}
|
|
}
|
|
|
|
module.exports = WebSocket;
|