diff --git a/src/main/java/io/socket/client/Manager.java b/src/main/java/io/socket/client/Manager.java index 4b925ed..450c579 100644 --- a/src/main/java/io/socket/client/Manager.java +++ b/src/main/java/io/socket/client/Manager.java @@ -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(); - 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. diff --git a/src/main/java/io/socket/parser/IOParser.java b/src/main/java/io/socket/parser/IOParser.java new file mode 100644 index 0000000..3e51f11 --- /dev/null +++ b/src/main/java/io/socket/parser/IOParser.java @@ -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 error() { + return new Packet(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 buffers = new ArrayList(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 p = new Packet(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 buffers; + + BinaryReconstructor(Packet packet) { + this.reconPack = packet; + this.buffers = new ArrayList(); + } + + 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(); + } + } +} + + diff --git a/src/main/java/io/socket/parser/Parser.java b/src/main/java/io/socket/parser/Parser.java index 79a65f6..fc9d88b 100644 --- a/src/main/java/io/socket/parser/Parser.java +++ b/src/main/java/io/socket/parser/Parser.java @@ -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 error() { - return new Packet(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 buffers = new ArrayList(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 p = new Packet(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 buffers; - - BinaryReconstructor(Packet packet) { - this.reconPack = packet; - this.buffers = new ArrayList(); - } - - 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(); + public void call(Packet packet); } } } diff --git a/src/test/java/io/socket/parser/ByteArrayTest.java b/src/test/java/io/socket/parser/ByteArrayTest.java index 5154cec..a358c15 100644 --- a/src/test/java/io/socket/parser/ByteArrayTest.java +++ b/src/test/java/io/socket/parser/ByteArrayTest.java @@ -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."); } }); diff --git a/src/test/java/io/socket/parser/Helpers.java b/src/test/java/io/socket/parser/Helpers.java index d56188b..0a3d461 100644 --- a/src/test/java/io/socket/parser/Helpers.java +++ b/src/test/java/io/socket/parser/Helpers.java @@ -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 errorPacket = new Packet(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); diff --git a/src/test/java/io/socket/parser/ParserTest.java b/src/test/java/io/socket/parser/ParserTest.java index d7ff220..c13005c 100644 --- a/src/test/java/io/socket/parser/ParserTest.java +++ b/src/test/java/io/socket/parser/ParserTest.java @@ -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() {