diff --git a/adafruit_httpserver.py b/adafruit_httpserver.py index ae2f75a..dd233b5 100644 --- a/adafruit_httpserver.py +++ b/adafruit_httpserver.py @@ -275,6 +275,7 @@ def __init__(self, socket_source: Any) -> None: self.routes = {} self._socket_source = socket_source self._sock = None + self.root_path = "/" def route(self, path: str, method: str = "GET"): """Decorator used to add a route. @@ -302,33 +303,50 @@ def serve_forever(self, host: str, port: int = 80, root: str = "") -> None: :param int port: port :param str root: root directory to serve files from """ - self._sock = self._socket_source.socket( - self._socket_source.AF_INET, self._socket_source.SOCK_STREAM - ) - self._sock.bind((host, port)) - self._sock.listen(1) + self.start(host, port, root) while True: try: - conn, _ = self._sock.accept() + self.poll() except OSError: continue - with conn: - # If reading fails, close connection and retry. - try: - length, _ = conn.recvfrom_into(self._buffer) - except OSError: - continue - request = _HTTPRequest(raw_request=self._buffer[:length]) + def start(self, host: str, port: int = 80, root: str = "") -> None: + """ + Start the HTTP server at the given host and port. Requires calling + poll() in a while loop to handle incoming requests. - # If a route exists for this request, call it. Otherwise try to serve a file. - route = self.routes.get(request, None) - if route: - response = route(request) - elif request.method == "GET": - response = HTTPResponse(filename=request.path, root=root) - else: - response = HTTPResponse(status=HTTPStatus.INTERNAL_SERVER_ERROR) + :param str host: host name or IP address + :param int port: port + :param str root: root directory to serve files from + """ + self.root_path = root + + self._sock = self._socket_source.socket( + self._socket_source.AF_INET, self._socket_source.SOCK_STREAM + ) + self._sock.bind((host, port)) + self._sock.listen(10) - response.send(conn) + def poll(self): + """ + Call this method inside your main event loop to get the server to + check for new incoming client requests. When a request comes in, + the application callable will be invoked. + """ + conn, _ = self._sock.accept() + with conn: + length, _ = conn.recvfrom_into(self._buffer) + + request = _HTTPRequest(raw_request=self._buffer[:length]) + + # If a route exists for this request, call it. Otherwise try to serve a file. + route = self.routes.get(request, None) + if route: + response = route(request) + elif request.method == "GET": + response = HTTPResponse(filename=request.path, root=self.root_path) + else: + response = HTTPResponse(status=HTTPStatus.INTERNAL_SERVER_ERROR) + + response.send(conn) diff --git a/examples/httpserve_simplepolling.py b/examples/httpserve_simplepolling.py new file mode 100644 index 0000000..12211fc --- /dev/null +++ b/examples/httpserve_simplepolling.py @@ -0,0 +1,36 @@ +# SPDX-FileCopyrightText: 2022 Dan Halbert for Adafruit Industries +# +# SPDX-License-Identifier: Unlicense + +from secrets import secrets # pylint: disable=no-name-in-module + +import socketpool +import wifi + +from adafruit_httpserver import HTTPServer, HTTPResponse + +ssid = secrets["ssid"] +print("Connecting to", ssid) +wifi.radio.connect(ssid, secrets["password"]) +print("Connected to", ssid) +print(f"Listening on http://{wifi.radio.ipv4_address}:80") + +pool = socketpool.SocketPool(wifi.radio) +server = HTTPServer(pool) + + +@server.route("/") +def base(request): # pylint: disable=unused-argument + """Default reponse is /index.html""" + return HTTPResponse(filename="/index.html") + + +# startup the server +server.start(str(wifi.radio.ipv4_address)) + +while True: + try: + # processing any waiting requests + server.poll() + except OSError: + continue