From 0fa7e565a5261f6d707dc807eea39d379eca9941 Mon Sep 17 00:00:00 2001 From: meebox Date: Thu, 3 Sep 2020 11:20:10 +0800 Subject: [PATCH 1/9] correct mimetypes for files. --- ESP8266WebServer.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/ESP8266WebServer.py b/ESP8266WebServer.py index 24edf5a..1ffd5d9 100644 --- a/ESP8266WebServer.py +++ b/ESP8266WebServer.py @@ -23,6 +23,13 @@ # Data for template tplData = {} +# MIME types +mimeTypes = { + ".css":"text/css", + ".jpg":"image/jpg", + ".png":"image/png", +} + def begin(port): """Function to start http server """ @@ -163,11 +170,11 @@ def handle(socket): # Responds the header first socket.write("HTTP/1.1 200 OK\r\n") - socket.write("Content-Type: text/html\r\n\r\n") -# if filePath.endswith(".css"): -# socket.write("Content-Type: text/css\r\n\r\n") -# else: -# socket.write("Content-Type: text/html\r\n\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.") From 0e2faf2e7f5338534470fcd94f21df9fdc121ea8 Mon Sep 17 00:00:00 2001 From: meebox Date: Thu, 3 Sep 2020 11:41:46 +0800 Subject: [PATCH 2/9] check for wrong path in doc directory. --- ESP8266WebServer.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ESP8266WebServer.py b/ESP8266WebServer.py index 1ffd5d9..96d6ec1 100644 --- a/ESP8266WebServer.py +++ b/ESP8266WebServer.py @@ -143,22 +143,22 @@ def handle(socket): # 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": # Only accept GET request + elif method != "GET": # Only accept GET request err(socket, "501", "Not Implemented") elif path in handlers: # Check for registered path handlers[path](socket, args) - #elif not path.startswith(docPath): # Check for path to any document - # err(socket, "400", "Bad Request") - else: + elif not path.startswith(docPath): # Check for wrong path + err(socket, "400", "Bad Request") + else: # find file in the document path filePath = path - # find the file - if not __fileExist(filePath): + # find the file + if not __fileExist(filePath): filePath = path + ("index.html" if path.endswith("/") else "/index.html") # find index.html in the path if not __fileExist(filePath): filePath = path + ("index.p.html" if path.endswith("/") else "/index.p.html") # find index.p.html in the path - if not __fileExist(filePath): + if not __fileExist(filePath): # no default html file found if notFoundHandler: notFoundHandler(socket) else: From de7e862b5363605d5896e3747acd0dad08757321 Mon Sep 17 00:00:00 2001 From: meebox Date: Thu, 3 Sep 2020 12:08:13 +0800 Subject: [PATCH 3/9] Correct the error treating path not ended with '/' as folder. So relative paths in the html would be process correctly. --- ESP8266WebServer.py | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/ESP8266WebServer.py b/ESP8266WebServer.py index 96d6ec1..92433ba 100644 --- a/ESP8266WebServer.py +++ b/ESP8266WebServer.py @@ -151,23 +151,25 @@ def handle(socket): err(socket, "400", "Bad Request") else: # find file in the document path filePath = path + fileFound = True # find the file - if not __fileExist(filePath): - filePath = path + ("index.html" if path.endswith("/") else "/index.html") - # find index.html in the path - if not __fileExist(filePath): - filePath = path + ("index.p.html" if path.endswith("/") else "/index.p.html") - # find index.p.html in the path - if not __fileExist(filePath): # no default html file found - if notFoundHandler: - notFoundHandler(socket) - else: - err(socket, "404", "Not Found") - return - socket.write("HTTP/1.1 302 Found\r\n") - socket.write("Location: " + filePath + "\r\n\r\n") + if not __fileExist(filePath): + if not path.endswith("/"): + fileFound = False + else: + filePath = path + "index.html" + # find index.html in the path + if not __fileExist(filePath): + filePath = path + "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" From e232d2988780e09873b34965f4700d08186660f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=83=E6=98=95=E6=9A=90?= Date: Thu, 3 Sep 2020 12:13:39 +0800 Subject: [PATCH 4/9] Updating the documentation in READ.ME Getting the default index(.p).html file now should specified the path ended with "/". --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5420389..d2aed68 100644 --- a/README.md +++ b/README.md @@ -54,8 +54,8 @@ Check for new request and call corresponding handler to process it. You can upload www directory and index.p.html to "/" on ESP8266 board and run TestWebServer.py to see how it works. -TestWebServer.py will show its own IP address through serial monitor.Just open your browser and connect it to http://serverIP:8899 or http://serverIP:8899/index.p.html, you'll get the main page that can turn on/off the buildin led on ESP8266 board. The main also demonstrate the template file usage. +TestWebServer.py will show its own IP address through serial monitor.Just open your browser and connect it to http://serverIP:8899 or http://serverIP:8899/index.p.html, you'll get the main page that can turn on/off the buildin led on ESP8266 board. The main page also demonstrate the template file usage. -You can also open http://serverip:8899/www/index.html or http://serverip:8899/www to view alternative version of controlling page that use AJAX to asynchronously turn on/off led. +You can also open http://serverip:8899/www/index.html or http://serverip:8899/www/ to view alternative version of controlling page that use AJAX to asynchronously turn on/off led. You can use http://serverip:8899/switch to switch led on/off led directly. Or you can use http://serverip:8899/cmd?led=on to turn the led on and http://serverip:8899/cmd?led=off to turn the led off. From cf4a388945673df6c6220142912afe09bed16362 Mon Sep 17 00:00:00 2001 From: meebox Date: Wed, 4 Nov 2020 19:06:48 +0800 Subject: [PATCH 5/9] Add timeout for socket.readline() --- ESP8266WebServer.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ESP8266WebServer.py b/ESP8266WebServer.py index 92433ba..f9ee1dc 100644 --- a/ESP8266WebServer.py +++ b/ESP8266WebServer.py @@ -54,6 +54,7 @@ def handleClient(): res = poller.poll(1) if res: # There's a new client connection (socket, sockaddr) = server.accept() + socket.settimeout(0.02) # set timeout to avoid blocking handle(socket) socket.close() @@ -118,7 +119,10 @@ def handle(socket): """Processing new GET request """ global docPath, handlers - currLine = str(socket.readline(), 'utf-8') + 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 From 288116d79dc2dba6565dd9191deab8c8a8c4f463 Mon Sep 17 00:00:00 2001 From: meebox Date: Thu, 3 Dec 2020 19:10:26 +0800 Subject: [PATCH 6/9] add default port 80 to begin() --- ESP8266WebServer.py | 4 ++-- TestWebServer.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/ESP8266WebServer.py b/ESP8266WebServer.py index f9ee1dc..d19a7a5 100644 --- a/ESP8266WebServer.py +++ b/ESP8266WebServer.py @@ -30,7 +30,7 @@ ".png":"image/png", } -def begin(port): +def begin(port=80): """Function to start http server """ global server, poller @@ -54,7 +54,7 @@ def handleClient(): res = poller.poll(1) if res: # There's a new client connection (socket, sockaddr) = server.accept() - socket.settimeout(0.02) # set timeout to avoid blocking + socket.settimeout(0.02) # set timeout for readline to avoid blocking handle(socket) socket.close() diff --git a/TestWebServer.py b/TestWebServer.py index f4e83a2..72a3779 100644 --- a/TestWebServer.py +++ b/TestWebServer.py @@ -66,7 +66,8 @@ def handleSwitch(socket, args): updateInfo(socket) # Start the server @ port 8899 -ESP8266WebServer.begin(8899) +# ESP8266WebServer.begin(8899) +ESP8266WebServer.begin() # use default 80 port # Register handler for each path # ESP8266WebServer.onPath("/", handleRoot) From 8a49af14449ebe6bb15e189d511859823d040efd Mon Sep 17 00:00:00 2001 From: Tobias Kamber Date: Fri, 1 Apr 2022 18:01:06 +0200 Subject: [PATCH 7/9] Adds POST/PUT-Handling --- .gitignore | 1 + ESP8266WebServer.py | 118 +++++++++++++++++++++++++++++--------------- README.md | 8 ++- index.p.html | 10 ++++ main.py | 87 ++++++++++++++++++++++++++++++++ 5 files changed, 182 insertions(+), 42 deletions(-) create mode 100644 .gitignore create mode 100644 main.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..85e7c1d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/.idea/ diff --git a/ESP8266WebServer.py b/ESP8266WebServer.py index d19a7a5..72d85cb 100644 --- a/ESP8266WebServer.py +++ b/ESP8266WebServer.py @@ -22,6 +22,8 @@ docPath = "/" # Data for template tplData = {} +# Max. POST/PUT-Data size +maxContentLength = 1024 # MIME types mimeTypes = { @@ -115,6 +117,45 @@ def __fileExist(path): print("Not Found.") return False +def __serveFile(socket, path): + """Serves a file from the filesystem + """ + filePath = path + fileFound = True + # find the file + if not __fileExist(filePath): + if not path.endswith("/"): + fileFound = False + else: + filePath = path + "index.html" + # find index.html in the path + if not __fileExist(filePath): + filePath = path + "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"): + 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 """ @@ -132,64 +173,53 @@ def handle(socket): 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 == b"": + header = socket.readline()# TODO read content & content type header + 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": + 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": # Only accept GET request + elif (method != "GET" and method != "PUT" and method != "POST"): # Only accept GET request err(socket, "501", "Not Implemented") elif path in handlers: # Check for registered path - handlers[path](socket, args) + handlers[path](socket, args, method, contentType, content) elif not path.startswith(docPath): # Check for wrong path err(socket, "400", "Bad Request") else: # find file in the document path filePath = path - fileFound = True - # find the file - if not __fileExist(filePath): - if not path.endswith("/"): - fileFound = False - else: - filePath = path + "index.html" - # find index.html in the path - if not __fileExist(filePath): - filePath = path + "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) + print("Serve File " + filePath) + __serveFile(socket, filePath) + def onPath(path, handler): """Register handler for processing request of specified path @@ -214,3 +244,9 @@ def setTplData(data): """ global tplData tplData = data + +def setMaxContentLength(max): + """Set the maximum content lenpth for incomming data bodies + """ + global maxContentLength + maxContentLength = max \ No newline at end of file diff --git a/README.md b/README.md index d2aed68..a64304c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # ESP8266WebServer -This is a very lightweight web server for MicroPython on ESP8266.It only accept GET requests.It adopts the programming style of ESP8266WebServer library in ESP8266 Arduino Core.This make it suitable for serving REST API.The original code was inspired from the project [Controlling a GPIO through an ESP8266-based web server](https://lab.whitequark.org/notes/2016-10-20/controlling-a-gpio-through-an-esp8266-based-web-server/). +This is a very lightweight web server for MicroPython on ESP8266. It only accept GET, POST and PUT requests. It adopts the programming style of ESP8266WebServer library in ESP8266 Arduino Core.This make it suitable for serving REST API.The original code was inspired from the project [Controlling a GPIO through an ESP8266-based web server](https://lab.whitequark.org/notes/2016-10-20/controlling-a-gpio-through-an-esp8266-based-web-server/). ## Installation @@ -46,6 +46,10 @@ Specified the directory in the filesystem containing all the HTML files. Specified the dictionary for template file. `dic` sould be a dictionary with all keys are string and contains all the names in replacing fields in all the template files. +### setMaxContentLength(size) + +Defines the maximum Content Length of incoming request bodies (POST, PUT) in bytes. Default: 1024 + ### handleClient() Check for new request and call corresponding handler to process it. @@ -54,6 +58,8 @@ Check for new request and call corresponding handler to process it. You can upload www directory and index.p.html to "/" on ESP8266 board and run TestWebServer.py to see how it works. +`main.py` contains an example for handling Request Body content. + TestWebServer.py will show its own IP address through serial monitor.Just open your browser and connect it to http://serverIP:8899 or http://serverIP:8899/index.p.html, you'll get the main page that can turn on/off the buildin led on ESP8266 board. The main page also demonstrate the template file usage. You can also open http://serverip:8899/www/index.html or http://serverip:8899/www/ to view alternative version of controlling page that use AJAX to asynchronously turn on/off led. diff --git a/index.p.html b/index.p.html index 65c5ea8..ae8ee42 100644 --- a/index.p.html +++ b/index.p.html @@ -7,5 +7,15 @@ {title} Status:{status}
Turn {switch}
HOME + +
+
+
+
+
+

+ +
+ diff --git a/main.py b/main.py new file mode 100644 index 0000000..46cfc1b --- /dev/null +++ b/main.py @@ -0,0 +1,87 @@ +import ESP8266WebServer +import network +import machine + +GPIO_NUM = 2 # Builtin led (D4) +# Get pin object for controlling builtin LED +pin = machine.Pin(GPIO_NUM, machine.Pin.OUT) +pin.on() # Turn LED off (it use sinking input) + +# Dictionary for template file +ledData = { + "title":"Remote LED", + "color":"red", + "status":"Off", + "switch":"on" +} + +# Update information +def updateInfo(socket): + global ledData, color, status, switch + ledData["color"] = "red" if pin.value() else "green" + ledData["status"] = "Off" if pin.value() else "On" + ledData["switch"] = "on" if pin.value() else "off" + ESP8266WebServer.ok( + socket, + "200", + ledData["status"]) + +def handleStop(socket): + ESP8266WebServer.ok( + socket, + "200", + "stopped") + running = False + ESP8266WebServer.close() + +def handlePost(socket, args, method, contenttype, content): + ESP8266WebServer.ok( + socket, + "200", + method+" "+contenttype+" "+content.decode('UTF-8')) + +# Handler for path "/cmd?led=[on|off]" +def handleCmd(socket, args): + if 'led' in args: + if args['led'] == 'on': + pin.off() + elif args['led'] == 'off': + pin.on() + updateInfo(socket) + else: + ESP8266WebServer.err(socket, "400", "Bad Request") + +# handler for path "/switch" +def handleSwitch(socket, args): + pin.value(not pin.value()) # Switch back and forth + updateInfo(socket) + +# Start the server @ port 8899 +# ESP8266WebServer.begin(8899) +ESP8266WebServer.begin() # use default 80 port + +# Register handler for each path +# ESP8266WebServer.onPath("/", handleRoot) +ESP8266WebServer.onPath("/cmd", handleCmd) +ESP8266WebServer.onPath("/switch", handleSwitch) +ESP8266WebServer.onPath("/post", handlePost) + +# Setting the path to documents +ESP8266WebServer.setDocPath("/") + +# Setting data for template +ESP8266WebServer.setTplData(ledData) + +# Setting maximum Body Content Size. Set to 0 to disable posting. Default: 1024 +ESP8266WebServer.setMaxContentLength(1024) + +def checkForClients(): + try: + while True: + # Let server process requests + ESP8266WebServer.handleClient() + except: + ESP8266WebServer.close() + +checkForClients() + From 0ee82f60c0c85d6fce370fa8250ba8b597386d94 Mon Sep 17 00:00:00 2001 From: Tobias Kamber Date: Fri, 1 Apr 2022 18:05:07 +0200 Subject: [PATCH 8/9] Cleans up comments --- ESP8266WebServer.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ESP8266WebServer.py b/ESP8266WebServer.py index 72d85cb..4c61097 100644 --- a/ESP8266WebServer.py +++ b/ESP8266WebServer.py @@ -184,7 +184,7 @@ def handle(socket): args[arg[0]] = arg[1] while True: # Read until blank line after header - header = socket.readline()# TODO read content & content type header + header = socket.readline() if header.startswith(b"Content-Length"): (key, contentLengthStr) = str(header).split(" ") contentLength = int(contentLengthStr[0:-5]) @@ -209,7 +209,7 @@ def handle(socket): # 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 request + elif (method != "GET" and method != "PUT" and method != "POST"): # Only accept GET,PUT and POST request err(socket, "501", "Not Implemented") elif path in handlers: # Check for registered path handlers[path](socket, args, method, contentType, content) @@ -249,4 +249,4 @@ def setMaxContentLength(max): """Set the maximum content lenpth for incomming data bodies """ global maxContentLength - maxContentLength = max \ No newline at end of file + maxContentLength = max From 2563ad7b085f3a04bfb447b3afdfba718bea3b95 Mon Sep 17 00:00:00 2001 From: Tobias Kamber Date: Sat, 2 Apr 2022 12:28:28 +0200 Subject: [PATCH 9/9] Adds a reference to the ESPWebServer Version --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index a64304c..47d2937 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ +This Library is no longer supported. Visit https://github.com/codemee/ESPWebServer for a version that supports ESP8266 and ESP32. + +---- # ESP8266WebServer This is a very lightweight web server for MicroPython on ESP8266. It only accept GET, POST and PUT requests. It adopts the programming style of ESP8266WebServer library in ESP8266 Arduino Core.This make it suitable for serving REST API.The original code was inspired from the project [Controlling a GPIO through an ESP8266-based web server](https://lab.whitequark.org/notes/2016-10-20/controlling-a-gpio-through-an-esp8266-based-web-server/).