Stabilize VPN farm startup path

This commit is contained in:
2026-05-15 10:31:29 +03:00
parent 96566cbe55
commit e3f21d591f
6 changed files with 113 additions and 16 deletions
+2 -2
View File
@@ -30,8 +30,8 @@ android {
applicationId "su.cin.rapvpn"
minSdk 26
targetSdk 35
versionCode 190
versionName "0.2.190"
versionCode 191
versionName "0.2.191"
buildConfigField "String", "DEFAULT_BACKEND_URL", "\"${normalizeGradleString(defaultBackendUrl)}\""
buildConfigField "String", "DEFAULT_CLUSTER_ID", "\"${normalizeGradleString(defaultClusterId)}\""
buildConfigField "String", "DEFAULT_ORGANIZATION_ID", "\"${normalizeGradleString(defaultOrganizationId)}\""
@@ -25,6 +25,7 @@ final class VpnPacketWebSocketRelay {
private static final int MAX_PACKET_BATCH_BYTES = 1024 * 1024;
private static final int MAX_SINGLE_PACKET_BYTES = 65535;
private static final long CONNECTING_STALE_MS = 8000;
private static final long OPEN_WAIT_MS = 3500;
private final String baseUrl;
private final VpnService vpnService;
@@ -123,8 +124,12 @@ final class VpnPacketWebSocketRelay {
return true;
}
connect(clusterId, vpnConnectionId);
if (!awaitOpen(OPEN_WAIT_MS)) {
return false;
}
WebSocket socket = webSocket;
if (socket == null || !open) {
if (socket == null) {
lastError = "websocket missing after open";
return false;
}
byte[] payload = encodePacketBatch(packets);
@@ -140,6 +145,7 @@ final class VpnPacketWebSocketRelay {
List<byte[]> receiveClientPacketBatch(String clusterId, String vpnConnectionId, int timeoutMs) throws InterruptedException {
connect(clusterId, vpnConnectionId);
awaitOpen(Math.min(OPEN_WAIT_MS, Math.max(1, timeoutMs)));
int waitMs = Math.max(1, timeoutMs);
List<byte[]> packets = incoming.poll(waitMs, TimeUnit.MILLISECONDS);
return packets == null ? new ArrayList<>() : packets;
@@ -165,6 +171,29 @@ final class VpnPacketWebSocketRelay {
webSocket = null;
}
private boolean awaitOpen(long timeoutMs) {
long deadline = System.currentTimeMillis() + Math.max(1, timeoutMs);
synchronized (lock) {
while (!open && connecting) {
long waitMs = deadline - System.currentTimeMillis();
if (waitMs <= 0) {
break;
}
try {
lock.wait(waitMs);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
lastError = "interrupted waiting for websocket open";
return false;
}
}
if (!open && "connecting".equals(lastError)) {
lastError = "connecting_timeout";
}
return open;
}
}
private String webSocketUrl(String clusterId, String vpnConnectionId) {
try {
URI uri = URI.create(baseUrl);
@@ -187,10 +216,13 @@ final class VpnPacketWebSocketRelay {
private final class Listener extends WebSocketListener {
@Override
public void onOpen(WebSocket webSocket, Response response) {
open = true;
connecting = false;
reconnectAfterMs = 0;
lastError = "";
synchronized (lock) {
open = true;
connecting = false;
reconnectAfterMs = 0;
lastError = "";
lock.notifyAll();
}
Log.i(TAG, "vpn packet websocket opened " + baseUrl);
}
@@ -208,22 +240,28 @@ final class VpnPacketWebSocketRelay {
@Override
public void onClosed(WebSocket webSocket, int code, String reason) {
open = false;
connecting = false;
reconnectAfterMs = System.currentTimeMillis() + 1000;
lastError = "closed " + code + " " + reason;
synchronized (lock) {
open = false;
connecting = false;
reconnectAfterMs = System.currentTimeMillis() + 1000;
lastError = "closed " + code + " " + reason;
lock.notifyAll();
}
}
@Override
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
open = false;
connecting = false;
reconnectAfterMs = System.currentTimeMillis() + 3000;
String responseStatus = "";
if (response != null) {
responseStatus = " status=" + response.code();
}
lastError = (t == null ? "websocket failure" : t.getClass().getSimpleName() + ": " + t.getMessage()) + responseStatus;
synchronized (lock) {
open = false;
connecting = false;
reconnectAfterMs = System.currentTimeMillis() + 3000;
lastError = (t == null ? "websocket failure" : t.getClass().getSimpleName() + ": " + t.getMessage()) + responseStatus;
lock.notifyAll();
}
Log.w(TAG, "vpn packet websocket failed " + baseUrl + ": " + lastError);
}
}