Exemple #1
0
    def _connect_backend(self, server_url):

        if self.backend_handle is not None:
            raise JanusCloudError('Already connected', JANUS_ERROR_INTERNAL_ERROR)
        if self.videocall_user is None:
            raise JanusCloudError('Register a username first', JANUS_VIDEOCALL_ERROR_REGISTER_FIRST)

        backend_session = get_backend_session(server_url,
                                              auto_destroy=BACKEND_SESSION_AUTO_DESTROY_TIME)
        backend_handle = backend_session.attach_handle(JANUS_VIDEOCALL_PACKAGE, handle_listener=self)

        # register
        try:
            body = {
                   'request':  'register',
                   'username': self.videocall_user.username
            }
            self._send_backend_meseage(backend_handle, body)
            if self._pending_set_body:
                self._send_backend_meseage(backend_handle, self._pending_set_body)
            if len(self._pending_candidates) > 0:
                backend_handle.send_trickle(candidates=self._pending_candidates)

        except Exception:
            backend_handle.detach()
            raise

        # connect & setup successfully
        if self._pending_set_body:
            self._pending_set_body = None
        if len(self._pending_candidates) > 0:
            self._pending_candidates.clear()
        self.backend_handle = backend_handle
Exemple #2
0
    def _send_aync_event(self, to_user, event_msg):
        if self.p2pcall_user is None:
            raise JanusCloudError('Register a username first', JANUS_P2PCALL_ERROR_REGISTER_FIRST)
        from_user = self.p2pcall_user.username
        peer = self._plugin.user_dao.get_by_username(to_user)
        if peer is None:
            raise JanusCloudError('Username \'{}\' doesn\'t exist'.format(to_user),
                                    JANUS_P2PCALL_ERROR_NO_SUCH_USERNAME)

        if peer.handle:
            # if dest user is handled by the same proxy, send to him directly
            log.debug('An async event ({}) is sent from \'{}\' to \'{}\' at local proxy'.format(
                event_msg, from_user, to_user
            ))

            peer.handle.on_async_event(from_user, event_msg)
        elif peer.api_url:
            # if dest user is handled by the other proxy, send to him by RESTful api
            log.debug('An async event ({}) is sent from \'{}\' to \'{}\' by {}'.format(
                event_msg, from_user, to_user, peer.api_url
            ))
            r = requests.post(peer.api_url,
                              json={
                                  'from_user': from_user,
                                  'async_event': event_msg
                              })
            if r.status_code != requests.codes.ok:
                try:
                    text = r.json()['info']
                except Exception:
                    text = r.text
                raise JanusCloudError(text, r.status_code)
        else:
            raise JanusCloudError('Username \'{}\' doesn\'t exist'.format(to_user),
                                    JANUS_P2PCALL_ERROR_NO_SUCH_USERNAME)
Exemple #3
0
    def incoming_request(self, request):
        """ handle the request from the transport module

        Args:
            request: the request to handle

        Returns:
            a dict to response to the initial client

        """

        try:
            log.debug('Request ({}) is incoming to handle'.format(
                request.message))
            handler = getattr(self, '_handle_' + request.janus)
            if handler is None or self._frontend_session_mgr is None:
                raise JanusCloudError(
                    'Unknown request \'{0}\''.format(request.janus),
                    JANUS_ERROR_UNKNOWN_REQUEST)

            # check secret valid
            if self._api_secret and request.janus not in {'ping', 'info'}:
                if self._api_secret != request.apisecret:
                    raise JanusCloudError(
                        "Unauthorized request (wrong or missing secret/token)",
                        JANUS_ERROR_UNAUTHORIZED)

            response = handler(request)
            log.debug('Response ({}) is to return'.format(response))
            return response
        except Exception as e:
            log.warn('Request ({}) processing failed'.format(request.message),
                     exc_info=True)
            return error_to_janus_msg(request.session_id, request.transaction,
                                      e)
Exemple #4
0
    def _handle_trickle(self, request):

        trickle_params_schema = Schema({
            Optional('candidate'): dict,
            Optional('candidates'): [dict],
            AutoDel(str): object  # for all other key we don't care
        })
        params = trickle_params_schema.validate(request.message)
        candidate = params.get('candidate')
        candidates = params.get('candidates')

        if candidate is None and candidates is None:
            raise JanusCloudError(
                'Missing mandatory element (candidate|candidates)',
                JANUS_ERROR_MISSING_MANDATORY_ELEMENT)

        if candidate and candidates:
            raise JanusCloudError('Can\'t have both candidate and candidates',
                                  JANUS_ERROR_INVALID_JSON)

        # dispatch to plugin handle
        handle = self._get_plugin_handle(request)
        handle.handle_trickle(candidate=candidate, candidates=candidates)

        return create_janus_msg('ack', request.session_id, request.transaction)
Exemple #5
0
 def send_trickle(self, candidate=None, candidates=None):
     if self._has_detach:
         raise JanusCloudError('backend handle {} has been destroyed'.format(self.handle_id),
                               JANUS_ERROR_PLUGIN_DETACH)
     if candidate is None and candidates is None:
         raise JanusCloudError('Missing mandatory element (candidate|candidates)',
                               JANUS_ERROR_MISSING_MANDATORY_ELEMENT)
     if candidate and candidates:
         raise JanusCloudError('Can\'t have both candidate and candidates',
                               JANUS_ERROR_INVALID_JSON)
     params = {}
     if candidate:
         params['candidate'] = candidate
     if candidates:
         params['candidates'] = candidates
     trickle_msg = create_janus_msg('trickle', handle_id=self.handle_id, **params)
     response = self._session.send_request(trickle_msg, ignore_ack=False)
     if response['janus'] == 'ack':
         pass # successful
     elif response['janus'] == 'error':
         raise JanusCloudError(response['error']['reason'], response['error']['code'])
     else:
         raise JanusCloudError(
             'unknown backend response {}'.format(response),
             JANUS_ERROR_BAD_GATEWAY)
Exemple #6
0
    def send_request(self, client, msg, ignore_ack=True):

        if self._has_destroy:
            raise JanusCloudError('Janus server already destory',
                                  JANUS_ERROR_SERVICE_UNAVAILABLE)
        if client is None:
            raise JanusCloudError('websocket client not ready',
                                  JANUS_ERROR_SERVICE_UNAVAILABLE)

        transaction_id = self._generate_new_tid()
        send_msg = dict.copy(msg)
        send_msg['transaction'] = transaction_id
        transaction = BackendTransaction(transaction_id,
                                         send_msg,
                                         url=client.url,
                                         ignore_ack=ignore_ack)

        try:
            self._transactions[transaction_id] = transaction
            log.debug('Send Request {} to Janus server: {}'.format(
                send_msg, client.url))
            client.send_message(send_msg)
            response = transaction.wait_response(timeout=self._request_timeout)
            log.debug('Receive Response {} from Janus server: {}'.format(
                response, client.url))
            if response['janus'] == 'error':
                raise JanusCloudError(response['error']['reason'],
                                      response['error']['code'])
            return response
        finally:
            self._transactions.pop(transaction_id, None)
Exemple #7
0
    def send_message(self, body, jsep=None):
        if self._has_detach:
            raise JanusCloudError(
                'backend handle {} has been destroyed'.format(self.handle_id),
                JANUS_ERROR_PLUGIN_DETACH)

        params = dict()
        params['body'] = body
        if jsep:
            params['jsep'] = jsep

        message = create_janus_msg('message',
                                   handle_id=self.handle_id,
                                   **params)
        response = self._session.send_request(message)
        if response['janus'] == 'event' or response['janus'] == 'success':
            data = response['plugindata']['data']
            reply_jsep = response.get('jsep')
            return data, reply_jsep
        elif response['janus'] == 'error':
            raise JanusCloudError(response['error']['reason'],
                                  response['error']['code'])
        else:
            raise JanusCloudError(
                'unknown backend response {}'.format(response),
                JANUS_ERROR_BAD_GATEWAY)
Exemple #8
0
    def handle_incoming_call(self, caller_username, backend_server_url):
        if self.videocall_user is None:
            raise JanusCloudError('Register a username first',
                                  JANUS_VIDEOCALL_ERROR_REGISTER_FIRST)
        if self.videocall_user.incall:
            raise JanusCloudError(
                'User {} busy'.format(self.videocall_user.username),
                JANUS_VIDEOCALL_ERROR_ALREADY_IN_CALL)
        self.videocall_user.incall = True
        self.videocall_user.peer_name = caller_username
        self.videocall_user.utime = time.time()
        try:
            self._connect_backend(backend_server_url)
            self._plugin.user_dao.update(self.videocall_user)
        except Exception:
            self._disconnect_backend()
            self.videocall_user.peer_name = ''
            self.videocall_user.incall = False
            raise

        # if incoming_call event cannot be received in INCOMMING_CALL_TIMEOUT(10) seconds,
        # auto disconnect the backend server
        if self._auto_disconnect_greenlet is None:
            self._auto_disconnect_greenlet = gevent.spawn_later(
                INCOMMING_CALL_TIMEOUT, self._auto_disconnect_routine)
Exemple #9
0
 def call_peer(self,
               peer_username,
               caller_username,
               backend_server_url,
               backend_keepalive_interval=10):
     peer = self.user_dao.get_by_username(peer_username)
     if peer is None:
         raise JanusCloudError(
             'Username \'{}\' doesn\'t exist'.format(peer_username),
             JANUS_VIDEOCALL_ERROR_NO_SUCH_USERNAME)
     if peer.handle:
         # the peer is handled by self
         peer.handle.handle_incoming_call(caller_username,
                                          backend_server_url)
     elif peer.api_url:
         # the peer is handled by the other janus-proxy
         caller = self.user_dao.get_by_username(caller_username)
         if caller is None or caller.handle is None:
             raise JanusCloudError('Not support relay http request',
                                   JANUS_VIDEOCALL_ERROR_INVALID_REQUEST)
         r = requests.post(peer.api_url,
                           data={
                               'caller_username':
                               caller_username,
                               'backend_server_url':
                               backend_server_url,
                               'backend_keepalive_interval':
                               str(backend_keepalive_interval)
                           })
         if r.status_code != requests.codes.ok:
             try:
                 text = r.json()['info']
             except Exception:
                 text = r.text
             raise JanusCloudError(text, r.status_code)
Exemple #10
0
 def _send_backend_meseage(backend_handle, body, jsep=None):
     if backend_handle is None:
         raise JanusCloudError('Not connected', JANUS_ERROR_INTERNAL_ERROR)
     data, reply_jsep = backend_handle.send_message(body=body, jsep=jsep)
     if 'error_code' in data:
         raise JanusCloudError(data.get('error','unknown'), data['error_code'])
     elif 'result' not in data:
         raise JanusCloudError('Invalid Response payload: {}'.format(data), JANUS_ERROR_BAD_GATEWAY)
     return data['result'], reply_jsep
Exemple #11
0
def _send_backend_message(backend_handle, body, jsep=None):
    if backend_handle is None:
        raise JanusCloudError('Not connected', JANUS_ERROR_INTERNAL_ERROR)
    data, reply_jsep = backend_handle.send_message(body=body, jsep=jsep)
    if 'error_code' in data:
        raise JanusCloudError(
            data.get('error', 'unknown'),
            data.get('error_code', JANUS_VIDEOROOM_ERROR_UNKNOWN_ERROR))

    return data, reply_jsep
Exemple #12
0
 def handle_async_event(self, to_user, from_user, async_event):
     p2pcall_user = self.user_dao.get_by_username(to_user)
     if p2pcall_user is None:
         raise JanusCloudError('Username \'{}\' doesn\'t exist'.format(to_user),
                                 JANUS_P2PCALL_ERROR_NO_SUCH_USERNAME)
     if p2pcall_user.handle is None:
         raise JanusCloudError('Not support relay http request',
                                     JANUS_P2PCALL_ERROR_INVALID_REQUEST)
     log.debug('an async event ({}) from \'{}\' to \'{}\' is received by http API'.
               format(async_event, from_user, to_user))
     p2pcall_user.handle.on_async_event(from_user, async_event)
Exemple #13
0
 def _get_session(self, request):
     if request.session_id == 0:
         raise JanusCloudError(
             "Unhandled request '{}' at this path".format(request.janus),
             JANUS_ERROR_INVALID_REQUEST_PATH)
     session = self._frontend_session_mgr.find_session(request.session_id)
     if session is None:
         raise JanusCloudError(
             'No such session {}'.format(request.session_id),
             JANUS_ERROR_SESSION_NOT_FOUND)
     session.activate()
     return session
Exemple #14
0
 def _get_plugin_handle(self, request):
     session = self._get_session(request)
     if request.handle_id == 0:
         raise JanusCloudError(
             "Unhandled request '{}' at this path".format(request.janus),
             JANUS_ERROR_INVALID_REQUEST_PATH)
     handle = session.get_handle(request.handle_id)
     if handle is None:
         raise JanusCloudError(
             "No such handle {} in session {}".format(
                 request.handle_id, request.session_id),
             JANUS_ERROR_HANDLE_NOT_FOUND)
     return handle
Exemple #15
0
 def _create_janus_session(self):
     response = self.send_request(create_janus_msg('create'))
     if response['janus'] == 'success':
         return response['data']['id']
     elif response['janus'] == 'error':
         raise JanusCloudError(
             'Create session error for Janus server {} with reason {}'.
             format(self.url,
                    response['error']['reason']), response['error']['code'])
     else:
         raise JanusCloudError(
             'Create session error for Janus server: {} with invalid response {}'
             .format(self.url, response), JANUS_ERROR_BAD_GATEWAY)
Exemple #16
0
 def _get_session_timeout(self):
     response = self.send_request(create_janus_msg('info'))
     if response['janus'] == 'server_info':
         return response.get('session-timeout', 30)
     elif response['janus'] == 'error':
         raise JanusCloudError(
             'Create session error for Janus server {} with reason {}'.
             format(self.url,
                    response['error']['reason']), response['error']['code'])
     else:
         raise JanusCloudError(
             'Create session error for Janus server: {} with invalid response {}'
             .format(self.url, response), JANUS_ERROR_BAD_GATEWAY)
Exemple #17
0
    def attach_handle(self,
                      plugin_package_name,
                      opaque_id=None,
                      handle_listener=None):
        """

        :param plugin_pacakge_name:  str plugin package name
        :param opaque_id:   str opaque id
        :param handle_listener: handle related  callback listener which cannot block
        :return: BackendHandle object
        """
        if self.state == BACKEND_SESSION_STATE_DESTROYED:
            raise JanusCloudError(
                'Session has destroy for Janus server: {}'.format(self.url),
                JANUS_ERROR_SERVICE_UNAVAILABLE)

        attach_request_msg = create_janus_msg('attach',
                                              plugin=plugin_package_name)
        if opaque_id:
            attach_request_msg['opaque_id'] = opaque_id

        response = self.send_request(attach_request_msg)  # would block for IO
        if response['janus'] == 'success':
            handle_id = response['data']['id']
        elif response['janus'] == 'error':
            raise JanusCloudError(
                'attach error for Janus server {} with reason {}'.format(
                    self.url, response['error']['reason']),
                response['error']['code'])
        else:
            raise JanusCloudError(
                'attach error for Janus server: {} with invalid response {}'.
                format(self.url, response), JANUS_ERROR_BAD_GATEWAY)

        # check again when wake up from block IO
        if self.state == BACKEND_SESSION_STATE_DESTROYED:
            raise JanusCloudError(
                'Session has destroy for Janus server: {}'.format(self.url),
                JANUS_ERROR_SERVICE_UNAVAILABLE)

        handle = BackendHandle(handle_id,
                               plugin_package_name,
                               self,
                               opaque_id=opaque_id,
                               handle_listener=handle_listener)
        self._handles[handle_id] = handle
        if self._auto_destroy_greenlet:
            gevent.kill(self._auto_destroy_greenlet)
            self._auto_destroy_greenlet = None
        return handle
Exemple #18
0
 def send_hangup(self):
     if self._has_detach:
         raise JanusCloudError('backend handle {} has been destroyed'.format(self.handle_id),
                               JANUS_ERROR_PLUGIN_DETACH)
     hangup_msg = create_janus_msg('hangup', handle_id=self.handle_id)
     response = self._session.send_request(hangup_msg)
     if response['janus'] == 'success':
         pass # successful
     elif response['janus'] == 'error':
         raise JanusCloudError(response['error']['reason'], response['error']['code'])
     else:
         raise JanusCloudError(
             'unknown backend response {}'.format(response),
             JANUS_ERROR_BAD_GATEWAY)
 def find_session(self, session_id):
     session = self._sessions.get(session_id)
     if session is None:
         log.error("Couldn't find any session {}".format(session_id))
         raise JanusCloudError('No such session {}'.format(session_id),
                               JANUS_ERROR_SESSION_NOT_FOUND)
     return session
Exemple #20
0
 def update(self, videocall_user):
     org_videocall_user = self._users_by_name.get(videocall_user.username)
     if not org_videocall_user:
         raise JanusCloudError(
             'server {} NOT found'.format(videocall_user.username),
             JANUS_ERROR_NOT_FOUND)
     org_videocall_user.__dict__.update(videocall_user.__dict__)
Exemple #21
0
 def wait_response(self, timeout=None):
     ready = self._response_ready.wait(timeout=timeout)
     if not ready:
         raise JanusCloudError(
             'Request {} Timeout for backend Janus server: {}'.format(
                 self.request_msg, self._url), JANUS_ERROR_GATEWAY_TIMEOUT)
     return self._response
Exemple #22
0
    def send_request(self, msg, ignore_ack=True, timeout=30):

        if self.state == BACKEND_SESSION_STATE_DESTROYED:
            raise JanusCloudError(
                'Session has destroy for Janus server: {}'.format(self.url),
                JANUS_ERROR_SERVICE_UNAVAILABLE)
        transaction_id = self._genrate_new_tid()

        send_msg = dict.copy(msg)
        send_msg['session_id'] = self.session_id
        send_msg['transaction'] = transaction_id
        if self._api_secret:
            send_msg['apisecret'] = self._api_secret
        transaction = BackendTransaction(transaction_id,
                                         send_msg,
                                         url=self.url,
                                         ignore_ack=ignore_ack)
        try:
            self._transactions[transaction_id] = transaction
            log.debug('Send Request {} to Janus server: {}'.format(
                send_msg, self.url))
            self._ws_client.send_message(send_msg)
            response = transaction.wait_response(timeout=timeout)
            log.debug('Receive Response {} from Janus server: {}'.format(
                response, self.url))
            return response
        finally:
            self._transactions.pop(transaction_id, None)
Exemple #23
0
    def post(self):
        data = {
            'name': self._janus_server.server_name,
            'url': self._janus_server.public_url,
            'status': self._janus_server.status,
            'start_time': self._janus_server.start_time,
            'expire': self.expire,
        }

        if self._janus_server.session_num >= 0:
            data['session_num'] = int(self._janus_server.session_num)
        if self._janus_server.handle_num >= 0:
            data['handle_num'] = int(self._janus_server.handle_num)

        for i in range(len(self.post_urls)):
            url = self.post_urls[self._cur_index]
            self._cur_index += 1
            if self._cur_index >= len(self.post_urls):
                self._cur_index = 0
            try:
                r = self._post_session.post(url,
                                            data=data,
                                            timeout=self._http_timeout)
                if r.status_code == requests.codes.ok:
                    return True
                else:
                    raise JanusCloudError(
                        'HTTP Return error (Status code: {}, text: {})'.format(
                            r.status_code, r.text), r.status_code)

            except Exception as e:
                log.warning('Http post failed for url {}: {}'.format(url, e))
                pass

        return False
Exemple #24
0
    def start(self):
        """ start the watcher and launch its process
        
        """
        if self._started:
            return

        if self._popen is not None:
            # other greenlet is stopping this watcher
            raise JanusCloudError("ProcWatcher (%d) in Stopping" % self.wid,
                                  500)

        try:
            self._launch_process()  # start up the process

            self.auto_restart_count = 0
            self._started = True

            # spawn a poll greenlet to watch it
            self._poll_greenlet = gevent.spawn(self._polling_run,
                                               weakref.ref(self))

        except Exception:
            self._started = False
            self._poll_greenlet = None
            if self._popen is not None:
                self._popen.kill()
                self._on_process_terminate(-1)
            raise
Exemple #25
0
    def handle_hangup(self):
        if self.backend_handle is None:
            raise JanusCloudError('backend handle invalid',
                                  JANUS_ERROR_BAD_GATEWAY)

        # log.info('handle_hangup for echotest Handle {}'.format(self.handle_id))
        self.backend_handle.send_hangup()
Exemple #26
0
    def _handle_async_message(self, transaction, body, jsep):
        try:
            if self.backend_handle is None:
                raise JanusCloudError('backend handle invalid',
                                      JANUS_ERROR_BAD_GATEWAY)

            data, reply_jsep = self.backend_handle.send_message(body=body,
                                                                jsep=jsep)
            self._push_plugin_event(data, reply_jsep, transaction)

        except JanusCloudError as e:
            log.exception('Fail to send message to backend handle {}'.format(
                self.backend_handle.handle_id))
            self._push_plugin_event(
                {
                    'echotest': 'event',
                    'error_code': e.code,
                    'error': str(e),
                },
                transaction=transaction)
        except Exception as e:
            log.exception('Fail to send message to backend handle {}'.format(
                self.backend_handle.handle_id))
            self._push_plugin_event(
                {
                    'echotest': 'event',
                    'error_code': JANUS_ERROR_BAD_GATEWAY,
                    'error': str(e),
                },
                transaction=transaction)
Exemple #27
0
 def add(self, video_call_user):
     if video_call_user.username in self._users_by_name:
         raise JanusCloudError(
             'videocall user {} already in repo'.format(
                 video_call_user.name), JANUS_ERROR_CONFLICT)
     self._users_by_name[video_call_user.username] = copy.copy(
         video_call_user)
Exemple #28
0
 def handle_trickle(self, candidate=None, candidates=None):
     if self.backend_handle is None:
         raise JanusCloudError('backend handle invalid',
                               JANUS_ERROR_BAD_GATEWAY)
     # log.debug('handle_trickle for echotest handle {}.candidate:{} candidates:{}'.
     #          format(self.handle_id, candidate, candidates))
     self.backend_handle.send_trickle(candidate=candidate,
                                      candidates=candidates)
Exemple #29
0
 def _handle_destroy(self, request):
     if request.session_id == 0:
         raise JanusCloudError(
             "Unhandled request '{}' at this path".format(request.janus),
             JANUS_ERROR_INVALID_REQUEST_PATH)
     self._frontend_session_mgr.destroy_session(request.session_id)
     return create_janus_msg('success', request.session_id,
                             request.transaction)
Exemple #30
0
def add_poster(janus_server, post_type, name='', *args, **kwargs):
    poster_class = _poster_types.get(post_type)
    if poster_class is None:
        raise JanusCloudError('poster type {} not register'.format(post_type),
                              JANUS_ERROR_NOT_IMPLEMENTED)
    poster = poster_class(janus_server, post_type, name, *args, **kwargs)
    _posters.append(poster)
    return poster