Skip to content

Commit 5bd6c86

Browse files
committed
add in support for automatically serving files from a specified directory
1 parent b8fd02e commit 5bd6c86

File tree

1 file changed

+57
-4
lines changed

1 file changed

+57
-4
lines changed

adafruit_esp32spi/adafruit_esp32spi_server.py

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
"""
3232
# pylint: disable=no-name-in-module
3333

34+
import os
3435
from micropython import const
3536
import adafruit_esp32spi.adafruit_esp32spi_socket as socket
3637
from adafruit_esp32spi.adafruit_esp32spi_requests import parse_headers
@@ -43,6 +44,7 @@ def set_interface(iface):
4344
socket.set_interface(iface)
4445

4546
NO_SOCK_AVAIL = const(255)
47+
INDEX_HTML = "/index.html"
4648

4749

4850
# pylint: disable=unused-argument, redefined-builtin, invalid-name
@@ -54,6 +56,8 @@ def __init__(self, port=80, debug=False):
5456
self._client_sock = socket.socket(socknum=NO_SOCK_AVAIL)
5557
self._debug = debug
5658
self._listeners = {}
59+
self._static_dir = None
60+
self._static_files = []
5761

5862

5963
def start(self):
@@ -72,12 +76,43 @@ def on(self, method, path, request_handler):
7276
7377
request_handler should accept the following args:
7478
(Dict headers, bytes body, Socket client)
79+
7580
:param str method: the method of the HTTP request
7681
:param str path: the path of the HTTP request
7782
:param func request_handler: the function to call
7883
"""
7984
self._listeners[self._get_listener_key(method, path)] = request_handler
8085

86+
def set_static_dir(self, directory_path):
87+
"""
88+
allows for setting a directory of static files that will be auto-served
89+
when that file is GET requested at'/<fileName.extension>'
90+
index.html will also be made available at root path '/'
91+
92+
Note: does not support serving files in child folders at this time
93+
"""
94+
self._static_dir = directory_path
95+
self._static_files = ["/" + file for file in os.listdir(self._static_dir)]
96+
print(self._static_files)
97+
98+
def serve_file(self, file_path, dir=None):
99+
"""
100+
writes a file from the file system as a response to the client.
101+
102+
:param string file_path: path to the image to write to client.
103+
if dir is not present, it is treated as an absolute path
104+
:param string dir: path to directory that file is located in (optional)
105+
"""
106+
self._client_sock.write(b"HTTP/1.1 200 OK\r\n")
107+
self._client_sock.write(b"Content-Type:" + self._get_content_type(file_path) + b"\r\n")
108+
self._client_sock.write(b"\r\n")
109+
full_path = file_path if not dir else dir + file_path
110+
with open(full_path, 'rb') as fp:
111+
for line in fp:
112+
self._client_sock.write(line)
113+
self._client_sock.write(b"\r\n")
114+
self._client_sock.close()
115+
81116
def update_poll(self):
82117
"""
83118
Call this method inside your main event loop to get the server
@@ -91,16 +126,22 @@ def update_poll(self):
91126
if (client and client.available()):
92127
line = client.readline()
93128
line = line.split(None, 2)
94-
method = line[0]
95-
path = line[1]
129+
method = str(line[0], "utf-8")
130+
path = str(line[1], "utf-8")
96131
key = self._get_listener_key(method, path)
97132
if key in self._listeners:
98133
headers = parse_headers(client)
99134
body = client.read()
100135
self._listeners[key](headers, body, client)
136+
elif method.lower() == "get":
137+
client.read()
138+
if path in self._static_files:
139+
self.serve_file(path, dir=self._static_dir)
140+
elif path == "/" and INDEX_HTML in self._static_files:
141+
self.serve_file(INDEX_HTML, dir=self._static_dir)
101142
else:
102143
# TODO: support optional custom 404 handler?
103-
client.write(b"HTTP/1.1 404 NotFound\r\n")
144+
self._client_sock.write(b"HTTP/1.1 404 NotFound\r\n")
104145
client.close()
105146

106147

@@ -137,4 +178,16 @@ def client_available(self):
137178
return None
138179

139180
def _get_listener_key(self, method, path): # pylint: disable=no-self-use
140-
return "{0}|{1}".format(str(method.lower(), 'utf-8'), str(path, 'utf-8'))
181+
return "{0}|{1}".format(method.lower(), path)
182+
183+
184+
def _get_content_type(self, file): # pylint: disable=no-self-use
185+
ext = file.split('.')[-1]
186+
if ext in ("html", "htm"):
187+
return b"text/html"
188+
if ext == "js":
189+
return b"application/javascript"
190+
if ext == "css":
191+
return b"text/css"
192+
# TODO: test adding in support for image types as well
193+
return b"text/plain"

0 commit comments

Comments
 (0)