Skip to content

Commit fca3b95

Browse files
feat: emit with timeout
This feature allows to send a packet and expect an acknowledgement from the server within the given delay. Syntax: ```java socket.emit("hello", "world", new AckWithTimeout(5000) { @OverRide public void onTimeout() { // ... } @OverRide public void onSuccess(Object... args) { // ... } }); ``` Related: - #309 - #517
1 parent 7375763 commit fca3b95

File tree

3 files changed

+179
-6
lines changed

3 files changed

+179
-6
lines changed

Diff for: src/main/java/io/socket/client/AckWithTimeout.java

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package io.socket.client;
2+
3+
import java.util.Timer;
4+
import java.util.TimerTask;
5+
6+
public abstract class AckWithTimeout implements Ack {
7+
private final long timeout;
8+
private final Timer timer = new Timer();
9+
10+
/**
11+
*
12+
* @param timeout delay in milliseconds
13+
*/
14+
public AckWithTimeout(long timeout) {
15+
this.timeout = timeout;
16+
}
17+
18+
@Override
19+
public final void call(Object... args) {
20+
this.timer.cancel();
21+
this.onSuccess(args);
22+
}
23+
24+
public final void schedule(TimerTask task) {
25+
this.timer.schedule(task, this.timeout);
26+
}
27+
28+
public final void cancelTimer() {
29+
this.timer.cancel();
30+
}
31+
32+
public abstract void onSuccess(Object... args);
33+
public abstract void onTimeout();
34+
35+
}

Diff for: src/main/java/io/socket/client/Socket.java

+32-2
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,32 @@ public void run() {
210210
Packet<JSONArray> packet = new Packet<>(Parser.EVENT, jsonArgs);
211211

212212
if (ack != null) {
213-
logger.fine(String.format("emitting packet with ack id %d", ids));
214-
Socket.this.acks.put(ids, ack);
213+
final int ackId = Socket.this.ids;
214+
215+
logger.fine(String.format("emitting packet with ack id %d", ackId));
216+
217+
if (ack instanceof AckWithTimeout) {
218+
final AckWithTimeout ackWithTimeout = (AckWithTimeout) ack;
219+
ackWithTimeout.schedule(new TimerTask() {
220+
@Override
221+
public void run() {
222+
// remove the ack from the map (to prevent an actual acknowledgement)
223+
acks.remove(ackId);
224+
225+
// remove the packet from the buffer (if applicable)
226+
Iterator<Packet<JSONArray>> iterator = sendBuffer.iterator();
227+
while (iterator.hasNext()) {
228+
if (iterator.next().id == ackId) {
229+
iterator.remove();
230+
}
231+
}
232+
233+
ackWithTimeout.onTimeout();
234+
}
235+
});
236+
}
237+
238+
Socket.this.acks.put(ackId, ack);
215239
packet.id = ids++;
216240
}
217241

@@ -405,6 +429,12 @@ private void destroy() {
405429
this.subs = null;
406430
}
407431

432+
for (Ack ack : acks.values()) {
433+
if (ack instanceof AckWithTimeout) {
434+
((AckWithTimeout) ack).cancelTimer();
435+
}
436+
}
437+
408438
this.io.destroy();
409439
}
410440

Diff for: src/test/java/io/socket/client/SocketTest.java

+112-4
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,17 @@
44
import io.socket.util.Optional;
55
import org.json.JSONException;
66
import org.json.JSONObject;
7+
import org.junit.Assert;
78
import org.junit.Test;
89
import org.junit.runner.RunWith;
910
import org.junit.runners.JUnit4;
1011

1112
import java.util.Timer;
1213
import java.util.TimerTask;
13-
import java.util.concurrent.BlockingQueue;
14-
import java.util.concurrent.LinkedBlockingQueue;
14+
import java.util.concurrent.*;
1515

1616
import static java.util.Collections.singletonMap;
17-
import static org.hamcrest.CoreMatchers.is;
18-
import static org.hamcrest.CoreMatchers.not;
17+
import static org.hamcrest.CoreMatchers.*;
1918
import static org.junit.Assert.assertThat;
2019
import static org.junit.Assert.fail;
2120

@@ -287,4 +286,113 @@ public void call(Object... args) {
287286
assertThat(values.take(), is("first"));
288287
assertThat(values.take(), is("second"));
289288
}
289+
290+
@Test(timeout = TIMEOUT)
291+
public void shouldTimeoutAfterTheGivenDelayWhenSocketIsNotConnected() throws InterruptedException {
292+
final BlockingQueue<Boolean> values = new LinkedBlockingQueue<>();
293+
294+
socket = client();
295+
296+
socket.emit("event", new AckWithTimeout(50) {
297+
@Override
298+
public void onSuccess(Object... args) {
299+
fail();
300+
}
301+
302+
@Override
303+
public void onTimeout() {
304+
values.offer(true);
305+
}
306+
});
307+
308+
assertThat(values.take(), is(true));
309+
}
310+
311+
@Test(timeout = TIMEOUT)
312+
public void shouldTimeoutWhenTheServerDoesNotAcknowledgeTheEvent() throws InterruptedException {
313+
final BlockingQueue<Boolean> values = new LinkedBlockingQueue<>();
314+
315+
socket = client();
316+
317+
socket.on(Socket.EVENT_CONNECT, new Emitter.Listener() {
318+
@Override
319+
public void call(Object... args) {
320+
socket.emit("unknown", new AckWithTimeout(50) {
321+
@Override
322+
public void onTimeout() {
323+
values.offer(true);
324+
}
325+
326+
@Override
327+
public void onSuccess(Object... args) {
328+
fail();
329+
}
330+
});
331+
}
332+
});
333+
334+
socket.connect();
335+
336+
assertThat(values.take(), is(true));
337+
}
338+
339+
@Test(timeout = TIMEOUT)
340+
public void shouldTimeoutWhenTheServerDoesNotAcknowledgeTheEventInTime() throws InterruptedException {
341+
final BlockingQueue<Boolean> values = new LinkedBlockingQueue<>();
342+
343+
socket = client();
344+
345+
socket.on(Socket.EVENT_CONNECT, new Emitter.Listener() {
346+
@Override
347+
public void call(Object... args) {
348+
socket.emit("ack", new AckWithTimeout(0) {
349+
@Override
350+
public void onTimeout() {
351+
values.offer(true);
352+
}
353+
354+
@Override
355+
public void onSuccess(Object... args) {
356+
fail();
357+
}
358+
});
359+
}
360+
});
361+
362+
socket.connect();
363+
364+
assertThat(values.take(), is(true));
365+
}
366+
367+
@Test(timeout = TIMEOUT)
368+
public void shouldNotTimeoutWhenTheServerDoesAcknowledgeTheEvent() throws InterruptedException {
369+
final BlockingQueue<Object> values = new LinkedBlockingQueue<>();
370+
371+
socket = client();
372+
373+
socket.on(Socket.EVENT_CONNECT, new Emitter.Listener() {
374+
@Override
375+
public void call(Object... args) {
376+
socket.emit("ack", 1, "2", new byte[] { 3 }, new AckWithTimeout(200) {
377+
@Override
378+
public void onTimeout() {
379+
fail();
380+
}
381+
382+
@Override
383+
public void onSuccess(Object... args) {
384+
for (Object arg : args) {
385+
values.offer(arg);
386+
}
387+
}
388+
});
389+
}
390+
});
391+
392+
socket.connect();
393+
394+
assertThat((Integer) values.take(), is(1));
395+
assertThat((String) values.take(), is("2"));
396+
assertThat((byte[]) values.take(), is(new byte[] { 3 }));
397+
}
290398
}

0 commit comments

Comments
 (0)