def __init__(self, settings_name="zappa_settings", session=None): # We haven't cached our settings yet, load the settings and app. if not self.settings: # Loading settings from a python module self.settings = importlib.import_module(settings_name) self.settings_name = settings_name self.session = session # Custom log level if self.settings.LOG_LEVEL: level = logging.getLevelName(self.settings.LOG_LEVEL) logger.setLevel(level) remote_env = getattr(self.settings, 'REMOTE_ENV', None) remote_bucket, remote_file = parse_s3_url(remote_env) if remote_bucket and remote_file: self.load_remote_settings(remote_bucket, remote_file) # Let the system know that this will be a Lambda/Zappa/Stack os.environ["SERVERTYPE"] = "AWS Lambda" os.environ["FRAMEWORK"] = "Zappa" try: os.environ["PROJECT"] = self.settings.PROJECT_NAME os.environ["STAGE"] = self.settings.API_STAGE except Exception: # pragma: no cover pass # Set any locally defined env vars # Environement variable keys can't be Unicode # https://github.com/Miserlou/Zappa/issues/604 for key in self.settings.ENVIRONMENT_VARIABLES.keys(): os.environ[str(key)] = self.settings.ENVIRONMENT_VARIABLES[key] # Pulling from S3 if given a zip path project_zip_path = getattr(self.settings, 'ZIP_PATH', None) if project_zip_path: self.load_remote_project_zip(project_zip_path) # Django gets special treatment. if not self.settings.DJANGO_SETTINGS: # The app module self.app_module = importlib.import_module(self.settings.APP_MODULE) # The application wsgi_app_function = getattr(self.app_module, self.settings.APP_FUNCTION) self.trailing_slash = False else: try: # Support both for tests from zappa.ext.django import get_django_wsgi except ImportError: # pragma: no cover from django_zappa_app import get_django_wsgi # Get the Django WSGI app from our extension wsgi_app_function = get_django_wsgi(self.settings.DJANGO_SETTINGS) self.trailing_slash = True self.wsgi_app = ZappaWSGIMiddleware(wsgi_app_function)
def __init__(self, settings_name="zappa_settings", session=None): # We haven't cached our settings yet, load the settings and app. if not self.settings: # Loading settings from a python module self.settings = importlib.import_module(settings_name) self.settings_name = settings_name self.session = session # Custom log level if self.settings.LOG_LEVEL: level = logging.getLevelName(self.settings.LOG_LEVEL) logger.setLevel(level) remote_env = getattr(self.settings, 'REMOTE_ENV', None) remote_bucket, remote_file = parse_s3_url(remote_env) if remote_bucket and remote_file: self.load_remote_settings(remote_bucket, remote_file) # Let the system know that this will be a Lambda/Zappa/Stack os.environ["SERVERTYPE"] = "AWS Lambda" os.environ["FRAMEWORK"] = "Zappa" try: os.environ["PROJECT"] = self.settings.PROJECT_NAME os.environ["STAGE"] = self.settings.API_STAGE except Exception: # pragma: no cover pass # Set any locally defined env vars # Environement variable keys can't be Unicode # https://github.com/Miserlou/Zappa/issues/604 for key in self.settings.ENVIRONMENT_VARIABLES.keys(): os.environ[str(key)] = self.settings.ENVIRONMENT_VARIABLES[key] # Django gets special treatment. if not self.settings.DJANGO_SETTINGS: # The app module self.app_module = importlib.import_module( self.settings.APP_MODULE) # The application wsgi_app_function = getattr(self.app_module, self.settings.APP_FUNCTION) self.trailing_slash = False else: try: # Support both for tests from zappa.ext.django import get_django_wsgi except ImportError: # pragma: no cover from django_zappa_app import get_django_wsgi # Get the Django WSGI app from our extension wsgi_app_function = get_django_wsgi( self.settings.DJANGO_SETTINGS) self.trailing_slash = True self.wsgi_app = ZappaWSGIMiddleware(wsgi_app_function)
def handler(self, event, context): """ An AWS Lambda function which parses specific API Gateway input into a WSGI request, feeds it to our WSGI app, procceses the response, and returns that back to the API Gateway. """ settings = self.settings # If in DEBUG mode, log all raw incoming events. if settings.DEBUG: logger.debug('Zappa Event: {}'.format(event)) # This is the result of a keep alive, recertify # or scheduled event. if event.get('detail-type') == u'Scheduled Event': whole_function = event['resources'][0].split('/')[-1].split('-')[-1] # This is a scheduled function. if '.' in whole_function: app_function = self.import_module_and_get_function(whole_function) # Execute the function! return self.run_function(app_function, event, context) # Else, let this execute as it were. # This is a direct command invocation. elif event.get('command', None): whole_function = event['command'] app_function = self.import_module_and_get_function(whole_function) result = self.run_function(app_function, event, context) print("Result of %s:" % whole_function) print(result) return result # This is a direct, raw python invocation. # It's _extremely_ important we don't allow this event source # to be overriden by unsanitized, non-admin user input. elif event.get('raw_command', None): raw_command = event['raw_command'] exec(raw_command) return # This is a Django management command invocation. elif event.get('manage', None): from django.core import management try: # Support both for tests from zappa.ext.django_zappa import get_django_wsgi except ImportError as e: # pragma: no cover from django_zappa_app import get_django_wsgi # Get the Django WSGI app from our extension # We don't actually need the function, # but we do need to do all of the required setup for it. app_function = get_django_wsgi(self.settings.DJANGO_SETTINGS) # Couldn't figure out how to get the value into stdout with StringIO.. # Read the log for now. :[] management.call_command(*event['manage'].split(' ')) return {} # This is an AWS-event triggered invokation. elif event.get('Records', None): records = event.get('Records') result = None whole_function = self.get_function_for_aws_event(records[0]) if whole_function: app_function = self.import_module_and_get_function(whole_function) result = self.run_function(app_function, event, context) logger.debug(result) else: logger.error("Cannot find a function to process the triggered event.") return result # This is an API Gateway authorizer event elif event.get('type') == u'TOKEN': whole_function = self.settings.AUTHORIZER_FUNCTION if whole_function: app_function = self.import_module_and_get_function(whole_function) policy = self.run_function(app_function, event, context) return policy else: logger.error("Cannot find a function to process the authorization request.") raise Exception('Unauthorized') # Normal web app flow try: # Timing time_start = datetime.datetime.now() # This is a normal HTTP request if event.get('httpMethod', None): if settings.DOMAIN: # If we're on a domain, we operate normally script_name = '' else: # But if we're not, then our base URL # will be something like # https://blahblahblah.execute-api.us-east-1.amazonaws.com/dev # So, we need to make sure the WSGI app knows this. script_name = '/' + settings.API_STAGE # Create the environment for WSGI and handle the request environ = create_wsgi_request( event, script_name=script_name, trailing_slash=self.trailing_slash, binary_support=settings.BINARY_SUPPORT ) # We are always on https on Lambda, so tell our wsgi app that. environ['HTTPS'] = 'on' environ['wsgi.url_scheme'] = 'https' environ['lambda.context'] = context # Execute the application response = Response.from_app(self.wsgi_app, environ) # This is the object we're going to return. # Pack the WSGI response into our special dictionary. zappa_returndict = dict() if response.data: if settings.BINARY_SUPPORT: if not response.mimetype.startswith("text/") \ or response.mimetype != "application/json": zappa_returndict['body'] = base64.b64encode(response.data) zappa_returndict["isBase64Encoded"] = "true" else: zappa_returndict['body'] = response.data else: zappa_returndict['body'] = response.data zappa_returndict['statusCode'] = response.status_code zappa_returndict['headers'] = {} for key, value in response.headers: zappa_returndict['headers'][key] = value # Calculate the total response time, # and log it in the Common Log format. time_end = datetime.datetime.now() delta = time_end - time_start response_time_ms = delta.total_seconds() * 1000 response.content = response.data common_log(environ, response, response_time=response_time_ms) return zappa_returndict except Exception as e: # pragma: no cover # Print statements are visible in the logs either way print(e) exc_info = sys.exc_info() message = ('An uncaught exception happened while servicing this request. ' 'You can investigate this with the `zappa tail` command.') # If we didn't even build an app_module, just raise. if not settings.DJANGO_SETTINGS: try: self.app_module except NameError as ne: message = 'Failed to import module: {}'.format(ne.message) # Return this unspecified exception as a 500, using template that API Gateway expects. content = collections.OrderedDict() content['statusCode'] = 500 body = {'message': message} if settings.DEBUG: # only include traceback if debug is on. body['traceback'] = traceback.format_exception(*exc_info) # traceback as a list for readability. content['body'] = json.dumps(body, sort_keys=True, indent=4).encode('utf-8') return content
def handler(self, event, context): """ An AWS Lambda function which parses specific API Gateway input into a WSGI request, feeds it to our WSGI app, procceses the response, and returns that back to the API Gateway. """ settings = self.settings # If in DEBUG mode, log all raw incoming events. if settings.DEBUG: logger.debug('Zappa Event: {}'.format(event)) # This is the result of a keep alive, recertify # or scheduled event. if event.get('detail-type') == u'Scheduled Event': whole_function = event['resources'][0].split('/')[-1].split( '-')[-1] # This is a scheduled function. if '.' in whole_function: app_function = self.import_module_and_get_function( whole_function) # Execute the function! return self.run_function(app_function, event, context) # Else, let this execute as it were. # This is a direct command invocation. elif event.get('command', None): whole_function = event['command'] app_function = self.import_module_and_get_function(whole_function) result = self.run_function(app_function, event, context) print("Result of %s:" % whole_function) print(result) return result # This is a direct, raw python invocation. # It's _extremely_ important we don't allow this event source # to be overriden by unsanitized, non-admin user input. elif event.get('raw_command', None): raw_command = event['raw_command'] exec(raw_command) return # This is a Django management command invocation. elif event.get('manage', None): from django.core import management try: # Support both for tests from zappa.ext.django_zappa import get_django_wsgi except ImportError as e: # pragma: no cover from django_zappa_app import get_django_wsgi # Get the Django WSGI app from our extension # We don't actually need the function, # but we do need to do all of the required setup for it. app_function = get_django_wsgi(self.settings.DJANGO_SETTINGS) # Couldn't figure out how to get the value into stdout with StringIO.. # Read the log for now. :[] management.call_command(*event['manage'].split(' ')) return {} # This is an AWS-event triggered invokation. elif event.get('Records', None): records = event.get('Records') result = None whole_function = self.get_function_for_aws_event(records[0]) if whole_function: app_function = self.import_module_and_get_function( whole_function) result = self.run_function(app_function, event, context) logger.debug(result) else: logger.error( "Cannot find a function to process the triggered event.") return result # This is an API Gateway authorizer event elif event.get('type') == u'TOKEN': whole_function = self.settings.AUTHORIZER_FUNCTION if whole_function: app_function = self.import_module_and_get_function( whole_function) policy = self.run_function(app_function, event, context) return policy else: logger.error( "Cannot find a function to process the authorization request." ) raise Exception('Unauthorized') # Normal web app flow try: # Timing time_start = datetime.datetime.now() # This is a normal HTTP request if event.get('httpMethod', None): if settings.DOMAIN: # If we're on a domain, we operate normally script_name = '' else: # But if we're not, then our base URL # will be something like # https://blahblahblah.execute-api.us-east-1.amazonaws.com/dev # So, we need to make sure the WSGI app knows this. script_name = '/' + settings.API_STAGE # Create the environment for WSGI and handle the request environ = create_wsgi_request( event, script_name=script_name, trailing_slash=self.trailing_slash, binary_support=settings.BINARY_SUPPORT) # We are always on https on Lambda, so tell our wsgi app that. environ['HTTPS'] = 'on' environ['wsgi.url_scheme'] = 'https' environ['lambda.context'] = context # Execute the application response = Response.from_app(self.wsgi_app, environ) # This is the object we're going to return. # Pack the WSGI response into our special dictionary. zappa_returndict = dict() if response.data: if settings.BINARY_SUPPORT: if not response.mimetype.startswith("text/") \ or response.mimetype != "application/json": zappa_returndict['body'] = base64.b64encode( response.data) zappa_returndict["isBase64Encoded"] = "true" else: zappa_returndict['body'] = response.data else: zappa_returndict['body'] = response.data zappa_returndict['statusCode'] = response.status_code zappa_returndict['headers'] = {} for key, value in response.headers: zappa_returndict['headers'][key] = value # Calculate the total response time, # and log it in the Common Log format. time_end = datetime.datetime.now() delta = time_end - time_start response_time_ms = delta.total_seconds() * 1000 response.content = response.data common_log(environ, response, response_time=response_time_ms) return zappa_returndict except Exception as e: # pragma: no cover # Print statements are visible in the logs either way print(e) exc_info = sys.exc_info() message = ( 'An uncaught exception happened while servicing this request. ' 'You can investigate this with the `zappa tail` command.') # If we didn't even build an app_module, just raise. if not settings.DJANGO_SETTINGS: try: self.app_module except NameError as ne: message = 'Failed to import module: {}'.format(ne.message) # Return this unspecified exception as a 500, using template that API Gateway expects. content = collections.OrderedDict() content['statusCode'] = 500 body = {'message': message} if settings.DEBUG: # only include traceback if debug is on. body['traceback'] = traceback.format_exception( *exc_info) # traceback as a list for readability. content['body'] = json.dumps(body, sort_keys=True, indent=4).encode('utf-8') return content
def handler(self, event, context): """ An AWS Lambda function which parses specific API Gateway input into a WSGI request, feeds it to our WSGI app, procceses the response, and returns that back to the API Gateway. """ settings = self.settings # If in DEBUG mode, log all raw incoming events. if settings.DEBUG: print('Zappa Event: {}'.format(event)) logger.debug('Zappa Event: {}'.format(event)) # Custom log level if settings.LOG_LEVEL: level = logging.getLevelName(settings.LOG_LEVEL) logger.setLevel(level) # This is the result of a keep alive # or scheduled event. if event.get('detail-type') == u'Scheduled Event': whole_function = event['resources'][0].split('/')[-1].split('-')[-1] # This is a scheduled, non-keep-alive function. # This is not the best way to do this but it'll do. if '.' in whole_function: app_function = self.import_module_and_get_function(whole_function) # Execute the function! return self.run_function(app_function, event, context) # Else, let this execute as it were. # This is a direct command invocation. elif event.get('command', None): whole_function = event['command'] app_function = self.import_module_and_get_function(whole_function) result = self.run_function(app_function, event, context) print("Result of %s:" % whole_function) print(result) return result # This is a Django management command invocation. elif event.get('manage', None): from django.core import management try: # Support both for tests from zappa.ext.django import get_django_wsgi except ImportError as e: # pragma: no cover from django_zappa_app import get_django_wsgi # Get the Django WSGI app from our extension # We don't actually need the function, # but we do need to do all of the required setup for it. app_function = get_django_wsgi(self.settings.DJANGO_SETTINGS) # Couldn't figure out how to get the value into stdout with StringIO.. # Read the log for now. :[] management.call_command(*event['manage'].split(' ')) return {} # This is an AWS-event triggered invokation. elif event.get('Records', None): records = event.get('Records') event_types = ['dynamodb', 'kinesis', 's3', 'sns', 'events'] for record in records: for event_type in event_types: if record.has_key(event_type): whole_function = record[event_type]['configurationId'] app_function = self.import_module_and_get_function(whole_function) result = self.run_function(app_function, event, context) print(result) return result try: # Timing time_start = datetime.datetime.now() # Django gets special treatment. if not settings.DJANGO_SETTINGS: # The app module app_module = importlib.import_module(settings.APP_MODULE) # The application app_function = getattr(app_module, settings.APP_FUNCTION) trailing_slash = False else: try: # Support both for tests from zappa.ext.django import get_django_wsgi except ImportError as e: # pragma: no cover from django_zappa_app import get_django_wsgi # Get the Django WSGI app from our extension app_function = get_django_wsgi(settings.DJANGO_SETTINGS) trailing_slash = True app = ZappaWSGIMiddleware(app_function) # This is a normal HTTP request if event.get('method', None): # If we just want to inspect this, # return this event instead of processing the request # https://your_api.aws-api.com/?event_echo=true event_echo = getattr(settings, "EVENT_ECHO", True) if event_echo and 'event_echo' in event['params'].values(): return {'Content': str(event) + '\n' + str(context), 'Status': 200} if settings.DOMAIN: # If we're on a domain, we operate normally script_name = '' else: # But if we're not, then our base URL # will be something like # https://blahblahblah.execute-api.us-east-1.amazonaws.com/dev # So, we need to make sure the WSGI app knows this. script_name = '/' + settings.API_STAGE # Create the environment for WSGI and handle the request environ = create_wsgi_request(event, script_name=script_name, trailing_slash=trailing_slash ) # We are always on https on Lambda, so tell our wsgi app that. environ['HTTPS'] = 'on' environ['wsgi.url_scheme'] = 'https' environ['lambda.context'] = context # Execute the application response = Response.from_app(app, environ) # This is the object we're going to return. # Pack the WSGI response into our special dictionary. zappa_returndict = dict(response.headers) if 'Content' not in zappa_returndict and response.data: zappa_returndict['Content'] = response.data zappa_returndict['Status'] = response.status_code # To ensure correct status codes, we need to # pack the response as a deterministic B64 string and raise it # as an error to match our APIGW regex. # The DOCTYPE ensures that the page still renders in the browser. exception = None if response.status_code in ERROR_CODES: content = u"<!DOCTYPE html>" + unicode(response.status_code) + unicode('<meta charset="utf-8" />') + response.data.encode('utf-8') exception = base64.b64encode(content) # Internal are changed to become relative redirects # so they still work for apps on raw APIGW and on a domain. elif 300 <= response.status_code < 400 and hasattr(response, 'Location'): # Location is by default relative on Flask. Location is by default # absolute on Werkzeug. We can set autocorrect_location_header on # the response to False, but it doesn't work. We have to manually # remove the host part. location = response.location hostname = 'https://' + environ['HTTP_HOST'] if location.startswith(hostname): exception = location[len(hostname):] # Calculate the total response time, # and log it in the Common Log format. time_end = datetime.datetime.now() delta = time_end - time_start response_time_ms = delta.total_seconds() * 1000 response.content = response.data common_log(environ, response, response_time=response_time_ms) # Finally, return the response to API Gateway. if exception: # pragma: no cover raise LambdaException(exception) else: return zappa_returndict except LambdaException as e: # pragma: no cover raise e except Exception as e: # pragma: no cover # Print statements are visible in the logs either way print(e) # If we didn't even build an app_module, just raise. if not settings.DJANGO_SETTINGS: try: app_module except NameError: raise e # Print the error to the browser upon failure? if settings.DEBUG: # Return this unspecified exception as a 500. content = "<!DOCTYPE html>500. From Zappa: <pre>" + str(e) + "</pre><br /><pre>" + traceback.format_exc().replace('\n', '<br />') + "</pre>" exception = base64.b64encode(content) raise Exception(exception) else: raise e
def __init__(self, settings_name="zappa_settings", session=None): # We haven't cached our settings yet, load the settings and app. if not self.settings: # Loading settings from a python module self.settings = importlib.import_module(settings_name) self.settings_name = settings_name self.session = session # Custom log level if self.settings.LOG_LEVEL: level = logging.getLevelName(self.settings.LOG_LEVEL) logger.setLevel(level) remote_env = getattr(self.settings, 'REMOTE_ENV', None) remote_bucket, remote_file = parse_s3_url(remote_env) if remote_bucket and remote_file: self.load_remote_settings(remote_bucket, remote_file) # Let the system know that this will be a Lambda/Zappa/Stack os.environ["SERVERTYPE"] = "AWS Lambda" os.environ["FRAMEWORK"] = "Zappa" try: os.environ["PROJECT"] = self.settings.PROJECT_NAME os.environ["STAGE"] = self.settings.API_STAGE except Exception: # pragma: no cover pass # Set any locally defined env vars # Environement variable keys can't be Unicode # https://github.com/Miserlou/Zappa/issues/604 for key in self.settings.ENVIRONMENT_VARIABLES.keys(): os.environ[str(key)] = self.settings.ENVIRONMENT_VARIABLES[key] # Pulling from S3 if given a zip path project_zip_path = getattr(self.settings, 'ZIP_PATH', None) if project_zip_path: self.load_remote_project_zip(project_zip_path) # Load compliled library to the PythonPath # checks if we are the slim_handler since this is not needed otherwise # https://github.com/Miserlou/Zappa/issues/776 is_slim_handler = getattr(self.settings, 'SLIM_HANDLER', False) if is_slim_handler: included_libraries = getattr(self.settings, 'INCLUDE', ['libmysqlclient.so.18']) try: from ctypes import cdll, util for library in included_libraries: try: cdll.LoadLibrary(os.path.join(os.getcwd(), library)) except OSError: print ("Failed to find library...right filename?") except ImportError: print ("Failed to import cytpes library") # This is a non-WSGI application # https://github.com/Miserlou/Zappa/pull/748 if not hasattr(self.settings, 'APP_MODULE') and not self.settings.DJANGO_SETTINGS: self.app_module = None wsgi_app_function = None # This is probably a normal WSGI app elif not self.settings.DJANGO_SETTINGS: # The app module self.app_module = importlib.import_module(self.settings.APP_MODULE) # The application wsgi_app_function = getattr(self.app_module, self.settings.APP_FUNCTION) self.trailing_slash = False # Django gets special treatment. else: try: # Support both for tests from zappa.ext.django import get_django_wsgi except ImportError: # pragma: no cover from django_zappa_app import get_django_wsgi # Get the Django WSGI app from our extension wsgi_app_function = get_django_wsgi(self.settings.DJANGO_SETTINGS) self.trailing_slash = True self.wsgi_app = ZappaWSGIMiddleware(wsgi_app_function)
def handler(self, event, context): """ An AWS Lambda function which parses specific API Gateway input into a WSGI request, feeds it to our WSGI app, procceses the response, and returns that back to the API Gateway. """ settings = self.settings # If in DEBUG mode, log all raw incoming events. if settings.DEBUG: logger.debug('Zappa Event: {}'.format(event)) # This is the result of a keep alive, recertify # or scheduled event. if event.get('detail-type') == u'Scheduled Event': whole_function = event['resources'][0].split('/')[-1].split( '-')[-1] # This is a scheduled function. if '.' in whole_function: app_function = self.import_module_and_get_function( whole_function) # Execute the function! return self.run_function(app_function, event, context) # Else, let this execute as it were. # This is a direct command invocation. elif event.get('command', None): whole_function = event['command'] app_function = self.import_module_and_get_function(whole_function) result = self.run_function(app_function, event, context) print("Result of %s:" % whole_function) print(result) return result # This is a direct, raw python invocation. # It's _extremely_ important we don't allow this event source # to be overriden by unsanitized, non-admin user input. elif event.get('raw_command', None): raw_command = event['raw_command'] exec(raw_command) return # This is a Django management command invocation. elif event.get('manage', None): from django.core import management try: # Support both for tests from zappa.ext.django_zappa import get_django_wsgi except ImportError as e: # pragma: no cover from django_zappa_app import get_django_wsgi # Get the Django WSGI app from our extension # We don't actually need the function, # but we do need to do all of the required setup for it. app_function = get_django_wsgi(self.settings.DJANGO_SETTINGS) # Couldn't figure out how to get the value into stdout with StringIO.. # Read the log for now. :[] management.call_command(*event['manage'].split(' ')) return {} # This is an AWS-event triggered invokation. elif event.get('Records', None): records = event.get('Records') result = None whole_function = self.get_function_for_aws_event(records[0]) if whole_function: app_function = self.import_module_and_get_function( whole_function) result = self.run_function(app_function, event, context) logger.debug(result) else: logger.error( "Cannot find a function to process the triggered event.") return result # This is an API Gateway authorizer event elif event.get('type') == u'TOKEN': whole_function = self.settings.AUTHORIZER_FUNCTION if whole_function: app_function = self.import_module_and_get_function( whole_function) policy = self.run_function(app_function, event, context) return policy else: logger.error( "Cannot find a function to process the authorization request." ) raise Exception('Unauthorized') # Normal web app flow try: # Timing time_start = datetime.datetime.now() # This is a normal HTTP request if event.get('httpMethod', None): # If we just want to inspect this, # return this event instead of processing the request # https://your_api.aws-api.com/?event_echo=true # event_echo = getattr(settings, "EVENT_ECHO", True) # if event_echo and 'event_echo' in event['params'].values(): # return {'Content': str(event) + '\n' + str(context), 'Status': 200} if settings.DOMAIN: # If we're on a domain, we operate normally script_name = '' else: # But if we're not, then our base URL # will be something like # https://blahblahblah.execute-api.us-east-1.amazonaws.com/dev # So, we need to make sure the WSGI app knows this. script_name = '/' + settings.API_STAGE # Create the environment for WSGI and handle the request environ = create_wsgi_request( event, script_name=script_name, trailing_slash=self.trailing_slash) # We are always on https on Lambda, so tell our wsgi app that. environ['HTTPS'] = 'on' environ['wsgi.url_scheme'] = 'https' environ['lambda.context'] = context # Execute the application response = Response.from_app(self.wsgi_app, environ) # This is the object we're going to return. # Pack the WSGI response into our special dictionary. zappa_returndict = dict() if response.data: zappa_returndict['body'] = response.data zappa_returndict['statusCode'] = response.status_code zappa_returndict['headers'] = {} for key, value in response.headers: zappa_returndict['headers'][key] = value # To ensure correct status codes, we need to # pack the response as a deterministic B64 string and raise it # as an error to match our APIGW regex. # The DOCTYPE ensures that the page still renders in the browser. exception = None # this variable never used # if response.status_code in ERROR_CODES: # content = collections.OrderedDict() # content['http_status'] = response.status_code # content['content'] = base64.b64encode(response.data.encode('utf-8')) # exception = json.dumps(content) # # Internal are changed to become relative redirects # # so they still work for apps on raw APIGW and on a domain. # elif 300 <= response.status_code < 400 and hasattr(response, 'Location'): # # Location is by default relative on Flask. Location is by default # # absolute on Werkzeug. We can set autocorrect_location_header on # # the response to False, but it doesn't work. We have to manually # # remove the host part. # location = response.location # hostname = 'https://' + environ['HTTP_HOST'] # if location.startswith(hostname): # exception = location[len(hostname):] # else: # exception = location # Calculate the total response time, # and log it in the Common Log format. time_end = datetime.datetime.now() delta = time_end - time_start response_time_ms = delta.total_seconds() * 1000 response.content = response.data common_log(environ, response, response_time=response_time_ms) return zappa_returndict except Exception as e: # pragma: no cover # Print statements are visible in the logs either way print(e) exc_info = sys.exc_info() message = ( 'An uncaught exception happened while servicing this request. ' 'You can investigate this with the `zappa tail` command.') # If we didn't even build an app_module, just raise. if not settings.DJANGO_SETTINGS: try: self.app_module except NameError as ne: message = 'Failed to import module: {}'.format(ne.message) # Return this unspecified exception as a 500, using template that API Gateway expects. content = collections.OrderedDict() content['statusCode'] = 500 body = {'message': message} if settings.DEBUG: # only include traceback if debug is on. body['traceback'] = traceback.format_exception( *exc_info) # traceback as a list for readability. content['body'] = json.dumps(body, sort_keys=True, indent=4).encode('utf-8') return content
def handler(self, event, context): """ An AWS Lambda function which parses specific API Gateway input into a WSGI request, feeds it to our WSGI app, procceses the response, and returns that back to the API Gateway. """ settings = self.settings # If in DEBUG mode, log all raw incoming events. if settings.DEBUG: print('Zappa Event: {}'.format(event)) logger.debug('Zappa Event: {}'.format(event)) # This is the result of a keep alive, recertify # or scheduled event. if event.get('detail-type') == u'Scheduled Event': whole_function = event['resources'][0].split('/')[-1].split('-')[-1] # This is a scheduled function. if '.' in whole_function: app_function = self.import_module_and_get_function(whole_function) # Execute the function! return self.run_function(app_function, event, context) # Else, let this execute as it were. # This is a direct command invocation. elif event.get('command', None): whole_function = event['command'] app_function = self.import_module_and_get_function(whole_function) result = self.run_function(app_function, event, context) print("Result of %s:" % whole_function) print(result) return result # This is a direct, raw python invocation. # It's _extremely_ important we don't allow this event source # to be overriden by unsanitized, non-admin user input. elif event.get('raw_command', None): raw_command = event['raw_command'] exec(raw_command) return # This is a Django management command invocation. elif event.get('manage', None): from django.core import management try: # Support both for tests from zappa.ext.django_zappa import get_django_wsgi except ImportError as e: # pragma: no cover from django_zappa_app import get_django_wsgi # Get the Django WSGI app from our extension # We don't actually need the function, # but we do need to do all of the required setup for it. app_function = get_django_wsgi(self.settings.DJANGO_SETTINGS) # Couldn't figure out how to get the value into stdout with StringIO.. # Read the log for now. :[] management.call_command(*event['manage'].split(' ')) return {} # This is an AWS-event triggered invokation. elif event.get('Records', None): records = event.get('Records') result = None whole_function = self.get_function_for_aws_event(records[0]) if whole_function: app_function = self.import_module_and_get_function(whole_function) result = self.run_function(app_function, event, context) logger.debug(result) else: logger.error("Cannot find a function to process the triggered event.") return result # This is an API Gateway authorizer event elif event.get('type') == u'TOKEN': whole_function = self.settings.AUTHORIZER_FUNCTION if whole_function: app_function = self.import_module_and_get_function(whole_function) policy = self.run_function(app_function, event, context) return policy else: logger.error("Cannot find a function to process the authorization request.") raise Exception('Unauthorized') # Normal web app flow try: # Timing time_start = datetime.datetime.now() # This is a normal HTTP request if event.get('httpMethod', None): # If we just want to inspect this, # return this event instead of processing the request # https://your_api.aws-api.com/?event_echo=true # event_echo = getattr(settings, "EVENT_ECHO", True) # if event_echo and 'event_echo' in event['params'].values(): # return {'Content': str(event) + '\n' + str(context), 'Status': 200} if settings.DOMAIN: # If we're on a domain, we operate normally script_name = '' else: # But if we're not, then our base URL # will be something like # https://blahblahblah.execute-api.us-east-1.amazonaws.com/dev # So, we need to make sure the WSGI app knows this. script_name = '/' + settings.API_STAGE # Create the environment for WSGI and handle the request environ = create_wsgi_request( event, script_name=script_name, trailing_slash=self.trailing_slash ) # We are always on https on Lambda, so tell our wsgi app that. environ['HTTPS'] = 'on' environ['wsgi.url_scheme'] = 'https' environ['lambda.context'] = context # Execute the application response = Response.from_app(self.wsgi_app, environ) # This is the object we're going to return. # Pack the WSGI response into our special dictionary. zappa_returndict = dict() if response.data: zappa_returndict['body'] = response.data zappa_returndict['statusCode'] = response.status_code zappa_returndict['headers'] = {} for key, value in response.headers: zappa_returndict['headers'][key] = value # To ensure correct status codes, we need to # pack the response as a deterministic B64 string and raise it # as an error to match our APIGW regex. # The DOCTYPE ensures that the page still renders in the browser. exception = None # this variable never used # if response.status_code in ERROR_CODES: # content = collections.OrderedDict() # content['http_status'] = response.status_code # content['content'] = base64.b64encode(response.data.encode('utf-8')) # exception = json.dumps(content) # # Internal are changed to become relative redirects # # so they still work for apps on raw APIGW and on a domain. # elif 300 <= response.status_code < 400 and hasattr(response, 'Location'): # # Location is by default relative on Flask. Location is by default # # absolute on Werkzeug. We can set autocorrect_location_header on # # the response to False, but it doesn't work. We have to manually # # remove the host part. # location = response.location # hostname = 'https://' + environ['HTTP_HOST'] # if location.startswith(hostname): # exception = location[len(hostname):] # else: # exception = location # Calculate the total response time, # and log it in the Common Log format. time_end = datetime.datetime.now() delta = time_end - time_start response_time_ms = delta.total_seconds() * 1000 response.content = response.data common_log(environ, response, response_time=response_time_ms) return zappa_returndict except Exception as e: # pragma: no cover # Print statements are visible in the logs either way print(e) exc_info = sys.exc_info() message = ('An uncaught exception happened while servicing this request. ' 'You can investigate this with the `zappa tail` command.') # If we didn't even build an app_module, just raise. if not settings.DJANGO_SETTINGS: try: self.app_module except NameError as ne: message = 'Failed to import module: {}'.format(ne.message) # Return this unspecified exception as a 500, using template that API Gateway expects. content = collections.OrderedDict() content['statusCode'] = 500 body = {'message': message} if settings.DEBUG: # only include traceback if debug is on. body['traceback'] = traceback.format_exception(*exc_info) # traceback as a list for readability. content['body'] = json.dumps(body, sort_keys=True, indent=4).encode('utf-8') return content
def handler(self, event, context): """ An AWS Lambda function which parses specific API Gateway input into a WSGI request, feeds it to our WSGI app, procceses the response, and returns that back to the API Gateway. """ settings = self.settings # If in DEBUG mode, log all raw incoming events. if settings.DEBUG: print('Zappa Event: {}'.format(event)) logger.debug('Zappa Event: {}'.format(event)) # Custom log level if settings.LOG_LEVEL: level = logging.getLevelName(settings.LOG_LEVEL) logger.setLevel(level) # This is the result of a keep alive # or scheduled event. if event.get('detail-type') == u'Scheduled Event': whole_function = event['resources'][0].split('/')[-1].split( '-')[-1] # This is a scheduled, non-keep-alive function. # This is not the best way to do this but it'll do. if '.' in whole_function: app_function = self.import_module_and_get_function( whole_function) # Execute the function! return self.run_function(app_function, event, context) # Else, let this execute as it were. # This is a direct command invocation. elif event.get('command', None): whole_function = event['command'] app_function = self.import_module_and_get_function(whole_function) result = self.run_function(app_function, event, context) print("Result of %s:" % whole_function) print(result) return result # This is a Django management command invocation. elif event.get('manage', None): from django.core import management try: # Support both for tests from zappa.ext.django import get_django_wsgi except ImportError as e: # pragma: no cover from django_zappa_app import get_django_wsgi # Get the Django WSGI app from our extension # We don't actually need the function, # but we do need to do all of the required setup for it. app_function = get_django_wsgi(self.settings.DJANGO_SETTINGS) # Couldn't figure out how to get the value into stdout with StringIO.. # Read the log for now. :[] management.call_command(*event['manage'].split(' ')) return {} # This is an AWS-event triggered invokation. elif event.get('Records', None): records = event.get('Records') event_types = ['dynamodb', 'kinesis', 's3', 'sns', 'events'] for record in records: for event_type in event_types: if record.has_key(event_type): whole_function = record[event_type]['configurationId'] app_function = self.import_module_and_get_function( whole_function) result = self.run_function(app_function, event, context) print(result) return result try: # Timing time_start = datetime.datetime.now() # Django gets special treatment. if not settings.DJANGO_SETTINGS: # The app module app_module = importlib.import_module(settings.APP_MODULE) # The application app_function = getattr(app_module, settings.APP_FUNCTION) trailing_slash = False else: try: # Support both for tests from zappa.ext.django import get_django_wsgi except ImportError as e: # pragma: no cover from django_zappa_app import get_django_wsgi # Get the Django WSGI app from our extension app_function = get_django_wsgi(settings.DJANGO_SETTINGS) trailing_slash = True app = ZappaWSGIMiddleware(app_function) # This is a normal HTTP request if event.get('method', None): # If we just want to inspect this, # return this event instead of processing the request # https://your_api.aws-api.com/?event_echo=true event_echo = getattr(settings, "EVENT_ECHO", True) if event_echo and 'event_echo' in event['params'].values(): return { 'Content': str(event) + '\n' + str(context), 'Status': 200 } if settings.DOMAIN: # If we're on a domain, we operate normally script_name = '' else: # But if we're not, then our base URL # will be something like # https://blahblahblah.execute-api.us-east-1.amazonaws.com/dev # So, we need to make sure the WSGI app knows this. script_name = '/' + settings.API_STAGE # Create the environment for WSGI and handle the request environ = create_wsgi_request(event, script_name=script_name, trailing_slash=trailing_slash) # We are always on https on Lambda, so tell our wsgi app that. environ['HTTPS'] = 'on' environ['wsgi.url_scheme'] = 'https' environ['lambda.context'] = context # Execute the application response = Response.from_app(app, environ) # This is the object we're going to return. # Pack the WSGI response into our special dictionary. zappa_returndict = dict(response.headers) if 'Content' not in zappa_returndict and response.data: zappa_returndict['Content'] = response.data zappa_returndict['Status'] = response.status_code # To ensure correct status codes, we need to # pack the response as a deterministic B64 string and raise it # as an error to match our APIGW regex. # The DOCTYPE ensures that the page still renders in the browser. exception = None if response.status_code in ERROR_CODES: content = u"<!DOCTYPE html>" + unicode( response.status_code) + unicode( '<meta charset="utf-8" />') + response.data.encode( 'utf-8') exception = base64.b64encode(content) # Internal are changed to become relative redirects # so they still work for apps on raw APIGW and on a domain. elif 300 <= response.status_code < 400 and hasattr( response, 'Location'): # Location is by default relative on Flask. Location is by default # absolute on Werkzeug. We can set autocorrect_location_header on # the response to False, but it doesn't work. We have to manually # remove the host part. location = response.location hostname = 'https://' + environ['HTTP_HOST'] if location.startswith(hostname): exception = location[len(hostname):] # Calculate the total response time, # and log it in the Common Log format. time_end = datetime.datetime.now() delta = time_end - time_start response_time_ms = delta.total_seconds() * 1000 response.content = response.data common_log(environ, response, response_time=response_time_ms) # Finally, return the response to API Gateway. if exception: # pragma: no cover raise LambdaException(exception) else: return zappa_returndict except LambdaException as e: # pragma: no cover raise e except Exception as e: # pragma: no cover # Print statements are visible in the logs either way print(e) # If we didn't even build an app_module, just raise. if not settings.DJANGO_SETTINGS: try: app_module except NameError: raise e # Print the error to the browser upon failure? if settings.DEBUG: # Return this unspecified exception as a 500. content = "<!DOCTYPE html>500. From Zappa: <pre>" + str( e) + "</pre><br /><pre>" + traceback.format_exc().replace( '\n', '<br />') + "</pre>" exception = base64.b64encode(content) raise Exception(exception) else: raise e