Skip to content

Commit 5847bd3

Browse files
feat: Native Websocket Plugin (uses okhttp) (#1335)
* feat: ✨ Native Websocket Plugin (uses okhttp) * update: readme of cordova websocket plugin for importing it. * Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * feat: enhance WebSocket plugin with custom headers and improved close functionality - Updated `WebSocketPlugin.connect` to accept custom headers. - Enhanced `WebSocketInstance.close` method to allow specifying close code and reason. - Added `WebSocketPlugin.listClients` method to list active WebSocket instances. - Updated JavaScript interface to support new features and improved documentation. * feat: improve WebSocketInstance and WebSocketPlugin with enhanced logging and error handling - Updated `WebSocketInstance.send` and `close` methods to include detailed logging for better debugging. - Modified `WebSocketPlugin` to handle close operation errors and provide appropriate feedback to the callback context. - Ensured proper binding of event handlers in the JavaScript WebSocket interface for consistent context handling. * Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * refactor: replace HashMap with ConcurrentHashMap for thread-safe WebSocket instances management * feat: enhance WebSocket close event handling - Updated `WebSocketInstance.onClosed` to send a structured close event with code and reason. - Modified JavaScript event handling to properly receive and process the close event data. * feat: enhance WebSocketInstance with binary message support and improved event handling - Added support for binary messages in `WebSocketInstance` with a new `onMessage` method for handling `ByteString`. - Updated `sendEvent` method to include a flag indicating if the message is binary. - Modified JavaScript interface to handle binary data and added logging for better debugging. - Updated `WebSocketPlugin.connect` to accept a `binaryType` parameter for configuration. * fix: add missing import for ByteString in WebSocketInstance Fixes ` error: name clash: class WebSocketInstance has two methods with the same erasure, yet neither overrides the other public void onMessage(@nonnull WebSocket webSocket, @nonnull String text) { ^ first method: onMessage(WebSocket,ByteString) in WebSocketInstance second method: onMessage(WebSocket,ByteString) in WebSocketListener` * feat: add devcontainer configuration with Android SDK support * feat: enhance WebSocket plugin with binary message support and improved API - Updated `WebSocketPlugin.connect` to accept an optional `binaryType` parameter. - Modified `WebSocketInstance.send` method to handle both string and binary messages. - Enhanced JavaScript interface to support binary message sending and receiving. - Improved event handling for binary messages in both Java and JavaScript layers. - Added logging for better debugging of message sending and receiving. * feat: improve WebSocket instance management on close event - Added logic to remove the WebSocket instance from the plugin after a close event is received. - Introduced a new static method `removeInstance` in `WebSocketPlugin` to handle instance removal. * fix: ensure WebSocket closes properly on closing event - Added a call to `this.webSocket.close(code, reason)` in the `onClosing` method to ensure the WebSocket instance closes with the specified code and reason. Ref: square/okhttp#6510 (comment) --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent bef8c97 commit 5847bd3

File tree

9 files changed

+724
-0
lines changed

9 files changed

+724
-0
lines changed

.devcontainer/devcontainer.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"image": "mcr.microsoft.com/devcontainers/universal:2",
3+
"features": {
4+
"ghcr.io/nordcominc/devcontainer-features/android-sdk:1": {}
5+
}
6+
}

package-lock.json

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
"cordova-plugin-advanced-http": {
3737
"ANDROIDBLACKLISTSECURESOCKETPROTOCOLS": "SSLv3,TLSv1"
3838
},
39+
"cordova-plugin-websocket": {},
3940
"com.foxdebug.acode.exec": {}
4041
},
4142
"platforms": [
@@ -76,6 +77,7 @@
7677
"cordova-plugin-server": "file:src/plugins/server",
7778
"cordova-plugin-sftp": "file:src/plugins/sftp",
7879
"cordova-plugin-system": "file:src/plugins/system",
80+
"cordova-plugin-websocket": "file:src/plugins/websocket",
7981
"css-loader": "^7.1.2",
8082
"mini-css-extract-plugin": "^2.9.0",
8183
"path-browserify": "^1.0.1",

src/plugins/websocket/README.md

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
# Cordova Plugin: OkHttp WebSocket
2+
3+
A Cordova plugin that uses [OkHttp](https://square.github.io/okhttp/) to provide WebSocket support in your Cordova app.
4+
It aims to mimic the [WebSocket API](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) in JavaScript, with additional features.
5+
6+
## Features
7+
8+
* ✅ WebSocket API-like interface
9+
* ✅ Event support: `onopen`, `onmessage`, `onerror`, `onclose`
10+
*`extensions` and `readyState` properties
11+
*`listClients()` to list active connections
12+
* ✅ Support for protocols
13+
* ✅ Support for Custom Headers.
14+
* ✅ Compatible with Cordova for Android
15+
16+
---
17+
18+
## Usage
19+
20+
### Import
21+
22+
```javascript
23+
const WebSocketPlugin = cordova.websocket;
24+
```
25+
26+
### Connect to WebSocket
27+
28+
```javascript
29+
WebSocketPlugin.connect("wss://example.com/socket", ["protocol1", "protocol2"], headers)
30+
.then(ws => {
31+
ws.onopen = (e) => console.log("Connected!", e);
32+
ws.onmessage = (e) => console.log("Message:", e.data);
33+
ws.onerror = (e) => console.error("Error:", e);
34+
ws.onclose = (e) => console.log("Closed:", e);
35+
36+
ws.send("Hello from Cordova!");
37+
ws.close();
38+
})
39+
.catch(err => console.error("WebSocket connection failed:", err));
40+
```
41+
42+
---
43+
44+
## API Reference
45+
46+
### Methods
47+
48+
* `WebSocketPlugin.connect(url, protocols, headers, binaryType)`
49+
* Connects to a WebSocket server.
50+
* `url`: The WebSocket server URL.
51+
* `protocols`: (Optional) An array of subprotocol strings.
52+
* `headers`: (Optional) Custom headers as key-value pairs.
53+
* `binaryType`: (Optional) Initial binary type setting.
54+
* **Returns:** A Promise that resolves to a `WebSocketInstance`.
55+
56+
* `WebSocketPlugin.listClients()`
57+
* Lists all stored webSocket instance IDs.
58+
* **Returns:** `Promise` that resolves to an array of `instanceId` strings.
59+
60+
* `WebSocketPlugin.send(instanceId, message, binary)`
61+
* Sends a message to the server using an instance ID.
62+
* `instanceId`: The ID of the WebSocket instance.
63+
* `message`: The message to send (string or ArrayBuffer/ArrayBufferView).
64+
* `binary`: (Optional) Whether to send the message as binary, accepts `boolean`
65+
* **Returns:** `Promise` that resolves when the message is sent.
66+
67+
* `WebSocketPlugin.close(instanceId, code, reason)`
68+
* same as `WebSocketInstance.close(code, reason)` but needs `instanceId`.
69+
* **Returns:** `Promise` that resolves.
70+
71+
### WebSocketInstance Methods
72+
73+
* `WebSocketInstance.send(message, binary)`
74+
* Sends a message to the server.
75+
* `message`: The message to send (string or ArrayBuffer/ArrayBufferView).
76+
* `binary`: (Optional) Whether to send the message as binary. accepts `boolean`
77+
* Throws an error if the connection is not open.
78+
79+
* `WebSocketInstance.close(code, reason)`
80+
* Closes the connection.
81+
* `code`: (Optional) If unspecified, a close code for the connection is automatically set: to 1000 for a normal closure, or otherwise to [another standard value in the range 1001-1015](https://www.rfc-editor.org/rfc/rfc6455.html#section-7.4.1) that indicates the actual reason the connection was closed.
82+
* `reason`: A string providing a [custom WebSocket connection close reason](https://www.rfc-editor.org/rfc/rfc6455.html#section-7.1.6) (a concise human-readable prose explanation for the closure). The value must be no longer than 123 bytes (encoded in UTF-8).
83+
84+
---
85+
86+
### Properties of `WebSocketInstance`
87+
88+
* `onopen`: Event listener for connection open.
89+
* `onmessage`: Event listener for messages received.
90+
* `onclose`: Event listener for connection close.
91+
* `onerror`: Event listener for errors.
92+
* `readyState`: (number) The state of the connection.
93+
* 0 (`CONNECTING`): Socket created, not yet open.
94+
* 1 (`OPEN`): Connection is open and ready.
95+
* 2 (`CLOSING`): Connection is closing.
96+
* 3 (`CLOSED`): Connection is closed or couldn't be opened.
97+
* `extensions`: (string) Extensions negotiated by the server.
98+
* `binaryType`: (string) Type of binary data to use ('arraybuffer' or '' (binary payload returned as strings.)).
99+
* `url`: (string) The WebSocket server URL.
100+
* `instanceId`: (string) Unique identifier for this WebSocket instance.
101+
102+
### Event Handling
103+
104+
`WebSocketInstance` extends `EventTarget`, providing standard event handling methods:
105+
106+
* `addEventListener(type, listener)`: Registers an event listener.
107+
* `removeEventListener(type, listener)`: Removes an event listener.
108+
* `dispatchEvent(event)`: Dispatches an event to the object.
109+
110+
Example of using event listeners:
111+
```javascript
112+
const ws = await WebSocketPlugin.connect("wss://example.com/socket");
113+
114+
// Using on* properties
115+
ws.onmessage = (event) => console.log("Message:", event.data);
116+
117+
// Using addEventListener
118+
ws.addEventListener('message', (event) => console.log("Message:", event.data));
119+
```
120+
121+
### Constants
122+
123+
* `WebSocketInstance.CONNECTING`: 0
124+
* `WebSocketInstance.OPEN`: 1
125+
* `WebSocketInstance.CLOSING`: 2
126+
* `WebSocketInstance.CLOSED`: 3
127+
128+
---
129+
130+
## Notes
131+
132+
* Only supported on Android (via OkHttp).
133+
* Make sure to handle connection lifecycle properly (close sockets when done).
134+
* `listClients()` is useful for debugging and management.
135+
---

src/plugins/websocket/package.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"name": "cordova-plugin-websocket",
3+
"version": "0.0.1",
4+
"description": "This cordova plugin is created to use WebSocket (client) in web/js.",
5+
"cordova": {
6+
"id": "cordova-plugin-websocket",
7+
"platforms": [
8+
"android"
9+
]
10+
},
11+
"keywords": [
12+
"cordova",
13+
"websocket",
14+
"cordova-android",
15+
"ws"
16+
],
17+
"author": "Acode-Foundation (created by UnschooledGamer)",
18+
"license": "Apache-2.0",
19+
"scripts": {
20+
"test": "echo \"Error: no test specified\" && exit 1"
21+
}
22+
}

src/plugins/websocket/plugin.xml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<plugin id="cordova-plugin-websocket" version="0.0.1" xmlns="http://apache.org/cordova/ns/plugins/1.0">
3+
<name>cordova-plugin-websocket</name>
4+
<description>Cordova Websocket</description>
5+
<license>MIT</license>
6+
<keywords>cordova,ws,WebSocket</keywords>
7+
<js-module src="www/websocket.js" name="WebSocket">
8+
<clobbers target="cordova.websocket" />
9+
</js-module>
10+
11+
<platform name="android">
12+
<config-file target="res/xml/config.xml" parent="/*">
13+
<feature name="WebSocketPlugin">
14+
<param name="android-package" value="com.foxdebug.websocket.WebSocketPlugin" />
15+
</feature>
16+
</config-file>
17+
<source-file src="src/android/WebSocketPlugin.java" target-dir="src/com/foxdebug/websocket" />
18+
<source-file src="src/android/WebSocketInstance.java" target-dir="src/com/foxdebug/websocket" />
19+
<framework src="com.squareup.okhttp3:okhttp:4.12.0" />
20+
</platform>
21+
</plugin>

0 commit comments

Comments
 (0)