io.javalin.websocket.WsContext.kt Maven / Gradle / Ivy
/*
* 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