def test_normal_pickling(self): c = SubmitSmRespContent(self.body, 1) self.assertNotEquals(c.body, self.body) self.assertEquals(c.body, pickle.dumps(self.body, 2)) self.assertEquals(c['message-id'], 1) self.assertTrue('created_at' in c['headers'])
def test_normal_nopickling(self): c = SubmitSmRespContent(self.body, 1, prePickle=False) self.assertEquals(c.body, self.body) self.assertEquals(c['message-id'], 1) self.assertTrue('created_at' in c['headers'])
def submit_sm_resp_event(self, r, amqpMessage): msgid = amqpMessage.content.properties['message-id'] total_bill_amount = None will_be_retried = False try: submit_sm_resp_bill = pickle.loads( amqpMessage.content.properties['headers'] ['submit_sm_bill']).getSubmitSmRespBill() if r.response.status == CommandStatus.ESME_ROK: # No more retrials ! del self.submit_retrials[msgid] # Get bill information total_bill_amount = 0.0 if submit_sm_resp_bill is not None and submit_sm_resp_bill.getTotalAmounts( ) > 0: total_bill_amount = submit_sm_resp_bill.getTotalAmounts() # UDH is set ? UDHI_INDICATOR_SET = False if hasattr(r.request.params['esm_class'], 'gsmFeatures'): for gsmFeature in r.request.params[ 'esm_class'].gsmFeatures: if str(gsmFeature) == 'UDHI_INDICATOR_SET': UDHI_INDICATOR_SET = True break # What type of splitting ? splitMethod = None if 'sar_msg_ref_num' in r.request.params: splitMethod = 'sar' elif UDHI_INDICATOR_SET and r.request.params[ 'short_message'][:3] == '\x05\x00\x03': splitMethod = 'udh' # Concatenate short_message if splitMethod is not None: _pdu = r.request if splitMethod == 'sar': short_message = _pdu.params['short_message'] else: short_message = _pdu.params['short_message'][6:] while hasattr(_pdu, 'nextPdu'): _pdu = _pdu.nextPdu if splitMethod == 'sar': short_message += _pdu.params['short_message'] else: short_message += _pdu.params['short_message'][6:] # Increase bill amount for each submit_sm_resp if submit_sm_resp_bill is not None and submit_sm_resp_bill.getTotalAmounts( ) > 0: total_bill_amount += submit_sm_resp_bill.getTotalAmounts( ) else: short_message = r.request.params['short_message'] # Do not log text for privacy reasons # Added in #691 if self.config.log_privacy: logged_content = '** %s byte content **' % len( short_message) else: logged_content = '%r' % short_message self.log.info( "SMS-MT [cid:%s] [queue-msgid:%s] [smpp-msgid:%s] [status:%s] [prio:%s] [dlr:%s] [validity:%s] \ [from:%s] [to:%s] [content:%s]", self.SMPPClientFactory.config.id, msgid, r.response.params['message_id'], r.response.status, amqpMessage.content.properties['priority'], r.request.params['registered_delivery'].receipt, 'none' if ('headers' not in amqpMessage.content.properties or 'expiration' not in amqpMessage.content.properties['headers']) else amqpMessage.content.properties['headers']['expiration'], r.request.params['source_addr'], r.request.params['destination_addr'], logged_content) else: # Message must be retried ? if str(r.response.status) in self.config.submit_error_retrial: retrial = self.config.submit_error_retrial[str( r.response.status)] # Still have some retries to go ? if self.submit_retrials[msgid] < retrial['count']: # Requeue the message for later redelivery yield self.rejectAndRequeueMessage( amqpMessage, delay=retrial['delay']) will_be_retried = True else: # Prevent this list from over-growing del self.submit_retrials[msgid] # Do not log text for privacy reasons # Added in #691 if self.config.log_privacy: logged_content = '** %s byte content **' % len( r.request.params['short_message']) else: logged_content = '%r' % r.request.params['short_message'] # Log the message self.log.info( "SMS-MT [cid:%s] [queue-msgid:%s] [status:ERROR/%s] [retry:%s] [prio:%s] [dlr:%s] [validity:%s] \ [from:%s] [to:%s] [content:%s]", self.SMPPClientFactory.config.id, msgid, r.response.status, will_be_retried, amqpMessage.content.properties['priority'], r.request.params['registered_delivery'].receipt, 'none' if ('headers' not in amqpMessage.content.properties or 'expiration' not in amqpMessage.content.properties['headers']) else amqpMessage.content.properties['headers']['expiration'], r.request.params['source_addr'], r.request.params['destination_addr'], logged_content) # It is a final submit_sm_resp ! if not will_be_retried: # Cancel any mapped rejectTimer to this message # (in case this message was rejected in the past) self.clearRejectTimer(msgid) self.log.debug( "ACKing amqpMessage [%s] having routing_key [%s]", msgid, amqpMessage.routing_key) # ACK the message in queue, this will remove it from the queue yield self.ackMessage(amqpMessage) # Send DLR to DLRLookup if r.response.status == CommandStatus.ESME_ROK: dlr = DLR(pdu_type=r.response.id, msgid=msgid, status=r.response.status, smpp_msgid=r.response.params['message_id']) else: dlr = DLR(pdu_type=r.response.id, msgid=msgid, status=r.response.status) yield self.amqpBroker.publish(exchange='messaging', routing_key='dlr.submit_sm_resp', content=dlr) # Bill will be charged by bill_request.submit_sm_resp.UID queue consumer if total_bill_amount > 0: pubQueueName = 'bill_request.submit_sm_resp.%s' % submit_sm_resp_bill.user.uid content = SubmitSmRespBillContent(submit_sm_resp_bill.bid, submit_sm_resp_bill.user.uid, total_bill_amount) self.log.debug( "Requesting a SubmitSmRespBillContent from a bill [bid:%s] with routing_key[%s]: %s", submit_sm_resp_bill.bid, pubQueueName, total_bill_amount) yield self.amqpBroker.publish(exchange='billing', routing_key=pubQueueName, content=content) if self.config.publish_submit_sm_resp: # Send back submit_sm_resp to submit.sm.resp.CID queue # There's no actual listeners on this queue, it can be used to # track submit_sm_resp messages from a 3rd party app content = SubmitSmRespContent( r.response, msgid, pickleProtocol=self.pickleProtocol) self.log.debug( "Sending back SubmitSmRespContent[%s] with routing_key[%s]", msgid, amqpMessage.content.properties['reply-to']) yield self.amqpBroker.publish( exchange='messaging', routing_key=amqpMessage.content.properties['reply-to'], content=content) except Exception as e: self.log.error( '(%s) while handling submit_sm_resp pdu for msgid:%s: %s', type(e), msgid, e) else: if will_be_retried: defer.returnValue(False)
def submit_sm_resp_event(self, r, amqpMessage): msgid = amqpMessage.content.properties['message-id'] total_bill_amount = None will_be_retried = False if ('headers' not in amqpMessage.content.properties or 'submit_sm_resp_bill' not in amqpMessage.content.properties['headers']): submit_sm_resp_bill = None else: submit_sm_resp_bill = pickle.loads( amqpMessage.content.properties['headers']['submit_sm_resp_bill']) if r.response.status == CommandStatus.ESME_ROK: # No more retrials ! del self.submit_retrials[msgid] # Get bill information total_bill_amount = 0.0 if submit_sm_resp_bill is not None and submit_sm_resp_bill.getTotalAmounts() > 0: total_bill_amount = submit_sm_resp_bill.getTotalAmounts() # UDH is set ? UDHI_INDICATOR_SET = False if hasattr(r.request.params['esm_class'], 'gsmFeatures'): for gsmFeature in r.request.params['esm_class'].gsmFeatures: if str(gsmFeature) == 'UDHI_INDICATOR_SET': UDHI_INDICATOR_SET = True break # What type of splitting ? splitMethod = None if 'sar_msg_ref_num' in r.request.params: splitMethod = 'sar' elif UDHI_INDICATOR_SET and r.request.params['short_message'][:3] == '\x05\x00\x03': splitMethod = 'udh' # Concatenate short_message if splitMethod is not None: _pdu = r.request if splitMethod == 'sar': short_message = _pdu.params['short_message'] else: short_message = _pdu.params['short_message'][6:] while hasattr(_pdu, 'nextPdu'): _pdu = _pdu.nextPdu if splitMethod == 'sar': short_message += _pdu.params['short_message'] else: short_message += _pdu.params['short_message'][6:] # Increase bill amount for each submit_sm_resp if submit_sm_resp_bill is not None and submit_sm_resp_bill.getTotalAmounts() > 0: total_bill_amount += submit_sm_resp_bill.getTotalAmounts() else: short_message = r.request.params['short_message'] self.log.info("SMS-MT [cid:%s] [queue-msgid:%s] [smpp-msgid:%s] [status:%s] [prio:%s] [dlr:%s] [validity:%s] [from:%s] [to:%s] [content:%s]", self.SMPPClientFactory.config.id, msgid, r.response.params['message_id'], r.response.status, amqpMessage.content.properties['priority'], r.request.params['registered_delivery'].receipt, 'none' if ('headers' not in amqpMessage.content.properties or 'expiration' not in amqpMessage.content.properties['headers']) else amqpMessage.content.properties['headers']['expiration'], r.request.params['source_addr'], r.request.params['destination_addr'], re.sub(r'[^\x20-\x7E]+', '.', short_message)) else: # Message must be retried ? if str(r.response.status) in self.config.submit_error_retrial: retrial = self.config.submit_error_retrial[str(r.response.status)] # Still have some retrys to go ? if self.submit_retrials[msgid] < retrial['count']: # Requeue the message for later redelivery yield self.rejectAndRequeueMessage(amqpMessage, delay=retrial['delay']) will_be_retried = True # Log the message self.log.info("SMS-MT [cid:%s] [queue-msgid:%s] [status:ERROR/%s] [retry:%s] [prio:%s] [dlr:%s] [validity:%s] [from:%s] [to:%s] [content:%s]", self.SMPPClientFactory.config.id, msgid, r.response.status, will_be_retried, amqpMessage.content.properties['priority'], r.request.params['registered_delivery'].receipt, 'none' if ('headers' not in amqpMessage.content.properties or 'expiration' not in amqpMessage.content.properties['headers']) else amqpMessage.content.properties['headers']['expiration'], r.request.params['source_addr'], r.request.params['destination_addr'], re.sub(r'[^\x20-\x7E]+', '.', r.request.params['short_message'])) # It is a final submit_sm_resp ! if not will_be_retried: # Cancel any mapped rejectTimer to this message (in case this message was rejected in the past) self.clearRejectTimer(msgid) self.log.debug("ACKing amqpMessage [%s] having routing_key [%s]", msgid, amqpMessage.routing_key) # ACK the message in queue, this will remove it from the queue yield self.ackMessage(amqpMessage) # Redis client is connected ? # Check DLR mappings and publish receipt for later throwing if self.redisClient is not None: # Check for HTTP DLR request from redis 'dlr' key # If there's a pending delivery receipt request then serve it # back by publishing a DLRContentForHttpapi to the messaging exchange pickledDlr = None pickledSmppsMap = None pickledDlr = yield self.redisClient.get("dlr:%s" % msgid) if pickledDlr is None: pickledSmppsMap = yield self.redisClient.get("smppsmap:%s" % msgid) if pickledDlr is not None: self.log.debug('There is a HTTP DLR request for msgid[%s] ...', msgid) dlr = pickle.loads(pickledDlr) dlr_url = dlr['url'] dlr_level = dlr['level'] dlr_method = dlr['method'] dlr_expiry = dlr['expiry'] if dlr_level in [1, 3]: self.log.debug('Got DLR information for msgid[%s], url:%s, level:%s', msgid, dlr_url, dlr_level) # The dlr_url in DLRContentForHttpapi indicates the level # of the actual delivery receipt (1) and not the requested # one (maybe 1 or 3) content = DLRContentForHttpapi(str(r.response.status), msgid, dlr_url, dlr_level=1, method=dlr_method) routing_key = 'dlr_thrower.http' self.log.debug("Publishing DLRContentForHttpapi[%s] with routing_key[%s]", msgid, routing_key) yield self.amqpBroker.publish(exchange='messaging', routing_key=routing_key, content=content) # DLR request is removed if: # - If level 1 is requested (SMSC level only) # - SubmitSmResp returned an error (no more delivery will be tracked) # # When level 3 is requested, the DLR will be removed when # receiving a deliver_sm (terminal receipt) if dlr_level == 1 or r.response.status != CommandStatus.ESME_ROK: self.log.debug('Removing DLR request for msgid[%s]', msgid) yield self.redisClient.delete("dlr:%s" % msgid) else: self.log.debug( 'Terminal level receipt is requested, will not send any DLR receipt at this level.') if dlr_level in [2, 3] and r.response.status == CommandStatus.ESME_ROK: # Map received submit_sm_resp's message_id to the msg for later receipt handling self.log.debug('Mapping smpp msgid: %s to queue msgid: %s, expiring in %s', r.response.params['message_id'], msgid, dlr_expiry) hashKey = "queue-msgid:%s" % r.response.params['message_id'].upper().lstrip('0') hashValues = {'msgid': msgid, 'connector_type': 'httpapi',} yield self.redisClient.setex(hashKey, dlr_expiry, pickle.dumps(hashValues, self.pickleProtocol)) elif pickledSmppsMap is not None: self.log.debug('There is a SMPPs mapping for msgid[%s] ...', msgid) smpps_map = pickle.loads(pickledSmppsMap) system_id = smpps_map['system_id'] source_addr = smpps_map['source_addr'] destination_addr = smpps_map['destination_addr'] sub_date = smpps_map['sub_date'] registered_delivery = smpps_map['registered_delivery'] smpps_map_expiry = smpps_map['expiry'] # Do we need to forward the receipt to the original sender ? if ((r.response.status == CommandStatus.ESME_ROK and str(registered_delivery.receipt) in ['SMSC_DELIVERY_RECEIPT_REQUESTED_FOR_FAILURE', 'SMSC_DELIVERY_RECEIPT_REQUESTED']) or (r.response.status != CommandStatus.ESME_ROK and str(registered_delivery.receipt) == 'SMSC_DELIVERY_RECEIPT_REQUESTED_FOR_FAILURE')): self.log.debug('Got DLR information for msgid[%s], registered_deliver%s, system_id:%s', msgid, registered_delivery, system_id) if (r.response.status != CommandStatus.ESME_ROK or (r.response.status == CommandStatus.ESME_ROK and self.config.smpp_receipt_on_success_submit_sm_resp)): # Send back a receipt (by throwing deliver_sm or data_sm) content = DLRContentForSmpps(str(r.response.status), msgid, system_id, source_addr, destination_addr, sub_date) routing_key = 'dlr_thrower.smpps' self.log.debug("Publishing DLRContentForSmpps[%s] with routing_key[%s]", msgid, routing_key) yield self.amqpBroker.publish(exchange='messaging', routing_key=routing_key, content=content) if r.response.status == CommandStatus.ESME_ROK: # Map received submit_sm_resp's message_id to the msg for later rceipt handling self.log.debug('Mapping smpp msgid: %s to queue msgid: %s, expiring in %s', r.response.params['message_id'], msgid, smpps_map_expiry) hashKey = "queue-msgid:%s" % r.response.params['message_id'].upper().lstrip('0') hashValues = {'msgid': msgid, 'connector_type': 'smpps',} yield self.redisClient.setex(hashKey, smpps_map_expiry, pickle.dumps(hashValues, self.pickleProtocol)) else: self.log.warn('No valid RC were found while checking msg[%s] !', msgid) # Bill will be charged by bill_request.submit_sm_resp.UID queue consumer if total_bill_amount > 0: pubQueueName = 'bill_request.submit_sm_resp.%s' % submit_sm_resp_bill.user.uid content = SubmitSmRespBillContent(submit_sm_resp_bill.bid, submit_sm_resp_bill.user.uid, total_bill_amount) self.log.debug( "Requesting a SubmitSmRespBillContent from a bill [bid:%s] with routing_key[%s]: %s", submit_sm_resp_bill.bid, pubQueueName, total_bill_amount) yield self.amqpBroker.publish(exchange='billing', routing_key=pubQueueName, content=content) if self.config.publish_submit_sm_resp: # Send back submit_sm_resp to submit.sm.resp.CID queue # There's no actual listeners on this queue, it can be used to # track submit_sm_resp messages from a 3rd party app content = SubmitSmRespContent(r.response, msgid, pickleProtocol=self.pickleProtocol) self.log.debug("Sending back SubmitSmRespContent[%s] with routing_key[%s]", msgid, amqpMessage.content.properties['reply-to']) yield self.amqpBroker.publish(exchange='messaging', routing_key=amqpMessage.content.properties['reply-to'], content=content) if will_be_retried: defer.returnValue(False)
def test_normal_pickling(self): c = SubmitSmRespContent(self.body, 1) self.assertNotEquals(c.body, self.body) self.assertEquals(c.body, pickle.dumps(self.body, 2)) self.assertEquals(c['message-id'], 1)
def test_normal_nopickling(self): c = SubmitSmRespContent(self.body, 1, prePickle=False) self.assertEquals(c.body, self.body) self.assertEquals(c['message-id'], 1)