Skip to content

Commit f4178e5

Browse files
authored
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
1 parent 4576921 commit f4178e5

File tree

7 files changed

+46
-91
lines changed

7 files changed

+46
-91
lines changed

cores/esp8266/StreamSend.cpp

+3-30
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,7 @@ size_t Stream::SendGenericPeekBuffer(Print* to, const ssize_t len, const int rea
104104
{
105105
peekConsume(w);
106106
written += w;
107-
if (maxLen)
108-
{
109-
timedOut.reset();
110-
}
107+
timedOut.reset(); // something has been written
111108
}
112109
if (foundChar)
113110
{
@@ -116,12 +113,6 @@ size_t Stream::SendGenericPeekBuffer(Print* to, const ssize_t len, const int rea
116113
}
117114
}
118115

119-
if (!w && !maxLen && readUntilChar < 0)
120-
{
121-
// nothing has been transferred and no specific condition is requested
122-
break;
123-
}
124-
125116
if (timedOut)
126117
{
127118
// 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
195186
break;
196187
}
197188
written += 1;
198-
if (maxLen)
199-
{
200-
timedOut.reset();
201-
}
202-
}
203-
204-
if (!w && !maxLen && readUntilChar < 0)
205-
{
206-
// nothing has been transferred and no specific condition is requested
207-
break;
189+
timedOut.reset(); // something has been written
208190
}
209191

210192
if (timedOut)
@@ -288,16 +270,7 @@ size_t Stream::SendGenericRegular(Print* to, const ssize_t len, const esp8266::p
288270
setReport(Report::WriteError);
289271
break;
290272
}
291-
if (maxLen && w)
292-
{
293-
timedOut.reset();
294-
}
295-
}
296-
297-
if (!w && !maxLen)
298-
{
299-
// nothing has been transferred and no specific condition is requested
300-
break;
273+
timedOut.reset(); // something has been written
301274
}
302275

303276
if (timedOut)

libraries/ESP8266WebServer/src/ESP8266WebServer.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ class ESP8266WebServerTemplate
226226
size_t contentLength = 0;
227227
_streamFileCore(file.size(), file.name(), contentType);
228228
if (requestMethod == HTTP_GET) {
229-
contentLength = _currentClient.write(file);
229+
contentLength = file.sendAll(_currentClient);
230230
}
231231
return contentLength;
232232
}

libraries/ESP8266WiFi/examples/WiFiEcho/WiFiEcho.ino

+19-4
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,10 @@ void loop() {
8484
if (Serial.available()) {
8585
s = (s + 1) % (sizeof(sizes) / sizeof(sizes[0]));
8686
switch (Serial.read()) {
87-
case '1': if (t != 1) s = 0; t = 1; Serial.println("byte-by-byte (watch then press 2 or 3)"); break;
88-
case '2': if (t != 2) s = 1; t = 2; Serial.printf("through buffer (watch then press 2 again, or 1 or 3)\n"); break;
89-
case '3': if (t != 3) s = 0; t = 3; Serial.printf("direct access (watch then press 3 again, or 1 or 2)\n"); break;
87+
case '1': if (t != 1) s = 0; t = 1; Serial.println("byte-by-byte (watch then press 2, 3 or 4)"); break;
88+
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;
89+
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;
90+
case '4': t = 4; Serial.printf("direct access (sendAll - close peer to stop, then press 1, 2 or 3 before restarting peer)\n"); break;
9091
}
9192
tot = cnt = 0;
9293
ESP.resetFreeContStack();
@@ -125,7 +126,7 @@ void loop() {
125126
if (sizes[s]) {
126127
tot += client.sendSize(&client, sizes[s]);
127128
} else {
128-
tot += client.sendAll(&client);
129+
tot += client.sendAvailable(&client);
129130
}
130131
cnt++;
131132

@@ -138,4 +139,18 @@ void loop() {
138139
}
139140
}
140141

142+
else if (t == 4) {
143+
// stream to print, possibly with only one copy
144+
tot += client.sendAll(&client); // this one might not exit until peer close
145+
cnt++;
146+
147+
switch (client.getLastSendReport()) {
148+
case Stream::Report::Success: break;
149+
case Stream::Report::TimedOut: Serial.println("Stream::send: timeout"); break;
150+
case Stream::Report::ReadError: Serial.println("Stream::send: read error"); break;
151+
case Stream::Report::WriteError: Serial.println("Stream::send: write error"); break;
152+
case Stream::Report::ShortOperation: Serial.println("Stream::send: short transfer"); break;
153+
}
154+
}
155+
141156
}

libraries/ESP8266WiFi/src/WiFiClient.cpp

+5-14
Original file line numberDiff line numberDiff line change
@@ -213,28 +213,19 @@ size_t WiFiClient::write(const uint8_t *buf, size_t size)
213213
return 0;
214214
}
215215
_client->setTimeout(_timeout);
216-
StreamConstPtr ptr(buf, size);
217-
return _client->write(ptr);
218-
}
219-
220-
size_t WiFiClient::write(Stream& stream, size_t unused)
221-
{
222-
(void) unused;
223-
return WiFiClient::write(stream);
216+
return _client->write((const char*)buf, size);
224217
}
225218

226219
size_t WiFiClient::write(Stream& stream)
227220
{
221+
// (this method is deprecated)
222+
228223
if (!_client || !stream.available())
229224
{
230225
return 0;
231226
}
232-
if (stream.hasPeekBufferAPI())
233-
{
234-
_client->setTimeout(_timeout);
235-
return _client->write(stream);
236-
}
237-
return stream.sendAvailable(this);
227+
// core up to 2.7.4 was equivalent to this
228+
return stream.sendAll(this);
238229
}
239230

240231
size_t WiFiClient::write_P(PGM_P buf, size_t size)

libraries/ESP8266WiFi/src/WiFiClient.h

+1-4
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,7 @@ class WiFiClient : public Client, public SList<WiFiClient> {
5959
virtual size_t write(uint8_t) override;
6060
virtual size_t write(const uint8_t *buf, size_t size) override;
6161
virtual size_t write_P(PGM_P buf, size_t size);
62-
size_t write(Stream& stream);
63-
64-
// This one is deprecated, use write(Stream& instead)
65-
size_t write(Stream& stream, size_t unitSize) __attribute__ ((deprecated));
62+
size_t write(Stream& stream) [[ deprecated("use stream.sendHow(client...)") ]];
6663

6764
virtual int available() override;
6865
virtual int read() override;

libraries/ESP8266WiFi/src/include/ClientContext.h

+15-14
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ extern "C" void esp_yield();
3030
extern "C" void esp_schedule();
3131

3232
#include <assert.h>
33-
#include <StreamDev.h>
3433
#include <esp_priv.h>
3534

3635
bool getDefaultPrivateGlobalSyncValue ();
@@ -376,13 +375,12 @@ class ClientContext
376375
return _pcb->state;
377376
}
378377

379-
size_t write(Stream& stream)
378+
size_t write(const char* ds, const size_t dl)
380379
{
381380
if (!_pcb) {
382381
return 0;
383382
}
384-
assert(stream.hasPeekBufferAPI());
385-
return _write_from_source(&stream);
383+
return _write_from_source(ds, dl);
386384
}
387385

388386
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,23 +464,25 @@ class ClientContext
466464
}
467465
}
468466

469-
size_t _write_from_source(Stream* ds)
467+
size_t _write_from_source(const char* ds, const size_t dl)
470468
{
471469
assert(_datasource == nullptr);
472470
assert(!_send_waiting);
473471
_datasource = ds;
472+
_datalen = dl;
474473
_written = 0;
475474
_op_start_time = millis();
476475
do {
477476
if (_write_some()) {
478477
_op_start_time = millis();
479478
}
480479

481-
if (!_datasource->available() || _is_timeout() || state() == CLOSED) {
480+
if (_written == _datalen || _is_timeout() || state() == CLOSED) {
482481
if (_is_timeout()) {
483482
DEBUGV(":wtmo\r\n");
484483
}
485484
_datasource = nullptr;
485+
_datalen = 0;
486486
break;
487487
}
488488

@@ -507,20 +507,21 @@ class ClientContext
507507
return false;
508508
}
509509

510-
DEBUGV(":wr %d %d\r\n", _datasource->peekAvailable(), _written);
510+
DEBUGV(":wr %d %d\r\n", _datalen - _written, _written);
511511

512512
bool has_written = false;
513513

514-
while (_datasource) {
514+
while (_written < _datalen) {
515515
if (state() == CLOSED)
516516
return false;
517-
size_t next_chunk_size = std::min((size_t)tcp_sndbuf(_pcb), _datasource->peekAvailable());
517+
const auto remaining = _datalen - _written;
518+
size_t next_chunk_size = std::min((size_t)tcp_sndbuf(_pcb), remaining);
518519
if (!next_chunk_size)
519520
break;
520-
const char* buf = _datasource->peekBuffer();
521+
const char* buf = _datasource + _written;
521522

522523
uint8_t flags = 0;
523-
if (next_chunk_size < _datasource->peekAvailable())
524+
if (next_chunk_size < remaining)
524525
// PUSH is meant for peer, telling to give data to user app as soon as received
525526
// PUSH "may be set" when sender has finished sending a "meaningful" data block
526527
// PUSH does not break Nagle
@@ -534,10 +535,9 @@ class ClientContext
534535

535536
err_t err = tcp_write(_pcb, buf, next_chunk_size, flags);
536537

537-
DEBUGV(":wrc %d %d %d\r\n", next_chunk_size, _datasource->peekAvailable(), (int)err);
538+
DEBUGV(":wrc %d %d %d\r\n", next_chunk_size, remaining, (int)err);
538539

539540
if (err == ERR_OK) {
540-
_datasource->peekConsume(next_chunk_size);
541541
_written += next_chunk_size;
542542
has_written = true;
543543
} else {
@@ -695,7 +695,8 @@ class ClientContext
695695
discard_cb_t _discard_cb;
696696
void* _discard_cb_arg;
697697

698-
Stream* _datasource = nullptr;
698+
const char* _datasource = nullptr;
699+
size_t _datalen = 0;
699700
size_t _written = 0;
700701
uint32_t _timeout_ms = 5000;
701702
uint32_t _op_start_time = 0;

tests/host/common/include/ClientContext.h

+2-24
Original file line numberDiff line numberDiff line change
@@ -223,9 +223,9 @@ class ClientContext
223223
return _sock >= 0? ESTABLISHED: CLOSED;
224224
}
225225

226-
size_t write(const uint8_t* data, size_t size)
226+
size_t write(const char* data, size_t size)
227227
{
228-
ssize_t ret = mockWrite(_sock, data, size, _timeout_ms);
228+
ssize_t ret = mockWrite(_sock, (const uint8_t*)data, size, _timeout_ms);
229229
if (ret < 0)
230230
{
231231
abort();
@@ -234,28 +234,6 @@ class ClientContext
234234
return ret;
235235
}
236236

237-
size_t write(Stream& stream)
238-
{
239-
size_t avail = stream.available();
240-
uint8_t buf [avail];
241-
avail = stream.readBytes(buf, avail);
242-
size_t totwrote = 0;
243-
uint8_t* w = buf;
244-
while (avail && _sock >= 0)
245-
{
246-
size_t wrote = write(w, avail);
247-
w += wrote;
248-
avail -= wrote;
249-
totwrote += wrote;
250-
}
251-
return totwrote;
252-
}
253-
254-
size_t write_P(PGM_P buf, size_t size)
255-
{
256-
return write((const uint8_t*)buf, size);
257-
}
258-
259237
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)
260238
{
261239
(void) idle_sec;

0 commit comments

Comments
 (0)