Alternative Improve _uploadReadByte (#2656)
* add opportunity for more than one retry to _uploadReadByte * an alternative timeout-based method to making _uploadReadByte more resilient * move timing variables in the correct scope * implement and use client.getTimeout instead of hard-coded timeout in _uploadReadByte * add missing return * some refactoring to address respecting the timeout in a potentially deadlocked connection * fix spelling in comment * address review comments; move impl to cpp file for getTimeout, and remove local variable for currentMillis * remove redundant cast * need to check for timeout outside the inner while as well * update WebUpdate example to print something in unexpected callback condition * update log_e messages per review comments
This commit is contained in:
parent
e0beac88c9
commit
25c0b52212
@ -82,6 +82,9 @@ void Stream::setTimeout(unsigned long timeout) // sets the maximum number of mi
|
|||||||
{
|
{
|
||||||
_timeout = timeout;
|
_timeout = timeout;
|
||||||
}
|
}
|
||||||
|
unsigned long Stream::getTimeout(void) {
|
||||||
|
return _timeout;
|
||||||
|
}
|
||||||
|
|
||||||
// find returns true if the target string is found
|
// find returns true if the target string is found
|
||||||
bool Stream::find(const char *target)
|
bool Stream::find(const char *target)
|
||||||
|
@ -59,7 +59,7 @@ public:
|
|||||||
// parsing methods
|
// parsing methods
|
||||||
|
|
||||||
void setTimeout(unsigned long timeout); // sets maximum milliseconds to wait for stream data, default is 1 second
|
void setTimeout(unsigned long timeout); // sets maximum milliseconds to wait for stream data, default is 1 second
|
||||||
|
unsigned long getTimeout(void);
|
||||||
bool find(const char *target); // reads data from the stream until the target string is found
|
bool find(const char *target); // reads data from the stream until the target string is found
|
||||||
bool find(uint8_t *target)
|
bool find(uint8_t *target)
|
||||||
{
|
{
|
||||||
|
@ -50,6 +50,8 @@ void setup(void) {
|
|||||||
Update.printError(Serial);
|
Update.printError(Serial);
|
||||||
}
|
}
|
||||||
Serial.setDebugOutput(false);
|
Serial.setDebugOutput(false);
|
||||||
|
} else {
|
||||||
|
Serial.printf("Update Failed Unexpectedly (likely broken connection): status=%d\n", upload.status);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
server.begin();
|
server.begin();
|
||||||
|
@ -304,11 +304,41 @@ void WebServer::_uploadWriteByte(uint8_t b){
|
|||||||
|
|
||||||
int WebServer::_uploadReadByte(WiFiClient& client){
|
int WebServer::_uploadReadByte(WiFiClient& client){
|
||||||
int res = client.read();
|
int res = client.read();
|
||||||
if(res == -1){
|
if(res < 0) {
|
||||||
while(!client.available() && client.connected())
|
// keep trying until you either read a valid byte or timeout
|
||||||
|
unsigned long startMillis = millis();
|
||||||
|
long timeoutIntervalMillis = client.getTimeout();
|
||||||
|
boolean timedOut = false;
|
||||||
|
for(;;) {
|
||||||
|
// loosely modeled after blinkWithoutDelay pattern
|
||||||
|
while(!timedOut && !client.available() && client.connected()){
|
||||||
delay(2);
|
delay(2);
|
||||||
res = client.read();
|
timedOut = millis() - startMillis >= timeoutIntervalMillis;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
res = client.read();
|
||||||
|
if(res >= 0) {
|
||||||
|
return res; // exit on a valid read
|
||||||
|
}
|
||||||
|
// NOTE: it is possible to get here and have all of the following
|
||||||
|
// assertions hold true
|
||||||
|
//
|
||||||
|
// -- client.available() > 0
|
||||||
|
// -- client.connected == true
|
||||||
|
// -- res == -1
|
||||||
|
//
|
||||||
|
// a simple retry strategy overcomes this which is to say the
|
||||||
|
// assertion is not permanent, but the reason that this works
|
||||||
|
// is elusive, and possibly indicative of a more subtle underlying
|
||||||
|
// issue
|
||||||
|
|
||||||
|
timedOut = millis() - startMillis >= timeoutIntervalMillis;
|
||||||
|
if(timedOut) {
|
||||||
|
return res; // exit on a timeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,7 +279,7 @@ int WiFiClient::setOption(int option, int *value)
|
|||||||
{
|
{
|
||||||
int res = setsockopt(fd(), IPPROTO_TCP, option, (char *) value, sizeof(int));
|
int res = setsockopt(fd(), IPPROTO_TCP, option, (char *) value, sizeof(int));
|
||||||
if(res < 0) {
|
if(res < 0) {
|
||||||
log_e("%d", errno);
|
log_e("fail on fd %d, errno: %d, \"%s\"", fd(), errno, esp_err_to_name(errno));
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@ -289,7 +289,7 @@ int WiFiClient::getOption(int option, int *value)
|
|||||||
size_t size = sizeof(int);
|
size_t size = sizeof(int);
|
||||||
int res = getsockopt(fd(), IPPROTO_TCP, option, (char *)value, &size);
|
int res = getsockopt(fd(), IPPROTO_TCP, option, (char *)value, &size);
|
||||||
if(res < 0) {
|
if(res < 0) {
|
||||||
log_e("%d", errno);
|
log_e("fail on fd %d, errno: %d, \"%s\"", fd(), errno, esp_err_to_name(errno));
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@ -362,7 +362,7 @@ size_t WiFiClient::write(const uint8_t *buf, size_t size)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(res < 0) {
|
else if(res < 0) {
|
||||||
log_e("%d", errno);
|
log_e("fail on fd %d, errno: %d, \"%s\"", fd(), errno, esp_err_to_name(errno));
|
||||||
if(errno != EAGAIN) {
|
if(errno != EAGAIN) {
|
||||||
//if resource was busy, can try again, otherwise give up
|
//if resource was busy, can try again, otherwise give up
|
||||||
stop();
|
stop();
|
||||||
@ -406,7 +406,7 @@ int WiFiClient::read(uint8_t *buf, size_t size)
|
|||||||
int res = -1;
|
int res = -1;
|
||||||
res = _rxBuffer->read(buf, size);
|
res = _rxBuffer->read(buf, size);
|
||||||
if(_rxBuffer->failed()) {
|
if(_rxBuffer->failed()) {
|
||||||
log_e("%d", errno);
|
log_e("fail on fd %d, errno: %d, \"%s\"", fd(), errno, esp_err_to_name(errno));
|
||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
@ -416,7 +416,7 @@ int WiFiClient::peek()
|
|||||||
{
|
{
|
||||||
int res = _rxBuffer->peek();
|
int res = _rxBuffer->peek();
|
||||||
if(_rxBuffer->failed()) {
|
if(_rxBuffer->failed()) {
|
||||||
log_e("%d", errno);
|
log_e("fail on fd %d, errno: %d, \"%s\"", fd(), errno, esp_err_to_name(errno));
|
||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
@ -430,7 +430,7 @@ int WiFiClient::available()
|
|||||||
}
|
}
|
||||||
int res = _rxBuffer->available();
|
int res = _rxBuffer->available();
|
||||||
if(_rxBuffer->failed()) {
|
if(_rxBuffer->failed()) {
|
||||||
log_e("%d", errno);
|
log_e("fail on fd %d, errno: %d, \"%s\"", fd(), errno, esp_err_to_name(errno));
|
||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
@ -452,7 +452,7 @@ void WiFiClient::flush() {
|
|||||||
toRead = (a>WIFI_CLIENT_FLUSH_BUFFER_SIZE)?WIFI_CLIENT_FLUSH_BUFFER_SIZE:a;
|
toRead = (a>WIFI_CLIENT_FLUSH_BUFFER_SIZE)?WIFI_CLIENT_FLUSH_BUFFER_SIZE:a;
|
||||||
res = recv(fd(), buf, toRead, MSG_DONTWAIT);
|
res = recv(fd(), buf, toRead, MSG_DONTWAIT);
|
||||||
if(res < 0) {
|
if(res < 0) {
|
||||||
log_e("%d", errno);
|
log_e("fail on fd %d, errno: %d, \"%s\"", fd(), errno, esp_err_to_name(errno));
|
||||||
stop();
|
stop();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user