mirror of
https://github.com/esp8266/Arduino.git
synced 2025-04-19 23:22:16 +03:00
deprecate and update Stream::send*(Print -> Stream)
(#8874)
* deprecate and update Stream::send(Print -> Stream) in order to benefit from and use output's timeout value
This commit is contained in:
parent
01d1c8e46f
commit
a5d31a7187
@ -167,25 +167,49 @@ class Stream: public Print {
|
|||||||
// When result is 0 or less than requested maxLen, Print::getLastSend()
|
// When result is 0 or less than requested maxLen, Print::getLastSend()
|
||||||
// contains an error reason.
|
// contains an error reason.
|
||||||
|
|
||||||
|
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||||
|
|
||||||
// transfers already buffered / immediately available data (no timeout)
|
// transfers already buffered / immediately available data (no timeout)
|
||||||
// returns number of transferred bytes
|
// returns number of transferred bytes
|
||||||
size_t sendAvailable (Print* to) { return sendGeneric(to, -1, -1, oneShotMs::alwaysExpired); }
|
[[deprecated]] size_t sendAvailable (Print* to) { return sendGeneric(to, -1, -1, oneShotMs::alwaysExpired); }
|
||||||
size_t sendAvailable (Print& to) { return sendAvailable(&to); }
|
[[deprecated]] size_t sendAvailable (Print& to) { return sendAvailable(&to); }
|
||||||
|
|
||||||
// transfers data until timeout
|
// transfers data until timeout
|
||||||
// returns number of transferred bytes
|
// returns number of transferred bytes
|
||||||
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 sendGeneric(to, -1, -1, timeoutMs); }
|
||||||
size_t sendAll (Print& to, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendAll(&to, 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
|
// transfers data until a char is encountered (the char is swallowed but not transferred) with timeout
|
||||||
// returns number of transferred bytes
|
// 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); }
|
[[deprecated]] 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); }
|
[[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
|
// transfers data until requested size or timeout
|
||||||
// returns number of transferred bytes
|
// 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); }
|
[[deprecated]] 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); }
|
[[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 (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 (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 (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 (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)
|
// remaining size (-1 by default = unknown)
|
||||||
virtual ssize_t streamRemaining () { return -1; }
|
virtual ssize_t streamRemaining () { return -1; }
|
||||||
@ -202,11 +226,17 @@ class Stream: public Print {
|
|||||||
Report getLastSendReport () const { return _sendReport; }
|
Report getLastSendReport () const { return _sendReport; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
[[deprecated]]
|
||||||
size_t sendGeneric (Print* to,
|
size_t sendGeneric (Print* to,
|
||||||
const ssize_t len = -1,
|
const ssize_t len = -1,
|
||||||
const int readUntilChar = -1,
|
const int readUntilChar = -1,
|
||||||
oneShotMs::timeType timeoutMs = oneShotMs::neverExpires /* neverExpires=>getTimeout() */);
|
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 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 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);
|
size_t SendGenericRegular(Print* to, const ssize_t len, const oneShotMs::timeType timeoutMs);
|
||||||
|
@ -22,9 +22,17 @@
|
|||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <StreamDev.h>
|
#include <StreamDev.h>
|
||||||
|
|
||||||
size_t Stream::sendGeneric(Print* to, const ssize_t len, const int readUntilChar,
|
size_t Stream::sendGeneric(Stream* to, const ssize_t len, const int readUntilChar,
|
||||||
const esp8266::polledTimeout::oneShotFastMs::timeType timeoutMs)
|
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);
|
setReport(Report::Success);
|
||||||
|
|
||||||
if (len == 0)
|
if (len == 0)
|
||||||
@ -43,25 +51,60 @@ size_t Stream::sendGeneric(Print* to, const ssize_t len, const int readUntilChar
|
|||||||
|
|
||||||
if (hasPeekBufferAPI())
|
if (hasPeekBufferAPI())
|
||||||
{
|
{
|
||||||
return SendGenericPeekBuffer(to, len, readUntilChar, timeoutMs);
|
return SendGenericPeekBuffer(to, len, readUntilChar, mainTimeoutMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (readUntilChar >= 0)
|
if (readUntilChar >= 0)
|
||||||
{
|
{
|
||||||
return SendGenericRegularUntil(to, len, readUntilChar, timeoutMs);
|
return SendGenericRegularUntil(to, len, readUntilChar, mainTimeoutMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
return SendGenericRegular(to, len, timeoutMs);
|
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)
|
||||||
|
{
|
||||||
|
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, inputTimeoutMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (readUntilChar >= 0)
|
||||||
|
{
|
||||||
|
return SendGenericRegularUntil(to, len, readUntilChar, inputTimeoutMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
return SendGenericRegular(to, len, inputTimeoutMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
Stream::SendGenericPeekBuffer(Print* to, const ssize_t len, const int readUntilChar,
|
Stream::SendGenericPeekBuffer(Print* to, const ssize_t len, const int readUntilChar,
|
||||||
const esp8266::polledTimeout::oneShotFastMs::timeType timeoutMs)
|
const esp8266::polledTimeout::oneShotFastMs::timeType timeoutMs)
|
||||||
{
|
{
|
||||||
// "neverExpires (default, impossible)" is translated to default timeout
|
esp8266::polledTimeout::oneShotFastMs timedOut(timeoutMs);
|
||||||
esp8266::polledTimeout::oneShotFastMs timedOut(
|
|
||||||
timeoutMs >= esp8266::polledTimeout::oneShotFastMs::neverExpires ? getTimeout()
|
|
||||||
: timeoutMs);
|
|
||||||
// len==-1 => maxLen=0 <=> until starvation
|
// len==-1 => maxLen=0 <=> until starvation
|
||||||
const size_t maxLen = std::max((ssize_t)0, len);
|
const size_t maxLen = std::max((ssize_t)0, len);
|
||||||
size_t written = 0;
|
size_t written = 0;
|
||||||
@ -152,10 +195,8 @@ Stream::SendGenericRegularUntil(Print* to, const ssize_t len, const int readUnti
|
|||||||
// regular Stream API
|
// regular Stream API
|
||||||
// no other choice than reading byte by byte
|
// no other choice than reading byte by byte
|
||||||
|
|
||||||
// "neverExpires (default, impossible)" is translated to default timeout
|
esp8266::polledTimeout::oneShotFastMs timedOut(timeoutMs);
|
||||||
esp8266::polledTimeout::oneShotFastMs timedOut(
|
|
||||||
timeoutMs >= esp8266::polledTimeout::oneShotFastMs::neverExpires ? getTimeout()
|
|
||||||
: timeoutMs);
|
|
||||||
// len==-1 => maxLen=0 <=> until starvation
|
// len==-1 => maxLen=0 <=> until starvation
|
||||||
const size_t maxLen = std::max((ssize_t)0, len);
|
const size_t maxLen = std::max((ssize_t)0, len);
|
||||||
size_t written = 0;
|
size_t written = 0;
|
||||||
@ -231,10 +272,8 @@ size_t Stream::SendGenericRegular(Print* to, const ssize_t len,
|
|||||||
// regular Stream API
|
// regular Stream API
|
||||||
// use an intermediary buffer
|
// use an intermediary buffer
|
||||||
|
|
||||||
// "neverExpires (default, impossible)" is translated to default timeout
|
esp8266::polledTimeout::oneShotFastMs timedOut(timeoutMs);
|
||||||
esp8266::polledTimeout::oneShotFastMs timedOut(
|
|
||||||
timeoutMs >= esp8266::polledTimeout::oneShotFastMs::neverExpires ? getTimeout()
|
|
||||||
: timeoutMs);
|
|
||||||
// len==-1 => maxLen=0 <=> until starvation
|
// len==-1 => maxLen=0 <=> until starvation
|
||||||
const size_t maxLen = std::max((ssize_t)0, len);
|
const size_t maxLen = std::max((ssize_t)0, len);
|
||||||
size_t written = 0;
|
size_t written = 0;
|
||||||
|
@ -39,7 +39,7 @@ static_assert(std::is_move_assignable_v<HTTPClient>, "");
|
|||||||
static const char defaultUserAgentPstr[] PROGMEM = "ESP8266HTTPClient";
|
static const char defaultUserAgentPstr[] PROGMEM = "ESP8266HTTPClient";
|
||||||
const String HTTPClient::defaultUserAgent = defaultUserAgentPstr;
|
const String HTTPClient::defaultUserAgent = defaultUserAgentPstr;
|
||||||
|
|
||||||
static int StreamReportToHttpClientReport (Stream::Report streamSendError)
|
int HTTPClient::StreamReportToHttpClientReport (Stream::Report streamSendError)
|
||||||
{
|
{
|
||||||
switch (streamSendError)
|
switch (streamSendError)
|
||||||
{
|
{
|
||||||
@ -627,105 +627,6 @@ WiFiClient* HTTPClient::getStreamPtr(void)
|
|||||||
return nullptr;
|
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 all payload as String (may need lot of ram or trigger out of memory!)
|
||||||
* @return String
|
* @return String
|
||||||
|
@ -215,8 +215,8 @@ public:
|
|||||||
|
|
||||||
WiFiClient& getStream(void);
|
WiFiClient& getStream(void);
|
||||||
WiFiClient* getStreamPtr(void);
|
WiFiClient* getStreamPtr(void);
|
||||||
int writeToPrint(Print* print);
|
template <typename S> int writeToPrint(S* print) [[deprecated]] { return writeToStream(print); }
|
||||||
int writeToStream(Stream* stream);
|
template <typename S> int writeToStream(S* output);
|
||||||
const String& getString(void);
|
const String& getString(void);
|
||||||
static String errorToString(int error);
|
static String errorToString(int error);
|
||||||
|
|
||||||
@ -234,6 +234,7 @@ protected:
|
|||||||
bool sendHeader(const char * type);
|
bool sendHeader(const char * type);
|
||||||
int handleHeaderResponse();
|
int handleHeaderResponse();
|
||||||
int writeToStreamDataBlock(Stream * stream, int len);
|
int writeToStreamDataBlock(Stream * stream, int len);
|
||||||
|
static int StreamReportToHttpClientReport (Stream::Report streamSendError);
|
||||||
|
|
||||||
// The common pattern to use the class is to
|
// The common pattern to use the class is to
|
||||||
// {
|
// {
|
||||||
@ -274,4 +275,93 @@ protected:
|
|||||||
std::unique_ptr<StreamString> _payload;
|
std::unique_ptr<StreamString> _payload;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* write all message body / payload to Stream
|
||||||
|
* @param output Print*(obsolete) / Stream*
|
||||||
|
* @return bytes written ( negative values are error codes )
|
||||||
|
*/
|
||||||
|
template <typename S>
|
||||||
|
int HTTPClient::writeToStream(S * output)
|
||||||
|
{
|
||||||
|
if(!output) {
|
||||||
|
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(output, 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(output, 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;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* ESP8266HTTPClient_H_ */
|
#endif /* ESP8266HTTPClient_H_ */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user