gotify / android

An app for creating push notifications for new messages posted to gotify/server.
MIT License
934 stars 156 forks source link

Android app doesn't retry connecting to server upon 404 error #364

Closed SimoPk closed 2 months ago

SimoPk commented 2 months ago

I have a self hosted gotify instance running in a docker compose, and every night the container is stopped, the data folder is backed up, and then restarted.

During the brief time the server is down (approx 5 mins, because the backup procedure involves starting/stopping other services too) the android app loses the connection because it tries to connect but receives a 404 error, and it doesn't try anymore to connect to the server up until, for example, I cycle the connection method on the phone, like switching from wifi to cellular network.

Imho the app should retry connecting to the server periodically, and not just on some external event like network reload.

That's the relevant part of the log. The planned downtime is scheduled for 4am, and abt 1 min later the app tries to connect and receives a 404, and doesn't try anymore up until in the morning I switch from wifi to cellular.

2024-07-26 09:12:42.992 INFO: Entering LogsActivity 2024-07-26 09:12:39.271 INFO: Removing message with id 211 2024-07-26 09:12:35.378 INFO: Removing message with id 210 2024-07-26 09:12:12.940 INFO: Loading more messages for -1 2024-07-26 09:12:12.916 INFO: Entering MessagesActivity 2024-07-26 09:12:12.798 INFO: WebSocket(12): opened 2024-07-26 09:12:12.734 INFO: Server version: 2.4.0@2023-09-17-08:24:00 2024-07-26 09:12:12.461 INFO: WebSocket: Network available, reconnect if needed. 2024-07-26 09:12:12.455 INFO: WebSocket(12): starting... 2024-07-26 09:12:12.452 INFO: Starting WebSocketService 2024-07-26 09:12:12.435 INFO: Authenticated as xxxxxxxx 2024-07-26 09:12:11.943 INFO: Entering InitializationActivity 2024-07-26 04:01:14.069 ERROR: WebSocket(11): failure StatusCode: 404 Message: Not Found: java.net.ProtocolException: Expected HTTP 101 response but was '404 Not Found' at okhttp3.internal.ws.RealWebSocket.checkUpgradeSuccess$okhttp(RealWebSocket.kt:224) at okhttp3.internal.ws.RealWebSocket$connect$1.onResponse(RealWebSocket.kt:170) at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:519) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644) at java.lang.Thread.run(Thread.java:1012) 2024-07-26 04:01:13.535 INFO: WebSocket(11): starting... 2024-07-26 04:00:13.530 INFO: WebSocket: scheduling a restart in 60 second(s) (via alarm manager) 2024-07-26 04:00:13.495 ERROR: WebSocket(10): failure  Message: : java.io.EOFException at okio.RealBufferedSource.require(RealBufferedSource.kt:203) at okio.RealBufferedSource.readByte(RealBufferedSource.kt:213) at okhttp3.internal.ws.WebSocketReader.readHeader(WebSocketReader.kt:119) at okhttp3.internal.ws.WebSocketReader.processNextFrame(WebSocketReader.kt:102) at okhttp3.internal.ws.RealWebSocket.loopReader(RealWebSocket.kt:293) at okhttp3.internal.ws.RealWebSocket$connect$1.onResponse(RealWebSocket.kt:195) at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:519) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644) at java.lang.Thread.run(Thread.java:1012) 2024-07-25 23:37:34.593 INFO: WebSocket(10): opened

jmattheis commented 2 months ago

Yeah, currently the app doesn't reconnect for 4xx errors because they are client errors. The app should always reconnect.

SimoPk commented 2 months ago

Unfortunately I don't have any experience with Android app development and/or Kotlin, but just following my curiosity I tracked down what I think is the issue, and I'd like to propose a possible patch for this...

I'd change the onFailure method of WebSocketConnection to this (android/app/src/main/kotlin/com/github/gotify/service/WebSocketConnection.kt, from line 189):

        override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
            val code = if (response != null) "StatusCode: ${response.code}" else ""
            val message = response?.message ?: ""
            Logger.error(t) { "WebSocket($id): failure $code Message: $message" }
            syncExec(id) {
                closed()
                errorCount++
                val minutes = (errorCount * 2 - 1).coerceAtMost(20)

                if (response != null && response.code >= 400 && response.code <= 499) {
                    onBadRequest.execute(message)
                } else {
                    onNetworkFailure.execute(minutes)
                }

                scheduleReconnect(TimeUnit.MINUTES.toSeconds(minutes.toLong()))
            }

Hope this is of some help to you. Thanks for your work anyway!!