Fix for race conditioned caused by OkHttpClient NPE.
We catch OkHttpClient exceptions to deal with bugs in their code, but in some cases that was leaving our state information in a bad situation. // FREEBIEpull/1/head
parent
8a2caeef3d
commit
83d65228e9
@ -0,0 +1,142 @@
|
||||
package org.whispersystems.textsecure.internal.websocket;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
import com.squareup.okhttp.Request;
|
||||
import com.squareup.okhttp.Response;
|
||||
import com.squareup.okhttp.internal.ws.WebSocket;
|
||||
import com.squareup.okhttp.internal.ws.WebSocketListener;
|
||||
|
||||
import org.whispersystems.textsecure.api.push.TrustStore;
|
||||
import org.whispersystems.textsecure.api.util.CredentialsProvider;
|
||||
import org.whispersystems.textsecure.internal.util.BlacklistingTrustManager;
|
||||
import org.whispersystems.textsecure.internal.util.Util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
|
||||
import okio.Buffer;
|
||||
import okio.BufferedSource;
|
||||
|
||||
public class OkHttpClientWrapper implements WebSocketListener {
|
||||
|
||||
private static final String TAG = OkHttpClientWrapper.class.getSimpleName();
|
||||
|
||||
private final String uri;
|
||||
private final TrustStore trustStore;
|
||||
private final CredentialsProvider credentialsProvider;
|
||||
private final WebSocketEventListener listener;
|
||||
|
||||
private WebSocket webSocket;
|
||||
private boolean closed;
|
||||
private boolean connected;
|
||||
|
||||
public OkHttpClientWrapper(String uri, TrustStore trustStore,
|
||||
CredentialsProvider credentialsProvider,
|
||||
WebSocketEventListener listener)
|
||||
{
|
||||
Log.w(TAG, "Connecting to: " + uri);
|
||||
|
||||
this.uri = uri;
|
||||
this.trustStore = trustStore;
|
||||
this.credentialsProvider = credentialsProvider;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void connect() {
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
int attempt = 0;
|
||||
|
||||
while ((webSocket = newSocket()) != null) {
|
||||
try {
|
||||
Response response = webSocket.connect(OkHttpClientWrapper.this);
|
||||
|
||||
if (response.code() == 101) {
|
||||
synchronized (OkHttpClientWrapper.this) {
|
||||
if (closed) webSocket.close(1000, "OK");
|
||||
else connected = true;
|
||||
}
|
||||
|
||||
listener.onConnected();
|
||||
return;
|
||||
}
|
||||
|
||||
Log.w(TAG, "WebSocket Response: " + response.code());
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
|
||||
Util.sleep(Math.min(++attempt * 200, TimeUnit.SECONDS.toMillis(15)));
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
public synchronized void disconnect() {
|
||||
Log.w(TAG, "Calling disconnect()...");
|
||||
try {
|
||||
closed = true;
|
||||
if (webSocket != null && connected) {
|
||||
webSocket.close(1000, "OK");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendMessage(byte[] message) throws IOException {
|
||||
webSocket.sendMessage(WebSocket.PayloadType.BINARY, new Buffer().write(message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(BufferedSource payload, WebSocket.PayloadType type) throws IOException {
|
||||
Log.w(TAG, "onMessage: " + type);
|
||||
if (type.equals(WebSocket.PayloadType.BINARY)) {
|
||||
listener.onMessage(payload.readByteArray());
|
||||
}
|
||||
|
||||
payload.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(int code, String reason) {
|
||||
Log.w(TAG, String.format("onClose(%d, %s)", code, reason));
|
||||
listener.onClose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(IOException e) {
|
||||
Log.w(TAG, e);
|
||||
listener.onClose();
|
||||
}
|
||||
|
||||
private synchronized WebSocket newSocket() {
|
||||
if (closed) return null;
|
||||
|
||||
String filledUri = String.format(uri, credentialsProvider.getUser(), credentialsProvider.getPassword());
|
||||
SSLSocketFactory socketFactory = createTlsSocketFactory(trustStore);
|
||||
|
||||
return WebSocket.newWebSocket(new OkHttpClient().setSslSocketFactory(socketFactory),
|
||||
new Request.Builder().url(filledUri).build());
|
||||
}
|
||||
|
||||
private SSLSocketFactory createTlsSocketFactory(TrustStore trustStore) {
|
||||
try {
|
||||
SSLContext context = SSLContext.getInstance("TLS");
|
||||
context.init(null, BlacklistingTrustManager.createFor(trustStore), null);
|
||||
|
||||
return context.getSocketFactory();
|
||||
} catch (NoSuchAlgorithmException | KeyManagementException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package org.whispersystems.textsecure.internal.websocket;
|
||||
|
||||
public interface WebSocketEventListener {
|
||||
|
||||
public void onMessage(byte[] payload);
|
||||
public void onClose();
|
||||
public void onConnected();
|
||||
|
||||
}
|
Loading…
Reference in New Issue