mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-04-29 12:45:37 +08:00
Implement WebSocket module for Android. Fixes #2837
Summary: The JavaScript code for Android is same as the iOS counterpart, I just added few new lines and used arrow functions instead of binding `this`. Closes https://github.com/facebook/react-native/pull/2839 Reviewed By: @svcscm, @vjeux Differential Revision: D2498703 Pulled By: @mkonicek fb-gh-sync-id: 3fe958dd5af0efba00df07515f8e33b5d87eb05b
This commit is contained in:
committed by
facebook-github-bot-8
parent
57a7387d85
commit
f4857a6d42
@@ -9,7 +9,7 @@
|
|||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
1338BBE01B04ACC80064A9C9 /* RCTSRWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 1338BBDD1B04ACC80064A9C9 /* RCTSRWebSocket.m */; };
|
1338BBE01B04ACC80064A9C9 /* RCTSRWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 1338BBDD1B04ACC80064A9C9 /* RCTSRWebSocket.m */; };
|
||||||
1338BBE11B04ACC80064A9C9 /* RCTWebSocketExecutor.m in Sources */ = {isa = PBXBuildFile; fileRef = 1338BBDF1B04ACC80064A9C9 /* RCTWebSocketExecutor.m */; };
|
1338BBE11B04ACC80064A9C9 /* RCTWebSocketExecutor.m in Sources */ = {isa = PBXBuildFile; fileRef = 1338BBDF1B04ACC80064A9C9 /* RCTWebSocketExecutor.m */; };
|
||||||
3C86DF7C1ADF695F0047B81A /* RCTWebSocketManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 3C86DF7B1ADF695F0047B81A /* RCTWebSocketManager.m */; };
|
3C86DF7C1ADF695F0047B81A /* RCTWebSocketModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 3C86DF7B1ADF695F0047B81A /* RCTWebSocketModule.m */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
@@ -18,8 +18,8 @@
|
|||||||
1338BBDE1B04ACC80064A9C9 /* RCTWebSocketExecutor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTWebSocketExecutor.h; sourceTree = "<group>"; };
|
1338BBDE1B04ACC80064A9C9 /* RCTWebSocketExecutor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTWebSocketExecutor.h; sourceTree = "<group>"; };
|
||||||
1338BBDF1B04ACC80064A9C9 /* RCTWebSocketExecutor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTWebSocketExecutor.m; sourceTree = "<group>"; };
|
1338BBDF1B04ACC80064A9C9 /* RCTWebSocketExecutor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTWebSocketExecutor.m; sourceTree = "<group>"; };
|
||||||
3C86DF461ADF2C930047B81A /* libRCTWebSocket.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTWebSocket.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
3C86DF461ADF2C930047B81A /* libRCTWebSocket.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTWebSocket.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
3C86DF7A1ADF695F0047B81A /* RCTWebSocketManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTWebSocketManager.h; sourceTree = "<group>"; };
|
3C86DF7A1ADF695F0047B81A /* RCTWebSocketModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTWebSocketModule.h; sourceTree = "<group>"; };
|
||||||
3C86DF7B1ADF695F0047B81A /* RCTWebSocketManager.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = RCTWebSocketManager.m; sourceTree = "<group>"; tabWidth = 2; };
|
3C86DF7B1ADF695F0047B81A /* RCTWebSocketModule.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = RCTWebSocketModule.m; sourceTree = "<group>"; tabWidth = 2; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@@ -40,8 +40,8 @@
|
|||||||
1338BBDD1B04ACC80064A9C9 /* RCTSRWebSocket.m */,
|
1338BBDD1B04ACC80064A9C9 /* RCTSRWebSocket.m */,
|
||||||
1338BBDE1B04ACC80064A9C9 /* RCTWebSocketExecutor.h */,
|
1338BBDE1B04ACC80064A9C9 /* RCTWebSocketExecutor.h */,
|
||||||
1338BBDF1B04ACC80064A9C9 /* RCTWebSocketExecutor.m */,
|
1338BBDF1B04ACC80064A9C9 /* RCTWebSocketExecutor.m */,
|
||||||
3C86DF7A1ADF695F0047B81A /* RCTWebSocketManager.h */,
|
3C86DF7A1ADF695F0047B81A /* RCTWebSocketModule.h */,
|
||||||
3C86DF7B1ADF695F0047B81A /* RCTWebSocketManager.m */,
|
3C86DF7B1ADF695F0047B81A /* RCTWebSocketModule.m */,
|
||||||
3C86DF471ADF2C930047B81A /* Products */,
|
3C86DF471ADF2C930047B81A /* Products */,
|
||||||
);
|
);
|
||||||
indentWidth = 2;
|
indentWidth = 2;
|
||||||
@@ -112,7 +112,7 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
1338BBE01B04ACC80064A9C9 /* RCTSRWebSocket.m in Sources */,
|
1338BBE01B04ACC80064A9C9 /* RCTSRWebSocket.m in Sources */,
|
||||||
3C86DF7C1ADF695F0047B81A /* RCTWebSocketManager.m in Sources */,
|
3C86DF7C1ADF695F0047B81A /* RCTWebSocketModule.m in Sources */,
|
||||||
1338BBE11B04ACC80064A9C9 /* RCTWebSocketExecutor.m in Sources */,
|
1338BBE11B04ACC80064A9C9 /* RCTWebSocketExecutor.m in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
|||||||
@@ -9,6 +9,6 @@
|
|||||||
|
|
||||||
#import "RCTBridgeModule.h"
|
#import "RCTBridgeModule.h"
|
||||||
|
|
||||||
@interface RCTWebSocketManager : NSObject <RCTBridgeModule>
|
@interface RCTWebSocketModule : NSObject <RCTBridgeModule>
|
||||||
|
|
||||||
@end
|
@end
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import "RCTWebSocketManager.h"
|
#import "RCTWebSocketModule.h"
|
||||||
|
|
||||||
#import "RCTBridge.h"
|
#import "RCTBridge.h"
|
||||||
#import "RCTEventDispatcher.h"
|
#import "RCTEventDispatcher.h"
|
||||||
@@ -29,11 +29,11 @@
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface RCTWebSocketManager () <RCTSRWebSocketDelegate>
|
@interface RCTWebSocketModule () <RCTSRWebSocketDelegate>
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation RCTWebSocketManager
|
@implementation RCTWebSocketModule
|
||||||
{
|
{
|
||||||
RCTSparseArray *_sockets;
|
RCTSparseArray *_sockets;
|
||||||
}
|
}
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2015-present, Facebook, Inc.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the BSD-style license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree. An additional grant
|
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
|
||||||
*
|
|
||||||
* @providesModule WebSocket
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var WebSocketBase = require('WebSocketBase');
|
|
||||||
|
|
||||||
class WebSocket extends WebSocketBase {
|
|
||||||
|
|
||||||
connectToSocketImpl(url: string): void {
|
|
||||||
console.warn('WebSocket is not yet supported on Android');
|
|
||||||
}
|
|
||||||
|
|
||||||
closeConnectionImpl(): void{
|
|
||||||
console.warn('WebSocket is not yet supported on Android');
|
|
||||||
}
|
|
||||||
|
|
||||||
cancelConnectionImpl(): void {
|
|
||||||
console.warn('WebSocket is not yet supported on Android');
|
|
||||||
}
|
|
||||||
|
|
||||||
sendStringImpl(message: string): void {
|
|
||||||
console.warn('WebSocket is not yet supported on Android');
|
|
||||||
}
|
|
||||||
|
|
||||||
sendArrayBufferImpl(): void {
|
|
||||||
console.warn('WebSocket is not yet supported on Android');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = WebSocket;
|
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2015-present, Facebook, Inc.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the BSD-style license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree. An additional grant
|
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
|
||||||
*
|
|
||||||
* @providesModule WebSocket
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');
|
|
||||||
var RCTWebSocketManager = require('NativeModules').WebSocketManager;
|
|
||||||
|
|
||||||
var WebSocketBase = require('WebSocketBase');
|
|
||||||
|
|
||||||
class Event {
|
|
||||||
constructor(type) {
|
|
||||||
this.type = type.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class MessageEvent extends Event {
|
|
||||||
constructor(type, eventInitDict) {
|
|
||||||
super(type);
|
|
||||||
Object.assign(this, eventInitDict);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var WebSocketId = 0;
|
|
||||||
|
|
||||||
class WebSocket extends WebSocketBase {
|
|
||||||
_socketId: number;
|
|
||||||
_subs: any;
|
|
||||||
|
|
||||||
connectToSocketImpl(url: string): void {
|
|
||||||
this._socketId = WebSocketId++;
|
|
||||||
RCTWebSocketManager.connect(url, this._socketId);
|
|
||||||
this._registerEvents(this._socketId);
|
|
||||||
}
|
|
||||||
|
|
||||||
closeConnectionImpl(): void {
|
|
||||||
RCTWebSocketManager.close(this._socketId);
|
|
||||||
}
|
|
||||||
|
|
||||||
cancelConnectionImpl(): void {
|
|
||||||
RCTWebSocketManager.close(this._socketId);
|
|
||||||
}
|
|
||||||
|
|
||||||
sendStringImpl(message: string): void {
|
|
||||||
RCTWebSocketManager.send(message, this._socketId);
|
|
||||||
}
|
|
||||||
|
|
||||||
sendArrayBufferImpl(): void {
|
|
||||||
// TODO
|
|
||||||
console.warn('Sending ArrayBuffers is not yet supported');
|
|
||||||
}
|
|
||||||
|
|
||||||
_unregisterEvents(): void {
|
|
||||||
this._subs.forEach(e => e.remove());
|
|
||||||
this._subs = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
_registerEvents(id: number): void {
|
|
||||||
this._subs = [
|
|
||||||
RCTDeviceEventEmitter.addListener(
|
|
||||||
'websocketMessage',
|
|
||||||
function(ev) {
|
|
||||||
if (ev.id !== id) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var event = new MessageEvent('message', {
|
|
||||||
data: ev.data
|
|
||||||
});
|
|
||||||
this.onmessage && this.onmessage(event);
|
|
||||||
this.dispatchEvent(event);
|
|
||||||
}.bind(this)
|
|
||||||
),
|
|
||||||
RCTDeviceEventEmitter.addListener(
|
|
||||||
'websocketOpen',
|
|
||||||
function(ev) {
|
|
||||||
if (ev.id !== id) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.readyState = this.OPEN;
|
|
||||||
var event = new Event('open');
|
|
||||||
this.onopen && this.onopen(event);
|
|
||||||
this.dispatchEvent(event);
|
|
||||||
}.bind(this)
|
|
||||||
),
|
|
||||||
RCTDeviceEventEmitter.addListener(
|
|
||||||
'websocketClosed',
|
|
||||||
function(ev) {
|
|
||||||
if (ev.id !== id) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.readyState = this.CLOSED;
|
|
||||||
var event = new Event('close');
|
|
||||||
this.onclose && this.onclose(event);
|
|
||||||
this.dispatchEvent(event);
|
|
||||||
this._unregisterEvents();
|
|
||||||
RCTWebSocketManager.close(id);
|
|
||||||
}.bind(this)
|
|
||||||
),
|
|
||||||
RCTDeviceEventEmitter.addListener(
|
|
||||||
'websocketFailed',
|
|
||||||
function(ev) {
|
|
||||||
if (ev.id !== id) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var event = new Event('error');
|
|
||||||
event.message = ev.message;
|
|
||||||
this.onerror && this.onerror(event);
|
|
||||||
this.dispatchEvent(event);
|
|
||||||
this._unregisterEvents();
|
|
||||||
RCTWebSocketManager.close(id);
|
|
||||||
}.bind(this)
|
|
||||||
)
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = WebSocket;
|
|
||||||
124
Libraries/WebSocket/WebSocket.js
Normal file
124
Libraries/WebSocket/WebSocket.js
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*
|
||||||
|
* @providesModule WebSocket
|
||||||
|
*/
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');
|
||||||
|
var RCTWebSocketModule = require('NativeModules').WebSocketModule;
|
||||||
|
|
||||||
|
var Platform = require('Platform');
|
||||||
|
var WebSocketBase = require('WebSocketBase');
|
||||||
|
var WebSocketEvent = require('WebSocketEvent');
|
||||||
|
|
||||||
|
var WebSocketId = 0;
|
||||||
|
var CLOSE_NORMAL = 1000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Browser-compatible WebSockets implementation.
|
||||||
|
*
|
||||||
|
* See https://developer.mozilla.org/en-US/docs/Web/API/WebSocket
|
||||||
|
*/
|
||||||
|
class WebSocket extends WebSocketBase {
|
||||||
|
_socketId: number;
|
||||||
|
_subs: any;
|
||||||
|
|
||||||
|
connectToSocketImpl(url: string): void {
|
||||||
|
this._socketId = WebSocketId++;
|
||||||
|
|
||||||
|
RCTWebSocketModule.connect(url, this._socketId);
|
||||||
|
|
||||||
|
this._registerEvents(this._socketId);
|
||||||
|
}
|
||||||
|
|
||||||
|
closeConnectionImpl(code?: number, reason?: string): void {
|
||||||
|
this._closeWebSocket(this._socketId, code, reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelConnectionImpl(): void {
|
||||||
|
this._closeWebSocket(this._socketId);
|
||||||
|
}
|
||||||
|
|
||||||
|
sendStringImpl(message: string): void {
|
||||||
|
RCTWebSocketModule.send(message, this._socketId);
|
||||||
|
}
|
||||||
|
|
||||||
|
sendArrayBufferImpl(): void {
|
||||||
|
// TODO
|
||||||
|
console.warn('Sending ArrayBuffers is not yet supported');
|
||||||
|
}
|
||||||
|
|
||||||
|
_closeWebSocket(id: number, code?: number, reason?: string): void {
|
||||||
|
if (Platform.OS === 'android') {
|
||||||
|
/*
|
||||||
|
* See https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent
|
||||||
|
*/
|
||||||
|
var statusCode = typeof code === 'number' ? code : CLOSE_NORMAL;
|
||||||
|
var closeReason = typeof reason === 'string' ? reason : '';
|
||||||
|
RCTWebSocketModule.close(statusCode, closeReason, id);
|
||||||
|
} else {
|
||||||
|
RCTWebSocketModule.close(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_unregisterEvents(): void {
|
||||||
|
this._subs.forEach(e => e.remove());
|
||||||
|
this._subs = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
_registerEvents(id: number): void {
|
||||||
|
this._subs = [
|
||||||
|
RCTDeviceEventEmitter.addListener('websocketMessage', ev => {
|
||||||
|
if (ev.id !== id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var event = new WebSocketEvent('message', {
|
||||||
|
data: ev.data
|
||||||
|
});
|
||||||
|
this.onmessage && this.onmessage(event);
|
||||||
|
this.dispatchEvent(event);
|
||||||
|
}),
|
||||||
|
RCTDeviceEventEmitter.addListener('websocketOpen', ev => {
|
||||||
|
if (ev.id !== id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.readyState = this.OPEN;
|
||||||
|
var event = new WebSocketEvent('open');
|
||||||
|
this.onopen && this.onopen(event);
|
||||||
|
this.dispatchEvent(event);
|
||||||
|
}),
|
||||||
|
RCTDeviceEventEmitter.addListener('websocketClosed', ev => {
|
||||||
|
if (ev.id !== id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.readyState = this.CLOSED;
|
||||||
|
var event = new WebSocketEvent('close');
|
||||||
|
event.code = ev.code;
|
||||||
|
event.reason = ev.reason;
|
||||||
|
this.onclose && this.onclose(event);
|
||||||
|
this.dispatchEvent(event);
|
||||||
|
this._unregisterEvents();
|
||||||
|
this._closeWebSocket(id);
|
||||||
|
}),
|
||||||
|
RCTDeviceEventEmitter.addListener('websocketFailed', ev => {
|
||||||
|
if (ev.id !== id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var event = new WebSocketEvent('error');
|
||||||
|
event.message = ev.message;
|
||||||
|
this.onerror && this.onerror(event);
|
||||||
|
this.dispatchEvent(event);
|
||||||
|
this._unregisterEvents();
|
||||||
|
this._closeWebSocket(id);
|
||||||
|
})
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = WebSocket;
|
||||||
@@ -7,7 +7,6 @@
|
|||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*
|
*
|
||||||
* @providesModule WebSocketBase
|
* @providesModule WebSocketBase
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
@@ -94,7 +93,6 @@ class WebSocketBase extends EventTarget {
|
|||||||
sendArrayBufferImpl(): void {
|
sendArrayBufferImpl(): void {
|
||||||
throw new Error('Subclass must define sendArrayBufferImpl method');
|
throw new Error('Subclass must define sendArrayBufferImpl method');
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = WebSocketBase;
|
module.exports = WebSocketBase;
|
||||||
|
|||||||
29
Libraries/WebSocket/WebSocketEvent.js
Normal file
29
Libraries/WebSocket/WebSocketEvent.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*
|
||||||
|
* @providesModule WebSocketEvent
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event object passed to the `onopen`, `onclose`, `onmessage`, `onerror`
|
||||||
|
* callbacks of `WebSocket`.
|
||||||
|
*
|
||||||
|
* The `type` property is "open", "close", "message", "error" respectively.
|
||||||
|
*
|
||||||
|
* In case of "message", the `data` property contains the incoming data.
|
||||||
|
*/
|
||||||
|
class WebSocketEvent {
|
||||||
|
constructor(type, eventInitDict) {
|
||||||
|
this.type = type.toString();
|
||||||
|
Object.assign(this, eventInitDict);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = WebSocketEvent;
|
||||||
@@ -0,0 +1,170 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.facebook.react.modules.websocket;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import com.facebook.common.logging.FLog;
|
||||||
|
import com.facebook.react.bridge.Arguments;
|
||||||
|
import com.facebook.react.bridge.ReactApplicationContext;
|
||||||
|
import com.facebook.react.bridge.ReactContext;
|
||||||
|
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||||
|
import com.facebook.react.bridge.ReactMethod;
|
||||||
|
import com.facebook.react.bridge.WritableMap;
|
||||||
|
import com.facebook.react.common.ReactConstants;
|
||||||
|
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
||||||
|
|
||||||
|
import com.squareup.okhttp.OkHttpClient;
|
||||||
|
import com.squareup.okhttp.Request;
|
||||||
|
import com.squareup.okhttp.Response;
|
||||||
|
import com.squareup.okhttp.ws.WebSocket;
|
||||||
|
import com.squareup.okhttp.ws.WebSocketCall;
|
||||||
|
import com.squareup.okhttp.ws.WebSocketListener;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import okio.Buffer;
|
||||||
|
import okio.BufferedSource;
|
||||||
|
|
||||||
|
public class WebSocketModule extends ReactContextBaseJavaModule {
|
||||||
|
|
||||||
|
private Map<Integer, WebSocket> mWebSocketConnections = new HashMap<>();
|
||||||
|
private ReactContext mReactContext;
|
||||||
|
|
||||||
|
public WebSocketModule(ReactApplicationContext context) {
|
||||||
|
super(context);
|
||||||
|
mReactContext = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendEvent(String eventName, WritableMap params) {
|
||||||
|
mReactContext
|
||||||
|
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
|
||||||
|
.emit(eventName, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "WebSocketModule";
|
||||||
|
}
|
||||||
|
|
||||||
|
@ReactMethod
|
||||||
|
public void connect(final String url, final int id) {
|
||||||
|
OkHttpClient client = new OkHttpClient();
|
||||||
|
|
||||||
|
client.setConnectTimeout(10, TimeUnit.SECONDS);
|
||||||
|
client.setWriteTimeout(10, TimeUnit.SECONDS);
|
||||||
|
// Disable timeouts for read
|
||||||
|
client.setReadTimeout(0, TimeUnit.MINUTES);
|
||||||
|
|
||||||
|
Request request = new Request.Builder()
|
||||||
|
.tag(id)
|
||||||
|
.url(url)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
WebSocketCall.create(client, request).enqueue(new WebSocketListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onOpen(WebSocket webSocket, Response response) {
|
||||||
|
mWebSocketConnections.put(id, webSocket);
|
||||||
|
WritableMap params = Arguments.createMap();
|
||||||
|
params.putInt("id", id);
|
||||||
|
sendEvent("websocketOpen", params);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClose(int code, String reason) {
|
||||||
|
WritableMap params = Arguments.createMap();
|
||||||
|
params.putInt("id", id);
|
||||||
|
params.putInt("code", code);
|
||||||
|
params.putString("reason", reason);
|
||||||
|
sendEvent("websocketClosed", params);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(IOException e, Response response) {
|
||||||
|
notifyWebSocketFailed(id, e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPong(Buffer buffer) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMessage(BufferedSource bufferedSource, WebSocket.PayloadType payloadType) {
|
||||||
|
String message;
|
||||||
|
try {
|
||||||
|
message = bufferedSource.readUtf8();
|
||||||
|
} catch (IOException e) {
|
||||||
|
notifyWebSocketFailed(id, e.getMessage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
bufferedSource.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
FLog.e(
|
||||||
|
ReactConstants.TAG,
|
||||||
|
"Could not close BufferedSource for WebSocket id " + id,
|
||||||
|
e);
|
||||||
|
}
|
||||||
|
|
||||||
|
WritableMap params = Arguments.createMap();
|
||||||
|
params.putInt("id", id);
|
||||||
|
params.putString("data", message);
|
||||||
|
sendEvent("websocketMessage", params);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Trigger shutdown of the dispatcher's executor so this process can exit cleanly
|
||||||
|
client.getDispatcher().getExecutorService().shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@ReactMethod
|
||||||
|
public void close(int code, String reason, int id) {
|
||||||
|
WebSocket client = mWebSocketConnections.get(id);
|
||||||
|
if (client == null) {
|
||||||
|
// This is a programmer error
|
||||||
|
throw new RuntimeException("Cannot close WebSocket. Unknown WebSocket id " + id);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
client.close(code, reason);
|
||||||
|
mWebSocketConnections.remove(id);
|
||||||
|
} catch (Exception e) {
|
||||||
|
FLog.e(
|
||||||
|
ReactConstants.TAG,
|
||||||
|
"Could not close WebSocket connection for id " + id,
|
||||||
|
e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ReactMethod
|
||||||
|
public void send(String message, int id) {
|
||||||
|
WebSocket client = mWebSocketConnections.get(id);
|
||||||
|
if (client == null) {
|
||||||
|
// This is a programmer error
|
||||||
|
throw new RuntimeException("Cannot send a message. Unknown WebSocket id " + id);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
client.sendMessage(
|
||||||
|
WebSocket.PayloadType.TEXT,
|
||||||
|
new Buffer().writeUtf8(message));
|
||||||
|
} catch (IOException e) {
|
||||||
|
notifyWebSocketFailed(id, e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void notifyWebSocketFailed(int id, String message) {
|
||||||
|
WritableMap params = Arguments.createMap();
|
||||||
|
params.putInt("id", id);
|
||||||
|
params.putString("message", message);
|
||||||
|
sendEvent("websocketFailed", params);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,6 +21,7 @@ import com.facebook.react.modules.fresco.FrescoModule;
|
|||||||
import com.facebook.react.modules.network.NetworkingModule;
|
import com.facebook.react.modules.network.NetworkingModule;
|
||||||
import com.facebook.react.modules.storage.AsyncStorageModule;
|
import com.facebook.react.modules.storage.AsyncStorageModule;
|
||||||
import com.facebook.react.modules.toast.ToastModule;
|
import com.facebook.react.modules.toast.ToastModule;
|
||||||
|
import com.facebook.react.modules.websocket.WebSocketModule;
|
||||||
import com.facebook.react.uimanager.ViewManager;
|
import com.facebook.react.uimanager.ViewManager;
|
||||||
import com.facebook.react.views.drawer.ReactDrawerLayoutManager;
|
import com.facebook.react.views.drawer.ReactDrawerLayoutManager;
|
||||||
import com.facebook.react.views.image.ReactImageManager;
|
import com.facebook.react.views.image.ReactImageManager;
|
||||||
@@ -47,6 +48,7 @@ public class MainReactPackage implements ReactPackage {
|
|||||||
new AsyncStorageModule(reactContext),
|
new AsyncStorageModule(reactContext),
|
||||||
new FrescoModule(reactContext),
|
new FrescoModule(reactContext),
|
||||||
new NetworkingModule(reactContext),
|
new NetworkingModule(reactContext),
|
||||||
|
new WebSocketModule(reactContext),
|
||||||
new ToastModule(reactContext));
|
new ToastModule(reactContext));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user