Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixing behavior issue when route raising Exception #21

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
76 changes: 61 additions & 15 deletions phew/server.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,37 @@
import uasyncio, os, time
from . import logging
import sys
import io

_routes = []
catchall_handler = None
_error_handler = None
_default_error_message = """
<!DOCTYPE HTML>
<HTML>
<HEAD>
<TITLE>Error</TITLE>
</HEAD>
<BODY>
<h1>Internal Server Error</h1>
<p>Please check log for detail.</p>
</BODY>
</HTML>
"""

# convert exception to string
def convert_exc2str(e):
__ = io.StringIO()
sys.print_exception(e, __)
__.seek(0)
return __.read()

def file_exists(filename):
try:
return (os.stat(filename)[0] & 0x4000) == 0
except OSError:
return False


def urldecode(text):
text = text.replace("+", " ")
result = ""
Expand Down Expand Up @@ -137,7 +157,7 @@ def call_handler(self, request):
parameters[name] = compare

return self.handler(request, **parameters)

def __str__(self):
return f"""\
path: {self.path}
Expand Down Expand Up @@ -208,7 +228,7 @@ async def _parse_json_body(reader, headers):


status_message_map = {
200: "OK", 201: "Created", 202: "Accepted",
200: "OK", 201: "Created", 202: "Accepted",
203: "Non-Authoritative Information", 204: "No Content",
205: "Reset Content", 206: "Partial Content", 300: "Multiple Choices",
301: "Moved Permanently", 302: "Found", 303: "See Other",
Expand All @@ -217,12 +237,11 @@ async def _parse_json_body(reader, headers):
400: "Bad Request", 401: "Unauthorized", 403: "Forbidden",
404: "Not Found", 405: "Method Not Allowed", 406: "Not Acceptable",
408: "Request Timeout", 409: "Conflict", 410: "Gone",
414: "URI Too Long", 415: "Unsupported Media Type",
414: "URI Too Long", 415: "Unsupported Media Type",
416: "Range Not Satisfiable", 418: "I'm a teapot",
500: "Internal Server Error", 501: "Not Implemented"
}


# handle an incoming request to the web server
async def _handle_request(reader, writer):
response = None
Expand All @@ -245,13 +264,32 @@ async def _handle_request(reader, writer):
request.data = await _parse_json_body(reader, request.headers)
if request.headers["content-type"].startswith("application/x-www-form-urlencoded"):
form_data = await reader.read(int(request.headers["content-length"]))
request.form = _parse_query_string(form_data.decode())
request.form = _parse_query_string(form_data.decode())

route = _match_route(request)
if route:
response = route.call_handler(request)
elif catchall_handler:
response = catchall_handler(request)
global _error_handler
try:
if route:
response = route.call_handler(request)
elif catchall_handler:
response = catchall_handler(request)

except Exception as e:
error_msg = convert_exc2str(e)
try:

if _error_handler is not None:
response = _error_handler(e, error_msg)

except Exception as ee:
# replacing error message with new one
error_msg = convert_exc2str(ee)

finally:
if response is None:
response = Response(_default_error_message, status = 500)

logging.error(error_msg)

# if shorthand body generator only notation used then convert to tuple
if type(response).__name__ == "generator":
Expand All @@ -270,7 +308,7 @@ async def _handle_request(reader, writer):
response.add_header("Content-Type", content_type)
if hasattr(body, '__len__'):
response.add_header("Content-Length", len(body))

# write status line
status_message = status_message_map.get(response.status, "Unknown")
writer.write(f"HTTP/1.1 {response.status} {status_message}\r\n".encode("ascii"))
Expand All @@ -281,7 +319,7 @@ async def _handle_request(reader, writer):

# blank line to denote end of headers
writer.write("\r\n".encode("ascii"))

if isinstance(response, FileResponse):
# file
with open(response.file, "rb") as f:
Expand All @@ -300,10 +338,10 @@ async def _handle_request(reader, writer):
# string/bytes
writer.write(response.body)
await writer.drain()

writer.close()
await writer.wait_closed()

processing_time = time.ticks_ms() - request_start_time
logging.info(f"> {request.method} {request.path} ({response.status} {status_message}) [{processing_time}ms]")

Expand Down Expand Up @@ -335,7 +373,15 @@ def _catchall(f):
set_callback(f)
return f
return _catchall


def errorhandler():
def _wrap(f):
global _error_handler
_error_handler = f
return f
return _wrap



def redirect(url, status = 301):
return Response("", status, {"Location": url})
Expand Down