PageRenderTime 58ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 0ms

/src/fusre/service/node/basenode.py

https://bitbucket.org/deeso/fusre
Python | 394 lines | 391 code | 0 blank | 3 comment | 1 complexity | 5a938659df70072b4fab850a6ef622b6 MD5 | raw file
  1. '''
  2. Copyright 2011 Adam Pridgen <adam.pridgen@thecoverofnight.com>
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. @author: Adam Pridgen <adam.pridgen@thecoverofnight.com>
  13. '''
  14. import json
  15. from socket import *
  16. from service.networking.message import Message
  17. from service.networking.asynchsocket import MessageSocket
  18. from threading import Thread, Lock, Timer
  19. from time import sleep
  20. from service.util.ThreadSafe import List
  21. from service.util import ThreadSafe
  22. import traceback
  23. import sys
  24. import base64
  25. import signal
  26. class BaseNode(object):
  27. '''
  28. classdocs
  29. '''
  30. def __init__(self, server = None, port = 19955, request_spacing = .5,
  31. consumer_timer = .5, allowed_pending = 10):
  32. '''
  33. Constructor
  34. server: host to connect to initially if the crawler is not waiting for a client to connect to it
  35. port: port of the server to connect too.
  36. listen_port: port to listen on for new crawler clients
  37. request_spacing: time interval in seconds between successive requests through tor
  38. consumer_timer: time interval for polling for new messages.
  39. allowed_pending: number of requests the client is permitted to service at any given time
  40. torport: port to run tor on
  41. torhost: host interface to run tor on.
  42. '''
  43. self._msg_socks = {}
  44. if not server is None and port is None:
  45. try:
  46. peer = (server, port)
  47. self._msg_socks[peer] = self.init_msg_sock(server, port, sock = None)
  48. except:
  49. tb = traceback.format_exc(sys.exc_info())
  50. print("Exception while trying to connect to a: %s"%str(tb))
  51. raise
  52. self._server_sock = self.get_server_socket(port = listen_port)
  53. self._server_sock.register_handler("accept", self.accept_server)
  54. self._pending_jobs = ThreadSafe.List()
  55. self._running_jobs = {}
  56. self._running_lock = Lock()
  57. self._allowed_pending = allowed_pending
  58. self._send_handlers = {}
  59. self._recv_handlers = {}
  60. self._recv_types = {}
  61. self._send_types = {}
  62. self._running_jobs = {}
  63. self._handlers = {}
  64. self._adjust_time = Lock()
  65. self._consumer_timer = None
  66. self._consumer_time = consumer_timer
  67. self._continue_consume = False
  68. self._request_spacing = request_spacing
  69. # TODO create a logical clock class
  70. self._logical_clock = 0
  71. self._clock_lock = threading.RLock()
  72. def update_clock(self, clock_val):
  73. self._clock_lock.acquire()
  74. if self._logical_clock < clock_val:
  75. self._logical_clock = clock_val
  76. self._clock_lock.release()
  77. return self._logical_clock
  78. def increment_clock(self):
  79. self._clock_lock.acquire()
  80. self._logical_clock += 1
  81. v = self._logical_clock
  82. self._clock_lock.release()
  83. return v
  84. def get_clock(self):
  85. return self._logical_clock
  86. def start_service(self):
  87. '''
  88. Start the service
  89. '''
  90. self.start_consumer()
  91. self._server_sock.start_accept()
  92. for peer in self._msg_socks:
  93. msg_sock = self._msg_socks[peer]
  94. msg_sock.start_recv()
  95. msg_sock.start_send()
  96. signal.signal(signal.SIGALRM, self.sigterm_handler)
  97. def start_consumer(self):
  98. '''
  99. Start the consumer thread
  100. '''
  101. self._adjust_time.acquire()
  102. try:
  103. self._continue_consume = True
  104. if self._consumer_timer is None:
  105. self._consumer_timer = Timer(self._consumer_time, self.consume_jobs)
  106. self._consumer_timer.start()
  107. finally:
  108. self._adjust_time.release()
  109. def stop_consumer(self):
  110. '''
  111. Stop the consumer thread
  112. '''
  113. self._adjust_time.acquire()
  114. try:
  115. self._continue_consume = False
  116. if not self._consumer_timer is None:
  117. self._consumer_timer.cancel()
  118. self._consumer_timer = None
  119. finally:
  120. self._adjust_time.release()
  121. def check_reset_consumer(self):
  122. '''
  123. Reset the polling for the consumer thread
  124. '''
  125. self._adjust_time.acquire()
  126. try:
  127. if self._continue_consume:
  128. self._consumer_timer = Timer(self._consumer_time, self.consume_jobs)
  129. self._consumer_timer.start()
  130. else:
  131. self._consumer_timer = None
  132. finally:
  133. self._adjust_time.release()
  134. def consume_jobs(self):
  135. '''
  136. Handle all the pending jobs upto the number of allowed pending requests. Also maintain a
  137. strict interval of time between each request
  138. '''
  139. #TODO consume the next task
  140. self.check_reset_consumer()
  141. def init_msg_sock(self, host, port, sock = None):
  142. '''
  143. Initialize a message socket and set the respective "recv" and "send" handlers. These
  144. handlers will be called by the socket when a message is recieved or after a message is
  145. send.
  146. host: host name or ip for the endpoint
  147. port: port of the endpoint
  148. sock: if the socket has already been initialized (e.g. client connects to us)
  149. use this rather than starting a new socket.
  150. '''
  151. msg_sock = MessageSocket(host, port, self.certfile, self.keyfile, sock = sock)
  152. msg_sock.register_handler('recv', self.recv_msg)
  153. msg_sock.register_handler('send', self.sent_msg)
  154. return msg_sock
  155. def sent_msg(self, msg, instance):
  156. '''
  157. handler for after a message is sent by the underlying layer
  158. msg: Message sent by the lower layer network abstraction
  159. instance: lower layer network abstraction that performed the operation
  160. '''
  161. pass
  162. def get_bound_socket(self, server, port, backlog = 10):
  163. '''
  164. create a new listener socket to recieve connections on
  165. server: name to listen on (note this is not currently configurable by this crawler)
  166. port: port on which the server can connect to
  167. backlog: number of sockets allowed to be around while waiting
  168. '''
  169. s = socket()
  170. s.bind((server, port))
  171. s.listen(backlog)
  172. return s
  173. def accept_server(self, sock, peer, instance):
  174. '''
  175. handler called when the underlying network inteface accepts a connection.
  176. sock: client socket that is recieved
  177. peer: peer that connected to the server
  178. instance: lower layer network abstraction that performed the operation
  179. '''
  180. msg_sock = self.init_msg_sock(peer[0], peer[1], sock)
  181. msg_sock.start_recv()
  182. msg_sock.start_send()
  183. self._msg_socks[peer] = msg_sock
  184. def recv_msg(self, instance):
  185. '''
  186. handler called when the underlying network inteface has a completed message that needs to be
  187. processed. The handler will parse the message determine the type and pass the message off to the
  188. handler that handles the particular message type.
  189. instance: lower layer network abstraction that performed the operation
  190. '''
  191. while instance.has_recv_msg():
  192. msg = instance.next_recv_msg()
  193. msg_type = msg.msg_type()
  194. src = instance.getpeername()
  195. if not msg_type in self._recv_handlers:
  196. print("Dont know how to handle this type of message: %d from %s"%(msg_type, str(instance.getpeername())))
  197. handler = self._recv_handlers[msg_type]
  198. handler(src, msg)
  199. def send(self, peer, msg):
  200. '''
  201. Send the given message off to the addressed peer.
  202. peer: endpoint to get the message
  203. msg: message to send
  204. '''
  205. if peer in self._msg_socks:
  206. self._msg_socks[peer].send_msg(msg, self)
  207. def register_send_handler(self, msg_type, code, handler):
  208. '''
  209. Register send handlers for a particular type of message
  210. msg_type: string name for the message type
  211. code: byte/integer code for the message type (e.g. network/machine interpretation)
  212. handler: function that will handle the message.
  213. Parameters that should be expected by the send handler should be: message, instance
  214. where message is the message being sent and the src message source.
  215. eg. handler(src, message)
  216. '''
  217. self._send_handlers[msg_type] = handler
  218. self._send_handlers[code] = handler
  219. self._send_types[code] = msg_type
  220. self._send_types[msg_type] = code
  221. def register_recv_handler(self, msg_type, code, handler):
  222. '''
  223. Register recv handlers for a particular type of message
  224. msg_type: string name for the message type
  225. code: byte/integer code for the message type (e.g. network/machine interpretation)
  226. handler: function that will handle the message.
  227. Parameters that should be expected by the send handler should be: src, message
  228. where message is the message being sent and the src message source.
  229. eg. handler(src, message)
  230. '''
  231. self._recv_handlers[msg_type] = handler
  232. self._recv_handlers[code] = handler
  233. self._recv_types[code] = msg_type
  234. self._recv_types[msg_type] = code
  235. def handle_msg(self, src, msg):
  236. '''
  237. Handler that gets called when a message needs to be processed
  238. '''
  239. pass
  240. def handle_job_failure(self, job, src, msg, failure_str = "Job failed to get sent"):
  241. '''
  242. Handler that gets called when a message failed
  243. '''
  244. pass
  245. def handle_job_completion(self, job, src, msg):
  246. '''
  247. Handler that gets called when a job is completed
  248. '''
  249. pass
  250. def add_msg_to_pending(self, src, msg):
  251. '''
  252. Add a pending job to the queue without for the consumer thread to process
  253. '''
  254. self._pending_jobs.append_ts((src, msg))
  255. def add_job_to_running(self, t, job, msg, src):
  256. '''
  257. Add running/completed jobs from the running jobs dictionary in a thread safe manner
  258. '''
  259. self._running_lock.acquire()
  260. self._running_jobs[job] = ((t, job, msg, src))
  261. self._running_lock.release()
  262. def remove_job_from_running(self, job):
  263. '''
  264. Remove running/completed jobs from the running jobs dictionary in a thread safe manner
  265. '''
  266. self._running_lock.acquire()
  267. if not job in self._running_jobs:
  268. self._running_lock.release()
  269. return None, None, None, None
  270. t, job, msg, src = self._running_jobs[job]
  271. self._running_lock.release()
  272. return t, job, msg, src
  273. def get_server_socket(self, host = '', port = 19995, backlog = 10):
  274. '''
  275. Get a server/listener socket for future clients to connect too for web request
  276. '''
  277. sock = socket()
  278. sock.bind((host, port))
  279. sock.listen(backlog)
  280. return MessageSocket(host, listen_port, self.certfile,
  281. self.keyfile, sock = sock,
  282. is_server = True)
  283. def sigterm_handler(self, signum, frame):
  284. '''
  285. Cleanly handle the sigterm signal
  286. '''
  287. print("Handling Sigterm Event")
  288. self.stop_service()
  289. def stop_service(self):
  290. '''
  291. Stop all message socket polling threads, the consumer threads, and the tor interface.
  292. '''
  293. try:
  294. self.stop_consumer()
  295. except:
  296. print "failed to stop consumer thread"
  297. for peer in self._msg_socks.keys():
  298. try:
  299. self._msg_socks[peer].shutdown()
  300. except:
  301. tb = traceback.format_exc(sys.exc_info())
  302. print ("failed to shutdown msg socket: %s"%(str( peer)))
  303. print ("%s"%(tb))
  304. try:
  305. self._server_sock.shutdown()
  306. except:
  307. tb = traceback.format_exc(sys.exc_info())
  308. print ("failed to shutdown server socket: %s"%(str( peer)))
  309. print ("%s"%(tb))