Skip to content

Commit cedbbd9

Browse files
authored
GUACAMOLE-827: Merge fix BlobWriter race condition in chunk upload.
2 parents 3c6b462 + 929367e commit cedbbd9

1 file changed

Lines changed: 29 additions & 7 deletions

File tree

guacamole-common-js/src/main/webapp/modules/BlobWriter.js

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ Guacamole.BlobWriter = function BlobWriter(stream) {
109109

110110
var offset = 0;
111111
var reader = new FileReader();
112+
var inFlight = false;
113+
var ackHandled = false;
112114

113115
/**
114116
* Reads the next chunk of the blob provided to
@@ -120,6 +122,10 @@ Guacamole.BlobWriter = function BlobWriter(stream) {
120122
*/
121123
var readNextChunk = function readNextChunk() {
122124

125+
// Prevent concurrent chunk operations
126+
if (inFlight)
127+
return;
128+
123129
// If no further chunks remain, inform of completion and stop
124130
if (offset >= blob.size) {
125131

@@ -132,6 +138,10 @@ Guacamole.BlobWriter = function BlobWriter(stream) {
132138

133139
}
134140

141+
// Mark that a chunk operation is now in progress
142+
inFlight = true;
143+
ackHandled = false;
144+
135145
// Obtain reference to next chunk as a new blob
136146
var chunk = slice(blob, offset, offset + arrayBufferWriter.blobLength);
137147
offset += arrayBufferWriter.blobLength;
@@ -145,16 +155,20 @@ Guacamole.BlobWriter = function BlobWriter(stream) {
145155
// Send each chunk over the stream, continue reading the next chunk
146156
reader.onload = function chunkLoadComplete() {
147157

148-
// Send the successfully-read chunk
149-
arrayBufferWriter.sendData(reader.result);
150-
151-
// Continue sending more chunks after the latest chunk is
152-
// acknowledged
158+
// Register ACK handler BEFORE sending data to prevent race condition
153159
arrayBufferWriter.onack = function sendMoreChunks(status) {
154160

161+
// Prevent duplicate ACK handling
162+
if (ackHandled)
163+
return;
164+
ackHandled = true;
165+
155166
if (guacWriter.onack)
156167
guacWriter.onack(status);
157168

169+
// Clear in-flight flag now that ACK is received
170+
inFlight = false;
171+
158172
// Abort transfer if an error occurs
159173
if (status.isError())
160174
return;
@@ -163,16 +177,24 @@ Guacamole.BlobWriter = function BlobWriter(stream) {
163177
if (guacWriter.onprogress)
164178
guacWriter.onprogress(blob, offset - arrayBufferWriter.blobLength);
165179

166-
// Queue the next chunk for reading
167-
readNextChunk();
180+
// Schedule the next chunk for reading (avoid deep recursion)
181+
setTimeout(function() {
182+
readNextChunk();
183+
}, 0);
168184

169185
};
170186

187+
// Send the successfully-read chunk
188+
arrayBufferWriter.sendData(reader.result);
189+
171190
};
172191

173192
// If an error prevents further reading, inform of error and stop
174193
reader.onerror = function chunkLoadFailed() {
175194

195+
// Clear in-flight flag on error
196+
inFlight = false;
197+
176198
// Fire error event, including the context of the error
177199
if (guacWriter.onerror)
178200
guacWriter.onerror(blob, offset, reader.error);

0 commit comments

Comments
 (0)