Skip to content

WebSocket 扩展程序 API

Ktor WebSocket API 支持编写您自己的扩展程序(例如 RFC-7692)或任何自定义扩展程序。

安装扩展程序

为了安装和配置扩展程序,我们提供了两种方法:extensionsinstall,可以按以下方式使用:

kotlin
install(WebSockets) {
    extensions { /* WebSocketExtensionConfig.() -> Unit */
        install(MyWebSocketExtension) { /* MyWebSocketExtensionConfig.() -> Unit */
        /* 可选的扩展程序配置。 */ 
        }
    }
}

扩展程序按安装顺序使用。

检查扩展程序是否已协商

所有安装的扩展程序都会经过协商过程,成功协商的扩展程序将在请求期间使用。 您可以使用 WebSocketSession.extensions: List<WebSocketExtension<*>> 属性,其中包含当前会话使用的所有扩展程序的列表。

有两种方法可以检查扩展程序是否正在使用中:WebSocketSession.extensionWebSocketSession.extensionOrNull

kotlin
webSocket("/echo") {
    val myExtension = extension(MyWebSocketExtension) // 如果未协商 `MyWebSocketExtension`,将抛出异常
    // 或
    val myExtension = extensionOrNull(MyWebSocketExtension) ?: close() // 如果未协商 `MyWebSocketExtension`,将关闭会话
}

编写新扩展程序

实现新扩展程序有两个接口:WebSocketExtension<ConfigType: Any>WebSocketExtensionFactory<ConfigType : Any, ExtensionType : WebSocketExtension<ConfigType>>。 单个实现可以同时适用于客户端和服务器。

以下是一个简单的帧日志记录扩展程序的实现示例:

kotlin
class FrameLoggerExtension(val logger: Logger) : WebSocketExtension<FrameLogger.Config> {

该插件具有两组字段和方法。第一组用于扩展程序协商:

kotlin
    /** 要发送到客户端请求中以进行协商的协议列表 **/
    override val protocols: List<WebSocketExtensionHeader> = emptyList()
   
    /** 
      * 此方法将在服务器上调用,并将处理来自客户端的 `requestedProtocols`。
      * 因此,它将返回服务器同意使用的扩展程序列表。
      */
    override fun serverNegotiation(requestedProtocols: List<WebSocketExtensionHeader>): List<WebSocketExtensionHeader> {
        logger.log("Server negotiation")
        return emptyList()
    }

    /**
      * 此方法将在客户端上调用,并带有一个由 `serverNegotiation` 生成的协议列表。它将决定是否应使用这些扩展程序。 
      */ 
    override fun clientNegotiation(negotiatedProtocols: List<WebSocketExtensionHeader>): Boolean {
        logger.log("Client negotiation")
        return true
    }

第二组用于实际的帧处理。这些方法将接收一个帧,并在必要时生成一个新的处理后的帧:

kotlin
    override fun processOutgoingFrame(frame: Frame): Frame {
        logger.log("Process outgoing frame: $frame")
        return frame
    }

    override fun processIncomingFrame(frame: Frame): Frame {
        logger.log("Process incoming frame: $frame")
        return frame
    }

还有一些实现细节:插件具有 Config 和对原始 factory 的引用。

kotlin
    class Config {
        lateinit var logger: Logger
    }

    /**
    * 可以创建当前扩展程序实例的工厂。 
    */
    override val factory: WebSocketExtensionFactory<Config, FrameLogger> = FrameLoggerExtension

工厂通常在伴生对象中实现(类似于常规插件):

kotlin
    companion object : WebSocketExtensionFactory<Config, FrameLogger> {
        /* 用于发现已安装扩展程序实例的密钥 */
        override val key: AttributeKey<FrameLogger> = AttributeKey("frame-logger")

        /** 占用的 rsv 位列表。
         * 如果扩展程序占用了一个位,则该位不能在其他已安装的扩展程序中使用。我们使用这些位来防止插件冲突(防止安装多个压缩插件)。如果您正在根据某个 RFC 实现插件,则应在其中引用 rsv 占用的位。
         */
        override val rsv1: Boolean = false
        override val rsv2: Boolean = false
        override val rsv3: Boolean = false

       /** 创建插件实例。将针对每个 WebSocket 会话调用 **/
        override fun install(config: Config.() -> Unit): FrameLogger {
            return FrameLogger(Config().apply(config).logger)
        }
    }
}