From 7fa80ec0486d8f471a7ec44c3efb7b8a31348c2a Mon Sep 17 00:00:00 2001
From: Kelvin <kelvin@futo.org>
Date: Thu, 16 Nov 2023 17:13:23 +0100
Subject: [PATCH] Fix communication issues devportal

---
 .../api/http/server/HttpContext.kt            | 74 +++++++++++++++----
 .../api/http/server/ManagedHttpServer.kt      | 13 ++--
 .../developer/DeveloperEndpoints.kt           |  2 +-
 .../stores/db/ManagedDBIndex.kt               |  5 ++
 .../stores/db/ManagedDBStore.kt               |  5 ++
 5 files changed, 75 insertions(+), 24 deletions(-)
 create mode 100644 app/src/main/java/com/futo/platformplayer/stores/db/ManagedDBIndex.kt
 create mode 100644 app/src/main/java/com/futo/platformplayer/stores/db/ManagedDBStore.kt

diff --git a/app/src/main/java/com/futo/platformplayer/api/http/server/HttpContext.kt b/app/src/main/java/com/futo/platformplayer/api/http/server/HttpContext.kt
index 537ac7a2..5b7740c8 100644
--- a/app/src/main/java/com/futo/platformplayer/api/http/server/HttpContext.kt
+++ b/app/src/main/java/com/futo/platformplayer/api/http/server/HttpContext.kt
@@ -7,13 +7,15 @@ import com.futo.platformplayer.api.media.Serializer
 import kotlinx.serialization.decodeFromString
 import kotlinx.serialization.encodeToString
 import kotlinx.serialization.json.Json
+import java.io.BufferedInputStream
 import java.io.BufferedReader
 import java.io.OutputStream
 import java.io.StringWriter
 import java.net.SocketTimeoutException
+import java.nio.ByteBuffer
 
 class HttpContext : AutoCloseable {
-    private val _stream: BufferedReader;
+    private val _stream: BufferedInputStream;
     private var _responseStream: OutputStream? = null;
     
     var id: String? = null;
@@ -38,14 +40,40 @@ class HttpContext : AutoCloseable {
 
     private val _responseHeaders: HttpHeaders = HttpHeaders();
 
+    private val newLineByte = "\n"[0];
+    private fun readStreamLine(): String {
+        //TODO: This is not ideal..
+        var twoByteArray = ByteBuffer.allocate(2);
+        var lastChar: Char = Char.MIN_VALUE;
+        val builder = StringBuilder();
+        do {
+            val firstByte = _stream.read();
+            if(firstByte == -1)
+                break;
+            if(isCharacter2Bytes(firstByte)) {
+                twoByteArray.put(0, firstByte.toByte());
+                val secondByte = _stream.read();
+                if(secondByte == -1)
+                    break;
+                twoByteArray.put(1, secondByte.toByte());
+            }
+            else
+                lastChar = firstByte.toChar();
+            builder.append(lastChar);
+            if(lastChar == newLineByte)
+                break;
+        }
+        while(lastChar != Char.MIN_VALUE);
+        return builder.toString();
+    }
 
-    constructor(stream: BufferedReader, responseStream: OutputStream? = null, requestId: String? = null, timeout: Int? = null) {
+    constructor(stream: BufferedInputStream, responseStream: OutputStream? = null, requestId: String? = null, timeout: Int? = null) {
         _stream = stream;
         _responseStream = responseStream;
         this.id = requestId;
 
         try {
-            head = stream.readLine() ?: throw EmptyRequestException("No head found");
+            head = readStreamLine() ?: throw EmptyRequestException("No head found");
         }
         catch(ex: SocketTimeoutException) {
             if((timeout ?: 0) > 0)
@@ -78,7 +106,7 @@ class HttpContext : AutoCloseable {
         }
 
         while (true) {
-            val line = stream.readLine();
+            val line = readStreamLine();
             val headerEndIndex = line.indexOf(":");
             if (headerEndIndex == -1)
                 break;
@@ -172,27 +200,37 @@ class HttpContext : AutoCloseable {
         statusCode = status;
     }
 
-    fun readContentBytes(buffer: CharArray, length: Int) : Int {
-        val reading = Math.min(length, (contentLength - _totalRead).toInt());
+    fun readContentBytes(buffer: ByteArray, length: Int) : Int {
+        val reading = if(contentLength - _totalRead < length)
+            (contentLength - _totalRead).toInt();
+        else
+            length;
         val read = _stream.read(buffer, 0, reading);
         _totalRead += read;
-
-        //TODO: Fix this properly
-        if(contentLength - _totalRead < 400 && read < length) {
-            _totalRead = contentLength;
-        }
         return read;
     }
     fun readContentString() : String{
         val writer = StringWriter();
         var read = 0;
-        val buffer = CharArray(4096);
+        val buffer = ByteArray(8192);
+        val twoByteArray = ByteArray(2);
         do {
             read = readContentBytes(buffer, buffer.size);
-            writer.write(buffer, 0, read);
-        } while(read > 0);// && _stream.ready());
-        //if(!_stream.ready())
-        //    _totalRead = contentLength;
+
+            if(read > 0) {
+                if (isCharacter2Bytes(buffer[read - 1].toInt())) {
+                    //Fixes overlapping buffers
+                    writer.write(String(buffer, 0, read - 1));
+                    twoByteArray[0] = buffer[read - 1];
+                    val secondByte = _stream.read();
+                    if (secondByte < 0)
+                        break;
+                    twoByteArray[1] = secondByte.toByte();
+                    writer.write(String(twoByteArray));
+                } else
+                    writer.write(String(buffer, 0, read));
+            }
+        } while(read > 0);
         return writer.toString();
     }
     inline fun <reified T> readContentJson() : T {
@@ -210,6 +248,10 @@ class HttpContext : AutoCloseable {
         }
     }
 
+    private fun isCharacter2Bytes(firstByte: Int): Boolean {
+        return firstByte and 0xE0 == 0xC0
+    }
+
     companion object {
         private val TAG = "HttpRequest";
         private val statusCodeMap = mapOf(
diff --git a/app/src/main/java/com/futo/platformplayer/api/http/server/ManagedHttpServer.kt b/app/src/main/java/com/futo/platformplayer/api/http/server/ManagedHttpServer.kt
index 2579cf60..df695ed1 100644
--- a/app/src/main/java/com/futo/platformplayer/api/http/server/ManagedHttpServer.kt
+++ b/app/src/main/java/com/futo/platformplayer/api/http/server/ManagedHttpServer.kt
@@ -5,8 +5,7 @@ import com.futo.platformplayer.api.http.ManagedHttpClient
 import com.futo.platformplayer.api.http.server.exceptions.EmptyRequestException
 import com.futo.platformplayer.api.http.server.handlers.HttpFuntionHandler
 import com.futo.platformplayer.api.http.server.handlers.HttpHandler
-import java.io.BufferedReader
-import java.io.InputStreamReader
+import java.io.BufferedInputStream
 import java.io.OutputStream
 import java.lang.reflect.Field
 import java.lang.reflect.Method
@@ -76,12 +75,12 @@ class ManagedHttpServer(private val _requestedPort: Int = 0) {
 
     private fun handleClientRequest(socket: Socket) {
         _workerPool?.submit {
-            val requestReader = BufferedReader(InputStreamReader(socket.getInputStream()))
+            val requestStream = BufferedInputStream(socket.getInputStream());
             val responseStream = socket.getOutputStream();
 
             val requestId = UUID.randomUUID().toString().substring(0, 5);
             try {
-                keepAliveLoop(requestReader, responseStream, requestId) { req ->
+                keepAliveLoop(requestStream, responseStream, requestId) { req ->
                     req.use { httpContext ->
                         if(!httpContext.path.startsWith("/plugin/"))
                             Logger.i(TAG, "[${req.id}] ${httpContext.method}: ${httpContext.path}")
@@ -107,7 +106,7 @@ class ManagedHttpServer(private val _requestedPort: Int = 0) {
                 Logger.e(TAG, "Failed to handle client request.", e);
             }
             finally {
-                requestReader.close();
+                requestStream.close();
                 responseStream.close();
             }
         };
@@ -188,7 +187,7 @@ class ManagedHttpServer(private val _requestedPort: Int = 0) {
         }
     }
 
-    private fun keepAliveLoop(requestReader: BufferedReader, responseStream: OutputStream, requestId: String, handler: (HttpContext)->Unit) {
+    private fun keepAliveLoop(requestReader: BufferedInputStream, responseStream: OutputStream, requestId: String, handler: (HttpContext)->Unit) {
         val stopCount = _stopCount;
         var keepAlive = false;
         var requestsMax = 0;
@@ -200,7 +199,7 @@ class ManagedHttpServer(private val _requestedPort: Int = 0) {
             handler(req);
 
             requestsTotal++;
-            if(req.keepAlive){// && requestReader.ready()) {
+            if(req.keepAlive) {
                 keepAlive = true;
                 if(req.keepAliveMax > 0)
                     requestsMax = req.keepAliveMax;
diff --git a/app/src/main/java/com/futo/platformplayer/developer/DeveloperEndpoints.kt b/app/src/main/java/com/futo/platformplayer/developer/DeveloperEndpoints.kt
index 99cd4de7..95502b6d 100644
--- a/app/src/main/java/com/futo/platformplayer/developer/DeveloperEndpoints.kt
+++ b/app/src/main/java/com/futo/platformplayer/developer/DeveloperEndpoints.kt
@@ -301,7 +301,7 @@ class DeveloperEndpoints(private val context: Context) {
             if(method != "isLoggedIn")
                 Logger.i(TAG, "Remote Call [${objId}].${method}(...)");
 
-            val parameters = context.readContentString(); //TODO: Temporary
+            val parameters = context.readContentString();
 
             val remoteObj = getRemoteObject(objId);
             val paras = JsonParser.parseString(parameters);
diff --git a/app/src/main/java/com/futo/platformplayer/stores/db/ManagedDBIndex.kt b/app/src/main/java/com/futo/platformplayer/stores/db/ManagedDBIndex.kt
new file mode 100644
index 00000000..d67bde96
--- /dev/null
+++ b/app/src/main/java/com/futo/platformplayer/stores/db/ManagedDBIndex.kt
@@ -0,0 +1,5 @@
+package com.futo.platformplayer.stores.db
+
+class ManagedDBIndex {
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/futo/platformplayer/stores/db/ManagedDBStore.kt b/app/src/main/java/com/futo/platformplayer/stores/db/ManagedDBStore.kt
new file mode 100644
index 00000000..9d24c04f
--- /dev/null
+++ b/app/src/main/java/com/futo/platformplayer/stores/db/ManagedDBStore.kt
@@ -0,0 +1,5 @@
+package com.futo.platformplayer.stores.db
+
+class ManagedDBStore {
+
+}
\ No newline at end of file
-- 
GitLab