mirror of
https://github.com/esp8266/Arduino.git
synced 2025-06-06 05:21:22 +03:00
fixes for WiFiClient::write(Stream) (#7987)
fixes for WiFiClient::write(Stream) and Stream transfers - remove deprecated WiFiClient::write(Stream,size) - fix and deprecate WiFiClient::write(Stream) to use Stream::sendAll instead of ::sendAvailable - update ESP8266WebServer::streamFile to use file.sendAll(client) instead of client.write(file) - remove stream dependence in ClientContext - Stream::send(): honor timeout in all case, avoid short transfer when output is temporarily full - example WiFiEcho: show sendAll and sendAvailable
This commit is contained in:
parent
457692101a
commit
f4178e58dc
@ -104,10 +104,7 @@ size_t Stream::SendGenericPeekBuffer(Print* to, const ssize_t len, const int rea
|
|||||||
{
|
{
|
||||||
peekConsume(w);
|
peekConsume(w);
|
||||||
written += w;
|
written += w;
|
||||||
if (maxLen)
|
timedOut.reset(); // something has been written
|
||||||
{
|
|
||||||
timedOut.reset();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (foundChar)
|
if (foundChar)
|
||||||
{
|
{
|
||||||
@ -116,12 +113,6 @@ size_t Stream::SendGenericPeekBuffer(Print* to, const ssize_t len, const int rea
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!w && !maxLen && readUntilChar < 0)
|
|
||||||
{
|
|
||||||
// nothing has been transferred and no specific condition is requested
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (timedOut)
|
if (timedOut)
|
||||||
{
|
{
|
||||||
// either (maxLen>0) nothing has been transferred for too long
|
// either (maxLen>0) nothing has been transferred for too long
|
||||||
@ -195,16 +186,7 @@ size_t Stream::SendGenericRegularUntil(Print* to, const ssize_t len, const int r
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
written += 1;
|
written += 1;
|
||||||
if (maxLen)
|
timedOut.reset(); // something has been written
|
||||||
{
|
|
||||||
timedOut.reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!w && !maxLen && readUntilChar < 0)
|
|
||||||
{
|
|
||||||
// nothing has been transferred and no specific condition is requested
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timedOut)
|
if (timedOut)
|
||||||
@ -288,16 +270,7 @@ size_t Stream::SendGenericRegular(Print* to, const ssize_t len, const esp8266::p
|
|||||||
setReport(Report::WriteError);
|
setReport(Report::WriteError);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (maxLen && w)
|
timedOut.reset(); // something has been written
|
||||||
{
|
|
||||||
timedOut.reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!w && !maxLen)
|
|
||||||
{
|
|
||||||
// nothing has been transferred and no specific condition is requested
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timedOut)
|
if (timedOut)
|
||||||
|
@ -226,7 +226,7 @@ public:
|
|||||||
size_t contentLength = 0;
|
size_t contentLength = 0;
|
||||||
_streamFileCore(file.size(), file.name(), contentType);
|
_streamFileCore(file.size(), file.name(), contentType);
|
||||||
if (requestMethod == HTTP_GET) {
|
if (requestMethod == HTTP_GET) {
|
||||||
contentLength = _currentClient.write(file);
|
contentLength = file.sendAll(_currentClient);
|
||||||
}
|
}
|
||||||
return contentLength;
|
return contentLength;
|
||||||
}
|
}
|
||||||
|
@ -84,9 +84,10 @@ void loop() {
|
|||||||
if (Serial.available()) {
|
if (Serial.available()) {
|
||||||
s = (s + 1) % (sizeof(sizes) / sizeof(sizes[0]));
|
s = (s + 1) % (sizeof(sizes) / sizeof(sizes[0]));
|
||||||
switch (Serial.read()) {
|
switch (Serial.read()) {
|
||||||
case '1': if (t != 1) s = 0; t = 1; Serial.println("byte-by-byte (watch then press 2 or 3)"); break;
|
case '1': if (t != 1) s = 0; t = 1; Serial.println("byte-by-byte (watch then press 2, 3 or 4)"); break;
|
||||||
case '2': if (t != 2) s = 1; t = 2; Serial.printf("through buffer (watch then press 2 again, or 1 or 3)\n"); break;
|
case '2': if (t != 2) s = 1; t = 2; Serial.printf("through buffer (watch then press 2 again, or 1, 3 or 4)\n"); break;
|
||||||
case '3': if (t != 3) s = 0; t = 3; Serial.printf("direct access (watch then press 3 again, or 1 or 2)\n"); break;
|
case '3': if (t != 3) s = 0; t = 3; Serial.printf("direct access (sendAvailable - watch then press 3 again, or 1, 2 or 4)\n"); break;
|
||||||
|
case '4': t = 4; Serial.printf("direct access (sendAll - close peer to stop, then press 1, 2 or 3 before restarting peer)\n"); break;
|
||||||
}
|
}
|
||||||
tot = cnt = 0;
|
tot = cnt = 0;
|
||||||
ESP.resetFreeContStack();
|
ESP.resetFreeContStack();
|
||||||
@ -125,7 +126,7 @@ void loop() {
|
|||||||
if (sizes[s]) {
|
if (sizes[s]) {
|
||||||
tot += client.sendSize(&client, sizes[s]);
|
tot += client.sendSize(&client, sizes[s]);
|
||||||
} else {
|
} else {
|
||||||
tot += client.sendAll(&client);
|
tot += client.sendAvailable(&client);
|
||||||
}
|
}
|
||||||
cnt++;
|
cnt++;
|
||||||
|
|
||||||
@ -138,4 +139,18 @@ void loop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else if (t == 4) {
|
||||||
|
// stream to print, possibly with only one copy
|
||||||
|
tot += client.sendAll(&client); // this one might not exit until peer close
|
||||||
|
cnt++;
|
||||||
|
|
||||||
|
switch (client.getLastSendReport()) {
|
||||||
|
case Stream::Report::Success: break;
|
||||||
|
case Stream::Report::TimedOut: Serial.println("Stream::send: timeout"); break;
|
||||||
|
case Stream::Report::ReadError: Serial.println("Stream::send: read error"); break;
|
||||||
|
case Stream::Report::WriteError: Serial.println("Stream::send: write error"); break;
|
||||||
|
case Stream::Report::ShortOperation: Serial.println("Stream::send: short transfer"); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -213,28 +213,19 @@ size_t WiFiClient::write(const uint8_t *buf, size_t size)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
_client->setTimeout(_timeout);
|
_client->setTimeout(_timeout);
|
||||||
StreamConstPtr ptr(buf, size);
|
return _client->write((const char*)buf, size);
|
||||||
return _client->write(ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t WiFiClient::write(Stream& stream, size_t unused)
|
|
||||||
{
|
|
||||||
(void) unused;
|
|
||||||
return WiFiClient::write(stream);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t WiFiClient::write(Stream& stream)
|
size_t WiFiClient::write(Stream& stream)
|
||||||
{
|
{
|
||||||
|
// (this method is deprecated)
|
||||||
|
|
||||||
if (!_client || !stream.available())
|
if (!_client || !stream.available())
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (stream.hasPeekBufferAPI())
|
// core up to 2.7.4 was equivalent to this
|
||||||
{
|
return stream.sendAll(this);
|
||||||
_client->setTimeout(_timeout);
|
|
||||||
return _client->write(stream);
|
|
||||||
}
|
|
||||||
return stream.sendAvailable(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t WiFiClient::write_P(PGM_P buf, size_t size)
|
size_t WiFiClient::write_P(PGM_P buf, size_t size)
|
||||||
|
@ -59,10 +59,7 @@ public:
|
|||||||
virtual size_t write(uint8_t) override;
|
virtual size_t write(uint8_t) override;
|
||||||
virtual size_t write(const uint8_t *buf, size_t size) override;
|
virtual size_t write(const uint8_t *buf, size_t size) override;
|
||||||
virtual size_t write_P(PGM_P buf, size_t size);
|
virtual size_t write_P(PGM_P buf, size_t size);
|
||||||
size_t write(Stream& stream);
|
size_t write(Stream& stream) [[ deprecated("use stream.sendHow(client...)") ]];
|
||||||
|
|
||||||
// This one is deprecated, use write(Stream& instead)
|
|
||||||
size_t write(Stream& stream, size_t unitSize) __attribute__ ((deprecated));
|
|
||||||
|
|
||||||
virtual int available() override;
|
virtual int available() override;
|
||||||
virtual int read() override;
|
virtual int read() override;
|
||||||
|
@ -30,7 +30,6 @@ extern "C" void esp_yield();
|
|||||||
extern "C" void esp_schedule();
|
extern "C" void esp_schedule();
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <StreamDev.h>
|
|
||||||
#include <esp_priv.h>
|
#include <esp_priv.h>
|
||||||
|
|
||||||
bool getDefaultPrivateGlobalSyncValue ();
|
bool getDefaultPrivateGlobalSyncValue ();
|
||||||
@ -376,13 +375,12 @@ public:
|
|||||||
return _pcb->state;
|
return _pcb->state;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t write(Stream& stream)
|
size_t write(const char* ds, const size_t dl)
|
||||||
{
|
{
|
||||||
if (!_pcb) {
|
if (!_pcb) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
assert(stream.hasPeekBufferAPI());
|
return _write_from_source(ds, dl);
|
||||||
return _write_from_source(&stream);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void keepAlive (uint16_t idle_sec = TCP_DEFAULT_KEEPALIVE_IDLE_SEC, uint16_t intv_sec = TCP_DEFAULT_KEEPALIVE_INTERVAL_SEC, uint8_t count = TCP_DEFAULT_KEEPALIVE_COUNT)
|
void keepAlive (uint16_t idle_sec = TCP_DEFAULT_KEEPALIVE_IDLE_SEC, uint16_t intv_sec = TCP_DEFAULT_KEEPALIVE_INTERVAL_SEC, uint8_t count = TCP_DEFAULT_KEEPALIVE_COUNT)
|
||||||
@ -466,11 +464,12 @@ protected:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t _write_from_source(Stream* ds)
|
size_t _write_from_source(const char* ds, const size_t dl)
|
||||||
{
|
{
|
||||||
assert(_datasource == nullptr);
|
assert(_datasource == nullptr);
|
||||||
assert(!_send_waiting);
|
assert(!_send_waiting);
|
||||||
_datasource = ds;
|
_datasource = ds;
|
||||||
|
_datalen = dl;
|
||||||
_written = 0;
|
_written = 0;
|
||||||
_op_start_time = millis();
|
_op_start_time = millis();
|
||||||
do {
|
do {
|
||||||
@ -478,11 +477,12 @@ protected:
|
|||||||
_op_start_time = millis();
|
_op_start_time = millis();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_datasource->available() || _is_timeout() || state() == CLOSED) {
|
if (_written == _datalen || _is_timeout() || state() == CLOSED) {
|
||||||
if (_is_timeout()) {
|
if (_is_timeout()) {
|
||||||
DEBUGV(":wtmo\r\n");
|
DEBUGV(":wtmo\r\n");
|
||||||
}
|
}
|
||||||
_datasource = nullptr;
|
_datasource = nullptr;
|
||||||
|
_datalen = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -507,20 +507,21 @@ protected:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUGV(":wr %d %d\r\n", _datasource->peekAvailable(), _written);
|
DEBUGV(":wr %d %d\r\n", _datalen - _written, _written);
|
||||||
|
|
||||||
bool has_written = false;
|
bool has_written = false;
|
||||||
|
|
||||||
while (_datasource) {
|
while (_written < _datalen) {
|
||||||
if (state() == CLOSED)
|
if (state() == CLOSED)
|
||||||
return false;
|
return false;
|
||||||
size_t next_chunk_size = std::min((size_t)tcp_sndbuf(_pcb), _datasource->peekAvailable());
|
const auto remaining = _datalen - _written;
|
||||||
|
size_t next_chunk_size = std::min((size_t)tcp_sndbuf(_pcb), remaining);
|
||||||
if (!next_chunk_size)
|
if (!next_chunk_size)
|
||||||
break;
|
break;
|
||||||
const char* buf = _datasource->peekBuffer();
|
const char* buf = _datasource + _written;
|
||||||
|
|
||||||
uint8_t flags = 0;
|
uint8_t flags = 0;
|
||||||
if (next_chunk_size < _datasource->peekAvailable())
|
if (next_chunk_size < remaining)
|
||||||
// PUSH is meant for peer, telling to give data to user app as soon as received
|
// PUSH is meant for peer, telling to give data to user app as soon as received
|
||||||
// PUSH "may be set" when sender has finished sending a "meaningful" data block
|
// PUSH "may be set" when sender has finished sending a "meaningful" data block
|
||||||
// PUSH does not break Nagle
|
// PUSH does not break Nagle
|
||||||
@ -534,10 +535,9 @@ protected:
|
|||||||
|
|
||||||
err_t err = tcp_write(_pcb, buf, next_chunk_size, flags);
|
err_t err = tcp_write(_pcb, buf, next_chunk_size, flags);
|
||||||
|
|
||||||
DEBUGV(":wrc %d %d %d\r\n", next_chunk_size, _datasource->peekAvailable(), (int)err);
|
DEBUGV(":wrc %d %d %d\r\n", next_chunk_size, remaining, (int)err);
|
||||||
|
|
||||||
if (err == ERR_OK) {
|
if (err == ERR_OK) {
|
||||||
_datasource->peekConsume(next_chunk_size);
|
|
||||||
_written += next_chunk_size;
|
_written += next_chunk_size;
|
||||||
has_written = true;
|
has_written = true;
|
||||||
} else {
|
} else {
|
||||||
@ -695,7 +695,8 @@ private:
|
|||||||
discard_cb_t _discard_cb;
|
discard_cb_t _discard_cb;
|
||||||
void* _discard_cb_arg;
|
void* _discard_cb_arg;
|
||||||
|
|
||||||
Stream* _datasource = nullptr;
|
const char* _datasource = nullptr;
|
||||||
|
size_t _datalen = 0;
|
||||||
size_t _written = 0;
|
size_t _written = 0;
|
||||||
uint32_t _timeout_ms = 5000;
|
uint32_t _timeout_ms = 5000;
|
||||||
uint32_t _op_start_time = 0;
|
uint32_t _op_start_time = 0;
|
||||||
|
@ -223,9 +223,9 @@ public:
|
|||||||
return _sock >= 0? ESTABLISHED: CLOSED;
|
return _sock >= 0? ESTABLISHED: CLOSED;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t write(const uint8_t* data, size_t size)
|
size_t write(const char* data, size_t size)
|
||||||
{
|
{
|
||||||
ssize_t ret = mockWrite(_sock, data, size, _timeout_ms);
|
ssize_t ret = mockWrite(_sock, (const uint8_t*)data, size, _timeout_ms);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
abort();
|
abort();
|
||||||
@ -234,28 +234,6 @@ public:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t write(Stream& stream)
|
|
||||||
{
|
|
||||||
size_t avail = stream.available();
|
|
||||||
uint8_t buf [avail];
|
|
||||||
avail = stream.readBytes(buf, avail);
|
|
||||||
size_t totwrote = 0;
|
|
||||||
uint8_t* w = buf;
|
|
||||||
while (avail && _sock >= 0)
|
|
||||||
{
|
|
||||||
size_t wrote = write(w, avail);
|
|
||||||
w += wrote;
|
|
||||||
avail -= wrote;
|
|
||||||
totwrote += wrote;
|
|
||||||
}
|
|
||||||
return totwrote;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t write_P(PGM_P buf, size_t size)
|
|
||||||
{
|
|
||||||
return write((const uint8_t*)buf, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void keepAlive (uint16_t idle_sec = TCP_DEFAULT_KEEPALIVE_IDLE_SEC, uint16_t intv_sec = TCP_DEFAULT_KEEPALIVE_INTERVAL_SEC, uint8_t count = TCP_DEFAULT_KEEPALIVE_COUNT)
|
void keepAlive (uint16_t idle_sec = TCP_DEFAULT_KEEPALIVE_IDLE_SEC, uint16_t intv_sec = TCP_DEFAULT_KEEPALIVE_INTERVAL_SEC, uint8_t count = TCP_DEFAULT_KEEPALIVE_COUNT)
|
||||||
{
|
{
|
||||||
(void) idle_sec;
|
(void) idle_sec;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user