enable to implement custom parser

This commit is contained in:
nkzawa
2017-07-14 11:24:43 +09:00
parent 06068654c3
commit 7279919ef9
6 changed files with 279 additions and 213 deletions

View File

@@ -2,6 +2,7 @@ package io.socket.client;
import io.socket.backo.Backoff;
import io.socket.emitter.Emitter;
import io.socket.parser.IOParser;
import io.socket.parser.Packet;
import io.socket.parser.Parser;
import io.socket.thread.EventThread;
@@ -148,8 +149,10 @@ public class Manager extends Emitter {
this.uri = uri;
this.encoding = false;
this.packetBuffer = new ArrayList<Packet>();
this.encoder = new Parser.Encoder();
this.decoder = new Parser.Decoder();
Parser parser = opts.parser != null ? opts.parser : new IOParser();
this.encoder = parser.getEncoder();
this.decoder = parser.getDecoder();
}
private void emitAll(String event, Object... args) {
@@ -385,12 +388,12 @@ public class Manager extends Emitter {
Manager.this.onclose((String)objects[0]);
}
}));
this.subs.add(On.on(this.decoder, Parser.Decoder.EVENT_DECODED, new Listener() {
this.decoder.onDecoded(new Parser.Decoder.Callback() {
@Override
public void call(Object... objects) {
Manager.this.ondecoded((Packet) objects[0]);
public void call (Packet packet) {
Manager.this.ondecoded(packet);
}
}));
});
}
private void onping() {
@@ -631,6 +634,7 @@ public class Manager extends Emitter {
public long reconnectionDelay;
public long reconnectionDelayMax;
public double randomizationFactor;
public Parser parser;
/**
* Connection timeout (ms). Set -1 to disable.

View File

@@ -0,0 +1,242 @@
package io.socket.parser;
import io.socket.hasbinary.HasBinary;
import org.json.JSONException;
import org.json.JSONTokener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
final public class IOParser implements Parser {
private static final Logger logger = Logger.getLogger(IOParser.class.getName());
private static Packet<String> error() {
return new Packet<String>(ERROR, "parser error");
}
public IOParser() {}
@Override
public Parser.Encoder getEncoder () {
return new Encoder();
}
@Override
public Parser.Decoder getDecoder () {
return new Decoder();
}
final public static class Encoder implements Parser.Encoder {
public Encoder() {}
@Override
public void encode(Packet obj, Callback callback) {
if ((obj.type == EVENT || obj.type == ACK) && HasBinary.hasBinary(obj.data)) {
obj.type = obj.type == EVENT ? BINARY_EVENT : BINARY_ACK;
}
logger.fine(String.format("encoding packet %s", obj));
if (BINARY_EVENT == obj.type || BINARY_ACK == obj.type) {
encodeAsBinary(obj, callback);
} else {
String encoding = encodeAsString(obj);
callback.call(new String[] {encoding});
}
}
private String encodeAsString(Packet obj) {
StringBuilder str = new StringBuilder("" + obj.type);
if (BINARY_EVENT == obj.type || BINARY_ACK == obj.type) {
str.append(obj.attachments);
str.append("-");
}
if (obj.nsp != null && obj.nsp.length() != 0 && !"/".equals(obj.nsp)) {
str.append(obj.nsp);
str.append(",");
}
if (obj.id >= 0) {
str.append(obj.id);
}
if (obj.data != null) {
str.append(obj.data);
}
logger.fine(String.format("encoded %s as %s", obj, str));
return str.toString();
}
private void encodeAsBinary(Packet obj, Callback callback) {
Binary.DeconstructedPacket deconstruction = Binary.deconstructPacket(obj);
String pack = encodeAsString(deconstruction.packet);
List<Object> buffers = new ArrayList<Object>(Arrays.asList(deconstruction.buffers));
buffers.add(0, pack);
callback.call(buffers.toArray());
}
}
final public static class Decoder implements Parser.Decoder {
/*package*/ BinaryReconstructor reconstructor;
private Decoder.Callback onDecodedCallback;
public Decoder() {
this.reconstructor = null;
}
@Override
public void add(String obj) {
Packet packet = decodeString(obj);
if (BINARY_EVENT == packet.type || BINARY_ACK == packet.type) {
this.reconstructor = new BinaryReconstructor(packet);
if (this.reconstructor.reconPack.attachments == 0) {
if (this.onDecodedCallback != null) {
this.onDecodedCallback.call(packet);
}
}
} else {
if (this.onDecodedCallback != null) {
this.onDecodedCallback.call(packet);
}
}
}
@Override
public void add(byte[] obj) {
if (this.reconstructor == null) {
throw new RuntimeException("got binary data when not reconstructing a packet");
} else {
Packet packet = this.reconstructor.takeBinaryData(obj);
if (packet != null) {
this.reconstructor = null;
if (this.onDecodedCallback != null) {
this.onDecodedCallback.call(packet);
}
}
}
}
private static Packet decodeString(String str) {
int i = 0;
int length = str.length();
Packet<Object> p = new Packet<Object>(Character.getNumericValue(str.charAt(0)));
if (p.type < 0 || p.type > types.length - 1) return error();
if (BINARY_EVENT == p.type || BINARY_ACK == p.type) {
if (!str.contains("-") || length <= i + 1) return error();
StringBuilder attachments = new StringBuilder();
while (str.charAt(++i) != '-') {
attachments.append(str.charAt(i));
}
p.attachments = Integer.parseInt(attachments.toString());
}
if (length > i + 1 && '/' == str.charAt(i + 1)) {
StringBuilder nsp = new StringBuilder();
while (true) {
++i;
char c = str.charAt(i);
if (',' == c) break;
nsp.append(c);
if (i + 1 == length) break;
}
p.nsp = nsp.toString();
} else {
p.nsp = "/";
}
if (length > i + 1){
Character next = str.charAt(i + 1);
if (Character.getNumericValue(next) > -1) {
StringBuilder id = new StringBuilder();
while (true) {
++i;
char c = str.charAt(i);
if (Character.getNumericValue(c) < 0) {
--i;
break;
}
id.append(c);
if (i + 1 == length) break;
}
try {
p.id = Integer.parseInt(id.toString());
} catch (NumberFormatException e){
return error();
}
}
}
if (length > i + 1){
try {
str.charAt(++i);
p.data = new JSONTokener(str.substring(i)).nextValue();
} catch (JSONException e) {
logger.log(Level.WARNING, "An error occured while retrieving data from JSONTokener", e);
return error();
}
}
logger.fine(String.format("decoded %s as %s", str, p));
return p;
}
@Override
public void destroy() {
if (this.reconstructor != null) {
this.reconstructor.finishReconstruction();
}
this.onDecodedCallback = null;
}
@Override
public void onDecoded (Callback callback) {
this.onDecodedCallback = callback;
}
}
/*package*/ static class BinaryReconstructor {
public Packet reconPack;
/*package*/ List<byte[]> buffers;
BinaryReconstructor(Packet packet) {
this.reconPack = packet;
this.buffers = new ArrayList<byte[]>();
}
public Packet takeBinaryData(byte[] binData) {
this.buffers.add(binData);
if (this.buffers.size() == this.reconPack.attachments) {
Packet packet = Binary.reconstructPacket(this.reconPack,
this.buffers.toArray(new byte[this.buffers.size()][]));
this.finishReconstruction();
return packet;
}
return null;
}
public void finishReconstruction () {
this.reconPack = null;
this.buffers = new ArrayList<byte[]>();
}
}
}

View File

@@ -11,9 +11,7 @@ import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Parser {
private static final Logger logger = Logger.getLogger(Parser.class.getName());
public interface Parser {
/**
* Packet type `connect`.
@@ -65,66 +63,13 @@ public class Parser {
"BINARY_ACK"
};
public Encoder getEncoder();
private Parser() {}
public Decoder getDecoder();
private static Packet<String> error() {
return new Packet<String>(ERROR, "parser error");
}
public static interface Encoder {
public static class Encoder {
public Encoder() {}
public void encode(Packet obj, Callback callback) {
if ((obj.type == EVENT || obj.type == ACK) && HasBinary.hasBinary(obj.data)) {
obj.type = obj.type == EVENT ? BINARY_EVENT : BINARY_ACK;
}
logger.fine(String.format("encoding packet %s", obj));
if (BINARY_EVENT == obj.type || BINARY_ACK == obj.type) {
encodeAsBinary(obj, callback);
} else {
String encoding = encodeAsString(obj);
callback.call(new String[] {encoding});
}
}
private String encodeAsString(Packet obj) {
StringBuilder str = new StringBuilder("" + obj.type);
if (BINARY_EVENT == obj.type || BINARY_ACK == obj.type) {
str.append(obj.attachments);
str.append("-");
}
if (obj.nsp != null && obj.nsp.length() != 0 && !"/".equals(obj.nsp)) {
str.append(obj.nsp);
str.append(",");
}
if (obj.id >= 0) {
str.append(obj.id);
}
if (obj.data != null) {
str.append(obj.data);
}
logger.fine(String.format("encoded %s as %s", obj, str));
return str.toString();
}
private void encodeAsBinary(Packet obj, Callback callback) {
Binary.DeconstructedPacket deconstruction = Binary.deconstructPacket(obj);
String pack = encodeAsString(deconstruction.packet);
List<Object> buffers = new ArrayList<Object>(Arrays.asList(deconstruction.buffers));
buffers.add(0, pack);
callback.call(buffers.toArray());
}
public void encode(Packet obj, Callback callback);
public interface Callback {
@@ -132,141 +77,19 @@ public class Parser {
}
}
public static class Decoder extends Emitter {
public static interface Decoder {
public static String EVENT_DECODED = "decoded";
public void add(String obj);
/*package*/ BinaryReconstructor reconstructor;
public void add(byte[] obj);
public Decoder() {
this.reconstructor = null;
}
public void destroy();
public void add(String obj) {
Packet packet = decodeString(obj);
if (BINARY_EVENT == packet.type || BINARY_ACK == packet.type) {
this.reconstructor = new BinaryReconstructor(packet);
public void onDecoded(Callback callback);
if (this.reconstructor.reconPack.attachments == 0) {
this.emit(EVENT_DECODED, packet);
}
} else {
this.emit(EVENT_DECODED, packet);
}
}
public interface Callback {
public void add(byte[] obj) {
if (this.reconstructor == null) {
throw new RuntimeException("got binary data when not reconstructing a packet");
} else {
Packet packet = this.reconstructor.takeBinaryData(obj);
if (packet != null) {
this.reconstructor = null;
this.emit(EVENT_DECODED, packet);
}
}
}
private static Packet decodeString(String str) {
int i = 0;
int length = str.length();
Packet<Object> p = new Packet<Object>(Character.getNumericValue(str.charAt(0)));
if (p.type < 0 || p.type > types.length - 1) return error();
if (BINARY_EVENT == p.type || BINARY_ACK == p.type) {
if (!str.contains("-") || length <= i + 1) return error();
StringBuilder attachments = new StringBuilder();
while (str.charAt(++i) != '-') {
attachments.append(str.charAt(i));
}
p.attachments = Integer.parseInt(attachments.toString());
}
if (length > i + 1 && '/' == str.charAt(i + 1)) {
StringBuilder nsp = new StringBuilder();
while (true) {
++i;
char c = str.charAt(i);
if (',' == c) break;
nsp.append(c);
if (i + 1 == length) break;
}
p.nsp = nsp.toString();
} else {
p.nsp = "/";
}
if (length > i + 1){
Character next = str.charAt(i + 1);
if (Character.getNumericValue(next) > -1) {
StringBuilder id = new StringBuilder();
while (true) {
++i;
char c = str.charAt(i);
if (Character.getNumericValue(c) < 0) {
--i;
break;
}
id.append(c);
if (i + 1 == length) break;
}
try {
p.id = Integer.parseInt(id.toString());
} catch (NumberFormatException e){
return error();
}
}
}
if (length > i + 1){
try {
str.charAt(++i);
p.data = new JSONTokener(str.substring(i)).nextValue();
} catch (JSONException e) {
logger.log(Level.WARNING, "An error occured while retrieving data from JSONTokener", e);
return error();
}
}
logger.fine(String.format("decoded %s as %s", str, p));
return p;
}
public void destroy() {
if (this.reconstructor != null) {
this.reconstructor.finishReconstruction();
}
}
}
/*package*/ static class BinaryReconstructor {
public Packet reconPack;
/*package*/ List<byte[]> buffers;
BinaryReconstructor(Packet packet) {
this.reconPack = packet;
this.buffers = new ArrayList<byte[]>();
}
public Packet takeBinaryData(byte[] binData) {
this.buffers.add(binData);
if (this.buffers.size() == this.reconPack.attachments) {
Packet packet = Binary.reconstructPacket(this.reconPack,
this.buffers.toArray(new byte[this.buffers.size()][]));
this.finishReconstruction();
return packet;
}
return null;
}
public void finishReconstruction () {
this.reconPack = null;
this.buffers = new ArrayList<byte[]>();
public void call(Packet packet);
}
}
}

View File

@@ -16,7 +16,7 @@ import static org.junit.Assert.assertThat;
@RunWith(JUnit4.class)
public class ByteArrayTest {
private static Parser.Encoder encoder = new Parser.Encoder();
private static Parser.Encoder encoder = new IOParser.Encoder();
@Test
public void encodeByteArray() {
@@ -87,10 +87,10 @@ public class ByteArrayTest {
encoder.encode(packet, new Parser.Encoder.Callback() {
@Override
public void call(final Object[] encodedPackets) {
final Parser.Decoder decoder = new Parser.Decoder();
decoder.on(Parser.Decoder.EVENT_DECODED, new Emitter.Listener() {
final IOParser.Decoder decoder = new IOParser.Decoder();
decoder.onDecoded(new Parser.Decoder.Callback() {
@Override
public void call(Object... args) {
public void call(Packet packet) {
throw new RuntimeException("received a packet when not all binary data was sent.");
}
});

View File

@@ -14,18 +14,17 @@ import static org.junit.Assert.assertThat;
@RunWith(JUnit4.class)
public class Helpers {
private static Parser.Encoder encoder = new Parser.Encoder();
private static Parser.Encoder encoder = new IOParser.Encoder();
private static Packet<String> errorPacket = new Packet<String>(Parser.ERROR, "parser error");
public static void test(final Packet obj) {
encoder.encode(obj, new Parser.Encoder.Callback() {
@Override
public void call(Object[] encodedPackets) {
Parser.Decoder decoder = new Parser.Decoder();
decoder.on(Parser.Decoder.EVENT_DECODED, new Emitter.Listener() {
Parser.Decoder decoder = new IOParser.Decoder();
decoder.onDecoded(new Parser.Decoder.Callback() {
@Override
public void call(Object... args) {
Packet packet = (Packet)args[0];
public void call(Packet packet) {
assertPacket(packet, obj);
}
});
@@ -35,11 +34,10 @@ public class Helpers {
}
public static void testDecodeError(final String errorMessage) {
Parser.Decoder decoder = new Parser.Decoder();
decoder.on(Parser.Decoder.EVENT_DECODED, new Emitter.Listener() {
Parser.Decoder decoder = new IOParser.Decoder();
decoder.onDecoded(new IOParser.Decoder.Callback() {
@Override
public void call(Object... args) {
Packet packet = (Packet)args[0];
public void call(Packet packet) {
assertPacket(errorPacket, packet);
}
});
@@ -52,11 +50,10 @@ public class Helpers {
encoder.encode(obj, new Parser.Encoder.Callback() {
@Override
public void call(Object[] encodedPackets) {
Parser.Decoder decoder = new Parser.Decoder();
decoder.on(Parser.Decoder.EVENT_DECODED, new Emitter.Listener() {
Parser.Decoder decoder = new IOParser.Decoder();
decoder.onDecoded(new Parser.Decoder.Callback() {
@Override
public void call(Object... args) {
Packet packet = (Packet)args[0];
public void call(Packet packet) {
obj.data = originalData;
obj.attachments = -1;
assertPacket(packet, obj);

View File

@@ -9,7 +9,7 @@ import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public class ParserTest {
private static Parser.Encoder encoder = new Parser.Encoder();
private static Parser.Encoder encoder = new IOParser.Encoder();
@Test
public void encodeConnection() {