working
authorNiki Roo <niki@nikiroo.be>
Sun, 28 Apr 2019 16:10:34 +0000 (18:10 +0200)
committerNiki Roo <niki@nikiroo.be>
Sun, 28 Apr 2019 16:10:34 +0000 (18:10 +0200)
src/be/nikiroo/utils/serial/Importer.java
src/be/nikiroo/utils/serial/SerialUtils.java
src/be/nikiroo/utils/serial/server/ConnectAction.java
src/be/nikiroo/utils/serial/server/ConnectActionServer.java
src/be/nikiroo/utils/serial/server/ServerObject.java
src/be/nikiroo/utils/test_code/CryptUtilsTest.java
src/be/nikiroo/utils/test_code/SerialServerTest.java

index 810d694db5a316a4aca1290b5b31650312005f0d..f7fece064d6028d8fca8ff5a742cf997f52f52b2 100644 (file)
@@ -171,12 +171,7 @@ public class Importer {
                        } else {
                                if (line.endsWith(":")) {
                                        // construct
-                                       try {
-                                               me = SerialUtils.createObject(type);
-                                       } catch (NoSuchMethodException e) {
-                                               System.out.println("LINE: <" + line + ">");
-                                               throw e;
-                                       }
+                                       me = SerialUtils.createObject(type);
                                } else {
                                        // direct value
                                        int pos = line.indexOf(":");
index 480990a5be8dd2b06c43b44145822ac0dfdc81b0..52bc43aae85c054d03466c2e171f2f1bbfd7c8f1 100644 (file)
@@ -514,7 +514,7 @@ public class SerialUtils {
                        if (e instanceof IOException) {
                                throw (IOException) e;
                        }
-                       throw new IOException(e.getMessage());
+                       throw new IOException(e.getMessage(), e);
                }
        }
 
index 66d43e0e757f1912c753796bd95ec9c28cf93f75..901632627b218d35469fcc7cb6b71f2af2b8144d 100644 (file)
@@ -1,6 +1,7 @@
 package be.nikiroo.utils.serial.server;
 
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.Socket;
 
@@ -10,8 +11,11 @@ import be.nikiroo.utils.CryptUtils;
 import be.nikiroo.utils.IOUtils;
 import be.nikiroo.utils.serial.Exporter;
 import be.nikiroo.utils.serial.Importer;
+import be.nikiroo.utils.streams.BufferedOutputStream;
 import be.nikiroo.utils.streams.NextableInputStream;
 import be.nikiroo.utils.streams.NextableInputStreamStep;
+import be.nikiroo.utils.streams.ReplaceInputStream;
+import be.nikiroo.utils.streams.ReplaceOutputStream;
 
 /**
  * Base class used for the client/server basic handling.
@@ -30,12 +34,9 @@ abstract class ConnectAction {
 
        private Object lock = new Object();
        private NextableInputStream in;
-       private OutputStream out;
+       private BufferedOutputStream out;
        private boolean contentToSend;
 
-       private long bytesReceived;
-       private long bytesSent;
-
        /**
         * Method that will be called when an action is performed on either the
         * client or server this {@link ConnectAction} represent.
@@ -80,7 +81,7 @@ abstract class ConnectAction {
         * @return the amount of bytes received
         */
        public long getBytesReceived() {
-               return bytesReceived;
+               return in.getBytesRead();
        }
 
        /**
@@ -88,8 +89,8 @@ abstract class ConnectAction {
         * 
         * @return the amount of bytes sent
         */
-       public long getBytesSent() {
-               return bytesSent;
+       public long getBytesWritten() {
+               return out.getBytesWritten();
        }
 
        /**
@@ -97,20 +98,20 @@ abstract class ConnectAction {
         */
        public void connect() {
                try {
+                       // TODO: assure that \b is never used, make sure \n usage is OK
                        in = new NextableInputStream(s.getInputStream(),
                                        new NextableInputStreamStep('\b'));
 
                        try {
-                               out = s.getOutputStream();
+                               out = new BufferedOutputStream(s.getOutputStream());
+
                                try {
                                        action();
                                } finally {
                                        out.close();
-                                       out = null;
                                }
                        } finally {
                                in.close();
-                               in = null;
                        }
                } catch (Exception e) {
                        onError(e);
@@ -148,25 +149,7 @@ abstract class ConnectAction {
         */
        protected Object sendObject(Object data) throws IOException,
                        NoSuchFieldException, NoSuchMethodException, ClassNotFoundException {
-               synchronized (lock) {
-
-                       new Exporter(out).append(data);
-                       out.write('\b');
-
-                       if (server) {
-                               out.flush();
-                               return null;
-                       }
-
-                       contentToSend = true;
-                       try {
-                               return recObject();
-                       } catch (NullPointerException e) {
-                               // We accept no data here
-                       }
-
-                       return null;
-               }
+               return send(out, data, false);
        }
 
        /**
@@ -196,22 +179,7 @@ abstract class ConnectAction {
        protected Object recObject() throws IOException, NoSuchFieldException,
                        NoSuchMethodException, ClassNotFoundException,
                        java.lang.NullPointerException {
-               synchronized (lock) {
-                       if (server || contentToSend) {
-                               if (contentToSend) {
-                                       out.flush();
-                                       contentToSend = false;
-                               }
-
-                               if (in.next()) {
-                                       return new Importer().read(in).getValue();
-                               }
-
-                               throw new NullPointerException();
-                       }
-
-                       return null;
-               }
+               return rec(false);
        }
 
        /**
@@ -230,17 +198,20 @@ abstract class ConnectAction {
         *             in case of crypt error
         */
        protected String sendString(String line) throws IOException {
-               synchronized (lock) {
-                       writeLine(out, line);
-
-                       if (server) {
-                               out.flush();
-                               return null;
-                       }
-
-                       contentToSend = true;
-                       return recString();
+               try {
+                       return (String) send(out, line, true);
+               } catch (NoSuchFieldException e) {
+                       // Cannot happen
+                       e.printStackTrace();
+               } catch (NoSuchMethodException e) {
+                       // Cannot happen
+                       e.printStackTrace();
+               } catch (ClassNotFoundException e) {
+                       // Cannot happen
+                       e.printStackTrace();
                }
+
+               return null;
        }
 
        /**
@@ -260,71 +231,177 @@ abstract class ConnectAction {
         *             in case of crypt error
         */
        protected String recString() throws IOException {
-               synchronized (lock) {
-                       if (server || contentToSend) {
-                               if (contentToSend) {
-                                       out.flush();
-                                       contentToSend = false;
-                               }
-
-                               return readLine(in);
-                       }
-
-                       return null;
+               try {
+                       return (String) rec(true);
+               } catch (NoSuchFieldException e) {
+                       // Cannot happen
+                       e.printStackTrace();
+               } catch (NoSuchMethodException e) {
+                       // Cannot happen
+                       e.printStackTrace();
+               } catch (ClassNotFoundException e) {
+                       // Cannot happen
+                       e.printStackTrace();
+               } catch (NullPointerException e) {
+                       // Should happen
+                       e.printStackTrace();
                }
+
+               return null;
        }
 
        /**
-        * Read a possibly encrypted line, or NULL if no more content.
-        * 
-        * @param in
-        *            the stream to read from
+        * Serialise and send the given object to the counter part (and, only for
+        * client, return the deserialised answer -- the server will always receive
+        * NULL).
         * 
-        * @return the unencrypted line or NULL
+        * @param out
+        *            the stream to write to
+        * @param data
+        *            the data to write
+        * @param asString
+        *            TRUE to write it as a String, FALSE to write it as an Object
         * 
+        * @return the answer (which can be NULL if no answer, or NULL for an answer
+        *         which is NULL) if this action is a client, always NULL if it is a
+        *         server
         * 
         * @throws IOException
         *             in case of I/O error
         * @throws SSLException
         *             in case of crypt error
+        * @throws IOException
+        *             in case of I/O error
+        * @throws NoSuchFieldException
+        *             if the serialised data contains information about a field
+        *             which does actually not exist in the class we know of
+        * @throws NoSuchMethodException
+        *             if a class described in the serialised data cannot be created
+        *             because it is not compatible with this code
+        * @throws ClassNotFoundException
+        *             if a class described in the serialised data cannot be found
         */
-       private String readLine(NextableInputStream in) throws IOException {
-               if (in.next()) {
-                       String line = IOUtils.readSmallStream(in);
-                       bytesReceived += line.length();
+       private Object send(BufferedOutputStream out, Object data, boolean asString)
+                       throws IOException, NoSuchFieldException, NoSuchMethodException,
+                       ClassNotFoundException, java.lang.NullPointerException {
+
+               synchronized (lock) {
+                       OutputStream sub;
                        if (crypt != null) {
-                               line = crypt.decrypt64s(line, false);
+                               sub = crypt.encrypt64(out.open(), false);
+                       } else {
+                               sub = out.open();
                        }
 
-                       return line;
-               }
+                       // TODO: could be possible to check for non-crypt and only
+                       // do it for crypt
+                       sub = new ReplaceOutputStream(sub, //
+                                       new String[] { "\\", "\b" }, //
+                                       new String[] { "\\\\", "\\b" });
 
-               return null;
+                       try {
+                               if (asString) {
+                                       sub.write(data.toString().getBytes("UTF-8"));
+                               } else {
+                                       new Exporter(sub).append(data);
+                               }
+                       } finally {
+                               sub.close();
+                       }
+
+                       out.write('\b');
+
+                       if (server) {
+                               out.flush();
+                               return null;
+                       }
+
+                       contentToSend = true;
+                       try {
+                               return rec(asString);
+                       } catch (NullPointerException e) {
+                               // We accept no data here for Objects
+                       }
+
+                       return null;
+               }
        }
 
        /**
-        * Write a line, possible encrypted.
+        * Reserved for the server: flush the data to the client and retrieve its
+        * answer.
+        * <p>
+        * Also used internally for the client (only do something if there is
+        * contentToSend).
+        * <p>
+        * Will only flush the data if there is contentToSend.
+        * <p>
+        * Note that the behaviour is slightly different for String and Object
+        * reading regarding exceptions:
+        * <ul>
+        * <li>NULL means that the counter part has no more data to send</li>
+        * <li>All the exceptions except {@link IOException} are there for Object
+        * conversion</li>
+        * </ul>
+        * 
+        * @param asString
+        *            TRUE for String reading, FALSE for Object reading (which can
+        *            still be a String)
+        * 
+        * @return the deserialised answer (which can actually be NULL)
         * 
-        * @param out
-        *            the stream to write to
-        * @param line
-        *            the line to write
         * @throws IOException
         *             in case of I/O error
-        * @throws SSLException
-        *             in case of crypt error
+        * @throws NoSuchFieldException
+        *             if the serialised data contains information about a field
+        *             which does actually not exist in the class we know of
+        * @throws NoSuchMethodException
+        *             if a class described in the serialised data cannot be created
+        *             because it is not compatible with this code
+        * @throws ClassNotFoundException
+        *             if a class described in the serialised data cannot be found
+        * @throws java.lang.NullPointerException
+        *             for Objects only: if the counter part has no data to send
         */
-       private void writeLine(OutputStream out, String line) throws IOException {
-               if (crypt == null) {
-                       out.write(line.getBytes("UTF-8"));
-                       bytesSent += line.length();
-               } else {
-                       // TODO: how NOT to create so many big Strings?
-                       String b64 = crypt.encrypt64(line, false);
-                       out.write(b64.getBytes("UTF-8"));
-                       bytesSent += b64.length();
+       private Object rec(boolean asString) throws IOException,
+                       NoSuchFieldException, NoSuchMethodException,
+                       ClassNotFoundException, java.lang.NullPointerException {
+
+               synchronized (lock) {
+                       if (server || contentToSend) {
+                               if (contentToSend) {
+                                       out.flush();
+                                       contentToSend = false;
+                               }
+
+                               if (in.next()) {
+                                       // TODO: could be possible to check for non-crypt and only
+                                       // do it for crypt
+                                       InputStream read = new ReplaceInputStream(in.open(), //
+                                                       new String[] { "\\\\", "\\b" }, //
+                                                       new String[] { "\\", "\b" });
+
+                                       try {
+                                               if (crypt != null) {
+                                                       read = crypt.decrypt64(read, false);
+                                               }
+
+                                               if (asString) {
+                                                       return IOUtils.readSmallStream(read);
+                                               }
+
+                                               return new Importer().read(read).getValue();
+                                       } finally {
+                                               read.close();
+                                       }
+                               }
+
+                               if (!asString) {
+                                       throw new NullPointerException();
+                               }
+                       }
+
+                       return null;
                }
-               out.write('\b');
-               bytesSent++;
        }
 }
\ No newline at end of file
index aadc06c67503c6aebfa9aae5dc7785c46fce9841..d0ddb92acd253cb5befda49760c5803e379e1688 100644 (file)
@@ -105,7 +105,7 @@ abstract class ConnectActionServer {
         * @return the amount of bytes sent
         */
        public long getBytesSent() {
-               return action.getBytesSent();
+               return action.getBytesWritten();
        }
 
        /**
index e1001d860647975b093b9250d763ef21c425e844..0315f90c47f74b19d557e6a029e065da89dd1911 100644 (file)
@@ -78,6 +78,7 @@ abstract public class ServerObject extends Server {
                                                } catch (Exception e) {
                                                        onError(e);
                                                }
+
                                                send(rep);
                                        }
                                } catch (NullPointerException e) {
index 61e18fbb49198ec36240d361a9d16d13a44af7c3..11578f5be88901f6555ee3d6cb9a2c5801d756c7 100644 (file)
@@ -135,15 +135,17 @@ class CryptUtilsTest extends TestLauncher {
                addTest(new TestCase("Simple test") {
                        @Override
                        public void test() throws Exception {
-                               InputStream in = new ByteArrayInputStream(new byte[] {42, 127, 12});
-                               crypt.encryptInputStream(in);
+                               InputStream in = new ByteArrayInputStream(new byte[] { 42, 127,
+                                               12 });
+                               crypt.encrypt(in);
                                ByteArrayOutputStream out = new ByteArrayOutputStream();
                                IOUtils.write(in, out);
                                byte[] result = out.toByteArray();
-                               
-                               assertEquals("We wrote 3 bytes, we expected 3 bytes back but got: "
-                                               + result.length, result.length, result.length);
-                               
+
+                               assertEquals(
+                                               "We wrote 3 bytes, we expected 3 bytes back but got: "
+                                                               + result.length, result.length, result.length);
+
                                assertEquals(42, result[0]);
                                assertEquals(127, result[1]);
                                assertEquals(12, result[2]);
index 44d26da5e9c438e3d1dfd5bfdc755c7262894f5a..9c346fd5829b880622dd05307513720f816213b3 100644 (file)
@@ -18,7 +18,6 @@ class SerialServerTest extends TestLauncher {
 
                for (String key : new String[] { null,
                                "some super secret encryption key" }) {
-                       // TODO: re-add bridge
                        for (boolean bridge : new Boolean[] { false, true }) {
                                final String skey = (key != null ? "(encrypted)"
                                                : "(plain text)");