Skip to content

Commit 1b53df9

Browse files
authored
Merge pull request #11 from Hansanto/chore/upgrade_dependencies
chore(deps): Upgrade dependencies
2 parents 959b71f + 0702d2c commit 1b53df9

File tree

7 files changed

+652
-45
lines changed

7 files changed

+652
-45
lines changed

apollo-mockserver/api/mockserver.api renamed to apollo-mockserver/api/apollo-mockserver.api

+4
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,10 @@ public final class com/apollographql/mockserver/TextMessage : com/apollographql/
162162
public final fun getText ()Ljava/lang/String;
163163
}
164164

165+
public final class com/apollographql/mockserver/VersionKt {
166+
public static final field VERSION Ljava/lang/String;
167+
}
168+
165169
public abstract interface class com/apollographql/mockserver/WebSocketBody {
166170
public abstract fun close ()V
167171
public abstract fun enqueueMessage (Lcom/apollographql/mockserver/WebSocketMessage;)V

apollo-mockserver/api/mockserver.klib.api renamed to apollo-mockserver/api/apollo-mockserver.klib.api

+3-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
// - Show manifest properties: true
77
// - Show declarations: true
88

9-
// Library unique name: <apollo-kotlin-mockserver:mockserver>
9+
// Library unique name: <com.apollographql.mockserver:apollo-mockserver>
1010
abstract interface com.apollographql.mockserver/MockRequestBase { // com.apollographql.mockserver/MockRequestBase|null[0]
1111
abstract val headers // com.apollographql.mockserver/MockRequestBase.headers|{}headers[0]
1212
abstract fun <get-headers>(): kotlin.collections/Map<kotlin/String, kotlin/String> // com.apollographql.mockserver/MockRequestBase.headers.<get-headers>|<get-headers>(){}[0]
@@ -137,6 +137,8 @@ final class com.apollographql.mockserver/WebsocketMockRequest : com.apollographq
137137
final val version // com.apollographql.mockserver/WebsocketMockRequest.version|{}version[0]
138138
final fun <get-version>(): kotlin/String // com.apollographql.mockserver/WebsocketMockRequest.version.<get-version>|<get-version>(){}[0]
139139
}
140+
final const val com.apollographql.mockserver/VERSION // com.apollographql.mockserver/VERSION|{}VERSION[0]
141+
final fun <get-VERSION>(): kotlin/String // com.apollographql.mockserver/VERSION.<get-VERSION>|<get-VERSION>(){}[0]
140142
final fun (com.apollographql.mockserver/MockServer).com.apollographql.mockserver/assertNoRequest() // com.apollographql.mockserver/assertNoRequest|[email protected](){}[0]
141143
final fun (com.apollographql.mockserver/MockServer).com.apollographql.mockserver/enqueue(kotlin/String = ..., kotlin/Long = ..., kotlin/Int = ...) // com.apollographql.mockserver/enqueue|[email protected](kotlin.String;kotlin.Long;kotlin.Int){}[0]
142144
final fun (com.apollographql.mockserver/MockServer).com.apollographql.mockserver/enqueueError(kotlin/Int) // com.apollographql.mockserver/enqueueError|[email protected](kotlin.Int){}[0]

apollo-mockserver/src/commonMain/kotlin/com/apollographql/mockserver/TcpServer.kt

+8-3
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,14 @@ interface TcpSocket: Closeable {
2828
override fun close()
2929
}
3030

31+
/**
32+
* A server that handles [TcpSocket]
33+
* [TcpServer] is not thread safe and its method must be called from the same coroutine.
34+
*/
3135
interface TcpServer : Closeable {
3236
/**
33-
* Starts listening and calls [block] when on incoming connections
37+
* Starts a background coroutine that calls [block] on incoming connections.
38+
* [listen] can only be called once.
3439
*/
3540
fun listen(block: (socket: TcpSocket) -> Unit)
3641

@@ -41,7 +46,7 @@ interface TcpServer : Closeable {
4146

4247
/**
4348
* Closes the server.
44-
*
49+
* [close] is idempotent.
4550
*/
4651
override fun close()
4752
}
@@ -51,4 +56,4 @@ class Address(
5156
val port: Int
5257
)
5358

54-
expect fun TcpServer(port: Int): TcpServer
59+
expect fun TcpServer(port: Int): TcpServer

apollo-mockserver/src/concurrentMain/kotlin/com/apollographql/mockserver/TcpServer.concurrent.kt

+47-21
Original file line numberDiff line numberDiff line change
@@ -2,55 +2,63 @@ package com.apollographql.mockserver
22

33
import io.ktor.network.selector.SelectorManager
44
import io.ktor.network.sockets.InetSocketAddress
5+
import io.ktor.network.sockets.ServerSocket
56
import io.ktor.network.sockets.aSocket
67
import io.ktor.network.sockets.openReadChannel
78
import io.ktor.network.sockets.openWriteChannel
9+
import io.ktor.utils.io.readAvailable
10+
import io.ktor.utils.io.writeFully
811
import kotlinx.coroutines.CancellationException
912
import kotlinx.coroutines.CoroutineDispatcher
1013
import kotlinx.coroutines.CoroutineScope
14+
import kotlinx.coroutines.CoroutineStart
1115
import kotlinx.coroutines.Dispatchers
1216
import kotlinx.coroutines.IO
1317
import kotlinx.coroutines.SupervisorJob
1418
import kotlinx.coroutines.cancel
1519
import kotlinx.coroutines.channels.Channel
1620
import kotlinx.coroutines.coroutineScope
1721
import kotlinx.coroutines.delay
22+
import kotlinx.coroutines.isActive
1823
import kotlinx.coroutines.launch
1924
import kotlinx.coroutines.withTimeout
2025
import okio.IOException
2126
import io.ktor.network.sockets.Socket as WrappedSocket
2227

2328
actual fun TcpServer(port: Int): TcpServer = KtorTcpServer(port)
2429

25-
class KtorTcpServer(port: Int = 0, private val acceptDelayMillis: Int = 0, dispatcher: CoroutineDispatcher = Dispatchers.IO) : TcpServer {
30+
class KtorTcpServer(
31+
private val port: Int = 0,
32+
private val acceptDelayMillis: Int = 0,
33+
dispatcher: CoroutineDispatcher = Dispatchers.IO
34+
) : TcpServer {
35+
2636
private val selectorManager = SelectorManager(dispatcher)
27-
private val scope = CoroutineScope(SupervisorJob() + dispatcher)
28-
private val serverSocket = aSocket(selectorManager).tcp().bind("127.0.0.1", port)
37+
private val serverScope = CoroutineScope(SupervisorJob() + dispatcher)
38+
private var serverSocket: ServerSocket? = null
2939

40+
override fun listen(block: (socket: TcpSocket) -> Unit) {
41+
require(serverScope.isActive) { "Server is closed and cannot be restarted" }
42+
require(serverSocket == null) { "Server is already started" }
3043

31-
override fun close() {
32-
scope.cancel()
33-
selectorManager.close()
34-
serverSocket.close()
35-
}
44+
serverScope.launch(start = CoroutineStart.UNDISPATCHED) {
45+
serverSocket = aSocket(selectorManager).tcp().bind("127.0.0.1", port)
3646

37-
override fun listen(block: (socket: TcpSocket) -> Unit) {
38-
scope.launch {
39-
while (true) {
47+
while (isActive) {
4048
if (acceptDelayMillis > 0) {
4149
delay(acceptDelayMillis.toLong())
4250
}
4351
val socket: WrappedSocket = try {
44-
serverSocket.accept()
52+
serverSocket!!.accept()
4553
} catch (t: Throwable) {
46-
if (t is CancellationException) { throw t }
54+
if (t is CancellationException) {
55+
throw t
56+
}
4757
delay(1000)
4858
continue
4959
}
5060

51-
val ktorSocket = KtorTcpSocket(socket)
52-
block(ktorSocket)
53-
61+
val ktorSocket = KtorTcpSocket(socket).apply(block)
5462
launch {
5563
ktorSocket.loop()
5664
}
@@ -59,22 +67,40 @@ class KtorTcpServer(port: Int = 0, private val acceptDelayMillis: Int = 0, dispa
5967
}
6068

6169
override suspend fun address(): Address {
70+
require(serverScope.isActive) { "Server is closed" }
71+
requireNotNull(serverSocket) { "Server is not listening, please call listen() before calling address()" }
72+
6273
return withTimeout(1000) {
63-
var address: Address
64-
while(true) {
74+
var address: Address? = null
75+
while (address == null) {
76+
val serverSocket = requireNotNull(serverSocket) {
77+
"close() was called during a call to address()"
78+
}
79+
6580
try {
6681
address = (serverSocket.localAddress as InetSocketAddress).let {
6782
Address(it.hostname, it.port)
6883
}
69-
break
70-
} catch (e: Exception) {
84+
} catch (_: Exception) {
7185
delay(50)
7286
continue
7387
}
7488
}
7589
address
7690
}
7791
}
92+
93+
override fun close() {
94+
serverSocket?.let { serverSocket ->
95+
// Cancel the server scope only if the server was started before
96+
// If the server was not started, the scope keeps being active and server can be started for the first time
97+
// Otherwise, the scope is cancelled to prevent the server from being started again
98+
serverScope.cancel()
99+
selectorManager.close()
100+
serverSocket.close()
101+
this.serverSocket = null
102+
}
103+
}
78104
}
79105

80106
internal class KtorTcpSocket(private val socket: WrappedSocket) : TcpSocket {
@@ -127,4 +153,4 @@ internal class KtorTcpSocket(private val socket: WrappedSocket) : TcpSocket {
127153
override fun send(data: ByteArray) {
128154
writeQueue.trySend(data)
129155
}
130-
}
156+
}

apollo-mockserver/src/jsMain/kotlin/com/apollographql/mockserver/TcpServer.js.kt

+26-14
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import node.buffer.Buffer
66
import node.events.Event
77
import node.net.AddressInfo
88
import node.net.createServer
9+
import node.test.it
910
import okio.IOException
1011
import kotlin.coroutines.resume
1112
import kotlin.coroutines.suspendCoroutine
@@ -14,6 +15,7 @@ import node.net.Socket as WrappedSocket
1415

1516
internal class NodeTcpSocket(private val netSocket: WrappedSocket) : TcpSocket {
1617
private val readQueue = Channel<ByteArray>(Channel.UNLIMITED)
18+
1719
init {
1820
netSocket.on(Event.DATA) { chunk ->
1921
val bytes = when (chunk) {
@@ -47,38 +49,48 @@ internal class NodeTcpSocket(private val netSocket: WrappedSocket) : TcpSocket {
4749
}
4850

4951
internal class NodeTcpServer(private val port: Int) : TcpServer {
52+
5053
private var server: WrappedServer? = null
5154
private var address: Address? = null
52-
55+
private var isClosed = false
5356

5457
override fun listen(block: (socket: TcpSocket) -> Unit) {
58+
require(!isClosed) { "Server is closed and cannot be restarted" }
59+
require(server == null) { "Server is already started" }
60+
5561
server = createServer { netSocket ->
5662
block(NodeTcpSocket(netSocket))
63+
}.apply {
64+
listen(port)
5765
}
58-
59-
server!!.listen(port)
6066
}
6167

6268
override suspend fun address(): Address {
63-
check(server != null) {
64-
"You need to call start() before calling port()"
65-
}
69+
require(!isClosed) { "Server is closed" }
70+
val server = requireNotNull(this.server) { "Server is not listening, please call listen() before calling address()" }
6671

6772
return address ?: suspendCoroutine { cont ->
68-
server!!.on(Event.LISTENING) {
69-
val address = server!!.address().unsafeCast<AddressInfo>()
70-
71-
this.address = Address(address.address, address.port.toInt())
72-
cont.resume(this.address!!)
73+
server.once(Event.LISTENING) {
74+
val server = requireNotNull(this.server) {
75+
"close() was called during a call to address()"
76+
}
77+
val address = server.address().unsafeCast<AddressInfo>()
78+
this.address = Address(address.address, address.port.toInt()).also {
79+
cont.resume(it)
80+
}
7381
}
7482
}
7583
}
7684

7785
override fun close() {
78-
check(server != null) {
79-
"server is not started"
86+
if(isClosed) return
87+
88+
server?.let { server ->
89+
server.close()
90+
this.server = null
91+
// Is closed only if the server was started before
92+
isClosed = true
8093
}
81-
server!!.close()
8294
}
8395
}
8496

gradle/libs.versions.toml

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
[versions]
2-
kotlin-plugin = "2.0.0-RC2"
3-
okio = "3.9.0"
4-
ktor = "2.3.7"
5-
atomicfu = "0.23.1"
6-
kotlinx-coroutines = "1.8.0"
7-
okhttp = "4.11.0"
2+
kotlin-plugin = "2.0.21"
3+
okio = "3.9.1"
4+
ktor = "3.0.0"
5+
atomicfu = "0.25.0"
6+
kotlinx-coroutines = "1.9.0"
7+
okhttp = "4.12.0"
88

99
[libraries]
1010
kotlin-node = "org.jetbrains.kotlin-wrappers:kotlin-node:18.16.12-pre.634"

0 commit comments

Comments
 (0)