def media_endpoint(_id): """ This endpoint is active when RETURN_MEDIA_AS_URL is True. It retrieves a media file and streams it to the client. .. versionadded:: 0.6 """ file_ = app.media.get(_id) if file_ is None: return abort(404) if_modified_since = weak_date(request.headers.get('If-Modified-Since')) if if_modified_since is not None: if if_modified_since.tzinfo is None: if_modified_since = if_modified_since.replace( tzinfo=tz_util.utc) if if_modified_since > file_.upload_date: return Response(status=304) headers = { 'Last-Modified': date_to_rfc1123(file_.upload_date), 'Content-Length': file_.length, } response = Response(file_, headers=headers, mimetype=file_.content_type, direct_passthrough=True) return response
def media_endpoint(_id): """ This endpoint is active when RETURN_MEDIA_AS_URL is True. It retrieves a media file and streams it to the client. .. versionadded:: 0.6 """ file_ = app.media.get(_id) if file_ is None: return abort(404) headers = { "Last-Modified": date_to_rfc1123(file_.upload_date), "Content-Length": file_.length, "Accept-Ranges": "bytes", } range_header = request.headers.get("Range") if range_header: status = 206 size = file_.length try: m = re.search("(\d+)-(\d*)", range_header) begin, end = m.groups() begin = int(begin) end = int(end) except: begin, end = 0, None length = size - begin if end is not None: length = end - begin + 1 file_.seek(begin) data = file_.read(length) headers["Content-Range"] = "bytes {0}-{1}/{2}".format( begin, begin + length - 1, size) else: if_modified_since = weak_date(request.headers.get("If-Modified-Since")) if if_modified_since: if not if_modified_since.tzinfo: if_modified_since = if_modified_since.replace( tzinfo=tz_util.utc) if if_modified_since > file_.upload_date: return Response(status=304) data = file_ status = 200 response = Response( data, status=status, headers=headers, mimetype=file_.content_type, direct_passthrough=True, ) return response
def media_endpoint(_id): """ This endpoint is active when RETURN_MEDIA_AS_URL is True. It retrieves a media file and streams it to the client. .. versionadded:: 0.6 """ file_ = app.media.get(_id) if file_ is None: return abort(404) if_modified_since = weak_date(request.headers.get('If-Modified-Since')) if if_modified_since is not None: if if_modified_since.tzinfo is None: if_modified_since = if_modified_since.replace(tzinfo=tz_util.utc) if if_modified_since > file_.upload_date: return Response(status=304) headers = { 'Last-Modified': date_to_rfc1123(file_.upload_date), 'Content-Length': file_.length, } response = Response(file_, headers=headers, mimetype=file_.content_type, direct_passthrough=True) return response
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-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, 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 media_endpoint(_id): """ This endpoint is active when RETURN_MEDIA_AS_URL is True. It retrieves a media file and streams it to the client. .. versionadded:: 0.6 """ file_ = app.media.get(_id) if file_ is None: return abort(404) headers = { 'Last-Modified': date_to_rfc1123(file_.upload_date), 'Content-Length': file_.length, 'Accept-Ranges': 'bytes', } range_header = request.headers.get('Range') if range_header: status = 206 size = file_.length try: m = re.search('(\d+)-(\d*)', range_header) begin, end = m.groups() begin = int(begin) end = int(end) except: begin, end = 0, None length = size - begin if end is not None: length = end - begin + 1 file_.seek(begin) data = file_.read(length) headers['Content-Range'] = 'bytes {0}-{1}/{2}'.format( begin, begin + length - 1, size ) else: if_modified_since = weak_date(request.headers.get('If-Modified-Since')) if if_modified_since: if not if_modified_since.tzinfo: if_modified_since = if_modified_since.replace( tzinfo=tz_util.utc) if if_modified_since > file_.upload_date: return Response(status=304) data = file_ status = 200 response = Response( data, status=status, headers=headers, mimetype=file_.content_type, direct_passthrough=True ) return response
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