From 18d145cced718c5903140713457969eee0a47623 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Fri, 24 Jul 2015 11:39:12 +0200 Subject: [PATCH] add websocket to webpages --- lib/daemon.py | 3 + lib/websockets.py | 142 +++++++++++++++++++++++++++++++++++++++++++++ lib/www/index.html | 19 +++++- 3 files changed, 161 insertions(+), 3 deletions(-) create mode 100644 lib/websockets.py diff --git a/lib/daemon.py b/lib/daemon.py index 74f8f8ce5..426a3b265 100644 --- a/lib/daemon.py +++ b/lib/daemon.py @@ -216,6 +216,9 @@ if __name__ == '__main__': util.set_verbosity(True) server = NetworkServer(config) server.start() + if config.get('websocket_server'): + import websockets + websockets.WebSocketServer(config, server).start() try: daemon_loop(server) except KeyboardInterrupt: diff --git a/lib/websockets.py b/lib/websockets.py new file mode 100644 index 000000000..e772e8442 --- /dev/null +++ b/lib/websockets.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python +# +# Electrum - lightweight Bitcoin client +# Copyright (C) 2015 Thomas Voegtlin +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import threading, Queue, os, json, time +from collections import defaultdict +try: + from SimpleWebSocketServer import WebSocket, SimpleSSLWebSocketServer +except ImportError: + print "install SimpleWebSocketServer" + sys.exit() + +import util, daemon + +request_queue = Queue.Queue() + +class ElectrumWebSocket(WebSocket): + + def handleMessage(self): + assert self.data[0:3] == 'id:' + print "message received", self.data + request_id = self.data[3:] + request_queue.put((self, request_id)) + + def handleConnected(self): + print "connected", self.address + + def handleClose(self): + print "closed", self.address + + + +class WsClientThread(daemon.ClientThread): + + def __init__(self, config, server): + util.DaemonThread.__init__(self) + self.server = server + self.config = config + self.response_queue = Queue.Queue() + self.server.add_client(self) + self.subscriptions = defaultdict(list) + self.sub_ws = defaultdict(list) + self.counter = 0 + + def make_request(self, request_id): + # read json file + rdir = self.config.get('requests_dir') + n = os.path.join(rdir, request_id + '.json') + with open(n) as f: + s = f.read() + d = json.loads(s) + addr = d.get('address') + amount = d.get('amount') + return addr, amount + + def reading_thread(self): + while self.is_running(): + try: + ws, request_id = request_queue.get() + except Queue.Empty: + continue + try: + addr, amount = self.make_request(request_id) + except: + continue + method = 'blockchain.address.subscribe' + params = [addr] + request = {'method':method, 'params':params, 'id':self.counter} + self.subscriptions[method].append(params) + self.sub_ws[self.counter] = ws, amount, request + self.counter += 1 + self.server.send_request(self, request) + + def run(self): + threading.Thread(target=self.reading_thread).start() + while self.is_running(): + try: + r = self.response_queue.get(timeout=0.1) + except Queue.Empty: + continue + id = r.get('id') + if id is None: + method = r.get('method') + params = r.get('params') + else: + ws, amount, rr = self.sub_ws[id] + method = rr.get('method') + params = rr.get('params') + + result = r.get('result') + + if method == 'blockchain.address.subscribe': + print 'response', r + if result is not None: + request = {'method':'blockchain.address.get_balance', 'params':params, 'id':self.counter} + self.server.send_request(self, request) + self.sub_ws[self.counter] = ws, amount, request + self.counter += 1 + + if r.get('method') == 'blockchain.address.get_balance': + print 'response', r + if not ws.closed: + if sum(result.values()) >=amount: + ws.sendMessage(unicode('paid')) + + self.server.remove_client(self) + + +class WebSocketServer(threading.Thread): + + def __init__(self, config, ns): + threading.Thread.__init__(self) + self.config = config + self.net_server = ns + self.daemon = True + + def run(self): + t = WsClientThread(self.config, self.net_server) + t.start() + + host = self.config.get('websocket_server') + port = self.config.get('websocket_port', 9999) + certfile = self.config.get('ssl_chain') + keyfile = self.config.get('ssl_privkey') + self.server = SimpleSSLWebSocketServer(host, port, ElectrumWebSocket, certfile, keyfile) + self.server.serveforever() + + diff --git a/lib/www/index.html b/lib/www/index.html index 15a294594..0bbc347e3 100644 --- a/lib/www/index.html +++ b/lib/www/index.html @@ -26,7 +26,7 @@ var id = getUrlParameter('id'); if (id) { var jqxhr = $.getJSON(id + ".json", function() { - console.log( "success" ); + console.log("getJSON:success"); }) .done( function(data) { new QRCode(document.getElementById("qrcode"), data.URI); @@ -50,17 +50,30 @@ if (id) { value: current }); if (current >= max) { - $("#container").html("This payment request has expired"); + $("#container").html("This invoice has expired"); } }; var interval = setInterval(update, 1000); }); }) .fail(function() { - console.log( "error fail" ); + console.log("error fail"); $("

").text("error").appendTo($("p#error")); }); }; + +var ws = new WebSocket("wss://electrum.org:9999/"); +ws.onopen = function() { + ws.send('id:' + id); +}; +ws.onmessage = function (evt) { + var received_msg = evt.data; + if(received_msg == 'paid'){ + $("#container").html("This invoice has been paid."); + } + else alert("Message is received:"+ received_msg); +}; +