From bd6e3bfe6a684a4db5f403da913dbbd00f7e4db2 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 5 Dec 2018 15:35:02 +0100 Subject: [PATCH] pylightning: Add a compatibility mode for pre-\n\n versions We inadvertently broke the compatibility between the python library and the binary when switching to \n\n-delimiters. This reintroduces the old inefficient parsing, and dynamically upgrades to the faster version if it detects the \n\n-delimiter. Signed-off-by: Christian Decker --- CHANGELOG.md | 1 + contrib/pylightning/lightning/lightning.py | 29 +++++++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b17287f0..c64a22087 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - JSON API: `pay` and `decodepay` accept and ignore `lightning:` prefixes. - pylightning: Allow either keyword arguments or positional arguments. +- JSON-RPC: messages are now separated by 2 consecutive newlines. The pylightning client library has temporary support for both separators, but will eventually drop the single newline separator, which may cause it to stop working on older clients (see #2135 for details). ### Deprecated diff --git a/contrib/pylightning/lightning/lightning.py b/contrib/pylightning/lightning/lightning.py index 61cd88996..aaefc5165 100644 --- a/contrib/pylightning/lightning/lightning.py +++ b/contrib/pylightning/lightning/lightning.py @@ -20,11 +20,38 @@ class UnixDomainSocketRpc(object): self.executor = executor self.logger = logger + # Do we require the compatibility mode? + self._compat = True + @staticmethod def _writeobj(sock, obj): s = json.dumps(obj) sock.sendall(bytearray(s, 'UTF-8')) + def _readobj_compat(self, sock, buff=b''): + if not self._compat: + return self._readobj(sock, buff) + while True: + try: + b = sock.recv(1024) + buff += b + + if b'\n\n' in buff: + # The next read will use the non-compatible read instead + self._compat = False + + if len(b) == 0: + return {'error': 'Connection to RPC server lost.'} + if b' }\n' not in buff: + continue + # Convert late to UTF-8 so glyphs split across recvs do not + # impact us + objs, len_used = self.decoder.raw_decode(buff.decode("UTF-8")) + return objs, buff[len_used:].lstrip() + except ValueError: + # Probably didn't read enough + pass + def _readobj(self, sock, buff=b''): """Read a JSON object, starting with buff; returns object and any buffer left over""" while True: @@ -74,7 +101,7 @@ class UnixDomainSocketRpc(object): "params": payload, "id": 0 }) - resp, _ = self._readobj(sock) + resp, _ = self._readobj_compat(sock) sock.close() self.logger.debug("Received response for %s call: %r", method, resp)