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)