mirror of
				https://github.com/esp8266/Arduino.git
				synced 2025-10-27 05:56:11 +03:00 
			
		
		
		
	Web server refactoring
This commit is contained in:
		| @@ -34,7 +34,6 @@ | |||||||
| #include <SPI.h> | #include <SPI.h> | ||||||
| #include <SD.h> | #include <SD.h> | ||||||
|  |  | ||||||
| #define WWW_BUF_SIZE 1460 |  | ||||||
| #define DBG_OUTPUT_PORT Serial | #define DBG_OUTPUT_PORT Serial | ||||||
|  |  | ||||||
| const char* ssid = "**********"; | const char* ssid = "**********"; | ||||||
| @@ -47,28 +46,17 @@ ESP8266WebServer server(80); | |||||||
| static bool hasSD = false; | static bool hasSD = false; | ||||||
| File uploadFile; | File uploadFile; | ||||||
|  |  | ||||||
| void returnOK(){ |  | ||||||
|   WiFiClient client = server.client(); | void returnOK() { | ||||||
|   String message = "HTTP/1.1 200 OK\r\n"; |   server.sendHeader("Connection", "close"); | ||||||
|   message += "Content-Type: text/plain\r\n"; |   server.sendHeader("Access-Control-Allow-Origin", "*"); | ||||||
|   message += "Connection: close\r\n"; |   server.send(200, "text/plain", ""); | ||||||
|   message += "Access-Control-Allow-Origin: *\r\n"; |  | ||||||
|   message += "\r\n"; |  | ||||||
|   client.print(message); |  | ||||||
|   client.stop(); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void returnFail(String msg){ | void returnFail(String msg) { | ||||||
|   WiFiClient client = server.client(); |   server.sendHeader("Connection", "close"); | ||||||
|   String message = "HTTP/1.1 500 Fail\r\n"; |   server.sendHeader("Access-Control-Allow-Origin", "*"); | ||||||
|   message += "Content-Type: text/plain\r\n"; |   server.send(500, "text/plain", msg + "\r\n"); | ||||||
|   message += "Connection: close\r\n"; |  | ||||||
|   message += "Access-Control-Allow-Origin: *\r\n"; |  | ||||||
|   message += "\r\n"; |  | ||||||
|   message += msg; |  | ||||||
|   message += "\r\n"; |  | ||||||
|   client.print(message); |  | ||||||
|   client.stop(); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| bool loadFromSdCard(String path){ | bool loadFromSdCard(String path){ | ||||||
| @@ -94,58 +82,39 @@ bool loadFromSdCard(String path){ | |||||||
|     dataFile = SD.open(path.c_str()); |     dataFile = SD.open(path.c_str()); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   if (!dataFile) | ||||||
|  |     return false; | ||||||
|  |    | ||||||
|   if(server.hasArg("download")) dataType = "application/octet-stream"; |   if(server.hasArg("download")) dataType = "application/octet-stream"; | ||||||
|    |    | ||||||
|   if (dataFile) { |   server.sendHeader("Content-Length", String(dataFile.size())); | ||||||
|     WiFiClient client = server.client(); |   server.sendHeader("Connection", "close"); | ||||||
|     String head = "HTTP/1.1 200 OK\r\nContent-Type: "; |   server.sendHeader("Access-Control-Allow-Origin", "*"); | ||||||
|     head += dataType; |   server.send(200, dataType.c_str(), ""); | ||||||
|     head += "\r\nContent-Length: "; |  | ||||||
|     head += dataFile.size(); |  | ||||||
|     head += "\r\nConnection: close"; |  | ||||||
|     head += "\r\nAccess-Control-Allow-Origin: *"; |  | ||||||
|     head += "\r\n\r\n"; |  | ||||||
|     client.print(head); |  | ||||||
|     dataType = String(); |  | ||||||
|     path = String(); |  | ||||||
|  |  | ||||||
|     uint8_t obuf[WWW_BUF_SIZE]; |   WiFiClient client = server.client(); | ||||||
|      |   size_t totalSize = dataFile.size(); | ||||||
|     while (dataFile.available() > WWW_BUF_SIZE){ |   if (client.write(dataFile, PAYLOAD_UNIT_SIZE) != totalSize) { | ||||||
|       dataFile.read(obuf, WWW_BUF_SIZE); |     DBG_OUTPUT_PORT.println("Sent less data than expected!"); | ||||||
|       if(client.write(obuf, WWW_BUF_SIZE) != WWW_BUF_SIZE){ |  | ||||||
|         DBG_OUTPUT_PORT.println("Sent less data than expected!"); |  | ||||||
|         dataFile.close(); |  | ||||||
|         return true; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|     uint16_t leftLen = dataFile.available(); |  | ||||||
|     dataFile.read(obuf, leftLen); |  | ||||||
|     if(client.write(obuf, leftLen) != leftLen){ |  | ||||||
|       DBG_OUTPUT_PORT.println("Sent less data than expected!"); |  | ||||||
|       dataFile.close(); |  | ||||||
|       return true; |  | ||||||
|     } |  | ||||||
|     dataFile.close(); |  | ||||||
|     client.stop(); |  | ||||||
|     return true; |  | ||||||
|   } |   } | ||||||
|   return false; |  | ||||||
|  |   dataFile.close(); | ||||||
|  |   return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| void handleFileUpload(){ | void handleFileUpload(){ | ||||||
|   if(server.uri() != "/edit") return; |   if(server.uri() != "/edit") return; | ||||||
|   HTTPUpload upload = server.upload(); |   HTTPUpload& upload = server.upload(); | ||||||
|   if(upload.status == UPLOAD_FILE_START){ |   if(upload.status == UPLOAD_FILE_START){ | ||||||
|     if(SD.exists((char *)upload.filename.c_str())) SD.remove((char *)upload.filename.c_str()); |     if(SD.exists((char *)upload.filename.c_str())) SD.remove((char *)upload.filename.c_str()); | ||||||
|     uploadFile = SD.open(upload.filename.c_str(), FILE_WRITE); |     uploadFile = SD.open(upload.filename.c_str(), FILE_WRITE); | ||||||
|     DBG_OUTPUT_PORT.print("Upload: START, filename: "); DBG_OUTPUT_PORT.println(upload.filename); |     DBG_OUTPUT_PORT.print("Upload: START, filename: "); DBG_OUTPUT_PORT.println(upload.filename); | ||||||
|   } else if(upload.status == UPLOAD_FILE_WRITE){ |   } else if(upload.status == UPLOAD_FILE_WRITE){ | ||||||
|     if(uploadFile) uploadFile.write(upload.buf, upload.buflen); |     if(uploadFile) uploadFile.write(upload.buf, upload.currentSize); | ||||||
|     DBG_OUTPUT_PORT.print("Upload: WRITE, Bytes: "); DBG_OUTPUT_PORT.println(upload.buflen); |     DBG_OUTPUT_PORT.print("Upload: WRITE, Bytes: "); DBG_OUTPUT_PORT.println(upload.currentSize); | ||||||
|   } else if(upload.status == UPLOAD_FILE_END){ |   } else if(upload.status == UPLOAD_FILE_END){ | ||||||
|     if(uploadFile) uploadFile.close(); |     if(uploadFile) uploadFile.close(); | ||||||
|     DBG_OUTPUT_PORT.print("Upload: END, Size: "); DBG_OUTPUT_PORT.println(upload.size); |     DBG_OUTPUT_PORT.print("Upload: END, Size: "); DBG_OUTPUT_PORT.println(upload.totalSize); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -156,13 +125,12 @@ void deleteRecursive(String path){ | |||||||
|     SD.remove((char *)path.c_str()); |     SD.remove((char *)path.c_str()); | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   file.rewindDirectory(); |   file.rewindDirectory(); | ||||||
|   File entry; |  | ||||||
|   String entryPath; |  | ||||||
|   while(true) { |   while(true) { | ||||||
|     entry = file.openNextFile(); |     File entry = file.openNextFile(); | ||||||
|     if (!entry) break; |     if (!entry) break; | ||||||
|     entryPath = path + "/" +entry.name(); |     String entryPath = path + "/" +entry.name(); | ||||||
|     if(entry.isDirectory()){ |     if(entry.isDirectory()){ | ||||||
|       entry.close(); |       entry.close(); | ||||||
|       deleteRecursive(entryPath); |       deleteRecursive(entryPath); | ||||||
| @@ -170,27 +138,32 @@ void deleteRecursive(String path){ | |||||||
|       entry.close(); |       entry.close(); | ||||||
|       SD.remove((char *)entryPath.c_str()); |       SD.remove((char *)entryPath.c_str()); | ||||||
|     } |     } | ||||||
|     entryPath = String(); |  | ||||||
|     yield(); |     yield(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   SD.rmdir((char *)path.c_str()); |   SD.rmdir((char *)path.c_str()); | ||||||
|   path = String(); |  | ||||||
|   file.close(); |   file.close(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void handleDelete(){ | void handleDelete(){ | ||||||
|   if(server.args() == 0) return returnFail("BAD ARGS"); |   if(server.args() == 0) return returnFail("BAD ARGS"); | ||||||
|   String path = server.arg(0); |   String path = server.arg(0); | ||||||
|   if(path == "/" || !SD.exists((char *)path.c_str())) return returnFail("BAD PATH"); |   if(path == "/" || !SD.exists((char *)path.c_str())) { | ||||||
|  |     returnFail("BAD PATH"); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|   deleteRecursive(path); |   deleteRecursive(path); | ||||||
|   returnOK(); |   returnOK(); | ||||||
|   path = String(); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void handleCreate(){ | void handleCreate(){ | ||||||
|   if(server.args() == 0) return returnFail("BAD ARGS"); |   if(server.args() == 0) return returnFail("BAD ARGS"); | ||||||
|   String path = server.arg(0); |   String path = server.arg(0); | ||||||
|   if(path == "/" || SD.exists((char *)path.c_str())) return returnFail("BAD PATH"); |   if(path == "/" || SD.exists((char *)path.c_str())) {  | ||||||
|  |     returnFail("BAD PATH"); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   if(path.indexOf('.') > 0){ |   if(path.indexOf('.') > 0){ | ||||||
|     File file = SD.open((char *)path.c_str(), FILE_WRITE); |     File file = SD.open((char *)path.c_str(), FILE_WRITE); | ||||||
|     if(file){ |     if(file){ | ||||||
| @@ -201,7 +174,6 @@ void handleCreate(){ | |||||||
|     SD.mkdir((char *)path.c_str()); |     SD.mkdir((char *)path.c_str()); | ||||||
|   } |   } | ||||||
|   returnOK(); |   returnOK(); | ||||||
|   path = String(); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void printDirectory() { | void printDirectory() { | ||||||
| @@ -216,31 +188,31 @@ void printDirectory() { | |||||||
|   } |   } | ||||||
|   dir.rewindDirectory(); |   dir.rewindDirectory(); | ||||||
|    |    | ||||||
|   File entry; |   server.send(200, "text/json", ""); | ||||||
|   WiFiClient client = server.client(); |   WiFiClient client = server.client(); | ||||||
|   client.print("HTTP/1.1 200 OK\r\nContent-Type: text/json\r\n\r\n"); |  | ||||||
|   String output = "["; |   for (int cnt = 0; true; ++cnt) { | ||||||
|   while(true) { |     File entry = dir.openNextFile(); | ||||||
|    entry = dir.openNextFile(); |     if (!entry) | ||||||
|    if (!entry) break; |     break; | ||||||
|    if(output != "[") output += ','; |  | ||||||
|    output += "{\"type\":\""; |     String output; | ||||||
|    output += (entry.isDirectory())?"dir":"file"; |     if (cnt == 0)  | ||||||
|    output += "\",\"name\":\""; |       output = '['; | ||||||
|    output += entry.name(); |     else  | ||||||
|    output += "\""; |       output = ','; | ||||||
|    output += "}"; |  | ||||||
|    entry.close(); |     output += "{\"type\":\""; | ||||||
|    if(output.length() > 1460){ |     output += (entry.isDirectory()) ? "dir" : "file"; | ||||||
|      client.write(output.substring(0, 1460).c_str(), 1460); |     output += "\",\"name\":\""; | ||||||
|      output = output.substring(1460); |     output += entry.name(); | ||||||
|    } |     output += "\""; | ||||||
|  |     output += "}"; | ||||||
|  |     server.sendContent(output); | ||||||
|  |     entry.close(); | ||||||
|  } |  } | ||||||
|  |  server.sendContent("]"); | ||||||
|  dir.close(); |  dir.close(); | ||||||
|  output += "]"; |  | ||||||
|  client.write(output.c_str(), output.length()); |  | ||||||
|  client.stop(); |  | ||||||
|  output = String(); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void handleNotFound(){ | void handleNotFound(){ | ||||||
| @@ -280,14 +252,14 @@ void setup(void){ | |||||||
|   } |   } | ||||||
|   DBG_OUTPUT_PORT.print("Connected! IP address: "); |   DBG_OUTPUT_PORT.print("Connected! IP address: "); | ||||||
|   DBG_OUTPUT_PORT.println(WiFi.localIP()); |   DBG_OUTPUT_PORT.println(WiFi.localIP()); | ||||||
|   /* |    | ||||||
|   if (mdns.begin(hostname, WiFi.localIP())) { |   if (mdns.begin(hostname, WiFi.localIP())) { | ||||||
|     DBG_OUTPUT_PORT.println("MDNS responder started"); |     DBG_OUTPUT_PORT.println("MDNS responder started"); | ||||||
|     DBG_OUTPUT_PORT.print("You can now connect to http://"); |     DBG_OUTPUT_PORT.print("You can now connect to http://"); | ||||||
|     DBG_OUTPUT_PORT.print(hostname); |     DBG_OUTPUT_PORT.print(hostname); | ||||||
|     DBG_OUTPUT_PORT.println(".local"); |     DBG_OUTPUT_PORT.println(".local"); | ||||||
|   } |   } | ||||||
|   */ |    | ||||||
|    |    | ||||||
|   server.on("/list", HTTP_GET, printDirectory); |   server.on("/list", HTTP_GET, printDirectory); | ||||||
|   server.on("/edit", HTTP_DELETE, handleDelete); |   server.on("/edit", HTTP_DELETE, handleDelete); | ||||||
|   | |||||||
| @@ -26,8 +26,8 @@ | |||||||
| #include "WiFiClient.h" | #include "WiFiClient.h" | ||||||
| #include "ESP8266WebServer.h" | #include "ESP8266WebServer.h" | ||||||
|  |  | ||||||
| //#define DEBUG | // #define DEBUG | ||||||
| #define DEBUG_OUTPUT Serial1 | #define DEBUG_OUTPUT Serial | ||||||
|  |  | ||||||
| struct ESP8266WebServer::RequestHandler { | struct ESP8266WebServer::RequestHandler { | ||||||
|   RequestHandler(ESP8266WebServer::THandlerFunction fn, const char* uri, HTTPMethod method) |   RequestHandler(ESP8266WebServer::THandlerFunction fn, const char* uri, HTTPMethod method) | ||||||
| @@ -99,110 +99,32 @@ void ESP8266WebServer::handleClient() | |||||||
| #ifdef DEBUG | #ifdef DEBUG | ||||||
|   DEBUG_OUTPUT.println("New client"); |   DEBUG_OUTPUT.println("New client"); | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|   // Wait for data from client to become available |   // Wait for data from client to become available | ||||||
|   while(client.connected() && !client.available()){ |   while(client.connected() && !client.available()){ | ||||||
|     delay(1); |     delay(1); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   // Read the first line of HTTP request |   if (!_parseRequest(client)) { | ||||||
|   String req = client.readStringUntil('\r'); |  | ||||||
|   client.readStringUntil('\n'); |  | ||||||
|  |  | ||||||
|   // First line of HTTP request looks like "GET /path HTTP/1.1" |  | ||||||
|   // Retrieve the "/path" part by finding the spaces |  | ||||||
|   int addr_start = req.indexOf(' '); |  | ||||||
|   int addr_end = req.indexOf(' ', addr_start + 1); |  | ||||||
|   if (addr_start == -1 || addr_end == -1) { |  | ||||||
| #ifdef DEBUG |  | ||||||
|     DEBUG_OUTPUT.print("Invalid request: "); |  | ||||||
|     DEBUG_OUTPUT.println(req); |  | ||||||
| #endif |  | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   String methodStr = req.substring(0, addr_start); |   _currentClient = client; | ||||||
|   String url = req.substring(addr_start + 1, addr_end); |   _handleRequest(); | ||||||
|   String searchStr = ""; | } | ||||||
|   int hasSearch = url.indexOf('?'); |  | ||||||
|   if(hasSearch != -1){ | void ESP8266WebServer::sendHeader(String name, String value, bool first) { | ||||||
|     searchStr = url.substring(hasSearch + 1); |   String headerLine = name; | ||||||
|     url = url.substring(0, hasSearch); |   headerLine += ": "; | ||||||
|  |   headerLine += value; | ||||||
|  |   headerLine += "\r\n"; | ||||||
|  |  | ||||||
|  |   if (first) { | ||||||
|  |     _responseHeaders = headerLine + _responseHeaders; | ||||||
|   } |   } | ||||||
|   _currentUri      = url; |   else { | ||||||
|    |     _responseHeaders += headerLine; | ||||||
|   HTTPMethod method = HTTP_GET; |  | ||||||
|   if (methodStr == "POST") { |  | ||||||
|     method = HTTP_POST; |  | ||||||
|   } else if (methodStr == "DELETE") { |  | ||||||
|     method = HTTP_DELETE; |  | ||||||
|   } else if (methodStr == "PUT") { |  | ||||||
|     method = HTTP_PUT; |  | ||||||
|   } else if (methodStr == "PATCH") { |  | ||||||
|     method = HTTP_PATCH; |  | ||||||
|   } |   } | ||||||
|    |  | ||||||
| #ifdef DEBUG |  | ||||||
|   DEBUG_OUTPUT.print("method: "); |  | ||||||
|   DEBUG_OUTPUT.print(methodStr); |  | ||||||
|   DEBUG_OUTPUT.print(" url: "); |  | ||||||
|   DEBUG_OUTPUT.print(url); |  | ||||||
|   DEBUG_OUTPUT.print(" search: "); |  | ||||||
|   DEBUG_OUTPUT.println(searchStr); |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|   String formData; |  | ||||||
|   //bellow is needed only when POST type request |  | ||||||
|   if(method == HTTP_POST || method == HTTP_PUT || method == HTTP_PATCH || method == HTTP_DELETE){ |  | ||||||
|     String boundaryStr; |  | ||||||
|     String headerName; |  | ||||||
|     String headerValue; |  | ||||||
|     bool isForm = false; |  | ||||||
|     uint32_t contentLength = 0; |  | ||||||
|     //parse headers |  | ||||||
|     while(1){ |  | ||||||
|       req = client.readStringUntil('\r'); |  | ||||||
|       client.readStringUntil('\n'); |  | ||||||
|       if(req == "") break;//no moar headers |  | ||||||
|       int headerDiv = req.indexOf(':'); |  | ||||||
|       if(headerDiv == -1){ |  | ||||||
|         break; |  | ||||||
|       } |  | ||||||
|       headerName = req.substring(0, headerDiv); |  | ||||||
|       headerValue = req.substring(headerDiv + 2); |  | ||||||
|       if(headerName == "Content-Type"){ |  | ||||||
|         if(headerValue.startsWith("text/plain")){ |  | ||||||
|           isForm = false; |  | ||||||
|         } else if(headerValue.startsWith("multipart/form-data")){ |  | ||||||
|           boundaryStr = headerValue.substring(headerValue.indexOf('=')+1); |  | ||||||
|           isForm = true; |  | ||||||
|         } |  | ||||||
|       } else if(headerName == "Content-Length"){ |  | ||||||
|         contentLength = headerValue.toInt(); |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|    |  | ||||||
|     if(!isForm){ |  | ||||||
|       if(searchStr != "") searchStr += '&'; |  | ||||||
|       searchStr += client.readStringUntil('\r'); |  | ||||||
|       client.readStringUntil('\n'); |  | ||||||
|     } |  | ||||||
|     _parseArguments(searchStr); |  | ||||||
|     if(isForm){ |  | ||||||
|       _parseForm(client, boundaryStr, contentLength); |  | ||||||
|     } |  | ||||||
|   } else { |  | ||||||
|     _parseArguments(searchStr); |  | ||||||
|   } |  | ||||||
|   client.flush(); |  | ||||||
|  |  | ||||||
| #ifdef DEBUG |  | ||||||
|   DEBUG_OUTPUT.print("Request: "); |  | ||||||
|   DEBUG_OUTPUT.println(url); |  | ||||||
|   DEBUG_OUTPUT.print(" Arguments: "); |  | ||||||
|   DEBUG_OUTPUT.println(searchStr); |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|   _handleRequest(client, url, method); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void ESP8266WebServer::send(int code, const char* content_type, String content) { | void ESP8266WebServer::send(int code, const char* content_type, String content) { | ||||||
| @@ -214,11 +136,28 @@ void ESP8266WebServer::send(int code, const char* content_type, String content) | |||||||
|  |  | ||||||
|   if (!content_type) |   if (!content_type) | ||||||
|     content_type = "text/html"; |     content_type = "text/html"; | ||||||
|   _appendHeader(response, "Content-Type", content_type); |   sendHeader("Content-Type", content_type, true); | ||||||
|    |    | ||||||
|  |   response += _responseHeaders; | ||||||
|   response += "\r\n"; |   response += "\r\n"; | ||||||
|   response += content; |   response += content; | ||||||
|   _currentClient.print(response); |   _responseHeaders = String(); | ||||||
|  |   sendContent(response); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ESP8266WebServer::sendContent(String content) { | ||||||
|  |   size_t size_to_send = content.length(); | ||||||
|  |   size_t size_sent = 0; | ||||||
|  |   while(size_to_send) { | ||||||
|  |     const size_t unit_size = PAYLOAD_UNIT_SIZE; | ||||||
|  |     size_t will_send = (size_to_send < unit_size) ? size_to_send : unit_size; | ||||||
|  |     size_t sent = _currentClient.write(content.c_str() + size_sent, will_send); | ||||||
|  |     size_to_send -= sent; | ||||||
|  |     size_sent += sent; | ||||||
|  |     if (sent == 0) { | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| String ESP8266WebServer::arg(const char* name) { | String ESP8266WebServer::arg(const char* name) { | ||||||
| @@ -253,298 +192,6 @@ bool ESP8266WebServer::hasArg(const char* name) { | |||||||
|   return false; |   return false; | ||||||
| } | } | ||||||
|  |  | ||||||
| void ESP8266WebServer::_parseArguments(String data) { |  | ||||||
| #ifdef DEBUG |  | ||||||
|   DEBUG_OUTPUT.print("args: "); |  | ||||||
|   DEBUG_OUTPUT.println(data); |  | ||||||
| #endif |  | ||||||
|   if (_currentArgs) |  | ||||||
|     delete[] _currentArgs; |  | ||||||
|   _currentArgs = 0; |  | ||||||
|   if (data.length() == 0) { |  | ||||||
|     _currentArgCount = 0; |  | ||||||
|     return; |  | ||||||
|   } |  | ||||||
|   _currentArgCount = 1; |  | ||||||
|  |  | ||||||
|   for (int i = 0; i < data.length(); ) { |  | ||||||
|     i = data.indexOf('&', i); |  | ||||||
|     if (i == -1) |  | ||||||
|       break; |  | ||||||
|     ++i; |  | ||||||
|     ++_currentArgCount; |  | ||||||
|   } |  | ||||||
| #ifdef DEBUG |  | ||||||
|   DEBUG_OUTPUT.print("args count: "); |  | ||||||
|   DEBUG_OUTPUT.println(_currentArgCount); |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|   _currentArgs = new RequestArgument[_currentArgCount]; |  | ||||||
|   int pos = 0; |  | ||||||
|   int iarg; |  | ||||||
|   for (iarg = 0; iarg < _currentArgCount;) { |  | ||||||
|     int equal_sign_index = data.indexOf('=', pos); |  | ||||||
|     int next_arg_index = data.indexOf('&', pos); |  | ||||||
| #ifdef DEBUG |  | ||||||
|     DEBUG_OUTPUT.print("pos "); |  | ||||||
|     DEBUG_OUTPUT.print(pos); |  | ||||||
|     DEBUG_OUTPUT.print("=@ "); |  | ||||||
|     DEBUG_OUTPUT.print(equal_sign_index); |  | ||||||
|     DEBUG_OUTPUT.print(" &@ "); |  | ||||||
|     DEBUG_OUTPUT.println(next_arg_index); |  | ||||||
| #endif |  | ||||||
|     if ((equal_sign_index == -1) || ((equal_sign_index > next_arg_index) && (next_arg_index != -1))) { |  | ||||||
| #ifdef DEBUG |  | ||||||
|       DEBUG_OUTPUT.print("arg missing value: "); |  | ||||||
|       DEBUG_OUTPUT.println(iarg); |  | ||||||
| #endif |  | ||||||
|       if (next_arg_index == -1) |  | ||||||
|         break; |  | ||||||
|       pos = next_arg_index + 1; |  | ||||||
|       continue; |  | ||||||
|     } |  | ||||||
|     RequestArgument& arg = _currentArgs[iarg]; |  | ||||||
|     arg.key = data.substring(pos, equal_sign_index); |  | ||||||
|     arg.value = data.substring(equal_sign_index + 1, next_arg_index); |  | ||||||
| #ifdef DEBUG |  | ||||||
|     DEBUG_OUTPUT.print("arg "); |  | ||||||
|     DEBUG_OUTPUT.print(iarg); |  | ||||||
|     DEBUG_OUTPUT.print(" key: "); |  | ||||||
|     DEBUG_OUTPUT.print(arg.key); |  | ||||||
|     DEBUG_OUTPUT.print(" value: "); |  | ||||||
|     DEBUG_OUTPUT.println(arg.value); |  | ||||||
| #endif |  | ||||||
|     ++iarg; |  | ||||||
|     if (next_arg_index == -1) |  | ||||||
|       break; |  | ||||||
|     pos = next_arg_index + 1; |  | ||||||
|   } |  | ||||||
|   _currentArgCount = iarg; |  | ||||||
| #ifdef DEBUG |  | ||||||
|   DEBUG_OUTPUT.print("args count: "); |  | ||||||
|   DEBUG_OUTPUT.println(_currentArgCount); |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void ESP8266WebServer::_parseForm(WiFiClient& client, String boundary, uint32_t len){ |  | ||||||
|    |  | ||||||
| #ifdef DEBUG |  | ||||||
|   DEBUG_OUTPUT.print("Parse Form: Boundary: "); |  | ||||||
|   DEBUG_OUTPUT.print(boundary); |  | ||||||
|   DEBUG_OUTPUT.print("Length: "); |  | ||||||
|   DEBUG_OUTPUT.println(len); |  | ||||||
| #endif |  | ||||||
|   String line; |  | ||||||
|   line = client.readStringUntil('\r'); |  | ||||||
|   client.readStringUntil('\n'); |  | ||||||
|   //start reading the form |  | ||||||
|   if(line == ("--"+boundary)){ |  | ||||||
|     RequestArgument* postArgs = new RequestArgument[32]; |  | ||||||
|     int postArgsLen = 0; |  | ||||||
|     while(1){ |  | ||||||
|       String argName; |  | ||||||
|       String argValue; |  | ||||||
|       String argType; |  | ||||||
|       String argFilename; |  | ||||||
|       bool argIsFile = false; |  | ||||||
|        |  | ||||||
|       line = client.readStringUntil('\r'); |  | ||||||
|       client.readStringUntil('\n'); |  | ||||||
|       if(line.startsWith("Content-Disposition")){ |  | ||||||
|         int nameStart = line.indexOf('='); |  | ||||||
|         if(nameStart != -1){ |  | ||||||
|           argName = line.substring(nameStart+2); |  | ||||||
|           nameStart = argName.indexOf('='); |  | ||||||
|           if(nameStart == -1){ |  | ||||||
|             argName = argName.substring(0, argName.length() - 1); |  | ||||||
|           } else { |  | ||||||
|             argFilename = argName.substring(nameStart+2, argName.length() - 1); |  | ||||||
|             argName = argName.substring(0, argName.indexOf('"')); |  | ||||||
|             argIsFile = true; |  | ||||||
|   #ifdef DEBUG |  | ||||||
|             DEBUG_OUTPUT.print("PostArg FileName: "); |  | ||||||
|             DEBUG_OUTPUT.println(argFilename); |  | ||||||
|   #endif |  | ||||||
|             //use GET to set the filename if uploading using blob |  | ||||||
|             if(argFilename == "blob" && hasArg("filename")) argFilename = arg("filename"); |  | ||||||
|           } |  | ||||||
|   #ifdef DEBUG |  | ||||||
|           DEBUG_OUTPUT.print("PostArg Name: "); |  | ||||||
|           DEBUG_OUTPUT.println(argName); |  | ||||||
|   #endif |  | ||||||
|           argType = "text/plain"; |  | ||||||
|           line = client.readStringUntil('\r'); |  | ||||||
|           client.readStringUntil('\n'); |  | ||||||
|           if(line.startsWith("Content-Type")){ |  | ||||||
|             argType = line.substring(line.indexOf(':')+2); |  | ||||||
|             //skip next line |  | ||||||
|             client.readStringUntil('\r'); |  | ||||||
|             client.readStringUntil('\n'); |  | ||||||
|           } |  | ||||||
|   #ifdef DEBUG |  | ||||||
|           DEBUG_OUTPUT.print("PostArg Type: "); |  | ||||||
|           DEBUG_OUTPUT.println(argType); |  | ||||||
|   #endif |  | ||||||
|           if(!argIsFile){ |  | ||||||
|             while(1){ |  | ||||||
|               line = client.readStringUntil('\r'); |  | ||||||
|               client.readStringUntil('\n'); |  | ||||||
|               if(line.startsWith("--"+boundary)) break; |  | ||||||
|               if(argValue.length() > 0) argValue += "\n"; |  | ||||||
|               argValue += line; |  | ||||||
|             } |  | ||||||
|   #ifdef DEBUG |  | ||||||
|             DEBUG_OUTPUT.print("PostArg Value: "); |  | ||||||
|             DEBUG_OUTPUT.println(argValue); |  | ||||||
|             DEBUG_OUTPUT.println(); |  | ||||||
|   #endif |  | ||||||
|              |  | ||||||
|             RequestArgument& arg = postArgs[postArgsLen++]; |  | ||||||
|             arg.key = argName; |  | ||||||
|             arg.value = argValue; |  | ||||||
|              |  | ||||||
|             if(line == ("--"+boundary+"--")){ |  | ||||||
|   #ifdef DEBUG |  | ||||||
|               DEBUG_OUTPUT.println("Done Parsing POST"); |  | ||||||
|   #endif |  | ||||||
|               break; |  | ||||||
|             } |  | ||||||
|           } else { |  | ||||||
|             _currentUpload.status = UPLOAD_FILE_START; |  | ||||||
|             _currentUpload.name = argName; |  | ||||||
|             _currentUpload.filename = argFilename; |  | ||||||
|             _currentUpload.type = argType; |  | ||||||
|             _currentUpload.size = 0; |  | ||||||
|             _currentUpload.buflen = 0; |  | ||||||
| #ifdef DEBUG |  | ||||||
|             DEBUG_OUTPUT.print("Start File: "); |  | ||||||
|             DEBUG_OUTPUT.print(_currentUpload.filename); |  | ||||||
|             DEBUG_OUTPUT.print(" Type: "); |  | ||||||
|             DEBUG_OUTPUT.println(_currentUpload.type); |  | ||||||
| #endif |  | ||||||
|             if(_fileUploadHandler) _fileUploadHandler(); |  | ||||||
|             _currentUpload.status = UPLOAD_FILE_WRITE; |  | ||||||
|             uint8_t argByte = client.read(); |  | ||||||
| readfile: |  | ||||||
|             while(argByte != 0x0D){ |  | ||||||
|               _currentUpload.buf[_currentUpload.buflen++] = argByte; |  | ||||||
|               if(_currentUpload.buflen == 1460){ |  | ||||||
|   #ifdef DEBUG |  | ||||||
|                 DEBUG_OUTPUT.println("Write File: 1460"); |  | ||||||
|   #endif |  | ||||||
|                 if(_fileUploadHandler) _fileUploadHandler(); |  | ||||||
|                 _currentUpload.size += _currentUpload.buflen; |  | ||||||
|                 _currentUpload.buflen = 0; |  | ||||||
|               } |  | ||||||
|               argByte = client.read(); |  | ||||||
|             } |  | ||||||
|              |  | ||||||
|             argByte = client.read(); |  | ||||||
|             if(argByte == 0x0A){ |  | ||||||
| #ifdef DEBUG |  | ||||||
|               DEBUG_OUTPUT.print("Write File: "); |  | ||||||
|               DEBUG_OUTPUT.println(_currentUpload.buflen); |  | ||||||
| #endif |  | ||||||
|               if(_fileUploadHandler) _fileUploadHandler(); |  | ||||||
|               _currentUpload.size += _currentUpload.buflen; |  | ||||||
|               _currentUpload.buflen = 0; |  | ||||||
|                |  | ||||||
|               argByte = client.read(); |  | ||||||
|               if((char)argByte != '-'){ |  | ||||||
|                 //continue reading the file |  | ||||||
|                 _currentUpload.buf[_currentUpload.buflen++] = 0x0D; |  | ||||||
|                 _currentUpload.buf[_currentUpload.buflen++] = 0x0A; |  | ||||||
|                 goto readfile; |  | ||||||
|               } else { |  | ||||||
|                 argByte = client.read(); |  | ||||||
|                 if((char)argByte != '-'){ |  | ||||||
|                   //continue reading the file |  | ||||||
|                   _currentUpload.buf[_currentUpload.buflen++] = 0x0D; |  | ||||||
|                   _currentUpload.buf[_currentUpload.buflen++] = 0x0A; |  | ||||||
|                   _currentUpload.buf[_currentUpload.buflen++] = (uint8_t)('-'); |  | ||||||
|                   goto readfile; |  | ||||||
|                 } |  | ||||||
|               } |  | ||||||
|                |  | ||||||
|               uint8_t endBuf[boundary.length()]; |  | ||||||
|               client.readBytes(endBuf, boundary.length()); |  | ||||||
|                |  | ||||||
|               if(strstr((const char*)endBuf, (const char*)(boundary.c_str())) != NULL){ |  | ||||||
|                 _currentUpload.status = UPLOAD_FILE_END; |  | ||||||
| #ifdef DEBUG |  | ||||||
|                 DEBUG_OUTPUT.print("End File: "); |  | ||||||
|                 DEBUG_OUTPUT.print(_currentUpload.filename); |  | ||||||
|                 DEBUG_OUTPUT.print(" Type: "); |  | ||||||
|                 DEBUG_OUTPUT.print(_currentUpload.type); |  | ||||||
|                 DEBUG_OUTPUT.print(" Size: "); |  | ||||||
|                 DEBUG_OUTPUT.println(_currentUpload.size); |  | ||||||
| #endif |  | ||||||
|                 if(_fileUploadHandler) _fileUploadHandler(); |  | ||||||
|                 line = client.readStringUntil(0x0D); |  | ||||||
|                 client.readStringUntil(0x0A); |  | ||||||
|                 if(line == "--"){ |  | ||||||
| #ifdef DEBUG |  | ||||||
|                   DEBUG_OUTPUT.println("Done Parsing POST"); |  | ||||||
| #endif |  | ||||||
|                   break; |  | ||||||
|                 } |  | ||||||
|                 continue; |  | ||||||
|               } else { |  | ||||||
|                 _currentUpload.buf[_currentUpload.buflen++] = 0x0D; |  | ||||||
|                 _currentUpload.buf[_currentUpload.buflen++] = 0x0A; |  | ||||||
|                 uint32_t i = 0; |  | ||||||
|                 while(i < boundary.length()){ |  | ||||||
|                   _currentUpload.buf[_currentUpload.buflen++] = endBuf[i++]; |  | ||||||
|                   if(_currentUpload.buflen == 1460){ |  | ||||||
| #ifdef DEBUG |  | ||||||
|                     DEBUG_OUTPUT.println("Write File: 1460"); |  | ||||||
| #endif |  | ||||||
|                     if(_fileUploadHandler) _fileUploadHandler(); |  | ||||||
|                     _currentUpload.size += _currentUpload.buflen; |  | ||||||
|                     _currentUpload.buflen = 0; |  | ||||||
|                   } |  | ||||||
|                 } |  | ||||||
|                 argByte = client.read(); |  | ||||||
|                 goto readfile; |  | ||||||
|               } |  | ||||||
|             } else { |  | ||||||
|               _currentUpload.buf[_currentUpload.buflen++] = 0x0D; |  | ||||||
|               if(_currentUpload.buflen == 1460){ |  | ||||||
|   #ifdef DEBUG |  | ||||||
|                 DEBUG_OUTPUT.println("Write File: 1460"); |  | ||||||
|   #endif |  | ||||||
|                 if(_fileUploadHandler) _fileUploadHandler(); |  | ||||||
|                 _currentUpload.size += _currentUpload.buflen; |  | ||||||
|                 _currentUpload.buflen = 0; |  | ||||||
|               } |  | ||||||
|               goto readfile; |  | ||||||
|             } |  | ||||||
|             break; |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     int iarg; |  | ||||||
|     int totalArgs = ((32 - postArgsLen) < _currentArgCount)?(32 - postArgsLen):_currentArgCount; |  | ||||||
|     for (iarg = 0; iarg < totalArgs; iarg++){ |  | ||||||
|       RequestArgument& arg = postArgs[postArgsLen++]; |  | ||||||
|       arg.key = _currentArgs[iarg].key; |  | ||||||
|       arg.value = _currentArgs[iarg].value; |  | ||||||
|     } |  | ||||||
|     if (_currentArgs) delete[] _currentArgs; |  | ||||||
|     _currentArgs = new RequestArgument[postArgsLen]; |  | ||||||
|     for (iarg = 0; iarg < postArgsLen; iarg++){ |  | ||||||
|       RequestArgument& arg = _currentArgs[iarg]; |  | ||||||
|       arg.key = postArgs[iarg].key; |  | ||||||
|       arg.value = postArgs[iarg].value; |  | ||||||
|     } |  | ||||||
|     _currentArgCount = iarg; |  | ||||||
|     if (postArgs) delete[] postArgs; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void ESP8266WebServer::onFileUpload(THandlerFunction fn) { | void ESP8266WebServer::onFileUpload(THandlerFunction fn) { | ||||||
|   _fileUploadHandler = fn; |   _fileUploadHandler = fn; | ||||||
| } | } | ||||||
| @@ -553,18 +200,14 @@ void ESP8266WebServer::onNotFound(THandlerFunction fn) { | |||||||
|   _notFoundHandler = fn; |   _notFoundHandler = fn; | ||||||
| } | } | ||||||
|  |  | ||||||
| void ESP8266WebServer::_handleRequest(WiFiClient& client, String uri, HTTPMethod method) { | void ESP8266WebServer::_handleRequest() { | ||||||
|   _currentClient   = client; |  | ||||||
|   _currentUri      = uri; |  | ||||||
|   _currentMethod   = method; |  | ||||||
|  |  | ||||||
|   RequestHandler* handler; |   RequestHandler* handler; | ||||||
|   for (handler = _firstHandler; handler; handler = handler->next) |   for (handler = _firstHandler; handler; handler = handler->next) | ||||||
|   { |   { | ||||||
|     if (handler->method != HTTP_ANY && handler->method != method) |     if (handler->method != HTTP_ANY && handler->method != _currentMethod) | ||||||
|       continue; |       continue; | ||||||
|  |  | ||||||
|     if (handler->uri != uri) |     if (handler->uri != _currentUri) | ||||||
|       continue; |       continue; | ||||||
|  |  | ||||||
|     handler->fn(); |     handler->fn(); | ||||||
| @@ -580,26 +223,19 @@ void ESP8266WebServer::_handleRequest(WiFiClient& client, String uri, HTTPMethod | |||||||
|       _notFoundHandler(); |       _notFoundHandler(); | ||||||
|     } |     } | ||||||
|     else { |     else { | ||||||
|       send(404, "text/plain", String("Not found: ") + uri); |       send(404, "text/plain", String("Not found: ") + _currentUri); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   _currentClient   = WiFiClient(); |   _currentClient   = WiFiClient(); | ||||||
|   _currentUri      = String(); |   _currentUri      = String(); | ||||||
|    |  | ||||||
| } | } | ||||||
|  |  | ||||||
| const char* ESP8266WebServer::_responseCodeToString(int code) { | const char* ESP8266WebServer::_responseCodeToString(int code) { | ||||||
|   switch (code) { |   switch (code) { | ||||||
|     case 200: return "OK"; |     case 200: return "OK"; | ||||||
|     case 404: return "Not found"; |     case 404: return "Not found"; | ||||||
|  |     case 500: return "Fail"; | ||||||
|     default:  return ""; |     default:  return ""; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| void ESP8266WebServer::_appendHeader(String& response, const char* name, const char* value) { |  | ||||||
|   response += name; |  | ||||||
|   response += ": "; |  | ||||||
|   response += value; |  | ||||||
|   response += "\r\n"; |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -29,14 +29,17 @@ | |||||||
| enum HTTPMethod { HTTP_ANY, HTTP_GET, HTTP_POST, HTTP_PUT, HTTP_PATCH, HTTP_DELETE }; | enum HTTPMethod { HTTP_ANY, HTTP_GET, HTTP_POST, HTTP_PUT, HTTP_PATCH, HTTP_DELETE }; | ||||||
| enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END }; | enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END }; | ||||||
|  |  | ||||||
|  | #define PAYLOAD_UNIT_SIZE 1460 | ||||||
|  |  | ||||||
| typedef struct { | typedef struct { | ||||||
|   HTTPUploadStatus status; |   HTTPUploadStatus status; | ||||||
|   String filename; |   String filename; | ||||||
|   String name; |   String name; | ||||||
|   String type; |   String type; | ||||||
|   size_t size; |   size_t totalSize; // file size | ||||||
|   size_t buflen; |   size_t  currentSize; // size of data currently in buf | ||||||
|   uint8_t buf[1460]; |   uint8_t buf[PAYLOAD_UNIT_SIZE]; | ||||||
|  |  | ||||||
| } HTTPUpload; | } HTTPUpload; | ||||||
|  |  | ||||||
| class ESP8266WebServer | class ESP8266WebServer | ||||||
| @@ -58,7 +61,7 @@ public: | |||||||
|   String uri() { return _currentUri; } |   String uri() { return _currentUri; } | ||||||
|   HTTPMethod method() { return _currentMethod; } |   HTTPMethod method() { return _currentMethod; } | ||||||
|   WiFiClient client() { return _currentClient; } |   WiFiClient client() { return _currentClient; } | ||||||
|   HTTPUpload upload() { return _currentUpload; } |   HTTPUpload& upload() { return _currentUpload; } | ||||||
|    |    | ||||||
|   String arg(const char* name);   // get request argument value by name |   String arg(const char* name);   // get request argument value by name | ||||||
|   String arg(int i);              // get request argument value by number |   String arg(int i);              // get request argument value by number | ||||||
| @@ -72,11 +75,13 @@ public: | |||||||
|   // content - actual content body |   // content - actual content body | ||||||
|   void send(int code, const char* content_type = NULL, String content = String("")); |   void send(int code, const char* content_type = NULL, String content = String("")); | ||||||
|  |  | ||||||
|  |   void sendHeader(String name, String value, bool first = false); | ||||||
|  |   void sendContent(String content); | ||||||
| protected: | protected: | ||||||
|   void _handleRequest(WiFiClient& client, String uri, HTTPMethod method); |   void _handleRequest(); | ||||||
|  |   bool _parseRequest(WiFiClient& client); | ||||||
|   void _parseArguments(String data); |   void _parseArguments(String data); | ||||||
|   static const char* _responseCodeToString(int code); |   static const char* _responseCodeToString(int code); | ||||||
|   static void _appendHeader(String& response, const char* name, const char* value); |  | ||||||
|   void _parseForm(WiFiClient& client, String boundary, uint32_t len); |   void _parseForm(WiFiClient& client, String boundary, uint32_t len); | ||||||
|    |    | ||||||
|   struct RequestHandler; |   struct RequestHandler; | ||||||
| @@ -95,6 +100,8 @@ protected: | |||||||
|   RequestArgument* _currentArgs; |   RequestArgument* _currentArgs; | ||||||
|   HTTPUpload       _currentUpload; |   HTTPUpload       _currentUpload; | ||||||
|  |  | ||||||
|  |   String           _responseHeaders; | ||||||
|  |  | ||||||
|   RequestHandler*  _firstHandler; |   RequestHandler*  _firstHandler; | ||||||
|   RequestHandler*  _lastHandler; |   RequestHandler*  _lastHandler; | ||||||
|   THandlerFunction _notFoundHandler; |   THandlerFunction _notFoundHandler; | ||||||
|   | |||||||
							
								
								
									
										428
									
								
								libraries/ESP8266WebServer/src/Parsing.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										428
									
								
								libraries/ESP8266WebServer/src/Parsing.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,428 @@ | |||||||
|  | /*  | ||||||
|  |   Parsing.cpp - HTTP request parsing. | ||||||
|  |  | ||||||
|  |   Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. | ||||||
|  |   | ||||||
|  |   This library is free software; you can redistribute it and/or | ||||||
|  |   modify it under the terms of the GNU Lesser General Public | ||||||
|  |   License as published by the Free Software Foundation; either | ||||||
|  |   version 2.1 of the License, or (at your option) any later version. | ||||||
|  |  | ||||||
|  |   This library is distributed in the hope that it will be useful, | ||||||
|  |   but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||||
|  |   Lesser General Public License for more details. | ||||||
|  |  | ||||||
|  |   You should have received a copy of the GNU Lesser General Public | ||||||
|  |   License along with this library; if not, write to the Free Software | ||||||
|  |   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA | ||||||
|  |   Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling) | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | #include <Arduino.h> | ||||||
|  | #include "WiFiServer.h" | ||||||
|  | #include "WiFiClient.h" | ||||||
|  | #include "ESP8266WebServer.h" | ||||||
|  |  | ||||||
|  | // #define DEBUG | ||||||
|  | #define DEBUG_OUTPUT Serial1 | ||||||
|  |  | ||||||
|  | bool ESP8266WebServer::_parseRequest(WiFiClient& client) { | ||||||
|  |   // Read the first line of HTTP request | ||||||
|  |   String req = client.readStringUntil('\r'); | ||||||
|  |   client.readStringUntil('\n'); | ||||||
|  |  | ||||||
|  |   // First line of HTTP request looks like "GET /path HTTP/1.1" | ||||||
|  |   // Retrieve the "/path" part by finding the spaces | ||||||
|  |   int addr_start = req.indexOf(' '); | ||||||
|  |   int addr_end = req.indexOf(' ', addr_start + 1); | ||||||
|  |   if (addr_start == -1 || addr_end == -1) { | ||||||
|  | #ifdef DEBUG | ||||||
|  |     DEBUG_OUTPUT.print("Invalid request: "); | ||||||
|  |     DEBUG_OUTPUT.println(req); | ||||||
|  | #endif | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   String methodStr = req.substring(0, addr_start); | ||||||
|  |   String url = req.substring(addr_start + 1, addr_end); | ||||||
|  |   String searchStr = ""; | ||||||
|  |   int hasSearch = url.indexOf('?'); | ||||||
|  |   if(hasSearch != -1){ | ||||||
|  |     searchStr = url.substring(hasSearch + 1); | ||||||
|  |     url = url.substring(0, hasSearch); | ||||||
|  |   } | ||||||
|  |   _currentUri = url; | ||||||
|  |    | ||||||
|  |   HTTPMethod method = HTTP_GET; | ||||||
|  |   if (methodStr == "POST") { | ||||||
|  |     method = HTTP_POST; | ||||||
|  |   } else if (methodStr == "DELETE") { | ||||||
|  |     method = HTTP_DELETE; | ||||||
|  |   } else if (methodStr == "PUT") { | ||||||
|  |     method = HTTP_PUT; | ||||||
|  |   } else if (methodStr == "PATCH") { | ||||||
|  |     method = HTTP_PATCH; | ||||||
|  |   } | ||||||
|  |   _currentMethod = method; | ||||||
|  |    | ||||||
|  | #ifdef DEBUG | ||||||
|  |   DEBUG_OUTPUT.print("method: "); | ||||||
|  |   DEBUG_OUTPUT.print(methodStr); | ||||||
|  |   DEBUG_OUTPUT.print(" url: "); | ||||||
|  |   DEBUG_OUTPUT.print(url); | ||||||
|  |   DEBUG_OUTPUT.print(" search: "); | ||||||
|  |   DEBUG_OUTPUT.println(searchStr); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |   String formData; | ||||||
|  |   // below is needed only when POST type request | ||||||
|  |   if(method == HTTP_POST || method == HTTP_PUT || method == HTTP_PATCH || method == HTTP_DELETE){ | ||||||
|  |     String boundaryStr; | ||||||
|  |     String headerName; | ||||||
|  |     String headerValue; | ||||||
|  |     bool isForm = false; | ||||||
|  |     uint32_t contentLength = 0; | ||||||
|  |     //parse headers | ||||||
|  |     while(1){ | ||||||
|  |       req = client.readStringUntil('\r'); | ||||||
|  |       client.readStringUntil('\n'); | ||||||
|  |       if(req == "") break;//no moar headers | ||||||
|  |       int headerDiv = req.indexOf(':'); | ||||||
|  |       if(headerDiv == -1){ | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  |       headerName = req.substring(0, headerDiv); | ||||||
|  |       headerValue = req.substring(headerDiv + 2); | ||||||
|  |       if(headerName == "Content-Type"){ | ||||||
|  |         if(headerValue.startsWith("text/plain")){ | ||||||
|  |           isForm = false; | ||||||
|  |         } else if(headerValue.startsWith("multipart/form-data")){ | ||||||
|  |           boundaryStr = headerValue.substring(headerValue.indexOf('=')+1); | ||||||
|  |           isForm = true; | ||||||
|  |         } | ||||||
|  |       } else if(headerName == "Content-Length"){ | ||||||
|  |         contentLength = headerValue.toInt(); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |    | ||||||
|  |     if(!isForm){ | ||||||
|  |       if(searchStr != "") searchStr += '&'; | ||||||
|  |       searchStr += client.readStringUntil('\r'); | ||||||
|  |       client.readStringUntil('\n'); | ||||||
|  |     } | ||||||
|  |     _parseArguments(searchStr); | ||||||
|  |     if(isForm){ | ||||||
|  |       _parseForm(client, boundaryStr, contentLength); | ||||||
|  |     } | ||||||
|  |   } else { | ||||||
|  |     _parseArguments(searchStr); | ||||||
|  |   } | ||||||
|  |   client.flush(); | ||||||
|  |  | ||||||
|  | #ifdef DEBUG | ||||||
|  |   DEBUG_OUTPUT.print("Request: "); | ||||||
|  |   DEBUG_OUTPUT.println(url); | ||||||
|  |   DEBUG_OUTPUT.print(" Arguments: "); | ||||||
|  |   DEBUG_OUTPUT.println(searchStr); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | void ESP8266WebServer::_parseArguments(String data) { | ||||||
|  | #ifdef DEBUG | ||||||
|  |   DEBUG_OUTPUT.print("args: "); | ||||||
|  |   DEBUG_OUTPUT.println(data); | ||||||
|  | #endif | ||||||
|  |   if (_currentArgs) | ||||||
|  |     delete[] _currentArgs; | ||||||
|  |   _currentArgs = 0; | ||||||
|  |   if (data.length() == 0) { | ||||||
|  |     _currentArgCount = 0; | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |   _currentArgCount = 1; | ||||||
|  |  | ||||||
|  |   for (int i = 0; i < data.length(); ) { | ||||||
|  |     i = data.indexOf('&', i); | ||||||
|  |     if (i == -1) | ||||||
|  |       break; | ||||||
|  |     ++i; | ||||||
|  |     ++_currentArgCount; | ||||||
|  |   } | ||||||
|  | #ifdef DEBUG | ||||||
|  |   DEBUG_OUTPUT.print("args count: "); | ||||||
|  |   DEBUG_OUTPUT.println(_currentArgCount); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |   _currentArgs = new RequestArgument[_currentArgCount]; | ||||||
|  |   int pos = 0; | ||||||
|  |   int iarg; | ||||||
|  |   for (iarg = 0; iarg < _currentArgCount;) { | ||||||
|  |     int equal_sign_index = data.indexOf('=', pos); | ||||||
|  |     int next_arg_index = data.indexOf('&', pos); | ||||||
|  | #ifdef DEBUG | ||||||
|  |     DEBUG_OUTPUT.print("pos "); | ||||||
|  |     DEBUG_OUTPUT.print(pos); | ||||||
|  |     DEBUG_OUTPUT.print("=@ "); | ||||||
|  |     DEBUG_OUTPUT.print(equal_sign_index); | ||||||
|  |     DEBUG_OUTPUT.print(" &@ "); | ||||||
|  |     DEBUG_OUTPUT.println(next_arg_index); | ||||||
|  | #endif | ||||||
|  |     if ((equal_sign_index == -1) || ((equal_sign_index > next_arg_index) && (next_arg_index != -1))) { | ||||||
|  | #ifdef DEBUG | ||||||
|  |       DEBUG_OUTPUT.print("arg missing value: "); | ||||||
|  |       DEBUG_OUTPUT.println(iarg); | ||||||
|  | #endif | ||||||
|  |       if (next_arg_index == -1) | ||||||
|  |         break; | ||||||
|  |       pos = next_arg_index + 1; | ||||||
|  |       continue; | ||||||
|  |     } | ||||||
|  |     RequestArgument& arg = _currentArgs[iarg]; | ||||||
|  |     arg.key = data.substring(pos, equal_sign_index); | ||||||
|  |     arg.value = data.substring(equal_sign_index + 1, next_arg_index); | ||||||
|  | #ifdef DEBUG | ||||||
|  |     DEBUG_OUTPUT.print("arg "); | ||||||
|  |     DEBUG_OUTPUT.print(iarg); | ||||||
|  |     DEBUG_OUTPUT.print(" key: "); | ||||||
|  |     DEBUG_OUTPUT.print(arg.key); | ||||||
|  |     DEBUG_OUTPUT.print(" value: "); | ||||||
|  |     DEBUG_OUTPUT.println(arg.value); | ||||||
|  | #endif | ||||||
|  |     ++iarg; | ||||||
|  |     if (next_arg_index == -1) | ||||||
|  |       break; | ||||||
|  |     pos = next_arg_index + 1; | ||||||
|  |   } | ||||||
|  |   _currentArgCount = iarg; | ||||||
|  | #ifdef DEBUG | ||||||
|  |   DEBUG_OUTPUT.print("args count: "); | ||||||
|  |   DEBUG_OUTPUT.println(_currentArgCount); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ESP8266WebServer::_parseForm(WiFiClient& client, String boundary, uint32_t len){ | ||||||
|  |    | ||||||
|  | #ifdef DEBUG | ||||||
|  |   DEBUG_OUTPUT.print("Parse Form: Boundary: "); | ||||||
|  |   DEBUG_OUTPUT.print(boundary); | ||||||
|  |   DEBUG_OUTPUT.print("Length: "); | ||||||
|  |   DEBUG_OUTPUT.println(len); | ||||||
|  | #endif | ||||||
|  |   String line; | ||||||
|  |   line = client.readStringUntil('\r'); | ||||||
|  |   client.readStringUntil('\n'); | ||||||
|  |   //start reading the form | ||||||
|  |   if(line == ("--"+boundary)){ | ||||||
|  |     RequestArgument* postArgs = new RequestArgument[32]; | ||||||
|  |     int postArgsLen = 0; | ||||||
|  |     while(1){ | ||||||
|  |       String argName; | ||||||
|  |       String argValue; | ||||||
|  |       String argType; | ||||||
|  |       String argFilename; | ||||||
|  |       bool argIsFile = false; | ||||||
|  |        | ||||||
|  |       line = client.readStringUntil('\r'); | ||||||
|  |       client.readStringUntil('\n'); | ||||||
|  |       if(line.startsWith("Content-Disposition")){ | ||||||
|  |         int nameStart = line.indexOf('='); | ||||||
|  |         if(nameStart != -1){ | ||||||
|  |           argName = line.substring(nameStart+2); | ||||||
|  |           nameStart = argName.indexOf('='); | ||||||
|  |           if(nameStart == -1){ | ||||||
|  |             argName = argName.substring(0, argName.length() - 1); | ||||||
|  |           } else { | ||||||
|  |             argFilename = argName.substring(nameStart+2, argName.length() - 1); | ||||||
|  |             argName = argName.substring(0, argName.indexOf('"')); | ||||||
|  |             argIsFile = true; | ||||||
|  |   #ifdef DEBUG | ||||||
|  |             DEBUG_OUTPUT.print("PostArg FileName: "); | ||||||
|  |             DEBUG_OUTPUT.println(argFilename); | ||||||
|  |   #endif | ||||||
|  |             //use GET to set the filename if uploading using blob | ||||||
|  |             if(argFilename == "blob" && hasArg("filename")) argFilename = arg("filename"); | ||||||
|  |           } | ||||||
|  |   #ifdef DEBUG | ||||||
|  |           DEBUG_OUTPUT.print("PostArg Name: "); | ||||||
|  |           DEBUG_OUTPUT.println(argName); | ||||||
|  |   #endif | ||||||
|  |           argType = "text/plain"; | ||||||
|  |           line = client.readStringUntil('\r'); | ||||||
|  |           client.readStringUntil('\n'); | ||||||
|  |           if(line.startsWith("Content-Type")){ | ||||||
|  |             argType = line.substring(line.indexOf(':')+2); | ||||||
|  |             //skip next line | ||||||
|  |             client.readStringUntil('\r'); | ||||||
|  |             client.readStringUntil('\n'); | ||||||
|  |           } | ||||||
|  |   #ifdef DEBUG | ||||||
|  |           DEBUG_OUTPUT.print("PostArg Type: "); | ||||||
|  |           DEBUG_OUTPUT.println(argType); | ||||||
|  |   #endif | ||||||
|  |           if(!argIsFile){ | ||||||
|  |             while(1){ | ||||||
|  |               line = client.readStringUntil('\r'); | ||||||
|  |               client.readStringUntil('\n'); | ||||||
|  |               if(line.startsWith("--"+boundary)) break; | ||||||
|  |               if(argValue.length() > 0) argValue += "\n"; | ||||||
|  |               argValue += line; | ||||||
|  |             } | ||||||
|  |   #ifdef DEBUG | ||||||
|  |             DEBUG_OUTPUT.print("PostArg Value: "); | ||||||
|  |             DEBUG_OUTPUT.println(argValue); | ||||||
|  |             DEBUG_OUTPUT.println(); | ||||||
|  |   #endif | ||||||
|  |              | ||||||
|  |             RequestArgument& arg = postArgs[postArgsLen++]; | ||||||
|  |             arg.key = argName; | ||||||
|  |             arg.value = argValue; | ||||||
|  |              | ||||||
|  |             if(line == ("--"+boundary+"--")){ | ||||||
|  |   #ifdef DEBUG | ||||||
|  |               DEBUG_OUTPUT.println("Done Parsing POST"); | ||||||
|  |   #endif | ||||||
|  |               break; | ||||||
|  |             } | ||||||
|  |           } else { | ||||||
|  |             _currentUpload.status = UPLOAD_FILE_START; | ||||||
|  |             _currentUpload.name = argName; | ||||||
|  |             _currentUpload.filename = argFilename; | ||||||
|  |             _currentUpload.type = argType; | ||||||
|  |             _currentUpload.totalSize = 0; | ||||||
|  |             _currentUpload.currentSize = 0; | ||||||
|  | #ifdef DEBUG | ||||||
|  |             DEBUG_OUTPUT.print("Start File: "); | ||||||
|  |             DEBUG_OUTPUT.print(_currentUpload.filename); | ||||||
|  |             DEBUG_OUTPUT.print(" Type: "); | ||||||
|  |             DEBUG_OUTPUT.println(_currentUpload.type); | ||||||
|  | #endif | ||||||
|  |             if(_fileUploadHandler) _fileUploadHandler(); | ||||||
|  |             _currentUpload.status = UPLOAD_FILE_WRITE; | ||||||
|  |             uint8_t argByte = client.read(); | ||||||
|  | readfile: | ||||||
|  |             while(argByte != 0x0D){ | ||||||
|  |               _currentUpload.buf[_currentUpload.currentSize++] = argByte; | ||||||
|  |               if(_currentUpload.currentSize == PAYLOAD_UNIT_SIZE){ | ||||||
|  |   #ifdef DEBUG | ||||||
|  |                 DEBUG_OUTPUT.print("Write File: "); | ||||||
|  |                 DEBUG_OUTPUT.println(PAYLOAD_UNIT_SIZE); | ||||||
|  |   #endif | ||||||
|  |                 if(_fileUploadHandler) _fileUploadHandler(); | ||||||
|  |                 _currentUpload.totalSize += _currentUpload.currentSize; | ||||||
|  |                 _currentUpload.currentSize = 0; | ||||||
|  |               } | ||||||
|  |               argByte = client.read(); | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             argByte = client.read(); | ||||||
|  |             if(argByte == 0x0A){ | ||||||
|  | #ifdef DEBUG | ||||||
|  |               DEBUG_OUTPUT.print("Write File: "); | ||||||
|  |               DEBUG_OUTPUT.println(_currentUpload.currentSize); | ||||||
|  | #endif | ||||||
|  |               if(_fileUploadHandler) _fileUploadHandler(); | ||||||
|  |               _currentUpload.totalSize += _currentUpload.currentSize; | ||||||
|  |               _currentUpload.currentSize = 0; | ||||||
|  |                | ||||||
|  |               argByte = client.read(); | ||||||
|  |               if((char)argByte != '-'){ | ||||||
|  |                 //continue reading the file | ||||||
|  |                 _currentUpload.buf[_currentUpload.currentSize++] = 0x0D; | ||||||
|  |                 _currentUpload.buf[_currentUpload.currentSize++] = 0x0A; | ||||||
|  |                 goto readfile; | ||||||
|  |               } else { | ||||||
|  |                 argByte = client.read(); | ||||||
|  |                 if((char)argByte != '-'){ | ||||||
|  |                   //continue reading the file | ||||||
|  |                   _currentUpload.buf[_currentUpload.currentSize++] = 0x0D; | ||||||
|  |                   _currentUpload.buf[_currentUpload.currentSize++] = 0x0A; | ||||||
|  |                   _currentUpload.buf[_currentUpload.currentSize++] = (uint8_t)('-'); | ||||||
|  |                   goto readfile; | ||||||
|  |                 } | ||||||
|  |               } | ||||||
|  |                | ||||||
|  |               uint8_t endBuf[boundary.length()]; | ||||||
|  |               client.readBytes(endBuf, boundary.length()); | ||||||
|  |                | ||||||
|  |               if(strstr((const char*)endBuf, (const char*)(boundary.c_str())) != NULL){ | ||||||
|  |                 _currentUpload.status = UPLOAD_FILE_END; | ||||||
|  | #ifdef DEBUG | ||||||
|  |                 DEBUG_OUTPUT.print("End File: "); | ||||||
|  |                 DEBUG_OUTPUT.print(_currentUpload.filename); | ||||||
|  |                 DEBUG_OUTPUT.print(" Type: "); | ||||||
|  |                 DEBUG_OUTPUT.print(_currentUpload.type); | ||||||
|  |                 DEBUG_OUTPUT.print(" Size: "); | ||||||
|  |                 DEBUG_OUTPUT.println(_currentUpload.totalSize); | ||||||
|  | #endif | ||||||
|  |                 if(_fileUploadHandler) _fileUploadHandler(); | ||||||
|  |                 line = client.readStringUntil(0x0D); | ||||||
|  |                 client.readStringUntil(0x0A); | ||||||
|  |                 if(line == "--"){ | ||||||
|  | #ifdef DEBUG | ||||||
|  |                   DEBUG_OUTPUT.println("Done Parsing POST"); | ||||||
|  | #endif | ||||||
|  |                   break; | ||||||
|  |                 } | ||||||
|  |                 continue; | ||||||
|  |               } else { | ||||||
|  |                 _currentUpload.buf[_currentUpload.currentSize++] = 0x0D; | ||||||
|  |                 _currentUpload.buf[_currentUpload.currentSize++] = 0x0A; | ||||||
|  |                 uint32_t i = 0; | ||||||
|  |                 while(i < boundary.length()){ | ||||||
|  |                   _currentUpload.buf[_currentUpload.currentSize++] = endBuf[i++]; | ||||||
|  |                   if(_currentUpload.currentSize == PAYLOAD_UNIT_SIZE){ | ||||||
|  | #ifdef DEBUG | ||||||
|  |                     DEBUG_OUTPUT.print("Write File: "); | ||||||
|  |                     DEBUG_OUTPUT.println(PAYLOAD_UNIT_SIZE); | ||||||
|  | #endif | ||||||
|  |                     if(_fileUploadHandler) _fileUploadHandler(); | ||||||
|  |                     _currentUpload.totalSize += _currentUpload.currentSize; | ||||||
|  |                     _currentUpload.currentSize = 0; | ||||||
|  |                   } | ||||||
|  |                 } | ||||||
|  |                 argByte = client.read(); | ||||||
|  |                 goto readfile; | ||||||
|  |               } | ||||||
|  |             } else { | ||||||
|  |               _currentUpload.buf[_currentUpload.currentSize++] = 0x0D; | ||||||
|  |               if(_currentUpload.currentSize == PAYLOAD_UNIT_SIZE){ | ||||||
|  |   #ifdef DEBUG | ||||||
|  |                 DEBUG_OUTPUT.print("Write File: "); | ||||||
|  |                 DEBUG_OUTPUT.println(PAYLOAD_UNIT_SIZE); | ||||||
|  |   #endif | ||||||
|  |                 if(_fileUploadHandler) _fileUploadHandler(); | ||||||
|  |                 _currentUpload.totalSize += _currentUpload.currentSize; | ||||||
|  |                 _currentUpload.currentSize = 0; | ||||||
|  |               } | ||||||
|  |               goto readfile; | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     int iarg; | ||||||
|  |     int totalArgs = ((32 - postArgsLen) < _currentArgCount)?(32 - postArgsLen):_currentArgCount; | ||||||
|  |     for (iarg = 0; iarg < totalArgs; iarg++){ | ||||||
|  |       RequestArgument& arg = postArgs[postArgsLen++]; | ||||||
|  |       arg.key = _currentArgs[iarg].key; | ||||||
|  |       arg.value = _currentArgs[iarg].value; | ||||||
|  |     } | ||||||
|  |     if (_currentArgs) delete[] _currentArgs; | ||||||
|  |     _currentArgs = new RequestArgument[postArgsLen]; | ||||||
|  |     for (iarg = 0; iarg < postArgsLen; iarg++){ | ||||||
|  |       RequestArgument& arg = _currentArgs[iarg]; | ||||||
|  |       arg.key = postArgs[iarg].key; | ||||||
|  |       arg.value = postArgs[iarg].value; | ||||||
|  |     } | ||||||
|  |     _currentArgCount = iarg; | ||||||
|  |     if (postArgs) delete[] postArgs; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
| @@ -25,6 +25,7 @@ | |||||||
| #include "Print.h" | #include "Print.h" | ||||||
| #include "Client.h" | #include "Client.h" | ||||||
| #include "IPAddress.h" | #include "IPAddress.h" | ||||||
|  | #include <memory> | ||||||
|  |  | ||||||
| class ClientContext; | class ClientContext; | ||||||
| class WiFiServer; | class WiFiServer; | ||||||
| @@ -44,6 +45,9 @@ public: | |||||||
|   virtual int connect(const char *host, uint16_t port); |   virtual int connect(const char *host, uint16_t port); | ||||||
|   virtual size_t write(uint8_t); |   virtual size_t write(uint8_t); | ||||||
|   virtual size_t write(const uint8_t *buf, size_t size); |   virtual size_t write(const uint8_t *buf, size_t size); | ||||||
|  |   template <typename T> | ||||||
|  |   size_t write(T& source, size_t unitSize); | ||||||
|  |  | ||||||
|   virtual int available(); |   virtual int available(); | ||||||
|   virtual int read(); |   virtual int read(); | ||||||
|   virtual int read(uint8_t *buf, size_t size); |   virtual int read(uint8_t *buf, size_t size); | ||||||
| @@ -72,4 +76,24 @@ private: | |||||||
|  |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | template <typename T> | ||||||
|  | inline size_t WiFiClient::write(T& source, size_t unitSize) { | ||||||
|  |   std::unique_ptr<uint8_t[]> buffer(new uint8_t[unitSize]); | ||||||
|  |   size_t size_sent = 0; | ||||||
|  |   while(true) { | ||||||
|  |     size_t left = source.available(); | ||||||
|  |     if (!left) | ||||||
|  |       break; | ||||||
|  |     size_t will_send = (left < unitSize) ? left : unitSize; | ||||||
|  |     source.read(buffer.get(), will_send); | ||||||
|  |     size_t cb = write(buffer.get(), will_send); | ||||||
|  |     size_sent += cb; | ||||||
|  |     if (cb != will_send) { | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   return size_sent; | ||||||
|  | } | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user