URI: 
       tMade the jsonrpc code use a persistent connection - electrum-personal-server - Maximally lightweight electrum server for a single user
  HTML git clone https://git.parazyd.org/electrum-personal-server
   DIR Log
   DIR Files
   DIR Refs
   DIR README
       ---
   DIR commit 96b81af376581e3ce32317c8feb8d9e41357a185
   DIR parent 0d68b0827e7aac7c0202a1dc95d21a4353c6983e
  HTML Author: chris-belcher <chris-belcher@users.noreply.github.com>
       Date:   Tue, 20 Mar 2018 11:40:49 +0000
       
       Made the jsonrpc code use a persistent connection
       
       Diffstat:
         M jsonrpc.py                          |     125 ++++++++++++++++++++++++-------
       
       1 file changed, 97 insertions(+), 28 deletions(-)
       ---
   DIR diff --git a/jsonrpc.py b/jsonrpc.py
       t@@ -1,59 +1,128 @@
       -#jsonrpc.py from https://github.com/JoinMarket-Org/joinmarket/blob/master/joinmarket/jsonrpc.py
       -#copyright # Copyright (C) 2013,2015 by Daniel Kraft <d@domob.eu> and phelix / blockchained.com
       +# from https://github.com/JoinMarket-Org/joinmarket-clientserver/blob/py3/jmclient/jmclient/jsonrpc.py
       +
       +# Copyright (C) 2013,2015 by Daniel Kraft <d@domob.eu>
       +# Copyright (C) 2014 by phelix / blockchained.com
       +#
       +# Permission is hereby granted, free of charge, to any person obtaining a copy
       +# of this software and associated documentation files (the "Software"), to deal
       +# in the Software without restriction, including without limitation the rights
       +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       +# copies of the Software, and to permit persons to whom the Software is
       +# furnished to do so, subject to the following conditions:
       +#
       +# The above copyright notice and this permission notice shall be included in all
       +# copies or substantial portions of the Software.
       +#
       +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       +# SOFTWARE.
        
        import base64
       -import http.client
       +try:
       +    import http.client as httplib
       +except ImportError:
       +    import httplib
        import json
        
       +
        class JsonRpcError(Exception):
       +    """
       +    The called method returned an error in the JSON-RPC response.
       +    """
            def __init__(self, obj):
                self.code = obj["code"]
                self.message = obj["message"]
        
       -class JsonRpcConnectionError(JsonRpcError): pass
       +
       +class JsonRpcConnectionError(Exception):
       +    """
       +    Error thrown when the RPC connection itself failed.  This means
       +    that the server is either down or the connection settings
       +    are wrong.
       +    """
       +    pass
       +
        
        class JsonRpc(object):
       +    """
       +    Simple implementation of a JSON-RPC client that is used
       +    to connect to Bitcoin.
       +    """
            def __init__(self, host, port, user, password):
                self.host = host
                self.port = port
       -        self.authstr = "%s:%s" % (user, password)
       +        self.conn = httplib.HTTPConnection(self.host, self.port)
       +        self.authstr = bytes("%s:%s" % (user, password), "utf-8")
                self.queryId = 1
        
            def queryHTTP(self, obj):
       -        headers = {"User-Agent": "electrum-personal-server",
       +        """
       +        Send an appropriate HTTP query to the server.  The JSON-RPC
       +        request should be (as object) in 'obj'.  If the call succeeds,
       +        the resulting JSON object is returned.  In case of an error
       +        with the connection (not JSON-RPC itself), an exception is raised.
       +        """
       +        headers = {"User-Agent": "joinmarket",
                           "Content-Type": "application/json",
                           "Accept": "application/json"}
                headers["Authorization"] = "Basic %s" % base64.b64encode(
       -                                    self.authstr.encode()).decode()
       +            self.authstr).decode("utf-8")
                body = json.dumps(obj)
       -        try:
       -            conn = http.client.HTTPConnection(self.host, self.port)
       -            conn.request("POST", "", body, headers)
       -            response = conn.getresponse()
       -            if response.status == 401:
       -                conn.close()
       -                raise JsonRpcConnectionError(
       -                        "authentication for JSON-RPC failed")
       -            # All of the codes below are 'fine' from a JSON-RPC point of view.
       -            if response.status not in [200, 404, 500]:
       -                conn.close()
       -                raise JsonRpcConnectionError("unknown error in JSON-RPC")
       -            data = response.read()
       -            conn.close()
       -            return json.loads(data.decode())
       -        except JsonRpcConnectionError as exc:
       -            raise exc
       -        except Exception as exc:
       -            raise JsonRpcConnectionError("JSON-RPC connection failed. Err:" +
       -                                         repr(exc))
       +        while True:
       +            try:
       +                self.conn.request("POST", "", body, headers)
       +                response = self.conn.getresponse()
       +                if response.status == 401:
       +                    self.conn.close()
       +                    raise JsonRpcConnectionError(
       +                            "authentication for JSON-RPC failed")
       +                # All of below codes are 'fine' from a JSON-RPC point of view.
       +                if response.status not in [200, 404, 500]:
       +                    self.conn.close()
       +                    raise JsonRpcConnectionError("unknown error in JSON-RPC")
       +                data = response.read()
       +                return json.loads(data.decode("utf-8"))
       +            except JsonRpcConnectionError as exc:
       +                raise exc
       +            except httplib.BadStatusLine:
       +                return "CONNFAILURE"
       +            except Exception as exc:
       +                if str(exc) == "Connection reset by peer":
       +                    self.conn.connect()
       +                    continue
       +                else:
       +                    raise JsonRpcConnectionError("JSON-RPC connection failed" +
       +                        ". Err:" + repr(exc))
       +            break
        
            def call(self, method, params):
       +        """
       +        Call a method over JSON-RPC.
       +        """
                currentId = self.queryId
                self.queryId += 1
                request = {"method": method, "params": params, "id": currentId}
       -        response = self.queryHTTP(request)
       +        #query can fail from keepalive timeout; keep retrying if it does, up
       +        #to a reasonable limit, then raise (failure to access blockchain
       +        #is a critical failure). Note that a real failure to connect (e.g.
       +        #wrong port) is raised in queryHTTP directly.
       +        response_received = False
       +        for i in range(100):
       +            response = self.queryHTTP(request)
       +            if response != "CONNFAILURE":
       +                response_received = True
       +                break
       +            #Failure means keepalive timed out, just make a new one
       +            self.conn = httplib.HTTPConnection(self.host, self.port)
       +        if not response_received:
       +            raise JsonRpcConnectionError("Unable to connect over RPC")
                if response["id"] != currentId:
                    raise JsonRpcConnectionError("invalid id returned by query")
                if response["error"] is not None:
                    raise JsonRpcError(response["error"])
                return response["result"]
       +