Skip to content

Commit faf031a

Browse files
committed
add simple request method handling
- can register callbacks on specific HTTP methods and paths - Can call update_poll in event loop to check for new incoming requests - update server example script
1 parent 5e09b7b commit faf031a

File tree

4 files changed

+113
-48
lines changed

4 files changed

+113
-48
lines changed

adafruit_esp32spi/adafruit_esp32spi_requests.py

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -204,23 +204,11 @@ def request(method, url, data=None, json=None, headers=None, stream=False, timeo
204204
reason = ""
205205
if len(line) > 2:
206206
reason = line[2].rstrip()
207-
while True:
208-
line = sock.readline()
209-
if not line or line == b"\r\n":
210-
break
211-
212-
#print("**line: ", line)
213-
title, content = line.split(b': ', 1)
214-
if title and content:
215-
title = str(title.lower(), 'utf-8')
216-
content = str(content, 'utf-8')
217-
resp.headers[title] = content
218-
219-
if line.startswith(b"Transfer-Encoding:"):
220-
if b"chunked" in line:
221-
raise ValueError("Unsupported " + line)
222-
elif line.startswith(b"Location:") and not 200 <= status <= 299:
223-
raise NotImplementedError("Redirects not yet supported")
207+
resp.headers = self.parse_headers(sock)
208+
if "chunked" in resp.headers.get("transfer-encoding"):
209+
raise ValueError("Unsupported " + line)
210+
elif resp.headers.get("location") and not 200 <= status <= 299:
211+
raise NotImplementedError("Redirects not yet supported")
224212

225213
except:
226214
sock.close()
@@ -232,6 +220,27 @@ def request(method, url, data=None, json=None, headers=None, stream=False, timeo
232220
# pylint: enable=too-many-branches, too-many-statements, unused-argument
233221
# pylint: enable=too-many-arguments, too-many-locals
234222

223+
def parse_headers(sock):
224+
"""
225+
Parses the header portion of an HTTP request/response from the socket.
226+
Expects first line of HTTP request/response to have been read already
227+
return: header dictionary
228+
rtype: Dict
229+
"""
230+
headers = {}
231+
while True:
232+
line = sock.readline()
233+
if not line or line == b"\r\n":
234+
break
235+
236+
#print("**line: ", line)
237+
title, content = line.split(b': ', 1)
238+
if title and content:
239+
title = str(title.lower(), 'utf-8')
240+
content = str(content, 'utf-8')
241+
headers[title] = content
242+
return headers
243+
235244
def head(url, **kw):
236245
"""Send HTTP HEAD request"""
237246
return request("HEAD", url, **kw)

adafruit_esp32spi/adafruit_esp32spi_server.py

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131
"""
3232

3333
from micropython import const
34-
import adafruit_esp32spi_socket as socket
34+
import adafruit_esp32spi.adafruit_esp32spi_socket as socket
35+
from adafruit_esp32spi.adafruit_esp32spi_requests import parse_headers
3536

3637
_the_interface = None # pylint: disable=invalid-name
3738
def set_interface(iface):
@@ -51,6 +52,7 @@ def __init__(self, port=80, debug=False):
5152
self._server_sock = socket.socket(socknum=NO_SOCK_AVAIL)
5253
self._client_sock = socket.socket(socknum=NO_SOCK_AVAIL)
5354
self._debug = debug
55+
self._listeners = {}
5456

5557

5658
def start(self):
@@ -62,11 +64,43 @@ def start(self):
6264
print("Server available at {0}:{1}".format(ip, self.port))
6365
print("Sever status: ", _the_interface.get_server_state(self._server_sock.socknum))
6466

67+
def on(self, method, path, request_handler):
68+
"""
69+
Register a Request Handler for a particular HTTP method and path.
70+
request_handler will be called whenever a matching HTTP request is received.
71+
72+
request_handler should accept the following args:
73+
(Dict headers, bytes body, Socket client)
74+
:param str method: the method of the HTTP request
75+
:param str path: the path of the HTTP request
76+
:param func request_handler: the function to call
77+
"""
78+
self._listeners[self._get_listener_key(method, path)] = request_handler
79+
80+
def update_poll(self):
81+
client = self.client_available()
82+
if (client and client.available()):
83+
line = client.readline()
84+
method, path, ver = line.split(None, 2)
85+
key = self._get_listener_key(method, path)
86+
if key in self._listeners:
87+
headers = parse_headers(client)
88+
body = client.read()
89+
print("headers: ", headers)
90+
print("body: ", body)
91+
self._listeners[key](headers, body, client)
92+
else:
93+
# TODO: support optional custom 404 callback?
94+
client.write(b"HTTP/1.1 404 NotFound\r\n")
95+
client.close()
96+
97+
6598
def client_available(self):
6699
"""
67-
returns a client socket connection if available.otherwise, returns a non available socket
68-
:return the client
69-
:rtype Socket
100+
returns a client socket connection if available.
101+
Otherwise, returns None
102+
:return: the client
103+
:rtype: Socket
70104
"""
71105
sock = None
72106
if self._server_sock.socknum != NO_SOCK_AVAIL:
@@ -91,4 +125,7 @@ def client_available(self):
91125
self._client_sock = sock
92126
return self._client_sock
93127

94-
return socket.socket(socknum=NO_SOCK_AVAIL)
128+
return None
129+
130+
def _get_listener_key(self, method, path):
131+
return "{0}|{1}".format(str(method.lower(), 'utf-8'), str(path, 'utf-8'))

adafruit_esp32spi/adafruit_esp32spi_socket.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -162,14 +162,15 @@ def connected(self):
162162
return True
163163
else:
164164
status = _the_interface.socket_status(self.socknum)
165-
result = status not in (esp.SOCKET_LISTEN,
166-
esp.SOCKET_CLOSED,
167-
esp.SOCKET_FIN_WAIT_1,
168-
esp.SOCKET_FIN_WAIT_2,
169-
esp.SOCKET_TIME_WAIT,
170-
esp.SOCKET_SYN_SENT,
171-
esp.SOCKET_SYN_RCVD,
172-
esp.SOCKET_CLOSE_WAIT)
165+
# TODO: why is esp.<ConstantName> not defined? using magic numbers in mean time
166+
result = status not in (1,
167+
0,
168+
5,
169+
6,
170+
10,
171+
2,
172+
3,
173+
7)
173174
if not result:
174175
self.close()
175176
self._socknum = NO_SOCKET_AVAIL

examples/esp32spi_server.py

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,35 +20,53 @@
2020
esp32_reset = DigitalInOut(board.D7)
2121
esp32_gpio0 = DigitalInOut(board.D12)
2222

23+
"""Use below for Most Boards"""
24+
# status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2) # Uncomment for Most Boards
25+
"""Uncomment below for ItsyBitsy M4"""
26+
import adafruit_dotstar as dotstar
27+
status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=1)
28+
2329

2430
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
2531
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset, gpio0_pin=esp32_gpio0, debug=False)
2632

2733
## Connect to wifi with secrets
28-
wifi = wifimanager.ESPSPI_WiFiManager(esp, secrets, debug=True)
34+
wifi = wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light, debug=True)
2935
wifi.connect()
3036

3137
server.set_interface(esp)
32-
server = server.server(80, True)
33-
server.start()
38+
server = server.server(80, debug=False)
3439

35-
print("IP addr: ", esp.pretty_ip(esp.ip_address))
36-
print("server started!")
3740

41+
def onLedHigh(headers, body, client):
42+
print("led on!")
43+
status_light.fill((0, 0, 100))
44+
respond(headers, body, client)
3845

39-
while True:
46+
def onLedLow(headers, body, client):
47+
print("led off!")
48+
status_light.fill(0)
49+
respond(headers, body, client)
50+
51+
def respond(headers, body, client):
52+
client.write(b"HTTP/1.1 200 OK\r\n")
53+
client.write(b"Content-type:text/html\r\n")
54+
client.write(b"\r\n")
55+
56+
client.write(b"Click <a href=\"/H\">here</a> turn the LED on!!!<br>\r\n")
57+
client.write(b"Click <a href=\"/L\">here</a> turn the LED off!!!!<br>\r\n")
4058

41-
client = server.client_available()
42-
if (client.available()):
43-
data = client.read()
44-
if len(data):
45-
print(data)
46-
client.write(b"HTTP/1.1 200 OK\r\n")
47-
client.write(b"Content-type:text/html\r\n")
48-
client.write(b"\r\n")
59+
client.write(b"\r\n")
60+
client.close()
4961

50-
client.write(b"Click <a href=\"/H\">here</a> turn the LED on!!!<br>\r\n")
51-
client.write(b"Click <a href=\"/L\">here</a> turn the LED off!!!!<br>\r\n")
62+
server.on("GET", "/", respond)
63+
server.on("GET", "/H", onLedHigh)
64+
server.on("GET", "/L", onLedLow)
5265

53-
client.write(b"\r\n")
54-
client.close()
66+
67+
print("IP addr: ", esp.pretty_ip(esp.ip_address))
68+
69+
server.start()
70+
print("server started!")
71+
while True:
72+
server.update_poll()

0 commit comments

Comments
 (0)