Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ kotlin {
```kotlin
val opt = IO.Options()
// opt.trustAllCerts = true
// opt.httpClient = yourSharedHttpClient
IO.socket("http://localhost:3000", opt) { socket ->
socket.on(Socket.EVENT_CONNECT) { args ->
println("on connect ${args.joinToString()}")
Expand All @@ -53,6 +54,10 @@ IO.socket("http://localhost:3000", opt) { socket ->
}
```

If you set `opt.httpClient`, kmp-socketio will reuse this externally managed Ktor `HttpClient`
for both polling and websocket transports.
When websocket transport is enabled, make sure your shared client installs the Ktor `WebSockets` plugin.

Most of the APIs are the same as socket.io-client-java, here are some differences:

- Create socket is asynchronous, to make it's easier to guarantee thread safety.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ class EngineSocket(
opts.timestampParam = options?.timestampParam ?: opt.timestampParam
opts.extraHeaders = opt.extraHeaders
opts.trustAllCerts = opt.trustAllCerts
opts.httpClient = options?.httpClient ?: opt.httpClient

val transport = factory.create(name, opts, scope, rawMessage)
emit(EVENT_TRANSPORT, transport)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.piasy.kmp.socketio.engineio
import com.piasy.kmp.socketio.emitter.Emitter
import com.piasy.kmp.xlog.Logging
import com.piasy.kmp.socketio.parseqs.ParseQS
import io.ktor.client.HttpClient
import io.ktor.util.date.*
import kotlinx.coroutines.CoroutineScope
import org.hildan.socketio.EngineIOPacket
Expand Down Expand Up @@ -42,6 +43,12 @@ abstract class Transport(

@JvmField
var trustAllCerts: Boolean = false

/**
* Optional externally managed ktor HttpClient to reuse.
*/
@JvmField
var httpClient: HttpClient? = null
}

protected var state = State.INIT
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ open class PollingXHR(
opt: Options,
scope: CoroutineScope,
private val ioScope: CoroutineScope = CoroutineScope(Dispatchers.Default),
private val factory: HttpClientFactory = DefaultHttpClientFactory(trustAllCerts = opt.trustAllCerts),
private val factory: HttpClientFactory = DefaultHttpClientFactory(
externalHttpClient = opt.httpClient,
trustAllCerts = opt.trustAllCerts,
),
rawMessage: Boolean,
) : Transport(opt, scope, NAME, rawMessage) {
private var polling = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ open class WebSocket(
opt: Options,
scope: CoroutineScope,
private val ioScope: CoroutineScope = CoroutineScope(Dispatchers.Default),
private val factory: HttpClientFactory = DefaultHttpClientFactory(trustAllCerts = opt.trustAllCerts),
private val factory: HttpClientFactory = DefaultHttpClientFactory(
externalHttpClient = opt.httpClient,
trustAllCerts = opt.trustAllCerts,
),
rawMessage: Boolean,
) : Transport(opt, scope, NAME, rawMessage) {
private var ws: WebSocketSession? = null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,21 +60,24 @@ interface HttpClientFactory {
}

class DefaultHttpClientFactory(
externalHttpClient: HttpClient? = null,
trustAllCerts: Boolean = false,
): HttpClientFactory {
private val wsClient = httpClient(
trustAllCerts = trustAllCerts,
) {
install(Logging) {
logger = object : Logger {
override fun log(message: String) {
com.piasy.kmp.xlog.Logging.info("Net", message)
private val wsClient = externalHttpClient ?: run {
httpClient(
trustAllCerts = trustAllCerts,
) {
install(Logging) {
logger = object : Logger {
override fun log(message: String) {
com.piasy.kmp.xlog.Logging.info("Net", message)
}
}
level = LogLevel.ALL
}
install(WebSockets) {
pingIntervalMillis = 20_000
}
level = LogLevel.ALL
}
install(WebSockets) {
pingIntervalMillis = 20_000
}
}
// Linux curl engine doesn't work for simultaneous websocket and http request.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ package com.piasy.kmp.socketio.engineio
import com.piasy.kmp.socketio.engineio.transports.PollingXHR
import com.piasy.kmp.socketio.engineio.transports.TransportFactory
import com.piasy.kmp.socketio.engineio.transports.WebSocket
import io.ktor.client.HttpClient
import io.mockk.every
import io.mockk.mockk
import io.mockk.slot
import io.mockk.spyk
import io.mockk.verify
import kotlinx.coroutines.*
Expand All @@ -19,6 +21,7 @@ import org.hildan.socketio.SocketIOPacket
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertSame

class EngineSocketTest : BaseTest() {

Expand Down Expand Up @@ -116,6 +119,25 @@ class EngineSocketTest : BaseTest() {
assertEquals(listOf(EngineSocket.EVENT_TRANSPORT), sock.events)
}

@Test
fun openWithExternalHttpClient() = runTest {
val opt = EngineSocket.Options()
opt.transports = listOf(PollingXHR.NAME)
val externalHttpClient = mockk<HttpClient>(relaxed = true)
opt.httpClient = externalHttpClient

val transport = spyk(TestTransport(Transport.Options(), this, PollingXHR.NAME))
val factory = mockk<TransportFactory>()
val transportOpt = slot<Transport.Options>()
every { factory.create(any(), capture(transportOpt), any(), any()) } returns transport

val socket = EngineSocket("http://localhost", opt, this, factory)
socket.open()
advanceUntilIdle()

assertSame(externalHttpClient, transportOpt.captured.httpClient)
}

@Test
fun openSuccess() = runTest {
val sock = prepareSocket(listOf(WebSocket.NAME), this)
Expand Down
Loading