diff --git a/artifacts/index.html b/artifacts/index.html index f211123c0..b5a4af914 100644 --- a/artifacts/index.html +++ b/artifacts/index.html @@ -953,7 +953,7 @@

Web Socket artifactsKrossbow is a Kotlin multiplatform STOMP 1.2 client with a coroutine-based API.

It is built on a web socket client abstraction, and provides a bunch of adapters for popular web socket clients (OkHttp, Ktor, Spring, SockJS...). It also provides out-of-the-box a built-in web socket implementation (without third-party dependencies) for most platforms (see below).

Krossbow can also be used as a multiplatform web socket client without STOMP protocol.

"},{"location":"#features","title":"Features","text":"

All the STOMP 1.2 specification is implemented:

Additional features:

If you find a bug or a feature that's missing compared to the specification, please open an issue.

"},{"location":"#supported-targets","title":"Supported targets","text":"

Krossbow supports most Kotlin targets in its STOMP and web socket API modules: JVM, JS (browser and nodeJS), iOS, watchOS, tvOS, macOSX64, linuxX64, mingwX64.

However, each web socket client implementation has its own subset of supported targets (see below).

Android not tested on CI

Android 5.0+ (API level 21+) is supported by using JVM artifacts (e.g. OkHttp). However, the Android tooling's desugaring is currently not tested as part of the build, so any feedback on this use case is more than welcome. Please upvote the corresponding issue if you'd like to see proper CI or special packaging for the Android target.

"},{"location":"#web-socket-clients-target-support","title":"Web socket clients target support","text":"

Krossbow can use built-in web socket implementations without third-party dependencies on some platforms. It also provides adapters for third-party implementations which have different platform support. Here is a summary of the supported platforms by module:

Module Browser NodeJS JVM iOS / tvOS / watchOS macOS / Linux / Windows Transitive dependencies Built-in (JDK\u00a011+) None Ktor Ktor, and the relevant Ktor engine(s) OkHttp OkHttp SockJS sockjs-client (on JS), Spring websocket (on JVM) Spring Spring websocket

supported with actual web socket transport (RFC6455)

supported using SockJS protocol (requires a SockJS server)

"},{"location":"#contribute","title":"Contribute","text":"

Don't hesitate to open GitHub issues, even to ask questions or discuss a new feature. Pull-requests are welcome, but please open an issue first so that we can discuss the initial design or fix, which may avoid unnecessary work.

"},{"location":"artifacts/","title":"Artifacts summary","text":"

Krossbow offers a lot of possibilities so here is a summary of all available artifacts.

All these artifacts are published to Maven Central under the group ID org.hildan.krossbow and a common version.

"},{"location":"artifacts/#stomp-artifacts","title":"STOMP artifacts","text":"

You should pick only one of the krossbow-stomp-* artifacts, depending on whether you need automatic serialization of frame bodies:

Artifact Description
krossbow-stomp-core
The basic multiplatform STOMP client. It implements the STOMP 1.2 protocol on top of the web socket abstraction defined by the krossbow-websocket-core module.
krossbow-stomp-jackson
A superset of krossbow-stomp-core adding JSON conversion features using Jackson (JVM only)
krossbow-stomp-moshi
A superset of krossbow-stomp-core adding JSON conversion features using Moshi (JVM only)
krossbow-stomp-kxserialization
A superset of krossbow-stomp-core adding conversion features using Kotlinx Serialization library (multiplatform). You can leverage the multi-format capabilities of Kotlinx Serialization (JSON, protobuf, CBOR, ...).
krossbow-stomp-kxserialization-json
A superset of krossbow-stomp-kxserialization adding JSON helpers and the JSON format dependency.

Then add the dependency of your choice to your Gradle build. For instance, if you intend to use Krossbow with Kotlinx Serialization:

implementation(\"org.hildan.krossbow:krossbow-stomp-kxserialization:7.0.0\")\n

Don't need STOMP?

If you're just interested in the web socket client without STOMP protocol, don't declare a STOMP artifact, but instead choose one of the web socket artifacts below.

"},{"location":"artifacts/#web-socket-artifacts","title":"Web Socket artifacts","text":"

The STOMP artifacts depend on a web socket API that needs an implementation. Krossbow provides implementations for the built-in web socket API of most platforms, and also adapters for 3rd-party web socket implementations:

Artifact Description
krossbow-websocket-builtin
A multiplatform WebSocketClient implementation that adapts the built-in client for each supported platform without transitive dependency.
krossbow-websocket-ktor
A multiplatform WebSocketClient implementation based on Ktor 2.3.9's HttpClient.
krossbow-websocket-okhttp
A JVM implementation of the web socket API using OkHttp's client.
krossbow-websocket-sockjs
A multiplatform WebSocketClient implementation for use with SockJS servers. It uses Spring's SockJSClient on JVM, and npm sockjs-client for JavaScript (NodeJS and browser).
krossbow-websocket-spring
A JVM 8+ implementation of the web socket API using Spring's WebSocketClient. Provides both a normal WebSocket client and a SockJS one.

Peer dependencies

Some Krossbow modules are not opinionated and require some extra third-party peer dependencies. Make sure to read the usage section corresponding to the module of your choice for more details.

"},{"location":"license/","title":"MIT License","text":"

Copyright (c) 2019-2023 Joffrey Bion

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

"},{"location":"migration-guides/","title":"Migration guides","text":"

Here are some details about how to migrate from one major version to another.

"},{"location":"migration-guides/#from-6x-to-7x","title":"From 6.x to 7.x","text":""},{"location":"migration-guides/#web-socket-subprotocol-negotiation","title":"Web socket subprotocol negotiation","text":"

As part of issue #493, a separate protocols parameter was added to WebSocketClient.connect to enable web socket subprotocol negotiation.

Binary compatibility is preserved through some hidden synthetic functions. However, source compatibility isn't: usages of the connect() method that passed custom headers without a named headers parameter will no longer compile. Adding the headers = parameter name will solve the issue.

For full negotiation support, clients need to be aware of which subprotocol the server chose to speak. This is why the protocol property was added to WebSocketConnection (#498). Implementers of this interface must implement this property. There shouldn't be many 3rd party implementations of the connection interface, so binary compatibility should not be a real issue here.

"},{"location":"migration-guides/#stomp-web-socket-subprotocol-negotiation","title":"STOMP web socket subprotocol negotiation","text":"

Some servers like ActiveMQ require negotiating the STOMP protocol as a web socket subprotocol during the web socket handshake (see issue #492), and cannot work otherwise.

Breaking change: To make the experience smoother, Krossbow v7.0.0 now automatically sends STOMP subprotocols (in all supported versions) during the web socket handshake via the Sec-WebSocket-Protocol header.

If your server doesn't support it, you can customize the web socket handshake by manually connecting using WebSocketClient.connect() with the parameters that suit you best, and then connect at STOMP level using the WebSocketConnection.stomp() extension (without the need for a StompClient at all):

val client: WebSocketClient = TODO(\"get some web socket client implementation\")\nval wsConnection = client.connect(url) // without any subprotocols\nval stompConfig = StompConfig().apply { \n    // set your config here if needed\n}\nval stompSession = wsConnection.stomp(config)\n
"},{"location":"migration-guides/#no-host-header-sent-for-stomp-10","title":"No host header sent for STOMP 1.0","text":"

Thanks to the aforementioned changes, we can now detect the STOMP protocol version used by the server before sending the first STOMP frame (CONNECT or STOMP). If STOMP 1.0 is detected as web socket subprotocol during the web socket handshake, we no longer send by default the host header which was introduced in 1.1 (and effectively breaks some old servers, see #122). It can still be sent by manually specifying it of course.

"},{"location":"migration-guides/#stomp-protocol-version-negotiation","title":"STOMP protocol version negotiation","text":"

The STOMP protocol itself supports negotiation of the version via headers in the CONNECT (or STOMP) frame. So far, Krossbow only specified 1.2 as supported version. From now on, all 3 versions 1.0, 1.1, and 1.2 are advertised as supported by the client.

If necessary, this behavior can be overridden by sending the accept-version header manually in customStompConnectHeaders.

Because the protocol version can be negotiated both via web socket subprotocol and at STOMP level, there could potentially be a mismatch. If this happens, the connect() call throws an exception. This can be disabled with StompConfig.failOnStompVersionMismatch.

"},{"location":"migration-guides/#stompclientconnect-now-throws-a-different-websocketconnectionexception","title":"StompClient.connect() now throws a different WebSocketConnectionException","text":"

The org.hildan.krossbow.stomp.WebSocketConnectionException is deprecated in favor of org.hildan.krossbow.websocket.WebSocketConnectionException. That exception has been around for a few years now and encapsulates all connection failures from different client implementations already, so there is no need for a similar exception at the StompClient level.

If you used to catch this exception, make sure to update your import (the error-level deprecation should mitigate any risk of missing the new exception).

"},{"location":"migration-guides/#from-5x-to-6x","title":"From 5.x to 6.x","text":""},{"location":"migration-guides/#switch-to-kotlinx-io-and-the-bytestring-type","title":"Switch to kotlinx-io and the ByteString type","text":"

The kotlinx-io library has been revamped, with an implementation that closely matches Okio now. Krossbow has now internally switched from Okio to kotlinx-io as a result, but this part should have no visible effect for the consumers of the library.

However, since kotlinx-io is a somewhat \"standard\" extension library for Kotlin, Krossbow can now more legitimately use its ByteString type in public APIs that handled binary data (for both web socket and STOMP sessions). This type represents immutable sequences of bytes, which is more convenient API-wise than byte arrays.

You will have to switch to these types if you used binary-based APIs.

"},{"location":"migration-guides/#deprecations-cleanup","title":"Deprecations cleanup","text":"

This is a major version, and therefore we allowed ourselves some cleanup by removing a bunch of deprecated APIs. If you see unresolved references and are not sure how to fix them, please switch back to the Krossbow version 5 and fix deprecation warnings by following the corresponding instructions.

Please check the changelog for the list of removals.

"},{"location":"migration-guides/#from-4x-to-5x","title":"From 4.x to 5.x","text":""},{"location":"migration-guides/#end-of-ktor-1x-support","title":"End of Ktor 1.x support","text":"

krossbow-websocket-ktor-legacy artifact was removed. This means Krossbow no longer works with Ktor 1.x. If you were using this module, please migrate to Ktor 2, and use the non-legacy krossbow-websocket-ktor module.

"},{"location":"migration-guides/#kotlin-18","title":"Kotlin 1.8","text":"

This update of Krossbow brings Kotlin 1.8, which might bring some incompatible changes to the Kotlin stdlib. Please check the compatibility guide if you were using an older version of Kotlin.

"},{"location":"migration-guides/#from-3x-to-4x","title":"From 3.x to 4.x","text":""},{"location":"migration-guides/#withjsonconversions-moved-to-its-own-module","title":"withJsonConversions moved to its own module","text":"

If you were using Krossbow with krossbow-stomp-kxserialization, the withJsonConversions helper has moved to a new module called krossbow-stomp-kxserialization-json. This new module now transitively brings kotlinx-serialization-json so you don't need to depend on that one explicitly.

"},{"location":"migration-guides/#built-in-web-socket-clients-moved-to-their-own-module-and-default-stompclient-constructor-removed","title":"Built-in web socket clients moved to their own module and default StompClient constructor removed","text":"

Up to (and including) version 3.x of Krossbow, the built-in web socket clients for the supported platforms were part of the krossbow-websocket-core module. This module provided a WebSocketClient.Companion.default() factory function to provide the built-in web socket implementation of the current platform. Likewise, the krossbow-stomp-core module provided a StompClient constructor that used the \"default\" built-in web socket implementation for the current platform.

This approach limited the targets supported by those 2 core modules, even though all of their functionality was target-agnostic. In order to support all Kotlin platforms in pure Kotlin modules, the built-in websocket implementations had to be moved to a separate module, and the constructor without web socket client was moved to a separate module (and later removed completely for simplicity).

Breaking dependency changes, in short:

Note: the WebSocketClient.default() function was since renamed WebSocketClient.builtIn() in newer versions.

If you used other web socket implementations than the built-in ones, you don't have to change anything to your dependencies.

"},{"location":"migration-guides/#from-2x-to-3x","title":"From 2.x to 3.x","text":""},{"location":"migration-guides/#use-durations-instead-of-millis","title":"Use Durations instead of millis","text":"

StompConfiguration no longer uses amounts of milliseconds, but uses the kotlin.time.Duration API. The -Millis suffixes for the relevant properties were therefore dropped and the types changed.

Before:

val stomp = StompClient {\n    connectionTimeoutMillis = 2000\n    receiptTimeoutMillis = 5000\n    disconnectTimeoutMillis = 300\n}\n

After:

import kotlin.time.Duration.Companion.milliseconds\nimport kotlin.time.Duration.Companion.seconds\n\nval stomp = StompClient {\n    connectionTimeout = 2.seconds\n    receiptTimeout = 5.seconds\n    disconnectTimeout = 300.milliseconds\n}\n

"},{"location":"migration-guides/#flow-instead-of-channel-in-websocketconnection","title":"Flow instead of Channel in WebSocketConnection","text":"

If you used the websocket API directly, the incomingFrames channel is now a Flow.

Before:

val conn = wsClient.connect(url)\nfor (frame in conn.incomingFrames) {\n    // do stuff\n}\n

After:

val conn = wsClient.connect(url)\nconn.incomingFrames.collect {\n    // do stuff\n}\n
"},{"location":"migration-guides/#tyrus-no-longer-embedded-in-krossbow-websocket-spring","title":"Tyrus no longer embedded in krossbow-websocket-spring","text":"

krossbow-websocket-spring no longer transitively brings a dependency on Tyrus.

If you didn't add any JSR-356 implementation manually, you now have to explicitly depend on one. If you want the same behaviour as before, add the Tyrus dependency to your build.gradle.kts as follows:

dependencies {\n    implementation(\"org.glassfish.tyrus.bundles:tyrus-standalone-client-jdk:2.1.5\")\n}\n
"},{"location":"migration-guides/#from-1x-to-2x","title":"From 1.x to 2.x","text":""},{"location":"migration-guides/#stompsessionuse-now-passes-the-session-as-it-not-this","title":"StompSession.use now passes the session as it, not this","text":"

In order to align with Closeable.use, the lambda for StompSession.use now receives the session as an argument (it) and not receiver (this).

Before:

StompClient().connect(url).use {\n    sendText(\"/dest\", \"message\")\n}\n

After:

StompClient().connect(url).use {\n    it.sendText(\"/dest\", \"message\")\n}\n// or\nStompClient().connect(url).use { session ->\n    session.sendText(\"/dest\", \"message\")\n}\n
"},{"location":"stomp/advanced-features/","title":"Advanced features","text":""},{"location":"stomp/advanced-features/#receipts-suspension","title":"Receipts & Suspension","text":"

The STOMP protocol supports RECEIPT frames, allowing the client to know when the server has received a frame. This only happens if a receipt header is set on the client frame.

If auto-receipt is enabled, a receipt header is automatically generated and added to all client frames supporting the mechanism, and for which a receipt header is not already present. If auto-receipt is not enabled, a receipt header may still be provided manually in the parameters of some overloads.

When a receipt header is present (automatically added or manually provided), the method that is used to send the frame suspends until the corresponding RECEIPT frame is received from the server. If no RECEIPT frame is received from the server in the configured time limit, a LostReceiptException is thrown.

If no receipt is provided and auto-receipt is disabled, the method used to send the frame doesn't wait for a RECEIPT frame and never throws LostReceiptException. Instead, it returns immediately after the underlying web socket implementation is done sending the frame.

"},{"location":"stomp/advanced-features/#heart-beats","title":"Heart beats","text":"

When configured, heart beats can be used as a keep-alive to detect if the connection is lost. The heartBeat property should be used to configure heart beats in the StompClient.

Note that the heart beats for the STOMP session are negotiated with the server. The actual heart beats are defined by the CONNECTED frame received from the server as a result of the negotiation, and may differ from the StompClient configuration. The negotiation behaviour is defined by the specification.

Sending and checking heart beats is automatically handled by StompSession implementations, depending on the result of the negotiation with the server. If expected heart beats are not received in time, a MissingHeartBeatException is thrown and fails active subscriptions.

"},{"location":"stomp/advanced-features/#graceful-disconnect","title":"Graceful disconnect","text":"

The graceful disconnect (or graceful shutdown) is a disconnection procedure defined by the STOMP specification to make sure the server gets all the frames before dropping the connection.

If enabled in the config, when disconnecting from the server, the client first sends a DISCONNECT frame with a receipt header, and then waits for a RECEIPT frame before closing the connection.

If this graceful disconnect is disabled, then calling StompSession.disconnect() immediately closes the web socket connection. In this case, there is no guarantee that the server received all previous messages.

"},{"location":"stomp/advanced-features/#using-custom-headers-in-the-web-socket-handshake","title":"Using custom headers in the web socket handshake","text":"

Not supported in browsers

The browser's WebSocket API does not support custom headers in the handshake (see this open issue in the web socket standard repo). Because of this, Krossbow cannot support this feature for the JS browser platform. However, the JS web socket client adapter is designed in a way that allows other implementations to support it, such as a Node.js implementation.

Some servers or connection flows may require extra HTTP headers in the web socket handshake. The StompClient.connect() function doesn't support such headers out of the box, but this function is essentially just a shorthand for connecting at the web socket level, and then connecting at the STOMP level.

In fact, we technically don't need to create and use a StompClient at all in order to use STOMP with Krossbow. Krossbow provides a WebSocketConnection.stomp() extension function that establishes a STOMP connection from an existing web socket connection.

We can leverage this to customize the web socket connection at will before connecting at STOMP level. For example:

val webSocketClient = WebSocketClient.builtIn() // or another web socket client\n\n// connect at web socket level with custom headers\nval wsSession = webSocketClient.connect(url, headers = mapOf(\"Custom-Header\" to \"custom-value\"))\n\nval config = StompConfig().apply {\n    // here you can set up whatever config you would have done in the StompClient { ... } block\n}\n// connect at STOMP level on this open web socket, using the above config\nval stompSession = wsSession.stomp(config)\n
"},{"location":"stomp/config/","title":"Configuration","text":""},{"location":"stomp/config/#configuring-the-stompclient","title":"Configuring the StompClient","text":"

The StompClient can be configured at construction time using a convenient lambda block:

val stompClient = StompClient(WebSocketClient.builtIn()) {\n    connectionTimeout = 3.seconds\n    gracefulDisconnect = false\n}\n

You can also create the configuration separately and then pass it when constructing the client:

val stompConfig = StompConfig().apply {\n    connectionTimeout = 3.seconds\n    gracefulDisconnect = false\n}\n\nval stompClient = StompClient(WebSocketClient.builtIn(), stompConfig)\n
"},{"location":"stomp/config/#configuration-options","title":"Configuration options","text":"

You can find out about all configuration properties in the StompConfig KDoc.

"},{"location":"stomp/getting-started/","title":"Getting started","text":""},{"location":"stomp/getting-started/#choosing-a-web-socket-implementation","title":"Choosing a web socket implementation","text":"

Krossbow uses web sockets as transport for the STOMP communication. Multiple web socket client implementations are supported.

Check out the web socket client table to help you choose a web socket implementation based on the platforms you need to support. You can find more information about each client in their respective section of this doc.

We recommend the built-in client adapters if they cover the Kotlin targets you need to support, in order to limit 3rd party dependencies. Otherwise, Ktor is a good choice if you don't have special needs like SockJS.

"},{"location":"stomp/getting-started/#dependencies-setup","title":"Dependencies setup","text":"

For the basic usage of STOMP without serialization, add the krossbow-stomp-core dependency as well as the web socket module of your choice.

For example to use STOMP with the built-in web socket client:

implementation(\"org.hildan.krossbow:krossbow-stomp-core:7.0.0\")\nimplementation(\"org.hildan.krossbow:krossbow-websocket-builtin:7.0.0\")\n

For other web socket clients, check out their dedicated documentation page to find out which Krossbow dependencies are needed.

The rest of this guide uses the built-in client.

"},{"location":"stomp/getting-started/#basic-usage-without-body-conversions","title":"Basic usage (without body conversions)","text":"

This is how to create a STOMP client and interact with it:

val client = StompClient(WebSocketClient.builtIn()) // other config can be passed in here\nval session: StompSession = client.connect(url) // optional login/passcode can be provided here\n\n// Send text messages using this convenience function\nsession.sendText(destination = \"/some/destination\", body = \"Basic text message\")\n\n// Sometimes no message body is necessary\nsession.sendEmptyMsg(destination = \"/some/destination\") \n\n// This subscribe() call triggers a SUBSCRIBE frame\n// and returns the flow of messages for the subscription\nval subscription: Flow<String> = session.subscribeText(\"/some/topic/destination\")\n\n// Use an appropriate coroutine 'scope' to collect the received frames\nval collectorJob = scope.launch {\n    subscription.collect { msg ->\n        println(\"Received: $msg\")\n    }\n}\ndelay(3000)\n// cancelling the flow collector triggers an UNSUBSCRIBE frame\ncollectorJob.cancel()\n\nsession.disconnect()\n

If you want to disconnect automatically in case of exception or normal termination, you can use a try/finally block, or use StompSession.use(), which is similar to Closeable.use():

import kotlinx.coroutines.flow.*\nimport org.hildan.krossbow.stomp.*\nimport org.hildan.krossbow.websocket.*\nimport org.hildan.krossbow.websocket.builtin.*\n\nval client = StompClient(WebSocketClient.builtIn()) // other config can be passed in here\nval session: StompSession = client.connect(url) // optional login/passcode can be provided here\n\nsession.use { s ->\n    s.sendText(\"/some/destination\", \"Basic text message\") \n\n    val subscription: Flow<String> = s.subscribeText(\"/some/topic/destination\")\n\n    // terminal operators that finish early (like first) also trigger UNSUBSCRIBE automatically\n    val firstMessage: String = subscription.first()\n    println(\"Received: $firstMessage\")\n}\n// DISCONNECT frame was automatically sent at the end of the use{...} block\n
"},{"location":"stomp/getting-started/#using-body-conversions","title":"Using body conversions","text":"

You can use STOMP with basic text as frame bodies, but it really becomes interesting when you can convert the frame bodies back and forth into Kotlin objects.

Check out the following sections to see how to automatically convert your objects into STOMP frame bodies:

"},{"location":"stomp/conversions/custom/","title":"Custom conversions","text":"

If you want to use your own text conversion, you can implement TextMessageConverter without any additional module, and use withTextConversions to wrap your StompSession into a TypedStompSession.

Limited JS support

Reflection-based conversions may behave poorly on the JS platform. It is usually safer to rely on Kotlinx Serialization for multiplatform conversions.

val myConverter = object : TextMessageConverter {\n    override val mimeType: String = \"application/json;charset=utf-8\"\n\n    override fun <T> convertToString(value: T, type: KTypeRef<T>): String {\n        TODO(\"your own object -> text conversion\")\n    }\n\n    override fun <T> convertFromString(text: String, type: KTypeRef<T>): T {\n        TODO(\"your own text -> object conversion\")\n    }\n}\n\nStompClient(WebSocketClient.builtIn()).connect(url).withTextConversions(myConverter).use { session ->\n    session.convertAndSend(\"/some/destination\", MyPojo(\"Custom\", 42)) \n\n    val messages = session.subscribe<MyMessage>(\"/some/topic/destination\")\n    val firstMessage: MyMessage = messages.first()\n\n    println(\"Received: $firstMessage\")\n}\n
"},{"location":"stomp/conversions/jackson/","title":"STOMP with Jackson","text":"

The krossbow-stomp-jackson module is a JVM-only extension of krossbow-stomp-core that provides new APIs to send and receive properly typed classes, and automatically convert them to/from the JSON bodies of STOMP frames by leveraging Jackson and jackson-module-kotlin.

The main addition is the extension function StompSession.withJackson(), which turns your StompSession into a TypedStompSession. This new session type has additional methods that use Jackson to convert your objects into JSON and back:

StompClient(WebSocketClient.builtIn()).connect(url).withJackson().use { session ->\n    session.convertAndSend(\"/some/destination\", Person(\"Bob\", 42)) \n\n    val messages: Flow<MyMessage> = session.subscribe<MyMessage>(\"/some/topic/destination\")\n    val firstMessage: MyMessage = messages.first()\n\n    println(\"Received: $firstMessage\")\n}\n
"},{"location":"stomp/conversions/jackson/#using-a-custom-objectmapper","title":"Using a custom ObjectMapper","text":"

Jackson is highly configurable, and it's often useful to configure the ObjectMapper manually.

The withJackson() method takes an optional ObjectMapper parameter, so you can configure it as you please:

val customObjectMapper: ObjectMapper = jacksonObjectMapper()\n        .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)\n        .disable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES)\n        .enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)\n\nval client = StompClient(WebSocketClient.builtIn()).connect(url)\nval session = client.withJackson(customObjectMapper)\n
"},{"location":"stomp/conversions/jackson/#dependency","title":"Dependency","text":"

To use Jackson conversions, add krossbow-stomp-jackson to your Gradle dependencies (krossbow-stomp-core is unnecessary because it's transitively brought by this one):

implementation(\"org.hildan.krossbow:krossbow-stomp-jackson:7.0.0\")\n

This dependency transitively brings Jackson 2.17.0 with the Kotlin module.

"},{"location":"stomp/conversions/kx-serialization/","title":"STOMP with Kotlinx Serialization","text":"

Kotlinx Serialization is a multiplatform and multi-format serialization library provided by the Kotlin team. It is a popular choice for Kotlin multiplatform libraries especially because of its extensive support of Kotlin features.

The krossbow-stomp-kxserialization module is an extension of krossbow-stomp-core that provides new APIs to send and receive properly typed classes, and automatically convert STOMP frame bodies by leveraging Kotlinx Serialization.

"},{"location":"stomp/conversions/kx-serialization/#usage-with-the-json-format","title":"Usage with the JSON format","text":"

Since Kotlinx Serialization supports multiple formats with different dependencies, you normally have to add your own dependency to bring the format of your choice.

However, since JSON is so popular, Krossbow comes with the krossbow-stomp-kxserialization-json module, which adds dedicated helpers for JSON and the necessary transitive dependency on the JSON format (see dependencies section below).

This module brings the withJsonConversions helper to convert your StompSession into a StompSessionWithKxSerialization:

val session = StompClient(WebSocketClient.builtIn()).connect(url)\nval jsonStompSession = session.withJsonConversions()\n

This new session type has the additional convertAndSend method and subscribe overloads that use Kotlinx Serialization's serializers to convert your payloads using the format of your choice (in this case, JSON):

@Serializable\ndata class Person(val name: String, val age: Int)\n@Serializable\ndata class MyMessage(val timestamp: Long, val author: String, val content: String)\n\njsonStompSession.use { s ->\n    s.convertAndSend(\"/some/destination\", Person(\"Bob\", 42), Person.serializer()) \n\n    // overloads without explicit serializers exist, but should be avoided if you also target JavaScript\n    val messages: Flow<MyMessage> = s.subscribe(\"/some/topic/destination\", MyMessage.serializer())\n\n    messages.collect { msg ->\n        println(\"Received message from ${msg.author}: ${msg.content}\")\n    }\n}\n
"},{"location":"stomp/conversions/kx-serialization/#custom-json-instance","title":"Custom Json instance","text":"

The withJsonConversions() method takes an optional Json parameter, so you can configure it as you please:

// custom Json configuration\nval json = Json {\n    encodeDefaults = true\n    ignoreUnknownKeys = true\n}\n\nval session = StompClient(WebSocketClient.builtIn()).connect(url)\nval jsonStompSession = session.withJsonConversions(json)\n
"},{"location":"stomp/conversions/kx-serialization/#usage-with-other-formats","title":"Usage with other formats","text":"

For other formats than JSON, use the more general krossbow-stomp-kxserialization module, and add a dependency on the Kotlinx Serialization format of your choice (see dependencies section below).

This module brings the following extension functions on StompSession:

These helpers are equivalent to withJsonConversions, but more general, and also turn your StompSession into a StompSessionWithKxSerialization. You should provide the media type that you want to set as content-type header in the messages you send:

val session = StompClient(WebSocketClient.builtIn()).connect(url)\nval jsonStompSession = session.withBinaryConversions(Protobuf.Default, \"application/x-protobuf\")\n

You can then use convertAndSend and subscribe the same way as in the JSON section above.

"},{"location":"stomp/conversions/kx-serialization/#dependency","title":"Dependency","text":""},{"location":"stomp/conversions/kx-serialization/#general-case","title":"General case","text":"

Krossbow's base Kotlinx Serialization module is format-agnostic, so you need to add both the krossbow-stomp-kxserialization dependency and the Kotlinx Serialization dependency for the format you want to use. For instance in the case of protobuf, that would be kotlinx-serialization-protobuf:

implementation(\"org.hildan.krossbow:krossbow-stomp-kxserialization:7.0.0\")\nimplementation(\"org.jetbrains.kotlinx:kotlinx-serialization-protobuf:1.6.3\")\n
"},{"location":"stomp/conversions/kx-serialization/#json-format","title":"JSON format","text":"

Since JSON is so common, Krossbow provides an all-in-one module with additional helpers for JSON:

implementation(\"org.hildan.krossbow:krossbow-stomp-kxserialization-json:7.0.0\")\n

This module brings kotlinx-serialization-json transitively, so you don't have to add it yourself.

"},{"location":"stomp/conversions/kx-serialization/#additional-notes","title":"Additional notes","text":"

With this setup, krossbow-stomp-core is unnecessary because it's transitively brought by the krossbow-stomp-kxserialization modules.

Note that Kotlinx Serialization also requires a compiler plugin to generate serializers for your @Serializable classes. See the Kotlinx Serialization doc for more information about this.

"},{"location":"stomp/conversions/moshi/","title":"STOMP with Moshi","text":"

The krossbow-stomp-moshi module is a JVM-only extension of krossbow-stomp-core that provides new APIs to send and receive properly typed classes, and automatically convert them to/from the JSON bodies of STOMP frames by leveraging Moshi.

The main addition is the extension function StompSession.withMoshi(), which turns your StompSession into a TypedStompSession. This new session type has additional methods that use Moshi to convert your objects into JSON and back:

// example Moshi instance that converts Kotlin types using reflection\nval moshi = Moshi.Builder()\n    .addLast(KotlinJsonAdapterFactory())\n    .build()\nStompClient(WebSocketClient.builtIn()).connect(url).withMoshi(moshi).use { session ->\n    session.convertAndSend(\"/some/destination\", Person(\"Bob\", 42)) \n\n    val messages: Flow<MyMessage> = session.subscribe<MyMessage>(\"/some/topic/destination\")\n    val firstMessage: MyMessage = messages.first()\n\n    println(\"Received: $firstMessage\")\n}\n
"},{"location":"stomp/conversions/moshi/#dependency","title":"Dependency","text":"

To use Moshi conversions, add krossbow-stomp-moshi to your Gradle dependencies (krossbow-stomp-core is unnecessary because it's transitively brought by this one):

implementation(\"org.hildan.krossbow:krossbow-stomp-moshi:7.0.0\")\n

This dependency transitively brings Moshi 1.15.1.

"},{"location":"websocket/builtin/","title":"Krossbow Web Socket Built-in","text":"

The krossbow-websocket-builtin module defines implementations of the WebSocketClient interface by adapting clients that already exist in each platform (without 3rd-party dependencies).

For convenience in common source sets, this module also provides the builtIn() factory method, which returns the built-in implementation of the current platform.

"},{"location":"websocket/builtin/#jvm","title":"JVM","text":"

On the JVM target, the Jdk11WebSocketClient adapts the built-in HttpClient provided in the JRE since Java 11, and its WebSocket interface.

Android not supported

This adapter requires Java 11's HttpClient which is not available on Android. If you need to support Android, please use the OkHttp or Ktor adapter instead.

"},{"location":"websocket/builtin/#javascript","title":"JavaScript","text":"

On the JS target, the BrowserWebSocketClient adapts the browser's built-in WebSocket directly.

Also, the JsWebSocketClientAdapter allows to adapt anything that looks like the browser's WebSocket.

"},{"location":"websocket/builtin/#darwin-macos-ios-tvos-watchos","title":"Darwin (macOS, iOS, tvOS, watchOS)","text":"

On all Darwin targets, the DarwinWebSocketClient adapts the Foundation framework's NSURLSessionWebSocketTask.

"},{"location":"websocket/builtin/#dependency-information","title":"Dependency information","text":"

To use the built-in web socket clients, add the following to your build.gradle:

implementation(\"org.hildan.krossbow:krossbow-websocket-builtin:7.0.0\")\n
"},{"location":"websocket/custom/","title":"Implement Krossbow's WebSocketClient yourself","text":"

The krossbow-websocket-core module defines a standard web socket API abstraction that is used by the STOMP artifacts and that you can also use directly if you're only interested in the web socket protocol without STOMP.

Krossbow provides built-in implementations of the web socket interfaces in the krossbow-websocket-builtin module, but you can of course implement your own.

"},{"location":"websocket/custom/#basics","title":"Basics","text":"

You can create your own implementation of Krossbow's web socket client by implementing the WebSocketClient interface.

This interface simply has a connect() method returning an instance of WebSocketConnection. The WebSocketConnection actually contains the bulk of the web socket interactions implementation.

Please follow the KDoc of these interfaces to learn more about the contract that needs to be satisfied for each method.

"},{"location":"websocket/custom/#helpers","title":"Helpers","text":"

The krossbow-websocket-core module doesn't only provide interfaces to implement. It also provides some helper classes that help with most implementations of those interfaces.

The WebSocketListenerFlowAdapter allows to adapt listener-based web socket APIs to Krossbow's Flow API easily. It takes care of partial message handling automatically, and can provide backpressure on the callback caller thanks to its suspend callbacks.

The UnboundedWsListenerFlowAdapter also adapts listener-based APIs to Krossbow's flow, but without any backpressure support (functions are not suspend and return immediately). It adds new messages to an unbounded queue. This is necessary with some APIs like JS browsers WebSocket API, which cannot apply backpressure in any way on their web socket traffic.

"},{"location":"websocket/custom/#dependency-information","title":"Dependency information","text":"

Add the following to your build.gradle(.kts) in order to get the Krossbow's interfaces and helpers:

implementation(\"org.hildan.krossbow:krossbow-websocket-core:7.0.0\")\n
"},{"location":"websocket/ktor/","title":"Krossbow with Ktor","text":"

Krossbow allows you to use Ktor's web socket as transport for STOMP.

Ktor's implementation supports a variety of platforms and is very popular in the Kotlin world, especially in Kotlin multiplatform.

The krossbow-websocket-ktor module provides the KtorWebSocketClient, which adapts Ktor 2.3.9's HttpClient to Krossbow's web socket interface.

"},{"location":"websocket/ktor/#usage-with-stompclient","title":"Usage with StompClient","text":"

To use the KtorWebSocketClient pass an instance of it when creating your StompClient:

val client = StompClient(KtorWebSocketClient())\n

You can customize the actual Ktor HTTP client used behind the scenes by passing it to KtorWebSocketClient:

// You may configure Ktor HTTP client as you please,\n// but make sure at least the websocket feature is installed\nval httpClient = HttpClient {\n    install(WebSockets)\n}\nval wsClient = KtorWebSocketClient(httpClient)\nval stompClient = StompClient(wsClient)\n
"},{"location":"websocket/ktor/#dependency-information","title":"Dependency information","text":"

You will need to declare the following Gradle dependency to use the KtorWebSocketClient:

implementation(\"org.hildan.krossbow:krossbow-websocket-ktor:7.0.0\")\n

Ktor uses pluggable engines to perform the platform-specific network operations (just like Krossbow uses different web socket implementations). You need to pick an engine that supports web sockets in order to use Ktor's HttpClient with web sockets. Follow Ktor's documentation to find out more about how to use engines.

For instance, if you want to use Ktor's CIO engine with Krossbow, you need to declare the following:

implementation(\"org.hildan.krossbow:krossbow-websocket-ktor:7.0.0\")\nimplementation(\"io.ktor:ktor-client-cio:2.3.9\")\n
"},{"location":"websocket/okhttp/","title":"Krossbow with OkHttp","text":"

Krossbow allows you to use OkHttp's WebSocket as transport for STOMP.

OkHttp is very popular on Android, and is already part of many projects as HTTP client of choice.

The krossbow-websocket-okhttp module provides the OkHttpWebSocketClient, which adapts OkHttp's WebSocket to Krossbow's web socket interface.

"},{"location":"websocket/okhttp/#usage-with-stompclient","title":"Usage with StompClient","text":"

To use the OkHttpWebSocketClient pass an instance of it when creating your StompClient:

val client = StompClient(OkHttpWebSocketClient())\n

You can customize the actual OkHttpClient used behind the scenes by passing it to OkHttpWebSocketClient():

// This allows to configure the underlying OkHttpClient as you please\n// (or use an existing one from your project)\nval okHttpClient = OkHttpClient.Builder()\n    .callTimeout(Duration.ofMinutes(1))\n    .pingInterval(Duration.ofSeconds(10))\n    .build()\nval wsClient = OkHttpWebSocketClient(okHttpClient)\nval stompClient = StompClient(wsClient)\n
"},{"location":"websocket/okhttp/#dependency-information","title":"Dependency information","text":"

You will need to declare the following Gradle dependency to use the OkHttpWebSocketClient:

implementation(\"org.hildan.krossbow:krossbow-websocket-okhttp:7.0.0\")\n
"},{"location":"websocket/sockjs/","title":"Krossbow with SockJS","text":"

Krossbow allows you to use SockJS-compatible clients as transport for STOMP.

The krossbow-websocket-sockjs is a multiplatform facade implementing Krossbow's web socket interface by relying on different SockJS implementations. Here are the backing implementations for the different platforms:

Using a SockJS client requires a SockJS-enabled server.

"},{"location":"websocket/sockjs/#usage-with-stompclient","title":"Usage with StompClient","text":"

To use this client, just call SockJSClient() and the relevant platform-specific client will be instantiated for you:

val client = StompClient(SockJSClient())\n
"},{"location":"websocket/sockjs/#dependency-information","title":"Dependency information","text":"

You will need to declare the following Gradle dependency to use the SockJSClient:

implementation(\"org.hildan.krossbow:krossbow-websocket-sockjs:7.0.0\")\n
"},{"location":"websocket/spring/","title":"Krossbow with Spring","text":"

Krossbow allows you to use Spring's WebSocketClient as transport for STOMP.

The krossbow-websocket-spring module provides the .asKrossbowWebSocketClient() extension, which adapts any of Spring's WebSocketClient to Krossbow's web socket client interface.

For example, use StandardWebSocketClient().asKrossbowWebSocketClient() to wrap Spring's standard JSR-356 client.

"},{"location":"websocket/spring/#usage-with-stompclient","title":"Usage with StompClient","text":"

To use a Spring web socket client with Krossbow's StompClient, adapt it to a Krossbow WebSocketClient using .asKrossbowWebSocketClient() and pass it to the StompClient constructor:

val stompClient = StompClient(StandardWebSocketClient().asKrossbowWebSocketClient())\n

You can of course further customize your Spring client before adapting it to Krossbow:

// Pure Spring configuration\nval springWsClient = StandardWebSocketClient().apply {\n   taskExecutor = SimpleAsyncTaskExecutor(\"my-websocket-threads\")\n   userProperties = mapOf(\"my-prop\" to \"someValue\")\n}\n\n// Krossbow adapter\nval stompClient = StompClient(springWsClient.asKrossbowWebSocketClient())\n

Another example of custom client, using Spring's SockJS client:

// Pure Spring configuration\nval transports = listOf(\n    WebSocketTransport(StandardWebSocketClient()),\n    RestTemplateXhrTransport(myCustomRestTemplate),\n)\nval springSockJsWsClient = SockJsClient(transports)\n\n// Krossbow adapter\nval stompClient = StompClient(springSockJsWsClient.asKrossbowWebSocketClient())\n
"},{"location":"websocket/spring/#dependency-information","title":"Dependency information","text":"

You will need to declare the following Gradle dependency to use the Spring adapters:

implementation(\"org.hildan.krossbow:krossbow-websocket-spring:7.0.0\")\n

It transitively depends on spring-websocket, so you don't need to add it yourself.

Important: if you're using Spring's StandardWebSocketClient, you'll also need to add a dependency on a JSR-356 implementation, such as the Tyrus reference implementation:

implementation(\"org.glassfish.tyrus.bundles:tyrus-standalone-client-jdk:2.1.5\")\n
"}]} \ No newline at end of file +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Overview","text":"

Krossbow is a Kotlin multiplatform STOMP 1.2 client with a coroutine-based API.

It is built on a web socket client abstraction, and provides a bunch of adapters for popular web socket clients (OkHttp, Ktor, Spring, SockJS...). It also provides out-of-the-box a built-in web socket implementation (without third-party dependencies) for most platforms (see below).

Krossbow can also be used as a multiplatform web socket client without STOMP protocol.

"},{"location":"#features","title":"Features","text":"

All the STOMP 1.2 specification is implemented:

Additional features:

If you find a bug or a feature that's missing compared to the specification, please open an issue.

"},{"location":"#supported-targets","title":"Supported targets","text":"

Krossbow supports most Kotlin targets in its STOMP and web socket API modules: JVM, JS (browser and nodeJS), iOS, watchOS, tvOS, macOSX64, linuxX64, mingwX64.

However, each web socket client implementation has its own subset of supported targets (see below).

Android not tested on CI

Android 5.0+ (API level 21+) is supported by using JVM artifacts (e.g. OkHttp). However, the Android tooling's desugaring is currently not tested as part of the build, so any feedback on this use case is more than welcome. Please upvote the corresponding issue if you'd like to see proper CI or special packaging for the Android target.

"},{"location":"#web-socket-clients-target-support","title":"Web socket clients target support","text":"

Krossbow can use built-in web socket implementations without third-party dependencies on some platforms. It also provides adapters for third-party implementations which have different platform support. Here is a summary of the supported platforms by module:

Module Browser NodeJS JVM iOS / tvOS / watchOS macOS / Linux / Windows Transitive dependencies Built-in (JDK\u00a011+) None Ktor Ktor, and the relevant Ktor engine(s) OkHttp OkHttp SockJS sockjs-client (on JS), Spring websocket (on JVM) Spring Spring websocket

supported with actual web socket transport (RFC6455)

supported using SockJS protocol (requires a SockJS server)

"},{"location":"#contribute","title":"Contribute","text":"

Don't hesitate to open GitHub issues, even to ask questions or discuss a new feature. Pull-requests are welcome, but please open an issue first so that we can discuss the initial design or fix, which may avoid unnecessary work.

"},{"location":"artifacts/","title":"Artifacts summary","text":"

Krossbow offers a lot of possibilities so here is a summary of all available artifacts.

All these artifacts are published to Maven Central under the group ID org.hildan.krossbow and a common version.

"},{"location":"artifacts/#stomp-artifacts","title":"STOMP artifacts","text":"

You should pick only one of the krossbow-stomp-* artifacts, depending on whether you need automatic serialization of frame bodies:

Artifact Description
krossbow-stomp-core
The basic multiplatform STOMP client. It implements the STOMP 1.2 protocol on top of the web socket abstraction defined by the krossbow-websocket-core module.
krossbow-stomp-jackson
A superset of krossbow-stomp-core adding JSON conversion features using Jackson (JVM only)
krossbow-stomp-moshi
A superset of krossbow-stomp-core adding JSON conversion features using Moshi (JVM only)
krossbow-stomp-kxserialization
A superset of krossbow-stomp-core adding conversion features using Kotlinx Serialization library (multiplatform). You can leverage the multi-format capabilities of Kotlinx Serialization (JSON, protobuf, CBOR, ...).
krossbow-stomp-kxserialization-json
A superset of krossbow-stomp-kxserialization adding JSON helpers and the JSON format dependency.

Then add the dependency of your choice to your Gradle build. For instance, if you intend to use Krossbow with Kotlinx Serialization:

implementation(\"org.hildan.krossbow:krossbow-stomp-kxserialization:7.0.0\")\n

Don't need STOMP?

If you're just interested in the web socket client without STOMP protocol, don't declare a STOMP artifact, but instead choose one of the web socket artifacts below.

"},{"location":"artifacts/#web-socket-artifacts","title":"Web Socket artifacts","text":"

The STOMP artifacts depend on a web socket API that needs an implementation. Krossbow provides implementations for the built-in web socket API of most platforms, and also adapters for 3rd-party web socket implementations:

Artifact Description
krossbow-websocket-builtin
A multiplatform WebSocketClient implementation that adapts the built-in client for each supported platform without transitive dependency.
krossbow-websocket-ktor
A multiplatform WebSocketClient implementation based on Ktor 2.3.10's HttpClient.
krossbow-websocket-okhttp
A JVM implementation of the web socket API using OkHttp's client.
krossbow-websocket-sockjs
A multiplatform WebSocketClient implementation for use with SockJS servers. It uses Spring's SockJSClient on JVM, and npm sockjs-client for JavaScript (NodeJS and browser).
krossbow-websocket-spring
A JVM 8+ implementation of the web socket API using Spring's WebSocketClient. Provides both a normal WebSocket client and a SockJS one.

Peer dependencies

Some Krossbow modules are not opinionated and require some extra third-party peer dependencies. Make sure to read the usage section corresponding to the module of your choice for more details.

"},{"location":"license/","title":"MIT License","text":"

Copyright (c) 2019-2023 Joffrey Bion

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

"},{"location":"migration-guides/","title":"Migration guides","text":"

Here are some details about how to migrate from one major version to another.

"},{"location":"migration-guides/#from-6x-to-7x","title":"From 6.x to 7.x","text":""},{"location":"migration-guides/#web-socket-subprotocol-negotiation","title":"Web socket subprotocol negotiation","text":"

As part of issue #493, a separate protocols parameter was added to WebSocketClient.connect to enable web socket subprotocol negotiation.

Binary compatibility is preserved through some hidden synthetic functions. However, source compatibility isn't: usages of the connect() method that passed custom headers without a named headers parameter will no longer compile. Adding the headers = parameter name will solve the issue.

For full negotiation support, clients need to be aware of which subprotocol the server chose to speak. This is why the protocol property was added to WebSocketConnection (#498). Implementers of this interface must implement this property. There shouldn't be many 3rd party implementations of the connection interface, so binary compatibility should not be a real issue here.

"},{"location":"migration-guides/#stomp-web-socket-subprotocol-negotiation","title":"STOMP web socket subprotocol negotiation","text":"

Some servers like ActiveMQ require negotiating the STOMP protocol as a web socket subprotocol during the web socket handshake (see issue #492), and cannot work otherwise.

Breaking change: To make the experience smoother, Krossbow v7.0.0 now automatically sends STOMP subprotocols (in all supported versions) during the web socket handshake via the Sec-WebSocket-Protocol header.

If your server doesn't support it, you can customize the web socket handshake by manually connecting using WebSocketClient.connect() with the parameters that suit you best, and then connect at STOMP level using the WebSocketConnection.stomp() extension (without the need for a StompClient at all):

val client: WebSocketClient = TODO(\"get some web socket client implementation\")\nval wsConnection = client.connect(url) // without any subprotocols\nval stompConfig = StompConfig().apply { \n    // set your config here if needed\n}\nval stompSession = wsConnection.stomp(config)\n
"},{"location":"migration-guides/#no-host-header-sent-for-stomp-10","title":"No host header sent for STOMP 1.0","text":"

Thanks to the aforementioned changes, we can now detect the STOMP protocol version used by the server before sending the first STOMP frame (CONNECT or STOMP). If STOMP 1.0 is detected as web socket subprotocol during the web socket handshake, we no longer send by default the host header which was introduced in 1.1 (and effectively breaks some old servers, see #122). It can still be sent by manually specifying it of course.

"},{"location":"migration-guides/#stomp-protocol-version-negotiation","title":"STOMP protocol version negotiation","text":"

The STOMP protocol itself supports negotiation of the version via headers in the CONNECT (or STOMP) frame. So far, Krossbow only specified 1.2 as supported version. From now on, all 3 versions 1.0, 1.1, and 1.2 are advertised as supported by the client.

If necessary, this behavior can be overridden by sending the accept-version header manually in customStompConnectHeaders.

Because the protocol version can be negotiated both via web socket subprotocol and at STOMP level, there could potentially be a mismatch. If this happens, the connect() call throws an exception. This can be disabled with StompConfig.failOnStompVersionMismatch.

"},{"location":"migration-guides/#stompclientconnect-now-throws-a-different-websocketconnectionexception","title":"StompClient.connect() now throws a different WebSocketConnectionException","text":"

The org.hildan.krossbow.stomp.WebSocketConnectionException is deprecated in favor of org.hildan.krossbow.websocket.WebSocketConnectionException. That exception has been around for a few years now and encapsulates all connection failures from different client implementations already, so there is no need for a similar exception at the StompClient level.

If you used to catch this exception, make sure to update your import (the error-level deprecation should mitigate any risk of missing the new exception).

"},{"location":"migration-guides/#from-5x-to-6x","title":"From 5.x to 6.x","text":""},{"location":"migration-guides/#switch-to-kotlinx-io-and-the-bytestring-type","title":"Switch to kotlinx-io and the ByteString type","text":"

The kotlinx-io library has been revamped, with an implementation that closely matches Okio now. Krossbow has now internally switched from Okio to kotlinx-io as a result, but this part should have no visible effect for the consumers of the library.

However, since kotlinx-io is a somewhat \"standard\" extension library for Kotlin, Krossbow can now more legitimately use its ByteString type in public APIs that handled binary data (for both web socket and STOMP sessions). This type represents immutable sequences of bytes, which is more convenient API-wise than byte arrays.

You will have to switch to these types if you used binary-based APIs.

"},{"location":"migration-guides/#deprecations-cleanup","title":"Deprecations cleanup","text":"

This is a major version, and therefore we allowed ourselves some cleanup by removing a bunch of deprecated APIs. If you see unresolved references and are not sure how to fix them, please switch back to the Krossbow version 5 and fix deprecation warnings by following the corresponding instructions.

Please check the changelog for the list of removals.

"},{"location":"migration-guides/#from-4x-to-5x","title":"From 4.x to 5.x","text":""},{"location":"migration-guides/#end-of-ktor-1x-support","title":"End of Ktor 1.x support","text":"

krossbow-websocket-ktor-legacy artifact was removed. This means Krossbow no longer works with Ktor 1.x. If you were using this module, please migrate to Ktor 2, and use the non-legacy krossbow-websocket-ktor module.

"},{"location":"migration-guides/#kotlin-18","title":"Kotlin 1.8","text":"

This update of Krossbow brings Kotlin 1.8, which might bring some incompatible changes to the Kotlin stdlib. Please check the compatibility guide if you were using an older version of Kotlin.

"},{"location":"migration-guides/#from-3x-to-4x","title":"From 3.x to 4.x","text":""},{"location":"migration-guides/#withjsonconversions-moved-to-its-own-module","title":"withJsonConversions moved to its own module","text":"

If you were using Krossbow with krossbow-stomp-kxserialization, the withJsonConversions helper has moved to a new module called krossbow-stomp-kxserialization-json. This new module now transitively brings kotlinx-serialization-json so you don't need to depend on that one explicitly.

"},{"location":"migration-guides/#built-in-web-socket-clients-moved-to-their-own-module-and-default-stompclient-constructor-removed","title":"Built-in web socket clients moved to their own module and default StompClient constructor removed","text":"

Up to (and including) version 3.x of Krossbow, the built-in web socket clients for the supported platforms were part of the krossbow-websocket-core module. This module provided a WebSocketClient.Companion.default() factory function to provide the built-in web socket implementation of the current platform. Likewise, the krossbow-stomp-core module provided a StompClient constructor that used the \"default\" built-in web socket implementation for the current platform.

This approach limited the targets supported by those 2 core modules, even though all of their functionality was target-agnostic. In order to support all Kotlin platforms in pure Kotlin modules, the built-in websocket implementations had to be moved to a separate module, and the constructor without web socket client was moved to a separate module (and later removed completely for simplicity).

Breaking dependency changes, in short:

Note: the WebSocketClient.default() function was since renamed WebSocketClient.builtIn() in newer versions.

If you used other web socket implementations than the built-in ones, you don't have to change anything to your dependencies.

"},{"location":"migration-guides/#from-2x-to-3x","title":"From 2.x to 3.x","text":""},{"location":"migration-guides/#use-durations-instead-of-millis","title":"Use Durations instead of millis","text":"

StompConfiguration no longer uses amounts of milliseconds, but uses the kotlin.time.Duration API. The -Millis suffixes for the relevant properties were therefore dropped and the types changed.

Before:

val stomp = StompClient {\n    connectionTimeoutMillis = 2000\n    receiptTimeoutMillis = 5000\n    disconnectTimeoutMillis = 300\n}\n

After:

import kotlin.time.Duration.Companion.milliseconds\nimport kotlin.time.Duration.Companion.seconds\n\nval stomp = StompClient {\n    connectionTimeout = 2.seconds\n    receiptTimeout = 5.seconds\n    disconnectTimeout = 300.milliseconds\n}\n

"},{"location":"migration-guides/#flow-instead-of-channel-in-websocketconnection","title":"Flow instead of Channel in WebSocketConnection","text":"

If you used the websocket API directly, the incomingFrames channel is now a Flow.

Before:

val conn = wsClient.connect(url)\nfor (frame in conn.incomingFrames) {\n    // do stuff\n}\n

After:

val conn = wsClient.connect(url)\nconn.incomingFrames.collect {\n    // do stuff\n}\n
"},{"location":"migration-guides/#tyrus-no-longer-embedded-in-krossbow-websocket-spring","title":"Tyrus no longer embedded in krossbow-websocket-spring","text":"

krossbow-websocket-spring no longer transitively brings a dependency on Tyrus.

If you didn't add any JSR-356 implementation manually, you now have to explicitly depend on one. If you want the same behaviour as before, add the Tyrus dependency to your build.gradle.kts as follows:

dependencies {\n    implementation(\"org.glassfish.tyrus.bundles:tyrus-standalone-client-jdk:2.1.5\")\n}\n
"},{"location":"migration-guides/#from-1x-to-2x","title":"From 1.x to 2.x","text":""},{"location":"migration-guides/#stompsessionuse-now-passes-the-session-as-it-not-this","title":"StompSession.use now passes the session as it, not this","text":"

In order to align with Closeable.use, the lambda for StompSession.use now receives the session as an argument (it) and not receiver (this).

Before:

StompClient().connect(url).use {\n    sendText(\"/dest\", \"message\")\n}\n

After:

StompClient().connect(url).use {\n    it.sendText(\"/dest\", \"message\")\n}\n// or\nStompClient().connect(url).use { session ->\n    session.sendText(\"/dest\", \"message\")\n}\n
"},{"location":"stomp/advanced-features/","title":"Advanced features","text":""},{"location":"stomp/advanced-features/#receipts-suspension","title":"Receipts & Suspension","text":"

The STOMP protocol supports RECEIPT frames, allowing the client to know when the server has received a frame. This only happens if a receipt header is set on the client frame.

If auto-receipt is enabled, a receipt header is automatically generated and added to all client frames supporting the mechanism, and for which a receipt header is not already present. If auto-receipt is not enabled, a receipt header may still be provided manually in the parameters of some overloads.

When a receipt header is present (automatically added or manually provided), the method that is used to send the frame suspends until the corresponding RECEIPT frame is received from the server. If no RECEIPT frame is received from the server in the configured time limit, a LostReceiptException is thrown.

If no receipt is provided and auto-receipt is disabled, the method used to send the frame doesn't wait for a RECEIPT frame and never throws LostReceiptException. Instead, it returns immediately after the underlying web socket implementation is done sending the frame.

"},{"location":"stomp/advanced-features/#heart-beats","title":"Heart beats","text":"

When configured, heart beats can be used as a keep-alive to detect if the connection is lost. The heartBeat property should be used to configure heart beats in the StompClient.

Note that the heart beats for the STOMP session are negotiated with the server. The actual heart beats are defined by the CONNECTED frame received from the server as a result of the negotiation, and may differ from the StompClient configuration. The negotiation behaviour is defined by the specification.

Sending and checking heart beats is automatically handled by StompSession implementations, depending on the result of the negotiation with the server. If expected heart beats are not received in time, a MissingHeartBeatException is thrown and fails active subscriptions.

"},{"location":"stomp/advanced-features/#graceful-disconnect","title":"Graceful disconnect","text":"

The graceful disconnect (or graceful shutdown) is a disconnection procedure defined by the STOMP specification to make sure the server gets all the frames before dropping the connection.

If enabled in the config, when disconnecting from the server, the client first sends a DISCONNECT frame with a receipt header, and then waits for a RECEIPT frame before closing the connection.

If this graceful disconnect is disabled, then calling StompSession.disconnect() immediately closes the web socket connection. In this case, there is no guarantee that the server received all previous messages.

"},{"location":"stomp/advanced-features/#using-custom-headers-in-the-web-socket-handshake","title":"Using custom headers in the web socket handshake","text":"

Not supported in browsers

The browser's WebSocket API does not support custom headers in the handshake (see this open issue in the web socket standard repo). Because of this, Krossbow cannot support this feature for the JS browser platform. However, the JS web socket client adapter is designed in a way that allows other implementations to support it, such as a Node.js implementation.

Some servers or connection flows may require extra HTTP headers in the web socket handshake. The StompClient.connect() function doesn't support such headers out of the box, but this function is essentially just a shorthand for connecting at the web socket level, and then connecting at the STOMP level.

In fact, we technically don't need to create and use a StompClient at all in order to use STOMP with Krossbow. Krossbow provides a WebSocketConnection.stomp() extension function that establishes a STOMP connection from an existing web socket connection.

We can leverage this to customize the web socket connection at will before connecting at STOMP level. For example:

val webSocketClient = WebSocketClient.builtIn() // or another web socket client\n\n// connect at web socket level with custom headers\nval wsSession = webSocketClient.connect(url, headers = mapOf(\"Custom-Header\" to \"custom-value\"))\n\nval config = StompConfig().apply {\n    // here you can set up whatever config you would have done in the StompClient { ... } block\n}\n// connect at STOMP level on this open web socket, using the above config\nval stompSession = wsSession.stomp(config)\n
"},{"location":"stomp/config/","title":"Configuration","text":""},{"location":"stomp/config/#configuring-the-stompclient","title":"Configuring the StompClient","text":"

The StompClient can be configured at construction time using a convenient lambda block:

val stompClient = StompClient(WebSocketClient.builtIn()) {\n    connectionTimeout = 3.seconds\n    gracefulDisconnect = false\n}\n

You can also create the configuration separately and then pass it when constructing the client:

val stompConfig = StompConfig().apply {\n    connectionTimeout = 3.seconds\n    gracefulDisconnect = false\n}\n\nval stompClient = StompClient(WebSocketClient.builtIn(), stompConfig)\n
"},{"location":"stomp/config/#configuration-options","title":"Configuration options","text":"

You can find out about all configuration properties in the StompConfig KDoc.

"},{"location":"stomp/getting-started/","title":"Getting started","text":""},{"location":"stomp/getting-started/#choosing-a-web-socket-implementation","title":"Choosing a web socket implementation","text":"

Krossbow uses web sockets as transport for the STOMP communication. Multiple web socket client implementations are supported.

Check out the web socket client table to help you choose a web socket implementation based on the platforms you need to support. You can find more information about each client in their respective section of this doc.

We recommend the built-in client adapters if they cover the Kotlin targets you need to support, in order to limit 3rd party dependencies. Otherwise, Ktor is a good choice if you don't have special needs like SockJS.

"},{"location":"stomp/getting-started/#dependencies-setup","title":"Dependencies setup","text":"

For the basic usage of STOMP without serialization, add the krossbow-stomp-core dependency as well as the web socket module of your choice.

For example to use STOMP with the built-in web socket client:

implementation(\"org.hildan.krossbow:krossbow-stomp-core:7.0.0\")\nimplementation(\"org.hildan.krossbow:krossbow-websocket-builtin:7.0.0\")\n

For other web socket clients, check out their dedicated documentation page to find out which Krossbow dependencies are needed.

The rest of this guide uses the built-in client.

"},{"location":"stomp/getting-started/#basic-usage-without-body-conversions","title":"Basic usage (without body conversions)","text":"

This is how to create a STOMP client and interact with it:

val client = StompClient(WebSocketClient.builtIn()) // other config can be passed in here\nval session: StompSession = client.connect(url) // optional login/passcode can be provided here\n\n// Send text messages using this convenience function\nsession.sendText(destination = \"/some/destination\", body = \"Basic text message\")\n\n// Sometimes no message body is necessary\nsession.sendEmptyMsg(destination = \"/some/destination\") \n\n// This subscribe() call triggers a SUBSCRIBE frame\n// and returns the flow of messages for the subscription\nval subscription: Flow<String> = session.subscribeText(\"/some/topic/destination\")\n\n// Use an appropriate coroutine 'scope' to collect the received frames\nval collectorJob = scope.launch {\n    subscription.collect { msg ->\n        println(\"Received: $msg\")\n    }\n}\ndelay(3000)\n// cancelling the flow collector triggers an UNSUBSCRIBE frame\ncollectorJob.cancel()\n\nsession.disconnect()\n

If you want to disconnect automatically in case of exception or normal termination, you can use a try/finally block, or use StompSession.use(), which is similar to Closeable.use():

import kotlinx.coroutines.flow.*\nimport org.hildan.krossbow.stomp.*\nimport org.hildan.krossbow.websocket.*\nimport org.hildan.krossbow.websocket.builtin.*\n\nval client = StompClient(WebSocketClient.builtIn()) // other config can be passed in here\nval session: StompSession = client.connect(url) // optional login/passcode can be provided here\n\nsession.use { s ->\n    s.sendText(\"/some/destination\", \"Basic text message\") \n\n    val subscription: Flow<String> = s.subscribeText(\"/some/topic/destination\")\n\n    // terminal operators that finish early (like first) also trigger UNSUBSCRIBE automatically\n    val firstMessage: String = subscription.first()\n    println(\"Received: $firstMessage\")\n}\n// DISCONNECT frame was automatically sent at the end of the use{...} block\n
"},{"location":"stomp/getting-started/#using-body-conversions","title":"Using body conversions","text":"

You can use STOMP with basic text as frame bodies, but it really becomes interesting when you can convert the frame bodies back and forth into Kotlin objects.

Check out the following sections to see how to automatically convert your objects into STOMP frame bodies:

"},{"location":"stomp/conversions/custom/","title":"Custom conversions","text":"

If you want to use your own text conversion, you can implement TextMessageConverter without any additional module, and use withTextConversions to wrap your StompSession into a TypedStompSession.

Limited JS support

Reflection-based conversions may behave poorly on the JS platform. It is usually safer to rely on Kotlinx Serialization for multiplatform conversions.

val myConverter = object : TextMessageConverter {\n    override val mimeType: String = \"application/json;charset=utf-8\"\n\n    override fun <T> convertToString(value: T, type: KTypeRef<T>): String {\n        TODO(\"your own object -> text conversion\")\n    }\n\n    override fun <T> convertFromString(text: String, type: KTypeRef<T>): T {\n        TODO(\"your own text -> object conversion\")\n    }\n}\n\nStompClient(WebSocketClient.builtIn()).connect(url).withTextConversions(myConverter).use { session ->\n    session.convertAndSend(\"/some/destination\", MyPojo(\"Custom\", 42)) \n\n    val messages = session.subscribe<MyMessage>(\"/some/topic/destination\")\n    val firstMessage: MyMessage = messages.first()\n\n    println(\"Received: $firstMessage\")\n}\n
"},{"location":"stomp/conversions/jackson/","title":"STOMP with Jackson","text":"

The krossbow-stomp-jackson module is a JVM-only extension of krossbow-stomp-core that provides new APIs to send and receive properly typed classes, and automatically convert them to/from the JSON bodies of STOMP frames by leveraging Jackson and jackson-module-kotlin.

The main addition is the extension function StompSession.withJackson(), which turns your StompSession into a TypedStompSession. This new session type has additional methods that use Jackson to convert your objects into JSON and back:

StompClient(WebSocketClient.builtIn()).connect(url).withJackson().use { session ->\n    session.convertAndSend(\"/some/destination\", Person(\"Bob\", 42)) \n\n    val messages: Flow<MyMessage> = session.subscribe<MyMessage>(\"/some/topic/destination\")\n    val firstMessage: MyMessage = messages.first()\n\n    println(\"Received: $firstMessage\")\n}\n
"},{"location":"stomp/conversions/jackson/#using-a-custom-objectmapper","title":"Using a custom ObjectMapper","text":"

Jackson is highly configurable, and it's often useful to configure the ObjectMapper manually.

The withJackson() method takes an optional ObjectMapper parameter, so you can configure it as you please:

val customObjectMapper: ObjectMapper = jacksonObjectMapper()\n        .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)\n        .disable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES)\n        .enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)\n\nval client = StompClient(WebSocketClient.builtIn()).connect(url)\nval session = client.withJackson(customObjectMapper)\n
"},{"location":"stomp/conversions/jackson/#dependency","title":"Dependency","text":"

To use Jackson conversions, add krossbow-stomp-jackson to your Gradle dependencies (krossbow-stomp-core is unnecessary because it's transitively brought by this one):

implementation(\"org.hildan.krossbow:krossbow-stomp-jackson:7.0.0\")\n

This dependency transitively brings Jackson 2.17.0 with the Kotlin module.

"},{"location":"stomp/conversions/kx-serialization/","title":"STOMP with Kotlinx Serialization","text":"

Kotlinx Serialization is a multiplatform and multi-format serialization library provided by the Kotlin team. It is a popular choice for Kotlin multiplatform libraries especially because of its extensive support of Kotlin features.

The krossbow-stomp-kxserialization module is an extension of krossbow-stomp-core that provides new APIs to send and receive properly typed classes, and automatically convert STOMP frame bodies by leveraging Kotlinx Serialization.

"},{"location":"stomp/conversions/kx-serialization/#usage-with-the-json-format","title":"Usage with the JSON format","text":"

Since Kotlinx Serialization supports multiple formats with different dependencies, you normally have to add your own dependency to bring the format of your choice.

However, since JSON is so popular, Krossbow comes with the krossbow-stomp-kxserialization-json module, which adds dedicated helpers for JSON and the necessary transitive dependency on the JSON format (see dependencies section below).

This module brings the withJsonConversions helper to convert your StompSession into a StompSessionWithKxSerialization:

val session = StompClient(WebSocketClient.builtIn()).connect(url)\nval jsonStompSession = session.withJsonConversions()\n

This new session type has the additional convertAndSend method and subscribe overloads that use Kotlinx Serialization's serializers to convert your payloads using the format of your choice (in this case, JSON):

@Serializable\ndata class Person(val name: String, val age: Int)\n@Serializable\ndata class MyMessage(val timestamp: Long, val author: String, val content: String)\n\njsonStompSession.use { s ->\n    s.convertAndSend(\"/some/destination\", Person(\"Bob\", 42), Person.serializer()) \n\n    // overloads without explicit serializers exist, but should be avoided if you also target JavaScript\n    val messages: Flow<MyMessage> = s.subscribe(\"/some/topic/destination\", MyMessage.serializer())\n\n    messages.collect { msg ->\n        println(\"Received message from ${msg.author}: ${msg.content}\")\n    }\n}\n
"},{"location":"stomp/conversions/kx-serialization/#custom-json-instance","title":"Custom Json instance","text":"

The withJsonConversions() method takes an optional Json parameter, so you can configure it as you please:

// custom Json configuration\nval json = Json {\n    encodeDefaults = true\n    ignoreUnknownKeys = true\n}\n\nval session = StompClient(WebSocketClient.builtIn()).connect(url)\nval jsonStompSession = session.withJsonConversions(json)\n
"},{"location":"stomp/conversions/kx-serialization/#usage-with-other-formats","title":"Usage with other formats","text":"

For other formats than JSON, use the more general krossbow-stomp-kxserialization module, and add a dependency on the Kotlinx Serialization format of your choice (see dependencies section below).

This module brings the following extension functions on StompSession:

These helpers are equivalent to withJsonConversions, but more general, and also turn your StompSession into a StompSessionWithKxSerialization. You should provide the media type that you want to set as content-type header in the messages you send:

val session = StompClient(WebSocketClient.builtIn()).connect(url)\nval jsonStompSession = session.withBinaryConversions(Protobuf.Default, \"application/x-protobuf\")\n

You can then use convertAndSend and subscribe the same way as in the JSON section above.

"},{"location":"stomp/conversions/kx-serialization/#dependency","title":"Dependency","text":""},{"location":"stomp/conversions/kx-serialization/#general-case","title":"General case","text":"

Krossbow's base Kotlinx Serialization module is format-agnostic, so you need to add both the krossbow-stomp-kxserialization dependency and the Kotlinx Serialization dependency for the format you want to use. For instance in the case of protobuf, that would be kotlinx-serialization-protobuf:

implementation(\"org.hildan.krossbow:krossbow-stomp-kxserialization:7.0.0\")\nimplementation(\"org.jetbrains.kotlinx:kotlinx-serialization-protobuf:1.6.3\")\n
"},{"location":"stomp/conversions/kx-serialization/#json-format","title":"JSON format","text":"

Since JSON is so common, Krossbow provides an all-in-one module with additional helpers for JSON:

implementation(\"org.hildan.krossbow:krossbow-stomp-kxserialization-json:7.0.0\")\n

This module brings kotlinx-serialization-json transitively, so you don't have to add it yourself.

"},{"location":"stomp/conversions/kx-serialization/#additional-notes","title":"Additional notes","text":"

With this setup, krossbow-stomp-core is unnecessary because it's transitively brought by the krossbow-stomp-kxserialization modules.

Note that Kotlinx Serialization also requires a compiler plugin to generate serializers for your @Serializable classes. See the Kotlinx Serialization doc for more information about this.

"},{"location":"stomp/conversions/moshi/","title":"STOMP with Moshi","text":"

The krossbow-stomp-moshi module is a JVM-only extension of krossbow-stomp-core that provides new APIs to send and receive properly typed classes, and automatically convert them to/from the JSON bodies of STOMP frames by leveraging Moshi.

The main addition is the extension function StompSession.withMoshi(), which turns your StompSession into a TypedStompSession. This new session type has additional methods that use Moshi to convert your objects into JSON and back:

// example Moshi instance that converts Kotlin types using reflection\nval moshi = Moshi.Builder()\n    .addLast(KotlinJsonAdapterFactory())\n    .build()\nStompClient(WebSocketClient.builtIn()).connect(url).withMoshi(moshi).use { session ->\n    session.convertAndSend(\"/some/destination\", Person(\"Bob\", 42)) \n\n    val messages: Flow<MyMessage> = session.subscribe<MyMessage>(\"/some/topic/destination\")\n    val firstMessage: MyMessage = messages.first()\n\n    println(\"Received: $firstMessage\")\n}\n
"},{"location":"stomp/conversions/moshi/#dependency","title":"Dependency","text":"

To use Moshi conversions, add krossbow-stomp-moshi to your Gradle dependencies (krossbow-stomp-core is unnecessary because it's transitively brought by this one):

implementation(\"org.hildan.krossbow:krossbow-stomp-moshi:7.0.0\")\n

This dependency transitively brings Moshi 1.15.1.

"},{"location":"websocket/builtin/","title":"Krossbow Web Socket Built-in","text":"

The krossbow-websocket-builtin module defines implementations of the WebSocketClient interface by adapting clients that already exist in each platform (without 3rd-party dependencies).

For convenience in common source sets, this module also provides the builtIn() factory method, which returns the built-in implementation of the current platform.

"},{"location":"websocket/builtin/#jvm","title":"JVM","text":"

On the JVM target, the Jdk11WebSocketClient adapts the built-in HttpClient provided in the JRE since Java 11, and its WebSocket interface.

Android not supported

This adapter requires Java 11's HttpClient which is not available on Android. If you need to support Android, please use the OkHttp or Ktor adapter instead.

"},{"location":"websocket/builtin/#javascript","title":"JavaScript","text":"

On the JS target, the BrowserWebSocketClient adapts the browser's built-in WebSocket directly.

Also, the JsWebSocketClientAdapter allows to adapt anything that looks like the browser's WebSocket.

"},{"location":"websocket/builtin/#darwin-macos-ios-tvos-watchos","title":"Darwin (macOS, iOS, tvOS, watchOS)","text":"

On all Darwin targets, the DarwinWebSocketClient adapts the Foundation framework's NSURLSessionWebSocketTask.

"},{"location":"websocket/builtin/#dependency-information","title":"Dependency information","text":"

To use the built-in web socket clients, add the following to your build.gradle:

implementation(\"org.hildan.krossbow:krossbow-websocket-builtin:7.0.0\")\n
"},{"location":"websocket/custom/","title":"Implement Krossbow's WebSocketClient yourself","text":"

The krossbow-websocket-core module defines a standard web socket API abstraction that is used by the STOMP artifacts and that you can also use directly if you're only interested in the web socket protocol without STOMP.

Krossbow provides built-in implementations of the web socket interfaces in the krossbow-websocket-builtin module, but you can of course implement your own.

"},{"location":"websocket/custom/#basics","title":"Basics","text":"

You can create your own implementation of Krossbow's web socket client by implementing the WebSocketClient interface.

This interface simply has a connect() method returning an instance of WebSocketConnection. The WebSocketConnection actually contains the bulk of the web socket interactions implementation.

Please follow the KDoc of these interfaces to learn more about the contract that needs to be satisfied for each method.

"},{"location":"websocket/custom/#helpers","title":"Helpers","text":"

The krossbow-websocket-core module doesn't only provide interfaces to implement. It also provides some helper classes that help with most implementations of those interfaces.

The WebSocketListenerFlowAdapter allows to adapt listener-based web socket APIs to Krossbow's Flow API easily. It takes care of partial message handling automatically, and can provide backpressure on the callback caller thanks to its suspend callbacks.

The UnboundedWsListenerFlowAdapter also adapts listener-based APIs to Krossbow's flow, but without any backpressure support (functions are not suspend and return immediately). It adds new messages to an unbounded queue. This is necessary with some APIs like JS browsers WebSocket API, which cannot apply backpressure in any way on their web socket traffic.

"},{"location":"websocket/custom/#dependency-information","title":"Dependency information","text":"

Add the following to your build.gradle(.kts) in order to get the Krossbow's interfaces and helpers:

implementation(\"org.hildan.krossbow:krossbow-websocket-core:7.0.0\")\n
"},{"location":"websocket/ktor/","title":"Krossbow with Ktor","text":"

Krossbow allows you to use Ktor's web socket as transport for STOMP.

Ktor's implementation supports a variety of platforms and is very popular in the Kotlin world, especially in Kotlin multiplatform.

The krossbow-websocket-ktor module provides the KtorWebSocketClient, which adapts Ktor 2.3.10's HttpClient to Krossbow's web socket interface.

"},{"location":"websocket/ktor/#usage-with-stompclient","title":"Usage with StompClient","text":"

To use the KtorWebSocketClient pass an instance of it when creating your StompClient:

val client = StompClient(KtorWebSocketClient())\n

You can customize the actual Ktor HTTP client used behind the scenes by passing it to KtorWebSocketClient:

// You may configure Ktor HTTP client as you please,\n// but make sure at least the websocket feature is installed\nval httpClient = HttpClient {\n    install(WebSockets)\n}\nval wsClient = KtorWebSocketClient(httpClient)\nval stompClient = StompClient(wsClient)\n
"},{"location":"websocket/ktor/#dependency-information","title":"Dependency information","text":"

You will need to declare the following Gradle dependency to use the KtorWebSocketClient:

implementation(\"org.hildan.krossbow:krossbow-websocket-ktor:7.0.0\")\n

Ktor uses pluggable engines to perform the platform-specific network operations (just like Krossbow uses different web socket implementations). You need to pick an engine that supports web sockets in order to use Ktor's HttpClient with web sockets. Follow Ktor's documentation to find out more about how to use engines.

For instance, if you want to use Ktor's CIO engine with Krossbow, you need to declare the following:

implementation(\"org.hildan.krossbow:krossbow-websocket-ktor:7.0.0\")\nimplementation(\"io.ktor:ktor-client-cio:2.3.10\")\n
"},{"location":"websocket/okhttp/","title":"Krossbow with OkHttp","text":"

Krossbow allows you to use OkHttp's WebSocket as transport for STOMP.

OkHttp is very popular on Android, and is already part of many projects as HTTP client of choice.

The krossbow-websocket-okhttp module provides the OkHttpWebSocketClient, which adapts OkHttp's WebSocket to Krossbow's web socket interface.

"},{"location":"websocket/okhttp/#usage-with-stompclient","title":"Usage with StompClient","text":"

To use the OkHttpWebSocketClient pass an instance of it when creating your StompClient:

val client = StompClient(OkHttpWebSocketClient())\n

You can customize the actual OkHttpClient used behind the scenes by passing it to OkHttpWebSocketClient():

// This allows to configure the underlying OkHttpClient as you please\n// (or use an existing one from your project)\nval okHttpClient = OkHttpClient.Builder()\n    .callTimeout(Duration.ofMinutes(1))\n    .pingInterval(Duration.ofSeconds(10))\n    .build()\nval wsClient = OkHttpWebSocketClient(okHttpClient)\nval stompClient = StompClient(wsClient)\n
"},{"location":"websocket/okhttp/#dependency-information","title":"Dependency information","text":"

You will need to declare the following Gradle dependency to use the OkHttpWebSocketClient:

implementation(\"org.hildan.krossbow:krossbow-websocket-okhttp:7.0.0\")\n
"},{"location":"websocket/sockjs/","title":"Krossbow with SockJS","text":"

Krossbow allows you to use SockJS-compatible clients as transport for STOMP.

The krossbow-websocket-sockjs is a multiplatform facade implementing Krossbow's web socket interface by relying on different SockJS implementations. Here are the backing implementations for the different platforms:

Using a SockJS client requires a SockJS-enabled server.

"},{"location":"websocket/sockjs/#usage-with-stompclient","title":"Usage with StompClient","text":"

To use this client, just call SockJSClient() and the relevant platform-specific client will be instantiated for you:

val client = StompClient(SockJSClient())\n
"},{"location":"websocket/sockjs/#dependency-information","title":"Dependency information","text":"

You will need to declare the following Gradle dependency to use the SockJSClient:

implementation(\"org.hildan.krossbow:krossbow-websocket-sockjs:7.0.0\")\n
"},{"location":"websocket/spring/","title":"Krossbow with Spring","text":"

Krossbow allows you to use Spring's WebSocketClient as transport for STOMP.

The krossbow-websocket-spring module provides the .asKrossbowWebSocketClient() extension, which adapts any of Spring's WebSocketClient to Krossbow's web socket client interface.

For example, use StandardWebSocketClient().asKrossbowWebSocketClient() to wrap Spring's standard JSR-356 client.

"},{"location":"websocket/spring/#usage-with-stompclient","title":"Usage with StompClient","text":"

To use a Spring web socket client with Krossbow's StompClient, adapt it to a Krossbow WebSocketClient using .asKrossbowWebSocketClient() and pass it to the StompClient constructor:

val stompClient = StompClient(StandardWebSocketClient().asKrossbowWebSocketClient())\n

You can of course further customize your Spring client before adapting it to Krossbow:

// Pure Spring configuration\nval springWsClient = StandardWebSocketClient().apply {\n   taskExecutor = SimpleAsyncTaskExecutor(\"my-websocket-threads\")\n   userProperties = mapOf(\"my-prop\" to \"someValue\")\n}\n\n// Krossbow adapter\nval stompClient = StompClient(springWsClient.asKrossbowWebSocketClient())\n

Another example of custom client, using Spring's SockJS client:

// Pure Spring configuration\nval transports = listOf(\n    WebSocketTransport(StandardWebSocketClient()),\n    RestTemplateXhrTransport(myCustomRestTemplate),\n)\nval springSockJsWsClient = SockJsClient(transports)\n\n// Krossbow adapter\nval stompClient = StompClient(springSockJsWsClient.asKrossbowWebSocketClient())\n
"},{"location":"websocket/spring/#dependency-information","title":"Dependency information","text":"

You will need to declare the following Gradle dependency to use the Spring adapters:

implementation(\"org.hildan.krossbow:krossbow-websocket-spring:7.0.0\")\n

It transitively depends on spring-websocket, so you don't need to add it yourself.

Important: if you're using Spring's StandardWebSocketClient, you'll also need to add a dependency on a JSR-356 implementation, such as the Tyrus reference implementation:

implementation(\"org.glassfish.tyrus.bundles:tyrus-standalone-client-jdk:2.1.5\")\n
"}]} \ No newline at end of file diff --git a/websocket/ktor/index.html b/websocket/ktor/index.html index c7d970d84..206830cef 100644 --- a/websocket/ktor/index.html +++ b/websocket/ktor/index.html @@ -899,7 +899,7 @@

Krossbow with Ktor

Krossbow allows you to use Ktor's web socket as transport for STOMP.

Ktor's implementation supports a variety of platforms and is very popular in the Kotlin world, especially in Kotlin multiplatform.

-

The krossbow-websocket-ktor module provides the KtorWebSocketClient, which adapts Ktor 2.3.9's +

The krossbow-websocket-ktor module provides the KtorWebSocketClient, which adapts Ktor 2.3.10's HttpClient to Krossbow's web socket interface.

Usage with StompClient

To use the KtorWebSocketClient pass an instance of it when creating your StompClient:

@@ -924,7 +924,7 @@

Dependency information

For instance, if you want to use Ktor's CIO engine with Krossbow, you need to declare the following:

implementation("org.hildan.krossbow:krossbow-websocket-ktor:7.0.0")
-implementation("io.ktor:ktor-client-cio:2.3.9")
+implementation("io.ktor:ktor-client-cio:2.3.10")