Skip to content

Commit 3179fae

Browse files
author
Daniel Rees
authored
Fix WebSocketTransport not kicking off reconnect strategy (#49)
* Fixed failures not triggering reconnect strategy * Added tests for transport reconnect
1 parent e7cbc79 commit 3179fae

File tree

3 files changed

+51
-1
lines changed

3 files changed

+51
-1
lines changed

src/main/kotlin/org/phoenixframework/Socket.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,12 @@ internal data class StateChangeCallbacks(
5151
/** The code used when the socket was closed without error */
5252
const val WS_CLOSE_NORMAL = 1000
5353

54+
/** The socket was closed due to a SocketException. Likely the client lost connectivity */
55+
const val WS_CLOSE_SOCKET_EXCEPTION = 4000
56+
57+
/** The socket was closed due to an EOFException. Likely the server abruptly closed */
58+
const val WS_CLOSE_EOF_EXCEPTION = 4001
59+
5460
/**
5561
* Connects to a Phoenix Server
5662
*/

src/main/kotlin/org/phoenixframework/Transport.kt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ import okhttp3.Request
2727
import okhttp3.Response
2828
import okhttp3.WebSocket
2929
import okhttp3.WebSocketListener
30+
import java.io.EOFException
31+
import java.net.ConnectException
32+
import java.net.SocketException
3033
import java.net.URL
3134

3235
/**
@@ -131,6 +134,16 @@ class WebSocketTransport(
131134
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
132135
this.readyState = Transport.ReadyState.CLOSED
133136
this.onError?.invoke(t, response)
137+
138+
// Do not attempt to recover if the initial connection was refused
139+
if (t is ConnectException) return
140+
141+
// Check if the socket was closed for some recoverable reason
142+
if (t is SocketException) {
143+
this.onClosed(webSocket, WS_CLOSE_SOCKET_EXCEPTION, "Socket Exception")
144+
} else if (t is EOFException) {
145+
this.onClosed(webSocket, WS_CLOSE_EOF_EXCEPTION, "EOF Exception")
146+
}
134147
}
135148

136149
override fun onClosing(webSocket: WebSocket, code: Int, reason: String) {

src/test/kotlin/org/phoenixframework/WebSocketTransportTest.kt

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import com.google.common.truth.Truth.assertThat
44
import com.nhaarman.mockitokotlin2.any
55
import com.nhaarman.mockitokotlin2.mock
66
import com.nhaarman.mockitokotlin2.verify
7+
import com.nhaarman.mockitokotlin2.verifyZeroInteractions
78
import com.nhaarman.mockitokotlin2.whenever
89
import okhttp3.OkHttpClient
910
import okhttp3.Response
@@ -12,13 +13,14 @@ import org.junit.Before
1213
import org.junit.Test
1314
import org.mockito.Mock
1415
import org.mockito.MockitoAnnotations
16+
import java.io.EOFException
17+
import java.net.SocketException
1518
import java.net.URL
1619

1720
class WebSocketTransportTest {
1821

1922
@Mock lateinit var mockClient: OkHttpClient
2023
@Mock lateinit var mockWebSocket: WebSocket
21-
@Mock lateinit var mockChannel: Channel
2224
@Mock lateinit var mockResponse: Response
2325

2426
lateinit var transport: WebSocketTransport
@@ -72,6 +74,8 @@ class WebSocketTransportTest {
7274
@Test
7375
fun `onFailure sets ready state to CLOSED and invokes onError callback`() {
7476
val mockClosure = mock<(Throwable, Response?) -> Unit>()
77+
val mockOnClose = mock<(Int) -> Unit>()
78+
transport.onClose = mockOnClose
7579
transport.onError = mockClosure
7680

7781
transport.readyState = Transport.ReadyState.CONNECTING
@@ -80,6 +84,33 @@ class WebSocketTransportTest {
8084
transport.onFailure(mockWebSocket, throwable, mockResponse)
8185
assertThat(transport.readyState).isEqualTo(Transport.ReadyState.CLOSED)
8286
verify(mockClosure).invoke(throwable, mockResponse)
87+
verifyZeroInteractions(mockOnClose)
88+
}
89+
90+
@Test
91+
fun `onFailure also triggers onClose for SocketException`() {
92+
val mockOnError = mock<(Throwable, Response?) -> Unit>()
93+
val mockOnClose = mock<(Int) -> Unit>()
94+
transport.onClose = mockOnClose
95+
transport.onError = mockOnError
96+
97+
val throwable = SocketException()
98+
transport.onFailure(mockWebSocket, throwable, mockResponse)
99+
verify(mockOnError).invoke(throwable, mockResponse)
100+
verify(mockOnClose).invoke(4000)
101+
}
102+
103+
@Test
104+
fun `onFailure also triggers onClose for EOFException`() {
105+
val mockOnError = mock<(Throwable, Response?) -> Unit>()
106+
val mockOnClose = mock<(Int) -> Unit>()
107+
transport.onClose = mockOnClose
108+
transport.onError = mockOnError
109+
110+
val throwable = EOFException()
111+
transport.onFailure(mockWebSocket, throwable, mockResponse)
112+
verify(mockOnError).invoke(throwable, mockResponse)
113+
verify(mockOnClose).invoke(4001)
83114
}
84115

85116
@Test

0 commit comments

Comments
 (0)