8
8
// Initialize constants
9
9
const char * HttpClient::kUserAgent = " Arduino/2.2.0" ;
10
10
const char * HttpClient::kContentLengthPrefix = HTTP_HEADER_CONTENT_LENGTH " : " ;
11
+ const char * HttpClient::kTransferEncodingChunked = HTTP_HEADER_TRANSFER_ENCODING " : " HTTP_HEADER_VALUE_CHUNKED;
11
12
12
13
HttpClient::HttpClient (Client& aClient, const char * aServerName, uint16_t aServerPort)
13
14
: iClient(&aClient), iServerName(aServerName), iServerAddress(), iServerPort(aServerPort),
@@ -35,6 +36,9 @@ void HttpClient::resetState()
35
36
iContentLength = kNoContentLengthHeader ;
36
37
iBodyLengthConsumed = 0 ;
37
38
iContentLengthPtr = kContentLengthPrefix ;
39
+ iTransferEncodingChunkedPtr = kTransferEncodingChunked ;
40
+ iIsChunked = false ;
41
+ iChunkLength = 0 ;
38
42
iHttpResponseTimeout = kHttpResponseTimeout ;
39
43
}
40
44
@@ -62,7 +66,7 @@ void HttpClient::beginRequest()
62
66
int HttpClient::startRequest (const char * aURLPath, const char * aHttpMethod,
63
67
const char * aContentType, int aContentLength, const byte aBody[])
64
68
{
65
- if (iState == eReadingBody)
69
+ if (iState == eReadingBody || iState == eReadingChunkLength || iState == eReadingBodyChunk )
66
70
{
67
71
flushClientRx ();
68
72
@@ -528,6 +532,11 @@ int HttpClient::skipResponseHeaders()
528
532
}
529
533
}
530
534
535
+ bool HttpClient::endOfHeadersReached ()
536
+ {
537
+ return (iState == eReadingBody || iState == eReadingChunkLength || iState == eReadingBodyChunk);
538
+ };
539
+
531
540
int HttpClient::contentLength ()
532
541
{
533
542
// skip the response headers, if they haven't been read already
@@ -590,8 +599,65 @@ bool HttpClient::endOfBodyReached()
590
599
return false ;
591
600
}
592
601
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
+
593
651
int HttpClient::read ()
594
652
{
653
+ if (iState == eReadingBodyChunk)
654
+ {
655
+ if (!available ())
656
+ {
657
+ return -1 ;
658
+ }
659
+ }
660
+
595
661
int ret = iClient->read ();
596
662
if (ret >= 0 )
597
663
{
@@ -601,6 +667,16 @@ int HttpClient::read()
601
667
// So keep track of how many bytes are left
602
668
iBodyLengthConsumed++;
603
669
}
670
+
671
+ if (iState == eReadingBodyChunk)
672
+ {
673
+ iChunkLength--;
674
+
675
+ if (iChunkLength == 0 )
676
+ {
677
+ iState = eReadingChunkLength;
678
+ }
679
+ }
604
680
}
605
681
return ret;
606
682
}
@@ -714,15 +790,26 @@ int HttpClient::readHeader()
714
790
iBodyLengthConsumed = 0 ;
715
791
}
716
792
}
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 ' ))
718
805
{
719
806
// We've found a '\r' at the start of a line, so this is probably
720
807
// the end of the headers
721
808
iState = eLineStartingCRFound;
722
809
}
723
810
else
724
811
{
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
726
813
iState = eSkipToEndOfHeader;
727
814
}
728
815
break ;
@@ -742,7 +829,15 @@ int HttpClient::readHeader()
742
829
case eLineStartingCRFound:
743
830
if (c == ' \n ' )
744
831
{
745
- iState = eReadingBody;
832
+ if (iIsChunked)
833
+ {
834
+ iState = eReadingChunkLength;
835
+ iChunkLength = 0 ;
836
+ }
837
+ else
838
+ {
839
+ iState = eReadingBody;
840
+ }
746
841
}
747
842
break ;
748
843
default :
@@ -755,6 +850,7 @@ int HttpClient::readHeader()
755
850
// We've got to the end of this line, start processing again
756
851
iState = eStatusCodeRead;
757
852
iContentLengthPtr = kContentLengthPrefix ;
853
+ iTransferEncodingChunkedPtr = kTransferEncodingChunked ;
758
854
}
759
855
// And return the character read to whoever wants it
760
856
return c;
0 commit comments