mirror of
				https://github.com/KevinMidboe/TinyGSM.git
				synced 2025-10-29 18:00:18 +00:00 
			
		
		
		
	Merge pull request #302 from bhagman/FixFileDownload
Fixed data acquisition race condition in FileDownload example -- Fixes #301
This commit is contained in:
		| @@ -141,39 +141,114 @@ void loop() { | |||||||
|   client.print(String("Host: ") + server + "\r\n"); |   client.print(String("Host: ") + server + "\r\n"); | ||||||
|   client.print("Connection: close\r\n\r\n"); |   client.print("Connection: close\r\n\r\n"); | ||||||
|  |  | ||||||
|   long timeout = millis(); |   // This timeout check is unneeded since there is a timeout handler in the data retrieval below | ||||||
|   while (client.available() == 0) { |   // long timeout = millis(); | ||||||
|     if (millis() - timeout > 5000L) { |   // while (client.available() == 0) { | ||||||
|       SerialMon.println(F(">>> Client Timeout !")); |   //   if (millis() - timeout > 5000L) { | ||||||
|       client.stop(); |   //     SerialMon.println(F(">>> Client Timeout !")); | ||||||
|       delay(10000L); |   //     client.stop(); | ||||||
|       return; |   //     delay(10000L); | ||||||
|     } |   //     return; | ||||||
|   } |   //   } | ||||||
|  |   // } | ||||||
|  |  | ||||||
|   SerialMon.println(F("Reading response header")); |   // Let's see what the entire elapsed time is, from after we send the request. | ||||||
|   uint32_t contentLength = knownFileSize; |   unsigned long timeElapsed = millis(); | ||||||
|  |  | ||||||
|  |   SerialMon.println(F("Waiting for response header")); | ||||||
|  |  | ||||||
|  |   // While we are still looking for the end of the header (i.e. empty line FOLLOWED by a newline), | ||||||
|  |   // continue to read data into the buffer, parsing each line (data FOLLOWED by a newline). | ||||||
|  |   // If it takes too long to get data from the client, we need to exit. | ||||||
|  |  | ||||||
|  |   const uint32_t clientReadTimeout = 5000; | ||||||
|  |   uint32_t clientReadStartTime = millis(); | ||||||
|  |   String headerBuffer; | ||||||
|  |   bool finishedHeader = false; | ||||||
|  |   uint32_t contentLength = 0; | ||||||
|  |  | ||||||
|  |   while (!finishedHeader) { | ||||||
|  |     int nlPos; | ||||||
|  |  | ||||||
|  |     if (client.available()) { | ||||||
|  |       clientReadStartTime = millis(); | ||||||
|       while (client.available()) { |       while (client.available()) { | ||||||
|     String line = client.readStringUntil('\n'); |         char c = client.read(); | ||||||
|     line.trim(); |         headerBuffer += c; | ||||||
|     //SerialMon.println(line);    // Uncomment this to show response header |  | ||||||
|     line.toLowerCase(); |         // Uncomment the lines below to see the data coming into the buffer | ||||||
|     if (line.startsWith("content-length:")) { |         // if (c < 16) | ||||||
|       contentLength = line.substring(line.lastIndexOf(':') + 1).toInt(); |         //   SerialMon.print('0'); | ||||||
|     } else if (line.length() == 0) { |         // SerialMon.print(c, HEX); | ||||||
|  |         // SerialMon.print(' '); | ||||||
|  |         // if (isprint(c)) | ||||||
|  |         //   SerialMon.print((char) c); | ||||||
|  |         // else | ||||||
|  |         //   SerialMon.print('*'); | ||||||
|  |         // SerialMon.print(' '); | ||||||
|  |  | ||||||
|  |         // Let's exit and process if we find a new line | ||||||
|  |         if (headerBuffer.indexOf(F("\r\n")) >= 0) | ||||||
|  |           break; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |       if (millis() - clientReadStartTime > clientReadTimeout) { | ||||||
|  |         // Time-out waiting for data from client | ||||||
|  |         SerialMon.println(F(">>> Client Timeout !")); | ||||||
|         break; |         break; | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   SerialMon.println(F("Reading response data")); |     // See if we have a new line. | ||||||
|   timeout = millis(); |     nlPos = headerBuffer.indexOf(F("\r\n")); | ||||||
|  |  | ||||||
|  |     if (nlPos > 0) { | ||||||
|  |       headerBuffer.toLowerCase(); | ||||||
|  |       // Check if line contains content-length | ||||||
|  |       if (headerBuffer.startsWith(F("content-length:"))) { | ||||||
|  |         contentLength = headerBuffer.substring(headerBuffer.indexOf(':') + 1).toInt(); | ||||||
|  |         // SerialMon.print(F("Got Content Length: "));  // uncomment for | ||||||
|  |         // SerialMon.println(contentLength);            // confirmation | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       headerBuffer.remove(0, nlPos + 2);  // remove the line | ||||||
|  |     } | ||||||
|  |     else if (nlPos == 0) { | ||||||
|  |       // if the new line is empty (i.e. "\r\n" is at the beginning of the line), we are done with the header. | ||||||
|  |       finishedHeader = true; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // vv Broken vv | ||||||
|  |   // while (client.available()) {  // ** race condition -- if the client doesn't have enough data, we will exit. | ||||||
|  |   //   String line = client.readStringUntil('\n');  // ** Depending on what we get from the client, this could be a partial line. | ||||||
|  |   //   line.trim(); | ||||||
|  |   //   //SerialMon.println(line);    // Uncomment this to show response header | ||||||
|  |   //   line.toLowerCase(); | ||||||
|  |   //   if (line.startsWith("content-length:")) { | ||||||
|  |   //     contentLength = line.substring(line.lastIndexOf(':') + 1).toInt(); | ||||||
|  |   //   } else if (line.length() == 0) { | ||||||
|  |   //     break; | ||||||
|  |   //   } | ||||||
|  |   // } | ||||||
|  |   // ^^ Broken ^^ | ||||||
|  |   // | ||||||
|  |   // The two cases which are not managed properly are as follows: | ||||||
|  |   // 1. The client doesn't provide data quickly enough to keep up with this loop. | ||||||
|  |   // 2. If the client data is segmented in the middle of the 'Content-Length: ' header, | ||||||
|  |   //    then that header may be missed/damaged. | ||||||
|  |   // | ||||||
|  |  | ||||||
|   uint32_t readLength = 0; |   uint32_t readLength = 0; | ||||||
|   CRC32 crc; |   CRC32 crc; | ||||||
|  |  | ||||||
|   unsigned long timeElapsed = millis(); |   if (finishedHeader && contentLength == knownFileSize) { | ||||||
|  |     SerialMon.println(F("Reading response data")); | ||||||
|  |     clientReadStartTime = millis(); | ||||||
|  |  | ||||||
|     printPercent(readLength, contentLength); |     printPercent(readLength, contentLength); | ||||||
|   while (readLength < contentLength && client.connected() && millis() - timeout < 10000L) { |     while (readLength < contentLength && client.connected() && millis() - clientReadStartTime < clientReadTimeout) { | ||||||
|       while (client.available()) { |       while (client.available()) { | ||||||
|         uint8_t c = client.read(); |         uint8_t c = client.read(); | ||||||
|         //SerialMon.print((char)c);       // Uncomment this to show data |         //SerialMon.print((char)c);       // Uncomment this to show data | ||||||
| @@ -182,10 +257,12 @@ void loop() { | |||||||
|         if (readLength % (contentLength / 13) == 0) { |         if (readLength % (contentLength / 13) == 0) { | ||||||
|           printPercent(readLength, contentLength); |           printPercent(readLength, contentLength); | ||||||
|         } |         } | ||||||
|       timeout = millis(); |         clientReadStartTime = millis(); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     printPercent(readLength, contentLength); |     printPercent(readLength, contentLength); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   timeElapsed = millis() - timeElapsed; |   timeElapsed = millis() - timeElapsed; | ||||||
|   SerialMon.println(); |   SerialMon.println(); | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user