this isn't working right now; refactored and drastically minified microWebSrv, unfortunately on an esp8266 it won't import due to low memory. unsure why, will work out another day.
This commit is contained in:
parent
fd173c0f73
commit
7ed40e46a7
35
bme280.py
35
bme280.py
|
@ -1,37 +1,3 @@
|
||||||
# Authors: Paul Cunnane 2016, Peter Dahlebrg 2016
|
|
||||||
#
|
|
||||||
# This module borrows from the Adafruit BME280 Python library. Original
|
|
||||||
# Copyright notices are reproduced below.
|
|
||||||
#
|
|
||||||
# Those libraries were written for the Raspberry Pi. This modification is
|
|
||||||
# intended for the MicroPython and esp8266 boards.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2014 Adafruit Industries
|
|
||||||
# Author: Tony DiCola
|
|
||||||
#
|
|
||||||
# Based on the BMP280 driver with BME280 changes provided by
|
|
||||||
# David J Taylor, Edinburgh (www.satsignal.eu)
|
|
||||||
#
|
|
||||||
# Based on Adafruit_I2C.py created by Kevin Townsend.
|
|
||||||
#
|
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
|
||||||
# in the Software without restriction, including without limitation the rights
|
|
||||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
# copies of the Software, and to permit persons to whom the Software is
|
|
||||||
# furnished to do so, subject to the following conditions:
|
|
||||||
#
|
|
||||||
# The above copyright notice and this permission notice shall be included in
|
|
||||||
# all copies or substantial portions of the Software.
|
|
||||||
#
|
|
||||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
# THE SOFTWARE.
|
|
||||||
|
|
||||||
import time
|
import time
|
||||||
from ustruct import unpack, unpack_from
|
from ustruct import unpack, unpack_from
|
||||||
from array import array
|
from array import array
|
||||||
|
@ -51,7 +17,6 @@ BME280_REGISTER_CONTROL = 0xF4
|
||||||
|
|
||||||
|
|
||||||
class BME280:
|
class BME280:
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
mode=BME280_OSAMPLE_1,
|
mode=BME280_OSAMPLE_1,
|
||||||
address=BME280_I2CADDR,
|
address=BME280_I2CADDR,
|
||||||
|
|
13
boot.py
13
boot.py
|
@ -1,12 +1,7 @@
|
||||||
# This file is executed on every boot (including wake-boot from deepsleep)
|
|
||||||
#import esp
|
|
||||||
#esp.osdebug(None)
|
|
||||||
|
|
||||||
import network
|
|
||||||
from network import WLAN as wlan, STA_IF as staif, AP_IF as apif
|
from network import WLAN as wlan, STA_IF as staif, AP_IF as apif
|
||||||
import time
|
from time import sleep_ms
|
||||||
|
|
||||||
time.sleep_ms(100)
|
sleep_ms(100)
|
||||||
#make sure AP mode is off and station mode is on
|
#make sure AP mode is off and station mode is on
|
||||||
if wlan(apif).active() == True:
|
if wlan(apif).active() == True:
|
||||||
wlan(apif).active(False)
|
wlan(apif).active(False)
|
||||||
|
@ -30,7 +25,7 @@ if wlan(staif).isconnected() != True:
|
||||||
break
|
break
|
||||||
if found_net == False:
|
if found_net == False:
|
||||||
print("couldn't connect to wifi, nets found: %s" % nets)
|
print("couldn't connect to wifi, nets found: %s" % nets)
|
||||||
print("to fix temporarily, run: from network import STA_IF,WLAN as wlan;wlan(STA_IF).connect('yournet','yourpass'), followed shortly after by wlan(STA_IF).ifconfig()")
|
print("to fix temporarily, run: wlan(staif).connect('yournet','yourpass'), followed shortly after by wlan(staif).ifconfig()")
|
||||||
#exit
|
#exit
|
||||||
else:
|
else:
|
||||||
#Loop until wifi is connected and passes DHCP, or until 30 seconds have elapsed.
|
#Loop until wifi is connected and passes DHCP, or until 30 seconds have elapsed.
|
||||||
|
@ -38,7 +33,7 @@ if wlan(staif).isconnected() != True:
|
||||||
while wlan(staif).ifconfig()[0] == '0.0.0.0' or wlan(staif).isconnected() == False:
|
while wlan(staif).ifconfig()[0] == '0.0.0.0' or wlan(staif).isconnected() == False:
|
||||||
if slept > 300:
|
if slept > 300:
|
||||||
break
|
break
|
||||||
time.sleep_ms(100)
|
sleep_ms(100)
|
||||||
slept+=1
|
slept+=1
|
||||||
|
|
||||||
#import webrepl
|
#import webrepl
|
||||||
|
|
38
main.py
38
main.py
|
@ -6,58 +6,48 @@ hw = uPyConfig.esp8266(variant='d1-r2')
|
||||||
#init_sample.init_sample(hw)
|
#init_sample.init_sample(hw)
|
||||||
|
|
||||||
# Main app
|
# Main app
|
||||||
import uPySensor
|
|
||||||
sensors={
|
|
||||||
'bme280': uPySensor.BME280(hw.i2c.bus),
|
|
||||||
'lm75a': uPySensor.LM75A(hw.i2c.bus),
|
|
||||||
'sht21': uPySensor.SHT21(hw.i2c.bus),
|
|
||||||
}
|
|
||||||
|
|
||||||
from microWebSrv import MicroWebSrv
|
|
||||||
ws = MicroWebSrv()
|
|
||||||
ws.WebSocketThreaded = False
|
|
||||||
|
|
||||||
wshead={
|
wshead={
|
||||||
'Server':'horny',
|
'Server':'horny',
|
||||||
}
|
}
|
||||||
wsctype='application/json'
|
wsctype='application/json'
|
||||||
wscharset='UTF-8'
|
wscharset='UTF-8'
|
||||||
|
|
||||||
|
import uPySensor
|
||||||
|
bme280=uPySensor.BME280(hw.i2c.bus)
|
||||||
|
lm75a =uPySensor.LM75A(hw.i2c.bus)
|
||||||
|
sht21 =uPySensor.SHT21(hw.i2c.bus)
|
||||||
|
|
||||||
|
from microWebSrv import MicroWebSrv
|
||||||
@MicroWebSrv.route('/')
|
@MicroWebSrv.route('/')
|
||||||
def get_root(wscl, wsres):
|
def get_root(wscl, wsres):
|
||||||
wsres.WriteResponseOk(
|
wsres.WriteResponseOk(
|
||||||
headers=wshead,
|
headers=wshead,
|
||||||
contentType=wsctype,
|
contentType=wsctype,
|
||||||
contentCharset=wscharset,
|
|
||||||
content='{"result":"error","message":"use /bme280, /lm75a or /sht21 for sensor readings"}'
|
content='{"result":"error","message":"use /bme280, /lm75a or /sht21 for sensor readings"}'
|
||||||
)
|
)
|
||||||
@MicroWebSrv.route('/bme280')
|
@MicroWebSrv.route('/bme280')
|
||||||
def get_bme280(wscl, wsres):
|
def get_bme280(wscl, wsres):
|
||||||
sensors['bme280'].update_sensor()
|
bme280.update_sensor()
|
||||||
json = '{"temperature":"%0.2f","humidity":"%0.2f","pressure":"%0.2f"}' % (
|
|
||||||
sensors['bme280'].temperature, sensors['bme280'].humidity, sensors['bme280'].pressure)
|
|
||||||
wsres.WriteResponseOk(
|
wsres.WriteResponseOk(
|
||||||
headers=wshead,
|
headers=wshead,
|
||||||
contentType=wsctype,
|
contentType=wsctype,
|
||||||
contentCharset=wscharset,
|
content='{"temperature":"%0.2f","humidity":"%0.2f","pressure":"%0.2f"}' % (
|
||||||
content=None
|
bme280.temperature, bme280.humidity, bme280.pressure)
|
||||||
)
|
)
|
||||||
@MicroWebSrv.route('/lm75a')
|
@MicroWebSrv.route('/lm75a')
|
||||||
def get_lm75a(wscl, wsres):
|
def get_lm75a(wscl, wsres):
|
||||||
json = '{"temperature":"%0.1f"}' % sensors['lm75a'].read_tempC()
|
|
||||||
wsres.WriteResponseOk(
|
wsres.WriteResponseOk(
|
||||||
headers=wshead,
|
headers=wshead,
|
||||||
contentType=wsctype,
|
contentType=wsctype,
|
||||||
contentCharset=wscharset,
|
content='{"temperature":"%0.1f"}' % lm75a.read_tempC()
|
||||||
content=json
|
|
||||||
)
|
)
|
||||||
@MicroWebSrv.route('/sht21')
|
@MicroWebSrv.route('/sht21')
|
||||||
def sht21(wscl, wsres):
|
def sht21(wscl, wsres):
|
||||||
json = '{"temperature":"%0.3f","humidity":"%0.3f"}' % (sensors['sht21'].read_tempC(), sensors['sht21'].read_hum())
|
|
||||||
wsres.WriteResponseOk(
|
wsres.WriteResponseOk(
|
||||||
headers=wshead,
|
headers=wshead,
|
||||||
contentType=wsctype,
|
contentType=wsctype,
|
||||||
contentCharset=wscharset,
|
content='{"temperature":"%0.3f","humidity":"%0.3f"}' % (sht21.read_tempC(), sht21.read_hum())
|
||||||
content=json
|
|
||||||
)
|
)
|
||||||
|
|
||||||
ws.Start(threaded=False, stackSize=8192)
|
ws = MicroWebSrv()
|
||||||
|
ws.Start()
|
||||||
|
|
547
microWebSrv.py
547
microWebSrv.py
|
@ -1,17 +1,5 @@
|
||||||
"""
|
|
||||||
The MIT License (MIT)
|
|
||||||
Copyright © 2018 Jean-Christophe Bos & HC² (www.hc2.fr)
|
|
||||||
Copyright © 2018 LoBo (https://github.com/loboris/MicroPython_ESP32_psRAM_LoBo)
|
|
||||||
"""
|
|
||||||
|
|
||||||
from json import loads, dumps
|
|
||||||
from os import stat
|
|
||||||
import _thread
|
|
||||||
import network
|
|
||||||
import time
|
|
||||||
import socket
|
import socket
|
||||||
import gc
|
from ure import compile
|
||||||
import re
|
|
||||||
|
|
||||||
class MicroWebSrvRoute:
|
class MicroWebSrvRoute:
|
||||||
def __init__(self, route, method, func, routeArgNames, routeRegex):
|
def __init__(self, route, method, func, routeArgNames, routeRegex):
|
||||||
|
@ -21,12 +9,7 @@ class MicroWebSrvRoute:
|
||||||
self.routeArgNames = routeArgNames
|
self.routeArgNames = routeArgNames
|
||||||
self.routeRegex = routeRegex
|
self.routeRegex = routeRegex
|
||||||
|
|
||||||
|
|
||||||
class MicroWebSrv:
|
class MicroWebSrv:
|
||||||
# ============================================================================
|
|
||||||
# ===( Constants )============================================================
|
|
||||||
# ============================================================================
|
|
||||||
|
|
||||||
_html_escape_chars = {
|
_html_escape_chars = {
|
||||||
"&": "&",
|
"&": "&",
|
||||||
'"': """,
|
'"': """,
|
||||||
|
@ -34,62 +17,29 @@ class MicroWebSrv:
|
||||||
">": ">",
|
">": ">",
|
||||||
"<": "<"
|
"<": "<"
|
||||||
}
|
}
|
||||||
|
_decoratedRouteHandlers = []
|
||||||
# ============================================================================
|
|
||||||
# ===( Class globals )=======================================================
|
|
||||||
# ============================================================================
|
|
||||||
|
|
||||||
_docoratedRouteHandlers = []
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# ===( Utils )===============================================================
|
|
||||||
# ============================================================================
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def route(cls, url, method='GET'):
|
def route(cls, url, method='GET'):
|
||||||
""" Adds a route handler function to the routing list """
|
|
||||||
|
|
||||||
def route_decorator(func):
|
def route_decorator(func):
|
||||||
item = (url, method, func)
|
item = (url, method, func)
|
||||||
cls._docoratedRouteHandlers.append(item)
|
cls._decoratedRouteHandlers.append(item)
|
||||||
return func
|
return func
|
||||||
|
|
||||||
return route_decorator
|
return route_decorator
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def HTMLEscape(s):
|
def HTMLEscape(s):
|
||||||
return ''.join(MicroWebSrv._html_escape_chars.get(c, c) for c in s)
|
return ''.join(MicroWebSrv._html_escape_chars.get(c, c) for c in s)
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _tryAllocByteArray(size):
|
def _tryAllocByteArray(size):
|
||||||
for x in range(10):
|
for x in range(10):
|
||||||
try:
|
try:
|
||||||
gc.collect()
|
|
||||||
return bytearray(size)
|
return bytearray(size)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _tryStartThread(func, args=(), stacksize=8192):
|
|
||||||
_ = _thread.stack_size(stacksize)
|
|
||||||
for x in range(10):
|
|
||||||
try:
|
|
||||||
gc.collect()
|
|
||||||
th = _thread.start_new_thread("MicroWebServer", func, args)
|
|
||||||
return th
|
|
||||||
except:
|
|
||||||
time.sleep_ms(100)
|
|
||||||
return False
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _unquote(s):
|
def _unquote(s):
|
||||||
r = s.split('%')
|
r = s.split('%')
|
||||||
|
@ -101,33 +51,22 @@ class MicroWebSrv:
|
||||||
r[i] = '%' + s
|
r[i] = '%' + s
|
||||||
return ''.join(r)
|
return ''.join(r)
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _unquote_plus(s):
|
def _unquote_plus(s):
|
||||||
return MicroWebSrv._unquote(s.replace('+', ' '))
|
return MicroWebSrv._unquote(s.replace('+', ' '))
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# ===( Constructor )==========================================================
|
|
||||||
# ============================================================================
|
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
routeHandlers=[],
|
routeHandlers=[],
|
||||||
port=80,
|
port=80,
|
||||||
bindIP='0.0.0.0'):
|
bindIP='0.0.0.0'):
|
||||||
|
|
||||||
self._srvAddr = (bindIP, port)
|
self._srvAddr = (bindIP, port)
|
||||||
self._notFoundUrl = None
|
|
||||||
self._started = False
|
self._started = False
|
||||||
self.thID = None
|
|
||||||
self.isThreaded = False
|
|
||||||
self._state = "Stopped"
|
|
||||||
|
|
||||||
self._routeHandlers = []
|
self._routeHandlers = []
|
||||||
routeHandlers += self._docoratedRouteHandlers
|
routeHandlers += self._decoratedRouteHandlers
|
||||||
for route, method, func in routeHandlers:
|
for route, method, func in routeHandlers:
|
||||||
routeParts = route.split('/')
|
routeParts = route.split('/')
|
||||||
# -> ['', 'users', '<uID>', 'addresses', '<addrID>', 'test', '<anotherID>']
|
|
||||||
routeArgNames = []
|
routeArgNames = []
|
||||||
routeRegex = ''
|
routeRegex = ''
|
||||||
for s in routeParts:
|
for s in routeParts:
|
||||||
|
@ -137,133 +76,59 @@ class MicroWebSrv:
|
||||||
elif s:
|
elif s:
|
||||||
routeRegex += '/' + s
|
routeRegex += '/' + s
|
||||||
routeRegex += '$'
|
routeRegex += '$'
|
||||||
# -> '/users/(\w*)/addresses/(\w*)/test/(\w*)$'
|
routeRegex = compile(routeRegex)
|
||||||
routeRegex = re.compile(routeRegex)
|
|
||||||
|
|
||||||
self._routeHandlers.append(MicroWebSrvRoute(route, method, func, routeArgNames, routeRegex))
|
self._routeHandlers.append(MicroWebSrvRoute(route, method, func, routeArgNames, routeRegex))
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# ===( Server Process )=======================================================
|
|
||||||
# ============================================================================
|
|
||||||
|
|
||||||
def _serverProcess(self):
|
def _serverProcess(self):
|
||||||
self._started = True
|
self._started = True
|
||||||
self._state = "Running"
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
client, cliAddr = self._server.accepted()
|
client, cliAddr = self._server.accepted()
|
||||||
if client == None:
|
if client == None: continue
|
||||||
if self.isThreaded:
|
|
||||||
notify = _thread.getnotification()
|
|
||||||
if notify == _thread.EXIT:
|
|
||||||
break
|
|
||||||
elif notify == _thread.SUSPEND:
|
|
||||||
self._state = "Suspended"
|
|
||||||
while _thread.wait() != _thread.RESUME:
|
|
||||||
pass
|
|
||||||
self._state = "Running"
|
|
||||||
# gc.collect()
|
|
||||||
time.sleep_ms(2)
|
|
||||||
continue
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if not self.isThreaded:
|
print(e)
|
||||||
print(e)
|
|
||||||
break
|
break
|
||||||
self._client(self, client, cliAddr)
|
self._client(self, client, cliAddr)
|
||||||
self._started = False
|
self._started = False
|
||||||
self._state = "Stopped"
|
|
||||||
self.thID = None
|
|
||||||
|
|
||||||
# ============================================================================
|
def Start(self):
|
||||||
# ===( Functions )============================================================
|
if self._started: return
|
||||||
# ============================================================================
|
self._server = socket.socket(socket.AF_INET,
|
||||||
|
socket.SOCK_STREAM,
|
||||||
def Start(self, threaded=True, stackSize=8192):
|
socket.IPPROTO_TCP)
|
||||||
if not self._started:
|
self._server.setsockopt(socket.SOL_SOCKET,
|
||||||
if not network.WLAN().wifiactive():
|
socket.SO_REUSEADDR,
|
||||||
print("WLAN not connected!")
|
1)
|
||||||
return
|
self._server.bind(self._srvAddr)
|
||||||
gc.collect()
|
self._server.listen(1)
|
||||||
self._server = socket.socket(socket.AF_INET,
|
self._server.settimeout(0.5)
|
||||||
socket.SOCK_STREAM,
|
self._serverProcess()
|
||||||
socket.IPPROTO_TCP)
|
|
||||||
self._server.setsockopt(socket.SOL_SOCKET,
|
|
||||||
socket.SO_REUSEADDR,
|
|
||||||
1)
|
|
||||||
self._server.bind(self._srvAddr)
|
|
||||||
self._server.listen(1)
|
|
||||||
self.isThreaded = threaded
|
|
||||||
# using non-blocking socket
|
|
||||||
self._server.settimeout(0.5)
|
|
||||||
if threaded:
|
|
||||||
th = MicroWebSrv._tryStartThread(self._serverProcess, stacksize=stackSize)
|
|
||||||
if th:
|
|
||||||
self.thID = th
|
|
||||||
else:
|
|
||||||
self._serverProcess()
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def Stop(self):
|
def Stop(self):
|
||||||
if self._started:
|
if not self._started: return
|
||||||
self._server.close()
|
self._server.close()
|
||||||
if self.isThreaded:
|
|
||||||
_ = _thread.notify(self.thID, _thread.EXIT)
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def IsStarted(self):
|
|
||||||
return self._started
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def threadID(self):
|
|
||||||
return self.thID
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def State(self):
|
|
||||||
return self._state
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def SetNotFoundPageUrl(self, url=None):
|
|
||||||
self._notFoundUrl = url
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def GetRouteHandler(self, resUrl, method):
|
def GetRouteHandler(self, resUrl, method):
|
||||||
if self._routeHandlers:
|
if not self._routeHandlers:
|
||||||
# resUrl = resUrl.upper()
|
return (None, None)
|
||||||
if resUrl.endswith('/'):
|
if resUrl.endswith('/'):
|
||||||
resUrl = resUrl[:-1]
|
resUrl = resUrl[:-1]
|
||||||
method = method.upper()
|
method = method.upper()
|
||||||
for rh in self._routeHandlers:
|
for rh in self._routeHandlers:
|
||||||
if rh.method == method:
|
if rh.method == method:
|
||||||
m = rh.routeRegex.match(resUrl)
|
m = rh.routeRegex.match(resUrl)
|
||||||
if m: # found matching route?
|
if m and rh.routeArgNames:
|
||||||
if rh.routeArgNames:
|
routeArgs = {}
|
||||||
routeArgs = {}
|
for i, name in enumerate(rh.routeArgNames):
|
||||||
for i, name in enumerate(rh.routeArgNames):
|
value = m.group(i + 1)
|
||||||
value = m.group(i + 1)
|
try: value = int(value)
|
||||||
try:
|
except: pass
|
||||||
value = int(value)
|
routeArgs[name] = value
|
||||||
except:
|
return (rh.func, routeArgs)
|
||||||
pass
|
elif m: return (rh.func, None)
|
||||||
routeArgs[name] = value
|
|
||||||
return (rh.func, routeArgs)
|
|
||||||
else:
|
|
||||||
return (rh.func, None)
|
|
||||||
return (None, None)
|
return (None, None)
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# ===( Class Client )========================================================
|
|
||||||
# ============================================================================
|
|
||||||
|
|
||||||
class _client:
|
class _client:
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def __init__(self, microWebSrv, socket, addr):
|
def __init__(self, microWebSrv, socket, addr):
|
||||||
socket.settimeout(2)
|
socket.settimeout(2)
|
||||||
self._microWebSrv = microWebSrv
|
self._microWebSrv = microWebSrv
|
||||||
|
@ -281,63 +146,43 @@ class MicroWebSrv:
|
||||||
|
|
||||||
self._processRequest()
|
self._processRequest()
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def _processRequest(self):
|
def _processRequest(self):
|
||||||
try:
|
try:
|
||||||
response = MicroWebSrv._response(self)
|
response = MicroWebSrv._response(self)
|
||||||
if self._parseFirstLine(response):
|
if self._parseFirstLine(response) and self._parseHeader(response):
|
||||||
if self._parseHeader(response):
|
routeHandler, routeArgs = self._microWebSrv.GetRouteHandler(self._resPath, self._method)
|
||||||
upg = self._getConnUpgrade()
|
if routeHandler and routeArgs is not None:
|
||||||
if not upg:
|
routeHandler(self, response, routeArgs)
|
||||||
routeHandler, routeArgs = self._microWebSrv.GetRouteHandler(self._resPath, self._method)
|
elif routeHandler:
|
||||||
if routeHandler:
|
routeHandler(self, response)
|
||||||
if routeArgs is not None:
|
else: response.WriteResponseNotFound()
|
||||||
routeHandler(self, response, routeArgs)
|
except: response.WriteResponseInternalServerError()
|
||||||
else:
|
try: self._socket.close()
|
||||||
routeHandler(self, response)
|
except: pass
|
||||||
elif self._method.upper() == "GET":
|
|
||||||
response.WriteResponseNotFound()
|
|
||||||
else:
|
|
||||||
response.WriteResponseMethodNotAllowed()
|
|
||||||
else:
|
|
||||||
response.WriteResponseNotImplemented()
|
|
||||||
else:
|
|
||||||
response.WriteResponseBadRequest()
|
|
||||||
except:
|
|
||||||
response.WriteResponseInternalServerError()
|
|
||||||
try:
|
|
||||||
self._socket.close()
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def _parseFirstLine(self, response):
|
def _parseFirstLine(self, response):
|
||||||
try:
|
try:
|
||||||
elements = self._socket.readline().decode().strip().split()
|
elements = self._socket.readline().decode().strip().split()
|
||||||
if len(elements) == 3:
|
if len(elements) != 3: return False
|
||||||
self._method = elements[0].upper()
|
self._method = elements[0].upper()
|
||||||
self._path = elements[1]
|
self._path = elements[1]
|
||||||
self._httpVer = elements[2].upper()
|
self._httpVer = elements[2].upper()
|
||||||
elements = self._path.split('?', 1)
|
elements = self._path.split('?', 1)
|
||||||
if len(elements) > 0:
|
if len(elements) < 1: return True
|
||||||
self._resPath = MicroWebSrv._unquote_plus(elements[0])
|
self._resPath = MicroWebSrv._unquote_plus(elements[0])
|
||||||
if len(elements) > 1:
|
if len(elements) < 2: return True
|
||||||
self._queryString = elements[1]
|
self._queryString = elements[1]
|
||||||
elements = self._queryString.split('&')
|
elements = self._queryString.split('&')
|
||||||
for s in elements:
|
for s in elements:
|
||||||
param = s.split('=', 1)
|
param = s.split('=', 1)
|
||||||
if len(param) > 0:
|
if len(param) > 0:
|
||||||
value = MicroWebSrv._unquote(param[1]) if len(param) > 1 else ''
|
value = MicroWebSrv._unquote(param[1]) if len(param) > 1 else ''
|
||||||
self._queryParams[MicroWebSrv._unquote(param[0])] = value
|
self._queryParams[MicroWebSrv._unquote(param[0])] = value
|
||||||
return True
|
return True
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def _parseHeader(self, response):
|
def _parseHeader(self, response):
|
||||||
while True:
|
while True:
|
||||||
elements = self._socket.readline().decode().strip().split(':', 1)
|
elements = self._socket.readline().decode().strip().split(':', 1)
|
||||||
|
@ -351,75 +196,6 @@ class MicroWebSrv:
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def _getConnUpgrade(self):
|
|
||||||
if 'upgrade' in self._headers.get('Connection', '').lower():
|
|
||||||
return self._headers.get('Upgrade', '').lower()
|
|
||||||
return None
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def GetServer(self):
|
|
||||||
return self._microWebSrv
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def GetAddr(self):
|
|
||||||
return self._addr
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def GetIPAddr(self):
|
|
||||||
return self._addr[0]
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def GetPort(self):
|
|
||||||
return self._addr[1]
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def GetRequestMethod(self):
|
|
||||||
return self._method
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def GetRequestTotalPath(self):
|
|
||||||
return self._path
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def GetRequestPath(self):
|
|
||||||
return self._resPath
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def GetRequestQueryString(self):
|
|
||||||
return self._queryString
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def GetRequestQueryParams(self):
|
|
||||||
return self._queryParams
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def GetRequestHeaders(self):
|
|
||||||
return self._headers
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def GetRequestContentType(self):
|
|
||||||
return self._contentType
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def GetRequestContentLength(self):
|
|
||||||
return self._contentLength
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def ReadRequestContent(self, size=None):
|
def ReadRequestContent(self, size=None):
|
||||||
self._socket.setblocking(False)
|
self._socket.setblocking(False)
|
||||||
b = None
|
b = None
|
||||||
|
@ -433,59 +209,34 @@ class MicroWebSrv:
|
||||||
self._socket.setblocking(True)
|
self._socket.setblocking(True)
|
||||||
return b if b else b''
|
return b if b else b''
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def ReadRequestPostedFormData(self):
|
def ReadRequestPostedFormData(self):
|
||||||
res = {}
|
res = {}
|
||||||
data = self.ReadRequestContent()
|
data = self.ReadRequestContent()
|
||||||
if len(data) > 0:
|
if len(data) < 1: return res
|
||||||
elements = data.decode().split('&')
|
elements = data.decode().split('&')
|
||||||
for s in elements:
|
for s in elements:
|
||||||
param = s.split('=', 1)
|
param = s.split('=', 1)
|
||||||
if len(param) > 0:
|
if len(param) < 1: next
|
||||||
value = MicroWebSrv._unquote(param[1]) if len(param) > 1 else ''
|
value = MicroWebSrv._unquote(param[1]) if len(param) > 1 else ''
|
||||||
res[MicroWebSrv._unquote(param[0])] = value
|
res[MicroWebSrv._unquote(param[0])] = value
|
||||||
return res
|
return res
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def ReadRequestContentAsJSON(self):
|
|
||||||
try:
|
|
||||||
return loads(self.ReadRequestContent())
|
|
||||||
except:
|
|
||||||
return None
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# ===( Class Response )======================================================
|
|
||||||
# ============================================================================
|
|
||||||
|
|
||||||
class _response:
|
class _response:
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def __init__(self, client):
|
def __init__(self, client):
|
||||||
self._client = client
|
self._client = client
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def _write(self, data):
|
def _write(self, data):
|
||||||
if type(data) == str:
|
if type(data) == str:
|
||||||
data = data.encode()
|
data = data.encode()
|
||||||
return self._client._socket.write(data)
|
return self._client._socket.write(data)
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def _writeFirstLine(self, code):
|
def _writeFirstLine(self, code):
|
||||||
reason = self._responseCodes.get(code, ('Unknown reason',))[0]
|
reason = self._responseCodes.get(code, ('Unknown reason',))[0]
|
||||||
self._write("HTTP/1.1 %s %s\r\n" % (code, reason))
|
self._write("HTTP/1.1 %s %s\r\n" % (code, reason))
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def _writeHeader(self, name, value):
|
def _writeHeader(self, name, value):
|
||||||
self._write("%s: %s\r\n" % (name, value))
|
self._write("%s: %s\r\n" % (name, value))
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def _writeContentTypeHeader(self, contentType, charset=None):
|
def _writeContentTypeHeader(self, contentType, charset=None):
|
||||||
if contentType:
|
if contentType:
|
||||||
ct = contentType \
|
ct = contentType \
|
||||||
|
@ -494,18 +245,12 @@ class MicroWebSrv:
|
||||||
ct = "application/octet-stream"
|
ct = "application/octet-stream"
|
||||||
self._writeHeader("Content-Type", ct)
|
self._writeHeader("Content-Type", ct)
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def _writeServerHeader(self):
|
def _writeServerHeader(self):
|
||||||
self._writeHeader("Server", "MicroWebSrv by JC`zic")
|
self._writeHeader("Server", "MicroWebSrv lite")
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def _writeEndHeader(self):
|
def _writeEndHeader(self):
|
||||||
self._write("\r\n")
|
self._write("\r\n")
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def _writeBeforeContent(self, code, headers, contentType, contentCharset, contentLength):
|
def _writeBeforeContent(self, code, headers, contentType, contentCharset, contentLength):
|
||||||
self._writeFirstLine(code)
|
self._writeFirstLine(code)
|
||||||
if isinstance(headers, dict):
|
if isinstance(headers, dict):
|
||||||
|
@ -518,20 +263,6 @@ class MicroWebSrv:
|
||||||
self._writeHeader("Connection", "close")
|
self._writeHeader("Connection", "close")
|
||||||
self._writeEndHeader()
|
self._writeEndHeader()
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def WriteSwitchProto(self, upgrade, headers=None):
|
|
||||||
self._writeFirstLine(101)
|
|
||||||
self._writeHeader("Connection", "Upgrade")
|
|
||||||
self._writeHeader("Upgrade", upgrade)
|
|
||||||
if isinstance(headers, dict):
|
|
||||||
for header in headers:
|
|
||||||
self._writeHeader(header, headers[header])
|
|
||||||
self._writeServerHeader()
|
|
||||||
self._writeEndHeader()
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def WriteResponse(self, code, headers, contentType, contentCharset, content):
|
def WriteResponse(self, code, headers, contentType, contentCharset, content):
|
||||||
try:
|
try:
|
||||||
contentLength = len(content) if content else 0
|
contentLength = len(content) if content else 0
|
||||||
|
@ -542,175 +273,47 @@ class MicroWebSrv:
|
||||||
except:
|
except:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def WriteResponseOk(self, headers=None, contentType=None, contentCharset=None, content=None):
|
def WriteResponseOk(self, headers=None, contentType=None, contentCharset=None, content=None):
|
||||||
return self.WriteResponse(200, headers, contentType, contentCharset, content)
|
return self.WriteResponse(200, headers, contentType, contentCharset, content)
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def WriteResponseJSONOk(self, obj=None, headers=None):
|
|
||||||
return self.WriteResponse(200, headers, "application/json", "UTF-8", dumps(obj))
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def WriteResponseRedirect(self, location):
|
def WriteResponseRedirect(self, location):
|
||||||
headers = {"Location": location}
|
headers = {"Location": location}
|
||||||
return self.WriteResponse(302, headers, None, None, None)
|
return self.WriteResponse(302, headers, None, None, None)
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def WriteResponseError(self, code):
|
def WriteResponseError(self, code):
|
||||||
responseCode = self._responseCodes.get(code, ('Unknown reason', ''))
|
responseCode = self._responseCodes.get(code, ('Unknown reason', ''))
|
||||||
return self.WriteResponse(code,
|
return self.WriteResponse(code,
|
||||||
None,
|
None,
|
||||||
"text/html",
|
"text/html",
|
||||||
"UTF-8",
|
"UTF-8",
|
||||||
self._errCtnTmpl % {
|
'{"code":"%s","reason":"%s","message":"%s"}' % (
|
||||||
'code': code,
|
code, responseCode[0], responseCode[1]))
|
||||||
'reason': responseCode[0],
|
|
||||||
'message': responseCode[1]
|
|
||||||
})
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def WriteResponseJSONError(self, code, obj=None):
|
|
||||||
return self.WriteResponse(code,
|
|
||||||
None,
|
|
||||||
"application/json",
|
|
||||||
"UTF-8",
|
|
||||||
dumps(obj if obj else {}))
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def WriteResponseBadRequest(self):
|
def WriteResponseBadRequest(self):
|
||||||
return self.WriteResponseError(400)
|
return self.WriteResponseError(400)
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def WriteResponseForbidden(self):
|
def WriteResponseForbidden(self):
|
||||||
return self.WriteResponseError(403)
|
return self.WriteResponseError(403)
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def WriteResponseNotFound(self):
|
def WriteResponseNotFound(self):
|
||||||
if self._client._microWebSrv._notFoundUrl:
|
return self.WriteResponseError(404)
|
||||||
self.WriteResponseRedirect(self._client._microWebSrv._notFoundUrl)
|
|
||||||
else:
|
|
||||||
return self.WriteResponseError(404)
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def WriteResponseMethodNotAllowed(self):
|
|
||||||
return self.WriteResponseError(405)
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def WriteResponseInternalServerError(self):
|
def WriteResponseInternalServerError(self):
|
||||||
return self.WriteResponseError(500)
|
return self.WriteResponseError(500)
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def WriteResponseNotImplemented(self):
|
|
||||||
return self.WriteResponseError(501)
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
_errCtnTmpl = """\
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Error</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>%(code)d %(reason)s</h1>
|
|
||||||
%(message)s
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
"""
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
_execErrCtnTmpl = """\
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Page execution error</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>%(module)s page execution error</h1>
|
|
||||||
%(message)s
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
"""
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
_responseCodes = {
|
_responseCodes = {
|
||||||
100: ('Continue', 'Request received, please continue'),
|
100: ('Continue', 'Request received, please continue'),
|
||||||
101: ('Switching Protocols',
|
101: ('Switching Protocols',
|
||||||
'Switching to new protocol; obey Upgrade header'),
|
'Switching to new protocol; obey Upgrade header'),
|
||||||
|
|
||||||
200: ('OK', 'Request fulfilled, document follows'),
|
200: ('OK', 'Request fulfilled, document follows'),
|
||||||
201: ('Created', 'Document created, URL follows'),
|
201: ('Created', 'Document created, URL follows'),
|
||||||
202: ('Accepted',
|
202: ('Accepted',
|
||||||
'Request accepted, processing continues off-line'),
|
'Request accepted, processing continues off-line'),
|
||||||
203: ('Non-Authoritative Information', 'Request fulfilled from cache'),
|
|
||||||
204: ('No Content', 'Request fulfilled, nothing follows'),
|
204: ('No Content', 'Request fulfilled, nothing follows'),
|
||||||
205: ('Reset Content', 'Clear input form for further input.'),
|
|
||||||
206: ('Partial Content', 'Partial content follows.'),
|
|
||||||
|
|
||||||
300: ('Multiple Choices',
|
|
||||||
'Object has several resources -- see URI list'),
|
|
||||||
301: ('Moved Permanently', 'Object moved permanently -- see URI list'),
|
301: ('Moved Permanently', 'Object moved permanently -- see URI list'),
|
||||||
302: ('Found', 'Object moved temporarily -- see URI list'),
|
302: ('Found', 'Object moved temporarily -- see URI list'),
|
||||||
303: ('See Other', 'Object moved -- see Method and URL list'),
|
|
||||||
304: ('Not Modified',
|
|
||||||
'Document has not changed since given time'),
|
|
||||||
305: ('Use Proxy',
|
|
||||||
'You must use proxy specified in Location to access this '
|
|
||||||
'resource.'),
|
|
||||||
307: ('Temporary Redirect',
|
|
||||||
'Object moved temporarily -- see URI list'),
|
|
||||||
|
|
||||||
400: ('Bad Request',
|
|
||||||
'Bad request syntax or unsupported method'),
|
|
||||||
401: ('Unauthorized',
|
|
||||||
'No permission -- see authorization schemes'),
|
|
||||||
402: ('Payment Required',
|
|
||||||
'No payment -- see charging schemes'),
|
|
||||||
403: ('Forbidden',
|
|
||||||
'Request forbidden -- authorization will not help'),
|
|
||||||
404: ('Not Found', 'Nothing matches the given URI'),
|
404: ('Not Found', 'Nothing matches the given URI'),
|
||||||
405: ('Method Not Allowed',
|
|
||||||
'Specified method is invalid for this resource.'),
|
|
||||||
406: ('Not Acceptable', 'URI not available in preferred format.'),
|
|
||||||
407: ('Proxy Authentication Required', 'You must authenticate with '
|
|
||||||
'this proxy before proceeding.'),
|
|
||||||
408: ('Request Timeout', 'Request timed out; try again later.'),
|
|
||||||
409: ('Conflict', 'Request conflict.'),
|
|
||||||
410: ('Gone',
|
|
||||||
'URI no longer exists and has been permanently removed.'),
|
|
||||||
411: ('Length Required', 'Client must specify Content-Length.'),
|
|
||||||
412: ('Precondition Failed', 'Precondition in headers is false.'),
|
|
||||||
413: ('Request Entity Too Large', 'Entity is too large.'),
|
|
||||||
414: ('Request-URI Too Long', 'URI is too long.'),
|
|
||||||
415: ('Unsupported Media Type', 'Entity body in unsupported format.'),
|
|
||||||
416: ('Requested Range Not Satisfiable',
|
|
||||||
'Cannot satisfy request range.'),
|
|
||||||
417: ('Expectation Failed',
|
|
||||||
'Expect condition could not be satisfied.'),
|
|
||||||
|
|
||||||
500: ('Internal Server Error', 'Server got itself in trouble'),
|
500: ('Internal Server Error', 'Server got itself in trouble'),
|
||||||
501: ('Not Implemented',
|
501: ('Not Implemented',
|
||||||
'Server does not support this operation'),
|
'Server does not support this operation'),
|
||||||
502: ('Bad Gateway', 'Invalid responses from another server/proxy.'),
|
|
||||||
503: ('Service Unavailable',
|
|
||||||
'The server cannot process the request due to a high load'),
|
|
||||||
504: ('Gateway Timeout',
|
|
||||||
'The gateway server did not receive a timely response'),
|
|
||||||
505: ('HTTP Version Not Supported', 'Cannot fulfill request.'),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# ============================================================================
|
|
||||||
# ============================================================================
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue