@@ -176,6 +176,228 @@ typedef struct {
176
176
} Cookie;
177
177
typedef std::vector<Cookie> CookieJar;
178
178
179
+ class HTTPStream : public WiFiClient {
180
+ public:
181
+ HTTPStream () {
182
+ _conn = nullptr ;
183
+ _chunked = false ;
184
+ _chunkLen = 0 ;
185
+ _state = READ_HEX;
186
+ _partial = 0 ;
187
+ _eof = false ;
188
+ }
189
+
190
+ HTTPStream (const HTTPStream&);
191
+ HTTPStream& operator =(const HTTPStream&);
192
+
193
+ void reset (WiFiClient *connection, bool chunked) {
194
+ DEBUG_HTTPCLIENT (" [HTTPStream] reset(%p, %d)\n " , connection, chunked);
195
+ _conn = connection;
196
+ _partial = 0 ;
197
+ _state = READ_HEX;
198
+ _chunked = chunked;
199
+ _eof = false ;
200
+ }
201
+
202
+ virtual size_t write (uint8_t c) override {
203
+ return _conn->write (c);
204
+ }
205
+
206
+ virtual size_t write (const uint8_t *buf, size_t size) override {
207
+ return _conn->write (buf, size);
208
+ }
209
+
210
+ virtual uint8_t connected () override {
211
+ return _conn->connected ();
212
+ }
213
+
214
+ virtual int available () override {
215
+ if (!_chunked) {
216
+ return _conn->available ();
217
+ }
218
+ // We're chunked at this point
219
+ if (_eof) {
220
+ return 0 ;
221
+ }
222
+ if (!_chunkLen) {
223
+ tryReadChunkLen (1 );
224
+ }
225
+ return std::min (_chunkLen, _conn->available ());
226
+ }
227
+
228
+ virtual int availableForWrite () override {
229
+ return _conn->availableForWrite ();
230
+ }
231
+
232
+ virtual void flush () override {
233
+ _conn->flush ();
234
+ }
235
+
236
+ virtual void stop () override {
237
+ _conn->stop ();
238
+ }
239
+
240
+ virtual int read () override {
241
+ if (!_chunked) {
242
+ return _conn->read ();
243
+ }
244
+ // We're chunked at this point
245
+ if (_eof) {
246
+ DEBUG_HTTPCLIENT (" [HTTPStream] ::read() after EOF\n " );
247
+ return -1 ;
248
+ }
249
+ if (!_chunkLen) {
250
+ tryReadChunkLen (_timeout);
251
+ }
252
+ if (!_chunkLen) {
253
+ DEBUG_HTTPCLIENT (" [HTTPStream] ::read no chunkLen\n " );
254
+ return -1 ;
255
+ }
256
+ uint32_t start = millis ();
257
+ while (((millis () - start) < _timeout) && !_conn->available () && _conn->connected ()) {
258
+ delay (1 );
259
+ }
260
+ if (_conn->available ()) {
261
+ _chunkLen--;
262
+ return _conn->read ();
263
+ } else {
264
+ DEBUG_HTTPCLIENT (" [HTTPStream] ::read wrapped stream failure. avail = %d, conn = %d\n " , _conn->available (), _conn->connected ());
265
+ return -1 ;
266
+ }
267
+ }
268
+
269
+ virtual int read (uint8_t *buf, size_t size) override {
270
+ if (!_chunked) {
271
+ return _conn->read (buf, size);
272
+ }
273
+ for (int i = 0 ; i < (int )size; i++) {
274
+ int c = read ();
275
+ if (c < 0 ) {
276
+ return i;
277
+ }
278
+ *(buf++) = (uint8_t )c;
279
+ }
280
+ return size;
281
+ }
282
+
283
+ virtual int peek () override {
284
+ if (!_chunked) {
285
+ return _conn->peek ();
286
+ }
287
+ // We're chunked at this point
288
+ if (_eof) {
289
+ DEBUG_HTTPCLIENT (" [HTTPStream] ::peek after EOF\n " );
290
+ return -1 ;
291
+ }
292
+ if (!_chunkLen) {
293
+ tryReadChunkLen (_timeout);
294
+ }
295
+ if (!_chunkLen) {
296
+ DEBUG_HTTPCLIENT (" [HTTPStream] ::peek no chunkLen\n " );
297
+ return -1 ;
298
+ }
299
+ uint32_t start = millis ();
300
+ while (((millis () - start) < _timeout) && !_conn->available () && _conn->connected ()) {
301
+ delay (1 );
302
+ }
303
+ if (_conn->available ()) {
304
+ return _conn->peek ();
305
+ } else {
306
+ DEBUG_HTTPCLIENT (" [HTTPStream] ::peek wrapped stream failure. avail = %d, conn = %d\n " , _conn->available (), _conn->connected ());
307
+ return -1 ;
308
+ }
309
+ }
310
+
311
+ void setTimeout (unsigned long timeout) {
312
+ _timeout = timeout;
313
+ _conn->setTimeout (timeout);
314
+ }
315
+
316
+ private:
317
+ void tryReadChunkLen (uint32_t to) {
318
+ if (_state == ERROR) {
319
+ return ;
320
+ }
321
+ uint32_t start = millis ();
322
+ while ((millis () - start) <= to) {
323
+ if (_conn->available ()) {
324
+ int recv = _conn->read ();
325
+ if (recv < 0 ) {
326
+ DEBUG_HTTPCLIENT (" [HTTPStream] Read of available data failed\n " );
327
+ _state = ERROR;
328
+ return ;
329
+ }
330
+ switch (_state) {
331
+ case READ_HEX:
332
+ if (recv == ' \r ' ) {
333
+ DEBUG_HTTPCLIENT (" [HTTPStream] Saw \\ r of chunk len\r\n " );
334
+ _state = READ_LF;
335
+ break ;
336
+ }
337
+ if (recv >= ' 0' && recv <= ' 9' ) {
338
+ DEBUG_HTTPCLIENT (" [HTTPStream] Read %c of chunk size\n " , recv);
339
+ _partial <<= 4 ;
340
+ _partial |= recv - ' 0' ;
341
+ } else if (tolower (recv) >= ' a' && tolower (recv) <= ' f' ) {
342
+ DEBUG_HTTPCLIENT (" [HTTPStream] Read %c of chunk size\n " , recv);
343
+ _partial <<= 4 ;
344
+ _partial |= tolower (recv) - ' a' + 10 ;
345
+ } else {
346
+ DEBUG_HTTPCLIENT (" [HTTPStream] READ_HEX error '%c'\n " , recv);
347
+ _state = ERROR;
348
+ return ;
349
+ }
350
+ break ;
351
+ case READ_LF:
352
+ if (recv != ' \n ' ) {
353
+ _state = ERROR;
354
+ DEBUG_HTTPCLIENT (" [HTTPStream] READ_LF error '%02x'\n " , recv);
355
+ return ;
356
+ }
357
+ DEBUG_HTTPCLIENT (" [HTTPStream] Chunk len = %d\n " , _partial);
358
+ _chunkLen = _partial;
359
+ _partial = 0 ;
360
+ _state = TAIL_CR;
361
+ if (_chunkLen == 0 ) {
362
+ // 0-sized chunk is EOF special case
363
+ _eof = true ;
364
+ }
365
+ return ;
366
+ case TAIL_CR:
367
+ if (recv == ' \r ' ) {
368
+ DEBUG_HTTPCLIENT (" [HTTPStream] Saw \\ r of chunk end\n " );
369
+ _state = TAIL_LF;
370
+ break ;
371
+ }
372
+ DEBUG_HTTPCLIENT (" [HTTPStream] TAIL_CR error '%c'\n " , recv);
373
+ _state = ERROR;
374
+ return ;
375
+ case TAIL_LF:
376
+ if (recv == ' \n ' ) {
377
+ DEBUG_HTTPCLIENT (" [HTTPStream] Saw \\ n of chunk end\n " );
378
+ _state = READ_HEX;
379
+ break ;
380
+ }
381
+ DEBUG_HTTPCLIENT (" [HTTPStream] TAIL_LF error '%c'\n " , recv);
382
+ _state = ERROR;
383
+ return ;
384
+ case ERROR:
385
+ return ;
386
+ }
387
+ }
388
+ }
389
+ DEBUG_HTTPCLIENT (" [HTTPStream] Timeout waiting for chunk\n " );
390
+ }
391
+
392
+ WiFiClient *_conn;
393
+ bool _chunked;
394
+ int _chunkLen;
395
+ enum { READ_HEX, READ_LF, TAIL_CR, TAIL_LF, ERROR } _state;
396
+ int _partial;
397
+ bool _eof;
398
+ };
399
+
400
+
179
401
class HTTPClient {
180
402
public:
181
403
HTTPClient () = default ;
@@ -407,4 +629,6 @@ class HTTPClient {
407
629
std::unique_ptr<StreamString> _payload;
408
630
// Cookie jar support
409
631
CookieJar *_cookieJar = nullptr ;
632
+
633
+ HTTPStream _stream;
410
634
};
0 commit comments