def connect(self, readonly=False, force_commit=False): """ Pulls a connection from the pool when context is entered and returns it when context is exited. A COMMIT is performed on the current transaction if everything went well. Otherwise transaction is ROLLBACK, and everything cleaned up. """ with_transaction = not readonly and self.commit_manually session = None try: # Pull connection from pool. session = self.session_factory() # Start context yield session if not readonly and not self.commit_manually: # Mark session as dirty. self.invalidate(session) # Success if with_transaction: session.commit() elif force_commit: # Commit like would do a succesful request. zope_transaction.commit() except sqlalchemy.exc.SQLAlchemyError as e: logger.error(e) if session and with_transaction: session.rollback() raise exceptions.BackendError(original=e) finally: if session and self.commit_manually: # Give back to pool if commit done manually. session.close()
def fxa_oauth_token(request): """Return OAuth token from authorization code. """ state = request.validated['state'] code = request.validated['code'] # Require on-going session stored_redirect = request.cache.get(state) # Make sure we cannot try twice with the same code request.registry.cache.delete(state) if not stored_redirect: return authorization_required(request) # Trade the OAuth code for a longer-lived token auth_client = OAuthClient(server_url=fxa_conf(request, 'oauth_uri'), client_id=fxa_conf(request, 'client_id'), client_secret=fxa_conf(request, 'client_secret')) try: token = auth_client.trade_code(code) except fxa_errors.OutOfProtocolError: raise httpexceptions.HTTPServiceUnavailable() except fxa_errors.InProtocolError as error: logger.error(error) error_details = { 'name': 'code', 'location': 'querystring', 'description': 'Firefox Account code validation failed.' } errors.raise_invalid(request, **error_details) return httpexceptions.HTTPFound(location='%s%s' % (stored_redirect, token))
def connect(self, readonly=False): """Connect to the database and instantiates a cursor. At exiting the context manager, a COMMIT is performed on the current transaction if everything went well. Otherwise transaction is ROLLBACK, and everything cleaned up. If the database could not be be reached a 503 error is raised. """ conn = None cursor = None try: conn = self.pool.getconn() conn.autocommit = readonly options = dict(cursor_factory=psycopg2.extras.DictCursor) cursor = conn.cursor(**options) # Start context yield cursor # End context if not readonly: conn.commit() except psycopg2.Error as e: if cursor: logger.debug(cursor.query) logger.error(e) if conn and not conn.closed: conn.rollback() raise exceptions.BackendError(original=e) finally: if cursor: cursor.close() if conn and not conn.closed: self.pool.putconn(conn, close=self._always_close)
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 __call__(self, event): try: payload = json.dumps(event.payload) except TypeError: logger.error("Unable to dump the payload", exc_info=True) return try: self._client.lpush(self.listname, payload) except Exception: logger.error("Unable to send the payload to Redis", exc_info=True)
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) 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 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 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 _notify_event(self, result, action, old): """ Emit a :class:`cliquet.event.ResourceChanged` event with the impacted records. # XXX: this should be plugged with the transaction system. When plugged with pyramid_tm, this method will have to be rewritten to just populate the ``bound_data`` attribute of ``self.request``. (since it is shared among batch requests). """ # When plugged with the transaction system, impacted records will # always be a list. Therefore, here put single operations into a # list. if not isinstance(result, list): result = [result] if action == ACTIONS.READ: event_cls = ResourceRead else: event_cls = ResourceChanged if action == ACTIONS.READ: impacted = result elif action == ACTIONS.CREATE: impacted = [{'new': r} for r in result] elif action == ACTIONS.DELETE: impacted = [{'old': r} for r in result] elif action == ACTIONS.UPDATE: # XXX `old` is "always" the same, since currently, we can only # notify one update at a time. # This code will change when plugged with transactions (and batch). impacted = [{'new': r, 'old': old} for r in result] try: event = event_cls(action, self, impacted, self.request) self.request.registry.notify(event) except Exception: logger.error("Unable to notify", exc_info=True)