示例#1
0
    def _reader(self):
        """Wait forever for new chunks. Notify the user about the good ones through self._on_incoming_cb.

        If a response to a previously sent chunk is received, pop the corresponding
        response_cb from self.expected_responses and send the response there.
        """
        error = Failure(ConnectionDone())
        try:
            self.writer_job.link(self.reader_job)
            try:
                while self.state in ['CONNECTED', 'FLUSHING']:
                    chunk = self.msrp.read_chunk()
                    if chunk.method is None:  # response
                        self._handle_incoming_response(chunk)
                    else:
                        method = getattr(self,
                                         '_handle_incoming_%s' % chunk.method,
                                         None)
                        if method is not None:
                            method(chunk)
                        else:
                            response = make_response(chunk, 501,
                                                     'Method unknown')
                            self.outgoing.send(OutgoingChunk(response))
            except proc.LinkedExited:  # writer has exited
                pass
            finally:
                self.writer_job.unlink(self.reader_job)
                self.writer_job.kill()
            self.logger.debug('reader: expecting responses only')
            delay = time() - self.last_expected_response
            if delay >= 0 and self.expected_responses:
                # continue read the responses until the last timeout expires
                with api.timeout(delay, None):
                    while self.expected_responses:
                        chunk = self.msrp.read_chunk()
                        if chunk.method is None:
                            self._handle_incoming_response(chunk)
                        else:
                            self.logger.debug('dropping incoming %r', chunk)
                # read whatever left in the queue
                with api.timeout(0, None):
                    while self.msrp._queue:
                        chunk = self.msrp.read_chunk()
                        if chunk.method is None:
                            self._handle_incoming_response(chunk)
                        else:
                            self.logger.debug('dropping incoming %r', chunk)
            self.logger.debug('reader: done')
        except ConnectionClosedErrors as e:
            self.logger.debug('reader: exiting because of %r', e)
            error = Failure(e)
        except Exception:
            self.logger.exception('reader: captured unhandled exception:')
            error = Failure()
            raise
        finally:
            self._on_incoming_cb(error=error)
            self.msrp.loseConnection(wait=False)
            self.set_state('DONE')
示例#2
0
    def test_nested_timeout(self):
        with timeout(DELAY, None):
            with timeout(DELAY * 2, None):
                sleep(DELAY * 3)
            raise AssertionError('should not get there')

        with timeout(DELAY, _SilentException()):
            with timeout(DELAY * 2, _SilentException()):
                sleep(DELAY * 3)
            raise AssertionError('should not get there')
示例#3
0
    def _reader(self):
        """Wait forever for new chunks. Notify the user about the good ones through self._on_incoming_cb.

        If a response to a previously sent chunk is received, pop the corresponding
        response_cb from self.expected_responses and send the response there.
        """
        error = Failure(ConnectionDone())
        try:
            self.writer_job.link(self.reader_job)
            try:
                while self.state in ['CONNECTED', 'FLUSHING']:
                    chunk = self.msrp.read_chunk()
                    if chunk.method is None: # response
                        self._handle_incoming_response(chunk)
                    else:
                        method = getattr(self, '_handle_incoming_%s' % chunk.method, None)
                        if method is not None:
                            method(chunk)
                        else:
                            response = make_response(chunk, 501, 'Method unknown')
                            self.outgoing.send(OutgoingChunk(response))
            except proc.LinkedExited: # writer has exited
                pass
            finally:
                self.writer_job.unlink(self.reader_job)
                self.writer_job.kill()
            self.logger.debug('reader: expecting responses only')
            delay = time() - self.last_expected_response
            if delay>=0 and self.expected_responses:
                # continue read the responses until the last timeout expires
                with api.timeout(delay, None):
                    while self.expected_responses:
                        chunk = self.msrp.read_chunk()
                        if chunk.method is None:
                            self._handle_incoming_response(chunk)
                        else:
                            self.logger.debug('dropping incoming %r' % chunk)
                # read whatever left in the queue
                with api.timeout(0, None):
                    while self.msrp._queue:
                        chunk = self.msrp.read_chunk()
                        if chunk.method is None:
                            self._handle_incoming_response(chunk)
                        else:
                            self.logger.debug('dropping incoming %r' % chunk)
            self.logger.debug('reader: done')
        except ConnectionClosedErrors, ex:
            self.logger.debug('reader: exiting because of %r' % ex)
            error=Failure(ex)
示例#4
0
 def test_ref(self):
     err = Error()
     err_ref = weakref.ref(err)
     with timeout(DELAY * 2, err):
         sleep(DELAY)
     del err
     assert not err_ref(), repr(err_ref())
示例#5
0
    def test_api(self):
        # Nothing happens if with-block finishes before the timeout expires
        with timeout(DELAY * 2):
            sleep(DELAY)
        sleep(DELAY * 2)  # check if timer was actually cancelled

        # An exception will be raised if it's not
        try:
            with timeout(DELAY):
                sleep(DELAY * 2)
        except TimeoutError:
            pass
        else:
            raise AssertionError('must raise TimeoutError')

        # You can customize the exception raised:
        try:
            with timeout(DELAY, IOError("Operation takes way too long")):
                sleep(DELAY * 2)
        except IOError, ex:
            assert str(ex) == "Operation takes way too long", repr(ex)
示例#6
0
    def wait(self, timeout=None, *throw_args):
        """Wait until send() or send_exception() is called or `timeout' has
        expired. Return the argument of send or raise the argument of
        send_exception. If timeout has expired, None is returned.

        The arguments, when provided, specify how many seconds to wait and what
        to do when timeout has expired. They are treated the same way as
        api.timeout treats them.
        """
        if self.value is not _NOT_USED:
            if self._exc is None:
                return self.value
            else:
                api.getcurrent().throw(*self._exc)
        if timeout is not None:
            timer = api.timeout(timeout, *throw_args)
            timer.__enter__()
            if timeout == 0:
                if timer.__exit__(None, None, None):
                    return
                else:
                    try:
                        api.getcurrent().throw(*timer.throw_args)
                    except:
                        if not timer.__exit__(*sys.exc_info()):
                            raise
                    return
            EXC = True
        try:
            try:
                waiter = Waiter()
                self.link(waiter)
                try:
                    return waiter.wait()
                finally:
                    self.unlink(waiter)
            except:
                EXC = False
                if timeout is None or not timer.__exit__(*sys.exc_info()):
                    raise
        finally:
            if timeout is not None and EXC:
                timer.__exit__(None, None, None)
示例#7
0
    def wait(self, timeout=None, *throw_args):
        """Wait until send() or send_exception() is called or `timeout' has
        expired. Return the argument of send or raise the argument of
        send_exception. If timeout has expired, None is returned.

        The arguments, when provided, specify how many seconds to wait and what
        to do when timeout has expired. They are treated the same way as
        api.timeout treats them.
        """
        if self.value is not _NOT_USED:
            if self._exc is None:
                return self.value
            else:
                api.getcurrent().throw(*self._exc)
        if timeout is not None:
            timer = api.timeout(timeout, *throw_args)
            timer.__enter__()
            if timeout==0:
                if timer.__exit__(None, None, None):
                    return
                else:
                    try:
                        api.getcurrent().throw(*timer.throw_args)
                    except:
                        if not timer.__exit__(*sys.exc_info()):
                            raise
                    return
            EXC = True
        try:
            try:
                waiter = Waiter()
                self.link(waiter)
                try:
                    return waiter.wait()
                finally:
                    self.unlink(waiter)
            except:
                EXC = False
                if timeout is None or not timer.__exit__(*sys.exc_info()):
                    raise
        finally:
            if timeout is not None and EXC:
                timer.__exit__(None, None, None)
示例#8
0
    def _OH_ProcessRemoteOperation(self, operation):
        notification = operation.notification
        stanza = notification.data.stanza
        if notification.name == 'XMPPGotJingleSessionTerminate':
            if self.state not in ('incoming', 'connecting', 'connected_pending_accept', 'connected'):
                return
            if self._timer is not None and self._timer.active():
                self._timer.cancel()
            self._timer = None
            # Session ended remotely
            prev_state = self.state
            self.state = 'terminated'
            if prev_state == 'incoming':
                reason = stanza.jingle.reason.value if stanza.jingle.reason else 'cancel'
                notification.center.post_notification('JingleSessionDidFail', self, NotificationData(originator='remote', reason=reason))
            else:
                notification.center.post_notification('JingleSessionWillEnd', self, NotificationData(originator='remote'))
                streams = self.proposed_streams if prev_state == 'connecting' else self.streams
                for stream in streams:
                    notification.center.remove_observer(self, sender=stream)
                    stream.deactivate()
                    stream.end()
                self.end_time = datetime.now()
                notification.center.post_notification('JingleSessionDidEnd', self, NotificationData(originator='remote'))
            self._channel.send_exception(proc.ProcExit)
        elif notification.name == 'XMPPGotJingleSessionInfo':
            info = stanza.jingle.info
            if not info:
                return
            if info == 'ringing':
                if self.state not in ('connecting', 'connected_pending_accept'):
                    return
                notification.center.post_notification('JingleSessionGotRingIndication', self)
            elif info in ('hold', 'unhold'):
                if self.state != 'connected':
                    return
                notification.center.post_notification('JingleSessionDidChangeHoldState', self, NotificationData(originator='remote', on_hold=info=='hold', partial=False))
        elif notification.name == 'XMPPGotJingleDescriptionInfo':
            if self.state != 'connecting':
                return

            # Add candidates acquired on transport-info stanzas
            for s in self._pending_transport_info_stanzas:
                for c in s.jingle.content:
                    content = next(content for content in stanza.jingle.content if content.name == c.name)
                    content.transport.candidates.extend(c.transport.candidates)
                    if isinstance(content.transport, jingle.IceUdpTransport):
                        if not content.transport.ufrag and c.transport.ufrag:
                            content.transport.ufrag = c.transport.ufrag
                        if not content.transport.password and c.transport.password:
                            content.transport.password = c.transport.password

            remote_sdp = jingle_to_sdp(stanza.jingle)
            try:
                self._sdp_negotiator.set_remote_answer(remote_sdp)
                self._sdp_negotiator.negotiate()
            except SIPCoreError:
                # The description-info stanza may have been just a parameter change, not a full 'SDP'
                return

            if self._timer is not None and self._timer.active():
                self._timer.cancel()
            self._timer = None

            del self._pending_transport_info_stanzas[:]

            # Get active SDPs (negotiator may make changes)
            local_sdp = self._sdp_negotiator.active_local
            remote_sdp = self._sdp_negotiator.active_remote

            notification.center.post_notification('JingleSessionWillStart', sender=self)
            stream_map = dict((stream.index, stream) for stream in self.proposed_streams)
            for index, local_media in enumerate(local_sdp.media):
                remote_media = remote_sdp.media[index]
                stream = stream_map[index]
                if remote_media.port:
                    stream.start(local_sdp, remote_sdp, index)
                else:
                    notification.center.remove_observer(self, sender=stream)
                    self.proposed_streams.remove(stream)
                    del stream_map[stream.index]
                    stream.deactivate()
                    stream.end()
            removed_streams = [stream for stream in self.proposed_streams if stream.index >= len(local_sdp.media)]
            for stream in removed_streams:
                notification.center.remove_observer(self, sender=stream)
                self.proposed_streams.remove(stream)
                del stream_map[stream.index]
                stream.deactivate()
                stream.end()

            try:
                with api.timeout(self.media_stream_timeout):
                    wait_count = len(self.proposed_streams)
                    while wait_count > 0:
                        notification = operation.channel.wait()
                        if notification.name == 'MediaStreamDidStart':
                            wait_count -= 1
            except (MediaStreamDidFailError, api.TimeoutError), e:
                for stream in self.proposed_streams:
                    notification.center.remove_observer(self, sender=stream)
                    stream.deactivate()
                    stream.end()
                if isinstance(e, api.TimeoutError):
                    error = 'media stream timed out while starting'
                else:
                    error = 'media stream failed: %s' % e.data.reason
                self._fail(originator='local', reason='failed-application', description=error)
            else:
                self.state = 'connected_pending_accept'
                self.streams = self.proposed_streams
                self.proposed_streams = None
                self.start_time = datetime.now()
                # Hold the streams to prevent real RTP from flowing
                for stream in self.streams:
                    stream.hold()
示例#9
0
    def _OH_AcceptOperation(self, operation):
        if self.state != 'incoming':
            return

        notification_center = NotificationCenter()
        settings = SIPSimpleSettings()
        streams = operation.streams

        for stream in self.proposed_streams:
            if stream in streams:
                notification_center.add_observer(self, sender=stream)
                stream.initialize(self, direction='incoming')

        try:
            wait_count = len(self.proposed_streams)
            while wait_count > 0:
                notification = operation.channel.wait()
                if notification.name == 'MediaStreamDidInitialize':
                    wait_count -= 1

            remote_sdp = self._sdp_negotiator.current_remote
            local_ip = SIPConfig.local_ip.normalized
            local_sdp = SDPSession(local_ip, connection=SDPConnection(local_ip), name=settings.user_agent)
            stream_map = dict((stream.index, stream) for stream in self.proposed_streams)
            for index, media in enumerate(remote_sdp.media):
                stream = stream_map.get(index, None)
                if stream is not None:
                    media = stream.get_local_media(remote_sdp=remote_sdp, index=index)
                else:
                    media = SDPMediaStream.new(media)
                    media.port = 0
                    media.attributes = []
                local_sdp.media.append(media)
            try:
                self._sdp_negotiator.set_local_answer(local_sdp)
                self._sdp_negotiator.negotiate()
            except SIPCoreError, e:
                self._fail(originator='local', reason='incompatible-parameters', description=str(e))
                return

            self.local_focus = operation.is_focus

            notification_center.post_notification('JingleSessionWillStart', sender=self)

            # Get active SDPs (negotiator may make changes)
            local_sdp = self._sdp_negotiator.active_local
            remote_sdp = self._sdp_negotiator.active_remote

            # Build the payload and send it over
            payload = sdp_to_jingle(local_sdp)
            payload.sid = self._id
            if self.local_focus:
                payload.conference_info = jingle.ConferenceInfo(True)
            stanza = self._protocol.sessionAccept(self._local_jid, self._remote_jid, payload)
            d = self._send_stanza(stanza)
            block_on(d)

            wait_count = 0
            stream_map = dict((stream.index, stream) for stream in self.proposed_streams)
            for index, local_media in enumerate(local_sdp.media):
                remote_media = remote_sdp.media[index]
                stream = stream_map.get(index, None)
                if stream is not None:
                    if remote_media.port:
                        wait_count += 1
                        stream.start(local_sdp, remote_sdp, index)
                    else:
                        notification_center.remove_observer(self, sender=stream)
                        self.proposed_streams.remove(stream)
                        del stream_map[stream.index]
                        stream.deactivate()
                        stream.end()
            removed_streams = [stream for stream in self.proposed_streams if stream.index >= len(local_sdp.media)]
            for stream in removed_streams:
                notification_center.remove_observer(self, sender=stream)
                self.proposed_streams.remove(stream)
                del stream_map[stream.index]
                stream.deactivate()
                stream.end()
            with api.timeout(self.media_stream_timeout):
                while wait_count > 0:
                    notification = operation.channel.wait()
                    if notification.name == 'MediaStreamDidStart':
                        wait_count -= 1
示例#10
0
                else:
                    notification.center.remove_observer(self, sender=stream)
                    self.proposed_streams.remove(stream)
                    del stream_map[stream.index]
                    stream.deactivate()
                    stream.end()
            removed_streams = [stream for stream in self.proposed_streams if stream.index >= len(local_sdp.media)]
            for stream in removed_streams:
                notification.center.remove_observer(self, sender=stream)
                self.proposed_streams.remove(stream)
                del stream_map[stream.index]
                stream.deactivate()
                stream.end()

            try:
                with api.timeout(self.media_stream_timeout):
                    wait_count = len(self.proposed_streams)
                    while wait_count > 0:
                        notification = operation.channel.wait()
                        if notification.name == 'MediaStreamDidStart':
                            wait_count -= 1
            except (MediaStreamDidFailError, api.TimeoutError), e:
                for stream in self.proposed_streams:
                    notification.center.remove_observer(self, sender=stream)
                    stream.deactivate()
                    stream.end()
                if isinstance(e, api.TimeoutError):
                    error = 'media stream timed out while starting'
                else:
                    error = 'media stream failed: %s' % e.data.reason
                self._fail(originator='local', reason='failed-application', description=error)
示例#11
0
 def close(self):
     """Stop listening. Wait for the spawned greenlets to finish"""
     self.stopListening()
     with timeout(self.CLOSE_TIMEOUT, None):
         self.factory.waitall()
示例#12
0
 def timeout(cls, *throw_args):
     if not throw_args:
         throw_args = (cls, )
     return timeout(cls.seconds, *throw_args)
示例#13
0
    def _OH_AcceptOperation(self, operation):
        if self.state != 'incoming':
            return

        notification_center = NotificationCenter()
        settings = SIPSimpleSettings()
        streams = operation.streams

        for stream in self.proposed_streams:
            if stream in streams:
                notification_center.add_observer(self, sender=stream)
                stream.initialize(self, direction='incoming')

        try:
            wait_count = len(self.proposed_streams)
            while wait_count > 0:
                notification = operation.channel.wait()
                if notification.name == 'MediaStreamDidInitialize':
                    wait_count -= 1

            remote_sdp = self._sdp_negotiator.current_remote
            local_ip = SIPConfig.local_ip.normalized
            local_sdp = SDPSession(local_ip,
                                   connection=SDPConnection(local_ip),
                                   name=settings.user_agent)
            stream_map = dict(
                (stream.index, stream) for stream in self.proposed_streams)
            for index, media in enumerate(remote_sdp.media):
                stream = stream_map.get(index, None)
                if stream is not None:
                    media = stream.get_local_media(remote_sdp=remote_sdp,
                                                   index=index)
                else:
                    media = SDPMediaStream.new(media)
                    media.port = 0
                    media.attributes = []
                local_sdp.media.append(media)
            try:
                self._sdp_negotiator.set_local_answer(local_sdp)
                self._sdp_negotiator.negotiate()
            except SIPCoreError as e:
                self._fail(originator='local',
                           reason='incompatible-parameters',
                           description=str(e))
                return

            self.local_focus = operation.is_focus

            notification_center.post_notification('JingleSessionWillStart',
                                                  sender=self)

            # Get active SDPs (negotiator may make changes)
            local_sdp = self._sdp_negotiator.active_local
            remote_sdp = self._sdp_negotiator.active_remote

            # Build the payload and send it over
            payload = sdp_to_jingle(local_sdp)
            payload.sid = self._id
            if self.local_focus:
                payload.conference_info = jingle.ConferenceInfo(True)
            stanza = self._protocol.sessionAccept(self._local_jid,
                                                  self._remote_jid, payload)
            d = self._send_stanza(stanza)
            block_on(d)

            wait_count = 0
            stream_map = dict(
                (stream.index, stream) for stream in self.proposed_streams)
            for index, local_media in enumerate(local_sdp.media):
                remote_media = remote_sdp.media[index]
                stream = stream_map.get(index, None)
                if stream is not None:
                    if remote_media.port:
                        wait_count += 1
                        stream.start(local_sdp, remote_sdp, index)
                    else:
                        notification_center.remove_observer(self,
                                                            sender=stream)
                        self.proposed_streams.remove(stream)
                        del stream_map[stream.index]
                        stream.deactivate()
                        stream.end()
            removed_streams = [
                stream for stream in self.proposed_streams
                if stream.index >= len(local_sdp.media)
            ]
            for stream in removed_streams:
                notification_center.remove_observer(self, sender=stream)
                self.proposed_streams.remove(stream)
                del stream_map[stream.index]
                stream.deactivate()
                stream.end()
            with api.timeout(self.media_stream_timeout):
                while wait_count > 0:
                    notification = operation.channel.wait()
                    if notification.name == 'MediaStreamDidStart':
                        wait_count -= 1
        except (MediaStreamDidNotInitializeError, MediaStreamDidFailError,
                api.TimeoutError, IqTimeoutError, StanzaError) as e:
            for stream in self.proposed_streams:
                notification_center.remove_observer(self, sender=stream)
                stream.deactivate()
                stream.end()
            if isinstance(e, api.TimeoutError):
                error = 'media stream timed out while starting'
            elif isinstance(e, IqTimeoutError):
                error = 'timeout sending IQ stanza'
            elif isinstance(e, StanzaError):
                error = str(e.condition)
            else:
                error = 'media stream failed: %s' % e.data.reason
            self._fail(originator='local',
                       reason='failed-application',
                       description=error)
        else:
            self.state = 'connected'
            self.streams = self.proposed_streams
            self.proposed_streams = None
            self.start_time = datetime.now()
            notification_center.post_notification(
                'JingleSessionDidStart', self,
                NotificationData(streams=self.streams))
示例#14
0
 def wait(self):
     with api.timeout(self.timeout):
         return event.wait(self)
示例#15
0
                else:
                    notification.center.remove_observer(self, sender=stream)
                    self.proposed_streams.remove(stream)
                    del stream_map[stream.index]
                    stream.deactivate()
                    stream.end()
            removed_streams = [stream for stream in self.proposed_streams if stream.index >= len(local_sdp.media)]
            for stream in removed_streams:
                notification.center.remove_observer(self, sender=stream)
                self.proposed_streams.remove(stream)
                del stream_map[stream.index]
                stream.deactivate()
                stream.end()

            try:
                with api.timeout(self.media_stream_timeout):
                    wait_count = len(self.proposed_streams)
                    while wait_count > 0:
                        notification = operation.channel.wait()
                        if notification.name == 'MediaStreamDidStart':
                            wait_count -= 1
            except (MediaStreamDidFailError, api.TimeoutError), e:
                for stream in self.proposed_streams:
                    notification.center.remove_observer(self, sender=stream)
                    stream.deactivate()
                    stream.end()
                if isinstance(e, api.TimeoutError):
                    error = 'media stream timed out while starting'
                else:
                    error = 'media stream failed: %s' % e.data.reason
                self._fail(originator='local', reason='failed-application', description=error)
示例#16
0
 def wait(self):
     with api.timeout(self.timeout):
         return event.wait(self)
示例#17
0
    def _OH_ProcessRemoteOperation(self, operation):
        notification = operation.notification
        stanza = notification.data.stanza
        if notification.name == 'XMPPGotJingleSessionTerminate':
            if self.state not in ('incoming', 'connecting',
                                  'connected_pending_accept', 'connected'):
                return
            if self._timer is not None and self._timer.active():
                self._timer.cancel()
            self._timer = None
            # Session ended remotely
            prev_state = self.state
            self.state = 'terminated'
            if prev_state == 'incoming':
                reason = stanza.jingle.reason.value if stanza.jingle.reason else 'cancel'
                notification.center.post_notification(
                    'JingleSessionDidFail', self,
                    NotificationData(originator='remote', reason=reason))
            else:
                notification.center.post_notification(
                    'JingleSessionWillEnd', self,
                    NotificationData(originator='remote'))
                streams = self.proposed_streams if prev_state == 'connecting' else self.streams
                for stream in streams:
                    notification.center.remove_observer(self, sender=stream)
                    stream.deactivate()
                    stream.end()
                self.end_time = datetime.now()
                notification.center.post_notification(
                    'JingleSessionDidEnd', self,
                    NotificationData(originator='remote'))
            self._channel.send_exception(proc.ProcExit)
        elif notification.name == 'XMPPGotJingleSessionInfo':
            info = stanza.jingle.info
            if not info:
                return
            if info == 'ringing':
                if self.state not in ('connecting',
                                      'connected_pending_accept'):
                    return
                notification.center.post_notification(
                    'JingleSessionGotRingIndication', self)
            elif info in ('hold', 'unhold'):
                if self.state != 'connected':
                    return
                notification.center.post_notification(
                    'JingleSessionDidChangeHoldState', self,
                    NotificationData(originator='remote',
                                     on_hold=info == 'hold',
                                     partial=False))
        elif notification.name == 'XMPPGotJingleDescriptionInfo':
            if self.state != 'connecting':
                return

            # Add candidates acquired on transport-info stanzas
            for s in self._pending_transport_info_stanzas:
                for c in s.jingle.content:
                    content = next(content for content in stanza.jingle.content
                                   if content.name == c.name)
                    content.transport.candidates.extend(c.transport.candidates)
                    if isinstance(content.transport, jingle.IceUdpTransport):
                        if not content.transport.ufrag and c.transport.ufrag:
                            content.transport.ufrag = c.transport.ufrag
                        if not content.transport.password and c.transport.password:
                            content.transport.password = c.transport.password

            remote_sdp = jingle_to_sdp(stanza.jingle)
            try:
                self._sdp_negotiator.set_remote_answer(remote_sdp)
                self._sdp_negotiator.negotiate()
            except SIPCoreError:
                # The description-info stanza may have been just a parameter change, not a full 'SDP'
                return

            if self._timer is not None and self._timer.active():
                self._timer.cancel()
            self._timer = None

            del self._pending_transport_info_stanzas[:]

            # Get active SDPs (negotiator may make changes)
            local_sdp = self._sdp_negotiator.active_local
            remote_sdp = self._sdp_negotiator.active_remote

            notification.center.post_notification('JingleSessionWillStart',
                                                  sender=self)
            stream_map = dict(
                (stream.index, stream) for stream in self.proposed_streams)
            for index, local_media in enumerate(local_sdp.media):
                remote_media = remote_sdp.media[index]
                stream = stream_map[index]
                if remote_media.port:
                    stream.start(local_sdp, remote_sdp, index)
                else:
                    notification.center.remove_observer(self, sender=stream)
                    self.proposed_streams.remove(stream)
                    del stream_map[stream.index]
                    stream.deactivate()
                    stream.end()
            removed_streams = [
                stream for stream in self.proposed_streams
                if stream.index >= len(local_sdp.media)
            ]
            for stream in removed_streams:
                notification.center.remove_observer(self, sender=stream)
                self.proposed_streams.remove(stream)
                del stream_map[stream.index]
                stream.deactivate()
                stream.end()

            try:
                with api.timeout(self.media_stream_timeout):
                    wait_count = len(self.proposed_streams)
                    while wait_count > 0:
                        notification = operation.channel.wait()
                        if notification.name == 'MediaStreamDidStart':
                            wait_count -= 1
            except (MediaStreamDidFailError, api.TimeoutError) as e:
                for stream in self.proposed_streams:
                    notification.center.remove_observer(self, sender=stream)
                    stream.deactivate()
                    stream.end()
                if isinstance(e, api.TimeoutError):
                    error = 'media stream timed out while starting'
                else:
                    error = 'media stream failed: %s' % e.data.reason
                self._fail(originator='local',
                           reason='failed-application',
                           description=error)
            else:
                self.state = 'connected_pending_accept'
                self.streams = self.proposed_streams
                self.proposed_streams = None
                self.start_time = datetime.now()
                # Hold the streams to prevent real RTP from flowing
                for stream in self.streams:
                    stream.hold()
        elif notification.name == 'XMPPGotJingleSessionAccept':
            if self.state not in ('connecting', 'connected_pending_accept'):
                return
            if self._timer is not None and self._timer.active():
                self._timer.cancel()
            self._timer = None

            if self.state == 'connected_pending_accept':
                # We already negotiated ICE and media is 'flowing' (not really because streams are on hold)
                # unhold the streams and pretend the session just started
                for stream in self.streams:
                    stream.unhold()
                self.state = 'connected'
                notification.center.post_notification(
                    'JingleSessionDidStart', self,
                    NotificationData(streams=self.streams))
                return

            # Add candidates acquired on transport-info stanzas
            for s in self._pending_transport_info_stanzas:
                for c in s.jingle.content:
                    content = next(content for content in stanza.jingle.content
                                   if content.name == c.name)
                    content.transport.candidates.extend(c.transport.candidates)
                    if isinstance(content.transport, jingle.IceUdpTransport):
                        if not content.transport.ufrag and c.transport.ufrag:
                            content.transport.ufrag = c.transport.ufrag
                        if not content.transport.password and c.transport.password:
                            content.transport.password = c.transport.password
            del self._pending_transport_info_stanzas[:]

            remote_sdp = jingle_to_sdp(stanza.jingle)
            try:
                self._sdp_negotiator.set_remote_answer(remote_sdp)
                self._sdp_negotiator.negotiate()
            except SIPCoreError as e:
                for stream in self.proposed_streams:
                    notification.center.remove_observer(self, sender=stream)
                    stream.deactivate()
                    stream.end()
                self._fail(originator='remote',
                           reason='incompatible-parameters',
                           description=str(e))
                return

            # Get active SDPs (negotiator may make changes)
            local_sdp = self._sdp_negotiator.active_local
            remote_sdp = self._sdp_negotiator.active_remote

            notification.center.post_notification('JingleSessionWillStart',
                                                  sender=self)
            stream_map = dict(
                (stream.index, stream) for stream in self.proposed_streams)
            for index, local_media in enumerate(local_sdp.media):
                remote_media = remote_sdp.media[index]
                stream = stream_map[index]
                if remote_media.port:
                    stream.start(local_sdp, remote_sdp, index)
                else:
                    notification.center.remove_observer(self, sender=stream)
                    self.proposed_streams.remove(stream)
                    del stream_map[stream.index]
                    stream.deactivate()
                    stream.end()
            removed_streams = [
                stream for stream in self.proposed_streams
                if stream.index >= len(local_sdp.media)
            ]
            for stream in removed_streams:
                notification.center.remove_observer(self, sender=stream)
                self.proposed_streams.remove(stream)
                del stream_map[stream.index]
                stream.deactivate()
                stream.end()

            try:
                with api.timeout(self.media_stream_timeout):
                    wait_count = len(self.proposed_streams)
                    while wait_count > 0:
                        notification = operation.channel.wait()
                        if notification.name == 'MediaStreamDidStart':
                            wait_count -= 1
            except (MediaStreamDidFailError, api.TimeoutError) as e:
                for stream in self.proposed_streams:
                    notification.center.remove_observer(self, sender=stream)
                    stream.deactivate()
                    stream.end()
                if isinstance(e, api.TimeoutError):
                    error = 'media stream timed out while starting'
                else:
                    error = 'media stream failed: %s' % e.data.reason
                self._fail(originator='local',
                           reason='failed-application',
                           description=error)
            else:
                self.state = 'connected'
                self.streams = self.proposed_streams
                self.proposed_streams = None
                self.start_time = datetime.now()
                notification.center.post_notification(
                    'JingleSessionDidStart', self,
                    NotificationData(streams=self.streams))
        elif notification.name == 'XMPPGotJingleTransportInfo':
            if self.state != 'connecting':
                # ICE trickling not supported yet, so only accept candidates before accept
                return

            for c in stanza.jingle.content:
                content = next(content for content in stanza.jingle.content
                               if content.name == c.name)
                content.transport.candidates.extend(c.transport.candidates)
                if isinstance(content.transport,
                              jingle.IceUdpTransport) or isinstance(
                                  content.transport, jingle.RawUdpTransport):
                    for cand in content.transport.candidates:
                        if cand.port == 0:
                            continue

                        idx = "%s:%s:%s" % (cand.protocol, cand.ip, cand.port)
                        if idx in self.candidates:
                            continue

                        self.candidates.add(idx)
                        self._pending_transport_info_stanzas.append(stanza)
示例#18
0
    def test_api(self):
        # Nothing happens if with-block finishes before the timeout expires
        with timeout(DELAY * 2):
            sleep(DELAY)
        sleep(DELAY * 2)  # check if timer was actually cancelled

        # An exception will be raised if it's not
        try:
            with timeout(DELAY):
                sleep(DELAY * 2)
        except TimeoutError:
            pass
        else:
            raise AssertionError('must raise TimeoutError')

        # You can customize the exception raised:
        try:
            with timeout(DELAY, IOError("Operation takes way too long")):
                sleep(DELAY * 2)
        except IOError as ex:
            assert str(ex) == "Operation takes way too long", repr(ex)

        # Providing classes instead of values should be possible too:
        try:
            with timeout(DELAY, ValueError):
                sleep(DELAY * 2)
        except ValueError:
            pass

        # basically, anything that greenlet.throw accepts work:
        try:
            1 / 0
        except:
            try:
                with timeout(DELAY, *sys.exc_info()):
                    sleep(DELAY * 2)
                    raise AssertionError('should not get there')
                raise AssertionError('should not get there')
            except ZeroDivisionError:
                pass
        else:
            raise AssertionError('should not get there')

        # It's possible to cancel the timer inside the block:
        with timeout(DELAY) as timer:
            timer.cancel()
            sleep(DELAY * 2)

        # To silent the exception, pass None as second parameter. The with-block
        # will be interrupted with _SilentException, but it won't be propagated
        # outside.
        XDELAY = 0.1
        start = time.time()
        with timeout(XDELAY, None):
            sleep(XDELAY * 2)
        delta = (time.time() - start)
        assert delta < XDELAY * 2, delta

        # passing None as seconds disables the timer
        with timeout(None):
            sleep(DELAY)
        sleep(DELAY)