/**
* 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);
}
}