def _raise_304_if_not_modified(self, record=None): """Raise 304 if current timestamp is inferior to the one specified in headers. :raises: :exc:`~pyramid:pyramid.httpexceptions.HTTPNotModified` """ if_none_match = self.request.headers.get('If-None-Match') if not if_none_match: return if_none_match = decode_header(if_none_match) try: if not (if_none_match[0] == if_none_match[-1] == '"'): raise ValueError() modified_since = int(if_none_match[1:-1]) except (IndexError, ValueError): if if_none_match == '*': return error_details = { 'location': 'headers', 'description': "Invalid value for If-None-Match" } raise_invalid(self.request, **error_details) if record: current_timestamp = record[self.model.modified_field] else: current_timestamp = self.model.timestamp() if current_timestamp <= modified_since: response = HTTPNotModified() self._add_timestamp_header(response, timestamp=current_timestamp) raise response
def check_precondition_headers(viewfunc, request): """View decorator to check X-If-[Unm|M]odified-Since headers. This decorator checks pre-validated vlaues from the X-If-Modified-Since and X-If-Unmodified-Since headers against the actual last-modified time of the target resource. If the preconditions are not met then it raises the appropriate error response. In addition, any retreived value for the last-modified time will be stored in the response headers for return to the client. This may save having to look it up again when the response is being rendered. """ if "if_modified_since" in request.validated: ts = get_resource_timestamp(request) request.response.headers["X-Last-Modified"] = str(ts) if ts <= request.validated["if_modified_since"]: raise HTTPNotModified(headers={ "X-Last-Modified": str(ts), }) if "if_unmodified_since" in request.validated: ts = get_resource_timestamp(request) request.response.headers["X-Last-Modified"] = str(ts) if ts > request.validated["if_unmodified_since"]: raise HTTPPreconditionFailed(headers={ "X-Last-Modified": str(ts), }) return viewfunc(request)
def _check_condition_none_match(self): self.set_etag() etag = self.request.response.etag if etag is None: return if etag == self.request.if_none_match.etags[0]: # pragma: no branch raise HTTPNotModified()
def etag_cache(request, etag_key): """Use the HTTP Entity Tag cache for Browser side caching If a "If-None-Match" header is found, and equivalent to ``key``, then a ``304`` HTTP message will be returned with the ETag to tell the browser that it should use its current cache of the page. Otherwise, the ETag header will be added to the response headers. Suggested use is within a view like so: .. code-block:: python def view(request): etag_cache(request, key=1) return render('/splash.mako') .. note:: This works because etag_cache will raise an HTTPNotModified exception if the ETag received matches the key provided. Implementation adapted from: https://github.com/Pylons/pylons/blob/799c310/pylons/controllers/util.py#L148 # noqa """ # we are always using a weak ETag validator etag = 'W/"%s"' % etag_key etag_matcher = request.if_none_match if str(etag_key) in etag_matcher: headers = [('ETag', etag)] log.debug("ETag match, returning 304 HTTP Not Modified Response") raise HTTPNotModified(headers=headers) else: request.response.headers['ETag'] = etag log.debug("ETag didn't match, returning response object")
def check_etag(request, etag): etag = str(etag) if etag in request.if_none_match: raise HTTPNotModified() request.response.vary = 'Cookie' request.response.cache_control = 'max-age=3600, private, must-revalidate' request.response.etag = etag
def wrapped(context, request): etag = request.registry.settings['snovault.app_version'] if etag in request.if_none_match: raise HTTPNotModified() result = view_callable(context, request) request.response.etag = etag return result
def view_asset(request): """Handles the ``/parts/{pid}/files/name/assets/{filename}`` URL, sending back the correct :class:`~wte.models.Asset`. Requires that the user has "view" rights on the :class:`~wte.models.Part`. """ dbsession = DBSession() part = dbsession.query(Part).filter( Part.id == request.matchdict['pid']).first() if part.type == 'page': part = part.parent asset = dbsession.query(Asset).join(Part.assets).\ filter(and_(Asset.filename == request.matchdict['filename'], Part.id == part.id)).first() if part and asset: if part.allow('view', request.current_user): if 'If-None-Match' in request.headers and request.headers[ 'If-None-Match'] == asset.etag: raise HTTPNotModified() headerlist = [('Content-Type', str(asset.mimetype))] if asset.etag is not None: headerlist.append(('ETag', str(asset.etag))) if 'download' in request.params: if request.params['download'].lower() == 'true': headerlist.append( ('Content-Disposition', str('attachment; filename="%s"' % (asset.filename)))) return Response(body=asset.data, headerlist=headerlist) else: unauthorised_redirect(request) else: raise HTTPNotFound()
def view_file(request): """Handles the ``parts/{ptid}/pages/{pid}/users/{uid}/files/name/{filename}`` URL, sending back the correct :class:`~wte.models.Asset`. Requires that the user has "view" rights on the :class:`~wte.models.Part`. It will also only send an :class:`~wte.models.Asset` belonging to the current :class:`~wte.models.User`. """ dbsession = DBSession() part = dbsession.query(Part).filter( Part.id == request.matchdict['pid']).first() if part: if part.allow('view', request.current_user): progress = get_user_part_progress(dbsession, request.current_user, part) for user_file in progress.files: if user_file.filename == request.matchdict['filename']: if 'If-None-Match' in request.headers and request.headers[ 'If-None-Match'] == user_file.etag: raise HTTPNotModified() headers = [('Content-Type', str(user_file.mimetype))] if user_file.etag is not None: headers.append(('ETag', str(user_file.etag))) if 'download' in request.params: headers.append(('Content-Disposition', str('attachment; filename="%s"' % (user_file.filename)))) return Response(body=user_file.data, headerlist=headers) raise HTTPNotFound() else: unauthorised_redirect(request) else: raise HTTPNotFound()
def test_process_conditional_put_requests(web_app, pyramid_request, etag, if_match, if_none_match, status_code): root = pyramid_request.root root['resource'] = resource = DummyResource({'foo': 'Hello', 'bar': 123}) resource.etag = etag resource_url = pyramid_request.resource_url(root['resource']) headers = {} if if_match: headers['If-Match'] = if_match if if_none_match: headers['If-None-Match'] = if_none_match kwargs = {'headers': headers} if status_code == 304: kwargs['exception'] = HTTPNotModified() elif status_code == 412: kwargs['exception'] = HTTPPreconditionFailed( {'etag': None if etag is None else etag.serialize()}) else: kwargs['status'] = status_code new_params = {'foo': 'World', 'bar': 456} web_app.put_json(resource_url, params=new_params, **kwargs)
def _check_condition_modified_since(self): self.set_last_modified() last_modified = self.request.response.last_modified if last_modified is None: # pragma: no coverage return modified_since = self.request.if_modified_since if last_modified <= modified_since: # pragma: no branch raise HTTPNotModified()
def wrapped(context, request): app_version = request.registry.settings['snovault.app_version'] etag = app_version + ' ' + ' '.join(sorted(request.effective_principals)) if etag in request.if_none_match: raise HTTPNotModified() result = view_callable(context, request) request.response.etag = etag cache_control = request.response.cache_control cache_control.private = True cache_control.max_age = 0 cache_control.must_revalidate = True return result
def notmodified_tween(request): if os.path.isfile(lock_file): response = Response( render('templates/maintenance.jinja2', {}, request)) response.status_code = 503 return response if (request.if_modified_since is not None and request.if_modified_since >= publication_date.replace(microsecond=0)): return HTTPNotModified() response = handler(request) response.last_modified = publication_date return response
def wrapped(context, request): if len(manager.stack) != 1: return view_callable(context, request) format = request.environ.get('encoded.format', 'html') session = DBSession() last_tid = session.query(func.max(TransactionRecord.order)).scalar() processid = request.registry['encoded.processid'] userid = authenticated_userid(request) or '' etag = u'%s;%s;%s;%s' % (last_tid, processid, format, userid) etag = quote(etag.encode('utf-8'), ';:@') if etag in request.if_none_match: raise HTTPNotModified() result = view_callable(context, request) request.response.etag = etag cache_control = request.response.cache_control cache_control.private = True cache_control.max_age = 0 cache_control.must_revalidate = True return result
def notmodified_tween(request): if request.path == "/healthcheck": return handler(request) publication_date = gmt.localize( Publication.last(request.dbsession).date) if False: response = Response( render("templates/maintenance.jinja2", {}, request)) response.status_code = 503 return response if (request.if_modified_since is not None and request.if_modified_since >= publication_date.replace(microsecond=0)): return HTTPNotModified() request.publication_date = publication_date response = handler(request) response.last_modified = publication_date return response
def _raise_304_if_not_modified(self, record=None): """Raise 304 if current timestamp is inferior to the one specified in headers. :raises: :exc:`~pyramid:pyramid.httpexceptions.HTTPNotModified` """ if_none_match = self.request.validated['header'].get('If-None-Match') if not if_none_match: return if if_none_match == '*': return if record: current_timestamp = record[self.model.modified_field] else: current_timestamp = self.model.timestamp() if current_timestamp == if_none_match: response = HTTPNotModified() self._add_timestamp_header(response, timestamp=current_timestamp) raise response
def mapped_view(context, request: PyramidRequest): if context is not request.root: if_match = request.if_match if_none_match = request.if_none_match if if_match is not AnyETag or if_none_match is not NoETag: etag = context.get_etag() if etag is None: if None not in if_match: raise HTTPPreconditionFailed({'etag': None}) else: # https://tools.ietf.org/html/rfc7232#section-6 # https://tools.ietf.org/html/rfc7232#section-2.3.2 if if_match is not AnyETag: if not etag.is_strict or etag.value not in if_match: raise HTTPPreconditionFailed( {'etag': etag.serialize()}) if etag.value in if_none_match: if request.method in ('GET', 'HEAD'): raise HTTPNotModified() raise HTTPPreconditionFailed( {'etag': etag.serialize()}) return view(context, request)
def get_blocklist(request): prefix = request.matchdict['prefix'] api_ver = int(request.matchdict['api_ver']) app = request.matchdict['application_guid'] app_ver = request.matchdict['application_ver'] # 1. Verify that we have a config for that prefix if prefix not in request.registry.amo_resources: raise HTTPNotFound() # Addons blocklist addons_records, addons_last_modified = get_records(request, prefix, 'addons') # Plugins blocklist plugins_records, plugins_last_modified = get_records( request, prefix, 'plugins') # GFX blocklist gfx_records, gfx_last_modified = get_records(request, prefix, 'gfx') # Certificates blocklist cert_records, cert_last_modified = get_records(request, prefix, 'certificates') # Expose highest timestamp in response headers. last_update = max(addons_last_modified, plugins_last_modified, gfx_last_modified, cert_last_modified) last_etag = '"{}"'.format(last_update) request.response.headers['ETag'] = last_etag request.response.last_modified = last_update / 1000.0 if_none_match = request.headers.get('If-None-Match') if_modified_since = request.headers.get('If-Modified-Since') if if_none_match is not None or if_modified_since is not None: has_changed = ( if_none_match != last_etag and request.if_modified_since != request.response.last_modified) if not has_changed: response = HTTPNotModified() response.headers['ETag'] = last_etag response.last_modified = last_update / 1000.0 raise response xml_tree = etree.Element( 'blocklist', xmlns="http://www.mozilla.org/2006/addons-blocklist", lastupdate='%s' % last_update) write_addons_items(xml_tree, addons_records, api_ver=api_ver, app_id=app, app_ver=app_ver) write_plugin_items(xml_tree, plugins_records, api_ver=api_ver, app_id=app, app_ver=app_ver) write_gfx_items(xml_tree, gfx_records, api_ver=api_ver, app_id=app) write_cert_items(xml_tree, cert_records, api_ver=api_ver, app_id=app, app_ver=app_ver) doc = etree.ElementTree(xml_tree) request.response.content_type = "application/xml;charset=UTF-8" request.response.write( etree.tostring(doc, pretty_print=True, xml_declaration=True, encoding='UTF-8').decode('utf-8')) return request.response