Fix WebSocket events being silently dropped on buffer overflow (#313)

* Fix WebSocket events being silently dropped on buffer overflow

Increases the MutableSharedFlow `extraBufferCapacity` from 64 to 256
and changes the `onBufferOverflow` policy to `BufferOverflow.DROP_OLDEST`
in `HermesWsClient.kt`. This fixes an issue where the slow collection
(e.g., due to DB writes) caused the buffer to fill up and silently drop
incoming real-time WebSocket events.

By setting `DROP_OLDEST`, `tryEmit()` remains functional without
suspending, allowing real-time events to be retained and prioritized.

Fixes #275

* Fix ktlint formatting issues in HermesWsClient.kt

The formatting changes adhere to ktlint rules required by the CI pipeline:
- Removed a trailing comma in MutableSharedFlow initialization to avoid multiline expression wrapping errors.
- Sorted imports to be in lexicographical order.

These formatting changes fix the failing CI check.

Fixes #275

* Fix WebSocket events being silently dropped on buffer overflow

Increases the MutableSharedFlow `extraBufferCapacity` from 64 to 256
and changes the `onBufferOverflow` policy to `BufferOverflow.DROP_OLDEST`
in `HermesWsClient.kt`. This fixes an issue where the slow collection
(e.g., due to DB writes) caused the buffer to fill up and silently drop
incoming real-time WebSocket events.

By setting `DROP_OLDEST`, `tryEmit()` remains functional without
suspending, allowing real-time events to be retained and prioritized.

Fixes #275

* Fix WebSocket events being silently dropped on buffer overflow

Increases the MutableSharedFlow extraBufferCapacity from 64 to 256
and changes the onBufferOverflow policy to BufferOverflow.DROP_OLDEST
in HermesWsClient.kt. This fixes an issue where the slow collection
(e.g., due to DB writes) caused the buffer to fill up and silently drop
incoming real-time WebSocket events.

By setting DROP_OLDEST, tryEmit() remains functional without
suspending, allowing real-time events to be retained and prioritized.

Fixes #275

* Fix WebSocket events being silently dropped on buffer overflow

Increases the MutableSharedFlow extraBufferCapacity from 64 to 256
and changes the onBufferOverflow policy to BufferOverflow.DROP_OLDEST
in HermesWsClient.kt. This fixes an issue where the slow collection
(e.g., due to DB writes) caused the buffer to fill up and silently drop
incoming real-time WebSocket events.

By setting DROP_OLDEST, tryEmit() remains functional without
suspending, allowing real-time events to be retained and prioritized.

Fixes #275

* Fix WebSocket events being silently dropped on buffer overflow

Increases the MutableSharedFlow extraBufferCapacity from 64 to 256
and changes the onBufferOverflow policy to BufferOverflow.DROP_OLDEST
in HermesWsClient.kt. This fixes an issue where the slow collection
(e.g., due to DB writes) caused the buffer to fill up and silently drop
incoming real-time WebSocket events.

By setting DROP_OLDEST, tryEmit() remains functional without
suspending, allowing real-time events to be retained and prioritized.

Fixes #275

---------

Co-authored-by: M57 <hy4ri@users.noreply.github.com>
This commit is contained in:
M57andM57 authored and GitHub committed 2026-06-23 22:13:55 +03:00
1 parent 2c6e437a5b
commit eb71ae3a46
41 files changed
+666 -246

No files matched your search

@@ -39,6 +39,22 @@ class ConnectViewModel(private val app: Application) : ViewModel() {
loadSavedValues()
}
private suspend fun isHostAllowed(host: String): Boolean =
withContext(Dispatchers.IO) {
if (host == "localhost" || host == "127.0.0.1" || host == "0.0.0.0") return@withContext false
if (host.startsWith("169.254.")) return@withContext false
if (host == "metadata.google.internal") return@withContext false
try {
val address = java.net.InetAddress.getByName(host)
if (address.isLoopbackAddress || address.isAnyLocalAddress || address.isLinkLocalAddress) {
return@withContext false
}
true
} catch (e: Exception) {
false
}
}
fun loadSavedValues() {
val savedToken = AuthManager.getToken() ?: ""
val savedHost = AuthManager.getHost()
@@ -189,6 +205,10 @@ class ConnectViewModel(private val app: Application) : ViewModel() {
is NetworkError.Http -> {
when (err.code) {
401 -> {
AuthManager.setToken(null)
if (AuthManager.getSelectedProfileId() != null) {
AuthManager.setProfileToken(AuthManager.getSelectedProfileId()!!, null)
}
app.getString(R.string.connect_error_401)
}
@@ -265,15 +285,25 @@ class ConnectViewModel(private val app: Application) : ViewModel() {
val port = uri.getQueryParameter("port")
val token = uri.getQueryParameter("token")
if (host != null && port != null && token != null) {
_uiState.update {
it.copy(
host = host,
port = port,
token = token,
errorMessage = null,
)
viewModelScope.launch {
if (!isHostAllowed(host)) {
_uiState.update {
it.copy(
errorMessage = app.getString(R.string.connect_error_invalid_host),
)
}
return@launch
}
_uiState.update {
it.copy(
host = host,
port = port,
token = token,
errorMessage = null,
)
}
connect()
}
connect()
return
}
}
@@ -291,15 +321,25 @@ class ConnectViewModel(private val app: Application) : ViewModel() {
val port = json.get("port")?.asInt?.toString() ?: json.get("port")?.asString
val token = json.get("token")?.asString
if (host != null && port != null && token != null) {
_uiState.update {
it.copy(
host = host,
port = port,
token = token,
errorMessage = null,
)
viewModelScope.launch {
if (!isHostAllowed(host)) {
_uiState.update {
it.copy(
errorMessage = app.getString(R.string.connect_error_invalid_host),
)
}
return@launch
}
_uiState.update {
it.copy(
host = host,
port = port,
token = token,
errorMessage = null,
)
}
connect()
}
connect()
return
}
// Decoded valid JSON but missing required fields