Ejemplo n.º 1
0
    def test_webservice_skill_handler_dispatch_serialization_failure_throw_exc(
            self):
        self.mock_serializer.deserialize.side_effect = AskSdkException(
            "test deserialization exception")
        test_webservice_skill_handler = WebserviceSkillHandler(
            skill=self.mock_skill, verify_signature=False,
            verify_timestamp=False, verifiers=[self.mock_verifier])

        with self.assertRaises(AskSdkException) as exc:
            test_webservice_skill_handler.verify_request_and_dispatch(
                http_request_headers=None, http_request_body=None)

        self.assertIn(
            "test deserialization exception", str(exc.exception),
            "Webservice skill handler didn't raise deserialization exception "
            "during skill dispatch")

        self.assertFalse(
            self.mock_verifier.verify.called,
            "Webservice skill handler called verifier verify when request "
            "deserialization failed")

        self.assertFalse(
            self.mock_skill.invoke.called,
            "Webservice skill handler called skill invoke when request "
            "verification failed")
Ejemplo n.º 2
0
    def _create_webservice_handler(self, skill, verifiers):
        # type: (CustomSkill, List[AbstractVerifier]) -> None
        """Create the handler for request verification and dispatch.

        :param skill: A :py:class:`ask_sdk_core.skill.CustomSkill`
            instance. If you are using the skill builder from ask-sdk,
            then you can use the ``create`` method under it, to create a
            skill instance
        :type skill: ask_sdk_core.skill.CustomSkill
        :param verifiers: A list of verifiers, that needs to
            be applied on the input request, before invoking the
            request handlers.
        :type verifiers:
            list[ask_sdk_webservice_support.verifier.AbstractVerifier]
        :rtype: None
        """
        if verifiers is None:
            verifiers = []

        self._webservice_handler = WebserviceSkillHandler(
            skill=skill,
            verify_signature=current_app.config.get(
                VERIFY_SIGNATURE_APP_CONFIG, True),
            verify_timestamp=current_app.config.get(
                VERIFY_TIMESTAMP_APP_CONFIG, True),
            verifiers=verifiers)

        self._webservice_handler._add_custom_user_agent("flask-ask-sdk")
Ejemplo n.º 3
0
 def _create_webservice_handler(self):
     self._webservice_handler = WebserviceSkillHandler(
         skill=self._skill,
         verify_signature=self._verify_signature,
         verify_timestamp=self._verify_timestamp,
         verifiers=self._verifiers)
     self._webservice_handler._add_custom_user_agent("cherrypy-ask-sdk")
Ejemplo n.º 4
0
    def __init__(self,
                 skill,
                 verify_signature=True,
                 verify_timestamp=True,
                 verifiers=None):
        # type: (CustomSkill, bool, bool, List[AbstractVerifier]) -> None
        """Instantiate the view and set the verifiers on the handler.

        :param skill: A :py:class:`ask_sdk_core.skill.CustomSkill`
            instance. If you are using the skill builder from ask-sdk,
            then you can use the ``create`` method under it, to create a
            skill instance
        :type skill: ask_sdk_core.skill.CustomSkill
        :param verify_signature: Enable request signature verification
        :type verify_signature: bool
        :param verify_timestamp: Enable request timestamp verification
        :type verify_timestamp: bool
        :param verifiers: An optional list of verifiers, that needs to
            be applied on the input request, before invoking the
            request handlers. For more information, look at
            `hosting the skill as webservice <https://developer.amazon.com/docs/custom-skills/host-a-custom-skill-as-a-web-service.html>`_
        :type verifiers:
            list[ask_sdk_webservice_support.verifier.AbstractVerifier]
        :raises: :py:class:`TypeError` if
            the provided skill instance is not a
            :py:class:`ask_sdk_core.skill.CustomSkill` instance
        """
        self._skill = skill

        if not isinstance(skill, CustomSkill):
            raise TypeError(
                "Invalid skill instance provided. Expected a custom "
                "skill instance.")

        if verifiers is None:
            verifiers = []

        self._verifiers = verifiers

        if verify_signature:
            request_verifier = RequestVerifier(
                signature_cert_chain_url_key=SIGNATURE_CERT_CHAIN_URL_KEY,
                signature_key=SIGNATURE_KEY)
            self._verifiers.append(request_verifier)

        self._webservice_handler = WebserviceSkillHandler(
            skill=self._skill,
            verify_signature=False,
            verify_timestamp=verify_timestamp,
            verifiers=self._verifiers)
        self._webservice_handler._add_custom_user_agent("django-ask-sdk")

        super(SkillAdapter, self).__init__()
Ejemplo n.º 5
0
 def test_webservice_skill_handler_dispatch_runs_verification_skill_invoke(
         self):
     try:
         test_webservice_skill_handler = WebserviceSkillHandler(
             skill=self.mock_skill, verify_signature=False,
             verify_timestamp=False, verifiers=[self.mock_verifier])
         test_webservice_skill_handler.verify_request_and_dispatch(
             http_request_headers=None, http_request_body=None)
     except:
         self.fail(
             "Webservice skill handler failed request verification "
             "and request dispatch for a valid input request")
Ejemplo n.º 6
0
    def test_webservice_skill_handler_init_create_custom_user_agent(self):
        WebserviceSkillHandler(skill=self.mock_skill, verify_signature=False,
                               verify_timestamp=False)

        self.assertEqual(
            self.mock_skill.custom_user_agent, "ask-webservice",
            "Webservice skill handler didn't create new custom user agent "
            "value for a valid custom skill without a user agent")
Ejemplo n.º 7
0
    def test_webservice_skill_handler_init_invalid_skill_raise_exception(self):
        with self.assertRaises(TypeError) as exc:
            WebserviceSkillHandler(skill=None)

        self.assertIn(
            "Expected a custom skill instance", str(exc.exception),
            "Webservice skill handler didn't raise TypError on "
            "initialization when an invalid skill instance is provided")
Ejemplo n.º 8
0
    def test_webservice_skill_handler_init_append_custom_user_agent(self):
        self.mock_skill.custom_user_agent = "test-value"
        WebserviceSkillHandler(skill=self.mock_skill, verify_signature=False,
                               verify_timestamp=False)

        self.assertEqual(
            self.mock_skill.custom_user_agent, "test-value ask-webservice",
            "Webservice skill handler didn't update custom user agent "
            "for a valid custom skill with an existing user agent")
Ejemplo n.º 9
0
    def test_webservice_skill_handler_init_add_custom_user_agent(self):
        WebserviceSkillHandler(skill=self.mock_skill,
                               verify_signature=False,
                               verify_timestamp=False)

        self.assertEqual(
            self.mock_skill.custom_user_agent, " ask-webservice",
            "Webservice skill handler didn't update custom user agent "
            "for a valid custom skill")
Ejemplo n.º 10
0
    def test_webservice_skill_handler_init_with_no_verifiers(self):
        test_webservice_skill_handler = WebserviceSkillHandler(
            skill=self.mock_skill, verify_signature=False,
            verify_timestamp=False)

        default_verifiers = test_webservice_skill_handler._verifiers

        self.assertEqual(
            len(default_verifiers), 0,
            "Webservice skill handler initialized invalid number of "
            "default verifiers")
Ejemplo n.º 11
0
    def test_webservice_skill_handler_init_default_with_verifiers_set_correctly(
            self):
        test_verifier = mock.MagicMock(spec=AbstractVerifier)
        test_verifier.return_value = "Test"
        test_webservice_skill_handler = WebserviceSkillHandler(
            skill=self.mock_skill, verifiers=[test_verifier()])

        default_verifiers = test_webservice_skill_handler._verifiers

        self.assertEqual(
            len(default_verifiers), 3,
            "Webservice skill handler initialized invalid number of "
            "verifiers, when an input list is passed")
Ejemplo n.º 12
0
    def test_webservice_skill_handler_init_with_default_verifiers(self):
        test_webservice_skill_handler = WebserviceSkillHandler(
            skill=self.mock_skill)

        default_verifiers = test_webservice_skill_handler._verifiers

        self.assertEqual(
            len(default_verifiers), 2,
            "Webservice skill handler initialized invalid number of "
            "default verifiers")

        for verifier in default_verifiers:
            if not (isinstance(verifier, RequestVerifier) or
                    isinstance(verifier, TimestampVerifier)):
                self.fail(
                    "Webservice skill handler initialized invalid verifier "
                    "when left as default")
Ejemplo n.º 13
0
    def test_webservice_skill_handler_init_signature_check_disabled(self):
        test_webservice_skill_handler = WebserviceSkillHandler(
            skill=self.mock_skill, verify_signature=False)

        default_verifiers = test_webservice_skill_handler._verifiers

        self.assertEqual(
            len(default_verifiers), 1,
            "Webservice skill handler initialized invalid number of "
            "default verifiers, when signature verification env property is "
            "disabled")

        verifier = default_verifiers[0]
        self.assertIsInstance(
            verifier, TimestampVerifier,
            "Webservice skill handler initialized invalid default verifier, "
            "when request signature verification env property is set to false")
Ejemplo n.º 14
0
class SkillAdapter(View):
    """Provides a base interface to register skill and dispatch the request.

    The class constructor takes a
    :py:class:`ask_sdk_core.skill.CustomSkill` instance, an optional
    verify_request boolean, an optional verify_timestamp boolean and
    an optional list of
    :py:class:`ask_sdk_webservice_support.verifier.AbstractVerifier`
    instances.

    The :meth:`post` function is the only available method on the view,
    that intakes the input POST request from Alexa, verifies the request
    and dispatch it to the skill.

    By default, the
    :py:class:`ask_sdk_webservice_support.verifier.RequestVerifier` and
    :py:class:`ask_sdk_webservice_support.verifier.TimestampVerifier`
    instances are added to the skill verifier list. To disable this, set
    the ``verify_request`` and ``verify_timestamp`` input arguments to
    ``False`` respectively.

    For example, if you developed a skill using an instance
    of :py:class:`ask_sdk_core.skill_builder.SkillBuilder` or it's
    subclasses, then to register it as an endpoint in your django
    app ``example``, you can add the following in ``example.urls.py``:

    .. code-block:: python

        import skill
        from django_ask_sdk.skill_response import SkillAdapter

        view = SkillAdapter.as_view(skill=skill.sb.create())

        urlpatterns = [
            path("/myskill", view, name='index')
        ]
    """
    # Creating class attributes, since Django View `as_view` method
    # sets these from __init__ only if they exist on the class.
    skill = None
    verify_signature = None
    verify_timestamp = None
    verifiers = None

    def __init__(self,
                 skill,
                 verify_signature=True,
                 verify_timestamp=True,
                 verifiers=None):
        # type: (CustomSkill, bool, bool, List[AbstractVerifier]) -> None
        """Instantiate the view and set the verifiers on the handler.

        :param skill: A :py:class:`ask_sdk_core.skill.CustomSkill`
            instance. If you are using the skill builder from ask-sdk,
            then you can use the ``create`` method under it, to create a
            skill instance
        :type skill: ask_sdk_core.skill.CustomSkill
        :param verify_signature: Enable request signature verification
        :type verify_signature: bool
        :param verify_timestamp: Enable request timestamp verification
        :type verify_timestamp: bool
        :param verifiers: An optional list of verifiers, that needs to
            be applied on the input request, before invoking the
            request handlers. For more information, look at
            `hosting the skill as webservice <https://developer.amazon.com/docs/custom-skills/host-a-custom-skill-as-a-web-service.html>`_
        :type verifiers:
            list[ask_sdk_webservice_support.verifier.AbstractVerifier]
        :raises: :py:class:`TypeError` if
            the provided skill instance is not a
            :py:class:`ask_sdk_core.skill.CustomSkill` instance
        """
        self._skill = skill

        if not isinstance(skill, CustomSkill):
            raise TypeError(
                "Invalid skill instance provided. Expected a custom "
                "skill instance.")

        if verifiers is None:
            verifiers = []

        self._verifiers = verifiers

        if verify_signature:
            request_verifier = RequestVerifier(
                signature_cert_chain_url_key=SIGNATURE_CERT_CHAIN_URL_KEY,
                signature_key=SIGNATURE_KEY)
            self._verifiers.append(request_verifier)

        self._webservice_handler = WebserviceSkillHandler(
            skill=self._skill,
            verify_signature=False,
            verify_timestamp=verify_timestamp,
            verifiers=self._verifiers)
        self._webservice_handler._add_custom_user_agent("django-ask-sdk")

        super(SkillAdapter, self).__init__()

    @method_decorator(csrf_exempt)
    def dispatch(self, request, *args, **kwargs):
        # type: (HttpRequest, object, object) -> HttpResponse
        """Inspect the HTTP method and delegate to the view method.

        This is the default implementation of the
        :py:class:`django.views.View` method, which will inspect the
        HTTP method in the input request and delegate it to the
        corresponding method in the view. The only allowed method on
        this view is ``post``.

        :param request: The input request sent to the view
        :type request: django.http.HttpRequest
        :return: The response from the view
        :rtype: django.http.HttpResponse
        :raises: :py:class:`django.http.HttpResponseNotAllowed` if the
            method is invoked for other than HTTP POST request.
            :py:class:`django.http.HttpResponseBadRequest` if the
            request verification fails.
            :py:class:`django.http.HttpResponseServerError` for any
            internal exception.
        """
        return super(SkillAdapter, self).dispatch(request)

    def post(self, request, *args, **kwargs):
        # type: (HttpRequest, object, object) -> HttpResponse
        """The method that handles HTTP POST request on the view.

        This method is called when the view receives a HTTP POST
        request, which is generally the request sent from Alexa during
        skill invocation. The request is verified through the
        registered list of verifiers, before invoking the request
        handlers. The method returns a
        :py:class:`django.http.JsonResponse` in case of successful
        skill invocation.

        :param request: The input request sent by Alexa to the skill
        :type request: django.http.HttpRequest
        :return: The response from the skill to Alexa
        :rtype: django.http.JsonResponse
        :raises: :py:class:`django.http.HttpResponseBadRequest` if the
            request verification fails.
            :py:class:`django.http.HttpResponseServerError` for any
            internal exception.
        """
        try:
            content = request.body.decode(
                verifier_constants.CHARACTER_ENCODING)
            response = self._webservice_handler.verify_request_and_dispatch(
                http_request_headers=request.META, http_request_body=content)

            return JsonResponse(data=response, safe=False)
        except VerificationException:
            logger.exception(msg="Request verification failed")
            return HttpResponseBadRequest(
                content="Incoming request failed verification")
        except AskSdkException:
            logger.exception(msg="Skill dispatch exception")
            return HttpResponseServerError(
                content="Exception occurred during skill dispatch")
Ejemplo n.º 15
0
class SkillAdapter(object):
    """Provides a base interface to register skill and dispatch the request.

    The class constructor takes a
    :py:class:`ask_sdk_core.skill.CustomSkill` instance, the skill id,
    an optional list of
    :py:class:`ask_sdk_webservice_support.verifier.AbstractVerifier`
    instances and an optional flask application. One can also use the
    :meth:`init_app` method, to pass in a :py:class:`flask.Flask`
    application instance, to instantiate the config values and the
    webservice handler.

    The :meth:`dispatch_request` function can be used to map the input
    request to the skill invocation. The :meth:`register` function can
    also be used alternatively, to register the :meth:`dispatch_request`
    function to the provided route.

    By default, the
    :py:class:`ask_sdk_webservice_support.verifier.RequestVerifier` and
    :py:class:`ask_sdk_webservice_support.verifier.TimestampVerifier`
    instances are added to the skill verifier list. To disable this, set
    the :py:const:`VERIFY_SIGNATURE_APP_CONFIG` and
    :py:const:`VERIFY_TIMESTAMP_APP_CONFIG` app configuration values to
    ``False``.

    An instance of the extension is added to the application extensions
    mapping, under the key :py:const:`EXTENSION_NAME`. Since multiple
    skills can be configured on different routes in the same application,
    through multiple extension instances, each extension is added as a
    skill id mapping under the app extensions :py:const:`EXTENSION_NAME`
    dictionary.

    For example, to use this class with a skill created using ask-sdk-core:

    .. code-block:: python

        from flask import Flask
        from ask_sdk_core.skill_builder import SkillBuilder
        from flask_ask_sdk.skill_adapter import SkillAdapter

        app = Flask(__name__)
        skill_builder = SkillBuilder()
        # Register your intent handlers to skill_builder object

        skill_adapter = SkillAdapter(
            skill=skill_builder.create(), skill_id=<SKILL_ID>, app=app)

        @app.route("/"):
        def invoke_skill:
            return skill_adapter.dispatch_request()

    Alternatively, you can also use the ``register`` method:

    .. code-block:: python

        from flask import Flask
        from ask_sdk_core.skill_builder import SkillBuilder
        from flask_ask_sdk.skill_adapter import SkillAdapter

        app = Flask(__name__)
        skill_builder = SkillBuilder()
        # Register your intent handlers to skill_builder object

        skill_adapter = SkillAdapter(
            skill=skill_builder.create(), skill_id=<SKILL_ID>, app=app)

        skill_adapter.register(app=app, route="/")
    """
    def __init__(self, skill, skill_id, verifiers=None, app=None):
        # type: (CustomSkill, int, List[AbstractVerifier], Flask) -> None
        """Instantiate the extension and set the app config values.

        :param skill: A :py:class:`ask_sdk_core.skill.CustomSkill`
            instance. If you are using the skill builder from ask-sdk,
            then you can use the ``create`` method under it, to create a
            skill instance
        :type skill: ask_sdk_core.skill.CustomSkill
        :param skill_id: Skill ID for the skill instance. This is used
            to store the skill under the app extensions
        :type skill_id: int
        :param verifiers: An optional list of verifiers, that needs to
            be applied on the input request, before invoking the
            request handlers. For more information, look at
            `hosting the skill as webservice <https://developer.amazon.com/docs/custom-skills/host-a-custom-skill-as-a-web-service.html>`_
        :type verifiers:
            list[ask_sdk_webservice_support.verifier.AbstractVerifier]
        :param app: A :py:class:`flask.Flask` application instance
        :type app: flask.Flask
        :raises: :py:class:`TypeError` if
            the provided skill instance is not a
            :py:class:`ask_sdk_core.skill.CustomSkill` instance
        """
        self._skill_id = skill_id
        self._skill = skill
        self._webservice_handler = None

        if verifiers is None:
            verifiers = []

        self._verifiers = verifiers

        if not isinstance(skill, CustomSkill):
            raise TypeError(
                "Invalid skill instance provided. Expected a custom "
                "skill instance.")

        if app is not None:
            self.init_app(app)

    def init_app(self, app):
        # type: (Flask) -> None
        """Register the extension on the given Flask application.

        Use this function only when no Flask application was provided
        in the ``app`` keyword argument to the constructor of this class.

        The function sets ``True`` defaults for
        :py:const:`VERIFY_SIGNATURE_APP_CONFIG` and
        :py:const:`VERIFY_TIMESTAMP_APP_CONFIG` configurations. It adds
        the skill id: self instance mapping to the application
        extensions, and creates a
        :py:class:`ask_sdk_webservice_support.webservice_handler.WebserviceHandler`
        instance, for request verification and dispatch.

        :param app: A :py:class:`flask.Flask` application instance
        :type app: flask.Flask
        :rtype: None
        """
        app.config.setdefault(VERIFY_SIGNATURE_APP_CONFIG, True)
        app.config.setdefault(VERIFY_TIMESTAMP_APP_CONFIG, True)

        if EXTENSION_NAME not in app.extensions:
            app.extensions[EXTENSION_NAME] = {}

        app.extensions[EXTENSION_NAME][self._skill_id] = self

        with app.app_context():
            self._create_webservice_handler(self._skill, self._verifiers)

    def _create_webservice_handler(self, skill, verifiers):
        # type: (CustomSkill, List[AbstractVerifier]) -> None
        """Create the handler for request verification and dispatch.

        :param skill: A :py:class:`ask_sdk_core.skill.CustomSkill`
            instance. If you are using the skill builder from ask-sdk,
            then you can use the ``create`` method under it, to create a
            skill instance
        :type skill: ask_sdk_core.skill.CustomSkill
        :param verifiers: A list of verifiers, that needs to
            be applied on the input request, before invoking the
            request handlers.
        :type verifiers:
            list[ask_sdk_webservice_support.verifier.AbstractVerifier]
        :rtype: None
        """
        if verifiers is None:
            verifiers = []

        self._webservice_handler = WebserviceSkillHandler(
            skill=skill,
            verify_signature=current_app.config.get(
                VERIFY_SIGNATURE_APP_CONFIG, True),
            verify_timestamp=current_app.config.get(
                VERIFY_TIMESTAMP_APP_CONFIG, True),
            verifiers=verifiers)

        self._webservice_handler._add_custom_user_agent("flask-ask-sdk")

    def dispatch_request(self):
        # type: () -> Response
        """Method that handles request verification and routing.

        This method can be used as a function to register on the URL
        rule. The request is verified through the registered list of
        verifiers, before invoking the request handlers. The method
        returns a JSON response for the Alexa service to respond to the
        request.

        :return: The skill response for the input request
        :rtype: flask.Response
        :raises: :py:class:`werkzeug.exceptions.MethodNotAllowed` if the
            method is invoked for other than HTTP POST request.
            :py:class:`werkzeug.exceptions.BadRequest` if the
            verification fails.
            :py:class:`werkzeug.exceptions.InternalServerError` for any
            internal exception.
        """
        if flask_request.method != "POST":
            raise exceptions.MethodNotAllowed()

        try:
            content = flask_request.data.decode(
                verifier_constants.CHARACTER_ENCODING)
            response = self._webservice_handler.verify_request_and_dispatch(
                http_request_headers=flask_request.headers,
                http_request_body=content)

            return jsonify(response)
        except VerificationException:
            current_app.logger.error("Request verification failed",
                                     exc_info=True)
            raise exceptions.BadRequest(
                description="Incoming request failed verification")
        except AskSdkException:
            current_app.logger.error("Skill dispatch exception", exc_info=True)
            raise exceptions.InternalServerError(
                description="Exception occurred during skill dispatch")

    def register(self, app, route, endpoint=None):
        # type: (Flask, str, str) -> None
        """Method to register the routing on the app at provided route.

        This is a utility method, that can be used for registering the
        ``dispatch_request`` on the provided :py:class:`flask.Flask`
        application at the provided URL ``route``.

        :param app: A :py:class:`flask.Flask` application instance
        :type app: flask.Flask
        :param route: The URL rule where the skill dispatch has to be
            registered
        :type route: str
        :param endpoint: The endpoint for the registered URL rule.
            This can be used to set multiple skill endpoints on same app.
        :type endpoint: str
        :rtype: None
        :raises: :py:class:`TypeError` if ``app`` or `route`` is not
            provided or is of an invalid type
        """
        if app is None or not isinstance(app, Flask):
            raise TypeError("Expected a valid Flask instance")

        if route is None or not isinstance(route, str):
            raise TypeError("Expected a valid URL rule string")

        app.add_url_rule(route,
                         view_func=self.dispatch_request,
                         methods=["POST"],
                         endpoint=endpoint)
Ejemplo n.º 16
0
class AskRequestHandler:
    def __init__(self,
                 skill,
                 verifiers=None,
                 verify_signature=True,
                 verify_timestamp=True):
        self._skill = skill
        self._verify_signature = verify_signature
        self._verify_timestamp = verify_timestamp
        if verifiers is None:
            verifiers = []
        self._verifiers = verifiers
        if not isinstance(skill, CustomSkill):
            raise TypeError(
                "Invalid skill instance provided. Expected a custom "
                "skill instance.")
        self._create_webservice_handler()

    def _create_webservice_handler(self):
        self._webservice_handler = WebserviceSkillHandler(
            skill=self._skill,
            verify_signature=self._verify_signature,
            verify_timestamp=self._verify_timestamp,
            verifiers=self._verifiers)
        self._webservice_handler._add_custom_user_agent("cherrypy-ask-sdk")

    def dispatch(self):
        """Dispatch the POST request coming from an alexa (Ask SDK).

        We are directly decoding/encoding the request and responses and just
        passing along the request body and responses with the expected encoding
        for the ask_sdk.

        If everything goes well, we remove any possible next handler for the
        request, given that is considered full server when is handler by the
        ask_sdk.

        Something to keep in mind, is that we are not modifying the default
        request processors, these are there to handle regular form inputs, but
        because the request that the ask_sdk uses to communicate are POST
        with JSON bodies, those are basically ignored (unless we use the JSON tool).
        """
        request = cherrypy.serving.request
        if request.method != "POST":
            raise cherrypy.HTTPError(405)
        try:
            response = self._webservice_handler.verify_request_and_dispatch(
                request.headers,
                request.body.read().decode(
                    verifier_constants.CHARACTER_ENCODING))
        except VerificationException:
            cherrypy.log.error("Request verification failed", traceback=True)
            raise cherrypy.HTTPError(400,
                                     "Incoming request failed verification")
        except AskSdkException:
            cherrypy.log.error("Skill dispatch exception", traceback=True)
            raise cherrypy.HTTPError(
                message="Exception occurred during skill dispatch")
        else:
            cherrypy.serving.response.body = json.dumps(response).encode(
                verifier_constants.CHARACTER_ENCODING)
            # remove the request handler if the request was handled as expected
            # using the ask sdk
            cherrypy.serving.request.handler = None