Exemplo n.º 1
0
    def _render(self, request):
        time_in = utils.now()
        # if not self._check_headers(request):
        #     self._render_error(Failure(InvalidHeaderError()), request, None)
        #     return server.NOT_DONE_YET
        session = request.getSession()
        session_id = session.uid
        finished_deferred = request.notifyFinish()

        if self._use_authentication:
            # if this is a new session, send a new secret and set the expiration
            # otherwise, session.touch()
            if self._initialize_session(session_id):

                def expire_session():
                    self._unregister_user_session(session_id)

                session.notifyOnExpire(expire_session)
                message = "OK"
                request.setResponseCode(200)
                self._set_headers(request, message, True)
                self._render_message(request, message)
                return server.NOT_DONE_YET
            else:
                session.touch()

        request.content.seek(0, 0)
        content = request.content.read().decode()
        try:
            parsed = jsonrpclib.loads(content)
        except json.JSONDecodeError:
            log.warning("Unable to decode request json")
            self._render_error(
                JSONRPCError(None, code=JSONRPCError.CODE_PARSE_ERROR),
                request, None)
            return server.NOT_DONE_YET

        request_id = None
        try:
            function_name = parsed.get('method')
            args = parsed.get('params', {})
            request_id = parsed.get('id', None)
            token = parsed.pop('hmac', None)
        except AttributeError as err:
            log.warning(err)
            self._render_error(
                JSONRPCError(None, code=JSONRPCError.CODE_INVALID_REQUEST),
                request, request_id)
            return server.NOT_DONE_YET

        reply_with_next_secret = False
        if self._use_authentication:
            try:
                self._verify_token(session_id, parsed, token)
            except InvalidAuthenticationToken as err:
                log.warning("API validation failed")
                self._render_error(
                    JSONRPCError.create_from_exception(
                        str(err),
                        code=JSONRPCError.CODE_AUTHENTICATION_ERROR,
                        traceback=format_exc()), request, request_id)
                return server.NOT_DONE_YET
            request.addCookie("TWISTED_SESSION", session_id)
            self._update_session_secret(session_id)
            reply_with_next_secret = True

        try:
            fn = self._get_jsonrpc_method(function_name)
        except UnknownAPIMethodError as err:
            log.warning('Failed to get function %s: %s', function_name, err)
            self._render_error(
                JSONRPCError(None, code=JSONRPCError.CODE_METHOD_NOT_FOUND),
                request, request_id)
            return server.NOT_DONE_YET

        if args in (EMPTY_PARAMS, []):
            _args, _kwargs = (), {}
        elif isinstance(args, dict):
            _args, _kwargs = (), args
        elif len(args) == 1 and isinstance(args[0], dict):
            # TODO: this is for backwards compatibility. Remove this once API and UI are updated
            # TODO: also delete EMPTY_PARAMS then
            _args, _kwargs = (), args[0]
        elif len(args) == 2 and isinstance(args[0], list) and isinstance(
                args[1], dict):
            _args, _kwargs = args
        else:
            raise ValueError('invalid args format')

        params_error, erroneous_params = self._check_params(fn, _args, _kwargs)
        if params_error is not None:
            params_error_message = '{} for {} command: {}'.format(
                params_error, function_name, ', '.join(erroneous_params))
            log.warning(params_error_message)
            self._render_error(
                JSONRPCError(params_error_message,
                             code=JSONRPCError.CODE_INVALID_PARAMS), request,
                request_id)
            return server.NOT_DONE_YET

        try:
            result = fn(self, *_args, **_kwargs)
            if isinstance(result, Deferred):
                d = result
            elif isinstance(result, Failure):
                d = defer.fail(result)
            elif asyncio.iscoroutine(result):
                d = Deferred.fromFuture(asyncio.ensure_future(result))
            else:
                d = defer.succeed(result)
        except:
            d = defer.fail(Failure(captureVars=Deferred.debug))

        # finished_deferred will callback when the request is finished
        # and errback if something went wrong. If the errback is
        # called, cancel the deferred stack. This is to prevent
        # request.finish() from being called on a closed request.
        finished_deferred.addErrback(self._handle_dropped_request, d,
                                     function_name)

        d.addCallback(self._callback_render, request, request_id,
                      reply_with_next_secret)
        d.addErrback(trap, ConnectionDone, ConnectionLost,
                     defer.CancelledError)
        d.addErrback(self._render_error, request, request_id)
        d.addBoth(lambda _: log.debug("%s took %f", function_name,
                                      (utils.now() - time_in).total_seconds()))
        return server.NOT_DONE_YET
Exemplo n.º 2
0
    def _render(self, request):
        time_in = utils.now()
        # assert self._check_headers(request), InvalidHeaderError
        session = request.getSession()
        session_id = session.uid
        finished_deferred = request.notifyFinish()

        if self._use_authentication:
            # if this is a new session, send a new secret and set the expiration
            # otherwise, session.touch()
            if self._initialize_session(session_id):

                def expire_session():
                    self._unregister_user_session(session_id)

                session.startCheckingExpiration()
                session.notifyOnExpire(expire_session)
                message = "OK"
                request.setResponseCode(200)
                self._set_headers(request, message, True)
                self._render_message(request, message)
                return server.NOT_DONE_YET
            else:
                session.touch()

        request.content.seek(0, 0)
        content = request.content.read()
        try:
            parsed = jsonrpclib.loads(content)
        except ValueError:
            log.warning("Unable to decode request json")
            self._render_error(
                JSONRPCError(None, JSONRPCError.CODE_PARSE_ERROR), request,
                None)
            return server.NOT_DONE_YET

        id_ = None
        try:
            function_name = parsed.get('method')
            is_queued = function_name in self.queued_methods
            args = parsed.get('params', {})
            id_ = parsed.get('id', None)
            token = parsed.pop('hmac', None)
        except AttributeError as err:
            log.warning(err)
            self._render_error(
                JSONRPCError(None, code=JSONRPCError.CODE_INVALID_REQUEST),
                request, id_)
            return server.NOT_DONE_YET

        reply_with_next_secret = False
        if self._use_authentication:
            if function_name in self.authorized_functions:
                try:
                    self._verify_token(session_id, parsed, token)
                except InvalidAuthenticationToken as err:
                    log.warning("API validation failed")
                    self._render_error(
                        JSONRPCError.create_from_exception(
                            err.message,
                            code=JSONRPCError.CODE_AUTHENTICATION_ERROR,
                            traceback=format_exc()), request, id_)
                    return server.NOT_DONE_YET
                self._update_session_secret(session_id)
                reply_with_next_secret = True

        try:
            function = self._get_jsonrpc_method(function_name)
        except UnknownAPIMethodError as err:
            log.warning('Failed to get function %s: %s', function_name, err)
            self._render_error(
                JSONRPCError(None, JSONRPCError.CODE_METHOD_NOT_FOUND),
                request, id_)
            return server.NOT_DONE_YET
        except NotAllowedDuringStartupError as err:
            log.warning('Function not allowed during startup %s: %s',
                        function_name, err)
            self._render_error(
                JSONRPCError(
                    "This method is unavailable until the daemon is fully started",
                    code=JSONRPCError.CODE_INVALID_REQUEST), request, id_)
            return server.NOT_DONE_YET
        except DeprecatedAPIMethodError:
            log.warning('API function is deprecated %s', function_name)
            self._render_error(
                JSONRPCError(None, JSONRPCError.CODE_METHOD_NOT_FOUND),
                request, id_)
            return server.NOT_DONE_YET

        if args == EMPTY_PARAMS or args == []:
            args_dict = {}
            _args, _kwargs = (), {}
        elif isinstance(args, dict):
            args_dict = args
        elif len(args) == 1 and isinstance(args[0], dict):
            # TODO: this is for backwards compatibility. Remove this once API and UI are updated
            # TODO: also delete EMPTY_PARAMS then
            args_dict = args[0]
            _args, _kwargs = (), args
        elif isinstance(args, list):
            _args, _kwargs = args, {}
        else:
            # d = defer.maybeDeferred(function, *args)  # if we want to support positional args too
            raise ValueError('Args must be a dict')

        params_error, erroneous_params = self._check_params(
            function, args_dict)
        if params_error is not None:
            params_error_message = '{} for {} command: {}'.format(
                params_error, function_name, ', '.join(erroneous_params))
            log.warning(params_error_message)
            self._render_error(
                JSONRPCError(params_error_message,
                             code=JSONRPCError.CODE_INVALID_PARAMS), request,
                id_)
            return server.NOT_DONE_YET

        if is_queued:
            d_lock = self._call_lock.get(function_name, False)
            if not d_lock:
                d = defer.maybeDeferred(function, self, **args_dict)
                self._call_lock[function_name] = finished_deferred

                def _del_lock(*args):
                    if function_name in self._call_lock:
                        del self._call_lock[function_name]
                    if args:
                        return args

                finished_deferred.addCallback(_del_lock)

            else:
                log.info("queued %s", function_name)
                d = d_lock
                d.addBoth(
                    lambda _: log.info("running %s from queue", function_name))
                d.addCallback(
                    lambda _: defer.maybeDeferred(function, self, **args_dict))
        else:
            d = defer.maybeDeferred(function, self, **args_dict)

        # finished_deferred will callback when the request is finished
        # and errback if something went wrong. If the errback is
        # called, cancel the deferred stack. This is to prevent
        # request.finish() from being called on a closed request.
        finished_deferred.addErrback(self._handle_dropped_request, d,
                                     function_name)

        d.addCallback(self._callback_render, request, id_,
                      reply_with_next_secret)
        # TODO: don't trap RuntimeError, which is presently caught to
        # handle deferredLists that won't peacefully cancel, namely
        # get_lbry_files
        d.addErrback(trap, ConnectionDone, ConnectionLost,
                     defer.CancelledError, RuntimeError)
        d.addErrback(log.fail(self._render_error, request, id_),
                     'Failed to process %s', function_name)
        d.addBoth(lambda _: log.debug("%s took %f", function_name,
                                      (utils.now() - time_in).total_seconds()))
        return server.NOT_DONE_YET
Exemplo n.º 3
0
    def render(self, request):
        time_in = utils.now()
        assert self._check_headers(request), InvalidHeaderError
        session = request.getSession()
        session_id = session.uid
        finished_deferred = request.notifyFinish()

        if self._use_authentication:
            # if this is a new session, send a new secret and set the expiration
            # otherwise, session.touch()
            if self._initialize_session(session_id):

                def expire_session():
                    self._unregister_user_session(session_id)

                session.startCheckingExpiration()
                session.notifyOnExpire(expire_session)
                message = "OK"
                request.setResponseCode(self.OK)
                self._set_headers(request, message, True)
                self._render_message(request, message)
                return server.NOT_DONE_YET
            else:
                session.touch()

        request.content.seek(0, 0)
        content = request.content.read()
        try:
            parsed = jsonrpclib.loads(content)
        except ValueError:
            log.warning("Unable to decode request json")
            self._render_error_string('Invalid JSON', request, None)
            return server.NOT_DONE_YET

        function_name = parsed.get('method')
        args = parsed.get('params', {})
        id_ = parsed.get('id')
        token = parsed.pop('hmac', None)
        version = self._get_jsonrpc_version(parsed.get('jsonrpc'), id_)

        reply_with_next_secret = False
        if self._use_authentication:
            if function_name in self.authorized_functions:
                try:
                    self._verify_token(session_id, parsed, token)
                except InvalidAuthenticationToken as err:
                    log.warning("API validation failed")
                    self._render_error(
                        err,
                        request,
                        id_,
                        version=version,
                        response_code=AuthJSONRPCServer.UNAUTHORIZED)
                    return server.NOT_DONE_YET
                self._update_session_secret(session_id)
                reply_with_next_secret = True

        try:
            function = self._get_jsonrpc_method(function_name)
        except (UnknownAPIMethodError, NotAllowedDuringStartupError) as err:
            log.warning('Failed to get function %s: %s', function_name, err)
            self._render_error(err, request, version)
            return server.NOT_DONE_YET

        if args == EMPTY_PARAMS or args == []:
            d = defer.maybeDeferred(function)
        elif isinstance(args, dict):
            d = defer.maybeDeferred(function, **args)
        elif len(args) == 1 and isinstance(args[0], dict):
            # TODO: this is for backwards compatibility. Remove this once API and UI are updated
            # TODO: also delete EMPTY_PARAMS then
            d = defer.maybeDeferred(function, **args[0])
        else:
            # d = defer.maybeDeferred(function, *args)  # if we want to support positional args too
            raise ValueError('Args must be a dict')

        # finished_deferred will callback when the request is finished
        # and errback if something went wrong. If the errback is
        # called, cancel the deferred stack. This is to prevent
        # request.finish() from being called on a closed request.
        finished_deferred.addErrback(self._handle_dropped_request, d,
                                     function_name)

        d.addCallback(self._callback_render, request, id_, version,
                      reply_with_next_secret)
        # TODO: don't trap RuntimeError, which is presently caught to
        # handle deferredLists that won't peacefully cancel, namely
        # get_lbry_files
        d.addErrback(trap, ConnectionDone, ConnectionLost,
                     defer.CancelledError, RuntimeError)
        d.addErrback(
            log.fail(self._render_error, request, id_, version=version),
            'Failed to process %s', function_name)
        d.addBoth(lambda _: log.debug("%s took %f", function_name,
                                      (utils.now() - time_in).total_seconds()))
        return server.NOT_DONE_YET
Exemplo n.º 4
0
    def render(self, request):
        time_in = utils.now()
        assert self._check_headers(request), InvalidHeaderError
        session = request.getSession()
        session_id = session.uid
        finished_deferred = request.notifyFinish()

        if self._use_authentication:
            # if this is a new session, send a new secret and set the expiration
            # otherwise, session.touch()
            if self._initialize_session(session_id):
                def expire_session():
                    self._unregister_user_session(session_id)

                session.startCheckingExpiration()
                session.notifyOnExpire(expire_session)
                message = "OK"
                request.setResponseCode(self.OK)
                self._set_headers(request, message, True)
                self._render_message(request, message)
                return server.NOT_DONE_YET
            else:
                session.touch()

        request.content.seek(0, 0)
        content = request.content.read()
        try:
            parsed = jsonrpclib.loads(content)
        except ValueError:
            log.warning("Unable to decode request json")
            self._render_error_string('Invalid JSON', request, None)
            return server.NOT_DONE_YET

        function_name = parsed.get('method')
        args = parsed.get('params', {})
        id_ = parsed.get('id')
        token = parsed.pop('hmac', None)
        version = self._get_jsonrpc_version(parsed.get('jsonrpc'), id_)

        reply_with_next_secret = False
        if self._use_authentication:
            if function_name in self.authorized_functions:
                try:
                    self._verify_token(session_id, parsed, token)
                except InvalidAuthenticationToken as err:
                    log.warning("API validation failed")
                    self._render_error(
                        err, request, id_, version=version,
                        response_code=AuthJSONRPCServer.UNAUTHORIZED)
                    return server.NOT_DONE_YET
                self._update_session_secret(session_id)
                reply_with_next_secret = True

        try:
            function = self._get_jsonrpc_method(function_name)
        except (UnknownAPIMethodError, NotAllowedDuringStartupError) as err:
            log.warning('Failed to get function %s: %s', function_name, err)
            self._render_error(err, request, version)
            return server.NOT_DONE_YET

        if args == EMPTY_PARAMS or args == []:
            d = defer.maybeDeferred(function)
        elif isinstance(args, dict):
            d = defer.maybeDeferred(function, **args)
        elif len(args) == 1 and isinstance(args[0], dict):
            # TODO: this is for backwards compatibility. Remove this once API and UI are updated
            # TODO: also delete EMPTY_PARAMS then
            d = defer.maybeDeferred(function, **args[0])
        else:
            # d = defer.maybeDeferred(function, *args)  # if we want to support positional args too
            raise ValueError('Args must be a dict')

        # finished_deferred will callback when the request is finished
        # and errback if something went wrong. If the errback is
        # called, cancel the deferred stack. This is to prevent
        # request.finish() from being called on a closed request.
        finished_deferred.addErrback(self._handle_dropped_request, d, function_name)

        d.addCallback(self._callback_render, request, id_, version, reply_with_next_secret)
        # TODO: don't trap RuntimeError, which is presently caught to
        # handle deferredLists that won't peacefully cancel, namely
        # get_lbry_files
        d.addErrback(trap, ConnectionDone, ConnectionLost, defer.CancelledError, RuntimeError)
        d.addErrback(log.fail(self._render_error, request, id_, version=version),
                     'Failed to process %s', function_name)
        d.addBoth(lambda _: log.debug("%s took %f",
                                      function_name,
                                      (utils.now() - time_in).total_seconds()))
        return server.NOT_DONE_YET
Exemplo n.º 5
0
    def _render(self, request):
        time_in = utils.now()
        # assert self._check_headers(request), InvalidHeaderError
        session = request.getSession()
        session_id = session.uid
        finished_deferred = request.notifyFinish()

        if self._use_authentication:
            # if this is a new session, send a new secret and set the expiration
            # otherwise, session.touch()
            if self._initialize_session(session_id):
                def expire_session():
                    self._unregister_user_session(session_id)

                session.startCheckingExpiration()
                session.notifyOnExpire(expire_session)
                message = "OK"
                request.setResponseCode(200)
                self._set_headers(request, message, True)
                self._render_message(request, message)
                return server.NOT_DONE_YET
            else:
                session.touch()

        request.content.seek(0, 0)
        content = request.content.read()
        try:
            parsed = jsonrpclib.loads(content)
        except ValueError:
            log.warning("Unable to decode request json")
            self._render_error(JSONRPCError(None, JSONRPCError.CODE_PARSE_ERROR), request, None)
            return server.NOT_DONE_YET

        id_ = None
        try:
            function_name = parsed.get('method')
            args = parsed.get('params', {})
            id_ = parsed.get('id', None)
            token = parsed.pop('hmac', None)
        except AttributeError as err:
            log.warning(err)
            self._render_error(
                JSONRPCError(None, code=JSONRPCError.CODE_INVALID_REQUEST), request, id_
            )
            return server.NOT_DONE_YET

        reply_with_next_secret = False
        if self._use_authentication:
            if function_name in self.authorized_functions:
                try:
                    self._verify_token(session_id, parsed, token)
                except InvalidAuthenticationToken as err:
                    log.warning("API validation failed")
                    self._render_error(
                        JSONRPCError.create_from_exception(
                            err.message, code=JSONRPCError.CODE_AUTHENTICATION_ERROR,
                            traceback=format_exc()
                        ),
                        request, id_
                    )
                    return server.NOT_DONE_YET
                self._update_session_secret(session_id)
                reply_with_next_secret = True

        try:
            fn = self._get_jsonrpc_method(function_name)
        except UnknownAPIMethodError as err:
            log.warning('Failed to get function %s: %s', function_name, err)
            self._render_error(
                JSONRPCError(None, JSONRPCError.CODE_METHOD_NOT_FOUND),
                request, id_
            )
            return server.NOT_DONE_YET
        except NotAllowedDuringStartupError:
            log.warning('Function not allowed during startup: %s', function_name)
            self._render_error(
                JSONRPCError("This method is unavailable until the daemon is fully started",
                             code=JSONRPCError.CODE_INVALID_REQUEST),
                request, id_
            )
            return server.NOT_DONE_YET

        if args == EMPTY_PARAMS or args == []:
            args_dict = {}
            _args, _kwargs = (), {}
        elif isinstance(args, dict):
            args_dict = args
        elif len(args) == 1 and isinstance(args[0], dict):
            # TODO: this is for backwards compatibility. Remove this once API and UI are updated
            # TODO: also delete EMPTY_PARAMS then
            args_dict = args[0]
            _args, _kwargs = (), args
        else:
            # d = defer.maybeDeferred(function, *args)  # if we want to support positional args too
            raise ValueError('Args must be a dict')

        params_error, erroneous_params = self._check_params(fn, args_dict)
        if params_error is not None:
            params_error_message = '{} for {} command: {}'.format(
                params_error, function_name, ', '.join(erroneous_params)
            )
            log.warning(params_error_message)
            self._render_error(
                JSONRPCError(params_error_message, code=JSONRPCError.CODE_INVALID_PARAMS),
                request, id_
            )
            return server.NOT_DONE_YET

        d = defer.maybeDeferred(fn, self, **args_dict)

        # finished_deferred will callback when the request is finished
        # and errback if something went wrong. If the errback is
        # called, cancel the deferred stack. This is to prevent
        # request.finish() from being called on a closed request.
        finished_deferred.addErrback(self._handle_dropped_request, d, function_name)

        d.addCallback(self._callback_render, request, id_, reply_with_next_secret)
        # TODO: don't trap RuntimeError, which is presently caught to
        # handle deferredLists that won't peacefully cancel, namely
        # get_lbry_files
        d.addErrback(trap, ConnectionDone, ConnectionLost, defer.CancelledError, RuntimeError)
        d.addErrback(self._render_error, request, id_)
        d.addBoth(lambda _: log.debug("%s took %f",
                                      function_name,
                                      (utils.now() - time_in).total_seconds()))
        return server.NOT_DONE_YET