-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathESPWebServer.py
278 lines (254 loc) · 8.35 KB
/
ESPWebServer.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
"""A simple HTTP server that only accept GET request
It adopt the programming style of ESP8266WebServer
library in ESP8266 Arduino Core
"""
import network
import machine
import socket
import uselect
import os
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# Use for checking a new client connection
poller = uselect.poll()
# Dict for registed GET handlers of all paths
getHandlers = {}
# Dict for registed POST handlers of all paths
postHandlers = {}
# Dict for registed PUT handlers of all paths
putHandlers = {}
# Function of handler for request not found
notFoundHandler = None
# The path to the web documents on MicroPython filesystem
docPath = "/"
# Data for template
tplData = {}
# Max. POST/PUT-Data size
maxContentLength = 1024
# MIME types
mimeTypes = {
".css":"text/css",
".jpg":"image/jpg",
".png":"image/png",
}
def begin(port=80):
"""Function to start http server
"""
global server, poller
server.bind(('0.0.0.0', port))
server.listen(1)
# Register for checking new client connection
poller.register(server, uselect.POLLIN)
def close():
"""Function to stop http server
"""
poller.unregister(server)
server.close()
def handleClient():
"""Check for new client connection and process the request
"""
global server, poller
# Note:don't call poll() with 0, that would randomly cause
# reset with "Fatal exception 28(LoadProhibitedCause)" message
res = poller.poll(1)
if res: # There's a new client connection
(socket, sockaddr) = server.accept()
socket.settimeout(0.02) # set timeout for readline to avoid blocking
handle(socket)
socket.close()
def __sendPage(socket, filePath):
"""Send the file as webpage to client
"""
try:
f = open(filePath, "rb")
while True:
data = f.read(64)
if (data == b""):
break
socket.write(data)
f.close()
except Exception as e:
print(e)
def err(socket, code, message):
"""Respong error meesage to client
"""
socket.write("HTTP/1.1 " + code + " " + message + "\r\n")
socket.write("Content-Type: text/html\r\n\r\n")
socket.write("<h1>" + message + "</h1>")
def ok(socket, code, *args):
"""Response successful message or webpage to client
"""
if len(args)==1:
content_type = "text/plain"
msg = args[0]
elif len(args)==2:
content_type = args[0]
msg = args[1]
else:
raise TypeError("ok() takes 3 or 4 positional arguments but "+ str(len(args)+2) +" were given")
socket.write("HTTP/1.1 " + code + " OK\r\n")
socket.write("Content-Type: " + content_type + "\r\n\r\n")
# if __fileExist(msg):
# filePath = msg
# __sendPage(socket, filePath)
# else:
socket.write(msg)
def __fileExist(path):
"""Check for file existence
"""
print(path)
try:
stat = os.stat(path)
# stat[0] bit 15 / 14 -> file/dir
if stat[0] & 0x8000 == 0x8000: # file
print("Found.")
return True
else: # Dir
return False
except:
print("Not Found.")
return False
def __serveFile(socket, path):
"""Serves a file from the filesystem
"""
filePath = docPath + path
fileFound = True
# find the file
if not __fileExist(filePath):
if not path.endswith("/"):
fileFound = False
else:
filePath = path + docPath + "/index.html"
# find index.html in the path
if not __fileExist(filePath):
filePath = path + docPath + "/index.p.html"
# find index.p.html in the path
if not __fileExist(filePath): # no default html file found
fileFound = False
if not fileFound: # file or default html file specified in path not found
if notFoundHandler:
notFoundHandler(socket)
else:
err(socket, "404", "Not Found")
return
# Responds the header first
socket.write("HTTP/1.1 200 OK\r\n")
contentType = "text/html"
for ext in mimeTypes:
if filePath.endswith(ext):
contentType = mimeTypes[ext]
socket.write("Content-Type: " + contentType + "\r\n\r\n")
# Responds the file content
if filePath.endswith(".p.html"):
print("template file.")
f = open(filePath, "r")
for l in f:
socket.write(l.format(**tplData))
f.close()
else:
__sendPage(socket, filePath)
def handle(socket):
"""Processing new GET request
"""
global docPath, handlers
try: # capture timeout for wainting a line
currLine = str(socket.readline(), 'utf-8')
except:
currLine = "" # readline timeout (not a complete line)
request = currLine.split(" ")
if len(request) != 3: # Discarded if it's a bad header
return
(method, url, version) = request
if "?" in url: # Check if there's query string?
(path, query) = url.split("?", 2)
else:
(path, query) = (url, "")
args = {}
contentType = ""
content = b""
contentLength = 0
if query: # Parsing the querying string
argPairs = query.split("&")
for argPair in argPairs:
arg = argPair.split("=")
args[arg[0]] = arg[1]
while True: # Read until blank line after header
header = socket.readline()
if header.startswith(b"Content-Length"):
(key, contentLengthStr) = str(header).split(" ")
contentLength = int(contentLengthStr[0:-5])
if (contentLength > maxContentLength):
err(socket, "400", "Bad Request")
return
if (header.startswith(b"Content-Type")):
(key, contentType) = str(header).split(" ")
contentType = contentType[0:-5]
if (header == b""):
return
if (header == b"\r\n" and contentLength > 0):
while(len(content) < contentLength):
content = content + socket.recv(contentLength)
if (len(content) > maxContentLength):
err(socket, "400", "Bad Request")
return
break
elif header == b"\r\n":
break
# Check for supported HTTP version
if version != "HTTP/1.0\r\n" and version != "HTTP/1.1\r\n":
err(socket, "505", "Version Not Supported")
elif (method != "GET" and method != "PUT" and method != "POST"): # Only accept GET,PUT and POST request
err(socket, "501", "Not Implemented")
elif (path in postHandlers and method == "POST"): # Check for registered POST path
postHandlers[path](socket, args, contentType, content)
elif (path in putHandlers and method == "PUT"): # Check for registered PUT path
putHandlers[path](socket, args, contentType, content)
elif (path in getHandlers and method == "GET"): # Check for registered GET path
getHandlers[path](socket, args)
elif path in getHandlers: # here to ensure compatibility
getHandlers[path](socket, args)
else: # find file in the document path
__serveFile(socket, path)
def onPath(path, handler):
"""Register handler for processing GET request of specified path,
Here to ensure compatibility.
"""
onGetPath(path, handler)
def onGetPath(path, handler):
"""Register handler for processing GET request of specified path
"""
global handlers
getHandlers[path] = handler
def onPostPath(path, handler):
"""Register handler for processing POST of specified path
"""
global handlers
postHandlers[path] = handler
def onPutPath(path, handler):
"""Register handler for processing PUT of specified path
"""
global handlers
putHandlers[path] = handler
def onNotFound(handler):
"""Register handler for processing request of not found path
"""
global notFoundHandler
notFoundHandler = handler
def setDocPath(path):
"""Set the path to documents' directory
"""
global docPath
if path.endswith("/"):
docPath = path[:-1]
else:
docPath = path
def setTplData(data):
"""Set data for template
"""
global tplData
tplData = data
def setMaxContentLength(max):
"""Set the maximum content lenpth for incomming data bodies
"""
global maxContentLength
maxContentLength = max