def make_environ(self): request_url = url_parse(self.path) def shutdown_server(): self.server.shutdown_signal = True url_scheme = 'http' if self.server.ssl_context is None else 'https' if not self.client_address: self.client_address = '<local>' if isinstance(self.client_address, str): self.client_address = (self.client_address, 0) else: pass path_info = url_unquote(request_url.path) environ = { 'wsgi.version': (1, 0), 'wsgi.url_scheme': url_scheme, 'wsgi.input': self.rfile, 'wsgi.errors': sys.stderr, 'wsgi.multithread': self.server.multithread, 'wsgi.multiprocess': self.server.multiprocess, 'wsgi.run_once': False, 'werkzeug.server.shutdown': shutdown_server, 'SERVER_SOFTWARE': self.server_version, 'REQUEST_METHOD': self.command, 'SCRIPT_NAME': '', 'PATH_INFO': wsgi_encoding_dance(path_info), 'QUERY_STRING': wsgi_encoding_dance(request_url.query), # Non-standard, added by mod_wsgi, uWSGI "REQUEST_URI": wsgi_encoding_dance(self.path), # Non-standard, added by gunicorn "RAW_URI": wsgi_encoding_dance(self.path), 'REMOTE_ADDR': self.address_string(), 'REMOTE_PORT': self.port_integer(), 'SERVER_NAME': self.server.server_address[0], 'SERVER_PORT': str(self.server.server_address[1]), 'SERVER_PROTOCOL': self.request_version } for key, value in self.get_header_items(): key = key.upper().replace('-', '_') if key not in ('CONTENT_TYPE', 'CONTENT_LENGTH'): key = 'HTTP_' + key if key in environ: value = "{},{}".format(environ[key], value) environ[key] = value if environ.get('HTTP_TRANSFER_ENCODING', '').strip().lower() == 'chunked': environ['wsgi.input_terminated'] = True environ['wsgi.input'] = DechunkedInput(environ['wsgi.input']) if request_url.scheme and request_url.netloc: environ['HTTP_HOST'] = request_url.netloc return environ
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 get_environ(self): """返回内置环境。""" input_stream = self.input_stream content_length = self.content_length content_type = self.content_type if input_stream is not None: start_pos = input_stream.tell() input_stream.seek(0, 2) end_pos = input_stream.tell() input_stream.seek(start_pos) content_length = end_pos - start_pos elif content_type == "multipart/form-data": values = CombinedMultiDict([self.form, self.files]) input_stream, content_length, boundary = stream_encode_multipart(values, charset=self.charset) content_type += '; boundary="%s"' % boundary elif content_type == "application/x-www-form-urlencoded": # py2v3 review values = url_encode(self.form, charset=self.charset) values = values.encode("ascii") content_length = len(values) input_stream = BytesIO(values) else: input_stream = _empty_stream result = {} if self.environ_base: result.update(self.environ_base) def _path_encode(x): return wsgi_encoding_dance(url_unquote(x, self.charset), self.charset) qs = wsgi_encoding_dance(self.query_string) result.update( { "REQUEST_METHOD": self.method, "SCRIPT_NAME": _path_encode(self.script_root), "PATH_INFO": _path_encode(self.path), "QUERY_STRING": qs, "SERVER_NAME": self.server_name, "SERVER_PORT": str(self.server_port), "HTTP_HOST": self.host, "SERVER_PROTOCOL": self.server_protocol, "CONTENT_TYPE": content_type or "", "CONTENT_LENGTH": str(content_length or "0"), "wsgi.version": self.wsgi_version, "wsgi.url_scheme": self.url_scheme, "wsgi.input": input_stream, "wsgi.errors": self.errors_stream, "wsgi.multithread": self.multithread, "wsgi.multiprocess": self.multiprocess, "wsgi.run_once": self.run_once, } ) for key, value in self.headers.to_wsgi_list(): result["HTTP_%s" % key.upper().replace("-", "_")] = value if self.environ_overrides: result.update(self.environ_overrides) return result
def test_cookie_unicode_keys(self): # Yes, this is technically against the spec but happens val = http.dump_cookie(u'fö', u'fö') self.assert_equal( val, wsgi_encoding_dance(u'fö="f\\303\\266"; Path=/', 'utf-8')) cookies = http.parse_cookie(val) self.assert_equal(cookies[u'fö'], u'fö')
def get_environ(self): """Return the built environ.""" input_stream = self.input_stream content_length = self.content_length content_type = self.content_type if input_stream is not None: start_pos = input_stream.tell() input_stream.seek(0, 2) end_pos = input_stream.tell() input_stream.seek(start_pos) content_length = end_pos - start_pos elif content_type == 'multipart/form-data': values = CombinedMultiDict([self.form, self.files]) input_stream, content_length, boundary = \ stream_encode_multipart(values, charset=self.charset) content_type += '; boundary="%s"' % boundary elif content_type == 'application/x-www-form-urlencoded': #py2v3 review values = url_encode(self.form, charset=self.charset) values = values.encode('ascii') content_length = len(values) input_stream = BytesIO(values) else: input_stream = _empty_stream result = {} if self.environ_base: result.update(self.environ_base) def _path_encode(x): return wsgi_encoding_dance(url_unquote(x, self.charset), self.charset) qs = wsgi_encoding_dance(self.query_string) result.update({ 'REQUEST_METHOD': self.method, 'SCRIPT_NAME': _path_encode(self.script_root), 'PATH_INFO': _path_encode(self.path), 'QUERY_STRING': qs, 'SERVER_NAME': self.server_name, 'SERVER_PORT': str(self.server_port), 'HTTP_HOST': self.host, 'SERVER_PROTOCOL': self.server_protocol, 'CONTENT_TYPE': content_type or '', 'CONTENT_LENGTH': str(content_length or '0'), 'wsgi.version': self.wsgi_version, 'wsgi.url_scheme': self.url_scheme, 'wsgi.input': input_stream, 'wsgi.errors': self.errors_stream, 'wsgi.multithread': self.multithread, 'wsgi.multiprocess': self.multiprocess, 'wsgi.run_once': self.run_once }) for key, value in self.headers.to_wsgi_list(): result['HTTP_%s' % key.upper().replace('-', '_')] = value if self.environ_overrides: result.update(self.environ_overrides) return result
def make_environ(self): request_url = url_parse(self.path) def shutdown_server(): self.server.shutdown_signal = True url_scheme = self.server.ssl_context is None and 'http' or 'https' path_info = url_unquote(request_url.path) environ = { 'wsgi.version': (1, 0), 'wsgi.url_scheme': url_scheme, 'wsgi.input': self.rfile, 'wsgi.errors': sys.stderr, 'wsgi.multithread': self.server.multithread, 'wsgi.multiprocess': self.server.multiprocess, 'wsgi.run_once': False, 'werkzeug.server.shutdown': shutdown_server, 'SERVER_SOFTWARE': self.server_version, 'REQUEST_METHOD': self.command, 'SCRIPT_NAME': '', 'PATH_INFO': wsgi_encoding_dance(path_info), 'QUERY_STRING': wsgi_encoding_dance(request_url.query), 'CONTENT_TYPE': self.headers.get('Content-Type', ''), 'CONTENT_LENGTH': self.headers.get('Content-Length', ''), 'REMOTE_ADDR': self.client_address[0], 'REMOTE_PORT': self.client_address[1], 'SERVER_NAME': self.server.server_address[0], 'SERVER_PORT': str(self.server.server_address[1]), 'SERVER_PROTOCOL': self.request_version } for h in self.headers.headers: key, value = h.split(':', 1) key = 'HTTP_' + key.upper().replace('-', '_') value = value.strip() if key not in ('HTTP_CONTENT_TYPE', 'HTTP_CONTENT_LENGTH'): if key in environ: environ[key] += ',' + value else: environ[key] = value if request_url.netloc: environ['HTTP_HOST'] = request_url.netloc return environ
def make_environ(self): request_url = url_parse(self.path) def shutdown_server(): self.server.shutdown_signal = True url_scheme = self.server.ssl_context is None and 'http' or 'https' path_info = url_unquote(request_url.path) environ = { 'REQUEST_UUID': uuid.uuid4().hex, 'wsgi.version': (1, 0), 'wsgi.url_scheme': url_scheme, 'wsgi.input': self.rfile, 'wsgi.errors': sys.stderr, 'wsgi.multithread': self.server.multithread, 'wsgi.multiprocess': self.server.multiprocess, 'wsgi.run_once': False, 'werkzeug.server.shutdown': shutdown_server, 'SERVER_SOFTWARE': self.server_version, 'REQUEST_METHOD': self.command, 'SCRIPT_NAME': '', 'PATH_INFO': wsgi_encoding_dance(path_info), 'QUERY_STRING': wsgi_encoding_dance(request_url.query), 'REMOTE_ADDR': self.address_string(), 'REMOTE_PORT': self.port_integer(), 'SERVER_NAME': self.server.server_address[0], 'SERVER_PORT': str(self.server.server_address[1]), 'SERVER_PROTOCOL': self.request_version } for key, value in self.headers.items(): key = key.upper().replace('-', '_') if key not in ('CONTENT_TYPE', 'CONTENT_LENGTH'): key = 'HTTP_' + key environ[key] = value if environ.get('HTTP_TRANSFER_ENCODING', '').strip().lower() == 'chunked': environ['wsgi.input_terminated'] = True environ['wsgi.input'] = DechunkedInput(environ['wsgi.input']) if request_url.scheme and request_url.netloc: environ['HTTP_HOST'] = request_url.netloc return environ
def make_environ(self): request_url = url_parse(self.path) def shutdown_server(): self.server.shutdown_signal = True url_scheme = self.server.ssl_context is None and "http" or "https" path_info = url_unquote(request_url.path) environ = { "wsgi.version": (1, 0), "wsgi.url_scheme": url_scheme, "wsgi.input": self.rfile, "wsgi.errors": sys.stderr, "wsgi.multithread": self.server.multithread, "wsgi.multiprocess": self.server.multiprocess, "wsgi.run_once": False, "werkzeug.server.shutdown": shutdown_server, "SERVER_SOFTWARE": self.server_version, "REQUEST_METHOD": self.command, "SCRIPT_NAME": "", "PATH_INFO": wsgi_encoding_dance(path_info), "QUERY_STRING": wsgi_encoding_dance(request_url.query), "REMOTE_ADDR": self.address_string(), "REMOTE_PORT": self.port_integer(), "SERVER_NAME": self.server.server_address[0], "SERVER_PORT": str(self.server.server_address[1]), "SERVER_PROTOCOL": self.request_version, } for key, value in self.headers.items(): key = key.upper().replace("-", "_") if key not in ("CONTENT_TYPE", "CONTENT_LENGTH"): key = "HTTP_" + key environ[key] = value if environ.get("HTTP_TRANSFER_ENCODING", "").strip().lower() == "chunked": environ["wsgi.input_terminated"] = True environ["wsgi.input"] = DechunkedInput(environ["wsgi.input"]) if request_url.scheme and request_url.netloc: environ["HTTP_HOST"] = request_url.netloc return environ
def make_environ(self): """ Create an environment that can be used with werkzeug """ # Derived from werkzeug's WSGIRequestHandler from werkzeug.urls import url_parse, url_unquote from werkzeug._compat import wsgi_encoding_dance request_url = url_parse(self.path) url_scheme = "http" path_info = url_unquote(request_url.path) environ = { 'wsgi.version': (1, 0), 'wsgi.url_scheme': url_scheme, 'wsgi.input': self.rfile, 'wsgi.errors': sys.stderr, 'wsgi.multithread': False, 'wsgi.multiprocess': True, 'wsgi.run_once': False, 'SERVER_SOFTWARE': self.server_version, 'REQUEST_METHOD': self.command, 'SCRIPT_NAME': '', 'PATH_INFO': wsgi_encoding_dance(path_info), 'QUERY_STRING': wsgi_encoding_dance(request_url.query), 'CONTENT_TYPE': self.headers.get('Content-Type', ''), 'CONTENT_LENGTH': self.headers.get('Content-Length', ''), 'REMOTE_ADDR': self.client_address[0], 'REMOTE_PORT': self.client_address[1], 'SERVER_NAME': self.server.server_address[0], 'SERVER_PORT': str(self.server.server_address[1]), 'SERVER_PROTOCOL': self.request_version } for key, value in self.headers.items(): key = 'HTTP_' + key.upper().replace('-', '_') if key not in ('HTTP_CONTENT_TYPE', 'HTTP_CONTENT_LENGTH'): environ[key] = value if request_url.netloc: environ['HTTP_HOST'] = request_url.netloc return environ
def setup_environ_items(environ, headers): 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 return environ
def make_environ(self): request_url = url_parse(self.path) def shutdown_server(): self.server.shutdown_signal = True url_scheme = self.server.ssl_context is None and 'http' or 'https' path_info = url_unquote(request_url.path) environ = { 'wsgi.version': (1, 0), 'wsgi.url_scheme': url_scheme, 'wsgi.input': self.rfile, 'wsgi.errors': sys.stderr, 'wsgi.multithread': self.server.multithread, 'wsgi.multiprocess': self.server.multiprocess, 'wsgi.run_once': False, 'werkzeug.server.shutdown': shutdown_server, 'SERVER_SOFTWARE': self.server_version, 'REQUEST_METHOD': self.command, 'SCRIPT_NAME': '', 'PATH_INFO': wsgi_encoding_dance(path_info), 'QUERY_STRING': wsgi_encoding_dance(request_url.query), 'CONTENT_TYPE': self.headers.get('Content-Type', ''), 'CONTENT_LENGTH': self.headers.get('Content-Length', ''), 'REMOTE_ADDR': self.client_address[0], 'REMOTE_PORT': self.client_address[1], 'SERVER_NAME': self.server.server_address[0], 'SERVER_PORT': str(self.server.server_address[1]), 'SERVER_PROTOCOL': self.request_version } for key, value in self.headers.items(): key = 'HTTP_' + key.upper().replace('-', '_') if key not in ('HTTP_CONTENT_TYPE', 'HTTP_CONTENT_LENGTH'): environ[key] = value if request_url.netloc: environ['HTTP_HOST'] = request_url.netloc return environ
def make_environ(self): request_url = url_parse(self.path) def shutdown_server(): self.server.shutdown_signal = True url_scheme = self.server.ssl_context is None and "http" or "https" path_info = url_unquote(request_url.path) environ = { "wsgi.version": (1, 0), "wsgi.url_scheme": url_scheme, "wsgi.input": self.rfile, "wsgi.errors": sys.stderr, "wsgi.multithread": self.server.multithread, "wsgi.multiprocess": self.server.multiprocess, "wsgi.run_once": False, "werkzeug.server.shutdown": shutdown_server, "SERVER_SOFTWARE": self.server_version, "REQUEST_METHOD": self.command, "SCRIPT_NAME": "", "PATH_INFO": wsgi_encoding_dance(path_info), "QUERY_STRING": wsgi_encoding_dance(request_url.query), "CONTENT_TYPE": self.headers.get("Content-Type", ""), "CONTENT_LENGTH": self.headers.get("Content-Length", ""), "REMOTE_ADDR": self.client_address[0], "REMOTE_PORT": self.client_address[1], "SERVER_NAME": self.server.server_address[0], "SERVER_PORT": str(self.server.server_address[1]), "SERVER_PROTOCOL": self.request_version, } for key, value in self.headers.items(): key = "HTTP_" + key.upper().replace("-", "_") if key not in ("HTTP_CONTENT_TYPE", "HTTP_CONTENT_LENGTH"): environ[key] = value if request_url.netloc: environ["HTTP_HOST"] = request_url.netloc return environ
def _path_encode(x): return wsgi_encoding_dance(url_unquote(x, self.charset), self.charset)
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 }
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 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 test_cookie_unicode_keys(self): # Yes, this is technically against the spec but happens val = http.dump_cookie(u"fö", u"fö") assert val == wsgi_encoding_dance(u'fö="f\\303\\266"; Path=/', "utf-8") cookies = http.parse_cookie(val) assert cookies[u"fö"] == u"fö"
def get_environ(self): """Return the built environ. .. versionchanged:: 0.15 The content type and length headers are set based on input stream detection. Previously this only set the WSGI keys. """ input_stream = self.input_stream content_length = self.content_length mimetype = self.mimetype content_type = self.content_type if input_stream is not None: start_pos = input_stream.tell() input_stream.seek(0, 2) end_pos = input_stream.tell() input_stream.seek(start_pos) content_length = end_pos - start_pos elif mimetype == 'multipart/form-data': values = CombinedMultiDict([self.form, self.files]) input_stream, content_length, boundary = \ stream_encode_multipart(values, charset=self.charset) content_type = mimetype + '; boundary="%s"' % boundary elif mimetype == 'application/x-www-form-urlencoded': # XXX: py2v3 review values = url_encode(self.form, charset=self.charset) values = values.encode('ascii') content_length = len(values) input_stream = BytesIO(values) else: input_stream = BytesIO() result = {} if self.environ_base: result.update(self.environ_base) def _path_encode(x): return wsgi_encoding_dance(url_unquote(x, self.charset), self.charset) qs = wsgi_encoding_dance(self.query_string) result.update({ 'REQUEST_METHOD': self.method, 'SCRIPT_NAME': _path_encode(self.script_root), 'PATH_INFO': _path_encode(self.path), 'QUERY_STRING': qs, # Non-standard, added by mod_wsgi, uWSGI "REQUEST_URI": wsgi_encoding_dance(self.path), # Non-standard, added by gunicorn "RAW_URI": wsgi_encoding_dance(self.path), 'SERVER_NAME': self.server_name, 'SERVER_PORT': str(self.server_port), 'HTTP_HOST': self.host, 'SERVER_PROTOCOL': self.server_protocol, 'wsgi.version': self.wsgi_version, 'wsgi.url_scheme': self.url_scheme, 'wsgi.input': input_stream, 'wsgi.errors': self.errors_stream, 'wsgi.multithread': self.multithread, 'wsgi.multiprocess': self.multiprocess, 'wsgi.run_once': self.run_once }) headers = self.headers.copy() if content_type is not None: result['CONTENT_TYPE'] = content_type headers.set("Content-Type", content_type) if content_length is not None: result['CONTENT_LENGTH'] = str(content_length) headers.set("Content-Length", content_length) for key, value in headers.to_wsgi_list(): result['HTTP_%s' % key.upper().replace('-', '_')] = value if self.environ_overrides: result.update(self.environ_overrides) return result
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
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 get_environ(self): """Return the built environ.""" input_stream = self.input_stream content_length = self.content_length mimetype = self.mimetype content_type = self.content_type if input_stream is not None: start_pos = input_stream.tell() input_stream.seek(0, 2) end_pos = input_stream.tell() input_stream.seek(start_pos) content_length = end_pos - start_pos elif mimetype == "multipart/form-data": values = CombinedMultiDict([self.form, self.files]) input_stream, content_length, boundary = stream_encode_multipart( values, charset=self.charset) content_type = mimetype + '; boundary="%s"' % boundary elif mimetype == "application/x-www-form-urlencoded": # XXX: py2v3 review values = url_encode(self.form, charset=self.charset) values = values.encode("ascii") content_length = len(values) input_stream = BytesIO(values) else: input_stream = _empty_stream result = {} if self.environ_base: result.update(self.environ_base) def _path_encode(x): return wsgi_encoding_dance(url_unquote(x, self.charset), self.charset) qs = wsgi_encoding_dance(self.query_string) result.update({ "REQUEST_METHOD": self.method, "SCRIPT_NAME": _path_encode(self.script_root), "PATH_INFO": _path_encode(self.path), "QUERY_STRING": qs, "SERVER_NAME": self.server_name, "SERVER_PORT": str(self.server_port), "HTTP_HOST": self.host, "SERVER_PROTOCOL": self.server_protocol, "CONTENT_TYPE": content_type or "", "CONTENT_LENGTH": str(content_length or "0"), "wsgi.version": self.wsgi_version, "wsgi.url_scheme": self.url_scheme, "wsgi.input": input_stream, "wsgi.errors": self.errors_stream, "wsgi.multithread": self.multithread, "wsgi.multiprocess": self.multiprocess, "wsgi.run_once": self.run_once, }) for key, value in self.headers.to_wsgi_list(): result["HTTP_%s" % key.upper().replace("-", "_")] = value if self.environ_overrides: result.update(self.environ_overrides) return result
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 test_cookie_unicode_keys(self): # Yes, this is technically against the spec but happens val = http.dump_cookie(u'fö', u'fö') self.assert_equal(val, wsgi_encoding_dance(u'fö="f\\303\\266"; Path=/', 'utf-8')) cookies = http.parse_cookie(val) self.assert_equal(cookies[u'fö'], u'fö')
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