| OLD | NEW |
| 1 # Copyright 2013 The Chromium Authors. All rights reserved. | 1 # Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 import hashlib |
| 5 import logging | 6 import logging |
| 6 import posixpath | 7 import posixpath |
| 7 import traceback | 8 import traceback |
| 8 | 9 |
| 9 from branch_utility import BranchUtility | 10 from branch_utility import BranchUtility |
| 10 from environment import IsPreviewServer | 11 from environment import IsPreviewServer |
| 11 from file_system import FileNotFoundError | 12 from file_system import FileNotFoundError |
| 12 from redirector import Redirector | 13 from redirector import Redirector |
| 13 from servlet import Servlet, Response | 14 from servlet import Servlet, Response |
| 14 from special_paths import SITE_VERIFICATION_FILE | 15 from special_paths import SITE_VERIFICATION_FILE |
| 15 from third_party.handlebar import Handlebar | 16 from third_party.handlebar import Handlebar |
| 16 | 17 |
| 17 | 18 |
| 18 def _MakeHeaders(content_type): | 19 def _MakeHeaders(content_type, etag=None): |
| 19 return { | 20 headers = { |
| 21 # See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.1. |
| 22 'Cache-Control': 'public, max-age=0, no-cache', |
| 23 'Content-Type': content_type, |
| 20 'X-Frame-Options': 'sameorigin', | 24 'X-Frame-Options': 'sameorigin', |
| 21 'Content-Type': content_type, | |
| 22 'Cache-Control': 'max-age=300', | |
| 23 } | 25 } |
| 26 if etag is not None: |
| 27 headers['ETag'] = etag |
| 28 return headers |
| 24 | 29 |
| 25 | 30 |
| 26 class RenderServlet(Servlet): | 31 class RenderServlet(Servlet): |
| 27 '''Servlet which renders templates. | 32 '''Servlet which renders templates. |
| 28 ''' | 33 ''' |
| 29 | 34 |
| 30 class Delegate(object): | 35 class Delegate(object): |
| 31 def CreateServerInstance(self): | 36 def CreateServerInstance(self): |
| 32 raise NotImplementedError(self.__class__) | 37 raise NotImplementedError(self.__class__) |
| 33 | 38 |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 115 server_instance.template_renderer.Render(content, self._request)) | 120 server_instance.template_renderer.Render(content, self._request)) |
| 116 # HACK: the site verification file (google2ed...) doesn't have a title. | 121 # HACK: the site verification file (google2ed...) doesn't have a title. |
| 117 content, doc_warnings = server_instance.document_renderer.Render( | 122 content, doc_warnings = server_instance.document_renderer.Render( |
| 118 template_content, | 123 template_content, |
| 119 path, | 124 path, |
| 120 render_title=path != SITE_VERIFICATION_FILE) | 125 render_title=path != SITE_VERIFICATION_FILE) |
| 121 warnings = template_warnings + doc_warnings | 126 warnings = template_warnings + doc_warnings |
| 122 if warnings: | 127 if warnings: |
| 123 sep = '\n - ' | 128 sep = '\n - ' |
| 124 logging.warning('Rendering %s:%s%s' % (path, sep, sep.join(warnings))) | 129 logging.warning('Rendering %s:%s%s' % (path, sep, sep.join(warnings))) |
| 130 # Content was dynamic. The new etag is a hash of the content. |
| 131 etag = None |
| 132 elif content_and_type.version is not None: |
| 133 # Content was static. The new etag is the version of the content. |
| 134 etag = '"%s"' % content_and_type.version |
| 135 else: |
| 136 # Sometimes non-dynamic content does not have a version, for example |
| 137 # .zip files. The new etag is a hash of the content. |
| 138 etag = None |
| 125 | 139 |
| 126 content_type = content_and_type.content_type | 140 content_type = content_and_type.content_type |
| 127 if isinstance(content, unicode): | 141 if isinstance(content, unicode): |
| 128 content = content.encode('utf-8') | 142 content = content.encode('utf-8') |
| 129 content_type += '; charset=utf-8' | 143 content_type += '; charset=utf-8' |
| 130 | 144 |
| 131 return Response.Ok(content, headers=_MakeHeaders(content_type)) | 145 if etag is None: |
| 146 # Note: we're using md5 as a convenient and fast-enough way to identify |
| 147 # content. It's not intended to be cryptographic in any way, and this |
| 148 # is *not* what etags is for. That's what SSL is for, this is unrelated. |
| 149 etag = '"%s"' % hashlib.md5(content).hexdigest() |
| 150 |
| 151 headers = _MakeHeaders(content_type, etag=etag) |
| 152 if etag == self._request.headers.get('If-None-Match'): |
| 153 return Response.NotModified('Not Modified', headers=headers) |
| 154 return Response.Ok(content, headers=headers) |
| OLD | NEW |