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

Added Features. #22

Merged
merged 2 commits into from
Dec 22, 2022
Merged
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
42 changes: 40 additions & 2 deletions adafruit_httpserver/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class HTTPResponse:
status: HTTPStatus
headers: Dict[str, str]
content_type: str

cache: Optional[int]
filename: Optional[str]
root_path: str

Expand All @@ -41,6 +41,7 @@ def __init__( # pylint: disable=too-many-arguments
body: str = "",
headers: Dict[str, str] = None,
content_type: str = MIMEType.TYPE_TXT,
cache: Optional[int] = 0,
filename: Optional[str] = None,
root_path: str = "",
http_version: str = "HTTP/1.1",
Expand All @@ -54,6 +55,7 @@ def __init__( # pylint: disable=too-many-arguments
self.body = body
self.headers = headers or {}
self.content_type = content_type
self.cache = cache
self.filename = filename
self.root_path = root_path
self.http_version = http_version
Expand All @@ -64,8 +66,10 @@ def _construct_response_bytes( # pylint: disable=too-many-arguments
status: HTTPStatus = CommonHTTPStatus.OK_200,
content_type: str = MIMEType.TYPE_TXT,
content_length: Union[int, None] = None,
cache: int = 0,
headers: Dict[str, str] = None,
body: str = "",
chunked: bool = False,
) -> bytes:
"""Constructs the response bytes from the given parameters."""

Expand All @@ -75,12 +79,17 @@ def _construct_response_bytes( # pylint: disable=too-many-arguments
response_headers = {} if headers is None else headers.copy()

response_headers.setdefault("Content-Type", content_type)
response_headers.setdefault("Content-Length", content_length or len(body))
response_headers.setdefault("Connection", "close")
if chunked:
response_headers.setdefault("Transfer-Encoding", "chunked")
else:
response_headers.setdefault("Content-Length", content_length or len(body))

for header, value in response_headers.items():
response += f"{header}: {value}\r\n"

response += f"Cache-Control: max-age={cache}\r\n"

response += f"\r\n{body}"

return response.encode("utf-8")
Expand Down Expand Up @@ -116,6 +125,33 @@ def send(self, conn: Union["SocketPool.Socket", "socket.socket"]) -> None:
body=self.body,
)

def send_chunk_headers(
self, conn: Union["SocketPool.Socket", "socket.socket"]
) -> None:
"""Send Headers for a chunked response over the given socket."""
self._send_bytes(
conn,
self._construct_response_bytes(
status=self.status,
content_type=self.content_type,
chunked=True,
cache=self.cache,
body="",
),
)

def send_body_chunk(
self, conn: Union["SocketPool.Socket", "socket.socket"], chunk: str
) -> None:
"""Send chunk of data to the given socket. Send an empty("") chunk to finish the session.

:param Union["SocketPool.Socket", "socket.socket"] conn: Current connection.
:param str chunk: String data to be sent.
"""
size = "%X\r\n".encode() % len(chunk)
self._send_bytes(conn, size)
self._send_bytes(conn, chunk.encode() + b"\r\n")

def _send_response( # pylint: disable=too-many-arguments
self,
conn: Union["SocketPool.Socket", "socket.socket"],
Expand All @@ -129,6 +165,7 @@ def _send_response( # pylint: disable=too-many-arguments
self._construct_response_bytes(
status=status,
content_type=content_type,
cache=self.cache,
headers=headers,
body=body,
),
Expand All @@ -148,6 +185,7 @@ def _send_file_response( # pylint: disable=too-many-arguments
status=self.status,
content_type=MIMEType.from_file_name(filename),
content_length=file_length,
cache=self.cache,
headers=headers,
),
)
Expand Down
21 changes: 19 additions & 2 deletions adafruit_httpserver/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,17 @@ def route_func(request):
print("Received a request of length", len(raw_text), "bytes")
return HTTPResponse(body="hello world")


@server.route(path, method)
def route_func(request, conn):
raw_text = request.raw_request.decode("utf8")
print("Received a request of length", len(raw_text), "bytes")
res = HTTPResponse(content_type="text/html")
res.send_chunk_headers(conn)
res.send_body_chunk(conn, "Some content")
res.send_body_chunk(conn, "Some more content")
res.send_body_chunk(conn, "") # Send empty packet to finish chunked stream
return None # Return None, so server knows that nothing else needs to be sent.
"""

def route_decorator(func: Callable) -> Callable:
Expand Down Expand Up @@ -162,12 +173,18 @@ def poll(self):

# If a handler for route exists and is callable, call it.
if handler is not None and callable(handler):
response = handler(request)
# Need to pass connection for chunked encoding to work.
try:
response = handler(request, conn)
except TypeError:
response = handler(request)
if response is None:
return

# If no handler exists and request method is GET, try to serve a file.
elif request.method == HTTPMethod.GET:
response = HTTPResponse(
filename=request.path, root_path=self.root_path
filename=request.path, root_path=self.root_path, cache=604800
)

# If no handler exists and request method is not GET, return 400 Bad Request.
Expand Down