def watch(self, interval, handler, evtype=None): """ Request to receive notice of subscribe/unsubscribe events for a given event type. (Currently there is no option to also select on subscriber.) Returns a Deferred status value is returned indicating the outcome of the subscribe operation - StatusVal.SUBSCRIBED indicates a successful outcome. HACK: for subscription notification events, the subscribed event type is treated as the source for subscription. This avoids having to keep a separate table for watchers. """ Trace("%s watch %us, handler %s to %s"%(self.getUri(), interval, str(handler), evtype), context="EventLib.EventPubSub") sts = self.subscribe(interval, handler, evtype=URI.EventSubscribeType, source=evtype) if sts.syncValue() == StatusVal.SUBSCRIBED: # Scan existing subscriptions for immediate watch events Trace("- scanning subscriptions", context="EventLib.EventPubSub") event = makeEvent(evtype=URI.EventSubscribeType, source=evtype) for (t,s,h) in self._sub.iterateWild(evtype, None): Trace("- subscribed (%s,%s,%s)"%(t,s,h), context="EventLib.EventPubSub") handler.handleEvent(event) pass return sts
def makeSubscribeEvent(subtype, subscriber, interval, evtype, source): """ Return an event object to convey the supplied subscription details. interval=0 for unsubscribe. """ payload = [interval, evtype, source] return makeEvent(subtype, subscriber, payload)
def close(self): """ Shut down the event router thread """ # 'EventCloseType' is a private event type used to shut down the event router, # which is otherwise ignored. self._closing = True self._queue.put( (self.getUri(), makeEvent(evtype=EventCloseType, source=self.getUri())) ) self._thread.join() return
def do_POST(self): """ The client is forwarding an event or subscription request: it needs to be processed for the underlying local event router. """ router = self.getRelay() msgbody = "" l = int(self.headers.get("content-length", "0")) if l: msgbody = self.rfile.read(l) Info("%s do_POST: '%s'" % (self.getRelay().getUri(), msgbody), "EventLib.EventRelayHTTPS") # Parse message and act accordingly msgdata = parseMessageData(msgbody) if msgdata == None: self.send_error(400, "Request body malformed") return if msgdata[0] == "forward": # msgdata = ["forward", [['R1', 'R2', 'R3'], 'ev:typ', 'ev:src', 'payload']] event = makeEvent(evtype=msgdata[1][1], source=msgdata[1][2], payload=msgdata[1][3]) env = constructEnvelope(msgdata[1][0], event) self.getRelay().forward(event, env) elif msgdata[0] == "idle": self.send_response(200, "OK idle") self.end_headers() return elif msgdata[0] == "closedown": self.send_response(200, "OK closedown") self.end_headers() return else: self.send_error(400, "Request body unrecognized option: " + msgdata[0]) return # Complete request with success response self.send_response(200, "OK") self.end_headers()
def do_POST(self): """ The client is forwarding an event or subscription request: it needs to be processed for the underlying local event router. """ router = self.getRelay() msgbody = "" l = int(self.headers.get("content-length","0")) if l: msgbody = self.rfile.read(l) Info("%s do_POST: '%s'"%(self.getRelay().getUri(),msgbody), "EventLib.EventRelayHTTPS") # Parse message and act accordingly msgdata = parseMessageData(msgbody) if msgdata == None: self.send_error(400, "Request body malformed") return if msgdata[0] == "forward": # msgdata = ["forward", [['R1', 'R2', 'R3'], 'ev:typ', 'ev:src', 'payload']] event = makeEvent(evtype=msgdata[1][1],source=msgdata[1][2],payload=msgdata[1][3]) env = constructEnvelope(msgdata[1][0], event) self.getRelay().forward(event, env) elif msgdata[0] == "idle": self.send_response(200, "OK idle") self.end_headers() return elif msgdata[0] == "closedown": self.send_response(200, "OK closedown") self.end_headers() return else: self.send_error(400, "Request body unrecognized option: "+msgdata[0]) return # Complete request with success response self.send_response(200, "OK") self.end_headers()
def processEvent(self): """ This function is the HTTP client worker thread. """ # Note: break out of event dispatch loop when closedown event is received # and closing flag is set. This is to prevent DoS attack by faked closedown # event type, and to ensure that prior events received are all processed. delay_on_error_min = 0.125 # Back off retry interval on error.. delay_on_error_max = 20.0 # .. delay_on_error = delay_on_error_min # .. while True: if delay_on_error < delay_on_error_max: delay_on_error *= 2 try: if not self._queue.empty(): Trace("%s queue.get ..." % (self.getUri()), "EventLib.EventRelayHTTPC") ###msgbody = self._queue.get() ###Trace("%s get msgbody: %s"%(self.getUri(),msgbody), "EventLib.EventRelayHTTPC") ###self._event.set() msgbody = self.getQueuedItem() [typ, env] = msgbody if typ == "closedown": if self._closing: break else: # process request as an HTTP POST request data = makeEnvelopeData(env) headers = { "Content-type": "text/plain", "Accept": "text/plain", "Content-length": str(len(data)) } self._httpcon.request("POST", "/request_path_ignored", data, headers) response = self._httpcon.getresponse() delay_on_error = delay_on_error_min else: # Nothing in queue: # issue a GET for incoming events Trace("%s HTTP get ..." % (self.getUri()), "EventLib.EventRelayHTTPC") headers = {"Accept": "text/plain"} self._httpcon.request("GET", "/request_path_ignored", None, headers) response = self._httpcon.getresponse() if response.status == 200: delay_on_error = delay_on_error_min msgbody = response.read() Trace("%s get msgbody: %s" % (self.getUri(), msgbody), "EventLib.EventRelayHTTPC") # Parse message and act accordingly msgdata = parseMessageData(msgbody) Trace( "%s get msgdata: %s" % (self.getUri(), str(msgdata)), "EventLib.EventRelayHTTPC") if msgdata == None: #TODO: Log "Request body malformed" pass elif msgdata[0] == "forward": # msgdata = ["forward", [['R1', 'R2', 'R3'], 'ev:typ', 'ev:src', 'payload']] event = makeEvent(evtype=msgdata[1][1], source=msgdata[1][2], payload=msgdata[1][3]) env = constructEnvelope(msgdata[1][0], event) self.forward(event, env) elif msgdata[0] == "idle": # Idle response gives client a chance to send if anything is queued pass else: #TODO: handle closedown message? Warn( "%s Request body unrecognized option: %s" % (self.getUri(), msgdata[0]), "EventRelayHTTPC") pass elif response.status == 503: Trace( "%s processEvent error response: %u, %s" % (self.getUri(), response.status, response.reason), "EventLib.EventRelayHTTPC") # Remote end closed down break else: # TODO: (log error response) Warn( "%s processEvent error response: %u, %s" % (self.getUri(), response.status, response.reason), "EventLib.EventRelayHTTPC") time.sleep(delay_on_error) except httplib.BadStatusLine, e: # This can happen at closedown Info( "%s processEvent bad response: %s" % (self.getUri(), str(e)), "EventLib.EventRelayHTTPC") time.sleep(delay_on_error) except httplib.CannotSendRequest, e: # This can happen at closedown Info("%s Cannot send request: %s" % (self.getUri(), str(e)), "EventLib.EventRelayHTTPC") time.sleep(delay_on_error)
def processEvent(self): """ This function is the HTTP client worker thread. """ # Note: break out of event dispatch loop when closedown event is received # and closing flag is set. This is to prevent DoS attack by faked closedown # event type, and to ensure that prior events received are all processed. delay_on_error_min = 0.125 # Back off retry interval on error.. delay_on_error_max = 20.0 # .. delay_on_error = delay_on_error_min # .. while True: if delay_on_error < delay_on_error_max: delay_on_error *= 2 try: # PLEASE NOTE: In the event that the HTTPC is run as duplex, not simplex # then the post methods will be delayed if nothing is sent down to the client # from the server. This timeout is controlled by QUEUE_WAIT_TIMEOUT in EventRouterHTTPS.py if self._simplex == True: self._queueEvent.wait() self._queueEvent.clear() if not self._queue.empty(): Trace("%s queue.get ..."%(self.getUri()), "EventLib.EventRelayHTTPC") ###msgbody = self._queue.get() ###Trace("%s get msgbody: %s"%(self.getUri(),msgbody), "EventLib.EventRelayHTTPC") ###self._event.set() msgbody = self.getQueuedItem() [typ,env] = msgbody if typ == "closedown": if self._closing: break else: # process request as an HTTP POST request data = makeEnvelopeData(env) headers = { "Content-type": "text/plain", "Accept": "text/plain", "Content-length": str(len(data)) } self._httpcon.request("POST", "/request_path_ignored", data, headers) response = self._httpcon.getresponse() delay_on_error = delay_on_error_min elif self._simplex == False: # Nothing in queue: # issue a GET for incoming events _log.info("%s HTTP get ..."%(self.getUri())) headers = { "Accept": "text/plain" } self._httpcon.request("GET", "/request_path_ignored", None, headers) response = self._httpcon.getresponse() if response.status == 200: delay_on_error = delay_on_error_min msgbody = response.read() Trace("%s get msgbody: %s"%(self.getUri(),msgbody), "EventLib.EventRelayHTTPC") # Parse message and act accordingly msgdata = parseMessageData(msgbody) Trace("%s get msgdata: %s"%(self.getUri(),str(msgdata)), "EventLib.EventRelayHTTPC") if msgdata == None: #TODO: Log "Request body malformed" pass elif msgdata[0] == "forward": # msgdata = ["forward", [['R1', 'R2', 'R3'], 'ev:typ', 'ev:src', 'payload']] event = makeEvent(evtype=msgdata[1][1],source=msgdata[1][2],payload=msgdata[1][3]) env = constructEnvelope(msgdata[1][0], event) self.forward(event, env) elif msgdata[0] == "idle": # Idle response gives client a chance to send if anything is queued pass else: #TODO: handle closedown message? Warn( "%s Request body unrecognized option: %s"%(self.getUri(),msgdata[0]), "EventRelayHTTPC") pass elif response.status == 503: Trace( "%s processEvent error response: %u, %s"%(self.getUri(),response.status,response.reason), "EventLib.EventRelayHTTPC") # Remote end closed down break else: # TODO: (log error response) Warn( "%s processEvent error response: %u, %s"%(self.getUri(),response.status,response.reason), "EventLib.EventRelayHTTPC") time.sleep(delay_on_error) except httplib.BadStatusLine, e: # This can happen at closedown Info( "%s processEvent bad response: %s"%(self.getUri(), str(e)), "EventLib.EventRelayHTTPC") time.sleep(delay_on_error) except httplib.CannotSendRequest, e: # This can happen at closedown Info( "%s Cannot send request: %s"%(self.getUri(), str(e)), "EventLib.EventRelayHTTPC") time.sleep(delay_on_error)