Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 522cf5d

Browse filesBrowse files
committed
Add support for chunked response bodies
1 parent 5bda5b6 commit 522cf5d
Copy full SHA for 522cf5d

File tree

Expand file treeCollapse file tree

3 files changed

+120
-7
lines changed
Filter options
Expand file treeCollapse file tree

3 files changed

+120
-7
lines changed

‎keywords.txt

Copy file name to clipboardExpand all lines: keywords.txt
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ endOfHeadersReached KEYWORD2
3030
endOfBodyReached KEYWORD2
3131
completed KEYWORD2
3232
contentLength KEYWORD2
33+
isChunked KEYWORD2
3334
connectionKeepAlive KEYWORD2
3435
noDefaultRequestHeaders KEYWORD2
3536
headerAvailable KEYWORD2

‎src/HttpClient.cpp

Copy file name to clipboardExpand all lines: src/HttpClient.cpp
+100-4Lines changed: 100 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
// Initialize constants
99
const char* HttpClient::kUserAgent = "Arduino/2.2.0";
1010
const char* HttpClient::kContentLengthPrefix = HTTP_HEADER_CONTENT_LENGTH ": ";
11+
const char* HttpClient::kTransferEncodingChunked = HTTP_HEADER_TRANSFER_ENCODING ": " HTTP_HEADER_VALUE_CHUNKED;
1112

1213
HttpClient::HttpClient(Client& aClient, const char* aServerName, uint16_t aServerPort)
1314
: iClient(&aClient), iServerName(aServerName), iServerAddress(), iServerPort(aServerPort),
@@ -35,6 +36,9 @@ void HttpClient::resetState()
3536
iContentLength = kNoContentLengthHeader;
3637
iBodyLengthConsumed = 0;
3738
iContentLengthPtr = kContentLengthPrefix;
39+
iTransferEncodingChunkedPtr = kTransferEncodingChunked;
40+
iIsChunked = false;
41+
iChunkLength = 0;
3842
iHttpResponseTimeout = kHttpResponseTimeout;
3943
}
4044

@@ -62,7 +66,7 @@ void HttpClient::beginRequest()
6266
int HttpClient::startRequest(const char* aURLPath, const char* aHttpMethod,
6367
const char* aContentType, int aContentLength, const byte aBody[])
6468
{
65-
if (iState == eReadingBody)
69+
if (iState == eReadingBody || iState == eReadingChunkLength || iState == eReadingBodyChunk)
6670
{
6771
flushClientRx();
6872

@@ -528,6 +532,11 @@ int HttpClient::skipResponseHeaders()
528532
}
529533
}
530534

535+
bool HttpClient::endOfHeadersReached()
536+
{
537+
return (iState == eReadingBody || iState == eReadingChunkLength || iState == eReadingBodyChunk);
538+
};
539+
531540
int HttpClient::contentLength()
532541
{
533542
// skip the response headers, if they haven't been read already
@@ -590,8 +599,65 @@ bool HttpClient::endOfBodyReached()
590599
return false;
591600
}
592601

602+
int HttpClient::available()
603+
{
604+
if (iState == eReadingChunkLength)
605+
{
606+
while (iClient->available())
607+
{
608+
char c = iClient->read();
609+
610+
if (c == '\n')
611+
{
612+
iState = eReadingBodyChunk;
613+
break;
614+
}
615+
else if (c == '\r')
616+
{
617+
// no-op
618+
}
619+
else if (isHexadecimalDigit(c))
620+
{
621+
char digit[2] = {c, '\0'};
622+
623+
iChunkLength = (iChunkLength * 16) + strtol(digit, NULL, 16);
624+
}
625+
}
626+
}
627+
628+
if (iState == eReadingBodyChunk && iChunkLength == 0)
629+
{
630+
iState = eReadingChunkLength;
631+
}
632+
633+
if (iState == eReadingChunkLength)
634+
{
635+
return 0;
636+
}
637+
638+
int clientAvailable = iClient->available();
639+
640+
if (iState == eReadingBodyChunk)
641+
{
642+
return min(clientAvailable, iChunkLength);
643+
}
644+
else
645+
{
646+
return clientAvailable;
647+
}
648+
}
649+
650+
593651
int HttpClient::read()
594652
{
653+
if (iState == eReadingBodyChunk)
654+
{
655+
if (!available())
656+
{
657+
return -1;
658+
}
659+
}
660+
595661
int ret = iClient->read();
596662
if (ret >= 0)
597663
{
@@ -601,6 +667,16 @@ int HttpClient::read()
601667
// So keep track of how many bytes are left
602668
iBodyLengthConsumed++;
603669
}
670+
671+
if (iState == eReadingBodyChunk)
672+
{
673+
iChunkLength--;
674+
675+
if (iChunkLength == 0)
676+
{
677+
iState = eReadingChunkLength;
678+
}
679+
}
604680
}
605681
return ret;
606682
}
@@ -714,15 +790,26 @@ int HttpClient::readHeader()
714790
iBodyLengthConsumed = 0;
715791
}
716792
}
717-
else if ((iContentLengthPtr == kContentLengthPrefix) && (c == '\r'))
793+
else if (*iTransferEncodingChunkedPtr == c)
794+
{
795+
// This character matches, just move along
796+
iTransferEncodingChunkedPtr++;
797+
if (*iTransferEncodingChunkedPtr == '\0')
798+
{
799+
// We've reached the end of the Transfer Encoding: chunked header
800+
iIsChunked = true;
801+
iState = eSkipToEndOfHeader;
802+
}
803+
}
804+
else if (((iContentLengthPtr == kContentLengthPrefix) && (iTransferEncodingChunkedPtr == kTransferEncodingChunked)) && (c == '\r'))
718805
{
719806
// We've found a '\r' at the start of a line, so this is probably
720807
// the end of the headers
721808
iState = eLineStartingCRFound;
722809
}
723810
else
724811
{
725-
// This isn't the Content-Length header, skip to the end of the line
812+
// This isn't the Content-Length or Transfer Encoding chunked header, skip to the end of the line
726813
iState = eSkipToEndOfHeader;
727814
}
728815
break;
@@ -742,7 +829,15 @@ int HttpClient::readHeader()
742829
case eLineStartingCRFound:
743830
if (c == '\n')
744831
{
745-
iState = eReadingBody;
832+
if (iIsChunked)
833+
{
834+
iState = eReadingChunkLength;
835+
iChunkLength = 0;
836+
}
837+
else
838+
{
839+
iState = eReadingBody;
840+
}
746841
}
747842
break;
748843
default:
@@ -755,6 +850,7 @@ int HttpClient::readHeader()
755850
// We've got to the end of this line, start processing again
756851
iState = eStatusCodeRead;
757852
iContentLengthPtr = kContentLengthPrefix;
853+
iTransferEncodingChunkedPtr = kTransferEncodingChunked;
758854
}
759855
// And return the character read to whoever wants it
760856
return c;

‎src/HttpClient.h

Copy file name to clipboardExpand all lines: src/HttpClient.h
+19-3Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ static const int HTTP_ERROR_INVALID_RESPONSE =-4;
3434
#define HTTP_HEADER_CONTENT_LENGTH "Content-Length"
3535
#define HTTP_HEADER_CONTENT_TYPE "Content-Type"
3636
#define HTTP_HEADER_CONNECTION "Connection"
37+
#define HTTP_HEADER_TRANSFER_ENCODING "Transfer-Encoding"
3738
#define HTTP_HEADER_USER_AGENT "User-Agent"
39+
#define HTTP_HEADER_VALUE_CHUNKED "chunked"
3840

3941
class HttpClient : public Client
4042
{
@@ -247,7 +249,7 @@ class HttpClient : public Client
247249
/** Test whether all of the response headers have been consumed.
248250
@return true if we are now processing the response body, else false
249251
*/
250-
bool endOfHeadersReached() { return (iState == eReadingBody); };
252+
bool endOfHeadersReached();
251253

252254
/** Test whether the end of the body has been reached.
253255
Only works if the Content-Length header was returned by the server
@@ -265,6 +267,11 @@ class HttpClient : public Client
265267
*/
266268
int contentLength();
267269

270+
/** Returns if the response body is chunked
271+
@return true if response body is chunked, false otherwise
272+
*/
273+
int isChunked() { return iIsChunked; }
274+
268275
/** Return the response body as a String
269276
Also skips response headers if they have not been read already
270277
MUST be called after responseStatusCode()
@@ -286,7 +293,7 @@ class HttpClient : public Client
286293
virtual size_t write(uint8_t aByte) { if (iState < eRequestSent) { finishHeaders(); }; return iClient-> write(aByte); };
287294
virtual size_t write(const uint8_t *aBuffer, size_t aSize) { if (iState < eRequestSent) { finishHeaders(); }; return iClient->write(aBuffer, aSize); };
288295
// Inherited from Stream
289-
virtual int available() { return iClient->available(); };
296+
virtual int available();
290297
/** Read the next byte from the server.
291298
@return Byte read or -1 if there are no bytes available.
292299
*/
@@ -332,6 +339,7 @@ class HttpClient : public Client
332339
// processing)
333340
static const int kHttpResponseTimeout = 30*1000;
334341
static const char* kContentLengthPrefix;
342+
static const char* kTransferEncodingChunked;
335343
typedef enum {
336344
eIdle,
337345
eRequestStarted,
@@ -341,7 +349,9 @@ class HttpClient : public Client
341349
eReadingContentLength,
342350
eSkipToEndOfHeader,
343351
eLineStartingCRFound,
344-
eReadingBody
352+
eReadingBody,
353+
eReadingChunkLength,
354+
eReadingBodyChunk
345355
} tHttpState;
346356
// Client we're using
347357
Client* iClient;
@@ -360,6 +370,12 @@ class HttpClient : public Client
360370
int iBodyLengthConsumed;
361371
// How far through a Content-Length header prefix we are
362372
const char* iContentLengthPtr;
373+
// How far through a Transfer-Encoding chunked header we are
374+
const char* iTransferEncodingChunkedPtr;
375+
// Stores if the response body is chunked
376+
bool iIsChunked;
377+
// Stores the value of the current chunk length, if present
378+
int iChunkLength;
363379
uint32_t iHttpResponseTimeout;
364380
bool iConnectionClose;
365381
bool iSendDefaultRequestHeaders;

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.