Пример #1
0
    def handle_error(self, e):
        '''
        Error handler for the API transforms a raised exception into a Flask response,
        with the appropriate HTTP status code and body.

        :param Exception e: the raised Exception object

        '''
        got_request_exception.send(current_app._get_current_object(), exception=e)

        headers = Headers()
        if e.__class__ in self.error_handlers:
            handler = self.error_handlers[e.__class__]
            result = handler(e)
            default_data, code, headers = unpack(result, 500)
        elif isinstance(e, HTTPException):
            code = e.code
            default_data = {
                'message': getattr(e, 'description', HTTP_STATUS_CODES.get(code, ''))
            }
            headers = e.get_response().headers
        elif self._default_error_handler:
            result = self._default_error_handler(e)
            default_data, code, headers = unpack(result, 500)
        else:
            code = 500
            default_data = {
                'message': HTTP_STATUS_CODES.get(code, str(e)),
            }

        default_data['message'] = default_data.get('message', str(e))
        data = getattr(e, 'data', default_data)
        fallback_mediatype = None

        if code >= 500:
            exc_info = sys.exc_info()
            if exc_info[1] is None:
                exc_info = None
            current_app.log_exception(exc_info)

        elif code == 404 and current_app.config.get("ERROR_404_HELP", True):
            data['message'] = self._help_on_404(data.get('message', None))

        elif code == 406 and self.default_mediatype is None:
            # if we are handling NotAcceptable (406), make sure that
            # make_response uses a representation we support as the
            # default mediatype (so that make_response doesn't throw
            # another NotAcceptable error).
            supported_mediatypes = list(self.representations.keys())
            fallback_mediatype = supported_mediatypes[0] if supported_mediatypes else "text/plain"

        # Remove blacklisted headers
        for header in HEADERS_BLACKLIST:
            headers.pop(header, None)

        resp = self.make_response(data, code, headers, fallback_mediatype=fallback_mediatype)

        if code == 401:
            resp = self.unauthorized(resp)
        return resp
Пример #2
0
    def handle_error(self, e):
        '''
        Error handler for the API transforms a raised exception into a Flask response,
        with the appropriate HTTP status code and body.

        :param Exception e: the raised Exception object

        '''
        got_request_exception.send(current_app._get_current_object(), exception=e)

        headers = Headers()
        if e.__class__ in self.error_handlers:
            handler = self.error_handlers[e.__class__]
            result = handler(e)
            default_data, code, headers = unpack(result, 500)
        elif isinstance(e, HTTPException):
            code = e.code
            default_data = {
                'message': getattr(e, 'description', HTTP_STATUS_CODES.get(code, ''))
            }
            headers = e.get_response().headers
        elif self._default_error_handler:
            result = self._default_error_handler(e)
            default_data, code, headers = unpack(result, 500)
        else:
            code = 500
            default_data = {
                'message': HTTP_STATUS_CODES.get(code, str(e)),
            }

        default_data['message'] = default_data.get('message', str(e))
        data = getattr(e, 'data', default_data)
        fallback_mediatype = None

        if code >= 500:
            exc_info = sys.exc_info()
            if exc_info[1] is None:
                exc_info = None
            current_app.log_exception(exc_info)

        elif code == 404 and current_app.config.get("ERROR_404_HELP", True):
            data['message'] = self._help_on_404(data.get('message', None))

        elif code == 406 and self.default_mediatype is None:
            # if we are handling NotAcceptable (406), make sure that
            # make_response uses a representation we support as the
            # default mediatype (so that make_response doesn't throw
            # another NotAcceptable error).
            supported_mediatypes = list(self.representations.keys())
            fallback_mediatype = supported_mediatypes[0] if supported_mediatypes else "text/plain"

        # Remove blacklisted headers
        for header in HEADERS_BLACKLIST:
            headers.pop(header, None)

        resp = self.make_response(data, code, headers, fallback_mediatype=fallback_mediatype)

        if code == 401:
            resp = self.unauthorized(resp)
        return resp
Пример #3
0
def _create_error_out(api_exception):
    got_request_exception.send(current_app._get_current_object(),
                               exception=api_exception)

    headers = Headers()
    if isinstance(api_exception, HTTPException):
        status_code = api_exception.code
        msg = getattr(api_exception, 'description',
                      http_status_message(status_code))
        headers = api_exception.get_response().headers
    else:
        status_code = 500
        msg = http_status_message(status_code)

    remove_headers = ('Content-Length', )

    for header in remove_headers:
        headers.pop(header, None)

    if status_code and status_code >= 500:
        exc_info = sys.exc_info()
        current_app.log_exception(exc_info)

    error_out = {
        'error': {
            'message': msg,
            'name': type(api_exception).__name__
        },
        'status': status_code,
        'success': False
    }

    return error_out, status_code, headers
Пример #4
0
    def from_environ(cls, environ, **kwargs):
        """Turn an environ dict back into a builder. Any extra kwargs
        override the args extracted from the environ.

        .. versionadded:: 0.15
        """
        headers = Headers(EnvironHeaders(environ))
        out = {
            "path": environ["PATH_INFO"],
            "base_url": cls._make_base_url(
                environ["wsgi.url_scheme"],
                headers.pop("Host"),
                environ["SCRIPT_NAME"],
            ),
            "query_string": environ["QUERY_STRING"],
            "method": environ["REQUEST_METHOD"],
            "input_stream": environ["wsgi.input"],
            "content_type": headers.pop("Content-Type", None),
            "content_length": headers.pop("Content-Length", None),
            "errors_stream": environ["wsgi.errors"],
            "multithread": environ["wsgi.multithread"],
            "multiprocess": environ["wsgi.multiprocess"],
            "run_once": environ["wsgi.run_once"],
            "headers": headers,
        }
        out.update(kwargs)
        return cls(**out)
Пример #5
0
def get_request():
    # copy of headers
    headers = Headers(request.headers)
    # remove unwanted extra headers
    for h in REMOVE_HEADERS:
        if headers.has_key(h):
            headers.pop(h)
    json_headers = json.dumps(OrderedDict(headers))
    # create file if not exists
    if not os.path.isfile(os.path.abspath(HTTP_HEADERS_FILENAME)):
        with open(os.path.abspath(HTTP_HEADERS_FILENAME), "w+"):
            pass

    # read file
    with open(os.path.abspath(HTTP_HEADERS_FILENAME), "r") as f:
        headers_data = f.readlines()
    # append new headers
    headers_data.append(json_headers + ",\n")
    # deduplicate
    headers_data_to_write = dedup(headers_data)

    # write output data
    with open(os.path.abspath(HTTP_HEADERS_FILENAME), "w") as f:
        for h in headers_data_to_write:
            f.write(h)

    return json_headers
Пример #6
0
    def get_pdf(self, report, record_id, context=None):
        """Used to return the content of a generated PDF.

        :returns: pdf
        """
        url = "/report/pdf/report/" + report.report_file + "/" + str(record_id)
        reqheaders = Headers(request.httprequest.headers)
        reqheaders.pop("Accept")
        reqheaders.add("Accept", "application/pdf")
        reqheaders.pop("Content-Type")
        reqheaders.add("Content-Type", "text/plain")
        response = Client(request.httprequest.app, BaseResponse).get(url, headers=reqheaders, follow_redirects=True)
        return response.data
Пример #7
0
def copy_headers(remote: requests.Response, response: Response, *, server_map,
                 **kwargs) -> Headers:
    server_origin = server_map['origins']['main']
    remote_url: SplitResult = urlsplit(remote.url)
    headers = Headers(remote.headers.items())

    headers.pop('Set-Cookie', None)
    headers.pop('Transfer-Encoding', None)
    response.headers = headers

    if 'Location' in headers:
        headers[
            'Location'] = f'{server_origin}/{urljoin(remote_url.geturl(), headers["Location"])}'

    response.headers.update(headers)
    return headers
Пример #8
0
    def handle_error(self, e):
        """Error handler for the API transforms a raised exception into a Flask
        response, with the appropriate HTTP status code and body.

        :param e: the raised Exception object
        :type e: Exception

        """
        got_request_exception.send(current_app._get_current_object(), exception=e)

        if not isinstance(e, HTTPException) and current_app.propagate_exceptions:
            exc_type, exc_value, tb = sys.exc_info()
            if exc_value is e:
                raise
            else:
                raise e

        headers = Headers()
        if isinstance(e, HTTPException):
            code = e.code
            default_data = {
                'message': getattr(e, 'description', http_status_message(code))
            }
            headers = e.get_response().headers
        else:
            code = 500
            default_data = {
                'message': http_status_message(code),
            }

        # Werkzeug exceptions generate a content-length header which is added
        # to the response in addition to the actual content-length header
        # https://github.com/flask-restful/flask-restful/issues/534
        remove_headers = ('Content-Length',)

        for header in remove_headers:
            headers.pop(header, None)

        data = getattr(e, 'data', default_data)

        if code and code >= 500:
            exc_info = sys.exc_info()
            if exc_info[1] is None:
                exc_info = None
            current_app.log_exception(exc_info)

        help_on_404 = current_app.config.get("ERROR_404_HELP", True)
        if code == 404 and help_on_404:
            rules = dict([(re.sub('(<.*>)', '', rule.rule), rule.rule)
                          for rule in current_app.url_map.iter_rules()])
            close_matches = difflib.get_close_matches(request.path, rules.keys())
            if close_matches:
                # If we already have a message, add punctuation and continue it.
                if "message" in data:
                    data["message"] = data["message"].rstrip('.') + '. '
                else:
                    data["message"] = ""

                data['message'] += 'You have requested this URI [' + request.path + \
                                   '] but did you mean ' + \
                                   ' or '.join((
                                       rules[match] for match in close_matches)
                                   ) + ' ?'

        error_cls_name = type(e).__name__
        if error_cls_name in self.errors:
            custom_data = self.errors.get(error_cls_name, {})
            code = custom_data.get('status', 500)
            data.update(custom_data)

        if code == 406 and self.default_mediatype is None:
            # if we are handling NotAcceptable (406), make sure that
            # make_response uses a representation we support as the
            # default mediatype (so that make_response doesn't throw
            # another NotAcceptable error).
            supported_mediatypes = list(self.representations.keys())
            fallback_mediatype = supported_mediatypes[0] if supported_mediatypes else "text/plain"
            resp = self.make_response(
                data,
                code,
                headers,
                fallback_mediatype = fallback_mediatype
            )
        else:
            resp = self.make_response(data, code, headers)

        if code == 401:
            resp = self.unauthorized(resp)
        return resp
Пример #9
0
    def handle_error(self, e):
        """Error handler for the API transforms a raised exception into a Flask
        response, with the appropriate HTTP status code and body.

        :param e: the raised Exception object
        :type e: Exception

        """
        got_request_exception.send(current_app._get_current_object(), exception=e)

        if not isinstance(e, HTTPException) and current_app.propagate_exceptions:
            exc_type, exc_value, tb = sys.exc_info()
            if exc_value is e:
                raise
            else:
                raise e

        headers = Headers()
        if isinstance(e, HTTPException):
            code = e.code
            default_data = {
                'message': getattr(e, 'description', http_status_message(code))
            }
            headers = e.get_response().headers
        else:
            code = 500
            default_data = {
                'message': http_status_message(code),
            }

        # Werkzeug exceptions generate a content-length header which is added
        # to the response in addition to the actual content-length header
        # https://github.com/flask-restful/flask-restful/issues/534
        remove_headers = ('Content-Length',)

        for header in remove_headers:
            headers.pop(header, None)

        data = getattr(e, 'data', default_data)

        if code >= 500:
            exc_info = sys.exc_info()
            if exc_info[1] is None:
                exc_info = None
            current_app.log_exception(exc_info)

        help_on_404 = current_app.config.get("ERROR_404_HELP", True)
        if code == 404 and help_on_404:
            rules = dict([(re.sub('(<.*>)', '', rule.rule), rule.rule)
                          for rule in current_app.url_map.iter_rules()])
            close_matches = difflib.get_close_matches(request.path, rules.keys())
            if close_matches:
                # If we already have a message, add punctuation and continue it.
                if "message" in data:
                    data["message"] = data["message"].rstrip('.') + '. '
                else:
                    data["message"] = ""

                data['message'] += 'You have requested this URI [' + request.path + \
                                   '] but did you mean ' + \
                                   ' or '.join((
                                       rules[match] for match in close_matches)
                                   ) + ' ?'

        error_cls_name = type(e).__name__
        if error_cls_name in self.errors:
            custom_data = self.errors.get(error_cls_name, {})
            code = custom_data.get('status', 500)
            data.update(custom_data)

        if code == 406 and self.default_mediatype is None:
            # if we are handling NotAcceptable (406), make sure that
            # make_response uses a representation we support as the
            # default mediatype (so that make_response doesn't throw
            # another NotAcceptable error).
            supported_mediatypes = list(self.representations.keys())
            fallback_mediatype = supported_mediatypes[0] if supported_mediatypes else "text/plain"
            resp = self.make_response(
                data,
                code,
                headers,
                fallback_mediatype = fallback_mediatype
            )
        else:
            resp = self.make_response(data, code, headers)

        if code == 401:
            resp = self.unauthorized(resp)
        return resp
Пример #10
0
    def handle_error(self, e):
        '''
        Error handler for the API transforms a raised exception into a Flask response,
        with the appropriate HTTP status code and body.

        :param Exception e: the raised Exception object

        '''
        got_request_exception.send(current_app._get_current_object(), exception=e)

        include_message_in_response = current_app.config.get("ERROR_INCLUDE_MESSAGE", True)
        default_data = {}

        headers = Headers()

        for typecheck, handler in six.iteritems(self._own_and_child_error_handlers):
            if isinstance(e, typecheck):
                result = handler(e)
                default_data, code, headers = unpack(result, HTTPStatus.INTERNAL_SERVER_ERROR)
                break
        else:
            if isinstance(e, HTTPException):
                code = HTTPStatus(e.code)
                if include_message_in_response:
                    default_data = {
                        'message': getattr(e, 'description', code.phrase)
                    }
                headers = e.get_response().headers
            elif self._default_error_handler:
                result = self._default_error_handler(e)
                default_data, code, headers = unpack(result, HTTPStatus.INTERNAL_SERVER_ERROR)
            else:
                code = HTTPStatus.INTERNAL_SERVER_ERROR
                if include_message_in_response:
                    default_data = {
                        'message': code.phrase,
                    }

        if include_message_in_response:
            default_data['message'] = default_data.get('message', str(e))

        data = getattr(e, 'data', default_data)
        fallback_mediatype = None

        if code >= HTTPStatus.INTERNAL_SERVER_ERROR:
            exc_info = sys.exc_info()
            if exc_info[1] is None:
                exc_info = None
            current_app.log_exception(exc_info)

        elif code == HTTPStatus.NOT_FOUND and current_app.config.get("ERROR_404_HELP", True) \
                and include_message_in_response:
            data['message'] = self._help_on_404(data.get('message', None))

        elif code == HTTPStatus.NOT_ACCEPTABLE and self.default_mediatype is None:
            # if we are handling NotAcceptable (406), make sure that
            # make_response uses a representation we support as the
            # default mediatype (so that make_response doesn't throw
            # another NotAcceptable error).
            supported_mediatypes = list(self.representations.keys())
            fallback_mediatype = supported_mediatypes[0] if supported_mediatypes else "text/plain"

        # Remove blacklisted headers
        for header in HEADERS_BLACKLIST:
            headers.pop(header, None)

        resp = self.make_response(data, code, headers, fallback_mediatype=fallback_mediatype)

        if code == HTTPStatus.UNAUTHORIZED:
            resp = self.unauthorized(resp)
        return resp
Пример #11
0
class Response(AbstractResponse, ResponseCookieMixin, ResponseRedirectMixin):
    def __init__(self,
                 context,
                 *,
                 status=200,
                 version=None,
                 headers=None,
                 body=None):
        super().__init__()
        self.context = context
        context.response = self
        self._status = HTTPStatus(status)
        self.version = version or 'HTTP/1.1'
        if headers is None:
            self.headers = Headers()
        else:
            self.headers = Headers(headers)
        self.body = body

    @property
    def status(self):
        return int(self._status)

    @status.setter
    def status(self, value):
        self._status = HTTPStatus(value)

    @property
    def status_text(self):
        return self._status.phrase

    @property
    def body(self):
        return self._body

    @body.setter
    def body(self, value):
        if value is None:
            self._body = stream(b'')
            self.content_length = 0
        elif isinstance(value, bytes):
            self._body = stream(value)
            self.content_length = len(value)
        elif isinstance(value, str):
            value = value.encode('utf-8')
            self._body = stream(value)
            self.content_length = len(value)
        elif inspect.isasyncgen(value):
            self._body = value
        else:
            msg = (f'response body should be bytes, str or async generator, '
                   f'type {type(value).__name__} is not supported')
            raise ValueError(msg)

    def json(self, value):
        sort_keys = self.context.config.json_sort_keys
        indent = None
        if self.context.config.json_pretty:
            indent = 4
        if self.context.config.json_ujson_enable:
            dumps = ujson.dumps
        else:
            dumps = json.dumps
        text = dumps(value,
                     ensure_ascii=False,
                     indent=indent,
                     sort_keys=sort_keys)
        self.body = text.encode('utf-8')
        self.headers['Content-Type'] = 'application/json;charset=utf-8'

    @property
    def chunked(self):
        return self.headers.get('Transfer-Encoding', '').lower() == 'chunked'

    @chunked.setter
    def chunked(self, value):
        if value:
            self.headers['Transfer-Encoding'] = 'chunked'
        else:
            self.headers.pop('Transfer-Encoding', None)

    @property
    def content_length(self):
        length = self.headers.get('Content-Length', '')
        if not length:
            return None
        return int(length)

    @content_length.setter
    def content_length(self, value):
        if value is None:
            self.headers.pop('Content-Length', None)
        else:
            self.headers['Content-Length'] = value

    @property
    def keep_alive(self):
        connection = self.headers.get('Connection', '').lower()
        if connection == 'keep-alive':
            return True
        elif connection == 'close':
            return False
        return None

    @keep_alive.setter
    def keep_alive(self, value):
        if value is None:
            self.headers.pop('Connection', None)
        elif value:
            self.headers['Connection'] = 'keep-alive'
        else:
            self.headers['Connection'] = 'close'

    def __repr__(self):
        return f'<{type(self).__name__} {self.status} {self.status_text}>'
Пример #12
0
def test_headers():
    # simple header tests
    headers = Headers()
    headers.add("Content-Type", "text/plain")
    headers.add("X-Foo", "bar")
    assert "x-Foo" in headers
    assert "Content-type" in headers

    headers["Content-Type"] = "foo/bar"
    assert headers["Content-Type"] == "foo/bar"
    assert len(headers.getlist("Content-Type")) == 1

    # list conversion
    assert headers.to_list() == [("Content-Type", "foo/bar"), ("X-Foo", "bar")]
    assert str(headers) == ("Content-Type: foo/bar\r\n" "X-Foo: bar\r\n" "\r\n")
    assert str(Headers()) == "\r\n"

    # extended add
    headers.add("Content-Disposition", "attachment", filename="foo")
    assert headers["Content-Disposition"] == "attachment; filename=foo"

    headers.add("x", "y", z='"')
    assert headers["x"] == r'y; z="\""'

    # defaults
    headers = Headers([("Content-Type", "text/plain"), ("X-Foo", "bar"), ("X-Bar", "1"), ("X-Bar", "2")])
    assert headers.getlist("x-bar") == ["1", "2"]
    assert headers.get("x-Bar") == "1"
    assert headers.get("Content-Type") == "text/plain"

    assert headers.setdefault("X-Foo", "nope") == "bar"
    assert headers.setdefault("X-Bar", "nope") == "1"
    assert headers.setdefault("X-Baz", "quux") == "quux"
    assert headers.setdefault("X-Baz", "nope") == "quux"
    headers.pop("X-Baz")

    # type conversion
    assert headers.get("x-bar", type=int) == 1
    assert headers.getlist("x-bar", type=int) == [1, 2]

    # list like operations
    assert headers[0] == ("Content-Type", "text/plain")
    assert headers[:1] == Headers([("Content-Type", "text/plain")])
    del headers[:2]
    del headers[-1]
    assert headers == Headers([("X-Bar", "1")])

    # copying
    a = Headers([("foo", "bar")])
    b = a.copy()
    a.add("foo", "baz")
    assert a.getlist("foo") == ["bar", "baz"]
    assert b.getlist("foo") == ["bar"]

    headers = Headers([("a", 1)])
    assert headers.pop("a") == 1
    assert headers.pop("b", 2) == 2
    assert_raises(KeyError, headers.pop, "c")

    # set replaces and accepts same arguments as add
    a = Headers()
    a.set("Content-Disposition", "useless")
    a.set("Content-Disposition", "attachment", filename="foo")
    assert a["Content-Disposition"] == "attachment; filename=foo"
Пример #13
0
    def handle_error(self, e):
        '''
        Error handler for the API transforms a raised exception into a Flask response,
        with the appropriate HTTP status code and body.

        :param Exception e: the raised Exception object

        '''
        got_request_exception.send(current_app._get_current_object(),
                                   exception=e)

        include_message_in_response = current_app.config.get(
            "ERROR_INCLUDE_MESSAGE", True)
        default_data = {}

        headers = Headers()
        if e.__class__ in self.error_handlers:
            handler = self.error_handlers[e.__class__]
            result = handler(e)
            default_data, code, headers = unpack(
                result, HTTPStatus.INTERNAL_SERVER_ERROR)
        elif isinstance(e, HTTPException):
            code = HTTPStatus(e.code)
            if include_message_in_response:
                default_data = {
                    'message': getattr(e, 'description', code.phrase)
                }
            headers = e.get_response().headers
        elif self._default_error_handler:
            result = self._default_error_handler(e)
            default_data, code, headers = unpack(
                result, HTTPStatus.INTERNAL_SERVER_ERROR)
        else:
            code = HTTPStatus.INTERNAL_SERVER_ERROR
            if include_message_in_response:
                default_data = {
                    'message': code.phrase,
                }

        if include_message_in_response:
            default_data['message'] = default_data.get('message', str(e))

        data = getattr(e, 'data', default_data)
        fallback_mediatype = None

        if code >= HTTPStatus.INTERNAL_SERVER_ERROR:
            exc_info = sys.exc_info()
            if exc_info[1] is None:
                exc_info = None
            current_app.log_exception(exc_info)

        elif code == HTTPStatus.NOT_FOUND and current_app.config.get("ERROR_404_HELP", True) \
                and include_message_in_response:
            data['message'] = self._help_on_404(data.get('message', None))

        elif code == HTTPStatus.NOT_ACCEPTABLE and self.default_mediatype is None:
            # if we are handling NotAcceptable (406), make sure that
            # make_response uses a representation we support as the
            # default mediatype (so that make_response doesn't throw
            # another NotAcceptable error).
            supported_mediatypes = list(self.representations.keys())
            fallback_mediatype = supported_mediatypes[
                0] if supported_mediatypes else "text/plain"

        # Remove blacklisted headers
        for header in HEADERS_BLACKLIST:
            headers.pop(header, None)

        resp = self.make_response(data,
                                  code,
                                  headers,
                                  fallback_mediatype=fallback_mediatype)

        if code == HTTPStatus.UNAUTHORIZED:
            resp = self.unauthorized(resp)
        return resp
Пример #14
0
def test_headers():
    # simple header tests
    headers = Headers()
    headers.add('Content-Type', 'text/plain')
    headers.add('X-Foo', 'bar')
    assert 'x-Foo' in headers
    assert 'Content-type' in headers

    headers['Content-Type'] = 'foo/bar'
    assert headers['Content-Type'] == 'foo/bar'
    assert len(headers.getlist('Content-Type')) == 1

    # list conversion
    assert headers.to_list() == [
        ('Content-Type', 'foo/bar'),
        ('X-Foo', 'bar')
    ]
    assert str(headers) == (
        "Content-Type: foo/bar\r\n"
        "X-Foo: bar\r\n"
        "\r\n")
    assert str(Headers()) == "\r\n"

    # extended add
    headers.add('Content-Disposition', 'attachment', filename='foo')
    assert headers['Content-Disposition'] == 'attachment; filename=foo'

    headers.add('x', 'y', z='"')
    assert headers['x'] == r'y; z="\""'

    # defaults
    headers = Headers([
        ('Content-Type', 'text/plain'),
        ('X-Foo',        'bar'),
        ('X-Bar',        '1'),
        ('X-Bar',        '2')
    ])
    assert headers.getlist('x-bar') == ['1', '2']
    assert headers.get('x-Bar') == '1'
    assert headers.get('Content-Type') == 'text/plain'

    assert headers.setdefault('X-Foo', 'nope') == 'bar'
    assert headers.setdefault('X-Bar', 'nope') == '1'
    assert headers.setdefault('X-Baz', 'quux') == 'quux'
    assert headers.setdefault('X-Baz', 'nope') == 'quux'
    headers.pop('X-Baz')

    # type conversion
    assert headers.get('x-bar', type=int) == 1
    assert headers.getlist('x-bar', type=int) == [1, 2]

    # list like operations
    assert headers[0] == ('Content-Type', 'text/plain')
    assert headers[:1] == Headers([('Content-Type', 'text/plain')])
    del headers[:2]
    del headers[-1]
    assert headers == Headers([('X-Bar', '1')])

    # copying
    a = Headers([('foo', 'bar')])
    b = a.copy()
    a.add('foo', 'baz')
    assert a.getlist('foo') == ['bar', 'baz']
    assert b.getlist('foo') == ['bar']

    headers = Headers([('a', 1)])
    assert headers.pop('a') == 1
    assert headers.pop('b', 2) == 2
    assert_raises(KeyError, headers.pop, 'c')

    # set replaces and accepts same arguments as add
    a = Headers()
    a.set('Content-Disposition', 'useless')
    a.set('Content-Disposition', 'attachment', filename='foo')
    assert a['Content-Disposition'] == 'attachment; filename=foo'
Пример #15
0
def test_headers():
    # simple header tests
    headers = Headers()
    headers.add('Content-Type', 'text/plain')
    headers.add('X-Foo', 'bar')
    assert 'x-Foo' in headers
    assert 'Content-type' in headers

    headers['Content-Type'] = 'foo/bar'
    assert headers['Content-Type'] == 'foo/bar'
    assert len(headers.getlist('Content-Type')) == 1

    # list conversion
    assert headers.to_list() == [('Content-Type', 'foo/bar'), ('X-Foo', 'bar')]
    assert str(headers) == ("Content-Type: foo/bar\r\n"
                            "X-Foo: bar\r\n"
                            "\r\n")
    assert str(Headers()) == "\r\n"

    # extended add
    headers.add('Content-Disposition', 'attachment', filename='foo')
    assert headers['Content-Disposition'] == 'attachment; filename=foo'

    headers.add('x', 'y', z='"')
    assert headers['x'] == r'y; z="\""'

    # defaults
    headers = Headers([('Content-Type', 'text/plain'), ('X-Foo', 'bar'),
                       ('X-Bar', '1'), ('X-Bar', '2')])
    assert headers.getlist('x-bar') == ['1', '2']
    assert headers.get('x-Bar') == '1'
    assert headers.get('Content-Type') == 'text/plain'

    assert headers.setdefault('X-Foo', 'nope') == 'bar'
    assert headers.setdefault('X-Bar', 'nope') == '1'
    assert headers.setdefault('X-Baz', 'quux') == 'quux'
    assert headers.setdefault('X-Baz', 'nope') == 'quux'
    headers.pop('X-Baz')

    # type conversion
    assert headers.get('x-bar', type=int) == 1
    assert headers.getlist('x-bar', type=int) == [1, 2]

    # list like operations
    assert headers[0] == ('Content-Type', 'text/plain')
    assert headers[:1] == Headers([('Content-Type', 'text/plain')])
    del headers[:2]
    del headers[-1]
    assert headers == Headers([('X-Bar', '1')])

    # copying
    a = Headers([('foo', 'bar')])
    b = a.copy()
    a.add('foo', 'baz')
    assert a.getlist('foo') == ['bar', 'baz']
    assert b.getlist('foo') == ['bar']

    headers = Headers([('a', 1)])
    assert headers.pop('a') == 1
    assert headers.pop('b', 2) == 2
    assert_raises(KeyError, headers.pop, 'c')

    # set replaces and accepts same arguments as add
    a = Headers()
    a.set('Content-Disposition', 'useless')
    a.set('Content-Disposition', 'attachment', filename='foo')
    assert a['Content-Disposition'] == 'attachment; filename=foo'
Пример #16
0
    def handle_error(self, e):
        """Error handler for the API transforms a raised exception into a Flask
        response, with the appropriate HTTP status code and body.

        :param e: the raised Exception object
        :type e: Exception

        """
        got_request_exception.send(current_app._get_current_object(),
                                   exception=e)

        if not isinstance(e,
                          HTTPException) and current_app.propagate_exceptions:
            exc_type, exc_value, tb = sys.exc_info()
            if exc_value is e:
                raise
            else:
                raise e

        headers = Headers()
        if isinstance(e, HTTPException):
            if e.response is not None:
                # If HTTPException is initialized with a response, then return e.get_response().
                # This prevents specified error response from being overridden.
                # eg. HTTPException(response=Response("Hello World"))
                resp = e.get_response()
                return resp

            code = e.code
            default_data = {
                'message': getattr(e, 'description', http_status_message(code))
            }
            headers = e.get_response().headers
        else:
            code = 500
            default_data = {
                'message': http_status_message(code),
            }

        # Werkzeug exceptions generate a content-length header which is added
        # to the response in addition to the actual content-length header
        # https://github.com/flask-restful/flask-restful/issues/534
        remove_headers = ('Content-Length', )

        for header in remove_headers:
            headers.pop(header, None)

        data = getattr(e, 'data', default_data)

        if code and code >= 500:
            exc_info = sys.exc_info()
            if exc_info[1] is None:
                exc_info = None
            current_app.log_exception(exc_info)

        error_cls_name = type(e).__name__
        if error_cls_name in self.errors:
            custom_data = self.errors.get(error_cls_name, {})
            code = custom_data.get('status', 500)
            data.update(custom_data)

        if code == 406 and self.default_mediatype is None:
            # if we are handling NotAcceptable (406), make sure that
            # make_response uses a representation we support as the
            # default mediatype (so that make_response doesn't throw
            # another NotAcceptable error).
            supported_mediatypes = list(self.representations.keys())
            fallback_mediatype = supported_mediatypes[
                0] if supported_mediatypes else "text/plain"
            resp = self.make_response(data,
                                      code,
                                      headers,
                                      fallback_mediatype=fallback_mediatype)
        else:
            resp = self.make_response(data, code, headers)

        if code == 401:
            resp = self.unauthorized(resp)
        return resp
Пример #17
0
    def handle_error(self, e):
        """
        Error handler for the API transforms a raised exception into a Flask response,
        with the appropriate HTTP status code and body.

        :param Exception e: the raised Exception object

        """
        got_request_exception.send(current_app._get_current_object(),
                                   exception=e)

        # When propagate_exceptions is set, do not return the exception to the
        # client if a handler is configured for the exception.
        if (not isinstance(e, HTTPException)
                and current_app.propagate_exceptions
                and not isinstance(e, tuple(self.error_handlers.keys()))):

            exc_type, exc_value, tb = sys.exc_info()
            if exc_value is e:
                raise
            else:
                raise e

        include_message_in_response = current_app.config.get(
            "ERROR_INCLUDE_MESSAGE", True)
        default_data = {}

        headers = Headers()

        for typecheck, handler in six.iteritems(
                self._own_and_child_error_handlers):
            if isinstance(e, typecheck):
                result = handler(e)
                default_data, code, headers = unpack(
                    result, HTTPStatus.INTERNAL_SERVER_ERROR)
                break
        else:
            if isinstance(e, HTTPException):
                code = HTTPStatus(e.code)
                if include_message_in_response:
                    default_data = {
                        "message": getattr(e, "description", code.phrase)
                    }
                headers = e.get_response().headers
            elif self._default_error_handler:
                result = self._default_error_handler(e)
                default_data, code, headers = unpack(
                    result, HTTPStatus.INTERNAL_SERVER_ERROR)
            else:
                code = HTTPStatus.INTERNAL_SERVER_ERROR
                if include_message_in_response:
                    default_data = {
                        "message": code.phrase,
                    }

        if include_message_in_response:
            default_data["message"] = default_data.get("message", str(e))

        data = getattr(e, "data", default_data)
        fallback_mediatype = None

        if code >= HTTPStatus.INTERNAL_SERVER_ERROR:
            exc_info = sys.exc_info()
            if exc_info[1] is None:
                exc_info = None
            current_app.log_exception(exc_info)

        elif (code == HTTPStatus.NOT_FOUND
              and current_app.config.get("ERROR_404_HELP", True)
              and include_message_in_response):
            data["message"] = self._help_on_404(data.get("message", None))

        elif code == HTTPStatus.NOT_ACCEPTABLE and self.default_mediatype is None:
            # if we are handling NotAcceptable (406), make sure that
            # make_response uses a representation we support as the
            # default mediatype (so that make_response doesn't throw
            # another NotAcceptable error).
            supported_mediatypes = list(self.representations.keys())
            fallback_mediatype = (supported_mediatypes[0]
                                  if supported_mediatypes else "text/plain")

        # Remove blacklisted headers
        for header in HEADERS_BLACKLIST:
            headers.pop(header, None)

        resp = self.make_response(data,
                                  code,
                                  headers,
                                  fallback_mediatype=fallback_mediatype)

        if code == HTTPStatus.UNAUTHORIZED:
            resp = self.unauthorized(resp)
        return resp
Пример #18
0
    def handle_error(self, e):
        """Error handler for the API transforms a raised exception into a Flask
        response, with the appropriate HTTP status code and body.

        :param e: the raised Exception object
        :type e: Exception

        """
        got_request_exception.send(current_app._get_current_object(), exception=e)

        if not isinstance(e, HTTPException) and current_app.propagate_exceptions:
            exc_type, exc_value, tb = sys.exc_info()
            if exc_value is e:
                raise
            else:
                raise e

        headers = Headers()
        if isinstance(e, HTTPException):
            if e.response is not None:
                # If HTTPException is initialized with a response, then return e.get_response().
                # This prevents specified error response from being overridden.
                # eg. HTTPException(response=Response("Hello World"))
                resp = e.get_response()
                return resp

            code = e.code
            default_data = {
                'message': getattr(e, 'description', http_status_message(code))
            }
            headers = e.get_response().headers
        else:
            code = 500
            default_data = {
                'message': http_status_message(code),
            }

        # Werkzeug exceptions generate a content-length header which is added
        # to the response in addition to the actual content-length header
        # https://github.com/flask-restful/flask-restful/issues/534
        remove_headers = ('Content-Length',)

        for header in remove_headers:
            headers.pop(header, None)

        data = getattr(e, 'data', default_data)

        if code and code >= 500:
            exc_info = sys.exc_info()
            if exc_info[1] is None:
                exc_info = None
            current_app.log_exception(exc_info)

        error_cls_name = type(e).__name__
        if error_cls_name in self.errors:
            custom_data = self.errors.get(error_cls_name, {})
            code = custom_data.get('status', 500)
            data.update(custom_data)

        if code == 406 and self.default_mediatype is None:
            # if we are handling NotAcceptable (406), make sure that
            # make_response uses a representation we support as the
            # default mediatype (so that make_response doesn't throw
            # another NotAcceptable error).
            supported_mediatypes = list(self.representations.keys())
            fallback_mediatype = supported_mediatypes[0] if supported_mediatypes else "text/plain"
            resp = self.make_response(
                data,
                code,
                headers,
                fallback_mediatype = fallback_mediatype
            )
        else:
            resp = self.make_response(data, code, headers)

        if code == 401:
            resp = self.unauthorized(resp)
        return resp