From 5884537431fa5a8a7effc28cb7113bfcb5c83482 Mon Sep 17 00:00:00 2001 From: Naoyuki Kanezawa Date: Tue, 8 Apr 2014 02:27:58 +0900 Subject: [PATCH] support binary --- .../nkzawa/socketio/client/Manager.java | 10 +- .../github/nkzawa/socketio/client/Socket.java | 20 ++-- .../github/nkzawa/socketio/parser/Binary.java | 96 +++++++++++++++++++ .../github/nkzawa/socketio/parser/Parser.java | 15 ++- .../nkzawa/socketio/parser/ParserTest.java | 55 +++++++++-- 5 files changed, 174 insertions(+), 22 deletions(-) create mode 100644 src/main/java/com/github/nkzawa/socketio/parser/Binary.java diff --git a/src/main/java/com/github/nkzawa/socketio/client/Manager.java b/src/main/java/com/github/nkzawa/socketio/client/Manager.java index c98a241..af0b321 100644 --- a/src/main/java/com/github/nkzawa/socketio/client/Manager.java +++ b/src/main/java/com/github/nkzawa/socketio/client/Manager.java @@ -348,9 +348,13 @@ public class Manager extends Emitter { self.encoding = true; this.encoder.encode(packet, new Parser.Encoder.Callback() { @Override - public void call(String[] encodedPackets) { - for (int i = 0; i < encodedPackets.length; i++) { - self.engine.write(encodedPackets[i]); + public void call(Object[] encodedPackets) { + for (Object packet : encodedPackets) { + if (packet instanceof String) { + self.engine.write((String)packet); + } else if (packet instanceof byte[]) { + self.engine.write((byte[])packet); + } } self.encoding = false; self.processPacketQueue(); diff --git a/src/main/java/com/github/nkzawa/socketio/client/Socket.java b/src/main/java/com/github/nkzawa/socketio/client/Socket.java index 07090de..b595c77 100644 --- a/src/main/java/com/github/nkzawa/socketio/client/Socket.java +++ b/src/main/java/com/github/nkzawa/socketio/client/Socket.java @@ -134,26 +134,26 @@ public class Socket extends Emitter { * @return a reference to this object. */ @Override - public Emitter emit(final String event, final Object... arguments) { + public Emitter emit(final String event, final Object... args) { EventThread.exec(new Runnable() { @Override public void run() { if (events.containsKey(event)) { - Socket.super.emit(event, arguments); + Socket.super.emit(event, args); return; } - List args = new ArrayList(arguments.length + 1); - args.add(event); - args.addAll(Arrays.asList(arguments)); - JSONArray _args = new JSONArray(args); + List _args = new ArrayList(args.length + 1); + _args.add(event); + _args.addAll(Arrays.asList(args)); + JSONArray jsonArgs = new JSONArray(_args); int parserType = Parser.EVENT; - if (HasBinaryData.hasBinary(_args)) { parserType = Parser.BINARY_EVENT; } - Packet packet = new Packet(parserType, _args); + if (HasBinaryData.hasBinary(jsonArgs)) { parserType = Parser.BINARY_EVENT; } + Packet packet = new Packet(parserType, jsonArgs); - if (args.get(args.size() - 1) instanceof Ack) { + if (_args.get(_args.size() - 1) instanceof Ack) { logger.fine(String.format("emitting packet with ack id %d", Socket.this.ids)); - Socket.this.acks.put(Socket.this.ids, (Ack)args.remove(args.size() - 1)); + Socket.this.acks.put(Socket.this.ids, (Ack)_args.remove(_args.size() - 1)); packet.id = Socket.this.ids++; } diff --git a/src/main/java/com/github/nkzawa/socketio/parser/Binary.java b/src/main/java/com/github/nkzawa/socketio/parser/Binary.java new file mode 100644 index 0000000..3be23f7 --- /dev/null +++ b/src/main/java/com/github/nkzawa/socketio/parser/Binary.java @@ -0,0 +1,96 @@ +package com.github.nkzawa.socketio.parser; + +import org.json.JSONArray; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public class Binary { + + private static final String KEY_PLACEHOLDER = "_placeholder"; + + private static final String KEY_NUM = "num"; + + + public static DeconstructedPacket deconstructPacket(Packet packet) { + List buffers = new ArrayList(); + + packet.data = deconstructBinPackRecursive(packet.data, buffers); + packet.attachments = buffers.size(); + + DeconstructedPacket result = new DeconstructedPacket(); + result.packet = packet; + result.buffers = buffers.toArray(new byte[buffers.size()][]); + return result; + } + + private static Object deconstructBinPackRecursive(Object data, List buffers) { + if (data == null) return null; + + if (data instanceof byte[]) { + JSONObject placeholder = new JSONObject(); + placeholder.put(KEY_PLACEHOLDER, true); + placeholder.put(KEY_NUM, buffers.size()); + buffers.add((byte[])data); + return placeholder; + } else if (data instanceof JSONArray) { + JSONArray newData = new JSONArray(); + JSONArray _data = (JSONArray)data; + int len = _data.length(); + for (int i = 0; i < len; i ++) { + newData.put(i, deconstructBinPackRecursive(_data.get(i), buffers)); + } + return newData; + } else if (data instanceof JSONObject) { + JSONObject newData = new JSONObject(); + JSONObject _data = (JSONObject)data; + Iterator iterator = _data.keys(); + while (iterator.hasNext()) { + String key = (String)iterator.next(); + newData.put(key, deconstructBinPackRecursive(_data.get(key), buffers)); + } + return newData; + } + return data; + } + + public static Packet reconstructPacket(Packet packet, byte[][] buffers) { + packet.data = reconstructBinPackRecursive(packet.data, buffers); + packet.attachments = -1; + return packet; + } + + private static Object reconstructBinPackRecursive(Object data, byte[][] buffers) { + if (data instanceof JSONArray) { + JSONArray _data = (JSONArray)data; + int len = _data.length(); + for (int i = 0; i < len; i ++) { + _data.put(i, reconstructBinPackRecursive(_data.get(i), buffers)); + } + return _data; + } else if (data instanceof JSONObject) { + JSONObject _data = (JSONObject)data; + if (_data.optBoolean(KEY_PLACEHOLDER)) { + int num = _data.optInt(KEY_NUM, -1); + return num >= 0 && num < buffers.length ? buffers[num] : null; + } + Iterator iterator = _data.keys(); + while (iterator.hasNext()) { + String key = (String)iterator.next(); + _data.put(key, reconstructBinPackRecursive(_data.get(key), buffers)); + } + return _data; + } + return data; + } + + public static class DeconstructedPacket { + + public Packet packet; + public byte[][] buffers; + } +} + + diff --git a/src/main/java/com/github/nkzawa/socketio/parser/Parser.java b/src/main/java/com/github/nkzawa/socketio/parser/Parser.java index 806ec1c..98bde5d 100644 --- a/src/main/java/com/github/nkzawa/socketio/parser/Parser.java +++ b/src/main/java/com/github/nkzawa/socketio/parser/Parser.java @@ -5,6 +5,7 @@ import org.json.JSONException; import org.json.JSONTokener; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.logging.Logger; @@ -113,12 +114,17 @@ public class Parser { } private void encodeAsBinary(Packet obj, Callback callback) { - // TODO + 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 interface Callback { - public void call(String[] data); + public void call(Object[] data); } } @@ -242,7 +248,10 @@ public class Parser { public Packet takeBinaryData(byte[] binData) { this.buffers.add(binData); if (this.buffers.size() == this.reconPack.attachments) { - // TODO: + Packet packet = Binary.reconstructPacket(this.reconPack, + this.buffers.toArray(new byte[this.buffers.size()][])); + this.finishReconstruction(); + return packet; } return null; } diff --git a/src/test/java/com/github/nkzawa/socketio/parser/ParserTest.java b/src/test/java/com/github/nkzawa/socketio/parser/ParserTest.java index a637587..c76e68f 100644 --- a/src/test/java/com/github/nkzawa/socketio/parser/ParserTest.java +++ b/src/test/java/com/github/nkzawa/socketio/parser/ParserTest.java @@ -6,6 +6,8 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import java.nio.charset.Charset; + import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; @@ -16,21 +18,21 @@ public class ParserTest { @Test - public void connect() { + public void encodeConnection() { Packet packet = new Packet(Parser.CONNECT); packet.nsp = "/woot"; test(packet); } @Test - public void disconnect() { + public void encodeDisconnect() { Packet packet = new Packet(Parser.DISCONNECT); packet.nsp = "/woot"; test(packet); } @Test - public void event() { + public void encodeEvent() { Packet packet1 = new Packet(Parser.EVENT); packet1.data = new JSONTokener("[\"a\", 1, {}]").nextValue(); packet1.nsp = "/"; @@ -43,7 +45,7 @@ public class ParserTest { } @Test - public void ack() { + public void encodeAck() { Packet packet = new Packet(Parser.ACK); packet.data = new JSONTokener("[\"a\", 1, {}]").nextValue(); packet.id = 123; @@ -51,10 +53,19 @@ public class ParserTest { test(packet); } + @Test + public void encodeBytes() { + Packet packet = new Packet(Parser.BINARY_EVENT); + packet.data = "abc".getBytes(Charset.forName("UTF-8")); + packet.id = 23; + packet.nsp = "/cool"; + testBin(packet); + } + private void test(final Packet obj) { encoder.encode(obj, new Parser.Encoder.Callback() { @Override - public void call(String[] encodedPackets) { + public void call(Object[] encodedPackets) { Parser.Decoder decoder = new Parser.Decoder(); decoder.on(Parser.Decoder.EVENT_DECODED, new Emitter.Listener() { @Override @@ -71,7 +82,39 @@ public class ParserTest { assertThat(packet.attachments, is(obj.attachments)); } }); - decoder.add(encodedPackets[0]); + decoder.add((String)encodedPackets[0]); + } + }); + } + + private void testBin(final Packet obj) { + final Object originalData = obj.data; + 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() { + @Override + public void call(Object... args) { + Packet packet = (Packet)args[0]; + obj.data = originalData; + obj.attachments = -1; + + assertThat(packet.type, is(obj.type)); + assertThat(packet.id, is(obj.id)); + assertThat(packet.data, is(obj.data)); + assertThat(packet.nsp, is(obj.nsp)); + assertThat(packet.attachments, is(obj.attachments)); + } + }); + + for (Object packet : encodedPackets) { + if (packet instanceof String) { + decoder.add((String)packet); + } else if (packet instanceof byte[]) { + decoder.add((byte[])packet); + } + } } }); }