@@ -3,13 +3,21 @@ package org.hildan.krossbow.websocket.ktor
3
3
import java.net.ProtocolException
4
4
import java.net.UnknownHostException
5
5
import java.net.http.WebSocketHandshakeException
6
+ import kotlin.contracts.ExperimentalContracts
7
+ import kotlin.contracts.contract
6
8
7
- internal actual fun extractHandshakeStatusCode (handshakeException : Exception ): Int? = when (handshakeException) {
8
- is WebSocketHandshakeException -> handshakeException.response.statusCode()
9
- // with OkHttp engine, we get ProtocolException with itself as cause - we can only parse the message
10
- is ProtocolException -> extractHandshakeStatusCode(handshakeException)
11
- is UnknownHostException -> null
12
- else -> null
9
+ internal actual fun extractHandshakeStatusCode (handshakeException : Exception ): Int? {
10
+ val e = handshakeException
11
+ return when {
12
+ // no status code if we can't even contact the host
13
+ e is UnknownHostException -> null
14
+ // with OkHttp engine, we get ProtocolException with itself as cause - we can only parse the message
15
+ e is ProtocolException -> extractHandshakeStatusCode(e)
16
+ e.safeIs<WebSocketHandshakeException >(" java.net.http.WebSocketHandshakeException" ) -> {
17
+ extractHandshakeStatusCode(e)
18
+ }
19
+ else -> null
20
+ }
13
21
}
14
22
15
23
private val protocolExceptionMessageRegex = Regex (""" Expected HTTP 101 response but was '(\d{3}) [^']+'""" )
@@ -18,3 +26,38 @@ private fun extractHandshakeStatusCode(handshakeException: ProtocolException): I
18
26
val message = handshakeException.message ? : return null
19
27
return protocolExceptionMessageRegex.matchEntire(message)?.groupValues?.get(1 )?.toInt()
20
28
}
29
+
30
+ private fun extractHandshakeStatusCode (webSocketHandshakeException : WebSocketHandshakeException ) =
31
+ webSocketHandshakeException.response.statusCode()
32
+
33
+ /* *
34
+ * Returns true if [C] is on the classpath and `this` is an instance of [C].
35
+ * The given [className] must match the fully qualified name of [C].
36
+ *
37
+ * Doesn't fail with [NoClassDefFoundError] if [C] is not present.
38
+ */
39
+ @OptIn(ExperimentalContracts ::class )
40
+ private inline fun <reified C : Any > Any.safeIs (className : String ): Boolean {
41
+ contract {
42
+ returns(true ) implies(this @safeIs is C )
43
+ }
44
+ if (! classExists(className)) {
45
+ return false
46
+ }
47
+ checkMatches<C >(className) // prevent developer mistakes
48
+ return this is C
49
+ }
50
+
51
+ private inline fun <reified T : Any > checkMatches (className : String ) {
52
+ val typeName = T ::class .qualifiedName
53
+ require(typeName == className) {
54
+ " Mismatch between the given class name '$className ' and the actual parameter type of the action lambda '$typeName "
55
+ }
56
+ }
57
+
58
+ private fun classExists (className : String ): Boolean = try {
59
+ Class .forName(className)
60
+ true
61
+ } catch (e: ClassNotFoundException ) {
62
+ false
63
+ }
0 commit comments