Update both server and client for websocket support
This commit is contained in:
		@@ -24,7 +24,7 @@ tasks.withType<Jar> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
dependencies {
 | 
					dependencies {
 | 
				
			||||||
    testImplementation(kotlin("test"))
 | 
					    testImplementation(kotlin("test"))
 | 
				
			||||||
    implementation("com.github.kittinunf.fuel:fuel:3.0.0-alpha03")
 | 
					    implementation("com.squareup.okhttp3:okhttp:4.12.0")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
tasks.test {
 | 
					tasks.test {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,9 @@
 | 
				
			|||||||
package xyz.maxwellj.chookpen.client
 | 
					package xyz.maxwellj.chookpen.client
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import fuel.Fuel
 | 
					import okhttp3.*
 | 
				
			||||||
import fuel.get
 | 
					import java.util.Scanner
 | 
				
			||||||
 | 
					import kotlin.system.exitProcess
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.io.File
 | 
					import java.io.File
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import kotlin.system.exitProcess
 | 
					import kotlin.system.exitProcess
 | 
				
			||||||
@@ -14,7 +16,7 @@ fun md5(input:String): String {
 | 
				
			|||||||
    return BigInteger(1, md.digest(input.toByteArray())).toString(16).padStart(32, '0')
 | 
					    return BigInteger(1, md.digest(input.toByteArray())).toString(16).padStart(32, '0')
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
suspend fun main(args: Array<String>) {
 | 
					fun main() {
 | 
				
			||||||
	// Variables 
 | 
						// Variables 
 | 
				
			||||||
    var name = ""
 | 
					    var name = ""
 | 
				
			||||||
    var server = ""
 | 
					    var server = ""
 | 
				
			||||||
@@ -23,50 +25,6 @@ suspend fun main(args: Array<String>) {
 | 
				
			|||||||
    var password = ""
 | 
					    var password = ""
 | 
				
			||||||
    var configFile = File("${System.getProperty("user.home")}/chookpen.profile")
 | 
					    var configFile = File("${System.getProperty("user.home")}/chookpen.profile")
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    if (!configFile.exists()) {
 | 
					 | 
				
			||||||
        println("You don't have a Chookpen profile set up yet. If you've got a Chookpen profile, type 'l' to login and press enter. Otherwise, just press enter.")
 | 
					 | 
				
			||||||
        val userInput = readln()
 | 
					 | 
				
			||||||
        if (userInput == "l") {
 | 
					 | 
				
			||||||
            println("Username:")
 | 
					 | 
				
			||||||
            name = readln()
 | 
					 | 
				
			||||||
            println("Password:")
 | 
					 | 
				
			||||||
            password = readln()
 | 
					 | 
				
			||||||
            val request = Fuel.get("http://localhost:7070/api/logintest/username:{$name}token:{${md5(password)}}").body.string()
 | 
					 | 
				
			||||||
            if (request == "Invalid token") {
 | 
					 | 
				
			||||||
                println("Invalid password. Please rerun the program and put in the right password")
 | 
					 | 
				
			||||||
                exitProcess(1)
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                configFile.createNewFile()
 | 
					 | 
				
			||||||
                configFile.writeText("$name:$password:localhost:7070:0")
 | 
					 | 
				
			||||||
                println("Logged in! Run the command again to start talking!")
 | 
					 | 
				
			||||||
                exitProcess(0)
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            println("Choose a username:")
 | 
					 | 
				
			||||||
            val newName = readln()
 | 
					 | 
				
			||||||
            if (newName == "") {
 | 
					 | 
				
			||||||
                println("Please choose a username! Rerun the program and try again")
 | 
					 | 
				
			||||||
                exitProcess(1)
 | 
					 | 
				
			||||||
            println("Choose a password:")
 | 
					 | 
				
			||||||
            val newPassword = readln()
 | 
					 | 
				
			||||||
            if (newPassword == "") {
 | 
					 | 
				
			||||||
                println("Please choose a password! Rerun the program and try again")
 | 
					 | 
				
			||||||
                exitProcess(1)
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            val request = Fuel.get("http://localhost:7070/api/createAccount/username:{$newName}token:{${md5(newPassword)}}").body.string()
 | 
					 | 
				
			||||||
            if (request == "That username already exists on the server! Please choose a different username") {
 | 
					 | 
				
			||||||
                println("That username already exists on the server! Rerun the program and choose a different username")
 | 
					 | 
				
			||||||
                exitProcess(1)
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                configFile.createNewFile()
 | 
					 | 
				
			||||||
                configFile.writeText("$newName:$newPassword:localhost:7070:0")
 | 
					 | 
				
			||||||
                println("Account created!")
 | 
					 | 
				
			||||||
                exitProcess(0)
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    var configStage = 0
 | 
					    var configStage = 0
 | 
				
			||||||
    for (char in configFile.readText()) {
 | 
					    for (char in configFile.readText()) {
 | 
				
			||||||
        if (char == ':') {configStage ++}
 | 
					        if (char == ':') {configStage ++}
 | 
				
			||||||
@@ -88,22 +46,67 @@ suspend fun main(args: Array<String>) {
 | 
				
			|||||||
    hasHTTPS = hasHTTPS.replace(":", "")
 | 
					    hasHTTPS = hasHTTPS.replace(":", "")
 | 
				
			||||||
    password = password.replace(":", "")
 | 
					    password = password.replace(":", "")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Actual code
 | 
					    if (password == "x") {
 | 
				
			||||||
    println("Chookpen Client initialised!")
 | 
					        println("Enter your password:")
 | 
				
			||||||
    println("Hello $name@$server")
 | 
					        password = readln()
 | 
				
			||||||
    val protocol = "http"
 | 
					 | 
				
			||||||
    if (hasHTTPS == "1") {
 | 
					 | 
				
			||||||
        val protocol = "https"
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (args.count() == 0) {
 | 
					
 | 
				
			||||||
        println(Fuel.get("$protocol://$server:$port/api/syncmessages/username:{$name}token:{${md5(password)}}").body.string())
 | 
					    val client = OkHttpClient.Builder()
 | 
				
			||||||
    } else {
 | 
					        //.pingInterval(30, TimeUnit.SECONDS)
 | 
				
			||||||
        val message = args[0]
 | 
					        .build()
 | 
				
			||||||
        val isSuccessful = Fuel.get("$protocol://$server:$port/api/send/username:{$name}token:{${md5(password)}}message:{$message}").body.string()
 | 
					
 | 
				
			||||||
        if (isSuccessful != "Success") {
 | 
					    val request = Request.Builder()
 | 
				
			||||||
            println(isSuccessful)
 | 
					        .url("ws://localhost:7070/api/websocket")
 | 
				
			||||||
 | 
					        .build()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var webSocket: WebSocket? = null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    val listener = object : WebSocketListener() {
 | 
				
			||||||
 | 
					        override fun onOpen(webSocket: WebSocket, response: Response) {
 | 
				
			||||||
 | 
					            println(password)
 | 
				
			||||||
 | 
					            println(md5(password))
 | 
				
			||||||
 | 
					            println("Connection opened")
 | 
				
			||||||
 | 
					            webSocket.send("username:{$name}token:{${md5(password)}}message:{Joined the room}")
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        override fun onMessage(webSocket: WebSocket, text: String) {
 | 
				
			||||||
 | 
					            println("$text")
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        override fun onClosing(webSocket: WebSocket, code: Int, reason: String) {
 | 
				
			||||||
 | 
					            println("Connection closing: $reason")
 | 
				
			||||||
 | 
					            webSocket.close(1000, null)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
 | 
				
			||||||
 | 
					            println("Connection failed: ${t.message}")
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Set up shutdown hook for Ctrl+C handling
 | 
				
			||||||
 | 
					    Runtime.getRuntime().addShutdownHook(Thread {
 | 
				
			||||||
 | 
					        println("\nShutting down gracefully...")
 | 
				
			||||||
 | 
					        webSocket?.close(1000, "Client shutting down")
 | 
				
			||||||
 | 
					        client.dispatcher.executorService.shutdown()
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Initialize WebSocket connection
 | 
				
			||||||
 | 
					    webSocket = client.newWebSocket(request, listener)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Set up input handling
 | 
				
			||||||
 | 
					    val scanner = Scanner(System.`in`)
 | 
				
			||||||
 | 
					    println("Type your messages (press Enter to send, Ctrl+C to quit):")
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    while (true) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            val input = scanner.nextLine()
 | 
				
			||||||
 | 
					            if (input.isNotEmpty()) {
 | 
				
			||||||
 | 
					                webSocket?.send("username:{$name}token:{${md5(password)}}message:{$input}")
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } catch (e: Exception) {
 | 
				
			||||||
 | 
					            // Handle any input-related exceptions
 | 
				
			||||||
 | 
					            println("Error reading input: ${e.message}")
 | 
				
			||||||
 | 
					            break
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        println(Fuel.get("$protocol://$server:$port/api/syncmessages/username:{$name}token:{${md5(password)}}").body.string())
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    exitProcess(0)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,23 +1,75 @@
 | 
				
			|||||||
package xyz.maxwellj.chookpen
 | 
					package xyz.maxwellj.chookpen
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import io.javalin.Javalin
 | 
					import io.javalin.Javalin
 | 
				
			||||||
 | 
					import io.javalin.websocket.WsContext
 | 
				
			||||||
import com.sun.net.httpserver.HttpExchange
 | 
					import java.util.concurrent.ConcurrentHashMap
 | 
				
			||||||
import com.sun.net.httpserver.HttpHandler
 | 
					import java.util.UUID
 | 
				
			||||||
import com.sun.net.httpserver.HttpServer
 | 
					 | 
				
			||||||
import java.net.InetSocketAddress
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.io.File
 | 
					import java.io.File
 | 
				
			||||||
import java.io.BufferedReader
 | 
					import java.io.BufferedReader
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					object WsSessionManager {
 | 
				
			||||||
 | 
					    val sessions = ConcurrentHashMap<String, WsContext>()
 | 
				
			||||||
 | 
					    fun addSession(sessionID: String, ctx: WsContext) {
 | 
				
			||||||
 | 
					        sessions[sessionID] = ctx
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fun removeSession(sessionID: String) {
 | 
				
			||||||
 | 
					        sessions.remove(sessionID)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fun broadcast(message: String) {
 | 
				
			||||||
 | 
					        sessions.values.forEach { ctx ->
 | 
				
			||||||
 | 
					            ctx.send(message)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					object WsSessionManager {
 | 
				
			||||||
 | 
					    private val sessions = ConcurrentHashMap<String, WsContext>()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fun main(args: Array<String>) {
 | 
					    fun addSession(ctx: WsContext) {
 | 
				
			||||||
    val app = Javalin.create()
 | 
					        // Generate our own UUID for the session since we can't access Javalin's private sessionId
 | 
				
			||||||
        .get("/") { ctx -> ctx.result("dingus") }
 | 
					        val sessionId = UUID.randomUUID().toString()
 | 
				
			||||||
        .get("/api/send/{content}") { ctx -> ctx.result(handleSentMessage(ctx.pathParam("content")))}
 | 
					        sessions[sessionId] = ctx
 | 
				
			||||||
        .get("/api/createaccount/{content}") { ctx -> ctx.result(createAccount(ctx.pathParam("content")))}
 | 
					    }
 | 
				
			||||||
        .get("/api/syncmessages/{content}") { ctx -> ctx.result(syncMessages(ctx.pathParam("content")))}
 | 
					
 | 
				
			||||||
        .get("/api/authkey/{content}") { ctx -> ctx.result(authKey(ctx.pathParam("content")))}
 | 
					    fun removeSession(ctx: WsContext) {
 | 
				
			||||||
        .start(7070)
 | 
					        // Find and remove the session by context
 | 
				
			||||||
 | 
					        sessions.entries.removeIf { it.value === ctx }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fun broadcast(message: String) {
 | 
				
			||||||
 | 
					        sessions.values.forEach { ctx ->
 | 
				
			||||||
 | 
					            ctx.send(message)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fun extractMessageContent(inputData: String): String {
 | 
				
			||||||
 | 
					    var username = ""
 | 
				
			||||||
 | 
					    var message = ""
 | 
				
			||||||
 | 
					    var dataType = ""
 | 
				
			||||||
 | 
					    var isParsingData = 0
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    for (char in inputData) {
 | 
				
			||||||
 | 
					        if (char == ':') {
 | 
				
			||||||
 | 
					            isParsingData = 1
 | 
				
			||||||
 | 
					        } else if (isParsingData == 1) {
 | 
				
			||||||
 | 
					            if (char == '}') {
 | 
				
			||||||
 | 
					                isParsingData = 0
 | 
				
			||||||
 | 
					                dataType = ""
 | 
				
			||||||
 | 
					            } else if (char != '{') {
 | 
				
			||||||
 | 
					                if (dataType == "username") {
 | 
				
			||||||
 | 
					                    username += char
 | 
				
			||||||
 | 
					                } else if (dataType == "message") {
 | 
				
			||||||
 | 
					                    message += char
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            dataType += char
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    return("$username: $message")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fun handleSentMessage(inputData: String): String {
 | 
					fun handleSentMessage(inputData: String): String {
 | 
				
			||||||
@@ -306,3 +358,78 @@ fun authKey(inputData: String): String {
 | 
				
			|||||||
    return("Success")
 | 
					    return("Success")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fun main(args: Array<String>) {
 | 
				
			||||||
 | 
					    val app = Javalin.create()
 | 
				
			||||||
 | 
					        .get("/") { ctx -> ctx.result("dingus") }
 | 
				
			||||||
 | 
					        .get("/api/send/{content}") { ctx -> 
 | 
				
			||||||
 | 
					            val result = handleSentMessage(ctx.pathParam("content"))
 | 
				
			||||||
 | 
					            if (result == "Success") {
 | 
				
			||||||
 | 
					                val messageContent = extractMessageContent(ctx.pathParam("content"))
 | 
				
			||||||
 | 
					                WsSessionManager.broadcast(messageContent)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            ctx.result(result)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        .get("/api/createaccount/{content}") { ctx -> ctx.result(createAccount(ctx.pathParam("content")))}
 | 
				
			||||||
 | 
					        .get("/api/syncmessages/{content}") { ctx -> ctx.result(syncMessages(ctx.pathParam("content")))}
 | 
				
			||||||
 | 
					        .get("/api/authkey/{content}") { ctx -> ctx.result(authKey(ctx.pathParam("content")))}
 | 
				
			||||||
 | 
					        .ws("/api/websocket") { ws -> 
 | 
				
			||||||
 | 
					            ws.onConnect { ctx -> 
 | 
				
			||||||
 | 
					                WsSessionManager.addSession(ctx)
 | 
				
			||||||
 | 
					                ctx.send("Websocket success")
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            ws.onClose { ctx ->
 | 
				
			||||||
 | 
					                WsSessionManager.removeSession(ctx)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            ws.onMessage { ctx -> 
 | 
				
			||||||
 | 
					                println(ctx.message()) 
 | 
				
			||||||
 | 
					                val successState = handleSentMessage(ctx.message())
 | 
				
			||||||
 | 
					                if (successState != "Success") {
 | 
				
			||||||
 | 
					                    ctx.send(successState)
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    // Broadcast the message to all clients if successful
 | 
				
			||||||
 | 
					                    val messageContent = extractMessageContent(ctx.message())
 | 
				
			||||||
 | 
					                    WsSessionManager.broadcast(messageContent)
 | 
				
			||||||
 | 
					                    ctx.send("Message sent successfully")
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } 
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        .start(7070)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					fun main(args: Array<String>) {
 | 
				
			||||||
 | 
					    val app = Javalin.create()
 | 
				
			||||||
 | 
					        .get("/") { ctx -> ctx.result("dingus") }
 | 
				
			||||||
 | 
					        .get("/api/send/{content}") { ctx -> 
 | 
				
			||||||
 | 
					            val result = handleSentMessage(ctx.pathParam("content"))
 | 
				
			||||||
 | 
					            if (result == "Success") {
 | 
				
			||||||
 | 
					                val messageContent = extractMessageContent(ctx.pathParam("content")
 | 
				
			||||||
 | 
					                WsSessionManager.broadcast(messageContent)
 | 
				
			||||||
 | 
					            ctx.result(result)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        .get("/api/createaccount/{content}") { ctx -> ctx.result(createAccount(ctx.pathParam("content")))}
 | 
				
			||||||
 | 
					        .get("/api/syncmessages/{content}") { ctx -> ctx.result(syncMessages(ctx.pathParam("content")))}
 | 
				
			||||||
 | 
					        .get("/api/authkey/{content}") { ctx -> ctx.result(authKey(ctx.pathParam("content")))}
 | 
				
			||||||
 | 
					        .ws("/api/websocket") { ws -> 
 | 
				
			||||||
 | 
					            ws.onConnect { ctx -> 
 | 
				
			||||||
 | 
					                WsSessionManager.addSession(ctx.sessionId, ctx)
 | 
				
			||||||
 | 
					                ctx.send("Websocket success")
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            ws.onClose { ctx ->
 | 
				
			||||||
 | 
					                WsSessionManager.removeSession(ctx.sessionId)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            ws.onMessage { ctx -> 
 | 
				
			||||||
 | 
					                println(ctx.message()) 
 | 
				
			||||||
 | 
					                val successState = handleSentMessage(ctx.message())
 | 
				
			||||||
 | 
					                if (successState != "Success") {
 | 
				
			||||||
 | 
					                    ctx.send(successState)
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    ctx.send("Message sent successfully")
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } 
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    .start(7070)
 | 
				
			||||||
 | 
					}*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user