@@ -651,20 +651,99 @@ void AsyncHttpClient::handleConnect(RequestContext* context) {
651651}
652652
653653void AsyncHttpClient::handleData (RequestContext* context, char * data, size_t len) {
654- bool storeBody = context && context->request && !context->request ->getNoStoreBody ();
655- bool bufferThisChunk = context && (!context->headersComplete || context->chunked );
654+ if (!context)
655+ return ;
656+ bool storeBody = context->request && !context->request ->getNoStoreBody ();
657+ bool bufferThisChunk = (!context->headersComplete || context->chunked );
656658 if (bufferThisChunk)
657659 context->responseBuffer .concat (data, len);
658660 bool enforceLimit = shouldEnforceBodyLimit (context);
659- auto wouldExceedLimit = [&](size_t incoming) -> bool {
661+ auto wouldExceedBodyLimit = [&](size_t incoming) -> bool {
660662 if (!enforceLimit)
661663 return false ;
662- size_t current = context->receivedContentLength ;
664+ size_t current = context->receivedBodyLength ;
663665 if (current >= _maxBodySize)
664666 return true ;
665667 return incoming > (_maxBodySize - current);
666668 };
667669
670+ auto emitBodyBytes = [&](const char * out, size_t outLen) -> bool {
671+ if (!out || outLen == 0 )
672+ return true ;
673+ if (wouldExceedBodyLimit (outLen)) {
674+ triggerError (context, MAX_BODY_SIZE_EXCEEDED, " Body exceeds configured maximum" );
675+ return false ;
676+ }
677+ if (storeBody) {
678+ context->response ->appendBody (out, outLen);
679+ }
680+ context->receivedBodyLength += outLen;
681+ auto cb = _bodyChunkCallback;
682+ if (cb)
683+ cb (out, outLen, false );
684+ return true ;
685+ };
686+
687+ auto deliverWireBytes = [&](const char * wire, size_t wireLen) -> bool {
688+ if (wireLen == 0 )
689+ return true ;
690+ context->receivedContentLength += wireLen;
691+ #if ASYNC_HTTP_ENABLE_GZIP_DECODE
692+ if (context->gzipDecodeActive ) {
693+ size_t offset = 0 ;
694+ while (offset < wireLen) {
695+ const uint8_t * outPtr = nullptr ;
696+ size_t outLen = 0 ;
697+ size_t consumed = 0 ;
698+ GzipDecoder::Result r = context->gzipDecoder .write (reinterpret_cast <const uint8_t *>(wire + offset),
699+ wireLen - offset, &consumed, &outPtr, &outLen, true );
700+ if (outLen > 0 ) {
701+ if (!emitBodyBytes (reinterpret_cast <const char *>(outPtr), outLen))
702+ return false ;
703+ }
704+ if (r == GzipDecoder::Result::kError ) {
705+ triggerError (context, GZIP_DECODE_FAILED, context->gzipDecoder .lastError ());
706+ return false ;
707+ }
708+ offset += consumed;
709+ if (consumed == 0 && outLen == 0 ) {
710+ triggerError (context, GZIP_DECODE_FAILED, " Gzip decoder stalled" );
711+ return false ;
712+ }
713+ if (r == GzipDecoder::Result::kNeedMoreInput && offset >= wireLen) {
714+ break ;
715+ }
716+ }
717+ return true ;
718+ }
719+ #endif
720+ return emitBodyBytes (wire, wireLen);
721+ };
722+
723+ auto finalizeDecoding = [&]() -> bool {
724+ #if ASYNC_HTTP_ENABLE_GZIP_DECODE
725+ if (!context->gzipDecodeActive )
726+ return true ;
727+ for (;;) {
728+ const uint8_t * outPtr = nullptr ;
729+ size_t outLen = 0 ;
730+ GzipDecoder::Result r = context->gzipDecoder .finish (&outPtr, &outLen);
731+ if (outLen > 0 ) {
732+ if (!emitBodyBytes (reinterpret_cast <const char *>(outPtr), outLen))
733+ return false ;
734+ }
735+ if (r == GzipDecoder::Result::kDone )
736+ return true ;
737+ if (r == GzipDecoder::Result::kOk )
738+ continue ;
739+ triggerError (context, GZIP_DECODE_FAILED, context->gzipDecoder .lastError ());
740+ return false ;
741+ }
742+ #else
743+ return true ;
744+ #endif
745+ };
746+
668747 if (!context->headersComplete ) {
669748 int headerEnd = context->responseBuffer .indexOf (" \r\n\r\n " );
670749 if (_maxHeaderBytes > 0 ) {
@@ -679,12 +758,17 @@ void AsyncHttpClient::handleData(RequestContext* context, char* data, size_t len
679758 String headerData = context->responseBuffer .substring (0 , headerEnd);
680759 if (parseResponseHeaders (context, headerData)) {
681760 context->headersComplete = true ;
682- if (enforceLimit && context->expectedContentLength > 0 &&
761+ #if ASYNC_HTTP_ENABLE_GZIP_DECODE
762+ bool gzipActive = context->gzipDecodeActive ;
763+ #else
764+ bool gzipActive = false ;
765+ #endif
766+ if (enforceLimit && !gzipActive && context->expectedContentLength > 0 &&
683767 context->expectedContentLength > _maxBodySize) {
684768 triggerError (context, MAX_BODY_SIZE_EXCEEDED, " Body exceeds configured maximum" );
685769 return ;
686770 }
687- if (storeBody && context->expectedContentLength > 0 && !context->chunked &&
771+ if (storeBody && !gzipActive && context->expectedContentLength > 0 && !context->chunked &&
688772 (!enforceLimit || context->expectedContentLength <= _maxBodySize)) {
689773 context->response ->reserveBody (context->expectedContentLength );
690774 }
@@ -693,17 +777,12 @@ void AsyncHttpClient::handleData(RequestContext* context, char* data, size_t len
693777 return ;
694778 if (!context->chunked && context->responseBuffer .length () > 0 ) {
695779 size_t incomingLen = context->responseBuffer .length ();
696- if (wouldExceedLimit (incomingLen)) {
780+ if (!gzipActive && wouldExceedBodyLimit (incomingLen)) {
697781 triggerError (context, MAX_BODY_SIZE_EXCEEDED, " Body exceeds configured maximum" );
698782 return ;
699783 }
700- if (storeBody) {
701- context->response ->appendBody (context->responseBuffer .c_str (), incomingLen);
702- }
703- context->receivedContentLength += incomingLen;
704- auto cb = _bodyChunkCallback;
705- if (cb)
706- cb (context->responseBuffer .c_str (), incomingLen, false );
784+ if (!deliverWireBytes (context->responseBuffer .c_str (), incomingLen))
785+ return ;
707786 context->responseBuffer = " " ;
708787 }
709788 } else {
@@ -712,17 +791,17 @@ void AsyncHttpClient::handleData(RequestContext* context, char* data, size_t len
712791 }
713792 }
714793 } else if (!context->chunked ) {
715- if (wouldExceedLimit (len)) {
794+ #if ASYNC_HTTP_ENABLE_GZIP_DECODE
795+ bool gzipActive = context->gzipDecodeActive ;
796+ #else
797+ bool gzipActive = false ;
798+ #endif
799+ if (!gzipActive && wouldExceedBodyLimit (len)) {
716800 triggerError (context, MAX_BODY_SIZE_EXCEEDED, " Body exceeds configured maximum" );
717801 return ;
718802 }
719- if (storeBody) {
720- context->response ->appendBody (data, len);
721- }
722- context->receivedContentLength += len;
723- auto cb2 = _bodyChunkCallback;
724- if (cb2)
725- cb2 (data, len, false );
803+ if (!deliverWireBytes (data, len))
804+ return ;
726805 }
727806
728807 while (context->headersComplete && context->chunked && !context->chunkedComplete ) {
@@ -796,7 +875,12 @@ void AsyncHttpClient::handleData(RequestContext* context, char* data, size_t len
796875 triggerError (context, CHUNKED_DECODE_FAILED, " Chunk size parse error" );
797876 return ;
798877 }
799- if (chunkSize > 0 && wouldExceedLimit (chunkSize)) {
878+ #if ASYNC_HTTP_ENABLE_GZIP_DECODE
879+ bool gzipActive = context->gzipDecodeActive ;
880+ #else
881+ bool gzipActive = false ;
882+ #endif
883+ if (!gzipActive && chunkSize > 0 && wouldExceedBodyLimit (chunkSize)) {
800884 triggerError (context, MAX_BODY_SIZE_EXCEEDED, " Body exceeds configured maximum" );
801885 return ;
802886 }
@@ -817,18 +901,9 @@ void AsyncHttpClient::handleData(RequestContext* context, char* data, size_t len
817901 return ;
818902 }
819903 size_t chunkLen = context->currentChunkRemaining ;
820- if (wouldExceedLimit (chunkLen)) {
821- triggerError (context, MAX_BODY_SIZE_EXCEEDED, " Body exceeds configured maximum" );
822- return ;
823- }
824904 const char * chunkPtr = context->responseBuffer .c_str ();
825- if (storeBody) {
826- context->response ->appendBody (chunkPtr, chunkLen);
827- }
828- context->receivedContentLength += chunkLen;
829- auto cb3 = _bodyChunkCallback;
830- if (cb3)
831- cb3 (chunkPtr, chunkLen, false );
905+ if (!deliverWireBytes (chunkPtr, chunkLen))
906+ return ;
832907 context->responseBuffer .remove (0 , needed);
833908 context->currentChunkRemaining = 0 ;
834909 }
@@ -841,6 +916,8 @@ void AsyncHttpClient::handleData(RequestContext* context, char* data, size_t len
841916 context->receivedContentLength >= context->expectedContentLength )
842917 complete = true ;
843918 if (complete) {
919+ if (!finalizeDecoding ())
920+ return ;
844921 processResponse (context);
845922 }
846923 }
@@ -893,6 +970,51 @@ void AsyncHttpClient::handleDisconnect(RequestContext* context) {
893970 triggerError (context, CONNECTION_CLOSED_MID_BODY, " Truncated response" );
894971 return ;
895972 }
973+ #if ASYNC_HTTP_ENABLE_GZIP_DECODE
974+ if (context->gzipDecodeActive ) {
975+ bool storeBody = context->request && !context->request ->getNoStoreBody ();
976+ bool enforceLimit = shouldEnforceBodyLimit (context);
977+ auto wouldExceedBodyLimit = [&](size_t incoming) -> bool {
978+ if (!enforceLimit)
979+ return false ;
980+ size_t current = context->receivedBodyLength ;
981+ if (current >= _maxBodySize)
982+ return true ;
983+ return incoming > (_maxBodySize - current);
984+ };
985+ auto emitBodyBytes = [&](const char * out, size_t outLen) -> bool {
986+ if (!out || outLen == 0 )
987+ return true ;
988+ if (wouldExceedBodyLimit (outLen)) {
989+ triggerError (context, MAX_BODY_SIZE_EXCEEDED, " Body exceeds configured maximum" );
990+ return false ;
991+ }
992+ if (storeBody) {
993+ context->response ->appendBody (out, outLen);
994+ }
995+ context->receivedBodyLength += outLen;
996+ auto cb = _bodyChunkCallback;
997+ if (cb)
998+ cb (out, outLen, false );
999+ return true ;
1000+ };
1001+ for (;;) {
1002+ const uint8_t * outPtr = nullptr ;
1003+ size_t outLen = 0 ;
1004+ GzipDecoder::Result r = context->gzipDecoder .finish (&outPtr, &outLen);
1005+ if (outLen > 0 ) {
1006+ if (!emitBodyBytes (reinterpret_cast <const char *>(outPtr), outLen))
1007+ return ;
1008+ }
1009+ if (r == GzipDecoder::Result::kDone )
1010+ break ;
1011+ if (r == GzipDecoder::Result::kOk )
1012+ continue ;
1013+ triggerError (context, GZIP_DECODE_FAILED, context->gzipDecoder .lastError ());
1014+ return ;
1015+ }
1016+ }
1017+ #endif
8961018 // Otherwise success: either Content-Length reached, or no Content-Length and closure marks the end
8971019 processResponse (context);
8981020}
@@ -939,6 +1061,16 @@ bool AsyncHttpClient::parseResponseHeaders(RequestContext* context, const String
9391061 context->response ->setContentLength (context->expectedContentLength );
9401062 } else if (name.equalsIgnoreCase (" Transfer-Encoding" ) && value.equalsIgnoreCase (" chunked" )) {
9411063 context->chunked = true ;
1064+ } else if (name.equalsIgnoreCase (" Content-Encoding" )) {
1065+ #if ASYNC_HTTP_ENABLE_GZIP_DECODE
1066+ String lower = value;
1067+ lower.toLowerCase ();
1068+ if (lower.indexOf (" gzip" ) != -1 ) {
1069+ context->gzipEncoded = true ;
1070+ context->gzipDecodeActive = true ;
1071+ context->gzipDecoder .begin ();
1072+ }
1073+ #endif
9421074 } else if (name.equalsIgnoreCase (" Connection" )) {
9431075 String lower = value;
9441076 lower.toLowerCase ();
@@ -1212,6 +1344,7 @@ void AsyncHttpClient::resetContextForRedirect(RequestContext* context, AsyncHttp
12121344 context->responseProcessed = false ;
12131345 context->expectedContentLength = 0 ;
12141346 context->receivedContentLength = 0 ;
1347+ context->receivedBodyLength = 0 ;
12151348 context->chunked = false ;
12161349 context->chunkedComplete = false ;
12171350 context->currentChunkRemaining = 0 ;
@@ -1224,6 +1357,11 @@ void AsyncHttpClient::resetContextForRedirect(RequestContext* context, AsyncHttp
12241357 context->serverRequestedClose = false ;
12251358 context->usingPooledConnection = false ;
12261359 context->resolvedTlsConfig = AsyncHttpTLSConfig ();
1360+ #if ASYNC_HTTP_ENABLE_GZIP_DECODE
1361+ context->gzipEncoded = false ;
1362+ context->gzipDecodeActive = false ;
1363+ context->gzipDecoder .reset ();
1364+ #endif
12271365#if !ASYNC_TCP_HAS_TIMEOUT
12281366 context->timeoutTimer = millis ();
12291367#endif
0 commit comments