Add support for chunked response decompression. PiperOrigin-RevId: 914516549
diff --git a/quiche/quic/masque/masque_ohttp_client.cc b/quiche/quic/masque/masque_ohttp_client.cc index e8d8056..ee854bc 100644 --- a/quiche/quic/masque/masque_ohttp_client.cc +++ b/quiche/quic/masque/masque_ohttp_client.cc
@@ -675,7 +675,8 @@ } int num_ohttp_chunks = per_request_config.num_ohttp_chunks(); if (num_ohttp_chunks > 0) { - pending_request.chunk_handler = std::make_unique<ChunkHandler>(); + pending_request.chunk_handler = + std::make_unique<ChunkHandler>(config_.handle_gzip_response()); QUICHE_ASSIGN_OR_RETURN( ChunkedObliviousHttpClient chunked_client, ChunkedObliviousHttpClient::Create( @@ -777,7 +778,7 @@ encapsulated_response.body = binary_response->body(); return encapsulated_response; } - ChunkHandler chunk_handler; + ChunkHandler chunk_handler(config_.handle_gzip_response()); chunk_handler.SetResponseChunkCallback( [this, request_id](absl::string_view chunk) { if (response_visitor_) { @@ -1061,8 +1062,6 @@ return connection_pool_.SendBodyChunk(request_id, encrypted_chunk, is_final); } -MasqueOhttpClient::ChunkHandler::ChunkHandler() : decoder_(this) {} - absl::Status MasqueOhttpClient::ChunkHandler::DecryptChunk( absl::string_view encrypted_chunk, bool end_stream) { if (!chunked_client_.has_value()) { @@ -1146,6 +1145,13 @@ absl::Status MasqueOhttpClient::ChunkHandler::OnFinalResponseHeadersDone() { QUICHE_LOG(INFO) << "Received incremental OHTTP response headers: " << response_.headers.DebugString(); + if (handle_gzip_response_) { + auto it = response_.headers.find("content-encoding"); + if (it != response_.headers.end() && + absl::EqualsIgnoreCase(it->second, "gzip")) { + is_gzipped_ = true; + } + } return absl::OkStatus(); } absl::Status MasqueOhttpClient::ChunkHandler::OnBodyChunk( @@ -1153,16 +1159,43 @@ body_chunk_count_++; QUICHE_LOG(INFO) << "Received body chunk #" << body_chunk_count_ << " of size " << body_chunk.size(); - std::cout << body_chunk; - response_.body += body_chunk; + + if (body_chunk.empty()) { + return absl::OkStatus(); + } + + std::string decompressed = ""; + absl::string_view processed_chunk = body_chunk; + if (is_gzipped_) { + if (!decompressor_) { + QUICHE_ASSIGN_OR_RETURN(decompressor_, GzipDecompressor::Create()); + } + QUICHE_ASSIGN_OR_RETURN( + decompressed, + decompressor_->Decompress(body_chunk, /*end_stream=*/false)); + processed_chunk = decompressed; + QUICHE_LOG(INFO) << "Decompressed chunk to size " << processed_chunk.size(); + if (processed_chunk.empty()) { + return absl::OkStatus(); + } + } + + std::cout << processed_chunk; + response_.body += processed_chunk; if (response_chunk_callback_) { - response_chunk_callback_(body_chunk); + response_chunk_callback_(processed_chunk); } return absl::OkStatus(); } absl::Status MasqueOhttpClient::ChunkHandler::OnBodyChunksDone() { + if (decompressor_ != nullptr) { + if (!decompressor_->IsFinished()) { + return absl::InternalError("Gzip stream truncated"); + } + decompressor_->EndDecompression(); + } return absl::OkStatus(); } absl::Status MasqueOhttpClient::ChunkHandler::OnTrailer(
diff --git a/quiche/quic/masque/masque_ohttp_client.h b/quiche/quic/masque/masque_ohttp_client.h index 6295b31..cba9716 100644 --- a/quiche/quic/masque/masque_ohttp_client.h +++ b/quiche/quic/masque/masque_ohttp_client.h
@@ -283,7 +283,8 @@ public: using ResponseChunkCallback = std::function<void(absl::string_view)>; - explicit ChunkHandler(); + explicit ChunkHandler(bool handle_gzip_response) + : decoder_(this), handle_gzip_response_(handle_gzip_response) {} void SetResponseChunkCallback(ResponseChunkCallback callback) { response_chunk_callback_ = std::move(callback); } @@ -339,6 +340,9 @@ std::optional<bool> is_chunked_response_; size_t decrypted_chunk_count_ = 0; size_t body_chunk_count_ = 0; + bool handle_gzip_response_ = false; + bool is_gzipped_ = false; + std::unique_ptr<GzipDecompressor> decompressor_; }; struct PendingRequest {