Fixed data acquisition race condition in FileDownload example

This commit is contained in:
Brett Hagman
2019-06-12 15:14:47 -04:00
parent 7c06a4cb9a
commit ccf68286a0

View File

@@ -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();