Files
react-native/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/ReconnectingWebSocket.java
T
Héctor Ramos 1151c096da Update copyright headers to yearless format
Summary: This change drops the year from the copyright headers and the LICENSE file.

Reviewed By: yungsters

Differential Revision: D9727774

fbshipit-source-id: df4fc1e4390733fe774b1a160dd41b4a3d83302a
2018-09-11 15:33:07 -07:00

197 lines
4.9 KiB
Java

/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.packagerconnection;
import javax.annotation.Nullable;
import java.io.IOException;
import java.nio.channels.ClosedChannelException;
import java.util.concurrent.TimeUnit;
import android.os.Handler;
import android.os.Looper;
import com.facebook.common.logging.FLog;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
import okio.ByteString;
/**
* A wrapper around WebSocketClient that reconnects automatically
*/
final public class ReconnectingWebSocket extends WebSocketListener {
private static final String TAG = ReconnectingWebSocket.class.getSimpleName();
private static final int RECONNECT_DELAY_MS = 2000;
public interface MessageCallback {
void onMessage(String text);
void onMessage(ByteString bytes);
}
public interface ConnectionCallback {
void onConnected();
void onDisconnected();
}
private final String mUrl;
private final Handler mHandler;
private boolean mClosed = false;
private boolean mSuppressConnectionErrors;
private @Nullable WebSocket mWebSocket;
private @Nullable MessageCallback mMessageCallback;
private @Nullable ConnectionCallback mConnectionCallback;
public ReconnectingWebSocket(
String url,
MessageCallback messageCallback,
ConnectionCallback connectionCallback) {
super();
mUrl = url;
mMessageCallback = messageCallback;
mConnectionCallback = connectionCallback;
mHandler = new Handler(Looper.getMainLooper());
}
public void connect() {
if (mClosed) {
throw new IllegalStateException("Can't connect closed client");
}
OkHttpClient httpClient = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.readTimeout(0, TimeUnit.MINUTES) // Disable timeouts for read
.build();
Request request = new Request.Builder().url(mUrl).build();
httpClient.newWebSocket(request, this);
}
private synchronized void delayedReconnect() {
// check that we haven't been closed in the meantime
if (!mClosed) {
connect();
}
}
private void reconnect() {
if (mClosed) {
throw new IllegalStateException("Can't reconnect closed client");
}
if (!mSuppressConnectionErrors) {
FLog.w(TAG, "Couldn't connect to \"" + mUrl + "\", will silently retry");
mSuppressConnectionErrors = true;
}
mHandler.postDelayed(
new Runnable() {
@Override
public void run() {
delayedReconnect();
}
},
RECONNECT_DELAY_MS);
}
public void closeQuietly() {
mClosed = true;
closeWebSocketQuietly();
mMessageCallback = null;
if (mConnectionCallback != null) {
mConnectionCallback.onDisconnected();
}
}
private void closeWebSocketQuietly() {
if (mWebSocket != null) {
try {
mWebSocket.close(1000, "End of session");
} catch (Exception e) {
// swallow, no need to handle it here
}
mWebSocket = null;
}
}
private void abort(String message, Throwable cause) {
FLog.e(TAG, "Error occurred, shutting down websocket connection: " + message, cause);
closeWebSocketQuietly();
}
@Override
public synchronized void onOpen(WebSocket webSocket, Response response) {
mWebSocket = webSocket;
mSuppressConnectionErrors = false;
if (mConnectionCallback != null) {
mConnectionCallback.onConnected();
}
}
@Override
public synchronized void onFailure(WebSocket webSocket, Throwable t, Response response) {
if (mWebSocket != null) {
abort("Websocket exception", t);
}
if (!mClosed) {
if (mConnectionCallback != null) {
mConnectionCallback.onDisconnected();
}
reconnect();
}
}
@Override
public synchronized void onMessage(WebSocket webSocket, String text) {
if (mMessageCallback != null) {
mMessageCallback.onMessage(text);
}
}
@Override
public synchronized void onMessage(WebSocket webSocket, ByteString bytes) {
if (mMessageCallback != null) {
mMessageCallback.onMessage(bytes);
}
}
@Override
public synchronized void onClosed(WebSocket webSocket, int code, String reason) {
mWebSocket = null;
if (!mClosed) {
if (mConnectionCallback != null) {
mConnectionCallback.onDisconnected();
}
reconnect();
}
}
public synchronized void sendMessage(String message) throws IOException {
if (mWebSocket != null) {
mWebSocket.send(message);
} else {
throw new ClosedChannelException();
}
}
public synchronized void sendMessage(ByteString message) throws IOException {
if (mWebSocket != null) {
mWebSocket.send(message);
} else {
throw new ClosedChannelException();
}
}
}