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)
        }
    }
}