Skip to content

Commit

Permalink
deprecate and update Stream::send*(Print -> Stream) (#8874)
Browse files Browse the repository at this point in the history
* deprecate and update Stream::send(Print -> Stream) in order to benefit from and use output's timeout value
  • Loading branch information
d-a-v committed Mar 28, 2023
1 parent 01d1c8e commit a5d31a7
Show file tree
Hide file tree
Showing 4 changed files with 185 additions and 125 deletions.
46 changes: 38 additions & 8 deletions cores/esp8266/Stream.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,25 +167,49 @@ class Stream: public Print {
// When result is 0 or less than requested maxLen, Print::getLastSend()
// contains an error reason.

#pragma GCC diagnostic ignored "-Wdeprecated-declarations"

// transfers already buffered / immediately available data (no timeout)
// returns number of transferred bytes
[[deprecated]] size_t sendAvailable (Print* to) { return sendGeneric(to, -1, -1, oneShotMs::alwaysExpired); }
[[deprecated]] size_t sendAvailable (Print& to) { return sendAvailable(&to); }

// transfers data until timeout
// returns number of transferred bytes
[[deprecated]] size_t sendAll (Print* to, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, -1, -1, timeoutMs); }
[[deprecated]] size_t sendAll (Print& to, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendAll(&to, timeoutMs); }

// transfers data until a char is encountered (the char is swallowed but not transferred) with timeout
// returns number of transferred bytes
[[deprecated]] size_t sendUntil (Print* to, const int readUntilChar, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, -1, readUntilChar, timeoutMs); }
[[deprecated]] size_t sendUntil (Print& to, const int readUntilChar, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendUntil(&to, readUntilChar, timeoutMs); }

// transfers data until requested size or timeout
// returns number of transferred bytes
[[deprecated]] size_t sendSize (Print* to, const ssize_t maxLen, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, maxLen, -1, timeoutMs); }
[[deprecated]] size_t sendSize (Print& to, const ssize_t maxLen, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendSize(&to, maxLen, timeoutMs); }

#pragma GCC diagnostic pop

// transfers already buffered / immediately available data (no timeout)
// returns number of transferred bytes
size_t sendAvailable (Print* to) { return sendGeneric(to, -1, -1, oneShotMs::alwaysExpired); }
size_t sendAvailable (Print& to) { return sendAvailable(&to); }
size_t sendAvailable (Stream* to) { return sendGeneric(to, -1, -1, oneShotMs::alwaysExpired); }
size_t sendAvailable (Stream& to) { return sendAvailable(&to); }

// transfers data until timeout
// returns number of transferred bytes
size_t sendAll (Print* to, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, -1, -1, timeoutMs); }
size_t sendAll (Print& to, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendAll(&to, timeoutMs); }
size_t sendAll (Stream* to, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, -1, -1, timeoutMs); }
size_t sendAll (Stream& to, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendAll(&to, timeoutMs); }

// transfers data until a char is encountered (the char is swallowed but not transferred) with timeout
// returns number of transferred bytes
size_t sendUntil (Print* to, const int readUntilChar, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, -1, readUntilChar, timeoutMs); }
size_t sendUntil (Print& to, const int readUntilChar, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendUntil(&to, readUntilChar, timeoutMs); }
size_t sendUntil (Stream* to, const int readUntilChar, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, -1, readUntilChar, timeoutMs); }
size_t sendUntil (Stream& to, const int readUntilChar, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendUntil(&to, readUntilChar, timeoutMs); }

// transfers data until requested size or timeout
// returns number of transferred bytes
size_t sendSize (Print* to, const ssize_t maxLen, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, maxLen, -1, timeoutMs); }
size_t sendSize (Print& to, const ssize_t maxLen, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendSize(&to, maxLen, timeoutMs); }
size_t sendSize (Stream* to, const ssize_t maxLen, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, maxLen, -1, timeoutMs); }
size_t sendSize (Stream& to, const ssize_t maxLen, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendSize(&to, maxLen, timeoutMs); }

// remaining size (-1 by default = unknown)
virtual ssize_t streamRemaining () { return -1; }
Expand All @@ -202,11 +226,17 @@ class Stream: public Print {
Report getLastSendReport () const { return _sendReport; }

protected:
[[deprecated]]
size_t sendGeneric (Print* to,
const ssize_t len = -1,
const int readUntilChar = -1,
oneShotMs::timeType timeoutMs = oneShotMs::neverExpires /* neverExpires=>getTimeout() */);

size_t sendGeneric (Stream* to,
const ssize_t len = -1,
const int readUntilChar = -1,
oneShotMs::timeType timeoutMs = oneShotMs::neverExpires /* neverExpires=>getTimeout() */);

size_t SendGenericPeekBuffer(Print* to, const ssize_t len, const int readUntilChar, const oneShotMs::timeType timeoutMs);
size_t SendGenericRegularUntil(Print* to, const ssize_t len, const int readUntilChar, const oneShotMs::timeType timeoutMs);
size_t SendGenericRegular(Print* to, const ssize_t len, const oneShotMs::timeType timeoutMs);
Expand Down
69 changes: 54 additions & 15 deletions cores/esp8266/StreamSend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,54 @@
#include <Arduino.h>
#include <StreamDev.h>

size_t Stream::sendGeneric(Stream* to, const ssize_t len, const int readUntilChar,
const esp8266::polledTimeout::oneShotFastMs::timeType timeoutMs)
{
// "neverExpires (default, impossible)" is translated to default timeout
esp8266::polledTimeout::oneShotFastMs::timeType inputTimeoutMs
= timeoutMs >= esp8266::polledTimeout::oneShotFastMs::neverExpires ? getTimeout()
: timeoutMs;

esp8266::polledTimeout::oneShotFastMs::timeType mainTimeoutMs = std::max(
inputTimeoutMs, (esp8266::polledTimeout::oneShotFastMs::timeType)to->getTimeout());

setReport(Report::Success);

if (len == 0)
{
return 0; // conveniently avoids timeout for no requested data
}

// There are two timeouts:
// - read (network, serial, ...)
// - write (network, serial, ...)
// However
// - getTimeout() is for reading only
// - there is no getOutputTimeout() api
// So we use getTimeout() for both,
// (also when inputCanTimeout() is false)

if (hasPeekBufferAPI())
{
return SendGenericPeekBuffer(to, len, readUntilChar, mainTimeoutMs);
}

if (readUntilChar >= 0)
{
return SendGenericRegularUntil(to, len, readUntilChar, mainTimeoutMs);
}

return SendGenericRegular(to, len, mainTimeoutMs);
}

size_t Stream::sendGeneric(Print* to, const ssize_t len, const int readUntilChar,
const esp8266::polledTimeout::oneShotFastMs::timeType timeoutMs)
{
// "neverExpires (default, impossible)" is translated to default timeout
esp8266::polledTimeout::oneShotFastMs::timeType inputTimeoutMs
= timeoutMs >= esp8266::polledTimeout::oneShotFastMs::neverExpires ? getTimeout()
: timeoutMs;

setReport(Report::Success);

if (len == 0)
Expand All @@ -43,25 +88,23 @@ size_t Stream::sendGeneric(Print* to, const ssize_t len, const int readUntilChar

if (hasPeekBufferAPI())
{
return SendGenericPeekBuffer(to, len, readUntilChar, timeoutMs);
return SendGenericPeekBuffer(to, len, readUntilChar, inputTimeoutMs);
}

if (readUntilChar >= 0)
{
return SendGenericRegularUntil(to, len, readUntilChar, timeoutMs);
return SendGenericRegularUntil(to, len, readUntilChar, inputTimeoutMs);
}

return SendGenericRegular(to, len, timeoutMs);
return SendGenericRegular(to, len, inputTimeoutMs);
}

size_t
Stream::SendGenericPeekBuffer(Print* to, const ssize_t len, const int readUntilChar,
const esp8266::polledTimeout::oneShotFastMs::timeType timeoutMs)
{
// "neverExpires (default, impossible)" is translated to default timeout
esp8266::polledTimeout::oneShotFastMs timedOut(
timeoutMs >= esp8266::polledTimeout::oneShotFastMs::neverExpires ? getTimeout()
: timeoutMs);
esp8266::polledTimeout::oneShotFastMs timedOut(timeoutMs);

// len==-1 => maxLen=0 <=> until starvation
const size_t maxLen = std::max((ssize_t)0, len);
size_t written = 0;
Expand Down Expand Up @@ -152,10 +195,8 @@ Stream::SendGenericRegularUntil(Print* to, const ssize_t len, const int readUnti
// regular Stream API
// no other choice than reading byte by byte

// "neverExpires (default, impossible)" is translated to default timeout
esp8266::polledTimeout::oneShotFastMs timedOut(
timeoutMs >= esp8266::polledTimeout::oneShotFastMs::neverExpires ? getTimeout()
: timeoutMs);
esp8266::polledTimeout::oneShotFastMs timedOut(timeoutMs);

// len==-1 => maxLen=0 <=> until starvation
const size_t maxLen = std::max((ssize_t)0, len);
size_t written = 0;
Expand Down Expand Up @@ -231,10 +272,8 @@ size_t Stream::SendGenericRegular(Print* to, const ssize_t len,
// regular Stream API
// use an intermediary buffer

// "neverExpires (default, impossible)" is translated to default timeout
esp8266::polledTimeout::oneShotFastMs timedOut(
timeoutMs >= esp8266::polledTimeout::oneShotFastMs::neverExpires ? getTimeout()
: timeoutMs);
esp8266::polledTimeout::oneShotFastMs timedOut(timeoutMs);

// len==-1 => maxLen=0 <=> until starvation
const size_t maxLen = std::max((ssize_t)0, len);
size_t written = 0;
Expand Down
101 changes: 1 addition & 100 deletions libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ static_assert(std::is_move_assignable_v<HTTPClient>, "");
static const char defaultUserAgentPstr[] PROGMEM = "ESP8266HTTPClient";
const String HTTPClient::defaultUserAgent = defaultUserAgentPstr;

static int StreamReportToHttpClientReport (Stream::Report streamSendError)
int HTTPClient::StreamReportToHttpClientReport (Stream::Report streamSendError)
{
switch (streamSendError)
{
Expand Down Expand Up @@ -627,105 +627,6 @@ WiFiClient* HTTPClient::getStreamPtr(void)
return nullptr;
}

/**
* write all message body / payload to Stream
* @param stream Stream *
* @return bytes written ( negative values are error codes )
*/
int HTTPClient::writeToStream(Stream * stream)
{
return writeToPrint(stream);
}

/**
* write all message body / payload to Print
* @param print Print *
* @return bytes written ( negative values are error codes )
*/
int HTTPClient::writeToPrint(Print * print)
{

if(!print) {
return returnError(HTTPC_ERROR_NO_STREAM);
}

// Only return error if not connected and no data available, because otherwise ::getString() will return an error instead of an empty
// string when the server returned a http code 204 (no content)
if(!connected() && _transferEncoding != HTTPC_TE_IDENTITY && _size > 0) {
return returnError(HTTPC_ERROR_NOT_CONNECTED);
}

// get length of document (is -1 when Server sends no Content-Length header)
int len = _size;
int ret = 0;

if(_transferEncoding == HTTPC_TE_IDENTITY) {
// len < 0: transfer all of it, with timeout
// len >= 0: max:len, with timeout
ret = _client->sendSize(print, len);

// do we have an error?
if(_client->getLastSendReport() != Stream::Report::Success) {
return returnError(StreamReportToHttpClientReport(_client->getLastSendReport()));
}
} else if(_transferEncoding == HTTPC_TE_CHUNKED) {
int size = 0;
while(1) {
if(!connected()) {
return returnError(HTTPC_ERROR_CONNECTION_LOST);
}
String chunkHeader = _client->readStringUntil('\n');

if(chunkHeader.length() <= 0) {
return returnError(HTTPC_ERROR_READ_TIMEOUT);
}

chunkHeader.trim(); // remove \r

// read size of chunk
len = (uint32_t) strtol((const char *) chunkHeader.c_str(), NULL, 16);
size += len;
DEBUG_HTTPCLIENT("[HTTP-Client] read chunk len: %d\n", len);

// data left?
if(len > 0) {
// read len bytes with timeout
int r = _client->sendSize(print, len);
if (_client->getLastSendReport() != Stream::Report::Success)
// not all data transferred
return returnError(StreamReportToHttpClientReport(_client->getLastSendReport()));
ret += r;
} else {

// if no length Header use global chunk size
if(_size <= 0) {
_size = size;
}

// check if we have write all data out
if(ret != _size) {
return returnError(HTTPC_ERROR_STREAM_WRITE);
}
break;
}

// read trailing \r\n at the end of the chunk
char buf[2];
auto trailing_seq_len = _client->readBytes((uint8_t*)buf, 2);
if (trailing_seq_len != 2 || buf[0] != '\r' || buf[1] != '\n') {
return returnError(HTTPC_ERROR_READ_TIMEOUT);
}

esp_yield();
}
} else {
return returnError(HTTPC_ERROR_ENCODING);
}

disconnect(true);
return ret;
}

/**
* return all payload as String (may need lot of ram or trigger out of memory!)
* @return String
Expand Down

0 comments on commit a5d31a7

Please sign in to comment.