def _prepare_response(resource, dct, last_modified=None, etag=None, status=200, headers=None): """ Prepares the response object according to the client request and available renderers, making sure that all accessory directives (caching, etag, last-modified) are present. :param resource: the resource involved. :param dct: the dict that should be sent back as a response. :param last_modified: Last-Modified header value. :param etag: ETag header value. :param status: response status. .. versionchanged:: 0.7 Add support for regexes in X_DOMAINS_RE. Closes #660, #974. ETag value now surrounded by double quotes. Closes #794. .. versionchanged:: 0.6 JSONP Support. .. versionchanged:: 0.4 Support for optional extra headers. Fix #381. 500 instead of 404 if CORS is enabled. .. versionchanged:: 0.3 Support for X_MAX_AGE. .. versionchanged:: 0.1.0 Support for optional HATEOAS. .. versionchanged:: 0.0.9 Support for Python 3.3. .. versionchanged:: 0.0.7 Support for Rate-Limiting. .. versionchanged:: 0.0.6 Support for HEAD requests. .. versionchanged:: 0.0.5 Support for Cross-Origin Resource Sharing (CORS). .. versionadded:: 0.0.4 """ if request.method == 'OPTIONS': resp = app.make_default_options_response() else: # obtain the best match between client's request and available mime # types, along with the corresponding render function. mime, renderer_cls = _best_mime() # invoke the render function and obtain the corresponding rendered item rendered = renderer_cls().render(dct) # JSONP if config.JSONP_ARGUMENT: jsonp_arg = config.JSONP_ARGUMENT if jsonp_arg in request.args and 'json' in mime: callback = request.args.get(jsonp_arg) rendered = "%s(%s)" % (callback, rendered) # build the main wsgi response object resp = make_response(rendered, status) resp.mimetype = mime # extra headers if headers: for header, value in headers: if header != 'Content-Type': resp.headers.add(header, value) # cache directives if request.method in ('GET', 'HEAD'): if resource: cache_control = config.DOMAIN[resource]['cache_control'] expires = config.DOMAIN[resource]['cache_expires'] else: cache_control = config.CACHE_CONTROL expires = config.CACHE_EXPIRES if cache_control: resp.headers.add('Cache-Control', cache_control) if expires: resp.expires = time.time() + expires # etag and last-modified if etag: resp.headers.add('ETag', '"' + etag + '"') if last_modified: resp.headers.add('Last-Modified', date_to_rfc1123(last_modified)) # CORS origin = request.headers.get('Origin') if origin and (config.X_DOMAINS or config.X_DOMAINS_RE): if config.X_DOMAINS is None: domains = [] elif isinstance(config.X_DOMAINS, str): domains = [config.X_DOMAINS] else: domains = config.X_DOMAINS if config.X_DOMAINS_RE is None: domains_re = [] elif isinstance(config.X_DOMAINS_RE, str): domains_re = [config.X_DOMAINS_RE] else: domains_re = config.X_DOMAINS_RE # precompile regexes and ignore invalids domains_re_compiled = [] for domain_re in domains_re: try: domains_re_compiled.append(re.compile(domain_re)) except re.error: continue if config.X_HEADERS is None: headers = [] elif isinstance(config.X_HEADERS, str): headers = [config.X_HEADERS] else: headers = config.X_HEADERS if config.X_EXPOSE_HEADERS is None: expose_headers = [] elif isinstance(config.X_EXPOSE_HEADERS, str): expose_headers = [config.X_EXPOSE_HEADERS] else: expose_headers = config.X_EXPOSE_HEADERS # The only accepted value for Access-Control-Allow-Credentials header # is "true" allow_credentials = config.X_ALLOW_CREDENTIALS is True methods = app.make_default_options_response().headers.get('allow', '') if '*' in domains: resp.headers.add('Access-Control-Allow-Origin', origin) resp.headers.add('Vary', 'Origin') elif any(origin == domain for domain in domains): resp.headers.add('Access-Control-Allow-Origin', origin) elif any(domain.match(origin) for domain in domains_re_compiled): resp.headers.add('Access-Control-Allow-Origin', origin) else: resp.headers.add('Access-Control-Allow-Origin', '') resp.headers.add('Access-Control-Allow-Headers', ', '.join(headers)) resp.headers.add('Access-Control-Expose-Headers', ', '.join(expose_headers)) resp.headers.add('Access-Control-Allow-Methods', methods) resp.headers.add('Access-Control-Max-Age', config.X_MAX_AGE) if allow_credentials: resp.headers.add('Access-Control-Allow-Credentials', "true") # Rate-Limiting limit = get_rate_limit() if limit and limit.send_x_headers: resp.headers.add('X-RateLimit-Remaining', str(limit.remaining)) resp.headers.add('X-RateLimit-Limit', str(limit.limit)) resp.headers.add('X-RateLimit-Reset', str(limit.reset)) return resp
def _prepare_response(resource, dct, last_modified=None, etag=None, status=200): """ Prepares the response object according to the client request and available renderers, making sure that all accessory directives (caching, etag, last-modified) are present. :param resource: the resource involved. :param dct: the dict that should be sent back as a response. :param last_modified: Last-Modified header value. :param etag: ETag header value. :param status: response status. .. versionchanged:: 0.0.7 Support for Rate-Limiting. .. versionchanged:: 0.0.6 Support for HEAD requests. .. versionchanged:: 0.0.5 Support for Cross-Origin Resource Sharing (CORS). .. versionadded:: 0.0.4 """ if request.method == 'OPTIONS': resp = app.make_default_options_response() else: # obtain the best match between client's request and available mime # types, along with the corresponding render function. mime, renderer = _best_mime() # invoke the render function and obtain the corresponding rendered item rendered = globals()[renderer](**dct) # build the main wsgi rensponse object resp = make_response(rendered, status) resp.mimetype = mime # cache directives if request.method in ('GET', 'HEAD'): if resource: cache_control = config.DOMAIN[resource]['cache_control'] expires = config.DOMAIN[resource]['cache_expires'] else: cache_control = config.CACHE_CONTROL expires = config.CACHE_EXPIRES if cache_control: resp.headers.add('Cache-Control', cache_control) if expires: resp.expires = time.time() + expires # etag and last-modified if etag: resp.headers.add('ETag', etag) if last_modified: resp.headers.add('Last-Modified', date_to_str(last_modified)) # CORS if 'Origin' in request.headers and config.X_DOMAINS is not None: if isinstance(config.X_DOMAINS, basestring): domains = [config.X_DOMAINS] else: domains = config.X_DOMAINS methods = app.make_default_options_response().headers['allow'] resp.headers.add('Access-Control-Allow-Origin', ', '.join(domains)) resp.headers.add('Access-Control-Allow-Methods', methods) resp.headers.add('Access-Control-Allow-Max-Age', 21600) # Rate-Limiting limit = get_rate_limit() if limit and limit.send_x_headers: resp.headers.add('X-RateLimit-Remaining', str(limit.remaining)) resp.headers.add('X-RateLimit-Limit', str(limit.limit)) resp.headers.add('X-RateLimit-Reset', str(limit.reset)) return resp
def _prepare_response(resource, dct, last_modified=None, etag=None, status=200, headers=None): """ Prepares the response object according to the client request and available renderers, making sure that all accessory directives (caching, etag, last-modified) are present. :param resource: the resource involved. :param dct: the dict that should be sent back as a response. :param last_modified: Last-Modified header value. :param etag: ETag header value. :param status: response status. .. versionchanged:: 0.6 JSONP Support. .. versionchanged:: 0.4 Support for optional extra headers. Fix #381. 500 instead of 404 if CORS is enabled. .. versionchanged:: 0.3 Support for X_MAX_AGE. .. versionchanged:: 0.1.0 Support for optional HATEOAS. .. versionchanged:: 0.0.9 Support for Python 3.3. .. versionchanged:: 0.0.7 Support for Rate-Limiting. .. versionchanged:: 0.0.6 Support for HEAD requests. .. versionchanged:: 0.0.5 Support for Cross-Origin Resource Sharing (CORS). .. versionadded:: 0.0.4 """ if request.method == 'OPTIONS': resp = app.make_default_options_response() else: # obtain the best match between client's request and available mime # types, along with the corresponding render function. mime, renderer = _best_mime() # invoke the render function and obtain the corresponding rendered item rendered = globals()[renderer](dct) # JSONP if config.JSONP_ARGUMENT: jsonp_arg = config.JSONP_ARGUMENT if jsonp_arg in request.args and 'json' in mime: callback = request.args.get(jsonp_arg) rendered = "%s(%s)" % (callback, rendered) # build the main wsgi rensponse object resp = make_response(rendered, status) resp.mimetype = mime # extra headers if headers: for header, value in headers: if header != 'Content-Type': resp.headers.add(header, value) # cache directives if request.method in ('GET', 'HEAD'): if resource: cache_control = config.DOMAIN[resource]['cache_control'] expires = config.DOMAIN[resource]['cache_expires'] else: cache_control = config.CACHE_CONTROL expires = config.CACHE_EXPIRES if cache_control: resp.headers.add('Cache-Control', cache_control) if expires: resp.expires = time.time() + expires # etag and last-modified if etag: resp.headers.add('ETag', etag) if last_modified: resp.headers.add('Last-Modified', date_to_rfc1123(last_modified)) # CORS origin = request.headers.get('Origin') if origin and config.X_DOMAINS: if isinstance(config.X_DOMAINS, str): domains = [config.X_DOMAINS] else: domains = config.X_DOMAINS if config.X_HEADERS is None: headers = [] elif isinstance(config.X_HEADERS, str): headers = [config.X_HEADERS] else: headers = config.X_HEADERS if config.X_EXPOSE_HEADERS is None: expose_headers = [] elif isinstance(config.X_EXPOSE_HEADERS, str): expose_headers = [config.X_EXPOSE_HEADERS] else: expose_headers = config.X_EXPOSE_HEADERS # The only accepted value for Access-Control-Allow-Credentials header # is "true" allow_credentials = config.X_ALLOW_CREDENTIALS is True methods = app.make_default_options_response().headers.get('allow', '') if '*' in domains: resp.headers.add('Access-Control-Allow-Origin', origin) resp.headers.add('Vary', 'Origin') elif origin in domains: resp.headers.add('Access-Control-Allow-Origin', origin) else: resp.headers.add('Access-Control-Allow-Origin', '') resp.headers.add('Access-Control-Allow-Headers', ', '.join(headers)) resp.headers.add('Access-Control-Expose-Headers', ', '.join(expose_headers)) resp.headers.add('Access-Control-Allow-Methods', methods) resp.headers.add('Access-Control-Max-Age', config.X_MAX_AGE) if allow_credentials: resp.headers.add('Access-Control-Allow-Credentials', "true") # Rate-Limiting limit = get_rate_limit() if limit and limit.send_x_headers: resp.headers.add('X-RateLimit-Remaining', str(limit.remaining)) resp.headers.add('X-RateLimit-Limit', str(limit.limit)) resp.headers.add('X-RateLimit-Reset', str(limit.reset)) return resp
def _prepare_response(resource, dct, last_modified=None, etag=None, status=200, headers=None): """ Prepares the response object according to the client request and available renderers, making sure that all accessory directives (caching, etag, last-modified) are present. :param resource: the resource involved. :param dct: the dict that should be sent back as a response. :param last_modified: Last-Modified header value. :param etag: ETag header value. :param status: response status. .. versionchanged:: 0.4 Support for optional extra headers. Fix #381. 500 instead of 404 if CORS is enabled. .. versionchanged:: 0.3 Support for X_MAX_AGE. .. versionchanged:: 0.1.0 Support for optional HATEOAS. .. versionchanged:: 0.0.9 Support for Python 3.3. .. versionchanged:: 0.0.7 Support for Rate-Limiting. .. versionchanged:: 0.0.6 Support for HEAD requests. .. versionchanged:: 0.0.5 Support for Cross-Origin Resource Sharing (CORS). .. versionadded:: 0.0.4 """ if request.method == 'OPTIONS': resp = app.make_default_options_response() else: # obtain the best match between client's request and available mime # types, along with the corresponding render function. mime, renderer = _best_mime() # invoke the render function and obtain the corresponding rendered item rendered = globals()[renderer](dct) # build the main wsgi rensponse object resp = make_response(rendered, status) resp.mimetype = mime # extra headers if headers: for header, value in headers: if header != 'Content-Type': resp.headers.add(header, value) # cache directives if request.method in ('GET', 'HEAD'): if resource: cache_control = config.DOMAIN[resource]['cache_control'] expires = config.DOMAIN[resource]['cache_expires'] else: cache_control = config.CACHE_CONTROL expires = config.CACHE_EXPIRES if cache_control: resp.headers.add('Cache-Control', cache_control) if expires: resp.expires = time.time() + expires # etag and last-modified if etag: resp.headers.add('ETag', etag) if last_modified: resp.headers.add('Last-Modified', date_to_rfc1123(last_modified)) # CORS origin = request.headers.get('Origin') if origin and config.X_DOMAINS: if isinstance(config.X_DOMAINS, str): domains = [config.X_DOMAINS] else: domains = config.X_DOMAINS if config.X_HEADERS is None: headers = [] elif isinstance(config.X_HEADERS, str): headers = [config.X_HEADERS] else: headers = config.X_HEADERS if config.X_EXPOSE_HEADERS is None: expose_headers = [] elif isinstance(config.X_EXPOSE_HEADERS, str): expose_headers = [config.X_EXPOSE_HEADERS] else: expose_headers = config.X_EXPOSE_HEADERS methods = app.make_default_options_response().headers.get('allow', '') if '*' in domains: resp.headers.add('Access-Control-Allow-Origin', origin) resp.headers.add('Vary', 'Origin') elif origin in domains: resp.headers.add('Access-Control-Allow-Origin', origin) else: resp.headers.add('Access-Control-Allow-Origin', '') resp.headers.add('Access-Control-Allow-Headers', ', '.join(headers)) resp.headers.add('Access-Control-Expose-Headers', ', '.join(expose_headers)) resp.headers.add('Access-Control-Allow-Methods', methods) resp.headers.add('Access-Control-Allow-Max-Age', config.X_MAX_AGE) # Rate-Limiting limit = get_rate_limit() if limit and limit.send_x_headers: resp.headers.add('X-RateLimit-Remaining', str(limit.remaining)) resp.headers.add('X-RateLimit-Limit', str(limit.limit)) resp.headers.add('X-RateLimit-Reset', str(limit.reset)) return resp
def _prepare_response(resource, dct, last_modified=None, etag=None, status=200): """ Prepares the response object according to the client request and available renderers, making sure that all accessory directives (caching, etag, last-modified) are present. :param resource: the resource involved. :param dct: the dict that should be sent back as a response. :param last_modified: Last-Modified header value. :param etag: ETag header value. :param status: response status. .. versionchanged:: 0.0.7 Support for Rate-Limiting. .. versionchanged:: 0.0.6 Support for HEAD requests. .. versionchanged:: 0.0.5 Support for Cross-Origin Resource Sharing (CORS). .. versionadded:: 0.0.4 """ if request.method == 'OPTIONS': resp = app.make_default_options_response() else: # obtain the best match between client's request and available mime # types, along with the corresponding render function. mime, renderer = _best_mime() # invoke the render function and obtain the corresponding rendered item rendered = globals()[renderer](**dct) # build the main wsgi rensponse object resp = make_response(rendered, status) resp.mimetype = mime # cache directives if request.method in ('GET', 'HEAD'): if resource: cache_control = config.DOMAIN[resource]['cache_control'] expires = config.DOMAIN[resource]['cache_expires'] else: cache_control = config.CACHE_CONTROL expires = config.CACHE_EXPIRES if cache_control: resp.headers.add('Cache-Control', cache_control) if expires: resp.expires = time.time() + expires # etag and last-modified if etag: resp.headers.add('ETag', etag) if last_modified: resp.headers.add('Last-Modified', date_to_str(last_modified)) # CORS if 'Origin' in request.headers and config.X_DOMAINS is not None: if isinstance(config.X_DOMAINS, basestring): domains = [config.X_DOMAINS] else: domains = config.X_DOMAINS if config.X_HEADERS is None: headers = [] elif isinstance(config.X_HEADERS, basestring): headers = [config.X_HEADERS] else: headers = config.X_HEADERS methods = app.make_default_options_response().headers['allow'] resp.headers.add('Access-Control-Allow-Origin', ', '.join(domains)) resp.headers.add('Access-Control-Allow-Headers', ', '.join(headers)) resp.headers.add('Access-Control-Allow-Methods', methods) resp.headers.add('Access-Control-Allow-Max-Age', 21600) # Rate-Limiting limit = get_rate_limit() if limit and limit.send_x_headers: resp.headers.add('X-RateLimit-Remaining', str(limit.remaining)) resp.headers.add('X-RateLimit-Limit', str(limit.limit)) resp.headers.add('X-RateLimit-Reset', str(limit.reset)) return resp
def _prepare_response(resource, dct, last_modified=None, etag=None, status=200, headers=None): """ Prepares the response object according to the client request and available renderers, making sure that all accessory directives (caching, etag, last-modified) are present. :param resource: the resource involved. :param dct: the dict that should be sent back as a response. :param last_modified: Last-Modified header value. :param etag: ETag header value. :param status: response status. .. versionchanged:: 0.7 Add support for regexes in X_DOMAINS_RE. Closes #660, #974. ETag value now surrounded by double quotes. Closes #794. .. versionchanged:: 0.6 JSONP Support. .. versionchanged:: 0.4 Support for optional extra headers. Fix #381. 500 instead of 404 if CORS is enabled. .. versionchanged:: 0.3 Support for X_MAX_AGE. .. versionchanged:: 0.1.0 Support for optional HATEOAS. .. versionchanged:: 0.0.9 Support for Python 3.3. .. versionchanged:: 0.0.7 Support for Rate-Limiting. .. versionchanged:: 0.0.6 Support for HEAD requests. .. versionchanged:: 0.0.5 Support for Cross-Origin Resource Sharing (CORS). .. versionadded:: 0.0.4 """ if request.method == "OPTIONS": resp = app.make_default_options_response() else: # obtain the best match between client's request and available mime # types, along with the corresponding render function. mime, renderer_cls = _best_mime() # invoke the render function and obtain the corresponding rendered item rendered = renderer_cls().render(dct) # JSONP if config.JSONP_ARGUMENT: jsonp_arg = config.JSONP_ARGUMENT if jsonp_arg in request.args and "json" in mime: callback = request.args.get(jsonp_arg) rendered = "%s(%s)" % (callback, rendered) # build the main wsgi response object resp = make_response(rendered, status) resp.mimetype = mime # extra headers if headers: for header, value in headers: if header != "Content-Type": resp.headers.add(header, value) # cache directives if request.method in ("GET", "HEAD"): if resource: cache_control = config.DOMAIN[resource]["cache_control"] expires = config.DOMAIN[resource]["cache_expires"] else: cache_control = config.CACHE_CONTROL expires = config.CACHE_EXPIRES if cache_control: resp.headers.add("Cache-Control", cache_control) if expires: resp.expires = time.time() + expires # etag and last-modified if etag: resp.headers.add("ETag", '"' + etag + '"') if last_modified: resp.headers.add("Last-Modified", date_to_rfc1123(last_modified)) # CORS origin = request.headers.get("Origin") if origin and (config.X_DOMAINS or config.X_DOMAINS_RE): if config.X_DOMAINS is None: domains = [] elif isinstance(config.X_DOMAINS, str): domains = [config.X_DOMAINS] else: domains = config.X_DOMAINS if config.X_DOMAINS_RE is None: domains_re = [] elif isinstance(config.X_DOMAINS_RE, str): domains_re = [config.X_DOMAINS_RE] else: domains_re = config.X_DOMAINS_RE # precompile regexes and ignore invalids domains_re_compiled = [] for domain_re in domains_re: try: domains_re_compiled.append(re.compile(domain_re)) except re.error: continue if config.X_HEADERS is None: headers = [] elif isinstance(config.X_HEADERS, str): headers = [config.X_HEADERS] else: headers = config.X_HEADERS if config.X_EXPOSE_HEADERS is None: expose_headers = [] elif isinstance(config.X_EXPOSE_HEADERS, str): expose_headers = [config.X_EXPOSE_HEADERS] else: expose_headers = config.X_EXPOSE_HEADERS # The only accepted value for Access-Control-Allow-Credentials header # is "true" allow_credentials = config.X_ALLOW_CREDENTIALS is True methods = app.make_default_options_response().headers.get("allow", "") if "*" in domains: resp.headers.add("Access-Control-Allow-Origin", origin) resp.headers.add("Vary", "Origin") elif any(origin == domain for domain in domains): resp.headers.add("Access-Control-Allow-Origin", origin) elif any(domain.match(origin) for domain in domains_re_compiled): resp.headers.add("Access-Control-Allow-Origin", origin) else: resp.headers.add("Access-Control-Allow-Origin", "") resp.headers.add("Access-Control-Allow-Headers", ", ".join(headers)) resp.headers.add("Access-Control-Expose-Headers", ", ".join(expose_headers)) resp.headers.add("Access-Control-Allow-Methods", methods) resp.headers.add("Access-Control-Max-Age", config.X_MAX_AGE) if allow_credentials: resp.headers.add("Access-Control-Allow-Credentials", "true") # Rate-Limiting limit = get_rate_limit() if limit and limit.send_x_headers: resp.headers.add("X-RateLimit-Remaining", str(limit.remaining)) resp.headers.add("X-RateLimit-Limit", str(limit.limit)) resp.headers.add("X-RateLimit-Reset", str(limit.reset)) return resp
def _prepare_response(resource, dct, last_modified=None, etag=None, status=200, headers=None): """ Prepares the response object according to the client request and available renderers, making sure that all accessory directives (caching, etag, last-modified) are present. :param resource: the resource involved. :param dct: the dict that should be sent back as a response. :param last_modified: Last-Modified header value. :param etag: ETag header value. :param status: response status. .. versionchanged:: 0.6 JSONP Support. .. versionchanged:: 0.4 Support for optional extra headers. Fix #381. 500 instead of 404 if CORS is enabled. .. versionchanged:: 0.3 Support for X_MAX_AGE. .. versionchanged:: 0.1.0 Support for optional HATEOAS. .. versionchanged:: 0.0.9 Support for Python 3.3. .. versionchanged:: 0.0.7 Support for Rate-Limiting. .. versionchanged:: 0.0.6 Support for HEAD requests. .. versionchanged:: 0.0.5 Support for Cross-Origin Resource Sharing (CORS). .. versionadded:: 0.0.4 """ if request.method == "OPTIONS": resp = app.make_default_options_response() else: # obtain the best match between client's request and available mime # types, along with the corresponding render function. mime, renderer = _best_mime() # invoke the render function and obtain the corresponding rendered item rendered = globals()[renderer](dct) # JSONP if config.JSONP_ARGUMENT: jsonp_arg = config.JSONP_ARGUMENT if jsonp_arg in request.args and "json" in mime: callback = request.args.get(jsonp_arg) rendered = "%s(%s)" % (callback, rendered) # build the main wsgi rensponse object resp = make_response(rendered, status) resp.mimetype = mime # extra headers if headers: for header, value in headers: if header != "Content-Type": resp.headers.add(header, value) # cache directives if request.method in ("GET", "HEAD"): if resource: cache_control = config.DOMAIN[resource]["cache_control"] expires = config.DOMAIN[resource]["cache_expires"] else: cache_control = config.CACHE_CONTROL expires = config.CACHE_EXPIRES if cache_control: resp.headers.add("Cache-Control", cache_control) if expires: resp.expires = time.time() + expires # etag and last-modified if etag: resp.headers.add("ETag", etag) if last_modified: resp.headers.add("Last-Modified", date_to_rfc1123(last_modified)) # CORS origin = request.headers.get("Origin") if origin and config.X_DOMAINS: if isinstance(config.X_DOMAINS, str): domains = [config.X_DOMAINS] else: domains = config.X_DOMAINS if config.X_HEADERS is None: headers = [] elif isinstance(config.X_HEADERS, str): headers = [config.X_HEADERS] else: headers = config.X_HEADERS if config.X_EXPOSE_HEADERS is None: expose_headers = [] elif isinstance(config.X_EXPOSE_HEADERS, str): expose_headers = [config.X_EXPOSE_HEADERS] else: expose_headers = config.X_EXPOSE_HEADERS # The only accepted value for Access-Control-Allow-Credentials header # is "true" allow_credentials = config.X_ALLOW_CREDENTIALS is True methods = app.make_default_options_response().headers.get("allow", "") if "*" in domains: resp.headers.add("Access-Control-Allow-Origin", origin) resp.headers.add("Vary", "Origin") elif origin in domains: resp.headers.add("Access-Control-Allow-Origin", origin) else: resp.headers.add("Access-Control-Allow-Origin", "") resp.headers.add("Access-Control-Allow-Headers", ", ".join(headers)) resp.headers.add("Access-Control-Expose-Headers", ", ".join(expose_headers)) resp.headers.add("Access-Control-Allow-Methods", methods) resp.headers.add("Access-Control-Allow-Max-Age", config.X_MAX_AGE) if allow_credentials: resp.headers.add("Access-Control-Allow-Credentials", "true") # Rate-Limiting limit = get_rate_limit() if limit and limit.send_x_headers: resp.headers.add("X-RateLimit-Remaining", str(limit.remaining)) resp.headers.add("X-RateLimit-Limit", str(limit.limit)) resp.headers.add("X-RateLimit-Reset", str(limit.reset)) return resp