loleaflet/src/core/Socket.js     |   18 +++++++++++++++++-
 loolwsd/LOOLProtocol.cpp         |   21 +++++++++++++++++++++
 loolwsd/LOOLProtocol.hpp         |   16 ++++++++++++++++
 loolwsd/LoadTest.cpp             |   16 ++++++++++++++++
 loolwsd/MasterProcessSession.cpp |   14 ++++++++++++++
 loolwsd/protocol.txt             |   20 ++++++++++++++++++++
 6 files changed, 104 insertions(+), 1 deletion(-)

New commits:
commit 2d385d697e2bced5a23c75aa5bcda43cafc33e61
Author: Ashod Nakashian <ashod.nakash...@collabora.co.uk>
Date:   Wed Jan 6 12:00:44 2016 -0500

    Protocol versioning added and documented
    
    Change-Id: I6e1df89c7330052bd2d442a42c0b24c8ae4facf6
    Reviewed-on: https://gerrit.libreoffice.org/21168
    Reviewed-by: Ashod Nakashian <ashnak...@gmail.com>
    Tested-by: Ashod Nakashian <ashnak...@gmail.com>

diff --git a/loleaflet/src/core/Socket.js b/loleaflet/src/core/Socket.js
index e8d34ef..eaf73a4 100644
--- a/loleaflet/src/core/Socket.js
+++ b/loleaflet/src/core/Socket.js
@@ -3,6 +3,8 @@
  */
 
 L.Socket = {
+       ProtocolVersionNumber: '0.1',
+
        connect: function (map) {
                try {
                        this.socket = new WebSocket(map.options.server);
@@ -44,6 +46,10 @@ L.Socket = {
        },
 
        _onOpen: function () {
+               // Always send the protocol version number.
+               // TODO: Move the version number somewhere sensible.
+               this.socket.send('loolclient ' + this.ProtocolVersionNumber);
+
                var msg = 'load url=' + this._map.options.doc;
                if (this._map._docLayer) {
                        // we are reconnecting after a lost connection
@@ -85,7 +91,17 @@ L.Socket = {
                        textMsg = String.fromCharCode.apply(null, 
imgBytes.subarray(0, index));
                }
 
-               if (!textMsg.startsWith('tile:') && 
!textMsg.startsWith('renderfont:')) {
+               if (textMsg.startsWith('loolserver ')) {
+                       // This must be the first message.
+                       if (this._map._docLayer) {
+                               this.fire('error', {msg: 'Unexpected loolserver 
message.'});
+                       }
+                       // TODO: For now we expect perfect match.
+                       if (textMsg.substring(11) !== 
this.ProtocolVersionNumber) {
+                               this.fire('error', {msg: 'Unsupported server 
version.'});
+                       }
+               }
+               else if (!textMsg.startsWith('tile:') && 
!textMsg.startsWith('renderfont:')) {
                        // log the tile msg separately as we need the tile 
coordinates
                        L.Log.log(textMsg, L.INCOMING);
                        if (imgBytes !== undefined) {
diff --git a/loolwsd/LOOLProtocol.cpp b/loolwsd/LOOLProtocol.cpp
index c831abc..0d358c2 100644
--- a/loolwsd/LOOLProtocol.cpp
+++ b/loolwsd/LOOLProtocol.cpp
@@ -23,6 +23,27 @@ using Poco::StringTokenizer;
 
 namespace LOOLProtocol
 {
+    std::tuple<signed, signed, std::string> ParseVersion(const std::string& 
version)
+    {
+        signed major = -1;
+        signed minor = -1;
+        std::string patch;
+
+        StringTokenizer firstTokens(version, ".", 
StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM);
+        if (firstTokens.count() > 0)
+        {
+            major = std::stoi(firstTokens[0]);
+
+            StringTokenizer secondTokens(firstTokens[1], "-", 
StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM);
+            minor = std::stoi(secondTokens[0]);
+
+            if (secondTokens.count() > 1)
+                patch = secondTokens[1];
+        }
+
+        return std::make_tuple(major, minor, patch);
+    }
+
     bool getTokenInteger(const std::string& token, const std::string& name, 
int& value)
     {
         size_t nextIdx;
diff --git a/loolwsd/LOOLProtocol.hpp b/loolwsd/LOOLProtocol.hpp
index 42e82df..1478713 100644
--- a/loolwsd/LOOLProtocol.hpp
+++ b/loolwsd/LOOLProtocol.hpp
@@ -62,6 +62,22 @@ namespace LOOLProtocol
         TILE,
     };
 
+    // Protocol Version Number.
+    // See protocol.txt.
+    constexpr unsigned ProtocolMajorVersionNumber = 0;
+    constexpr unsigned ProtocolMinorVersionNumber = 1;
+
+    inline
+    std::string GetProtocolVersion()
+    {
+        return std::to_string(ProtocolMajorVersionNumber) + '.'
+             + std::to_string(ProtocolMinorVersionNumber);
+    }
+
+    // Parse a string into a version tuple.
+    // Negative numbers for error.
+    std::tuple<signed, signed, std::string> ParseVersion(const std::string& 
version);
+
     bool getTokenInteger(const std::string& token, const std::string& name, 
int& value);
     bool getTokenString(const std::string& token, const std::string& name, 
std::string& value);
     bool getTokenKeyword(const std::string& token, const std::string& name, 
const std::map<std::string, int>& map, int& value);
diff --git a/loolwsd/LoadTest.cpp b/loolwsd/LoadTest.cpp
index 4fdc245..aaa2e3e 100644
--- a/loolwsd/LoadTest.cpp
+++ b/loolwsd/LoadTest.cpp
@@ -115,6 +115,20 @@ public:
 #endif
                         response = getFirstLine(buffer, n);
                     }
+                    else if (tokens[0] == "loolclient")
+                    {
+                        const auto versionTuple = ParseVersion(tokens[1]);
+                        if (std::get<0>(versionTuple) != 
ProtocolMajorVersionNumber ||
+                            std::get<1>(versionTuple) != 
ProtocolMinorVersionNumber)
+                        {
+                            const std::string error = "error: cmd=loolclient 
kind=badversion";
+                            _ws.sendFrame(error.c_str(), error.size());
+                            break;
+                        }
+
+                        const auto version = "loolserver " + 
GetProtocolVersion();
+                        _ws.sendFrame(version.c_str(), version.size());
+                    }
                     if (response.find("status:") == 0)
                     {
                         parseStatus(response, _type, _numParts, _currentPart, 
_width, _height);
@@ -203,6 +217,8 @@ private:
 
         thread.start(output);
 
+        sendTextFrame(ws, "loolclient " + GetProtocolVersion());
+
         if (document[0] == '/')
             sendTextFrame(ws, "load " + document);
         else
diff --git a/loolwsd/MasterProcessSession.cpp b/loolwsd/MasterProcessSession.cpp
index dd66fc2..704b93a 100644
--- a/loolwsd/MasterProcessSession.cpp
+++ b/loolwsd/MasterProcessSession.cpp
@@ -81,6 +81,20 @@ bool MasterProcessSession::_handleInput(const char *buffer, 
int length)
     const std::string firstLine = getFirstLine(buffer, length);
     StringTokenizer tokens(firstLine, " ", StringTokenizer::TOK_IGNORE_EMPTY | 
StringTokenizer::TOK_TRIM);
 
+    if (tokens[0] == "loolclient")
+    {
+        const auto versionTuple = ParseVersion(tokens[1]);
+        if (std::get<0>(versionTuple) != ProtocolMajorVersionNumber ||
+            std::get<1>(versionTuple) != ProtocolMinorVersionNumber)
+        {
+            sendTextFrame("error: cmd=loolclient kind=badversion");
+            return false;
+        }
+
+        sendTextFrame("loolserver " + GetProtocolVersion());
+        return true;
+    }
+
     if (haveSeparateProcess())
     {
         // Note that this handles both forwarding requests from the client to 
the child process, and
diff --git a/loolwsd/protocol.txt b/loolwsd/protocol.txt
index e9cce86..81a069b 100644
--- a/loolwsd/protocol.txt
+++ b/loolwsd/protocol.txt
@@ -11,6 +11,16 @@ tiles proactively (guessing what the client might need). Etc.
 client -> server
 ================
 
+loolclient <major.minor[-patch]>
+
+    Upon connection, a client must announce the version number it supports.
+    Major: an integer that must always match between client and server,
+           otherwise there are no guarantees of any sensible
+           compatibility. This is bumped when API changes.
+    Minor: an integer is more flexible and is at the discretion of either 
party.
+           Security fixes that do not alter the API would bump the minor 
version number.
+    Patch: an optional string that is informational.
+
 canceltiles
 
     All outstanding tile messages from the client to the server are
@@ -109,6 +119,16 @@ partpagerectangles
 server -> client
 ================
 
+loolserver <major.minor[-patch]>
+
+    Upon connection, the server must announce the version number it supports.
+    Major: an integer that must always match between client and server,
+           otherwise there are no guarantees of any sensible
+           compatibility. This is bumped when API changes.
+    Minor: an integer is more flexible and is at the discretion of either 
party.
+           Security fixes that do not alter the API would bump the minor 
version number.
+    Patch: an optional string that is informational.
+
 downloadas: jail=<jail directory> dir=<a tmp dir> name=<name> port=<port>
 
     The client should then request http://server:port/jail/dir/name in order 
to download
_______________________________________________
Libreoffice-commits mailing list
libreoffice-comm...@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/libreoffice-commits

Reply via email to