Example #1
0
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);
}
Example #2
0
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;
}
Example #3
0
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;
}
Example #4
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;
}