Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions example/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from lambda_proxy.proxy import API

APP = API(name="app")
APP = API(name="app", debug=True)


@APP.route("/", methods=["GET"], cors=True)
Expand All @@ -15,16 +15,16 @@ def main() -> Tuple[str, str, str]:
return ("OK", "text/plain", "Yo")


@APP.route("/<regex([0-9]{2}-[a-zA-Z]{5}):regex1>", methods=["GET"], cors=True)
@APP.route("/<regex(^[0-9]{2}-[a-zA-Z]{5}$):regex1>", methods=["GET"], cors=True)
def _re_one(regex1: str) -> Tuple[str, str, str]:
"""Return JSON Object."""
return ("OK", "text/plain", input)
return ("OK", "text/plain", regex1)


@APP.route("/<regex([0-9]{1}-[a-zA-Z]{5}):regex2>", methods=["GET"], cors=True)
def _re_two(regex2: str) -> Tuple[str, str, str]:
"""Return JSON Object."""
return ("OK", "text/plain", input)
return ("OK", "text/plain", regex2)


@APP.route("/add", methods=["GET", "POST"], cors=True)
Expand All @@ -33,8 +33,8 @@ def post(body) -> Tuple[str, str, str]:
return ("OK", "text/plain", body)


@APP.route("/<string:user>", methods=["GET"], cors=True)
@APP.route("/<string:user>/<int:num>", methods=["GET"], cors=True)
@APP.route("/user/<string:user>", methods=["GET"], cors=True)
@APP.route("/user/<string:user>/<int:num>", methods=["GET"], cors=True)
def double(user: str, num: int = 0) -> Tuple[str, str, str]:
"""Return JSON Object."""
return ("OK", "text/plain", f"{user}-{num}")
Expand All @@ -60,15 +60,15 @@ def json_handler() -> Tuple[str, str, str]:
return ("OK", "application/json", json.dumps({"app": "it works"}))


@APP.route("/binary", methods=["GET"], cors=True, payload_compression_method="gzip")
@APP.route("/bin/binary", methods=["GET"], cors=True, payload_compression_method="gzip")
def bin() -> Tuple[str, str, io.BinaryIO]:
"""Return image."""
with open("./rpix.png", "rb") as f:
return ("OK", "image/png", f.read())


@APP.route(
"/b64binary",
"/bin/b64binary",
methods=["GET"],
cors=True,
payload_compression_method="gzip",
Expand Down
61 changes: 59 additions & 2 deletions example/cli.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,39 @@
"""Launch server"""

import click
import base64
from urllib.parse import urlparse, parse_qsl

from http.server import HTTPServer, BaseHTTPRequestHandler

import click

from app import APP


class HTTPRequestHandler(BaseHTTPRequestHandler):
"""Requests handler."""

def do_OPTIONS(self):
"""OPTIONS requests."""
q = urlparse(self.path)
request = {
"headers": dict(self.headers),
"path": q.path,
"queryStringParameters": dict(parse_qsl(q.query)),
"httpMethod": self.command,
}
response = APP(request, None)

self.send_response(int(response["statusCode"]))
for r in response["headers"]:
self.send_header(r, response["headers"][r])
self.end_headers()

body = response.get("body", None)
if isinstance(body, str):
self.wfile.write(bytes(response["body"], "utf-8"))
elif body is not None:
self.wfile.write(response["body"])

def do_GET(self):
"""Get requests."""
q = urlparse(self.path)
Expand All @@ -32,6 +55,40 @@ def do_GET(self):
else:
self.wfile.write(response["body"])

def do_POST(self):
"""POST requests."""
q = urlparse(self.path)
q = urlparse(self.path)

body = self.rfile.read(int(dict(self.headers).get("Content-Length")))
body = base64.b64encode(body).decode()
request = {
"headers": dict(self.headers),
"path": q.path,
"queryStringParameters": dict(parse_qsl(q.query)),
"httpMethod": self.command,
}

request = {
"headers": dict(self.headers),
"path": q.path,
"queryStringParameters": dict(parse_qsl(q.query)),
"httpMethod": self.command,
"body": body,
"isBase64Encoded": True,
}
response = APP(request, None)

self.send_response(int(response["statusCode"]))
for r in response["headers"]:
self.send_header(r, response["headers"][r])
self.end_headers()

if isinstance(response["body"], str):
self.wfile.write(bytes(response["body"], "utf-8"))
else:
self.wfile.write(response["body"])


@click.command(short_help="Local Server")
@click.option("--port", type=int, default=8000, help="port")
Expand Down
32 changes: 25 additions & 7 deletions lambda_proxy/proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,9 @@ def _add_route(self, path: str, endpoint: callable, **kwargs) -> None:
"URL paths must be unique.".format(path)
)

if "OPTIONS" not in methods:
methods.append("OPTIONS")

self.routes[path] = RouteEntry(
endpoint,
path,
Expand Down Expand Up @@ -533,10 +536,9 @@ def response(
"image/jp2",
]

messageData = {
"statusCode": statusCode[status],
"headers": {"Content-Type": content_type},
}
messageData = {"statusCode": statusCode[status], "headers": {}}
if content_type is not None:
messageData["headers"].update({"Content-Type": content_type})

if cors:
messageData["headers"]["Access-Control-Allow-Origin"] = "*"
Expand All @@ -545,7 +547,11 @@ def response(
)
messageData["headers"]["Access-Control-Allow-Credentials"] = "true"

if compression and compression in accepted_compression:
if (
compression
and compression in accepted_compression
and response_body is not None
):
messageData["headers"]["Content-Encoding"] = compression
if isinstance(response_body, str):
response_body = bytes(response_body, "utf-8")
Expand Down Expand Up @@ -576,17 +582,20 @@ def response(

if ttl:
messageData["headers"]["Cache-Control"] = (
f"max-age={ttl}" if status == "OK" else "no-cache"
f"max-age={ttl}" if status not in ["ERROR", "CONFLICT"] else "no-cache"
)

if (
content_type in binary_types or not isinstance(response_body, str)
) and b64encode:
messageData["isBase64Encoded"] = True
messageData["body"] = base64.b64encode(response_body).decode()
else:
elif response_body is not None:
messageData["body"] = response_body

if messageData.get("body"):
messageData["headers"]["Content-Length"] = len(messageData["body"])

return messageData

def __call__(self, event, context):
Expand Down Expand Up @@ -644,6 +653,15 @@ def __call__(self, event, context):
{"errorMessage": "Unsupported method: {}".format(http_method)}
),
)
if http_method == "OPTIONS":
return self.response(
"EMPTY",
None,
None,
cors=route_entry.cors,
accepted_methods=route_entry.methods,
ttl=604800,
)

# remove access_token from kwargs
request_params.pop("access_token", False)
Expand Down
2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ envlist = py36,py37
[flake8]
ignore = D203
exclude = .git,__pycache__,docs/source/conf.py,old,build,dist
max-complexity = 10
max-complexity = 12
max-line-length = 90

[testenv]
Expand Down