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
@@ -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+
531540int 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+
593651int 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;
0 commit comments