def error(context, request): """Catch server errors and trace them.""" logger.error(context, exc_info=True) message = '%s %s: %s' % (context.response.status_code, context.response.reason, context.response.text) if context.response.status_code == 400: response = http_error(httpexceptions.HTTPBadRequest(), errno=ERRORS.INVALID_PARAMETERS, message=message) elif context.response.status_code == 401: response = http_error(httpexceptions.HTTPUnauthorized(), errno=ERRORS.INVALID_AUTH_TOKEN, message=message) # Forget the current user credentials. response.headers.extend(forget(request)) elif context.response.status_code == 403: response = http_error(httpexceptions.HTTPForbidden(), errno=ERRORS.FORBIDDEN, message=message) elif context.response.status_code == 404: response = http_error(httpexceptions.HTTPNotFound(), errno=ERRORS.INVALID_RESOURCE_ID, message=message) else: response = service_unavailable(context, request) return reapply_cors(request, response)
def build_sync_client(request): # Get the BID assertion is_authorization_defined = AUTHORIZATION_HEADER in request.headers starts_with_browser_id = False if is_authorization_defined: authorization = request.headers[AUTHORIZATION_HEADER].lower() starts_with_browser_id = authorization.startswith("browserid") if not is_authorization_defined or not starts_with_browser_id: error_msg = "Provide a BID assertion %s header." % ( AUTHORIZATION_HEADER) response = http_error(httpexceptions.HTTPUnauthorized(), errno=ERRORS.MISSING_AUTH_TOKEN, message=error_msg) response.headers.extend(forget(request)) raise response is_client_state_defined = CLIENT_STATE_HEADER in request.headers if not is_client_state_defined: error_msg = "Provide the tokenserver %s header." % ( CLIENT_STATE_HEADER) response = http_error(httpexceptions.HTTPUnauthorized(), errno=ERRORS.MISSING_AUTH_TOKEN, message=error_msg) response.headers.extend(forget(request)) raise response authorization_header = request.headers[AUTHORIZATION_HEADER] bid_assertion = authorization_header.split(" ", 1)[1] client_state = request.headers[CLIENT_STATE_HEADER] sync_client = SyncClient(bid_assertion, client_state) return sync_client
def post_batch(request): requests = request.validated['requests'] batch_size = len(requests) limit = request.registry.settings['batch_max_requests'] if limit and len(requests) > int(limit): error_msg = 'Number of requests is limited to %s' % limit request.errors.add('body', 'requests', error_msg) return if any([batch.path in req['path'] for req in requests]): error_msg = 'Recursive call on %s endpoint is forbidden.' % batch.path request.errors.add('body', 'requests', error_msg) return responses = [] sublogger = logger.new() for subrequest_spec in requests: subrequest = build_request(request, subrequest_spec) subrequest.parent = request sublogger.bind(path=subrequest.path, method=subrequest.method) try: subresponse = request.invoke_subrequest(subrequest) except httpexceptions.HTTPException as e: error_msg = 'Failed batch subrequest' subresponse = errors.http_error(e, message=error_msg) except Exception as e: logger.error(e) subresponse = errors.http_error( httpexceptions.HTTPInternalServerError()) sublogger.bind(code=subresponse.status_code) sublogger.info('subrequest.summary') subresponse = build_response(subresponse, subrequest) responses.append(subresponse) # Rebing batch request for summary logger.bind(path=batch.path, method=request.method, batch_size=batch_size, agent=request.headers.get('User-Agent'),) return { 'responses': responses }
def response_error(context, request): """Catch response error from Sync and trace them.""" message = '%s %s: %s' % (context.response.status_code, context.response.reason, context.response.text) # XXX: Make sure these HTTPError exception are coming from SyncClient. statsd_count(request, "syncclient.status_code.%s" % context.response.status_code) if context.response.status_code in (400, 401, 403, 404): # For this code we also want to log the info about the error. logger.info(context, exc_info=True) # For this specific code we do not want to log the error. if context.response.status_code == 304: response = httpexceptions.HTTPNotModified() elif context.response.status_code == 400: response = http_error(httpexceptions.HTTPBadRequest(), errno=ERRORS.INVALID_PARAMETERS, message=message) elif context.response.status_code == 401: response = http_error(httpexceptions.HTTPUnauthorized(), errno=ERRORS.INVALID_AUTH_TOKEN, message=message) # Forget the current user credentials. response.headers.extend(forget(request)) elif context.response.status_code == 403: response = http_error(httpexceptions.HTTPForbidden(), errno=ERRORS.FORBIDDEN, message=message) elif context.response.status_code == 404: response = http_error(httpexceptions.HTTPNotFound(), errno=ERRORS.INVALID_RESOURCE_ID, message=message) elif context.response.status_code == 412: message = 'Resource was modified meanwhile' response = http_error(httpexceptions.HTTPPreconditionFailed(), errno=ERRORS.MODIFIED_MEANWHILE, message=message) else: # For this code we also want to log the error. logger.error(context, exc_info=True) response = service_unavailable( httpexceptions.HTTPServiceUnavailable(), request) request.response = response export_headers(context.response, request) return reapply_cors(request, response)
def _raise_412_if_modified(self, record=None): """Raise 412 if current timestamp is superior to the one specified in headers. :raises: :exc:`~pyramid:pyramid.httpexceptions.HTTPPreconditionFailed` """ unmodified_since = self.request.headers.get('If-Unmodified-Since') if unmodified_since: unmodified_since = int(unmodified_since) if record: current_timestamp = record[self.modified_field] else: current_timestamp = self.db.collection_timestamp( **self.db_kwargs) if current_timestamp > unmodified_since: error_msg = 'Resource was modified meanwhile' response = http_error(HTTPPreconditionFailed(), errno=ERRORS.MODIFIED_MEANWHILE, message=error_msg) self._add_timestamp_header(response) raise response
def eos_tween(request): eos_date = registry.settings['cliquet.eos'] eos_url = registry.settings['cliquet.eos_url'] eos_message = registry.settings['cliquet.eos_message'] if eos_date: eos_date = dateparser.parse(eos_date) alert = {} if eos_url is not None: alert['url'] = eos_url if eos_message is not None: alert['message'] = eos_message if eos_date > datetime.datetime.now(): alert['code'] = "soft-eol" response = handler(request) else: response = errors.http_error( HTTPGone(), errno=errors.ERRORS.SERVICE_DEPRECATED, message=deprecation_msg) alert['code'] = "hard-eol" response.headers['Alert'] = utils.json.dumps(alert) return response return handler(request)
def page_not_found(request): """Return a JSON 404 error response.""" config_key = 'trailing_slash_redirect_enabled' redirect_enabled = request.registry.settings[config_key] trailing_slash_redirection_enabled = asbool(redirect_enabled) querystring = request.url[(request.url.rindex(request.path) + len(request.path)):] errno = ERRORS.MISSING_RESOURCE error_msg = "The resource you are looking for could not be found." if not request.path.startswith('/' + request.registry.route_prefix): errno = ERRORS.VERSION_NOT_AVAILABLE error_msg = ("The requested protocol version is not available " "on this server.") elif trailing_slash_redirection_enabled: redirect = None if request.path.endswith('/'): path = request.path.rstrip('/') redirect = '%s%s' % (path, querystring) elif request.path == '/' + request.registry.route_prefix: # Case for /v0 -> /v0/ redirect = '/%s/%s' % (request.registry.route_prefix, querystring) if redirect: return HTTPTemporaryRedirect(redirect) response = http_error(httpexceptions.HTTPNotFound(), errno=errno, message=error_msg) return response
def page_not_found(request): """Return a JSON 404 error response.""" error_msg = "The resource your are looking for could not be found." response = http_error(httpexceptions.HTTPNotFound(), errno=ERRORS.MISSING_RESOURCE, message=error_msg) return response
def method_not_allowed(context, request): if context.content_type == 'application/json': return context response = http_error(context, errno=ERRORS.METHOD_NOT_ALLOWED, message="Method not allowed on this endpoint.") return reapply_cors(request, response)
def authorization_required(request): """Distinguish authentication required (``401 Unauthorized``) from not allowed (``403 Forbidden``). """ if Authenticated not in request.effective_principals: error_msg = "Please authenticate yourself to use this endpoint." response = http_error(httpexceptions.HTTPUnauthorized(), errno=ERRORS.MISSING_AUTH_TOKEN, message=error_msg) response.headers.extend(forget(request)) return response error_msg = "This user cannot access this resource." response = http_error(httpexceptions.HTTPForbidden(), errno=ERRORS.FORBIDDEN, message=error_msg) return response
def service_unavailable(response, request): if response.content_type != 'application/json': error_msg = "Service unavailable due to high load, please retry later." response = http_error(response, errno=ERRORS.BACKEND, message=error_msg) retry_after = request.registry.settings['retry_after_seconds'] response.headers["Retry-After"] = encode_header('%s' % retry_after) return reapply_cors(request, response)
def service_unavailable(response, request): if response.content_type != 'application/json': error_msg = ("Service temporary unavailable " "due to overloading or maintenance, please retry later.") response = http_error(response, errno=ERRORS.BACKEND, message=error_msg) retry_after = request.registry.settings['retry_after_seconds'] response.headers["Retry-After"] = encode_header('%s' % retry_after) return reapply_cors(request, response)
def service_unavailable(context, request): error_msg = "Service unavailable due to high load, please retry later." response = http_error(httpexceptions.HTTPServiceUnavailable(), errno=ERRORS.BACKEND, message=error_msg) retry_after = request.registry.settings['cliquet.retry_after_seconds'] response.headers["Retry-After"] = ('%s' % retry_after).encode("utf-8") return reapply_cors(request, response)
def request_error(context, request): """Catch requests errors when issuing a request to Sync.""" logger.error(context, exc_info=True) error_msg = ("Unable to reach the service. " "Check your internet connection or firewall configuration.") response = http_error(httpexceptions.HTTPServiceUnavailable(), errno=ERRORS.BACKEND, message=error_msg) return service_unavailable(response, request)
def post_batch(request): requests = request.validated['requests'] batch_size = len(requests) limit = request.registry.settings['batch_max_requests'] if limit and len(requests) > int(limit): error_msg = 'Number of requests is limited to %s' % limit request.errors.add('body', 'requests', error_msg) return if any([batch.path in req['path'] for req in requests]): error_msg = 'Recursive call on %s endpoint is forbidden.' % batch.path request.errors.add('body', 'requests', error_msg) return responses = [] sublogger = logger.new() for subrequest_spec in requests: subrequest = build_request(request, subrequest_spec) sublogger.bind(path=subrequest.path, method=subrequest.method) try: # Invoke subrequest without individual transaction. resp, subrequest = request.follow_subrequest(subrequest, use_tweens=False) except httpexceptions.HTTPException as e: if e.content_type == 'application/json': resp = e else: # JSONify raw Pyramid errors. resp = errors.http_error(e) except Exception as e: resp = render_view_to_response(e, subrequest) if resp.status_code >= 500: raise e sublogger.bind(code=resp.status_code) sublogger.info('subrequest.summary') dict_resp = build_response(resp, subrequest) responses.append(dict_resp) # Rebing batch request for summary logger.bind(path=batch.path, method=request.method, batch_size=batch_size, agent=request.headers.get('User-Agent'),) return { 'responses': responses }
def _get_record_or_404(self, record_id): """Retrieve record from storage and raise ``404 Not found`` if missing. :raises: :exc:`~pyramid:pyramid.httpexceptions.HTTPNotFound` if the record is not found. """ try: return self.collection.get_record(record_id) except storage_exceptions.RecordNotFoundError: response = http_error(HTTPNotFound(), errno=ERRORS.INVALID_RESOURCE_ID) raise response
def test_503_can_have_custom_message(self): custom_503 = http_error(httpexceptions.HTTPServiceUnavailable(), errno=ERRORS.BACKEND, message="Unable to connect the server.") with mock.patch( 'cliquet.tests.testapp.views.Mushroom._extract_filters', side_effect=custom_503): response = self.app.get(self.sample_url, headers=self.headers, status=503) self.assertFormattedError( response, 503, ERRORS.BACKEND, "Service Unavailable", "Unable to connect the server.")
def test_405_can_have_custom_message(self): custom_405 = http_error(httpexceptions.HTTPMethodNotAllowed(), errno=ERRORS.METHOD_NOT_ALLOWED, message="Disabled from conf.") with mock.patch( 'cliquet.tests.testapp.views.Mushroom._extract_filters', side_effect=custom_405): response = self.app.get(self.sample_url, headers=self.headers, status=405) self.assertFormattedError(response, 405, ERRORS.METHOD_NOT_ALLOWED, "Method Not Allowed", "Disabled from conf.")
def test_405_can_have_custom_message(self): custom_405 = http_error(httpexceptions.HTTPMethodNotAllowed(), errno=ERRORS.METHOD_NOT_ALLOWED, message="Disabled from conf.") with mock.patch( 'cliquet.tests.testapp.views.Mushroom._extract_filters', side_effect=custom_405): response = self.app.get(self.sample_url, headers=self.headers, status=405) self.assertFormattedError( response, 405, ERRORS.METHOD_NOT_ALLOWED, "Method Not Allowed", "Disabled from conf.")
def test_503_can_have_custom_message(self): custom_503 = http_error(httpexceptions.HTTPServiceUnavailable(), errno=ERRORS.BACKEND, message="Unable to connect the server.") with mock.patch( 'cliquet.tests.testapp.views.Mushroom._extract_filters', side_effect=custom_503): response = self.app.get(self.sample_url, headers=self.headers, status=503) self.assertFormattedError(response, 503, ERRORS.BACKEND, "Service Unavailable", "Unable to connect the server.")
def assert_endpoint_enabled(request, collection_name): """Check that endpoint is not disabled from configuration. """ settings = request.registry.settings method = request.method.lower() setting_key = 'record_%s_%s_enabled' % (collection_name, method) enabled = settings.get(setting_key, False) if not enabled: error_msg = 'Endpoint disabled for this collection in configuration.' response = errors.http_error(httpexceptions.HTTPMethodNotAllowed(), errno=errors.ERRORS.METHOD_NOT_ALLOWED, message=error_msg) raise response
def _raise_412_if_modified(self, record=None): """Raise 412 if current timestamp is superior to the one specified in headers. :raises: :exc:`~pyramid:pyramid.httpexceptions.HTTPPreconditionFailed` """ if_match = self.request.headers.get('If-Match') if_none_match = self.request.headers.get('If-None-Match') if not if_match and not if_none_match: return if_match = decode_header(if_match) if if_match else None if record and if_none_match and decode_header(if_none_match) == '*': if record.get(self.model.deleted_field, False): # Tombstones should not prevent creation. return modified_since = -1 # Always raise. elif if_match: try: if not (if_match[0] == if_match[-1] == '"'): raise ValueError() modified_since = int(if_match[1:-1]) except (IndexError, ValueError): message = ("Invalid value for If-Match. The value should " "be integer between double quotes.") error_details = { 'location': 'headers', 'description': message } raise_invalid(self.request, **error_details) else: # In case _raise_304_if_not_modified() did not raise. return if record: current_timestamp = record[self.model.modified_field] else: current_timestamp = self.model.timestamp() if current_timestamp > modified_since: error_msg = 'Resource was modified meanwhile' details = {'existing': record} if record else {} response = http_error(HTTPPreconditionFailed(), errno=ERRORS.MODIFIED_MEANWHILE, message=error_msg, details=details) self._add_timestamp_header(response, timestamp=current_timestamp) raise response
def page_not_found(request): """Return a JSON 404 error response.""" if request.path.startswith('/' + request.registry.route_prefix): errno = ERRORS.MISSING_RESOURCE error_msg = "The resource your are looking for could not be found." else: errno = ERRORS.VERSION_NOT_AVAILABLE error_msg = ("The requested protocol version is not available " "on this server.") response = http_error(httpexceptions.HTTPNotFound(), errno=errno, message=error_msg) return response
def get_record(self, record_id): """Fetch current view related record, and raise 404 if missing. :raises: :class:`pyramid.httpexceptions.HTTPNotFound` :returns: the record from storage :rtype: dict """ try: return self.db.get(record_id=record_id, **self.db_kwargs) except storage_exceptions.RecordNotFoundError: response = http_error(HTTPNotFound(), errno=ERRORS.INVALID_RESOURCE_ID) raise response
def post_batch(request): requests = request.validated['requests'] batch_size = len(requests) limit = request.registry.settings['batch_max_requests'] if limit and len(requests) > int(limit): error_msg = 'Number of requests is limited to %s' % limit request.errors.add('body', 'requests', error_msg) return if any([batch.path in req['path'] for req in requests]): error_msg = 'Recursive call on %s endpoint is forbidden.' % batch.path request.errors.add('body', 'requests', error_msg) return responses = [] sublogger = logger.new() for subrequest_spec in requests: subrequest = build_request(request, subrequest_spec) sublogger.bind(path=subrequest.path, method=subrequest.method) try: # Invoke subrequest without individual transaction. resp, subrequest = request.follow_subrequest(subrequest, use_tweens=False) except httpexceptions.HTTPException as e: if e.content_type == 'application/json': resp = e else: # JSONify raw Pyramid errors. resp = errors.http_error(e) sublogger.bind(code=resp.status_code) sublogger.info('subrequest.summary') dict_resp = build_response(resp, subrequest) responses.append(dict_resp) # Rebing batch request for summary logger.bind( path=batch.path, method=request.method, batch_size=batch_size, agent=request.headers.get('User-Agent'), ) return {'responses': responses}
def _raise_conflict(self, exception): """Helper to raise conflict responses. :param exception: the original unicity error :type exception: :class:`cliquet.storage.exceptions.UnicityError` :raises: :class:`pyramid.httpexceptions.HTTPConflict` """ field = exception.field existing = exception.record[self.id_field] message = 'Conflict of field {0} on record {1}'.format(field, existing) response = http_error(HTTPConflict(), errno=ERRORS.CONSTRAINT_VIOLATED, message=message) response.existing = exception.record raise response
def test_formatted_error_are_passed_through(self): response = http_error(HTTPBadRequest(), errno=ERRORS.INVALID_PARAMETERS, message='Yop') with mock.patch.object(self.storage, 'create') as mocked: mocked.side_effect = [ {"id": "abc", "last_modified": 43}, {"id": "abc", "last_modified": 44}, response ] resp = self.app.post(self.collection_url + '/records', headers=self.headers, status=400) self.assertEqual(resp.body, response.body)
def test_formatted_error_are_passed_through(self): request = mock.MagicMock() request.method = "PUT" request.url = "http://localhost/v1/buckets/default/collections/tasks" request.path = "http://localhost/v1/buckets/default/collections/tasks" request.prefixed_userid = "fxa:abcd" request.registry.settings = {"readonly": False, "userid_hmac_secret": "This is no secret"} response = http_error(HTTPBadRequest(), errno=ERRORS.INVALID_PARAMETERS, message="Yop") with mock.patch("kinto.views.buckets.create_bucket"): with mock.patch("kinto.views.buckets.create_collection"): request.invoke_subrequest.side_effect = response resp = default_bucket(request) self.assertEqual(resp.body, response.body)
def _raise_412_if_modified(self, record=None): """Raise 412 if current timestamp is superior to the one specified in headers. :raises: :exc:`~pyramid:pyramid.httpexceptions.HTTPPreconditionFailed` """ if_match = self.request.headers.get('If-Match') if_none_match = self.request.headers.get('If-None-Match') if not if_match and not if_none_match: return if_match = decode_header(if_match) if if_match else None if record and if_none_match and decode_header(if_none_match) == '*': if record.get(self.model.deleted_field, False): # Tombstones should not prevent creation. return modified_since = -1 # Always raise. elif if_match: try: if not (if_match[0] == if_match[-1] == '"'): raise ValueError() modified_since = int(if_match[1:-1]) except (IndexError, ValueError): message = ("Invalid value for If-Match. The value should " "be integer between double quotes.") error_details = {'location': 'headers', 'description': message} raise_invalid(self.request, **error_details) else: # In case _raise_304_if_not_modified() did not raise. return if record: current_timestamp = record[self.model.modified_field] else: current_timestamp = self.model.timestamp() if current_timestamp > modified_since: error_msg = 'Resource was modified meanwhile' details = {'existing': record} if record else {} response = http_error(HTTPPreconditionFailed(), errno=ERRORS.MODIFIED_MEANWHILE, message=error_msg, details=details) self._add_timestamp_header(response, timestamp=current_timestamp) raise response
def _get_record_or_404(self, record_id): """Retrieve record from storage and raise ``404 Not found`` if missing. :raises: :exc:`~pyramid:pyramid.httpexceptions.HTTPNotFound` if the record is not found. """ if self.context and self.context.current_record: # Set during authorization. Save a storage hit. return self.context.current_record try: return self.model.get_record(record_id) except storage_exceptions.RecordNotFoundError: response = http_error(HTTPNotFound(), errno=ERRORS.INVALID_RESOURCE_ID) raise response
def test_formatted_error_are_passed_through(self): request = mock.MagicMock() request.method = 'PUT' request.url = 'http://localhost/v1/buckets/default/collections/tasks' request.path = 'http://localhost/v1/buckets/default/collections/tasks' request.prefixed_userid = 'fxa:abcd' request.registry.settings = {'userid_hmac_secret': 'This is no secret'} response = http_error(HTTPBadRequest(), errno=ERRORS.INVALID_PARAMETERS, message='Yop') with mock.patch('kinto.views.buckets.create_bucket'): with mock.patch('kinto.views.buckets.create_collection'): request.invoke_subrequest.side_effect = response resp = default_bucket(request) self.assertEqual(resp.body, response.body)
def test_formatted_error_are_passed_through(self): response = http_error(HTTPBadRequest(), errno=ERRORS.INVALID_PARAMETERS, message='Yop') with mock.patch.object(self.storage, 'create') as mocked: mocked.side_effect = [{ "id": "abc", "last_modified": 43 }, { "id": "abc", "last_modified": 44 }, response] resp = self.app.post(self.collection_url + '/records', headers=self.headers, status=400) self.assertEqual(resp.body, response.body)
def error(context, request): """Catch server errors and trace them.""" if isinstance(context, httpexceptions.Response): return reapply_cors(request, context) if isinstance(context, storage_exceptions.BackendError): logger.critical(context.original, exc_info=True) return service_unavailable(context, request) logger.error(context, exc_info=True) error_msg = "A programmatic error occured, developers have been informed." info = "https://github.com/mozilla-services/cliquet/issues/" response = http_error(httpexceptions.HTTPInternalServerError(), message=error_msg, info=info) return reapply_cors(request, response)
def _raise_conflict(self, exception): """Helper to raise conflict responses. :param exception: the original unicity error :type exception: :class:`cliquet.storage.exceptions.UnicityError` :raises: :exc:`~pyramid:pyramid.httpexceptions.HTTPConflict` """ field = exception.field record_id = exception.record[self.model.id_field] message = 'Conflict of field %s on record %s' % (field, record_id) details = { "field": field, "existing": exception.record, } response = http_error(HTTPConflict(), errno=ERRORS.CONSTRAINT_VIOLATED, message=message, details=details) raise response
def _raise_412_if_modified(self, record=None): """Raise 412 if current timestamp is superior to the one specified in headers. :raises: :exc:`~pyramid:pyramid.httpexceptions.HTTPPreconditionFailed` """ if_match = self.request.headers.get('If-Match') if_none_match = self.request.headers.get('If-None-Match') if not if_match and not if_none_match: return if_match = if_match.decode('utf-8') if if_match else None if if_none_match and if_none_match.decode('utf-8') == '*': modified_since = -1 # Always raise. elif if_match: try: assert if_match[0] == if_match[-1] == '"' modified_since = int(if_match[1:-1]) except (IndexError, AssertionError, ValueError): error_details = { 'location': 'headers', 'description': "Invalid value for If-Match" } raise_invalid(self.request, **error_details) else: # In case _raise_304_if_not_modified() did not raise. return if record: current_timestamp = record[self.collection.modified_field] else: current_timestamp = self.collection.timestamp() if current_timestamp > modified_since: error_msg = 'Resource was modified meanwhile' response = http_error(HTTPPreconditionFailed(), errno=ERRORS.MODIFIED_MEANWHILE, message=error_msg) self._add_timestamp_header(response, timestamp=current_timestamp) raise response
def eos_tween(request): eos_date = registry.settings["eos"] eos_url = registry.settings["eos_url"] eos_message = registry.settings["eos_message"] if not eos_date: return handler(request) eos_date = dateparser.parse(eos_date) if eos_date > datetime.now(): code = "soft-eol" request.response = handler(request) else: code = "hard-eol" request.response = errors.http_error( HTTPGone(), errno=errors.ERRORS.SERVICE_DEPRECATED, message=deprecation_msg ) errors.send_alert(request, eos_message, url=eos_url, code=code) return request.response
def error(context, request): """Catch server errors and trace them.""" if isinstance(context, httpexceptions.Response): return reapply_cors(request, context) if isinstance(context, storage_exceptions.BackendError): logger.critical(context.original, exc_info=True) response = httpexceptions.HTTPServiceUnavailable() return service_unavailable(response, request) logger.error(context, exc_info=True) error_msg = "A programmatic error occured, developers have been informed." info = request.registry.settings['error_info_link'] response = http_error(httpexceptions.HTTPInternalServerError(), message=error_msg, info=info) return reapply_cors(request, response)
def eos_tween(request): eos_date = registry.settings['eos'] eos_url = registry.settings['eos_url'] eos_message = registry.settings['eos_message'] if not eos_date: return handler(request) eos_date = dateparser.parse(eos_date) if eos_date > datetime.now(): code = "soft-eol" request.response = handler(request) else: code = "hard-eol" request.response = errors.http_error( HTTPGone(), errno=errors.ERRORS.SERVICE_DEPRECATED, message=deprecation_msg) errors.send_alert(request, eos_message, url=eos_url, code=code) return request.response
def timestamp(self): """Return the current collection timestamp. :rtype: int """ try: return self.model.timestamp() except storage_exceptions.BackendError as e: is_readonly = self.request.registry.settings['readonly'] if not is_readonly: raise e # If the instance is configured to be readonly, and if the # collection is empty, the backend will try to bump the timestamp. # It fails if the configured db user has not write privileges. logger.exception(e) error_msg = ("Collection timestamp cannot be written. " "Records endpoint must be hit at least once from a " "writable instance.") raise http_error(HTTPServiceUnavailable(), errno=ERRORS.BACKEND, message=error_msg)