bool FOTAService(void) { FOTA_StateMachine_t state_current; String storage_bucket = EE_GetFirebaseStorageBucket(); state_current = state; switch (state) { case FOTA_Sm_IDLE: break; case FOTA_Sm_GET_MD5: { String md5file_url = String(F("/v0/b/")) + storage_bucket + String(F("/o/")) + VERS_HW_VER + String(FPSTR(md5file_name)) + String(F("?alt=media")); addr = String(F("https://")) + String(FPSTR(storage_host)) + md5file_url; DEBUG_PRINT("FOTA_Sm_GET_MD5 %s\n", addr.c_str()); http = new HTTPClient; http->setReuse(true); http->setTimeout(3000); bool res = http->begin(addr, storage_fingerprint); if (res == true) { int httpCode = http->GET(); // httpCode will be negative on error if (httpCode > 0) { // HTTP header has been send and Server response header has been handled DEBUG_PRINT("[HTTP] GET... code: %d\n", httpCode); // file found at server if (httpCode == HTTP_CODE_OK) { int size = http->getSize(); if (size == DIGEST_MD5_SIZE) { DEBUG_PRINT("md5file size %d\n", size); WiFiClient *stream = http->getStreamPtr(); digest_MD5 = (uint8_t *)malloc(DIGEST_MD5_SIZE); stream->read(digest_MD5, size); state = FOTA_Sm_CHECK; } else { DEBUG_PRINT("md5file size error: %d\n", size); state = FOTA_Sm_ERROR; } } else { DEBUG_PRINT("md5file httpCode error: %d\n", httpCode); state = FOTA_Sm_ERROR; } } else { DEBUG_PRINT("[HTTP] GET... failed, error: %s\n", http->errorToString(httpCode).c_str()); state = FOTA_Sm_ERROR; } } else { DEBUG_PRINT("[HTTP] begin... failed, error: %d\n", res); state = FOTA_Sm_ERROR; } } break; case FOTA_Sm_CHECK: { String file_url = String(F("/v0/b/")) + storage_bucket + String(F("/o/")) + VERS_HW_VER + String(FPSTR(file_name)) + String(F("?alt=media")); addr = String(F("https://")) + String(FPSTR(storage_host)) + file_url; DEBUG_PRINT("FOTA_Sm_CHECK %s\n", addr.c_str()); http->setReuse(true); bool res = http->begin(addr, storage_fingerprint); if (res == true) { int httpCode = http->GET(); // httpCode will be negative on error if (httpCode > 0) { http->end(); // HTTP header has been send and Server response header has been handled DEBUG_PRINT("[HTTP] GET... code: %d\n", httpCode); int size = http->getSize(); DEBUG_PRINT("file size %d\n", size); block = 0; num_blocks = (size + block_size - 1) / block_size; if (Update.begin(size, 0)) { Update.setMD5((char *)digest_MD5); buffer = (uint8_t *)malloc(block_size); state = FOTA_Sm_GET_BLOCK; } else { state = FOTA_Sm_ERROR; } } else { DEBUG_PRINT("file httpCode error: %d\n", httpCode); state = FOTA_Sm_ERROR; } } else { state = FOTA_Sm_ERROR; } } break; case FOTA_Sm_GET_BLOCK: { http->setReuse(true); bool res = http->begin(addr, storage_fingerprint); if (res == true) { String range = String(F("bytes=")) + String(block * block_size) + String(F("-")) + String(((block + 1) * block_size) - 1); http->addHeader(String(F("Range")), range); int httpCode = http->GET(); // httpCode will be negative on error if (httpCode > 0) { // HTTP header has been send and Server response header has been handled // Serial.printf("[HTTP] GET... code: %d\n", httpCode); int len = http->getSize(); // get tcp stream WiFiClient *stream = http->getStreamPtr(); int pos = 0; bool run = true; bool fail = false; while (run == true) { delay(1); if (pos < len) { size_t size = stream->available(); if (size) { uint16_t c = stream->readBytes(&buffer[pos], size); pos += c; } if (!http->connected() && (pos < len)) { run = false; fail = true; } } else { run = false; } } if (fail == false) { Update.write(buffer, pos); DEBUG_PRINT("[%03d]: %02d%% -- %d\r", block, 100 * block / num_blocks, ESP.getFreeHeap()); block++; if (block < num_blocks) { /* move to next block */ state = FOTA_Sm_GET_BLOCK; } else { if (!Update.end()) { DEBUG_PRINT("Update Error\n"); } state = FOTA_Sm_COMPLETE; } } else { state = FOTA_Sm_ERROR; } } else { DEBUG_PRINT("[HTTP] GET... failed, error: %s\n", http->errorToString(httpCode).c_str()); state = FOTA_Sm_ERROR; } } else { DEBUG_PRINT("begin error @ %d\n", block); state = FOTA_Sm_ERROR; } } break; case FOTA_Sm_ERROR: { if (http_fail_cnt++ < 20) { DEBUG_PRINT("retry %d\n", http_fail_cnt); state = state_last; } else { /* give-up */ DEBUG_PRINT("retry give-up\n"); Serial.flush(); ESP.restart(); } } break; case FOTA_Sm_COMPLETE: { DEBUG_PRINT("closing connection\n"); Serial.flush(); free(buffer); /* restar node anycase */ ESP.restart(); state = FOTA_Sm_IDLE; } break; } state_last = state_current; return (state != FOTA_Sm_IDLE); }
bool ESP8266WebServer::_parseForm(WiFiClient& client, String boundary, uint32_t len){ #ifdef DEBUG_ESP_HTTP_SERVER DEBUG_OUTPUT.print("Parse Form: Boundary: "); DEBUG_OUTPUT.print(boundary); DEBUG_OUTPUT.print(" Length: "); DEBUG_OUTPUT.println(len); #endif String line; int retry = 0; do { line = client.readStringUntil('\r'); ++retry; } while (line.length() == 0 && retry < 3); 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_ESP_HTTP_SERVER 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_ESP_HTTP_SERVER 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_ESP_HTTP_SERVER 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_ESP_HTTP_SERVER 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_ESP_HTTP_SERVER 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_ESP_HTTP_SERVER DEBUG_OUTPUT.print("Start File: "); DEBUG_OUTPUT.print(_currentUpload.filename); DEBUG_OUTPUT.print(" Type: "); DEBUG_OUTPUT.println(_currentUpload.type); #endif if(_currentHandler && _currentHandler->canUpload(_currentUri)) _currentHandler->upload(*this, _currentUri, _currentUpload); _currentUpload.status = UPLOAD_FILE_WRITE; uint8_t argByte = _uploadReadByte(client); readfile: while(argByte != 0x0D){ if (!client.connected()) return _parseFormUploadAborted(); _uploadWriteByte(argByte); argByte = _uploadReadByte(client); } argByte = _uploadReadByte(client); if (!client.connected()) return _parseFormUploadAborted(); if (argByte == 0x0A){ argByte = _uploadReadByte(client); if (!client.connected()) return _parseFormUploadAborted(); if ((char)argByte != '-'){ //continue reading the file _uploadWriteByte(0x0D); _uploadWriteByte(0x0A); goto readfile; } else { argByte = _uploadReadByte(client); if (!client.connected()) return _parseFormUploadAborted(); if ((char)argByte != '-'){ //continue reading the file _uploadWriteByte(0x0D); _uploadWriteByte(0x0A); _uploadWriteByte((uint8_t)('-')); goto readfile; } } uint8_t endBuf[boundary.length()]; client.readBytes(endBuf, boundary.length()); if (strstr((const char*)endBuf, boundary.c_str()) != NULL){ if(_currentHandler && _currentHandler->canUpload(_currentUri)) _currentHandler->upload(*this, _currentUri, _currentUpload); _currentUpload.totalSize += _currentUpload.currentSize; _currentUpload.status = UPLOAD_FILE_END; if(_currentHandler && _currentHandler->canUpload(_currentUri)) _currentHandler->upload(*this, _currentUri, _currentUpload); #ifdef DEBUG_ESP_HTTP_SERVER 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 line = client.readStringUntil(0x0D); client.readStringUntil(0x0A); if (line == "--"){ #ifdef DEBUG_ESP_HTTP_SERVER DEBUG_OUTPUT.println("Done Parsing POST"); #endif break; } continue; } else { _uploadWriteByte(0x0D); _uploadWriteByte(0x0A); _uploadWriteByte((uint8_t)('-')); _uploadWriteByte((uint8_t)('-')); uint32_t i = 0; while(i < boundary.length()){ _uploadWriteByte(endBuf[i++]); } argByte = _uploadReadByte(client); goto readfile; } } else { _uploadWriteByte(0x0D); 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; return true; } #ifdef DEBUG_ESP_HTTP_SERVER DEBUG_OUTPUT.print("Error: line: "); DEBUG_OUTPUT.println(line); #endif return false; }
int uploadFile(const char* _filename, const char* _fileurl) { // upload new file to fs by downloading from a remote server, rather than reflash the entire spiffs int ret = false; HTTPClient http; //const char* fileUrl = "http://mypi3/iot/index.html"; //const char* fileName = "/test2.html"; if (hasSerial) Serial.printf("url %s\n", _fileurl); if (hasSerial) Serial.printf("file %s\n", _filename); http.begin(_fileurl); // init http client // start connection and send HTTP header int httpCode = http.GET(); // httpCode will be negative on error if(httpCode > 0) { // HTTP header has been send and Server response header has been handled // file found at server if(httpCode == HTTP_CODE_OK) { if (hasSerial) Serial.printf("HTTP client http status %d\n", httpCode); // get lenght of document (is -1 when Server sends no Content-Length header) int len = http.getSize(); int paysize = len; if (hasSerial) Serial.printf("HTTP content size %d bytes\n", paysize); // create buffer for read uint8_t buff[128] = { 0 }; // get tcp stream WiFiClient * stream = http.getStreamPtr(); // create or recreate file on spiffs File configFile = SPIFFS.open(_filename, "w"); if (!configFile) { if (hasSerial) Serial.printf("Failed to open %s for write.\n",_filename); return ret; } if (hasSerial) Serial.println("File open, write start."); // read all data from server while(http.connected() && (len > 0 || len == -1)) { // get available data size size_t size = stream->available(); if (size) { // read up to 128 byte int c = stream->readBytes(buff, ((size > sizeof(buff)) ? sizeof(buff) : size)); // write it to Serial configFile.write(buff, c); if(len > 0) { len -= c; } } } // EoF or http connection closed configFile.close(); http.end(); if (hasSerial) Serial.println("File closed, write complete."); return paysize; } else { if (hasSerial) Serial.printf("HTTP client http error %d\n", httpCode); return httpCode; } } else { if (hasSerial) Serial.printf("HTTP client http error %d\n", httpCode); return httpCode; } return 0; }
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 += '&'; //some clients send headers first and data after (like we do) //give them a chance int tries = 100;//100ms max wait while(!client.available() && tries--)delay(1); size_t plainLen = client.available(); char *plainBuf = (char*)malloc(plainLen+1); client.readBytes(plainBuf, plainLen); plainBuf[plainLen] = '\0'; #ifdef DEBUG DEBUG_OUTPUT.print("Plain: "); DEBUG_OUTPUT.println(plainBuf); #endif if(plainBuf[0] == '{' || plainBuf[0] == '[' || strstr(plainBuf, "=") == NULL){ //plain post json or other data searchStr += "plain="; searchStr += plainBuf; } else { searchStr += plainBuf; } free(plainBuf); } _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; }