140 lines
4.7 KiB
Java
140 lines
4.7 KiB
Java
package cy.agorise.graphenej.network;
|
|
|
|
import android.os.Handler;
|
|
import android.os.Looper;
|
|
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
|
|
import cy.agorise.graphenej.api.android.NetworkService;
|
|
import io.reactivex.subjects.PublishSubject;
|
|
import okhttp3.OkHttpClient;
|
|
import okhttp3.Request;
|
|
import okhttp3.Response;
|
|
import okhttp3.WebSocket;
|
|
import okhttp3.WebSocketListener;
|
|
|
|
/**
|
|
* Class that encapsulates the node latency verification task
|
|
*/
|
|
public class NodeLatencyVerifier {
|
|
|
|
public static final int DEFAULT_LATENCY_VERIFICATION_PERIOD = 5 * 1000;
|
|
|
|
// Variable used to store the list of nodes that should be verified
|
|
private List<FullNode> mNodeList;
|
|
|
|
// Variable used to store the desired verification period
|
|
private long verificationPeriod;
|
|
|
|
// Subject used to publish the result to interested parties
|
|
private PublishSubject<FullNode> subject = PublishSubject.create();
|
|
|
|
private HashMap<String, FullNode> nodeURLMap = new HashMap<>();
|
|
|
|
// private WebSocket webSocket;
|
|
|
|
// Map used to store the first timestamp required for a RTT (Round Trip Time) measurement.
|
|
// If:
|
|
// RTT = t2 - t1
|
|
// This map will hold the value of t1 for each one of the nodes to be measured.
|
|
private HashMap<FullNode, Long> timestamps = new HashMap<>();
|
|
|
|
private HashMap<String, Request> requestMap = new HashMap<>();
|
|
|
|
private Handler mHandler = new Handler(Looper.getMainLooper());
|
|
|
|
private OkHttpClient client;
|
|
|
|
public NodeLatencyVerifier(List<FullNode> nodes){
|
|
this(nodes, DEFAULT_LATENCY_VERIFICATION_PERIOD);
|
|
}
|
|
|
|
public NodeLatencyVerifier(List<FullNode> nodes, long period){
|
|
mNodeList = nodes;
|
|
verificationPeriod = period;
|
|
}
|
|
|
|
/**
|
|
* Method used to start the latency verification task.
|
|
* <p>
|
|
* The returning object can be used for interested parties to receive constant updates
|
|
* regarding new latency measurements for every full node.
|
|
* </p>
|
|
* @return A {@link PublishSubject} class instance.
|
|
*/
|
|
public PublishSubject start(){
|
|
mHandler.post(mVerificationTask);
|
|
return subject;
|
|
}
|
|
|
|
/**
|
|
* Method used to cancel the verification task.
|
|
*/
|
|
public void stop(){
|
|
mHandler.removeCallbacks(mVerificationTask);
|
|
}
|
|
|
|
/**
|
|
* Node latency verification task.
|
|
*/
|
|
private final Runnable mVerificationTask = new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
for(FullNode fullNode : mNodeList){
|
|
long before = System.currentTimeMillis();
|
|
timestamps.put(fullNode, before);
|
|
|
|
// We want to reuse the same OkHttoClient instance if possible
|
|
if(client == null) client = new OkHttpClient();
|
|
|
|
// Same thing with the Request instance, we want to reuse them. But since
|
|
// we might have one request per node, we keep them in a map.
|
|
Request request;
|
|
if(requestMap.containsKey(fullNode.getUrl())){
|
|
request = requestMap.get(fullNode.getUrl());
|
|
}else{
|
|
// If the map had no entry for the request we want, we create one
|
|
// and add it to the map.
|
|
request = new Request.Builder().url(fullNode.getUrl()).build();
|
|
requestMap.put(fullNode.getUrl(), request);
|
|
}
|
|
|
|
client.newWebSocket(request, mWebSocketListener);
|
|
if(!nodeURLMap.containsKey(fullNode.getUrl())){
|
|
nodeURLMap.put(fullNode.getUrl(), fullNode);
|
|
}
|
|
}
|
|
mHandler.postDelayed(this, verificationPeriod);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Listener that will be called upon a server response.
|
|
*/
|
|
private WebSocketListener mWebSocketListener = new WebSocketListener() {
|
|
@Override
|
|
public void onOpen(WebSocket webSocket, Response response) {
|
|
super.onOpen(webSocket, response);
|
|
handleResponse(webSocket, response);
|
|
}
|
|
|
|
@Override
|
|
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
|
|
super.onFailure(webSocket, t, response);
|
|
handleResponse(webSocket, response);
|
|
}
|
|
|
|
private void handleResponse(WebSocket webSocket, Response response){
|
|
String url = "wss://" + webSocket.request().url().host() + webSocket.request().url().encodedPath();
|
|
FullNode fullNode = nodeURLMap.get(url);
|
|
long after = System.currentTimeMillis();
|
|
long before = timestamps.get(fullNode);
|
|
long delay = after - before;
|
|
fullNode.addLatencyValue(delay);
|
|
subject.onNext(fullNode);
|
|
webSocket.close(NetworkService.NORMAL_CLOSURE_STATUS, null);
|
|
}
|
|
};
|
|
}
|