88// Initialize constants
99const char * HttpClient::kUserAgent = " Arduino/2.2.0" ;
1010const char * HttpClient::kContentLengthPrefix = HTTP_HEADER_CONTENT_LENGTH " : " ;
11+ const char * HttpClient::kTransferEncodingChunked = HTTP_HEADER_TRANSFER_ENCODING " : " HTTP_HEADER_VALUE_CHUNKED;
1112
1213HttpClient::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()
6266int 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
@@ -533,6 +537,11 @@ int HttpClient::skipResponseHeaders()
533537 }
534538}
535539
540+ bool HttpClient::endOfHeadersReached ()
541+ {
542+ return (iState == eReadingBody || iState == eReadingChunkLength || iState == eReadingBodyChunk);
543+ };
544+
536545int HttpClient::contentLength ()
537546{
538547 // skip the response headers, if they haven't been read already
@@ -595,8 +604,62 @@ bool HttpClient::endOfBodyReached()
595604 return false ;
596605}
597606
607+ int HttpClient::available ()
608+ {
609+ if (iState == eReadingChunkLength)
610+ {
611+ while (iClient->available ())
612+ {
613+ char c = iClient->read ();
614+
615+ if (c == ' \n ' )
616+ {
617+ iState = eReadingBodyChunk;
618+ break ;
619+ }
620+ else if (c == ' \r ' )
621+ {
622+ // no-op
623+ }
624+ else if (isHexadecimalDigit (c))
625+ {
626+ char digit[2 ] = {c, ' \0 ' };
627+
628+ iChunkLength = (iChunkLength * 16 ) + strtol (digit, NULL , 16 );
629+ }
630+ }
631+ }
632+
633+ if (iState == eReadingBodyChunk && iChunkLength == 0 )
634+ {
635+ iState = eReadingChunkLength;
636+ }
637+
638+ if (iState == eReadingChunkLength)
639+ {
640+ return 0 ;
641+ }
642+
643+ int clientAvailable = iClient->available ();
644+
645+ if (iState == eReadingBodyChunk)
646+ {
647+ return min (clientAvailable, iChunkLength);
648+ }
649+ else
650+ {
651+ return clientAvailable;
652+ }
653+ }
654+
655+
598656int HttpClient::read ()
599657{
658+ if (iIsChunked && !available ())
659+ {
660+ return -1 ;
661+ }
662+
600663 int ret = iClient->read ();
601664 if (ret >= 0 )
602665 {
@@ -606,6 +669,16 @@ int HttpClient::read()
606669 // So keep track of how many bytes are left
607670 iBodyLengthConsumed++;
608671 }
672+
673+ if (iState == eReadingBodyChunk)
674+ {
675+ iChunkLength--;
676+
677+ if (iChunkLength == 0 )
678+ {
679+ iState = eReadingChunkLength;
680+ }
681+ }
609682 }
610683 return ret;
611684}
@@ -719,15 +792,26 @@ int HttpClient::readHeader()
719792 iBodyLengthConsumed = 0 ;
720793 }
721794 }
722- else if ((iContentLengthPtr == kContentLengthPrefix ) && (c == ' \r ' ))
795+ else if (*iTransferEncodingChunkedPtr == c)
796+ {
797+ // This character matches, just move along
798+ iTransferEncodingChunkedPtr++;
799+ if (*iTransferEncodingChunkedPtr == ' \0 ' )
800+ {
801+ // We've reached the end of the Transfer Encoding: chunked header
802+ iIsChunked = true ;
803+ iState = eSkipToEndOfHeader;
804+ }
805+ }
806+ else if (((iContentLengthPtr == kContentLengthPrefix ) && (iTransferEncodingChunkedPtr == kTransferEncodingChunked )) && (c == ' \r ' ))
723807 {
724808 // We've found a '\r' at the start of a line, so this is probably
725809 // the end of the headers
726810 iState = eLineStartingCRFound;
727811 }
728812 else
729813 {
730- // This isn't the Content-Length header, skip to the end of the line
814+ // This isn't the Content-Length or Transfer Encoding chunked header, skip to the end of the line
731815 iState = eSkipToEndOfHeader;
732816 }
733817 break ;
@@ -747,7 +831,15 @@ int HttpClient::readHeader()
747831 case eLineStartingCRFound:
748832 if (c == ' \n ' )
749833 {
750- iState = eReadingBody;
834+ if (iIsChunked)
835+ {
836+ iState = eReadingChunkLength;
837+ iChunkLength = 0 ;
838+ }
839+ else
840+ {
841+ iState = eReadingBody;
842+ }
751843 }
752844 break ;
753845 default :
@@ -760,6 +852,7 @@ int HttpClient::readHeader()
760852 // We've got to the end of this line, start processing again
761853 iState = eStatusCodeRead;
762854 iContentLengthPtr = kContentLengthPrefix ;
855+ iTransferEncodingChunkedPtr = kTransferEncodingChunked ;
763856 }
764857 // And return the character read to whoever wants it
765858 return c;
0 commit comments