Beispiel #1
0
    def build(
        self, req: Union[Request, ASGIRequest], resp: Union[_Response, _ASGIResponse]
    ):
        self.build_content(req, resp)
        resp.content_type = self.content_type
        if isinstance(self.status, int):
            self.status = code_to_http_status(self.status)

        resp.status = self.status
        resp.set_headers(self.headers)
        for name, params in self._stacked.items():
            method = getattr(resp, name)
            for args, kwds in params:
                method(*args, **kwds)
Beispiel #2
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:
            # 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