Esempio n. 1
0
    def create(self, params, meta, **kwargs):
        consumer_id = kwargs['validated']['consumer_id']
        service_catalog_code = kwargs['validated']['service_catalog_code']
        project_id = kwargs['validated']['project_id']
        account_id = kwargs['token']['account_id']

        service = self.db.get(model=Service,
                              account_id=account_id,
                              project_id=project_id,
                              service_catalog_code=service_catalog_code)

        if not service:
            raise HTTPBadRequest(
                description="Please check your service parameters! "
                "There is no service identified with your parameters.")
        elif not self.db.get_attached_consumer(consumer_id=consumer_id,
                                               project_id=project_id):
            raise HTTPBadRequest(
                description=
                "There is no such consumer attached to specified project."
                "Please check your parameters or try to attach consumer first!"
            )
        elif not service.is_item_limit_available():
            raise HTTPPaymentRequired(
                description="Please check your service message limit")
        else:
            token = generate_token()
            add_consumer_token(account_id, project_id, service_catalog_code,
                               consumer_id, token)

            return {"token": token}
Esempio n. 2
0
    def on_get(self, req, resp):
        user = req.context["user"]

        if not user:
            raise HTTPBadRequest()

        if not user.api_enabled:
            resp.append_header("Refresh", "5;url=/profile")

            return self.render_template(
                req,
                resp,
                "message_gate.html",
                gate_message=Message(
                    "danger", "No access",
                    "You don't have API access - please contact a member of staff if you need it."
                ),
                redirect_uri="/")

        db_session = req.context["db_session"]
        keys = {}

        for key in db_session.query(APIKey).filter_by(user_id=user.id).all():
            keys[key.key] = key.name

        self.render_template(req,
                             resp,
                             "users/api_keys.html",
                             user=user,
                             keys=keys)
Esempio n. 3
0
    def retrieve(self, params, meta, **kwargs):
        project_id = kwargs['project_id']
        account_id = kwargs['token']['account_id']
        admin_id = kwargs['token']['sub']

        project = self.db.get_project(project_id=project_id, account_id=account_id)

        if not project:
            raise HTTPBadRequest(description="Project not found")

        token_payload = {'admin_id': admin_id,
                         'account_id': account_id,
                         'project_id': project_id}

        old_token = get_project_services_admin_token(project_id)
        if old_token:
            del_project_services_admin_token(old_token)

        token = generate_user_token()
        set_project_services_admin_token(project_id, token)
        add_project_services_admin_token(token, token_payload)

        return {
            "project_services_admin_token": token
        }
Esempio n. 4
0
 def on_put(self, req, resp, test_id):
     resp.status = falcon.HTTP_204
     payload = json.loads(req.stream.read())
     if '_id' not in payload or not isinstance(payload.get('_id'), str):
         raise HTTPBadRequest()
     try:
         update_resource(test_id, payload)
     except StopIteration:
         raise HTTPNotFound()
Esempio n. 5
0
 def json(self):
     if not hasattr(self, "_json"):
         if not self.client_accepts_json:
             raise falcon.HTTPUnsupportedMediaType(
                 'This API only supports the JSON formated data')
         try:
             self._json = json.loads(self.stream.read().decode('utf8'))
         except json.decoder.JSONDecodeError as err:
             raise HTTPBadRequest("JSONDecodeError", str(err))
     return self._json
Esempio n. 6
0
 def _do(self):
     try:
         login_credentials = self.current['request']['context']['data']['login_crd']
     except KeyError:
         raise HTTPBadRequest("Missing login data")
     user = authenticate(login_credentials)
     is_login_successful = bool(user)
     if is_login_successful:
         self.current.request.context['result'] = {'success': True}
         self.current.request.session['user'] = user
     self.current['task'].data['is_login_successful'] = is_login_successful
Esempio n. 7
0
    def on_post(self, req, resp):

        file = req.get_param('file')
        try:
            image = file.file
        except AttributeError:
            raise HTTPBadRequest('Bad multipart form', 'File is not valid')
        raw_response = recognize(image)
        response = serializer(raw_response)

        resp.body = response
Esempio n. 8
0
def remove_consumer_token(project_id, service_catalog_code, token):
    token_key = CACHE_REFRESH_TOKEN.format(project_id=project_id,
                                           service=service_catalog_code,
                                           token=token)

    t = cache.hget(CACHE_TOKENS_KEYS, token)

    if token_key != t.decode():
        raise HTTPBadRequest(description="Token does not found!.")

    cache.hdel(CACHE_TOKENS_KEYS, token)
    cache.delete(token_key)
Esempio n. 9
0
    def on_get(self, req, resp):
        user = req.context["user"]

        if not user:
            raise HTTPBadRequest()

        self.render_template(
            req,
            resp,
            "users/profile.html",
            user=user,
            avatar="https://www.gravatar.com/avatar/{}".format(
                self.gravatar_hash(user.email)))
Esempio n. 10
0
    def on_get(self, req, resp):
        path = req.get_param('path')
        if not path:
            description = "Specify a valid `path` querystring"
            raise HTTPBadRequest(title="Bad Request", description=description)
        cassandra_driver = CassandraStorageDriver()
        cassandra_driver.connect(conf['cassandra']['keyspace'])
        current_time = time.time()
        query_time = current_time - conf['canary']['interval']
        job_details = \
            cassandra_driver.get_job_details(date=canonicalize(query_time),
                                             path=path)
        cassandra_driver.close_connection()
        resp.status = falcon.HTTP_200

        for i in range(len(job_details)):
            job_details[i]['jobs'] = json.loads(
                json.loads(job_details[i]['jobs']))
        resp.body = json.dumps(job_details)
    def process_request(self, req, resp):
        # type: (Request, Response) -> None
        """Ensure that a request does not contain an empty body

        If a request has content length, but its body is empty,
        raise an HTTPBadRequest error.

        :param req: the passed request object
        :param resp: the passed response object

        :raises HTTPBadRequest: if the request has content length with
            an empty body
        """
        log.debug("EmptyRequestDropper.process_request(%s, %s)", req, resp)
        if req.content_length in (None, 0):
            return

        content = get_stashed_content(req)

        # If the content is _still_ Falsy (e.g., something empty like b'')
        if not content:
            raise HTTPBadRequest(description=(
                "Empty response body. A valid JSON document is required."))
Esempio n. 12
0
File: jobs.py Progetto: mkmad/canary
    def on_get(self, req, resp):
        path = req.get_param('path')
        if not path:
            description = "Specify a valid `path` querystring"
            raise HTTPBadRequest(title="Bad Request", description=description)

        redis_driver = redisdriver.RedisStorageDriver()
        job_details = redis_driver.get_job_details()
        resp.status = falcon.HTTP_200

        for i in range(len(job_details)):
            flow_status = []
            job_details[i] = json.loads(base64.b64decode(job_details[i]))
            for val in job_details[i]['jobs']:
                stat = {}
                sub_tasks = val['flow_status'].values()[0]['tasks']
                for sub_val in sub_tasks:
                    if sub_val:
                        for task_name, task_values in sub_val.items():
                            stat[task_name] = task_values['state']
                        flow_status.append(stat)

            job_details[i]['flow_status'] = flow_status
        resp.body = json.dumps(job_details)
Esempio n. 13
0
def bad_request(req, resp, **kwargs):
    """Raise 400 HTTPBadRequest error"""
    raise HTTPBadRequest('Bad request', 'Invalid HTTP method')
Esempio n. 14
0
    def __call__(self, env, start_response):  # noqa: C901
        """WSGI `app` method.

        Makes instances of App callable from a WSGI server. May be used to
        host an App or called directly in order to simulate requests when
        testing the App.

        (See also: PEP 3333)

        Args:
            env (dict): A WSGI environment dictionary
            start_response (callable): A WSGI helper function for setting
                status and headers on a response.

        """
        req = self._request_type(env, options=self.req_options)
        resp = self._response_type(options=self.resp_options)
        resource = None
        responder = None
        params = {}

        dependent_mw_resp_stack = []
        mw_req_stack, mw_rsrc_stack, mw_resp_stack = self._middleware

        req_succeeded = False

        try:
            if req.method in self._META_METHODS:
                raise HTTPBadRequest()

            # NOTE(ealogar): The execution of request middleware
            # should be before routing. This will allow request mw
            # to modify the path.
            # NOTE: if flag set to use independent middleware, execute
            # request middleware independently. Otherwise, only queue
            # response middleware after request middleware succeeds.
            if self._independent_middleware:
                for process_request in mw_req_stack:
                    process_request(req, resp)
                    if resp.complete:
                        break
            else:
                for process_request, process_response in mw_req_stack:
                    if process_request and not resp.complete:
                        process_request(req, resp)
                    if process_response:
                        dependent_mw_resp_stack.insert(0, process_response)

            if not resp.complete:
                # NOTE(warsaw): Moved this to inside the try except
                # because it is possible when using object-based
                # traversal for _get_responder() to fail.  An example is
                # a case where an object does not have the requested
                # next-hop child resource. In that case, the object
                # being asked to dispatch to its child will raise an
                # HTTP exception signalling the problem, e.g. a 404.
                responder, params, resource, req.uri_template = self._get_responder(
                    req)
        except Exception as ex:
            if not self._handle_exception(req, resp, ex, params):
                raise
        else:
            try:
                # NOTE(kgriffs): If the request did not match any
                # route, a default responder is returned and the
                # resource is None. In that case, we skip the
                # resource middleware methods. Resource will also be
                # None when a middleware method already set
                # resp.complete to True.
                if resource:
                    # Call process_resource middleware methods.
                    for process_resource in mw_rsrc_stack:
                        process_resource(req, resp, resource, params)
                        if resp.complete:
                            break

                if not resp.complete:
                    responder(req, resp, **params)

                req_succeeded = True
            except Exception as ex:
                if not self._handle_exception(req, resp, ex, params):
                    raise

        # Call process_response middleware methods.
        for process_response in mw_resp_stack or dependent_mw_resp_stack:
            try:
                process_response(req, resp, resource, req_succeeded)
            except Exception as ex:
                if not self._handle_exception(req, resp, ex, params):
                    raise

                req_succeeded = False

        body = []
        length = 0

        try:
            body, length = self._get_body(resp, env.get('wsgi.file_wrapper'))
        except Exception as ex:
            if not self._handle_exception(req, resp, ex, params):
                raise

            req_succeeded = False

        resp_status = code_to_http_status(resp.status)
        default_media_type = self.resp_options.default_media_type

        if req.method == 'HEAD' or resp_status in _BODILESS_STATUS_CODES:
            body = []

            # PERF(vytas): move check for the less common and much faster path
            # of resp_status being in {204, 304} here; NB: this builds on the
            # assumption _TYPELESS_STATUS_CODES <= _BODILESS_STATUS_CODES.

            # NOTE(kgriffs): Based on wsgiref.validate's interpretation of
            # RFC 2616, as commented in that module's source code. The
            # presence of the Content-Length header is not similarly
            # enforced.
            if resp_status in _TYPELESS_STATUS_CODES:
                default_media_type = None
            elif (length is not None and req.method == 'HEAD'
                  and resp_status not in _BODILESS_STATUS_CODES
                  and 'content-length' not in resp._headers):
                # NOTE(kgriffs): We really should be returning a Content-Length
                #   in this case according to my reading of the RFCs. By
                #   optionally using len(data) we let a resource simulate HEAD
                #   by turning around and calling it's own on_get().
                resp._headers['content-length'] = str(length)

        else:
            # PERF(kgriffs): Böse mußt sein. Operate directly on resp._headers
            #   to reduce overhead since this is a hot/critical code path.
            # NOTE(kgriffs): We always set content-length to match the
            #   body bytes length, even if content-length is already set. The
            #   reason being that web servers and LBs behave unpredictably
            #   when the header doesn't match the body (sometimes choosing to
            #   drop the HTTP connection prematurely, for example).
            if length is not None:
                resp._headers['content-length'] = str(length)

        headers = resp._wsgi_headers(default_media_type)

        # Return the response per the WSGI spec.
        start_response(resp_status, headers)
        return body
Esempio n. 15
0
    def on_post(self, req, resp):
        params = {}

        user = req.context["user"]
        db_session = req.context["db_session"]

        keys = {}

        if not user:
            raise HTTPBadRequest()

        if not user.api_enabled:
            resp.append_header("Refresh", "5;url=/profile")

            return self.render_template(
                req,
                resp,
                "message_gate.html",
                gate_message=Message(
                    "danger", "No access",
                    "You don't have API access - please contact a member of staff if you need it."
                ),
                redirect_uri="/")

        for key in db_session.query(APIKey).filter_by(user_id=user.id).all():
            keys[key.key] = key.name

        if not req.get_param("action", store=params):
            raise HTTPBadRequest()

        if params["action"] == "create":
            if len(keys) >= 10:
                return self.render_template(
                    req,
                    resp,
                    "users/api_keys.html",
                    user=user,
                    keys=keys,
                    message=Message(
                        "danger", "Too many keys",
                        "You already have 10 api keys! If you need more, contact staff directly."
                    ))

            if not req.get_param("name", store=params) or not params["name"]:
                return self.render_template(
                    req,
                    resp,
                    "users/api_keys.html",
                    user=user,
                    keys=keys,
                    message=Message(
                        "danger", "Missing name",
                        "Please supply a name for your new API key"))

            new_key = APIKey(user=user,
                             name=params["name"],
                             key=secrets.token_urlsafe(32))
            db_session.add(new_key)

            keys[new_key.key] = new_key.name

            return self.render_template(
                req,
                resp,
                "users/api_keys.html",
                user=user,
                keys=keys,
                message=Message(
                    "success", "Key created",
                    "New API key \"{}\" created successfully.".format(
                        new_key.name)))
        elif params["action"] == "delete":
            if not req.get_param("key", store=params):
                raise HTTPBadRequest()

            if params["key"] not in keys:
                return self.render_template(
                    req,
                    resp,
                    "users/api_keys.html",
                    user=user,
                    keys=keys,
                    message=Message("danger", "No such key",
                                    "Unknown key: {}.".format(params["key"])))

            old_key = db_session.query(APIKey).filter_by(
                key=params["key"]).one()
            db_session.delete(old_key)
            del keys[params["key"]]

            return self.render_template(
                req,
                resp,
                "users/api_keys.html",
                user=user,
                keys=keys,
                message=Message(
                    "success", "Key deleted",
                    "API key \"{}\" deleted successfully.".format(
                        old_key.name)))
        else:
            raise HTTPBadRequest()
Esempio n. 16
0
    def process_resource(self, req, resp, resource, params):
        # type: (Request, Response, object, dict) -> None
        """Deserialize request body with any resource-specific schemas

        Store deserialized data on the ``req.context`` object
        under the ``req_key`` provided to the class constructor
        or on the ``json`` key if none was provided.

        If a Marshmallow schema is defined on the passed ``resource``,
        use it to deserialize the request body.

        If no schema is defined and the class was instantiated with
        ``force_json=True``, request data will be deserialized with
        any ``json_module`` passed to the class constructor or
        ``simplejson`` by default.

        :param falcon.Request req: the request object
        :param falcon.Response resp: the response object
        :param object resource: the resource object
        :param dict params: any parameters parsed from the url

        :rtype: None
        :raises falcon.HTTPBadRequest: if the data cannot be
            deserialized or decoded
        """
        log.debug('Marshmallow.process_resource(%s, %s, %s, %s)', req, resp,
                  resource, params)
        if req.content_length in (None, 0):
            return

        sch = self._get_schema(resource, req.method)

        if sch is not None:
            if not isinstance(sch, Schema):
                raise TypeError(
                    'The schema and <method>_schema properties of a resource '
                    'must be instantiated Marshmallow schemas.')

            try:
                body = get_stashed_content(req).decode('utf-8')
                parsed = self._json.loads(body)
            except UnicodeDecodeError:
                raise HTTPBadRequest('Body was not encoded as UTF-8')
            except self._json.JSONDecodeError:
                raise HTTPBadRequest('Request must be valid JSON')

            data, errors = sch.load(parsed)

            if errors:
                raise HTTPUnprocessableEntity(
                    description=self._json.dumps(errors))

            req.context[self._req_key] = data

        elif self._force_json:

            body = get_stashed_content(req)
            try:
                req.context[self._req_key] = self._json.loads(
                    body.decode('utf-8'))
            except (ValueError, UnicodeDecodeError):
                raise HTTPBadRequest(description=(
                    'Could not decode the request body, either because '
                    'it was not valid JSON or because it was not encoded '
                    'as UTF-8.'))
Esempio n. 17
0
    async def __call__(self, scope, receive, send):  # noqa: C901
        try:
            asgi_info = scope['asgi']

            # NOTE(kgriffs): We only check this here because
            #   uvicorn does not explicitly set the 'asgi' key, which
            #   would normally mean we should assume '2.0', but uvicorn
            #   actually *does* support 3.0. But in that case, we will
            #   end up in the except clause, below, and not raise an
            #   error.
            # PERF(kgriffs): This should usually be present, so use a
            #   try..except
            try:
                version = asgi_info['version']
            except KeyError:
                # NOTE(kgriffs): According to the ASGI spec, "2.0" is
                #   the default version.
                version = '2.0'

            if not version.startswith('3.'):
                raise UnsupportedScopeError(
                    f'Falcon requires ASGI version 3.x. Detected: {asgi_info}')

        except KeyError:
            asgi_info = scope['asgi'] = {'version': '2.0'}

        # NOTE(kgriffs): The ASGI spec requires the 'type' key to be present.
        scope_type = scope['type']
        if scope_type != 'http':
            if scope_type == 'lifespan':
                try:
                    spec_version = asgi_info['spec_version']
                except KeyError:
                    spec_version = '1.0'

                if not spec_version.startswith(
                        '1.') and not spec_version.startswith('2.'):
                    raise UnsupportedScopeError(
                        'Only versions 1.x and 2.x of the ASGI "lifespan" scope are supported.'
                    )

                await self._call_lifespan_handlers(spec_version, scope,
                                                   receive, send)
                return

            elif scope_type == 'websocket':
                try:
                    spec_version = asgi_info['spec_version']
                except KeyError:
                    spec_version = '2.0'

                if not spec_version.startswith('2.'):
                    raise UnsupportedScopeError(
                        'Only versions 2.x of the ASGI "websocket" scope are supported.'
                    )

                try:
                    http_version = scope['http_version']
                except KeyError:
                    http_version = '1.1'

                if http_version not in {'1.1', '2', '3'}:
                    raise UnsupportedError(
                        f'The ASGI "websocket" scope does not support HTTP version {http_version}.'
                    )

                await self._handle_websocket(spec_version, scope, receive,
                                             send)
                return

            # NOTE(kgriffs): According to the ASGI spec: "Applications should
            #   actively reject any protocol that they do not understand with
            #   an Exception (of any type)."
            raise UnsupportedScopeError(
                f'The ASGI "{scope_type}" scope type is not supported.')

        # PERF(kgriffs): This is slighter faster than using dict.get()
        # TODO(kgriffs): Use this to determine what features are supported by
        #   the server (e.g., the headers key in the WebSocket Accept
        #   response).
        spec_version = asgi_info[
            'spec_version'] if 'spec_version' in asgi_info else '2.0'

        if not spec_version.startswith('2.'):
            raise UnsupportedScopeError(
                f'The ASGI http scope version {spec_version} is not supported.'
            )

        # NOTE(kgriffs): Per the ASGI spec, we should not proceed with request
        #   processing until after we receive an initial 'http.request' event.
        first_event = await receive()
        first_event_type = first_event['type']
        if first_event_type == EventType.HTTP_DISCONNECT:
            # NOTE(kgriffs): Bail out immediately to minimize resource usage
            return

        # NOTE(kgriffs): This is the only other type defined by the ASGI spec,
        #   but we just assert it to make it easier to track down a potential
        #   incompatibility with a future spec version.
        assert first_event_type == EventType.HTTP_REQUEST

        req = self._request_type(scope,
                                 receive,
                                 first_event=first_event,
                                 options=self.req_options)
        resp = self._response_type(options=self.resp_options)

        if self.req_options.auto_parse_form_urlencoded:
            raise UnsupportedError(
                'The deprecated WSGI RequestOptions.auto_parse_form_urlencoded option '
                'is not supported for ASGI apps. Please use Request.get_media() instead. '
            )

        resource = None
        responder = None
        params = {}

        dependent_mw_resp_stack = []
        mw_req_stack, mw_rsrc_stack, mw_resp_stack = self._middleware

        req_succeeded = False

        try:
            if req.method in self._META_METHODS:
                raise HTTPBadRequest()

            # NOTE(ealogar): The execution of request middleware
            # should be before routing. This will allow request mw
            # to modify the path.
            # NOTE: if flag set to use independent middleware, execute
            # request middleware independently. Otherwise, only queue
            # response middleware after request middleware succeeds.
            if self._independent_middleware:
                for process_request in mw_req_stack:
                    await process_request(req, resp)

                    if resp.complete:
                        break
            else:
                for process_request, process_response in mw_req_stack:
                    if process_request and not resp.complete:
                        await process_request(req, resp)

                    if process_response:
                        dependent_mw_resp_stack.insert(0, process_response)

            if not resp.complete:
                # NOTE(warsaw): Moved this to inside the try except
                # because it is possible when using object-based
                # traversal for _get_responder() to fail.  An example is
                # a case where an object does not have the requested
                # next-hop child resource. In that case, the object
                # being asked to dispatch to its child will raise an
                # HTTP exception signaling the problem, e.g. a 404.
                responder, params, resource, req.uri_template = self._get_responder(
                    req)

        except Exception as ex:
            if not await self._handle_exception(req, resp, ex, params):
                raise

        else:
            try:
                # NOTE(kgriffs): If the request did not match any
                # route, a default responder is returned and the
                # resource is None. In that case, we skip the
                # resource middleware methods. Resource will also be
                # None when a middleware method already set
                # resp.complete to True.
                if resource:
                    # Call process_resource middleware methods.
                    for process_resource in mw_rsrc_stack:
                        await process_resource(req, resp, resource, params)

                        if resp.complete:
                            break

                if not resp.complete:
                    await responder(req, resp, **params)

                req_succeeded = True

            except Exception as ex:
                if not await self._handle_exception(req, resp, ex, params):
                    raise

        # Call process_response middleware methods.
        for process_response in mw_resp_stack or dependent_mw_resp_stack:
            try:
                await process_response(req, resp, resource, req_succeeded)

            except Exception as ex:
                if not await self._handle_exception(req, resp, ex, params):
                    raise

                req_succeeded = False

        data = b''

        try:
            data = await resp.render_body()
        except Exception as ex:
            if not await self._handle_exception(req, resp, ex, params):
                raise

            req_succeeded = False

        resp_status = http_status_to_code(resp.status)
        default_media_type = self.resp_options.default_media_type

        if req.method == 'HEAD' or resp_status in _BODILESS_STATUS_CODES:
            #
            # PERF(vytas): move check for the less common and much faster path
            # of resp_status being in {204, 304} here; NB: this builds on the
            # assumption _TYPELESS_STATUS_CODES <= _BODILESS_STATUS_CODES.
            #
            # NOTE(kgriffs): Based on wsgiref.validate's interpretation of
            # RFC 2616, as commented in that module's source code. The
            # presence of the Content-Length header is not similarly
            # enforced.
            #
            # NOTE(kgriffs): Assuming the same for ASGI until proven otherwise.
            #
            if resp_status in _TYPELESS_STATUS_CODES:
                default_media_type = None
            elif (
                    # NOTE(kgriffs): If they are going to stream using an
                    #   async generator, we can't know in advance what the
                    #   content length will be.
                (data is not None or not resp.stream) and req.method == 'HEAD'
                    and resp_status not in _BODILESS_STATUS_CODES
                    and 'content-length' not in resp._headers):
                # NOTE(kgriffs): We really should be returning a Content-Length
                #   in this case according to my reading of the RFCs. By
                #   optionally using len(data) we let a resource simulate HEAD
                #   by turning around and calling it's own on_get().
                resp._headers['content-length'] = str(
                    len(data)) if data else '0'

            await send({
                'type': EventType.HTTP_RESPONSE_START,
                'status': resp_status,
                'headers': resp._asgi_headers(default_media_type)
            })

            await send(_EVT_RESP_EOF)
            self._schedule_callbacks(resp)
            return

        sse_emitter = resp.sse
        if sse_emitter:
            if isasyncgenfunction(sse_emitter):
                raise TypeError(
                    'Response.sse must be an async iterable. This can be obtained by '
                    'simply executing the async generator function and then setting '
                    'the result to Response.sse, e.g.: resp.sse = some_asyncgen_function()'
                )

            # NOTE(kgriffs): This must be done in a separate task because
            #   receive() can block for some time (until the connection is
            #   actually closed).
            async def watch_disconnect():
                while True:
                    received_event = await receive()
                    if received_event['type'] == EventType.HTTP_DISCONNECT:
                        break

            watcher = falcon.create_task(watch_disconnect())

            await send({
                'type': EventType.HTTP_RESPONSE_START,
                'status': resp_status,
                'headers': resp._asgi_headers('text/event-stream')
            })

            self._schedule_callbacks(resp)

            handler, _, _ = self.resp_options.media_handlers._resolve(
                MEDIA_JSON, MEDIA_JSON, raise_not_found=False)

            # TODO(kgriffs): Do we need to do anything special to handle when
            #   a connection is closed?
            async for event in sse_emitter:
                if not event:
                    event = SSEvent()

                # NOTE(kgriffs): According to the ASGI spec, once the client
                #   disconnects, send() acts as a no-op. We have to check
                #   the connection state using watch_disconnect() above.
                await send({
                    'type': EventType.HTTP_RESPONSE_BODY,
                    'body': event.serialize(handler),
                    'more_body': True
                })

                if watcher.done():
                    break

            watcher.cancel()
            try:
                await watcher
            except asyncio.CancelledError:
                pass

            await send({'type': EventType.HTTP_RESPONSE_BODY})
            return

        if data is not None:
            # PERF(kgriffs): Böse mußt sein. Operate directly on resp._headers
            #   to reduce overhead since this is a hot/critical code path.
            # NOTE(kgriffs): We always set content-length to match the
            #   body bytes length, even if content-length is already set. The
            #   reason being that web servers and LBs behave unpredictably
            #   when the header doesn't match the body (sometimes choosing to
            #   drop the HTTP connection prematurely, for example).
            resp._headers['content-length'] = str(len(data))

            await send({
                'type': EventType.HTTP_RESPONSE_START,
                'status': resp_status,
                'headers': resp._asgi_headers(default_media_type)
            })

            await send({'type': EventType.HTTP_RESPONSE_BODY, 'body': data})

            self._schedule_callbacks(resp)
            return

        stream = resp.stream
        if not stream:
            resp._headers['content-length'] = '0'

        await send({
            'type': EventType.HTTP_RESPONSE_START,
            'status': resp_status,
            'headers': resp._asgi_headers(default_media_type)
        })

        if stream:
            # Detect whether this is one of the following:
            #
            #   (a) async file-like object (e.g., aiofiles)
            #   (b) async generator
            #   (c) async iterator
            #

            if hasattr(stream, 'read'):
                while True:
                    data = await stream.read(self._STREAM_BLOCK_SIZE)
                    if data == b'':
                        break
                    else:
                        await send({
                            'type': EventType.HTTP_RESPONSE_BODY,

                            # NOTE(kgriffs): Handle the case in which data == None
                            'body': data or b'',
                            'more_body': True
                        })
            else:
                # NOTE(kgriffs): Works for both async generators and iterators
                try:
                    async for data in stream:
                        # NOTE(kgriffs): We can not rely on StopIteration
                        #   because of Pep 479 that is implemented starting
                        #   with Python 3.7. AFAICT this is only an issue
                        #   when using an async iterator instead of an async
                        #   generator.
                        if data is None:
                            break

                        await send({
                            'type': EventType.HTTP_RESPONSE_BODY,
                            'body': data,
                            'more_body': True
                        })
                except TypeError as ex:
                    if isasyncgenfunction(stream):
                        raise TypeError(
                            'The object assigned to Response.stream appears to '
                            'be an async generator function. A generator '
                            'object is expected instead. This can be obtained '
                            'simply by calling the generator function, e.g.: '
                            'resp.stream = some_asyncgen_function()')

                    raise TypeError(
                        'Response.stream must be a generator or implement an '
                        '__aiter__ method. Error raised while iterating over '
                        'Response.stream: ' + str(ex))

            if hasattr(stream, 'close'):
                await stream.close()

        await send(_EVT_RESP_EOF)
        self._schedule_callbacks(resp)
Esempio n. 18
0
 def error_invalid_parameter(error = ERR_INVALID_PARAMETER , description=None):
     raise HTTPBadRequest(error.get('title'),description , code = error.get('code'))
Esempio n. 19
0
 def error_invalid_header( error = ERR_INVALID_HEADER , description=None):
     raise HTTPBadRequest(title=error.get('title'),description = description , code = error.get('code'))
Esempio n. 20
0
async def bad_request_async(req, resp, **kwargs):
    """Raise 400 HTTPBadRequest error"""
    raise HTTPBadRequest(title='Bad request', description='Invalid HTTP method')
Esempio n. 21
0
def error_handler(ex, req, resp, params):
    if isinstance(ex, NotImplementedError):
        resp.status = falcon.HTTP_NOT_IMPLEMENTED
    else:
        raise HTTPBadRequest(type(ex).__name__, str(ex))
    def process_resource(self, req, resp, resource, params):
        # type: (Request, Response, object, dict) -> None
        """Deserialize request body with any resource-specific schemas

        Store deserialized data on the ``req.context`` object
        under the ``req_key`` provided to the class constructor
        or on the ``json`` key if none was provided.

        If a Marshmallow schema is defined on the passed ``resource``,
        use it to deserialize the request body.

        If no schema is defined and the class was instantiated with
        ``force_json=True``, request data will be deserialized with
        any ``json_module`` passed to the class constructor or
        ``simplejson`` by default.

        :param falcon.Request req: the request object
        :param falcon.Response resp: the response object
        :param object resource: the resource object
        :param dict params: any parameters parsed from the url

        :rtype: None
        :raises falcon.HTTPBadRequest: if the data cannot be
            deserialized or decoded
        """
        log.debug(
            "Marshmallow.process_resource(%s, %s, %s, %s)",
            req,
            resp,
            resource,
            params,
        )
        if req.content_length in (None, 0):
            return

        sch = self._get_schema(resource, req.method, "request")

        if sch is not None:
            if not isinstance(sch, Schema):
                raise TypeError(
                    "The schema and <method>_schema properties of a resource "
                    "must be instantiated Marshmallow schemas.")

            try:
                body = get_stashed_content(req)
                parsed = self._json.loads(body)
            except UnicodeDecodeError:
                raise HTTPBadRequest("Body was not encoded as UTF-8")
            except self._json.JSONDecodeError:
                raise HTTPBadRequest("Request must be valid JSON")

            if MARSHMALLOW_2:
                data, errors = sch.load(parsed)

                if errors:
                    raise HTTPUnprocessableEntity(
                        description=self._json.dumps(errors))
            else:
                # Marshmallow 3 or higher raises a ValidationError
                # instead of returning a (data, errors) tuple.
                try:
                    data = sch.load(parsed)
                except ValidationError as exc:
                    raise HTTPUnprocessableEntity(
                        description=self._json.dumps(exc.messages))
                except Exception as exc:
                    raise HTTPUnprocessableEntity(
                        description=self._json.dumps({"error": exc}))

            req.context[self._req_key] = data

        elif self._force_json:

            body = get_stashed_content(req)
            try:
                req.context[self._req_key] = self._json.loads(body)
            except (ValueError, UnicodeDecodeError):
                raise HTTPBadRequest(description=(
                    "Could not decode the request body, either because "
                    "it was not valid JSON or because it was not encoded "
                    "as UTF-8."))
Esempio n. 23
0
    async def __call__(self, scope, receive, send):  # noqa: C901
        # NOTE(kgriffs): The ASGI spec requires the 'type' key to be present.
        scope_type = scope['type']

        # PERF(kgriffs): This should usually be present, so use a
        #   try..except
        try:
            asgi_info = scope['asgi']
        except KeyError:
            # NOTE(kgriffs): According to the ASGI spec, "2.0" is
            #   the default version.
            asgi_info = scope['asgi'] = {'version': '2.0'}

        try:
            spec_version = asgi_info['spec_version']
        except KeyError:
            spec_version = None

        try:
            http_version = scope['http_version']
        except KeyError:
            http_version = '1.1'

        spec_version = _validate_asgi_scope(scope_type, spec_version,
                                            http_version)

        if scope_type != 'http':
            # PERF(vytas): Evaluate the potentially recurring WebSocket path
            #   first (in contrast to one-shot lifespan events).
            if scope_type == 'websocket':
                await self._handle_websocket(spec_version, scope, receive,
                                             send)
                return

            # NOTE(vytas): Else 'lifespan' -- other scope_type values have been
            #   eliminated by _validate_asgi_scope at this point.
            await self._call_lifespan_handlers(spec_version, scope, receive,
                                               send)
            return

        # NOTE(kgriffs): Per the ASGI spec, we should not proceed with request
        #   processing until after we receive an initial 'http.request' event.
        first_event = await receive()
        first_event_type = first_event['type']
        # PERF(vytas): Inline the value of EventType.HTTP_DISCONNECT in this
        #   critical code path.
        if first_event_type == 'http.disconnect':
            # NOTE(kgriffs): Bail out immediately to minimize resource usage
            return

        # NOTE(kgriffs): This is the only other type defined by the ASGI spec,
        #   but we just assert it to make it easier to track down a potential
        #   incompatibility with a future spec version.
        # PERF(vytas): Inline the value of EventType.HTTP_REQUEST in this
        #   critical code path.
        assert first_event_type == 'http.request'

        req = self._request_type(scope,
                                 receive,
                                 first_event=first_event,
                                 options=self.req_options)
        resp = self._response_type(options=self.resp_options)

        resource = None
        responder = None
        params = {}

        dependent_mw_resp_stack = []
        mw_req_stack, mw_rsrc_stack, mw_resp_stack = self._middleware

        req_succeeded = False

        try:
            if req.method in self._META_METHODS:
                raise HTTPBadRequest()

            # NOTE(ealogar): The execution of request middleware
            # should be before routing. This will allow request mw
            # to modify the path.
            # NOTE: if flag set to use independent middleware, execute
            # request middleware independently. Otherwise, only queue
            # response middleware after request middleware succeeds.
            if self._independent_middleware:
                for process_request in mw_req_stack:
                    await process_request(req, resp)

                    if resp.complete:
                        break
            else:
                for process_request, process_response in mw_req_stack:
                    if process_request and not resp.complete:
                        await process_request(req, resp)

                    if process_response:
                        dependent_mw_resp_stack.insert(0, process_response)

            if not resp.complete:
                # NOTE(warsaw): Moved this to inside the try except
                # because it is possible when using object-based
                # traversal for _get_responder() to fail.  An example is
                # a case where an object does not have the requested
                # next-hop child resource. In that case, the object
                # being asked to dispatch to its child will raise an
                # HTTP exception signaling the problem, e.g. a 404.
                responder, params, resource, req.uri_template = self._get_responder(
                    req)

        except Exception as ex:
            if not await self._handle_exception(req, resp, ex, params):
                raise

        else:
            try:
                # NOTE(kgriffs): If the request did not match any
                # route, a default responder is returned and the
                # resource is None. In that case, we skip the
                # resource middleware methods. Resource will also be
                # None when a middleware method already set
                # resp.complete to True.
                if resource:
                    # Call process_resource middleware methods.
                    for process_resource in mw_rsrc_stack:
                        await process_resource(req, resp, resource, params)

                        if resp.complete:
                            break

                if not resp.complete:
                    await responder(req, resp, **params)

                req_succeeded = True

            except Exception as ex:
                if not await self._handle_exception(req, resp, ex, params):
                    raise

        # Call process_response middleware methods.
        for process_response in mw_resp_stack or dependent_mw_resp_stack:
            try:
                await process_response(req, resp, resource, req_succeeded)

            except Exception as ex:
                if not await self._handle_exception(req, resp, ex, params):
                    raise

                req_succeeded = False

        data = b''

        try:
            # NOTE(vytas): It is only safe to inline Response.render_body()
            #   where we can be sure it hasn't been overridden, either directly
            #   or by modifying the behavior of its dependencies.
            if self._standard_response_type:
                # PERF(vytas): inline Response.render_body() in this critical code
                #   path in order to shave off an await.
                text = resp.text
                if text is None:
                    data = resp._data

                    if data is None and resp._media is not None:
                        # NOTE(kgriffs): We use a special _UNSET singleton since
                        #   None is ambiguous (the media handler might return None).
                        if resp._media_rendered is _UNSET:
                            if not resp.content_type:
                                resp.content_type = resp.options.default_media_type

                            handler, serialize_sync, _ = resp.options.media_handlers._resolve(
                                resp.content_type,
                                resp.options.default_media_type)

                            if serialize_sync:
                                resp._media_rendered = serialize_sync(
                                    resp._media)
                            else:
                                resp._media_rendered = await handler.serialize_async(
                                    resp._media, resp.content_type)

                        data = resp._media_rendered
                else:
                    try:
                        # NOTE(kgriffs): Normally we expect text to be a string
                        data = text.encode()
                    except AttributeError:
                        # NOTE(kgriffs): Assume it was a bytes object already
                        data = text

            else:
                # NOTE(vytas): Custom reponse type.
                data = await resp.render_body()

        except Exception as ex:
            if not await self._handle_exception(req, resp, ex, params):
                raise

            req_succeeded = False

        resp_status = http_status_to_code(resp.status)
        default_media_type = self.resp_options.default_media_type

        if req.method == 'HEAD' or resp_status in _BODILESS_STATUS_CODES:
            #
            # PERF(vytas): move check for the less common and much faster path
            # of resp_status being in {204, 304} here; NB: this builds on the
            # assumption _TYPELESS_STATUS_CODES <= _BODILESS_STATUS_CODES.
            #
            # NOTE(kgriffs): Based on wsgiref.validate's interpretation of
            # RFC 2616, as commented in that module's source code. The
            # presence of the Content-Length header is not similarly
            # enforced.
            #
            # NOTE(kgriffs): Assuming the same for ASGI until proven otherwise.
            #
            if resp_status in _TYPELESS_STATUS_CODES:
                default_media_type = None
            elif (
                    # NOTE(kgriffs): If they are going to stream using an
                    #   async generator, we can't know in advance what the
                    #   content length will be.
                (data is not None or not resp.stream) and req.method == 'HEAD'
                    and resp_status not in _BODILESS_STATUS_CODES
                    and 'content-length' not in resp._headers):
                # NOTE(kgriffs): We really should be returning a Content-Length
                #   in this case according to my reading of the RFCs. By
                #   optionally using len(data) we let a resource simulate HEAD
                #   by turning around and calling it's own on_get().
                resp._headers['content-length'] = str(
                    len(data)) if data else '0'

            await send({
                # PERF(vytas): Inline the value of
                #   EventType.HTTP_RESPONSE_START in this critical code path.
                'type': 'http.response.start',
                'status': resp_status,
                'headers': resp._asgi_headers(default_media_type)
            })

            await send(_EVT_RESP_EOF)

            # PERF(vytas): Check resp._registered_callbacks directly to shave
            #   off a function call since this is a hot/critical code path.
            if resp._registered_callbacks:
                self._schedule_callbacks(resp)
            return

        # PERF(vytas): Operate directly on the resp private interface to reduce
        #   overhead since this is a hot/critical code path.
        if resp._sse:
            sse_emitter = resp._sse
            if isasyncgenfunction(sse_emitter):
                raise TypeError(
                    'Response.sse must be an async iterable. This can be obtained by '
                    'simply executing the async generator function and then setting '
                    'the result to Response.sse, e.g.: resp.sse = some_asyncgen_function()'
                )

            # NOTE(kgriffs): This must be done in a separate task because
            #   receive() can block for some time (until the connection is
            #   actually closed).
            async def watch_disconnect():
                while True:
                    received_event = await receive()
                    if received_event['type'] == EventType.HTTP_DISCONNECT:
                        break

            watcher = falcon.create_task(watch_disconnect())

            await send({
                'type': EventType.HTTP_RESPONSE_START,
                'status': resp_status,
                'headers': resp._asgi_headers('text/event-stream')
            })

            # PERF(vytas): Check resp._registered_callbacks directly to shave
            #   off a function call since this is a hot/critical code path.
            if resp._registered_callbacks:
                self._schedule_callbacks(resp)

            handler, _, _ = self.resp_options.media_handlers._resolve(
                MEDIA_JSON, MEDIA_JSON, raise_not_found=False)

            # TODO(kgriffs): Do we need to do anything special to handle when
            #   a connection is closed?
            async for event in sse_emitter:
                if not event:
                    event = SSEvent()

                # NOTE(kgriffs): According to the ASGI spec, once the client
                #   disconnects, send() acts as a no-op. We have to check
                #   the connection state using watch_disconnect() above.
                await send({
                    'type': EventType.HTTP_RESPONSE_BODY,
                    'body': event.serialize(handler),
                    'more_body': True
                })

                if watcher.done():
                    break

            watcher.cancel()
            try:
                await watcher
            except asyncio.CancelledError:
                pass

            await send({'type': EventType.HTTP_RESPONSE_BODY})
            return

        if data is not None:
            # PERF(kgriffs): Böse mußt sein. Operate directly on resp._headers
            #   to reduce overhead since this is a hot/critical code path.
            # NOTE(kgriffs): We always set content-length to match the
            #   body bytes length, even if content-length is already set. The
            #   reason being that web servers and LBs behave unpredictably
            #   when the header doesn't match the body (sometimes choosing to
            #   drop the HTTP connection prematurely, for example).
            resp._headers['content-length'] = str(len(data))

            await send({
                # PERF(vytas): Inline the value of
                #   EventType.HTTP_RESPONSE_START in this critical code path.
                'type': 'http.response.start',
                'status': resp_status,
                'headers': resp._asgi_headers(default_media_type)
            })

            await send({
                # PERF(vytas): Inline the value of
                #   EventType.HTTP_RESPONSE_BODY in this critical code path.
                'type': 'http.response.body',
                'body': data
            })

            # PERF(vytas): Check resp._registered_callbacks directly to shave
            #   off a function call since this is a hot/critical code path.
            if resp._registered_callbacks:
                self._schedule_callbacks(resp)
            return

        stream = resp.stream
        if not stream:
            resp._headers['content-length'] = '0'

        await send({
            # PERF(vytas): Inline the value of
            #   EventType.HTTP_RESPONSE_START in this critical code path.
            'type': 'http.response.start',
            'status': resp_status,
            'headers': resp._asgi_headers(default_media_type)
        })

        if stream:
            # Detect whether this is one of the following:
            #
            #   (a) async file-like object (e.g., aiofiles)
            #   (b) async generator
            #   (c) async iterator
            #

            if hasattr(stream, 'read'):
                while True:
                    data = await stream.read(self._STREAM_BLOCK_SIZE)
                    if data == b'':
                        break
                    else:
                        await send({
                            'type': EventType.HTTP_RESPONSE_BODY,

                            # NOTE(kgriffs): Handle the case in which data == None
                            'body': data or b'',
                            'more_body': True
                        })
            else:
                # NOTE(kgriffs): Works for both async generators and iterators
                try:
                    async for data in stream:
                        # NOTE(kgriffs): We can not rely on StopIteration
                        #   because of Pep 479 that is implemented starting
                        #   with Python 3.7. AFAICT this is only an issue
                        #   when using an async iterator instead of an async
                        #   generator.
                        if data is None:
                            break

                        await send({
                            'type': EventType.HTTP_RESPONSE_BODY,
                            'body': data,
                            'more_body': True
                        })
                except TypeError as ex:
                    if isasyncgenfunction(stream):
                        raise TypeError(
                            'The object assigned to Response.stream appears to '
                            'be an async generator function. A generator '
                            'object is expected instead. This can be obtained '
                            'simply by calling the generator function, e.g.: '
                            'resp.stream = some_asyncgen_function()')

                    raise TypeError(
                        'Response.stream must be a generator or implement an '
                        '__aiter__ method. Error raised while iterating over '
                        'Response.stream: ' + str(ex))

            if hasattr(stream, 'close'):
                await stream.close()

        await send(_EVT_RESP_EOF)

        # PERF(vytas): Check resp._registered_callbacks directly to shave
        #   off a function call since this is a hot/critical code path.
        if resp._registered_callbacks:
            self._schedule_callbacks(resp)
Esempio n. 24
0
def validate_req_body(req, resp, resource, params):
    if "json" not in req.context:
        msg = "Empty response body. A valid JSON document is required."
        raise HTTPBadRequest("Bad request", msg)
Esempio n. 25
0
def error_handler(ex, req, resp, params):
    raise HTTPBadRequest(type(ex).__name__, str(ex))