Skip to content

Commit 96e915a

Browse files
authored
Merge pull request #17 from kwan3854/v2.0.3
Improves RPC error handling and removes chunking delay
2 parents b1a7b18 + d54281e commit 96e915a

File tree

8 files changed

+97
-49
lines changed

8 files changed

+97
-49
lines changed

Packages/WebviewRpc/Runtime/WebViewRpcClient.cs

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ public class WebViewRpcClient : IDisposable
1616
private readonly Dictionary<string, UniTaskCompletionSource<RpcEnvelope>> _pendingRequests = new();
1717
private readonly ChunkAssembler _chunkAssembler = new();
1818
private int _requestIdCounter = 1;
19+
private readonly object _pendingRequestsLock = new object();
1920
private bool _disposed;
2021

2122
public WebViewRpcClient(IWebViewBridge bridge)
@@ -32,11 +33,14 @@ public WebViewRpcClient(IWebViewBridge bridge)
3233
public async UniTask<TResponse> CallMethod<TResponse>(string method, IMessage request)
3334
where TResponse : IMessage<TResponse>, new()
3435
{
35-
var requestId = (_requestIdCounter++).ToString();
36+
var requestId = Interlocked.Increment(ref _requestIdCounter).ToString();
3637
var requestBytes = request.ToByteArray();
3738

3839
var tcs = new UniTaskCompletionSource<RpcEnvelope>();
39-
_pendingRequests[requestId] = tcs;
40+
lock (_pendingRequestsLock)
41+
{
42+
_pendingRequests[requestId] = tcs;
43+
}
4044

4145
try
4246
{
@@ -77,7 +81,10 @@ public async UniTask<TResponse> CallMethod<TResponse>(string method, IMessage re
7781
}
7882
finally
7983
{
80-
_pendingRequests.Remove(requestId);
84+
lock (_pendingRequestsLock)
85+
{
86+
_pendingRequests.Remove(requestId);
87+
}
8188
}
8289
}
8390

@@ -112,13 +119,9 @@ private async UniTask SendChunkedMessage(string requestId, string method, byte[]
112119
var bytes = envelope.ToByteArray();
113120
var base64 = Convert.ToBase64String(bytes);
114121
_bridge.SendMessageToWeb(base64);
115-
116-
// Optional: Add small delay between chunks to avoid overwhelming the bridge
117-
if (i < totalChunks)
118-
{
119-
await UniTask.Delay(1);
120-
}
121122
}
123+
124+
await UniTask.CompletedTask;
122125
}
123126

124127
private void OnBridgeMessage(string base64)
@@ -170,9 +173,18 @@ private void OnBridgeMessage(string base64)
170173

171174
private void ProcessCompleteEnvelope(RpcEnvelope envelope)
172175
{
173-
if (!envelope.IsRequest && _pendingRequests.TryGetValue(envelope.RequestId, out var tcs))
176+
if (!envelope.IsRequest)
174177
{
175-
tcs.TrySetResult(envelope);
178+
UniTaskCompletionSource<RpcEnvelope> tcs = null;
179+
lock (_pendingRequestsLock)
180+
{
181+
_pendingRequests.TryGetValue(envelope.RequestId, out tcs);
182+
}
183+
184+
if (tcs != null)
185+
{
186+
tcs.TrySetResult(envelope);
187+
}
176188
}
177189
}
178190

@@ -183,11 +195,14 @@ public void Dispose()
183195
_bridge.OnMessageReceived -= OnBridgeMessage;
184196

185197
// Cancel all pending requests
186-
foreach (var pending in _pendingRequests.Values)
198+
lock (_pendingRequestsLock)
187199
{
188-
pending.TrySetCanceled();
200+
foreach (var pending in _pendingRequests.Values)
201+
{
202+
pending.TrySetCanceled();
203+
}
204+
_pendingRequests.Clear();
189205
}
190-
_pendingRequests.Clear();
191206

192207
_disposed = true;
193208
}

Packages/WebviewRpc/Runtime/WebViewRpcServer.cs

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,31 @@ private async UniTask HandleRequestAsync(RpcEnvelope requestEnvelope)
114114
}
115115

116116
// Send response
117-
if (responsePayload != null)
117+
// Check if this is an error response
118+
if (!string.IsNullOrEmpty(error))
118119
{
120+
// Error response
121+
var responseEnvelope = new RpcEnvelope
122+
{
123+
RequestId = requestEnvelope.RequestId,
124+
IsRequest = false,
125+
Method = requestEnvelope.Method,
126+
Error = error
127+
};
128+
129+
// Include payload if available (even if empty)
130+
if (responsePayload != null)
131+
{
132+
responseEnvelope.Payload = responsePayload;
133+
}
134+
135+
var bytes = responseEnvelope.ToByteArray();
136+
var base64 = Convert.ToBase64String(bytes);
137+
_bridge.SendMessageToWeb(base64);
138+
}
139+
else if (responsePayload != null)
140+
{
141+
// Success response
119142
var responseBytes = responsePayload.ToByteArray();
120143

121144
// Check if chunking is needed
@@ -125,7 +148,7 @@ private async UniTask HandleRequestAsync(RpcEnvelope requestEnvelope)
125148
{
126149
// Send as chunks
127150
await SendChunkedMessage(requestEnvelope.RequestId, requestEnvelope.Method,
128-
responseBytes, false, error);
151+
responseBytes, false, null);
129152
}
130153
else
131154
{
@@ -138,26 +161,20 @@ await SendChunkedMessage(requestEnvelope.RequestId, requestEnvelope.Method,
138161
Payload = responsePayload
139162
};
140163

141-
// Only set Error if it's not null or empty
142-
if (!string.IsNullOrEmpty(error))
143-
{
144-
responseEnvelope.Error = error;
145-
}
146-
147164
var bytes = responseEnvelope.ToByteArray();
148165
var base64 = Convert.ToBase64String(bytes);
149166
_bridge.SendMessageToWeb(base64);
150167
}
151168
}
152169
else
153170
{
154-
// Error response
171+
// No payload and no error - this is an error condition
155172
var responseEnvelope = new RpcEnvelope
156173
{
157174
RequestId = requestEnvelope.RequestId,
158175
IsRequest = false,
159176
Method = requestEnvelope.Method,
160-
Error = error ?? "Unknown error"
177+
Error = "Method returned null without error"
161178
};
162179

163180
var bytes = responseEnvelope.ToByteArray();
@@ -204,13 +221,9 @@ private async UniTask SendChunkedMessage(string requestId, string method, byte[]
204221
var bytes = envelope.ToByteArray();
205222
var base64 = Convert.ToBase64String(bytes);
206223
_bridge.SendMessageToWeb(base64);
207-
208-
// Optional: Add small delay between chunks to avoid overwhelming the bridge
209-
if (i < totalChunks)
210-
{
211-
await UniTask.Delay(1);
212-
}
213224
}
225+
226+
await UniTask.CompletedTask;
214227
}
215228

216229
public void Dispose()

Packages/WebviewRpc/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "com.kwanjoong.webviewrpc",
3-
"version": "2.0.2",
3+
"version": "2.0.3",
44
"displayName": "Webview RPC",
55
"description": "The webview Remote Procedure Call bridge.",
66
"unity": "2022.3",

webview_rpc_runtime_library/CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [2.0.7] - 2025-01-22
9+
10+
### Fixed
11+
- Improved error handling: errors now take precedence over payload
12+
- Fixed issue where methods returning null without error were not properly handled
13+
- Removed unnecessary 1ms delay between chunks (chunk order is already guaranteed by index)
14+
15+
### Changed
16+
- Error responses can now include payload data if available
17+
- Clearer error messages when methods return null without setting an error
18+
819
## [2.0.6] - 2025-01-22
920

1021
### Added

webview_rpc_runtime_library/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "app-webview-rpc",
3-
"version": "2.0.6",
3+
"version": "2.0.7",
44
"type": "module",
55
"description": "WebView RPC provides an abstraction layer that allows communication between the App (e.g. Unity C#) and WebView (HTML, JS) using protobuf, similar to gRPC.",
66
"main": "./dist/cjs/index.js",

webview_rpc_runtime_library/src/core/webview_rpc_client.js

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,6 @@ export class WebViewRpcClient {
9292
const bytes = encodeRpcEnvelope(envelope);
9393
const base64 = uint8ArrayToBase64(bytes);
9494
this._bridge.sendMessage(base64);
95-
96-
// Optional: Add small delay between chunks
97-
if (i < totalChunks) {
98-
await new Promise(resolve => setTimeout(resolve, 1));
99-
}
10095
}
10196
}
10297

webview_rpc_runtime_library/src/core/webview_rpc_server.js

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,26 @@ export class WebViewRpcServer {
9999
}
100100

101101
// Send response
102-
if (responsePayload && !error) {
102+
// Check if this is an error response
103+
if (error) {
104+
// Error response
105+
const responseEnvelope = {
106+
requestId: requestEnvelope.requestId,
107+
isRequest: false,
108+
method: requestEnvelope.method,
109+
error: error
110+
};
111+
112+
// Include payload if available (even if empty)
113+
if (responsePayload) {
114+
responseEnvelope.payload = responsePayload;
115+
}
116+
117+
const responseBytes = encodeRpcEnvelope(responseEnvelope);
118+
const responseBase64 = uint8ArrayToBase64(responseBytes);
119+
this._bridge.sendMessage(responseBase64);
120+
} else if (responsePayload) {
121+
// Success response
103122
// Check if chunking is needed
104123
const effectivePayloadSize = WebViewRpcConfiguration.getEffectivePayloadSize();
105124
if (WebViewRpcConfiguration.enableChunking &&
@@ -110,7 +129,7 @@ export class WebViewRpcServer {
110129
requestEnvelope.method,
111130
responsePayload,
112131
false,
113-
error
132+
null
114133
);
115134
} else {
116135
// Send as single message
@@ -126,12 +145,12 @@ export class WebViewRpcServer {
126145
this._bridge.sendMessage(responseBase64);
127146
}
128147
} else {
129-
// Error response
148+
// No payload and no error - this is an error condition
130149
const responseEnvelope = {
131150
requestId: requestEnvelope.requestId,
132151
isRequest: false,
133152
method: requestEnvelope.method,
134-
error: error || 'Unknown error'
153+
error: 'Method returned null without error'
135154
};
136155

137156
const responseBytes = encodeRpcEnvelope(responseEnvelope);
@@ -175,11 +194,6 @@ export class WebViewRpcServer {
175194
const bytes = encodeRpcEnvelope(envelope);
176195
const base64 = uint8ArrayToBase64(bytes);
177196
this._bridge.sendMessage(base64);
178-
179-
// Optional: Add small delay between chunks
180-
if (i < totalChunks) {
181-
await new Promise(resolve => setTimeout(resolve, 1));
182-
}
183197
}
184198
}
185199

webview_rpc_sample/package-lock.json

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

0 commit comments

Comments
 (0)