Skip to content

deprecate and update Stream::send*(Print -> Stream) #8874

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading