# Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. # Copyright (c) 2006-2014 Sippy Software, Inc. All rights reserved. # # All rights reserved. # # Redistribution and use in source and binary forms, with or without modification, # are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation and/or # other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from __future__ import print_function from sippy.Time.Timeout import Timeout from threading import Thread, Condition from errno import EINTR, EPIPE, ENOTCONN, ECONNRESET from sippy.Time.MonoTime import MonoTime from sippy.Math.recfilter import recfilter from sippy.Rtp_proxy_client_net import Rtp_proxy_client_net from sippy.Rtp_proxy_cmd import Rtp_proxy_cmd import socket from sippy.Core.Exceptions import dump_exception from sippy.Core.EventDispatcher import ED2 _MAX_RECURSE = 10 class _RTPPLWorker(Thread): userv = None s = None def __init__(self, userv): Thread.__init__(self) self.userv = userv self.setDaemon(True) self.start() def connect(self): self.s = socket.socket(self.userv.family, socket.SOCK_STREAM) if self.userv.family == socket.AF_INET6: address = (self.userv.address[0][1:-1], self.userv.address[1]) else: address = self.userv.address self.s.connect(address) def send_raw(self, command, _recurse = 0, stime = None): if _recurse > _MAX_RECURSE: raise Exception('Cannot reconnect: %s' % (str(self.userv.address),)) if self.s == None: self.connect() #print('%s.send_raw(%s)' % (id(self), command)) if stime == None: stime = MonoTime() while True: try: self.s.send(command.encode()) break except socket.error as why: if why.errno == EINTR: continue elif why.errno in (EPIPE, ENOTCONN, ECONNRESET): self.s = None return self.send_raw(command, _recurse + 1, stime) raise why while True: try: rval = self.s.recv(1024) if len(rval) == 0: self.s = None return self.send_raw(command, _MAX_RECURSE, stime) rval = rval.decode().strip() break except socket.error as why: if why.errno == EINTR: continue elif why.errno in (EPIPE, ENOTCONN, ECONNRESET): self.s = None return self.send_raw(command, _recurse + 1, stime) raise why rtpc_delay = stime.offsetFromNow() return (rval, rtpc_delay) def run(self): #print(self.run, 'enter') while True: #print(self.run, 'spin') self.userv.wi_available.acquire() while len(self.userv.wi) == 0: self.userv.wi_available.wait() wi = self.userv.wi.pop(0) if wi == None: # Shutdown request, relay it further self.userv.wi.append(None) self.userv.wi_available.notify() self.userv.wi_available.release() if wi == None: break command, result_callback, callback_parameters = wi try: data, rtpc_delay = self.send_raw(command) if len(data) == 0: data, rtpc_delay = None, None except Exception as e: dump_exception('Rtp_proxy_client_stream: unhandled exception I/O RTPproxy') data, rtpc_delay = None, None if result_callback != None: ED2.callFromThread(self.dispatch, result_callback, data, callback_parameters) if rtpc_delay != None: ED2.callFromThread(self.userv.register_delay, rtpc_delay) self.userv = None def dispatch(self, result_callback, data, callback_parameters): try: result_callback(data, *callback_parameters) except: dump_exception('Rtp_proxy_client_stream: unhandled exception when processing RTPproxy reply') class Rtp_proxy_client_stream(Rtp_proxy_client_net): is_local = None wi_available = None wi = None nworkers = None nworkers_act = None workers = None delay_flt = None family = None sock_type = socket.SOCK_STREAM def __init__(self, global_config, address = '/var/run/rtpproxy.sock', \ bind_address = None, nworkers = 1, family = socket.AF_UNIX): #print('Rtp_proxy_client_stream.__init__', address, bind_address, nworkers, family) if family == socket.AF_UNIX: self.is_local = True self.address = address else: self.is_local = False self.address = self.getdestbyaddr(address, family) self.family = family self.wi_available = Condition() self.wi = [] self.nworkers = nworkers self.workers = [] for i in range(0, self.nworkers): try: self.workers.append(_RTPPLWorker(self)) except: break self.nworkers_act = i + 1 self.delay_flt = recfilter(0.95, 0.25) def send_command(self, command, result_callback = None, *callback_parameters): if self.nworkers_act == 0: self.rtpp_class._reconnect(self, self.address) if isinstance(command, Rtp_proxy_cmd): command = str(command) elif not command.endswith('\n'): command += '\n' self.wi_available.acquire() self.wi.append((command, result_callback, callback_parameters)) self.wi_available.notify() self.wi_available.release() def reconnect(self, address, bind_address = None): if not self.is_local: address = self.getdestbyaddr(address, family) self.rtpp_class._reconnect(self, address, bind_address) def _reconnect(self, address, bind_address = None): Rtp_proxy_client_stream.shutdown(self) self.address = address self.workers = [] for i in range(0, self.nworkers): try: self.workers.append(_RTPPLWorker(self)) except: break self.nworkers_act = i + 1 self.delay_flt = recfilter(0.95, 0.25) def shutdown(self): self.wi_available.acquire() self.wi.append(None) self.wi_available.notify() self.wi_available.release() for rworker in self.workers: rworker.join() self.workers = None def register_delay(self, rtpc_delay): self.delay_flt.apply(rtpc_delay) def get_rtpc_delay(self): return self.delay_flt.lastval if __name__ == '__main__': class robj(object): rval = None r = robj() def display(res, ro, arg): print(res, arg) ro.rval = (res, arg) ED2.breakLoop() r = Rtp_proxy_client_stream({'_sip_address':'1.2.3.4'}) r.send_command('VF 123456', display, r, 'abcd') ED2.loop() r.shutdown() print(r.rval) assert(r.rval == (u'0', 'abcd')) print('passed')