def finalize_response(self, request, response, *args, **kwargs): """ Returns the final response object. """ # Make the error obvious if a proper response is not returned assert isinstance(response, HttpResponseBase), ( 'Expected a `Response`, `HttpResponse` or `HttpStreamingResponse` ' 'to be returned from the view, but received a `%s`' % type(response) ) if isinstance(response, Response): if not getattr(request, 'accepted_renderer', None): neg = self.perform_content_negotiation(request, force=True) request.accepted_renderer, request.accepted_media_type = neg response.accepted_renderer = request.accepted_renderer response.accepted_media_type = request.accepted_media_type response.renderer_context = self.get_renderer_context() # Add new vary headers to the response instead of overwriting. vary_headers = self.headers.pop('Vary', None) if vary_headers is not None: patch_vary_headers(response, cc_delim_re.split(vary_headers)) for key, value in self.headers.items(): response[key] = value return response
def check_merged_vary_header(self, request, main_header, fragment_header_1, fragment_header_2): fragment_urls = [ '/vary/?headers=%s' % urllib.quote_plus(vary) for vary in (fragment_header_1, fragment_header_2) if vary ] html = '\n'.join('<esi:include src="%s" />' % url for url in fragment_urls) response = HttpResponse(html) if main_header: response['Vary'] = main_header request_url = '/page-with-esi-tags/' request.has_attr(_esi={'used': True}) request.provides('get_full_path').returns(request_url) request.provides('build_absolute_uri').returns('http://example.com%s' % request_url) result = full_process_response(request, response) vary_result = result.get('Vary', '') for header in (main_header, fragment_header_1, fragment_header_2): if not header: continue vary_fields = cc_delim_re.split(header) for field in vary_fields: self.assertEqual(len(re.findall(field, vary_result, re.I)), 1)
def learn_cache_key(request, response, tags=(), cache_timeout=None, key_prefix=None, cache=None): # patched """ Learns what headers to take into account for some request path from the response object. It stores those headers in a global path registry so that later access to that path will know what headers to take into account without building the response object itself. The headers are named in the Vary header of the response, but we want to prevent response generation. The list of headers to use for cache key generation is stored in the same cache as the pages themselves. If the cache ages some data out of the cache, this just means that we have to build the response once to get at the Vary header and so at the list of headers to use for the cache key. """ if key_prefix is None: key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX if cache_timeout is None: cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS cache_key = _generate_cache_header_key(key_prefix, request) if cache is None: cache = get_cache(settings.CACHE_MIDDLEWARE_ALIAS) if response.has_header('Vary'): headerlist = ['HTTP_'+header.upper().replace('-', '_') for header in cc_delim_re.split(response['Vary'])] cache.set(cache_key, headerlist, tags, cache_timeout) # patched return _generate_cache_key(request, request.method, headerlist, key_prefix) else: # if there is no Vary header, we still need a cache key # for the request.get_full_path() cache.set(cache_key, [], tags, cache_timeout) # patched return _generate_cache_key(request, request.method, [], key_prefix)
def learn_cache_key(request, response, cache_timeout=None, key_prefix=None): """ Learns what headers to take into account for some request path from the response object. It stores those headers in a global path registry so that later access to that path will know what headers to take into account without building the response object itself. The headers are named in the Vary header of the response, but we want to prevent response generation. The list of headers to use for cache key generation is stored in the same cache as the pages themselves. If the cache ages some data out of the cache, this just means that we have to build the response once to get at the Vary header and so at the list of headers to use for the cache key. """ if key_prefix is None: key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX if cache_timeout is None: cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS cache_key = _generate_cache_header_key(key_prefix, request) if response.has_header('Vary'): headerlist = [ 'HTTP_' + header.upper().replace('-', '_') for header in cc_delim_re.split(response['Vary']) ] cache.set(cache_key, headerlist, cache_timeout) return _generate_cache_key(request, headerlist, key_prefix) else: # if there is no Vary header, we still need a cache key # for the request.path cache.set(cache_key, [], cache_timeout) return _generate_cache_key(request, [], key_prefix)
def check_merged_vary_header(self, request, main_header, fragment_header_1, fragment_header_2): fragment_urls = ['/vary/?headers=%s' % urllib.quote_plus(vary) for vary in (fragment_header_1, fragment_header_2) if vary] html = '\n'.join('<esi:include src="%s" />' % url for url in fragment_urls) response = HttpResponse(html) if main_header: response['Vary'] = main_header request_url = '/page-with-esi-tags/' request.has_attr(_esi={'used': True}) request.provides('get_full_path').returns(request_url) request.provides('build_absolute_uri').returns( 'http://example.com%s' % request_url) result = full_process_response(request, response) vary_result = result.get('Vary', '') for header in (main_header, fragment_header_1, fragment_header_2): if not header: continue vary_fields = cc_delim_re.split(header) for field in vary_fields: self.assertEqual(len(re.findall(field, vary_result, re.I)), 1)
def needs_etag(self, response): """ Return True if an ETag header should be added to response. """ cache_control_headers = cc_delim_re.split( response.get('Cache-Control', '')) return all(header.lower() != 'no-store' for header in cache_control_headers)
def has_vary_header(response, header_query): """ Checks to see if the response has a given header name in its Vary header. Copied from Django 1.2.5 so we can use it in Django 1.2.4 """ if not response.has_header('Vary'): return False vary_headers = cc_delim_re.split(response['Vary']) existing_headers = set([header.lower() for header in vary_headers]) return header_query.lower() in existing_headers
def remove_vary_headers(response, deleteheaders): """ Removes the "Vary" header in the given HttpResponse object. newheaders is a list of header names that should be in "Vary". """ # Note that we need to keep the original order intact, because cache # implementations may rely on the order of the Vary contents in, say, # computing an MD5 hash. if response.has_header('Vary'): vary_headers = cc_delim_re.split(response['Vary']) # Use .lower() here so we treat headers as case-insensitive. deleteheaders = set(deleteheaders) headers = [header for header in vary_headers if header.lower() not in deleteheaders] if headers: response['Vary'] = ', '.join(headers) else: del response['Vary']
def learn_cache_key(request, response, tags=(), cache_timeout=None, key_prefix=None, cache=None): # patched """ Learns what headers to take into account for some request URL from the response object. It stores those headers in a global URL registry so that later access to that URL will know what headers to take into account without building the response object itself. The headers are named in the Vary header of the response, but we want to prevent response generation. The list of headers to use for cache key generation is stored in the same cache as the pages themselves. If the cache ages some data out of the cache, this just means that we have to build the response once to get at the Vary header and so at the list of headers to use for the cache key. """ if key_prefix is None: key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX if cache_timeout is None: cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS cache_key = _generate_cache_header_key(key_prefix, request) if cache is None: cache = caches[settings.CACHE_MIDDLEWARE_ALIAS] if response.has_header('Vary'): is_accept_language_redundant = settings.USE_I18N or settings.USE_L10N # If i18n or l10n are used, the generated cache key will be suffixed # with the current locale. Adding the raw value of Accept-Language is # redundant in that case and would result in storing the same content # under multiple keys in the cache. See #18191 for details. headerlist = [] for header in cc_delim_re.split(response['Vary']): header = header.upper().replace('-', '_') if header == 'ACCEPT_LANGUAGE' and is_accept_language_redundant: continue headerlist.append('HTTP_' + header) headerlist.sort() cache.set(cache_key, headerlist, tags, cache_timeout) # patched return _generate_cache_key(request, request.method, headerlist, key_prefix) else: # if there is no Vary header, we still need a cache key # for the request.build_absolute_uri() cache.set(cache_key, [], tags, cache_timeout) # patched return _generate_cache_key(request, request.method, [], key_prefix)
def reduce_vary_headers(response, additional): '''Merges the Vary header values so all headers are included.''' original = response.get('Vary', None) if original is not None: additional.append(original) # Keep track of normalized, lowercase header names in seen_headers while # maintaining the order and case of the header names in final_headers. seen_headers = set() final_headers = [] for vary_value in additional: headers = cc_delim_re.split(vary_value) for header in headers: if header.lower() in seen_headers: continue seen_headers.add(header.lower()) final_headers.append(header) response['Vary'] = ', '.join(final_headers)
def get_header_dict(response, header): """ returns a dictionary of the cache control headers the same as is used by django.utils.cache.patch_cache_control if there are no Cache-Control headers returns and empty dict """ def dictitem(s): t = s.split('=', 1) if len(t) > 1: return (t[0].lower(), t[1]) else: return (t[0].lower(), True) if response.has_header(header): hd = dict([dictitem(el) for el in cc_delim_re.split(response[header])]) else: hd= {} return hd
def get_header_dict(response, header): """ returns a dictionary of the cache control headers the same as is used by django.utils.cache.patch_cache_control if there are no Cache-Control headers returns and empty dict """ def dictitem(s): t = s.split('=', 1) if len(t) > 1: return (t[0].lower(), t[1]) else: return (t[0].lower(), True) if response.has_header(header): hd = dict([dictitem(el) for el in cc_delim_re.split(response[header])]) else: hd = {} return hd
def remove_vary_headers(response, deleteheaders): """ Removes the "Vary" header in the given HttpResponse object. newheaders is a list of header names that should be in "Vary". """ # Note that we need to keep the original order intact, because cache # implementations may rely on the order of the Vary contents in, say, # computing an MD5 hash. if response.has_header('Vary'): vary_headers = cc_delim_re.split(response['Vary']) # Use .lower() here so we treat headers as case-insensitive. deleteheaders = set(deleteheaders) headers = [ header for header in vary_headers if header.lower() not in deleteheaders ] if headers: response['Vary'] = ', '.join(headers) else: del response['Vary']
def learn_orderless_cache_key(request, response, cache_timeout=None, key_prefix=None, cache=None): """ Learns what headers to take into account for some request URL from the response object. It stores those headers in a global URL registry so that later access to that URL will know what headers to take into account without building the response object itself. The headers are named in the Vary header of the response, but we want to prevent response generation. The list of headers to use for cache key generation is stored in the same cache as the pages themselves. If the cache ages some data out of the cache, this just means that we have to build the response once to get at the Vary header and so at the list of headers to use for the cache key. """ if key_prefix is None: key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX if cache_timeout is None: cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS cache_key = _generate_cache_header_key(key_prefix, request) if cache is None: cache = caches[settings.CACHE_MIDDLEWARE_ALIAS] if response.has_header('Vary'): is_accept_language_redundant = settings.USE_I18N or settings.USE_L10N # If i18n or l10n are used, the generated cache key will be suffixed # with the current locale. Adding the raw value of Accept-Language is # redundant in that case and would result in storing the same content # under multiple keys in the cache. See #18191 for details. headerlist = [] for header in cc_delim_re.split(response['Vary']): header = header.upper().replace('-', '_') if header == 'ACCEPT_LANGUAGE' and is_accept_language_redundant: continue headerlist.append('HTTP_' + header) headerlist.sort() cache.set(cache_key, headerlist, cache_timeout) return _generate_orderless_cache_key(request, request.method, headerlist, key_prefix) else: # if there is no Vary header, we still need a cache key # for the request.build_absolute_uri() cache.set(cache_key, [], cache_timeout) return _generate_orderless_cache_key(request, request.method, [], key_prefix)
def drop_vary_headers(response, headers_to_drop): """ Remove an item from the "Vary" header of an ``HttpResponse`` object. If no items remain, delete the "Vary" header. This does the opposite effect of django.utils.cache.patch_vary_headers. """ if response.has_header('Vary'): vary_headers = cc_delim_re.split(response['Vary']) else: vary_headers = [] headers_to_drop = [header.lower() for header in headers_to_drop] updated_vary_headers = [] for header in vary_headers: if len(header): if header.lower() not in headers_to_drop: updated_vary_headers.append(header) if len(updated_vary_headers): response['Vary'] = ', '.join(updated_vary_headers) else: del response['Vary']
def learn_cache_key(request, response, cache_timeout=None, key_prefix=None): """ NOTE: This was copied from Django, with the following changes: * Caches based on full path, including the query string. * Stores the ETag and Last-Modified headers to support responding with "304 Not Modified" responses. * Supports custom "X-Vary-On-View" headers that can be used instead of the cookie header in the cache key for a response. This provides more control over what is cached, without altering the actual response. Learns what headers to take into account for some request path from the response object. It stores those headers in a global path registry so that later access to that path will know what headers to take into account without building the response object itself. The headers are named in the Vary header of the response, but we want to prevent response generation. The list of headers to use for cache key generation is stored in the same cache as the pages themselves. If the cache ages some data out of the cache, this just means that we have to build the response once to get at the Vary header and so at the list of headers to use for the cache key. """ if key_prefix is None: key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX if cache_timeout is None: cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS # Get the Vary headers, to build a cache key for this response. For # example, a response that varies on cookie would result in multiple # cached responses; one for each cookie value that is encountered. This # dictionary is effectively a list, as it does not have any values. response_headers = {} if response.has_header('Vary'): for header in cc_delim_re.split(response['Vary']): response_headers['HTTP_' + header.upper().replace('-', '_')] = None if response.has_header('X-Vary-On-View'): # When available, use the Vary-On-View header instead of the Cookie. response_headers['X-Vary-On-View'] = response['X-Vary-On-View'] if 'HTTP_COOKIE' in response_headers: del response_headers['HTTP_COOKIE'] key_headers = response_headers else: # When there is no vary on view, we can store the Etags/Last-Modified # values in the header list. Nothing varies, so we can be sure that # the values are not for a different version of the page. This avoids # fetching the response for no reason. key_headers = {} if response.has_header('Etag'): key_headers['HTTP_ETAG'] = response['Etag'] if response.has_header('Last-Modified'): key_headers['HTTP_LAST_MODIFIED'] = response['Last-Modified'] key_headers.update(response_headers) # Cache this list of headers against this request URL. # This is the "global path registry" that everyone is talking about. cache_key = _generate_cache_header_key(key_prefix, request) cache.set(cache_key, key_headers, cache_timeout) # Generate a cache key for this response. This will be based on the # request path and the request HTTP header values (the vary ones). return generate_cache_key(request, response_headers, key_prefix)
def vary(request): response = HttpResponse(request.GET['headers']) patch_vary_headers(response, cc_delim_re.split(request.GET['headers'])) return response
def needs_etag(self, response): """Return True if an ETag header should be added to response.""" cache_control_headers = cc_delim_re.split(response.get('Cache-Control', '')) return all(header.lower() != 'no-store' for header in cache_control_headers)
session_key = response.cookies[settings.SESSION_COOKIE_NAME] except KeyError: session_key = None if session_key is None: return response # Remove outgoing session cookie try: del response.cookies[settings.SESSION_COOKIE_NAME] except KeyError: pass # Strip "Vary: Coookie" header try: vary_headers = [x for x in cc_delim_re.split(response['Vary']) if x.lower() != 'cookie'] if vary_headers: response['Vary'] = ', '.join([x for x in vary_headers]) else: del response['Vary'] except KeyError: pass if isinstance(response, HttpResponseRedirect): location = embed_session_id(response['Location'], session_key, domain=request.get_host()) response = HttpResponseRedirect(location) return response response.content = session_uri(response.content,
return response if self.needs_etag(response) and not response.has_header('ETag'): set_response_etag(response) etag = response.get('ETag') last_modified = response.get('Last-Modified') if last_modified: last_modified = parse_http_date_safe(last_modified) if etag or last_modified: return get_conditional_response( request, etag=etag, last_modified=last_modified, response=response, ) return response def needs_etag(self, response): <<<<<<< HEAD """ Return True if an ETag header should be added to response. """ ======= """Return True if an ETag header should be added to response.""" >>>>>>> 37c99181c9a6b95433d60f8c8ef9af5731096435 cache_control_headers = cc_delim_re.split(response.get('Cache-Control', '')) return all(header.lower() != 'no-store' for header in cache_control_headers)