From 80fc81de63a5e25f8ded81f6d9414813ca76b756 Mon Sep 17 00:00:00 2001
From: Janne Koschinski <janne@kuschku.de>
Date: Wed, 17 Apr 2019 20:11:06 +0200
Subject: [PATCH] Connection handling improvements

---
 .../libquassel/connection/CoreConnection.kt   |  7 ++-
 .../util/compatibility/CompatibilityUtils.kt  | 24 ++-------
 ...BackportedSyncFlushDeflaterOutputStream.kt | 54 +++++++++++++++++++
 ...rlyClosingSyncFlushDeflaterOutputStream.kt | 35 ++++++++++++
 .../libquassel/util/nio/WrappedChannel.kt     | 37 +++++++++----
 5 files changed, 122 insertions(+), 35 deletions(-)
 create mode 100644 lib/src/main/java/de/kuschku/libquassel/util/compatibility/backport/BackportedSyncFlushDeflaterOutputStream.kt
 create mode 100644 lib/src/main/java/de/kuschku/libquassel/util/compatibility/backport/ProperlyClosingSyncFlushDeflaterOutputStream.kt

diff --git a/lib/src/main/java/de/kuschku/libquassel/connection/CoreConnection.kt b/lib/src/main/java/de/kuschku/libquassel/connection/CoreConnection.kt
index 668c0546f..4f1ffca97 100644
--- a/lib/src/main/java/de/kuschku/libquassel/connection/CoreConnection.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/connection/CoreConnection.kt
@@ -172,13 +172,12 @@ class CoreConnection(
   override fun close() {
     try {
       setState(ConnectionState.CLOSED)
-      channel?.flush()
       channel?.close()
-      setHandlers(null, null, null)
-      interrupt()
     } catch (e: Throwable) {
       log(WARN, TAG, "Error encountered while closing connection: $e")
     }
+    setHandlers(null, null, null)
+    interrupt()
   }
 
   fun dispatch(message: HandshakeMessage) {
@@ -218,7 +217,7 @@ class CoreConnection(
       connect()
       sendHandshake()
       readHandshake()
-      while (!isInterrupted && state != ConnectionState.CLOSED) {
+      while (!isInterrupted && state.value != ConnectionState.CLOSED) {
         sizeBuffer.clear()
         if (channel?.read(sizeBuffer) == -1)
           break
diff --git a/lib/src/main/java/de/kuschku/libquassel/util/compatibility/CompatibilityUtils.kt b/lib/src/main/java/de/kuschku/libquassel/util/compatibility/CompatibilityUtils.kt
index 663c9ab7b..9c531d84f 100644
--- a/lib/src/main/java/de/kuschku/libquassel/util/compatibility/CompatibilityUtils.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/util/compatibility/CompatibilityUtils.kt
@@ -19,6 +19,8 @@
 
 package de.kuschku.libquassel.util.compatibility
 
+import de.kuschku.libquassel.util.compatibility.backport.BackportedSyncFlushDeflaterOutputStream
+import de.kuschku.libquassel.util.compatibility.backport.ProperlyClosingSyncFlushDeflaterOutputStream
 import java.io.OutputStream
 import java.util.zip.Deflater
 import java.util.zip.DeflaterOutputStream
@@ -36,27 +38,9 @@ object CompatibilityUtils {
    */
   fun createDeflaterOutputStream(rawOut: OutputStream?): DeflaterOutputStream {
     return if (supportsCompression) {
-      DeflaterOutputStream(rawOut, true)
+      ProperlyClosingSyncFlushDeflaterOutputStream(rawOut)
     } else {
-      DeflaterOutputStream(rawOut, createSyncFlushDeflater())
+      BackportedSyncFlushDeflaterOutputStream(rawOut)
     }
   }
-
-  /**
-   * Creates a SyncFlush Deflater for use on pre-KitKat Android
-   *
-   * @return The modified Deflater, or null if the creation failed
-   */
-  private fun createSyncFlushDeflater(): Deflater? {
-    val def = Deflater()
-    try {
-      val f = def.javaClass.getDeclaredField("flushParm")
-      f.isAccessible = true
-      f.setInt(def, 2) // Z_SYNC_FLUSH
-    } catch (e: Exception) {
-      return null
-    }
-
-    return def
-  }
 }
diff --git a/lib/src/main/java/de/kuschku/libquassel/util/compatibility/backport/BackportedSyncFlushDeflaterOutputStream.kt b/lib/src/main/java/de/kuschku/libquassel/util/compatibility/backport/BackportedSyncFlushDeflaterOutputStream.kt
new file mode 100644
index 000000000..d222c3418
--- /dev/null
+++ b/lib/src/main/java/de/kuschku/libquassel/util/compatibility/backport/BackportedSyncFlushDeflaterOutputStream.kt
@@ -0,0 +1,54 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2019 Janne Koschinski
+ * Copyright (c) 2019 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.libquassel.util.compatibility.backport
+
+import java.io.OutputStream
+import java.util.zip.Deflater
+import java.util.zip.DeflaterOutputStream
+
+class BackportedSyncFlushDeflaterOutputStream(
+  rawOut: OutputStream?,
+  private val deflater: Deflater? = createSyncFlushDeflater()
+) : DeflaterOutputStream(rawOut, deflater) {
+  override fun close() {
+    deflater?.end()
+    super.close()
+  }
+
+  companion object {
+    /**
+     * Creates a SyncFlush Deflater for use on pre-KitKat Android
+     *
+     * @return The modified Deflater, or null if the creation failed
+     */
+    private fun createSyncFlushDeflater(): Deflater? {
+      val def = Deflater()
+      try {
+        val f = def.javaClass.getDeclaredField("flushParm")
+        f.isAccessible = true
+        f.setInt(def, 2) // Z_SYNC_FLUSH
+      } catch (e: Exception) {
+        return null
+      }
+
+      return def
+    }
+  }
+}
diff --git a/lib/src/main/java/de/kuschku/libquassel/util/compatibility/backport/ProperlyClosingSyncFlushDeflaterOutputStream.kt b/lib/src/main/java/de/kuschku/libquassel/util/compatibility/backport/ProperlyClosingSyncFlushDeflaterOutputStream.kt
new file mode 100644
index 000000000..a5630d8ed
--- /dev/null
+++ b/lib/src/main/java/de/kuschku/libquassel/util/compatibility/backport/ProperlyClosingSyncFlushDeflaterOutputStream.kt
@@ -0,0 +1,35 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2019 Janne Koschinski
+ * Copyright (c) 2019 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.libquassel.util.compatibility.backport
+
+import java.io.OutputStream
+import java.util.zip.DeflaterOutputStream
+
+class ProperlyClosingSyncFlushDeflaterOutputStream(
+  stream: OutputStream?
+) : DeflaterOutputStream(stream, true) {
+  override fun close() {
+    try {
+      super.close()
+    } finally {
+      def.end()
+    }
+  }
+}
diff --git a/lib/src/main/java/de/kuschku/libquassel/util/nio/WrappedChannel.kt b/lib/src/main/java/de/kuschku/libquassel/util/nio/WrappedChannel.kt
index 9c8bbaf72..d47bea712 100644
--- a/lib/src/main/java/de/kuschku/libquassel/util/nio/WrappedChannel.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/util/nio/WrappedChannel.kt
@@ -19,14 +19,15 @@
 
 package de.kuschku.libquassel.util.nio
 
+import de.kuschku.libquassel.connection.CoreConnection
 import de.kuschku.libquassel.connection.HostnameVerifier
 import de.kuschku.libquassel.connection.SocketAddress
 import de.kuschku.libquassel.util.compatibility.CompatibilityUtils
+import de.kuschku.libquassel.util.compatibility.LoggingHandler
+import de.kuschku.libquassel.util.compatibility.LoggingHandler.Companion.log
+import de.kuschku.libquassel.util.compatibility.LoggingHandler.LogLevel.*
 import de.kuschku.libquassel.util.compatibility.StreamChannelFactory
-import java.io.Flushable
-import java.io.IOException
-import java.io.InputStream
-import java.io.OutputStream
+import java.io.*
 import java.net.Socket
 import java.net.SocketException
 import java.nio.ByteBuffer
@@ -42,11 +43,12 @@ import javax.net.ssl.SSLSocket
 import javax.net.ssl.SSLSocketFactory
 import javax.net.ssl.X509TrustManager
 
-class WrappedChannel(
+class WrappedChannel private constructor(
   private val socket: Socket,
   private var rawInStream: InputStream? = null,
   private var rawOutStream: OutputStream? = null,
-  private var flusher: (() -> Unit)? = null
+  private var flusher: (() -> Unit)? = null,
+  private val closeListeners: List<Closeable> = emptyList()
 ) : Flushable, ByteChannel, InterruptibleChannel {
   private var rawIn: ReadableByteChannel? = null
   private var rawOut: WritableByteChannel? = null
@@ -62,10 +64,12 @@ class WrappedChannel(
   }
 
   companion object {
-    fun ofSocket(s: Socket): WrappedChannel {
+    fun ofSocket(s: Socket, closeListeners: List<Closeable> = emptyList()): WrappedChannel {
       return WrappedChannel(
-        s, s.getInputStream(),
-        s.getOutputStream()
+        s,
+        s.getInputStream(),
+        s.getOutputStream(),
+        closeListeners = closeListeners + s.getInputStream() + s.getOutputStream()
       )
     }
   }
@@ -74,7 +78,8 @@ class WrappedChannel(
     val deflaterOutputStream = CompatibilityUtils.createDeflaterOutputStream(rawOutStream)
     return WrappedChannel(
       socket, InflaterInputStream(rawInStream), deflaterOutputStream,
-      deflaterOutputStream::flush
+      deflaterOutputStream::flush,
+      closeListeners = closeListeners + deflaterOutputStream
     )
   }
 
@@ -96,7 +101,7 @@ class WrappedChannel(
       )
     }
     socket.startHandshake()
-    return WrappedChannel.ofSocket(socket)
+    return ofSocket(socket)
   }
 
   /**
@@ -206,6 +211,16 @@ class WrappedChannel(
     rawOut?.close()
     rawOut = null
     socket.close()
+    /*
+    for (listener in closeListeners + socket) {
+      try {
+        log(INFO, "WrappedChannel", "Closing: ${listener::class.java}")
+        listener.close()
+      } catch (e: Throwable) {
+        log(WARN, "WrappedChannel", "Error encountered while closing connection: $e")
+      }
+    }
+    */
   }
 
   /**
-- 
GitLab