feat: created decorator powered http server

This commit is contained in:
Elias Renman
2023-05-22 23:44:24 +02:00
parent 323edfacf7
commit a695989474
5 changed files with 156 additions and 49 deletions

3
pico-w/.gitignore vendored
View File

@@ -1,3 +1,4 @@
.env
env.json
config.py
config.py
__pycache__

6
pico-w/src/codes.py Normal file
View File

@@ -0,0 +1,6 @@
codes = {
200: 'OK',
400: 'Bad Request',
404: 'Not Found',
405: 'Method Not Allowed',
}

114
pico-w/src/decorators.py Normal file
View File

@@ -0,0 +1,114 @@
import socket
import json
from codes import codes
def respond(cl: socket.socket, status: int, response: dict | list):
stringified = json.dumps(response, separators=(',', ':'))
cl.send(
f'HTTP/1.0 {status} {codes.get(status)}\r\nContent-type: text/json\r\n\r\n')
cl.send(stringified)
cl.close()
class HttpError(Exception):
def __init__(self, status: int, message: dict | list):
super().__init__(message)
self.status = status
self.message = message
class Endpoint:
def __init__(self, path: str, method='GET'):
if method not in ('GET', 'POST', 'PATCH', 'PUT', 'DELETE'):
raise ValueError(
'Valid values are GET, POST, PATCH, PUT', 'DELETE')
self.method = method
self.path = path
def __call__(self, function):
def wrapper(instance, *args, **kwargs):
request: str = instance.__request # type: ignore
cl: socket.socket = instance.__cl # type: ignore
# TODO: Improve find checker here to be more precise when dealing with requests.
if request.find(self.path) == -1 and self.path != '*' or request.find(self.method) == -1:
return
try:
val = function(instance, *args, **kwargs)
respond(cl, 200, val)
return val
except HttpError as e:
respond(cl, e.status,
e.message)
return wrapper
class ServerHandler(object):
def __init__(self, s: socket.socket):
not_found_endpoints = (self.__post_not_found,
self.__get_not_found, self.__patch_not_found, self.__put_not_found, self.__delete_not_found)
while True:
try:
cl, addr = s.accept()
print('client connected from', addr)
request: bytes = cl.recv(1024)
print(f"{request} \n")
self.__request = request.decode('utf-8')
self.__cl = cl
method_list = dir(self.__class__)
alreadyReturned = False
for endpoint in method_list:
if endpoint.startswith('_') is True:
continue
func = getattr(self.__class__, endpoint)
if not callable(func):
continue
result = func(self)
if result:
alreadyReturned = True
break
if (not alreadyReturned):
for endpoint in not_found_endpoints:
result = endpoint()
if result:
break
except OSError as e:
print(e)
self.__cl.close()
s.close()
print('connection closed')
break
@Endpoint('*', 'POST')
def __post_not_found(self):
raise HttpError(404, {'status': 'Not Found'})
@Endpoint('*', 'GET')
def __get_not_found(self):
raise HttpError(404, {'status': 'Not Found'})
@Endpoint('*', 'PATCH')
def __patch_not_found(self):
raise HttpError(404, {'status': 'Not Found'})
@Endpoint('*', 'PUT')
def __put_not_found(self):
raise HttpError(404, {'status': 'Not Found'})
@Endpoint('*', 'DELETE')
def __delete_not_found(self):
raise HttpError(404, {'status': 'Not Found'})

View File

@@ -1,9 +1,7 @@
import socket
from utime import sleep_ms
from machine import Pin
from config import ssid, password
from server import initalize_app
from config import led
led = Pin("LED", Pin.OUT)
led.value(1)
sleep_ms(200)
@@ -11,47 +9,4 @@ led.value(0)
print("Successfully started pico...")
addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1]
s = socket.socket()
s.bind(addr)
s.listen(1)
print('listening on', addr)
def respond(cl, status, response):
cl.send(f'HTTP/1.0 {status} OK\r\nContent-type: text/html\r\n\r\n')
cl.send(response)
cl.close()
# Listen for connections
while True:
try:
cl, addr = s.accept()
print('client connected from', addr)
request = cl.recv(1024)
print(request)
request = str(request)
led_on = request.find('/light/on')
led_off = request.find('/light/off')
print('led on = ' + str(led_on))
print('led off = ' + str(led_off))
if led_on == 6:
print("led on")
led.value(1)
respond(cl, 200, '{"led": "on"}')
if led_off == 6:
print("led off")
led.value(0)
respond(cl, 200, '{"led": "off"}')
except OSError as e:
cl.close()
s.close()
print('connection closed')
initalize_app()

31
pico-w/src/server.py Normal file
View File

@@ -0,0 +1,31 @@
import socket
from config import led
from decorators import Endpoint, ServerHandler
def initalize_app():
addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1]
s = socket.socket()
s.bind(addr)
s.listen(1)
print('listening on', addr)
# Listen for connections
Handler(s)
class Handler(ServerHandler):
@Endpoint('light/on', 'POST')
def light_on(self):
print("led on")
led.value(1)
return {"led": "on"}
@Endpoint('light/off', 'POST')
def light_off(self):
print("led off")
led.value(0)
return {"led": "off"}