3 # Electrum - lightweight Bitcoin client
4 # Copyright (C) 2014 Thomas Voegtlin
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
28 from network import Network
32 class NetworkProxy(threading.Thread):
34 # sends requests, runs callbacks
36 def __init__(self, config):
37 threading.Thread.__init__(self)
40 self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
41 self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
42 self.socket.connect(('', 8000))
44 self.unanswered_requests = {}
45 self.subscriptions = {}
47 self.lock = threading.Lock()
50 def parse_json(self, message):
51 s = message.find('\n')
54 j = json.loads( message[0:s] )
55 return j, message[s+1:]
59 # read responses and trigger callbacks
63 data = self.socket.recv(1024)
71 response, message = self.parse_json(message)
72 if response is not None:
73 self.process(response)
77 print "NetworkProxy: exiting"
80 def process(self, response):
82 #print "<--", response
84 msg_id = response.get('id')
86 method, params, callback = self.unanswered_requests.pop(msg_id)
88 result = response.get('result')
89 callback({'method':method, 'params':params, 'result':result, 'id':msg_id})
92 def send(self, messages, callback):
93 # detect if it is a subscription
95 if self.subscriptions.get(callback) is None:
96 self.subscriptions[callback] = []
97 for message in messages:
98 if message not in self.subscriptions[callback]:
99 self.subscriptions[callback].append(message)
101 self.do_send( messages, callback )
104 def do_send(self, messages, callback):
105 """return the ids of the requests that we sent"""
110 request = json.dumps( { 'id':self.message_id, 'method':method, 'params':params } )
111 self.unanswered_requests[self.message_id] = method, params, callback
112 ids.append(self.message_id)
113 # print "-->", request
115 out += request + '\n'
117 sent = self.socket.send( out )
122 def synchronous_get(self, requests, timeout=100000000):
123 queue = Queue.Queue()
124 ids = self.do_send(requests, queue.put)
128 r = queue.get(True, timeout)
132 res[_id] = r.get('result')
139 def get_servers(self):
140 return self.synchronous_get([('network.getservers',[])])[0]
143 return self.synchronous_get([('network.shutdown',[])])[0]
150 class ClientThread(threading.Thread):
151 # read messages from client (socket), and sends them to Network
152 # responses are sent back on the same socket
154 def __init__(self, server, network, socket):
155 threading.Thread.__init__(self)
159 self.s.settimeout(0.1)
160 self.network = network
161 self.queue = Queue.Queue()
162 self.unanswered_requests = {}
168 self.send_responses()
170 data = self.s.recv(1024)
171 except socket.timeout:
179 cmd, message = self.parse_json(message)
184 #print "client thread terminating"
187 def parse_json(self, message):
188 n = message.find('\n')
191 j = json.loads( message[0:n] )
192 return j, message[n+1:]
195 def process(self, request):
196 #print "<--", request
197 method = request['method']
198 params = request['params']
201 if method.startswith('network.'):
202 if method == 'network.shutdown':
203 self.server.running = False
204 r = {'id':_id, 'result':True}
205 elif method == 'network.getservers':
206 servers = self.network.get_servers()
207 r = {'id':_id, 'result':servers}
209 r = {'id':_id, 'error':'unknown method'}
216 my_id = self.unanswered_requests.pop(_id)
220 new_id = self.network.interface.send([(method, params)], cb) [0]
221 self.unanswered_requests[new_id] = _id
224 def send_responses(self):
227 r = self.queue.get_nowait()
230 out = json.dumps(r) + '\n'
238 # start network() object
239 # accept connections, forward requests
244 def __init__(self, config):
245 network = Network(config)
246 if not network.start(wait=True):
247 print_msg("Not connected, aborting.")
249 self.network = network
250 self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
251 self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
252 self.server.bind(('', 8000))
253 self.server.listen(5)
254 self.server.settimeout(1)
264 connection, address = self.server.accept()
265 except socket.timeout:
266 if time.time() - t > self.timeout:
270 client = ClientThread(self, self.network, connection)
277 def start_daemon(config):
279 if (pid == 0): # The first child.
284 if (pid2 == 0): # Second child
285 server = NetworkServer(config)
288 except KeyboardInterrupt:
289 print "Ctrl C - Stopping server"
294 # should use a signal
299 if __name__ == '__main__':
301 config = simple_config.SimpleConfig({'verbose':True, 'server':'ecdsa.net:50002:s'})
302 server = NetworkServer(config)
305 except KeyboardInterrupt:
306 print "Ctrl C - Stopping server"