def now_handler(event, context): payload = json.loads(event['body']) headers = Headers(payload.get('headers', {})) body = payload.get('body', '') if body != '': if payload.get('encoding') == 'base64': body = base64.b64decode(body) if isinstance(body, string_types): body = to_bytes(body, charset='utf-8') path = unquote(payload['path']) query = urlparse(path).query environ = { 'CONTENT_LENGTH': str(len(body)), 'CONTENT_TYPE': headers.get('content-type', ''), 'PATH_INFO': path, 'QUERY_STRING': query, 'REMOTE_ADDR': headers.get( 'x-forwarded-for', headers.get( 'x-real-ip', payload.get( 'true-client-ip', ''))), 'REQUEST_METHOD': payload['method'], 'SERVER_NAME': headers.get('host', 'lambda'), 'SERVER_PORT': headers.get('x-forwarded-port', '80'), 'SERVER_PROTOCOL': 'HTTP/1.1', 'event': event, 'context': context, 'wsgi.errors': sys.stderr, 'wsgi.input': BytesIO(body), 'wsgi.multiprocess': False, 'wsgi.multithread': False, 'wsgi.run_once': False, 'wsgi.url_scheme': headers.get('x-forwarded-proto', 'http'), 'wsgi.version': (1, 0), } for key, value in environ.items(): if isinstance(value, string_types) and key != 'QUERY_STRING': environ[key] = wsgi_encoding_dance(value) for key, value in headers.items(): key = 'HTTP_' + key.upper().replace('-', '_') if key not in ('HTTP_CONTENT_TYPE', 'HTTP_CONTENT_LENGTH'): environ[key] = value response = Response.from_app(__NOW_HANDLER_FILENAME.app, environ) return_dict = { 'statusCode': response.status_code, 'headers': dict(response.headers) } if response.data: return_dict['body'] = base64.b64encode(response.data).decode('utf-8') return_dict['encoding'] = 'base64' return return_dict
def _create_headers_dictionary_flask( self, headers: Headers) -> Mapping[str, List[str]]: headers_dict: Mapping[str, List[str]] = defaultdict(list) for (key, value) in headers.items(): headers_dict[key].append(value) return dict(headers_dict)
def handle_request(application, event, context): if u"multiValueHeaders" in event: headers = Headers(event["multiValueHeaders"]) else: headers = Headers(event["headers"]) strip_stage_path = os.environ.get("STRIP_STAGE_PATH", "").lower().strip() in [ "yes", "y", "true", "t", "1", ] if u"apigw.tencentcs.com" in headers.get(u"Host", u"") and not strip_stage_path: script_name = "/{}".format(event["requestContext"].get(u"stage", "")) else: script_name = "" path_info = event["path"] base_path = os.environ.get("API_GATEWAY_BASE_PATH") if base_path: script_name = "/" + base_path if path_info.startswith(script_name): path_info = path_info[len(script_name):] or "/" if u"body" in event: body = event[u"body"] or "" else: body = "" if event.get("isBase64Encoded", False): body = base64.b64decode(body) if isinstance(body, string_types): body = to_bytes(body, charset="utf-8") environ = { "CONTENT_LENGTH": str(len(body)), "CONTENT_TYPE": headers.get(u"Content-Type", ""), "PATH_INFO": url_unquote(path_info), "QUERY_STRING": encode_query_string(event), "REMOTE_ADDR": event["requestContext"].get(u"identity", {}).get(u"sourceIp", ""), "REMOTE_USER": event["requestContext"].get(u"authorizer", {}).get(u"principalId", ""), "REQUEST_METHOD": event["httpMethod"], "SCRIPT_NAME": script_name, "SERVER_NAME": headers.get(u"Host", "lambda"), "SERVER_PORT": headers.get(u"X-Forwarded-Port", "80"), "SERVER_PROTOCOL": "HTTP/1.1", "wsgi.errors": sys.stderr, "wsgi.input": BytesIO(body), "wsgi.multiprocess": False, "wsgi.multithread": False, "wsgi.run_once": False, "wsgi.url_scheme": headers.get(u"X-Forwarded-Proto", "http"), "wsgi.version": (1, 0), "serverless.authorizer": event["requestContext"].get(u"authorizer"), "serverless.event": event, "serverless.context": context, # TODO: Deprecate the following entries, as they do not comply with the WSGI # spec. For custom variables, the spec says: # # Finally, the environ dictionary may also contain server-defined variables. # These variables should be named using only lower-case letters, numbers, dots, # and underscores, and should be prefixed with a name that is unique to the # defining server or gateway. "API_GATEWAY_AUTHORIZER": event["requestContext"].get(u"authorizer"), "event": event, "context": context, } for key, value in environ.items(): if isinstance(value, string_types): environ[key] = wsgi_encoding_dance(value) for key, value in headers.items(): key = "HTTP_" + key.upper().replace("-", "_") if key not in ("HTTP_CONTENT_TYPE", "HTTP_CONTENT_LENGTH"): environ[key] = value response = Response.from_app(application, environ) returndict = {u"statusCode": response.status_code} if u"multiValueHeaders" in event: returndict["multiValueHeaders"] = group_headers(response.headers) else: returndict["headers"] = split_headers(response.headers) if event.get("requestContext").get("elb"): # If the request comes from ALB we need to add a status description returndict["statusDescription"] = u"%d %s" % ( response.status_code, HTTP_STATUS_CODES[response.status_code], ) if response.data: mimetype = response.mimetype or "text/plain" if (mimetype.startswith("text/") or mimetype in TEXT_MIME_TYPES) and not response.headers.get( "Content-Encoding", ""): returndict["body"] = response.get_data(as_text=True) returndict["isBase64Encoded"] = False else: returndict["body"] = base64.b64encode( response.data).decode("utf-8") returndict["isBase64Encoded"] = True return returndict
def encode_headers(headers: Headers) -> List[Tuple[bytes, bytes]]: return [(key.lower().encode(), value.encode()) for key, value in headers.items()]
def handle_request(app, event, context): if event.get("source") in ["aws.events", "serverless-plugin-warmup"]: return {} if u"multiValueHeaders" in event: headers = Headers(event[u"multiValueHeaders"]) else: headers = Headers(event[u"headers"]) if u"amazonaws.com" in headers.get(u"Host", u""): script_name = "/{}".format(event[u"requestContext"].get(u"stage", "")) else: script_name = "" # If a user is using a custom domain on API Gateway, they may have a base # path in their URL. This allows us to strip it out via an optional # environment variable. path_info = event[u"path"] base_path = os.environ.get("API_GATEWAY_BASE_PATH", "") if base_path: script_name = "/" + base_path if path_info.startswith(script_name): path_info = path_info[len(script_name):] body = event[u"body"] or "" if event.get("isBase64Encoded", False): body = base64.b64decode(body) if isinstance(body, string_types): body = to_bytes(body, charset="utf-8") environ = { "CONTENT_LENGTH": str(len(body)), "CONTENT_TYPE": headers.get(u"Content-Type", ""), "PATH_INFO": path_info, "QUERY_STRING": encode_query_string(event), "REMOTE_ADDR": event[u"requestContext"].get(u"identity", {}).get(u"sourceIp", ""), "REMOTE_USER": event[u"requestContext"].get(u"authorizer", {}).get(u"principalId", ""), "REQUEST_METHOD": event[u"httpMethod"], "SCRIPT_NAME": script_name, "SERVER_NAME": headers.get(u"Host", "lambda"), "SERVER_PORT": headers.get(u"X-Forwarded-Port", "80"), "SERVER_PROTOCOL": "HTTP/1.1", "wsgi.errors": sys.stderr, "wsgi.input": BytesIO(body), "wsgi.multiprocess": False, "wsgi.multithread": False, "wsgi.run_once": False, "wsgi.url_scheme": headers.get(u"X-Forwarded-Proto", "http"), "wsgi.version": (1, 0), "API_GATEWAY_AUTHORIZER": event[u"requestContext"].get(u"authorizer"), "event": event, "context": context, } for key, value in environ.items(): if isinstance(value, string_types): environ[key] = wsgi_encoding_dance(value) for key, value in headers.items(): key = "HTTP_" + key.upper().replace("-", "_") if key not in ("HTTP_CONTENT_TYPE", "HTTP_CONTENT_LENGTH"): environ[key] = value response = Response.from_app(app, environ) returndict = {u"statusCode": response.status_code} if u"multiValueHeaders" in event: returndict[u"multiValueHeaders"] = group_headers(response.headers) else: returndict[u"headers"] = split_headers(response.headers) if event.get("requestContext").get("elb"): # If the request comes from ALB we need to add a status description returndict["statusDescription"] = u"%d %s" % ( response.status_code, HTTP_STATUS_CODES[response.status_code], ) if response.data: mimetype = response.mimetype or "text/plain" if (mimetype.startswith("text/") or mimetype in TEXT_MIME_TYPES) and not response.headers.get( "Content-Encoding", ""): returndict["body"] = response.get_data(as_text=True) returndict["isBase64Encoded"] = False else: returndict["body"] = base64.b64encode( response.data).decode("utf-8") returndict["isBase64Encoded"] = True return returndict
def handler(event, context): headers = Headers(event[u'headers']) if headers.get(u'Host', u'').endswith(u'.amazonaws.com'): script_name = '/{}'.format(event[u'requestContext'].get(u'stage', '')) else: script_name = '' # If a user is using a custom domain on API Gateway, they may have a base # path in their URL. This allows us to strip it out via an optional # environment variable. path_info = event[u'path'] base_path = os.environ.get('API_GATEWAY_BASE_PATH', '') if base_path: script_name = '/' + base_path if path_info.startswith(script_name): path_info = path_info[len(script_name):] body = event[u'body'] or '' if event.get('isBase64Encoded', False): body = base64.b64decode(body) if isinstance(body, string_types): body = to_bytes(wsgi_encoding_dance(body)) environ = { 'API_GATEWAY_AUTHORIZER': event[u'requestContext'].get(u'authorizer', None), 'CONTENT_LENGTH': str(len(body)), 'CONTENT_TYPE': headers.get(u'Content-Type', ''), 'PATH_INFO': path_info, 'QUERY_STRING': url_encode(event.get(u'queryStringParameters', None) or {}), 'REMOTE_ADDR': event[u'requestContext'].get(u'identity', {}).get(u'sourceIp', ''), 'REMOTE_USER': event[u'requestContext'].get(u'authorizer', {}).get(u'principalId', ''), 'REQUEST_METHOD': event[u'httpMethod'], 'SCRIPT_NAME': script_name, 'SERVER_NAME': headers.get(u'Host', 'lambda'), 'SERVER_PORT': headers.get(u'X-Forwarded-Port', '80'), 'SERVER_PROTOCOL': 'HTTP/1.1', 'event': event, 'context': context, 'wsgi.errors': sys.stderr, 'wsgi.input': BytesIO(body), 'wsgi.multiprocess': False, 'wsgi.multithread': False, 'wsgi.run_once': False, 'wsgi.url_scheme': headers.get(u'X-Forwarded-Proto', 'http'), 'wsgi.version': (1, 0), } for key, value in environ.items(): if isinstance(value, string_types): environ[key] = wsgi_encoding_dance(value) for key, value in headers.items(): key = 'HTTP_' + key.upper().replace('-', '_') if key not in ('HTTP_CONTENT_TYPE', 'HTTP_CONTENT_LENGTH'): environ[key] = value response = Response.from_app(wsgi_app, environ) # If there are multiple Set-Cookie headers, create case-mutated variations # in order to pass them through APIGW. This is a hack that's currently # needed. See: https://github.com/logandk/serverless-wsgi/issues/11 # Source: https://github.com/Miserlou/Zappa/blob/master/zappa/middleware.py new_headers = [x for x in response.headers if x[0] != 'Set-Cookie'] cookie_headers = [x for x in response.headers if x[0] == 'Set-Cookie'] if len(cookie_headers) > 1: for header, new_name in zip(cookie_headers, all_casings('Set-Cookie')): new_headers.append((new_name, header[1])) elif len(cookie_headers) == 1: new_headers.extend(cookie_headers) returndict = { u'statusCode': response.status_code, u'headers': dict(new_headers) } if response.data: mimetype = response.mimetype or 'text/plain' if ((mimetype.startswith('text/') or mimetype in TEXT_MIME_TYPES) and not response.headers.get('Content-Encoding', '')): returndict['body'] = response.get_data(as_text=True) else: returndict['body'] = base64.b64encode( response.data).decode('utf-8') returndict['isBase64Encoded'] = 'true' return returndict
class Response(object): """ A response is responsible (no pun intended) for delivering data to the client, again. The method :meth:`to_bytes` transforms this into a bytes response. :ivar code: The response code. :ivar cookies: The cookies to send down with the response. :ivar body: The string or bytes body of the request. :ivar headers: A dict of headers for the response. :ivar request: The request object this Response is handling. This is automatically set inside Kyoukai. """ def __init__(self, code: int, body: typing.Union[str, bytes], headers: dict = None): """ Create a new response. """ self.code = code self.cookies = SimpleCookie() self.body = body self.headers = Headers(headers) if headers else Headers() self._should_gzip = False self._is_head = False self.request = None @property def gzip(self): """ :return: If the request is to be gzip compressed. Note: This will always return False on a newly created Response, unless the ``request`` instance variable is set on the Response. """ if self.request: return ('gzip' in self.request.headers.get( "Accept-Encoding", "")) and self._should_gzip else: return False # return self._should_gzip @gzip.setter def gzip(self, value): self._should_gzip = value # Utility functions. def get_compressed_body(self, body: bytes) -> bytes: """ Returns the compressed body, if gzip is enabled. Otherwise, returns the normal body. :param body: The body to compress. :return: The compressed body, or just the body. """ if self.gzip: return gzip.compress(body, 5) else: return body def get_response_http_version(self): """ Gets what HTTP version the response should use. """ if self.request: return self.request.version else: return "1.0" def _mimetype(self, body): """ Calculates the mime type of the response, using libmagic. This is an **internal method**. """ if _has_magic: # libmagic is unreliable when the body is str. # so encode it to make sure it returns correctly if isinstance(self.body, str): mime = magic.from_buffer(body.encode(), mime=True) else: mime = magic.from_buffer(body, mime=True) if mime: return mime.decode() if isinstance(mime, bytes) else mime else: return "empty" else: return "text/plain" def _recalculate_headers(self): """ Override certain headers, like Content-Size. This is an **internal method**. """ if not self._is_head: # The +2 is for the \r\n at the end. self.headers["Content-Length"] = len(self.body) + 2 if 'Content-Type' not in self.headers: self.headers["Content-Type"] = self._mimetype( self.body) or "text/plain" # If it's gzip enabled, add the gzip header. if self.gzip: self.headers["Content-Encoding"] = "gzip" # Set cookies. self.headers["Date"] = formatdate() self.headers[ "Server"] = "Kyoukai/{} (see https://github.com/SunDwarf/Kyoukai)".format( util.VERSION) self.headers["X-Powered-By"] = "Kyoukai" def to_bytes(self): """ Serialize a Response into :class:`bytes` to return and send to the client. :return: The encoded data for the response. """ if self.request: if self.request.method.lower() == "head": self._is_head = True version = self.get_response_http_version() if isinstance(self.body, str): self.body = self.body.encode() elif isinstance(self.body, bytes): pass else: self.body = str(self.body).encode() if self.gzip: if 'Content-Type' not in self.headers: self.headers["Content-Type"] = self._mimetype(self.body) self.body = self.get_compressed_body(self.body) # Re-calculate headers to update everything as appropriate. self._recalculate_headers() fmt = "HTTP/{version} {code} {msg}\r\n{headers}{cookies}\r\n" headers_fmt = "" # Calculate headers for name, val in self.headers.items(): headers_fmt += "{}: {}\r\n".format(name, val) # Get the HTTP code. code = http.HTTPStatus(self.code).name.replace("_", " ") built = fmt.format(code=self.code, msg=code, headers=headers_fmt, cookies=(self.cookies.output() + "\r\n") if len(self.cookies) else "", version=version) # Encode the built string so far. built = built.encode() # Append the body, plus the terminator. built += self.body + b"\r\n" return built @classmethod def redirect(cls, location, code=302): """ Creates a new Response that redirects to a specific location. :param location: The location to redirect to. :param code: The code (usually a 301 or 302) to add to the response. :return: A new :class:`Response` for the redirect. """ # https://github.com/pallets/werkzeug/blob/master/werkzeug/utils.py#L373 # response body used from Werkzeug res = cls( code=code, body='<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">\n' '<title>Redirecting...</title>\n' '<h1>Redirecting...</h1>\n' '<p>You should be redirected automatically to target URL: ' '<a href="{location}">{location}</a>. If not click the link.'. format(location=location), headers={"Location": location}) return res
def handler(app, lambda_event, context): event = json.loads(lambda_event['body']) headers = Headers(event.get('headers', None)) parsed_url = urlparse(event['path']) body = event.get('body', '') if event.get('isBase64Encoded', False): body = base64.b64decode(body) if isinstance(body, string_types): body = to_bytes(body, charset='utf-8') environ = { 'CONTENT_LENGTH': str(len(body)), 'CONTENT_TYPE': headers.get('Content-Type', ''), 'PATH_INFO': parsed_url.path, 'QUERY_STRING': parsed_url.query, 'REMOTE_ADDR': event.get('x-real-ip', ''), 'REQUEST_METHOD': event['method'], 'SCRIPT_NAME': '', 'SERVER_NAME': headers.get('Host', 'lambda'), 'SERVER_PORT': headers.get('X-Forwarded-Port', '80'), 'SERVER_PROTOCOL': 'HTTP/1.1', 'event': lambda_event['body'], 'wsgi.errors': sys.stderr, 'wsgi.input': BytesIO(body), 'wsgi.multiprocess': False, 'wsgi.multithread': False, 'wsgi.run_once': False, 'wsgi.url_scheme': headers.get('X-Forwarded-Proto', 'http'), 'wsgi.version': (1, 0), } for key, value in environ.items(): if isinstance(value, string_types): environ[key] = wsgi_encoding_dance(value) for key, value in headers.items(): key = 'HTTP_' + key.upper().replace('-', '_') if key not in ('HTTP_CONTENT_TYPE', 'HTTP_CONTENT_LENGTH'): environ[key] = value response = Response.from_app(app, environ) # If there are multiple Set-Cookie headers, create case-mutated variations # in order to pass them through APIGW. This is a hack that's currently # needed. See: https://github.com/logandk/serverless-wsgi/issues/11 # Source: https://github.com/Miserlou/Zappa/blob/master/zappa/middleware.py new_headers = [x for x in response.headers if x[0] != 'Set-Cookie'] cookie_headers = [x for x in response.headers if x[0] == 'Set-Cookie'] if len(cookie_headers) > 1: for header, new_name in zip(cookie_headers, all_casings('Set-Cookie')): new_headers.append((new_name, header[1])) elif len(cookie_headers) == 1: new_headers.extend(cookie_headers) returndict = { 'statusCode': response.status_code, 'headers': dict(new_headers), 'body': '', } if response.data: mimetype = response.mimetype or 'text/plain' if (mimetype.startswith('text/') or mimetype in TEXT_MIME_TYPES) and not response.headers.get( 'Content-Encoding', ''): returndict['body'] = response.get_data(as_text=True) returndict['isBase64Encoded'] = False else: returndict['body'] = base64.b64encode(response.data)\ .decode('utf-8') returndict['isBase64Encoded'] = True return returndict
def handler(app, lambda_event, context): event = json.loads(lambda_event['body']) headers = Headers(event.get('headers', None)) parsed_url = urlparse(event['path']) body = event.get('body', '') encoding = event.get('encoding', None) if encoding == 'base64': body = base64.b64decode(body) else: body = to_bytes(body, charset='utf-8') environ = { 'CONTENT_LENGTH': str(len(body)), 'CONTENT_TYPE': headers.get('Content-Type', ''), 'PATH_INFO': parsed_url.path, 'QUERY_STRING': unquote(parsed_url.query), 'REMOTE_ADDR': event.get('x-real-ip', ''), 'REQUEST_METHOD': event['method'], 'SCRIPT_NAME': '', 'SERVER_NAME': headers.get('Host', 'lambda'), 'SERVER_PORT': headers.get('X-Forwarded-Port', '80'), 'SERVER_PROTOCOL': 'HTTP/1.1', 'event': lambda_event['body'], 'wsgi.errors': sys.stderr, 'wsgi.input': BytesIO(body), 'wsgi.multiprocess': False, 'wsgi.multithread': False, 'wsgi.run_once': False, 'wsgi.url_scheme': headers.get('X-Forwarded-Proto', 'http'), 'wsgi.version': (1, 0), } for key, value in environ.items(): if isinstance(value, string_types): environ[key] = wsgi_encoding_dance(value) for key, value in headers.items(): key = 'HTTP_' + key.upper().replace('-', '_') if key not in ('HTTP_CONTENT_TYPE', 'HTTP_CONTENT_LENGTH'): environ[key] = value response = Response.from_app(app, environ) # Handle multi-value headers headers = {} for key, value in response.headers: if key in headers: current_value = headers[key] if isinstance(current_value, list): headers[key] += [value] else: headers[key] = [current_value, value] else: headers[key] = value returndict = { 'statusCode': response.status_code, 'headers': headers, 'body': '', } if response.data: mimetype = response.mimetype or 'text/plain' if (mimetype.startswith('text/') or mimetype in TEXT_MIME_TYPES) and not response.headers.get( 'Content-Encoding', ''): returndict['body'] = response.get_data(as_text=True) else: returndict['body'] = base64.b64encode(response.data)\ .decode('utf-8') returndict['encoding'] = 'base64' return returndict
def handler(event, context): if event.get("source") in ["aws.events", "serverless-plugin-warmup"]: return {} headers = Headers(event[u"headers"]) if u"amazonaws.com" in headers.get(u"Host", u""): script_name = "/{}".format(event[u"requestContext"].get(u"stage", "")) else: script_name = "" # If a user is using a custom domain on API Gateway, they may have a base # path in their URL. This allows us to strip it out via an optional # environment variable. path_info = event[u"path"] base_path = os.environ.get("API_GATEWAY_BASE_PATH", "") if base_path: script_name = "/" + base_path if path_info.startswith(script_name): path_info = path_info[len(script_name):] body = event[u"body"] or "" if event.get("isBase64Encoded", False): body = base64.b64decode(body) if isinstance(body, string_types): body = to_bytes(body, charset="utf-8") environ = { "API_GATEWAY_AUTHORIZER": event[u"requestContext"].get(u"authorizer"), "CONTENT_LENGTH": str(len(body)), "CONTENT_TYPE": headers.get(u"Content-Type", ""), "PATH_INFO": path_info, "QUERY_STRING": url_encode(event.get(u"queryStringParameters") or {}), "REMOTE_ADDR": event[u"requestContext"].get(u"identity", {}).get(u"sourceIp", ""), "REMOTE_USER": event[u"requestContext"].get(u"authorizer", {}).get(u"principalId", ""), "REQUEST_METHOD": event[u"httpMethod"], "SCRIPT_NAME": script_name, "SERVER_NAME": headers.get(u"Host", "lambda"), "SERVER_PORT": headers.get(u"X-Forwarded-Port", "80"), "SERVER_PROTOCOL": "HTTP/1.1", "event": event, "context": context, "wsgi.errors": sys.stderr, "wsgi.input": BytesIO(body), "wsgi.multiprocess": False, "wsgi.multithread": False, "wsgi.run_once": False, "wsgi.url_scheme": headers.get(u"X-Forwarded-Proto", "http"), "wsgi.version": (1, 0), } for key, value in environ.items(): if isinstance(value, string_types): environ[key] = wsgi_encoding_dance(value) for key, value in headers.items(): key = "HTTP_" + key.upper().replace("-", "_") if key not in ("HTTP_CONTENT_TYPE", "HTTP_CONTENT_LENGTH"): environ[key] = value response = Response.from_app(wsgi_app, environ) # If there are multiple Set-Cookie headers, create case-mutated variations # in order to pass them through APIGW. This is a hack that's currently # needed. See: https://github.com/logandk/serverless-wsgi/issues/11 # Source: https://github.com/Miserlou/Zappa/blob/master/zappa/middleware.py new_headers = [x for x in response.headers if x[0] != "Set-Cookie"] cookie_headers = [x for x in response.headers if x[0] == "Set-Cookie"] if len(cookie_headers) > 1: for header, new_name in zip(cookie_headers, all_casings("Set-Cookie")): new_headers.append((new_name, header[1])) elif len(cookie_headers) == 1: new_headers.extend(cookie_headers) returndict = { u"statusCode": response.status_code, u"headers": dict(new_headers) } if response.data: mimetype = response.mimetype or "text/plain" if (mimetype.startswith("text/") or mimetype in TEXT_MIME_TYPES) and not response.headers.get( "Content-Encoding", ""): returndict["body"] = response.get_data(as_text=True) else: returndict["body"] = base64.b64encode( response.data).decode("utf-8") returndict["isBase64Encoded"] = "true" return returndict
def handler(event, context): headers = Headers(event[u'headers']) if headers.get(u'Host', u'').endswith(u'.amazonaws.com'): script_name = '/{}'.format(event[u'requestContext'].get(u'stage', '')) else: script_name = '' environ = { 'CONTENT_LENGTH': headers.get(u'Content-Length', str(len(event[u'body'] or ''))), 'CONTENT_TYPE': headers.get(u'Content-Type', ''), 'PATH_INFO': event[u'path'], 'QUERY_STRING': url_encode(event.get(u'queryStringParameters', None) or {}), 'REMOTE_ADDR': headers.get(u'X-Forwarded-For', '').split(', ')[0], 'REMOTE_USER': event[u'requestContext'].get(u'authorizer', {}).get(u'principalId', ''), 'REQUEST_METHOD': event[u'httpMethod'], 'SCRIPT_NAME': script_name, 'SERVER_NAME': headers.get(u'Host', 'lambda'), 'SERVER_PORT': headers.get(u'X-Forwarded-Port', '80'), 'SERVER_PROTOCOL': 'HTTP/1.1', 'wsgi.errors': StringIO(), 'wsgi.input': StringIO(wsgi_encoding_dance(event[u'body'] or '')), 'wsgi.multiprocess': False, 'wsgi.multithread': False, 'wsgi.run_once': False, 'wsgi.url_scheme': headers.get(u'X-Forwarded-Proto', 'http'), 'wsgi.version': (1, 0), } for key, value in environ.items(): if isinstance(value, basestring): environ[key] = wsgi_encoding_dance(value) for key, value in headers.items(): key = 'HTTP_' + key.upper().replace('-', '_') if key not in ('HTTP_CONTENT_TYPE', 'HTTP_CONTENT_LENGTH'): environ[key] = value response = Response.from_app(wsgi_app, environ) errors = environ['wsgi.errors'].getvalue() if errors: print errors return { u'statusCode': response.status_code, u'headers': dict(response.headers), u'body': response.data }
class HeadersParser(object): """Parse request headers """ NGINX_ADDED_HEADERS = [ 'x-remote-addr', 'x-remote-port', 'x-server-addr', 'x-host', 'x-scheme', 'x-is-secure', 'x-server-protocol', 'x-server-port', 'x-tcp-rtt', 'x-tcp-rttvar', 'x-tcp-snd-cwd', 'x-tcp-rcv-space', ] def __init__(self, headers_obj): """ headers_obj: request.headers object """ self.headers = Headers(headers_obj) def remove_extra_headers(self): extra_headers = ['content-type', 'content-length'] for header in self.headers.items(): h_name = header[0] h_value = header[1] if (h_name.lower() in extra_headers and h_value == '')\ or (h_name.lower in extra_headers and h_value != ''): del self.headers[h_name] return True def extract_nginx_headers_data(self, remove_data=True): """method to extract data from nginx added headers """ data = {} # if header name in NGINX_ADDED_HEADERS extract data for header in self.headers: h_name = header[0].lower() h_value = header[1] if h_name in self.NGINX_ADDED_HEADERS: data[h_name] = h_value if remove_data: self.remove_nginx_headers() # if header is missing set it's value to 0 for header in self.NGINX_ADDED_HEADERS: if header not in data: data[header] = '0' return data def remove_nginx_headers(self): """ method to delete all Nginx added headers from request headers """ for header in self.headers.items(): h_name = header[0] if h_name.lower() in self.NGINX_ADDED_HEADERS: del self.headers[h_name] return True def headers_to_json(self): """Converts list of headers to list of header name: value pairs""" return {header[0]: header[1] for header in self.headers}
def handler(event, context): headers = Headers(event[u'headers']) if headers.get(u'Host', u'').endswith(u'.amazonaws.com'): script_name = '/{}'.format(event[u'requestContext'].get(u'stage', '')) else: script_name = '' environ = { 'API_GATEWAY_AUTHORIZER': event[u'requestContext'].get(u'authorizer', None), 'CONTENT_LENGTH': headers.get(u'Content-Length', str(len(event[u'body'] or ''))), 'CONTENT_TYPE': headers.get(u'Content-Type', ''), 'PATH_INFO': event[u'path'], 'QUERY_STRING': url_encode(event.get(u'queryStringParameters', None) or {}), 'REMOTE_ADDR': headers.get(u'X-Forwarded-For', '').split(', ')[0], 'REMOTE_USER': event[u'requestContext'].get(u'authorizer', {}).get( u'principalId', ''), 'REQUEST_METHOD': event[u'httpMethod'], 'SCRIPT_NAME': script_name, 'SERVER_NAME': headers.get(u'Host', 'lambda'), 'SERVER_PORT': headers.get(u'X-Forwarded-Port', '80'), 'SERVER_PROTOCOL': 'HTTP/1.1', 'context': context, 'wsgi.errors': # Per PEP 333, this should be a "text mode" stream. StringIO(), 'wsgi.input': BytesIO(), 'wsgi.multiprocess': False, 'wsgi.multithread': False, 'wsgi.run_once': False, 'wsgi.url_scheme': headers.get(u'X-Forwarded-Proto', 'http'), 'wsgi.version': (1, 0), } body = event[u'body'] or '' encoded = wsgi_encoding_dance(body) if not PY2: encoded = bytes(encoded, 'utf-8', 'replace') # XXX: why do we have to supply an encoding here? # Doesn't wsgi_encoding_dance already do that? environ['wsgi.input'] = BytesIO(encoded) for key, value in environ.items(): if PY2: if isinstance(value, basestring): # noqa: F821 environ[key] = wsgi_encoding_dance(value) else: if isinstance(value, str): environ[key] = wsgi_encoding_dance(value) for key, value in headers.items(): key = 'HTTP_' + key.upper().replace('-', '_') if key not in ('HTTP_CONTENT_TYPE', 'HTTP_CONTENT_LENGTH'): environ[key] = value response = Response.from_app(wsgi_app, environ) errors = environ['wsgi.errors'].getvalue() if errors: print(errors) # If there are multiple Set-Cookie headers, create case-mutated variations # in order to pass them through APIGW. This is a hack that's currently # needed. See: https://github.com/logandk/serverless-wsgi/issues/11 # Source: https://github.com/Miserlou/Zappa/blob/master/zappa/middleware.py new_headers = [x for x in response.headers if x[0] != 'Set-Cookie'] cookie_headers = [x for x in response.headers if x[0] == 'Set-Cookie'] if len(cookie_headers) > 1: for header, new_name in zip(cookie_headers, all_casings('Set-Cookie')): new_headers.append((new_name, header[1])) elif len(cookie_headers) == 1: new_headers.extend(cookie_headers) returndict = { u'statusCode': response.status_code, u'headers': dict(new_headers) } if response.data: mimetype = response.mimetype or 'text/plain' if mimetype.startswith('text/') or mimetype in TEXT_MIME_TYPES: returndict['body'] = response.get_data(as_text=True) else: returndict['body'] = base64.b64encode(response.data).decode( 'utf-8') returndict["isBase64Encoded"] = "true" return returndict