All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.javalin.websocket.WsContext.kt Maven / Gradle / Ivy

There is a newer version: 6.3.0
Show newest version
/*
 * Javalin - https://javalin.io
 * Copyright 2017 David Åse
 * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
 */

package io.javalin.websocket

import io.javalin.http.Context
import io.javalin.jetty.upgradeContextKey
import io.javalin.jetty.upgradeSessionAttrsKey
import io.javalin.json.jsonMapper
import io.javalin.util.javalinLazy
import org.eclipse.jetty.websocket.api.CloseStatus
import org.eclipse.jetty.websocket.api.RemoteEndpoint
import org.eclipse.jetty.websocket.api.Session
import java.lang.reflect.Type
import java.nio.ByteBuffer
import java.util.concurrent.TimeUnit
import kotlin.LazyThreadSafetyMode.NONE
import kotlin.reflect.javaType
import kotlin.reflect.typeOf

/**
 * The [WsContext] class holds Jetty's [Session] and provides (convenient) delegate methods.
 * It adds functionality similar to the API found in [io.javalin.http.Context].
 * It also adds a [send] method, which calls [RemoteEndpoint.sendString] on [Session.getRemote]
 */
abstract class WsContext(val sessionId: String, @JvmField val session: Session) {

    internal val upgradeCtx by javalinLazy { session.jettyUpgradeRequest.httpServletRequest.getAttribute(upgradeContextKey) as Context }

    @Suppress("UNCHECKED_CAST")
    private val sessionAttributes by javalinLazy { session.jettyUpgradeRequest.httpServletRequest.getAttribute(upgradeSessionAttrsKey) as? Map }

    /** Returns the path that was used to match this request */
    fun matchedPath() = upgradeCtx.matchedPath()

    /** Reified version of [sendAsClass] (Kotlin only) */
    @OptIn(ExperimentalStdlibApi::class)
    inline fun  sendAsClass(message: T) = sendAsClass(message, typeOf().javaType)

    /** Serializes object to a JSON-string using the registered [io.javalin.json.JsonMapper] and sends it over the socket */
    fun send(message: Any) = sendAsClass(message, message::class.java)

    /** Serializes object to a JSON-string using the registered [io.javalin.json.JsonMapper] and sends it over the socket */
    fun sendAsClass(message: Any, type: Type) = send(upgradeCtx.jsonMapper().toJsonString(message, type))

    /** Sends a [String] over the socket */
    fun send(message: String) = session.remote.sendString(message)

    /** Sends a [ByteBuffer] over the socket */
    fun send(message: ByteBuffer) = session.remote.sendBytes(message)

    /** Sends a ping over the socket */
    @JvmOverloads
    fun sendPing(applicationData: ByteBuffer? = null) = session.remote.sendPing(applicationData ?: ByteBuffer.allocate(0))

    /** Enables automatic pings at the specified interval, preventing the connection from timing out */
    @JvmOverloads
    fun enableAutomaticPings(interval: Long = 1, unit: TimeUnit = TimeUnit.MINUTES, applicationData: ByteBuffer? = null) {
        enableAutomaticPings(this, interval, unit, applicationData)
    }

    /** Disables automatic pings */
    fun disableAutomaticPings() {
        disableAutomaticPings(this)
    }

    /** Returns the full query [String], or null if no query is present */
    fun queryString(): String? = upgradeCtx.queryString()

    /** Returns a [Map] of all the query parameters */
    fun queryParamMap(): Map> = upgradeCtx.queryParamMap()

    /** Returns a [List] of all the query parameters for the given key, or an empty [List] if no such parameter exists */
    fun queryParams(key: String): List = upgradeCtx.queryParams(key)

    /** Returns the first query parameter for the given key, or null if no such parameter exists */
    fun queryParam(key: String): String? = upgradeCtx.queryParam(key)

    /** Creates a typed [io.javalin.validation.Validator] for the [queryParam] value */
    fun  queryParamAsClass(key: String, clazz: Class) = upgradeCtx.queryParamAsClass(key, clazz)

    /** Reified version of [queryParamAsClass] (Kotlin only) */
    inline fun  queryParamAsClass(key: String) = queryParamAsClass(key, T::class.java)

    /** Returns a [Map] of all the path parameters */
    fun pathParamMap(): Map = upgradeCtx.pathParamMap()

    /** Returns a path param by name (ex: pathParam("param")).
     *
     * Ex: If the handler path is /users/{user-id}, and a browser GETs /users/123, pathParam("user-id") will return "123"
     */
    fun pathParam(key: String): String = upgradeCtx.pathParam(key)

    /** Creates a typed [io.javalin.validation.Validator] for the [pathParam] value */
    fun  pathParamAsClass(key: String, clazz: Class) = upgradeCtx.pathParamAsClass(key, clazz)

    /** Reified version of [pathParamAsClass] (Kotlin only) */
    inline fun  pathParamAsClass(key: String) = pathParamAsClass(key, T::class.java)

    /** Returns the host as a [String] */
    fun host(): String = session.jettyUpgradeRequest.host // why can't we get this from upgradeCtx?

    /** Gets a request header by name, or null. */
    fun header(header: String): String? = upgradeCtx.header(header)

    /** Gets a [Map] with all the header keys and values  */
    fun headerMap(): Map = upgradeCtx.headerMap()

    /** Creates a typed [io.javalin.validation.Validator] for the [header] value */
    fun  headerAsClass(header: String, clazz: Class) = upgradeCtx.headerAsClass(header, clazz)

    /** Gets a request cookie by name, or null. */
    fun cookie(name: String) = upgradeCtx.cookie(name)

    /** Gets a [Map] with all the request cookies */
    fun cookieMap(): Map = upgradeCtx.cookieMap()

    /** Sets an attribute on the request. Attributes are available to other handlers in the request lifecycle. */
    fun attribute(key: String, value: Any?) = upgradeCtx.attribute(key, value)

    /** Gets the specified attribute from the request. */
    fun  attribute(key: String): T? = upgradeCtx.attribute(key)

    /** Gets a [Map] with all the attribute keys and values on the request */
    fun attributeMap(): Map = upgradeCtx.attributeMap()

    /** Gets a session attribute by name */
    @Suppress("UNCHECKED_CAST")
    fun  sessionAttribute(key: String): T? = sessionAttributeMap()[key] as T

    /** Gets a [Map] with all the session attributes */
    fun sessionAttributeMap(): Map = sessionAttributes ?: mapOf()

    /** Close the session */
    fun closeSession() = session.close()

    /** Close the session with a [CloseStatus] */
    fun closeSession(closeStatus: CloseStatus) = session.close(closeStatus)

    /** Close the session with a code and reason */
    fun closeSession(code: Int, reason: String?) = session.close(code, reason)

    override fun equals(other: Any?) = session == (other as WsContext).session
    override fun hashCode() = session.hashCode()
}

class WsConnectContext(sessionId: String, session: Session) : WsContext(sessionId, session)

class WsErrorContext(sessionId: String, session: Session, private val error: Throwable?) : WsContext(sessionId, session) {
    /** Get the [Throwable] error that occurred */
    fun error() = error
}

class WsCloseContext(sessionId: String, session: Session, private val statusCode: Int, private val reason: String?) : WsContext(sessionId, session) {
    /** The int status for why connection was closed */
    fun status() = statusCode

    /** The reason for the close */
    fun reason() = reason
}

class WsBinaryMessageContext(sessionId: String, session: Session, private val data: ByteArray, private val offset: Int, private val length: Int) : WsContext(sessionId, session) {
    /** Get the binary data of the message */
    fun data() = data

    /** Get the offset of the binary data */
    fun offset() = offset

    /** Get the length of the binary data */
    fun length() = length
}

class WsMessageContext(sessionId: String, session: Session, private val message: String) : WsContext(sessionId, session) {
    /** Receive a string message from the client */
    fun message(): String = message

    /** Receive a message from the client as a class */
    fun  messageAsClass(type: Type): T = upgradeCtx.jsonMapper().fromJsonString(message, type)

    /** See Also: [messageAsClass] */
    fun  messageAsClass(clazz: Class): T = messageAsClass(type = clazz as Type)

    /** Reified version of [messageAsClass] (Kotlin only) */
    @OptIn(ExperimentalStdlibApi::class)
    inline fun  messageAsClass(): T = messageAsClass(typeOf().javaType)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy