feat: add support for Engine.IO v4

Reference: https://github.com/socketio/engine.io-protocol#difference-between-v3-and-v4
This commit is contained in:
Damien Arrachequesne
2020-12-09 01:57:52 +01:00
parent b1b7002691
commit 41f89a38b7
14 changed files with 829 additions and 864 deletions

View File

@@ -242,4 +242,20 @@ public class ConnectionTest extends Connection {
socket.open();
assertThat((Integer) values.take(), is(0));
}
@Test(timeout = TIMEOUT)
public void receivePing() throws InterruptedException {
final BlockingQueue<String> values = new LinkedBlockingQueue<>();
socket = new Socket(createOptions());
socket.on(Socket.EVENT_PING, new Emitter.Listener() {
@Override
public void call(Object... args) {
values.offer("end");
socket.close();
}
});
socket.open();
assertThat(values.take(), is("end"));
}
}

View File

@@ -1,6 +1,5 @@
package io.socket.engineio.parser;
import io.socket.utf8.UTF8Exception;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -15,7 +14,7 @@ public class ParserTest {
static final String ERROR_DATA = "parser error";
@Test
public void encodeAsString() throws UTF8Exception {
public void encodeAsString() {
encodePacket(new Packet<String>(Packet.MESSAGE, "test"), new EncodeCallback<String>() {
@Override
public void call(String data) {
@@ -25,7 +24,7 @@ public class ParserTest {
}
@Test
public void decodeAsPacket() throws UTF8Exception {
public void decodeAsPacket() {
encodePacket(new Packet<String>(Packet.MESSAGE, "test"), new EncodeCallback<String>() {
@Override
public void call(String data) {
@@ -35,7 +34,7 @@ public class ParserTest {
}
@Test
public void noData() throws UTF8Exception {
public void noData() {
encodePacket(new Packet(Packet.MESSAGE), new EncodeCallback<String>() {
@Override
public void call(String data) {
@@ -47,7 +46,7 @@ public class ParserTest {
}
@Test
public void encodeOpenPacket() throws UTF8Exception {
public void encodeOpenPacket() {
encodePacket(new Packet<String>(Packet.OPEN, "{\"some\":\"json\"}"), new EncodeCallback<String>() {
@Override
public void call(String data) {
@@ -59,7 +58,7 @@ public class ParserTest {
}
@Test
public void encodeClosePacket() throws UTF8Exception {
public void encodeClosePacket() {
encodePacket(new Packet<String>(Packet.CLOSE), new EncodeCallback<String>() {
@Override
public void call(String data) {
@@ -70,7 +69,7 @@ public class ParserTest {
}
@Test
public void encodePingPacket() throws UTF8Exception {
public void encodePingPacket() {
encodePacket(new Packet<String>(Packet.PING, "1"), new EncodeCallback<String>() {
@Override
public void call(String data) {
@@ -82,7 +81,7 @@ public class ParserTest {
}
@Test
public void encodePongPacket() throws UTF8Exception {
public void encodePongPacket() {
encodePacket(new Packet<String>(Packet.PONG, "1"), new EncodeCallback<String>() {
@Override
public void call(String data) {
@@ -94,7 +93,7 @@ public class ParserTest {
}
@Test
public void encodeMessagePacket() throws UTF8Exception {
public void encodeMessagePacket() {
encodePacket(new Packet<String>(Packet.MESSAGE, "aaa"), new EncodeCallback<String>() {
@Override
public void call(String data) {
@@ -106,7 +105,7 @@ public class ParserTest {
}
@Test
public void encodeUTF8SpecialCharsMessagePacket() throws UTF8Exception {
public void encodeUTF8SpecialCharsMessagePacket() {
encodePacket(new Packet<String>(Packet.MESSAGE, "utf8 — string"), new EncodeCallback<String>() {
@Override
public void call(String data) {
@@ -118,7 +117,7 @@ public class ParserTest {
}
@Test
public void encodeMessagePacketCoercingToString() throws UTF8Exception {
public void encodeMessagePacketCoercingToString() {
encodePacket(new Packet<Integer>(Packet.MESSAGE, 1), new EncodeCallback<String>() {
@Override
public void call(String data) {
@@ -130,7 +129,7 @@ public class ParserTest {
}
@Test
public void encodeUpgradePacket() throws UTF8Exception {
public void encodeUpgradePacket() {
encodePacket(new Packet<String>(Packet.UPGRADE), new EncodeCallback<String>() {
@Override
public void call(String data) {
@@ -141,7 +140,7 @@ public class ParserTest {
}
@Test
public void encodingFormat() throws UTF8Exception {
public void encodingFormat() {
encodePacket(new Packet<String>(Packet.MESSAGE, "test"), new EncodeCallback<String>() {
@Override
public void call(String data) {
@@ -156,19 +155,6 @@ public class ParserTest {
});
}
@Test
public void encodingStringMessageWithLoneSurrogatesReplacedByUFFFD() throws UTF8Exception {
String data = "\uDC00\uD834\uDF06\uDC00 \uD800\uD835\uDF07\uD800";
encodePacket(new Packet<String>(Packet.MESSAGE, data), true, new EncodeCallback<String>() {
@Override
public void call(String encoded) {
Packet<String> p = decodePacket(encoded, true);
assertThat(p.type, is(Packet.MESSAGE));
assertThat(p.data, is("\uFFFD\uD834\uDF06\uFFFD \uFFFD\uD835\uDF07\uFFFD"));
}
});
}
@Test
public void decodeEmptyPayload() {
Packet<String> p = decodePacket((String)null);
@@ -191,14 +177,7 @@ public class ParserTest {
}
@Test
public void decodeInvalidUTF8() {
Packet<String> p = decodePacket("4\uffff", true);
assertThat(p.type, is(Packet.ERROR));
assertThat(p.data, is(ERROR_DATA));
}
@Test
public void encodePayloads() throws UTF8Exception {
public void encodePayloads() {
encodePayload(new Packet[]{new Packet(Packet.PING), new Packet(Packet.PONG)}, new EncodeCallback<String>() {
@Override
public void call(String data) {
@@ -208,7 +187,7 @@ public class ParserTest {
}
@Test
public void encodeAndDecodePayloads() throws UTF8Exception {
public void encodeAndDecodePayloads() {
encodePayload(new Packet[] {new Packet<String>(Packet.MESSAGE, "a")}, new EncodeCallback<String>() {
@Override
public void call(String data) {
@@ -242,7 +221,7 @@ public class ParserTest {
}
@Test
public void encodeAndDecodeEmptyPayloads() throws UTF8Exception {
public void encodeAndDecodeEmptyPayloads() {
encodePayload(new Packet[] {}, new EncodeCallback<String>() {
@Override
public void call(String data) {
@@ -260,30 +239,20 @@ public class ParserTest {
}
@Test
public void notUTF8EncodeWhenDealingWithStringsOnly() throws UTF8Exception {
public void notUTF8EncodeWhenDealingWithStringsOnly() {
encodePayload(new Packet[] {
new Packet(Packet.MESSAGE, "€€€"),
new Packet(Packet.MESSAGE, "α")
}, new EncodeCallback<String>() {
@Override
public void call(String data) {
assertThat(data, is("4:4€€€2:4α"));
assertThat(data, is("4€€€\u001e4α"));
}
});
}
@Test
public void decodePayloadBadFormat() {
decodePayload("1!", new DecodePayloadCallback<String>() {
@Override
public boolean call(Packet<String> packet, int index, int total) {
boolean isLast = index + 1 == total;
assertThat(packet.type, is(Packet.ERROR));
assertThat(packet.data, is(ERROR_DATA));
assertThat(isLast, is(true));
return true;
}
});
decodePayload("", new DecodePayloadCallback<String>() {
@Override
public boolean call(Packet<String> packet, int index, int total) {
@@ -306,23 +275,9 @@ public class ParserTest {
});
}
@Test
public void decodePayloadBadLength() {
decodePayload("1:", new DecodePayloadCallback<String>() {
@Override
public boolean call(Packet<String> packet, int index, int total) {
boolean isLast = index + 1 == total;
assertThat(packet.type, is(Packet.ERROR));
assertThat(packet.data, is(ERROR_DATA));
assertThat(isLast, is(true));
return true;
}
});
}
@Test
public void decodePayloadBadPacketFormat() {
decodePayload("3:99:", new DecodePayloadCallback<String>() {
decodePayload("99:", new DecodePayloadCallback<String>() {
@Override
public boolean call(Packet<String> packet, int index, int total) {
boolean isLast = index + 1 == total;
@@ -332,17 +287,7 @@ public class ParserTest {
return true;
}
});
decodePayload("1:aa", new DecodePayloadCallback<String>() {
@Override
public boolean call(Packet<String> packet, int index, int total) {
boolean isLast = index + 1 == total;
assertThat(packet.type, is(Packet.ERROR));
assertThat(packet.data, is(ERROR_DATA));
assertThat(isLast, is(true));
return true;
}
});
decodePayload("1:a2:b", new DecodePayloadCallback<String>() {
decodePayload("aa", new DecodePayloadCallback<String>() {
@Override
public boolean call(Packet<String> packet, int index, int total) {
boolean isLast = index + 1 == total;
@@ -355,7 +300,7 @@ public class ParserTest {
}
@Test
public void encodeBinaryMessage() throws UTF8Exception {
public void encodeBinaryMessage() {
final byte[] data = new byte[5];
for (int i = 0; i < data.length; i++) {
data[0] = (byte)i;
@@ -371,7 +316,7 @@ public class ParserTest {
}
@Test
public void encodeBinaryContents() throws UTF8Exception {
public void encodeBinaryContents() {
final byte[] firstBuffer = new byte[5];
for (int i = 0 ; i < firstBuffer.length; i++) {
firstBuffer[0] = (byte)i;
@@ -384,9 +329,9 @@ public class ParserTest {
encodePayload(new Packet[]{
new Packet<byte[]>(Packet.MESSAGE, firstBuffer),
new Packet<byte[]>(Packet.MESSAGE, secondBuffer),
}, new EncodeCallback<byte[]>() {
}, new EncodeCallback<String>() {
@Override
public void call(byte[] data) {
public void call(String data) {
decodePayload(data, new DecodePayloadCallback() {
@Override
public boolean call(Packet packet, int index, int total) {
@@ -405,7 +350,7 @@ public class ParserTest {
}
@Test
public void encodeMixedBinaryAndStringContents() throws UTF8Exception {
public void encodeMixedBinaryAndStringContents() {
final byte[] firstBuffer = new byte[123];
for (int i = 0 ; i < firstBuffer.length; i++) {
firstBuffer[0] = (byte)i;
@@ -414,9 +359,9 @@ public class ParserTest {
new Packet<byte[]>(Packet.MESSAGE, firstBuffer),
new Packet<String>(Packet.MESSAGE, "hello"),
new Packet<String>(Packet.CLOSE),
}, new EncodeCallback<byte[]>() {
}, new EncodeCallback<String>() {
@Override
public void call(byte[] encoded) {
public void call(String encoded) {
decodePayload(encoded, new DecodePayloadCallback() {
@Override
public boolean call(Packet packet, int index, int total) {

View File

@@ -1,114 +0,0 @@
package io.socket.utf8;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
@RunWith(JUnit4.class)
public class UTF8Test {
private static final Data[] DATA = new Data[] {
// 1-byte
new Data(0x0000, "\u0000", "\u0000"),
new Data(0x005c, "\u005C\u005C", "\u005C\u005C"), // = backslash
new Data(0x007f, "\u007F", "\u007F"),
// 2-byte
new Data(0x0080, "\u0080", "\u00C2\u0080"),
new Data(0x05CA, "\u05CA", "\u00D7\u008A"),
new Data(0x07FF, "\u07FF", "\u00DF\u00BF"),
// 3-byte
new Data(0x0800, "\u0800", "\u00E0\u00A0\u0080"),
new Data(0x2C3C, "\u2C3C", "\u00E2\u00B0\u00BC"),
new Data(0x07FF, "\uFFFF", "\u00EF\u00BF\u00BF"),
// unmatched surrogate halves
// high surrogates: 0xD800 to 0xDBFF
new Data(0xD800, "\uD800", "\u00ED\u00A0\u0080", true),
new Data("High surrogate followed by another high surrogate",
"\uD800\uD800", "\u00ED\u00A0\u0080\u00ED\u00A0\u0080", true),
new Data("High surrogate followed by a symbol that is not a surrogate",
"\uD800A", "\u00ED\u00A0\u0080A", true),
new Data("Unmatched high surrogate, followed by a surrogate pair, followed by an unmatched high surrogate",
"\uD800\uD834\uDF06\uD800", "\u00ED\u00A0\u0080\u00F0\u009D\u008C\u0086\u00ED\u00A0\u0080", true),
new Data(0xD9AF, "\uD9AF", "\u00ED\u00A6\u00AF", true),
new Data(0xDBFF, "\uDBFF", "\u00ED\u00AF\u00BF", true),
// low surrogates: 0xDC00 to 0xDFFF
new Data(0xDC00, "\uDC00", "\u00ED\u00B0\u0080", true),
new Data("Low surrogate followed by another low surrogate",
"\uDC00\uDC00", "\u00ED\u00B0\u0080\u00ED\u00B0\u0080", true),
new Data("Low surrogate followed by a symbol that is not a surrogate",
"\uDC00A", "\u00ED\u00B0\u0080A", true),
new Data("Unmatched low surrogate, followed by a surrogate pair, followed by an unmatched low surrogate",
"\uDC00\uD834\uDF06\uDC00", "\u00ED\u00B0\u0080\u00F0\u009D\u008C\u0086\u00ED\u00B0\u0080", true),
new Data(0xDEEE, "\uDEEE", "\u00ED\u00BB\u00AE", true),
new Data(0xDFFF, "\uDFFF", "\u00ED\u00BF\u00BF", true),
// 4-byte
new Data(0x010000, "\uD800\uDC00", "\u00F0\u0090\u0080\u0080"),
new Data(0x01D306, "\uD834\uDF06", "\u00F0\u009D\u008C\u0086"),
new Data(0x010FFF, "\uDBFF\uDFFF", "\u00F4\u008F\u00BF\u00BF"),
};
@Rule
public ExpectedException exception = ExpectedException.none();
@Test
public void encodeAndDecode() throws UTF8Exception {
for (Data data : DATA) {
String reason = data.description != null? data.description : "U+" + Integer.toHexString(data.codePoint).toUpperCase();
if (data.error) {
exception.expect(UTF8Exception.class);
UTF8.decode(data.encoded);
exception.expect(UTF8Exception.class);
UTF8.encode(data.decoded);
} else {
assertThat("Encoding: " + reason, data.encoded, is(UTF8.encode(data.decoded)));
assertThat("Decoding: " + reason, data.decoded, is(UTF8.decode(data.encoded)));
}
}
exception.expect(UTF8Exception.class);
UTF8.decode("\uFFFF");
exception.expect(UTF8Exception.class);
UTF8.decode("\u00E9\u0000\u0000");
exception.expect(UTF8Exception.class);
UTF8.decode("\u00C2\uFFFF");
exception.expect(UTF8Exception.class);
UTF8.decode("\u00F0\u009D");
}
private static class Data {
public int codePoint = -1;
public String description;
public String decoded;
public String encoded;
public boolean error;
public Data(int codePoint, String decoded, String encoded) {
this(codePoint, decoded, encoded, false);
}
public Data(int codePoint, String decoded, String encoded, boolean error) {
this.codePoint = codePoint;
this.decoded = decoded;
this.encoded = encoded;
this.error = error;
}
public Data(String description, String decoded, String encoded) {
this(description, decoded, encoded, false);
}
public Data(String description, String decoded, String encoded, boolean error) {
this.description = description;
this.decoded = decoded;
this.encoded = encoded;
this.error = error;
}
}
}