Skip to content

Commit 3d549b5

Browse files
authored
feat: implement HybridWebSocket on Android (#6)
* chore: add initial implementation * revert changes * chore: stub URL * chore: remove todo * chore: add onclosed * chore: move to const
1 parent 86212a7 commit 3d549b5

File tree

5 files changed

+75
-19
lines changed

5 files changed

+75
-19
lines changed

README.md

-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ npm install react-native-fast-ws --save
1717
1818
### TODOs
1919

20-
- Provide Android support
2120
- Document things not implemented in spec, or different from spec
2221
- Fix outstanding (and known) issues
2322
- Figure out a better name and logo

example/App.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useCallback, useState } from 'react'
2-
import { ActivityIndicator, Button, StyleSheet, Text, View } from 'react-native'
2+
import { ActivityIndicator, Button, Platform, StyleSheet, Text, View } from 'react-native'
33
import { WebSocket as FastWebSocket } from 'react-native-fast-ws'
44

55
type Result = {
@@ -122,7 +122,7 @@ const testWebsocketMessages = async (opts: {
122122
incomingTime: number
123123
}> =>
124124
new Promise((resolve) => {
125-
const inst = new opts.Ws('ws://localhost:3000')
125+
const inst = new opts.Ws(WS_URL)
126126

127127
let outgoingTime: number
128128
let incomingTime: number
@@ -162,6 +162,7 @@ const testWebsocketMessages = async (opts: {
162162

163163
const INCOMING = 10000
164164
const OUTGOING = 10000
165+
const WS_URL = Platform.OS === 'android' ? 'ws://10.0.2.2:3000' : 'ws://localhost:3000'
165166

166167
const styles = StyleSheet.create({
167168
container: {

packages/react-native-fast-ws/android/build.gradle

+3
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,9 @@ dependencies {
126126

127127
// Add a dependency on NitroModules
128128
implementation project(":react-native-nitro-modules")
129+
130+
// Let's use whatever React Native uses right now to avoid duplicates
131+
implementation "com.squareup.okhttp3:okhttp:+"
129132
}
130133

131134
if (isNewArchitectureEnabled()) {
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,102 @@
11
package com.margelo.nitro.websocket
22

33
import com.margelo.nitro.core.ArrayBuffer
4+
import okhttp3.*
5+
import okio.ByteString
6+
import okio.ByteString.Companion.toByteString
7+
import java.util.concurrent.TimeUnit
48

59
class HybridWebSocket(url: String, protocols: Array<String>) : HybridWebSocketSpec() {
6-
override fun send(message: String) {
7-
TODO("Not implemented")
8-
}
10+
private var onOpenCallback: ((String) -> Unit)? = null
11+
private var onCloseCallback: ((Double, String) -> Unit)? = null
12+
private var onErrorCallback: ((String) -> Unit)? = null
13+
private var onMessageCallback: ((String) -> Unit)? = null
14+
private var onArrayBufferCallback: ((ArrayBuffer) -> Unit)? = null
915

10-
override fun sendArrayBuffer(buffer: ArrayBuffer) {
11-
TODO("Not implemented")
16+
private val client = OkHttpClient.Builder()
17+
.readTimeout(0, TimeUnit.MILLISECONDS) // Disable timeouts
18+
.build()
19+
20+
private var webSocket: WebSocket? = null
21+
22+
private val listener = object : WebSocketListener() {
23+
override fun onOpen(webSocket: WebSocket, response: Response) {
24+
val protocol = response.header("Sec-WebSocket-Protocol") ?: ""
25+
onOpenCallback?.invoke(protocol)
26+
}
27+
28+
override fun onMessage(webSocket: WebSocket, text: String) {
29+
onMessageCallback?.invoke(text)
30+
}
31+
32+
override fun onMessage(webSocket: WebSocket, bytes: ByteString) {
33+
val buffer = ArrayBuffer.allocate(bytes.size)
34+
buffer.getBuffer(false).put(bytes.toByteArray())
35+
onArrayBufferCallback?.invoke(buffer)
36+
}
37+
38+
override fun onClosing(webSocket: WebSocket, code: Int, reason: String) {
39+
onCloseCallback?.invoke(code.toDouble(), reason)
40+
}
41+
42+
override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
43+
webSocket.close(code, reason)
44+
}
45+
46+
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
47+
onErrorCallback?.invoke(t.message ?: "WebSocket error")
48+
}
1249
}
1350

51+
private val request = Request.Builder()
52+
.url(url)
53+
.apply {
54+
if (protocols.isNotEmpty()) {
55+
header("Sec-WebSocket-Protocol", protocols.joinToString(", "))
56+
}
57+
}
58+
.build()
59+
1460
override fun connect() {
15-
TODO("Not implemented")
61+
webSocket = client.newWebSocket(request, listener)
1662
}
1763

1864
override fun close() {
19-
TODO("Not implemented")
65+
webSocket?.close(1000, null)
66+
}
67+
68+
override fun send(message: String) {
69+
webSocket?.send(message)
70+
}
71+
72+
override fun sendArrayBuffer(buffer: ArrayBuffer) {
73+
webSocket?.send(buffer.getBuffer(false).toByteString())
2074
}
2175

2276
override fun ping() {
23-
TODO("Not implemented")
77+
webSocket?.send(ByteString.EMPTY)
2478
}
2579

2680
override fun onOpen(callback: (selectedProtocol: String) -> Unit) {
27-
TODO("Not implemented")
81+
onOpenCallback = callback
2882
}
2983

3084
override fun onClose(callback: (code: Double, reason: String) -> Unit) {
31-
TODO("Not implemented")
85+
onCloseCallback = callback
3286
}
3387

3488
override fun onError(callback: (error: String) -> Unit) {
35-
TODO("Not implemented")
89+
onErrorCallback = callback
3690
}
3791

3892
override fun onMessage(callback: (message: String) -> Unit) {
39-
TODO("Not implemented")
93+
onMessageCallback = callback
4094
}
4195

4296
override fun onArrayBuffer(callback: (buffer: ArrayBuffer) -> Unit) {
43-
TODO("Not implemented")
97+
onArrayBufferCallback = callback
4498
}
4599

46100
override val memorySize: Long
47-
get() = 0L
48-
101+
get() = 0L // Implement proper memory calculation if needed
49102
}

packages/react-native-fast-ws/src/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
import { NitroModules } from 'react-native-nitro-modules'
88

99
import { Blob } from './blob'
10-
import { WebSocket as HybridWebSocket, WebSocketManager } from './WebSocket.nitro'
10+
import { WebSocket as HybridWebSocket, WebSocketManager } from './spec.nitro'
1111

1212
const manager = NitroModules.createHybridObject<WebSocketManager>('WebSocketManager')
1313

0 commit comments

Comments
 (0)