|  | @@ -730,6 +730,14 @@ static void destroy_stream_locked(grpc_exec_ctx *exec_ctx, void *sp,
 | 
	
		
			
				|  |  |    grpc_slice_buffer_destroy_internal(exec_ctx,
 | 
	
		
			
				|  |  |                                       &s->unprocessed_incoming_frames_buffer);
 | 
	
		
			
				|  |  |    grpc_slice_buffer_destroy_internal(exec_ctx, &s->frame_storage);
 | 
	
		
			
				|  |  | +  if (s->compressed_data_buffer) {
 | 
	
		
			
				|  |  | +    grpc_slice_buffer_destroy_internal(exec_ctx, s->compressed_data_buffer);
 | 
	
		
			
				|  |  | +    gpr_free(s->compressed_data_buffer);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  if (s->decompressed_data_buffer) {
 | 
	
		
			
				|  |  | +    grpc_slice_buffer_destroy_internal(exec_ctx, s->decompressed_data_buffer);
 | 
	
		
			
				|  |  | +    gpr_free(s->decompressed_data_buffer);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    grpc_chttp2_list_remove_stalled_by_transport(t, s);
 | 
	
		
			
				|  |  |    grpc_chttp2_list_remove_stalled_by_stream(t, s);
 | 
	
	
		
			
				|  | @@ -780,6 +788,15 @@ static void destroy_stream(grpc_exec_ctx *exec_ctx, grpc_transport *gt,
 | 
	
		
			
				|  |  |    grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt;
 | 
	
		
			
				|  |  |    grpc_chttp2_stream *s = (grpc_chttp2_stream *)gs;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  if (s->stream_compression_ctx != NULL) {
 | 
	
		
			
				|  |  | +    grpc_stream_compression_context_destroy(s->stream_compression_ctx);
 | 
	
		
			
				|  |  | +    s->stream_compression_ctx = NULL;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  if (s->stream_decompression_ctx != NULL) {
 | 
	
		
			
				|  |  | +    grpc_stream_compression_context_destroy(s->stream_decompression_ctx);
 | 
	
		
			
				|  |  | +    s->stream_decompression_ctx = NULL;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    s->destroy_stream_arg = then_schedule_closure;
 | 
	
		
			
				|  |  |    GRPC_CLOSURE_SCHED(
 | 
	
		
			
				|  |  |        exec_ctx, GRPC_CLOSURE_INIT(&s->destroy_stream, destroy_stream_locked, s,
 | 
	
	
		
			
				|  | @@ -1367,8 +1384,8 @@ static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx, void *stream_op,
 | 
	
		
			
				|  |  |            "fetching_send_message_finished");
 | 
	
		
			
				|  |  |      } else {
 | 
	
		
			
				|  |  |        GPR_ASSERT(s->fetching_send_message == NULL);
 | 
	
		
			
				|  |  | -      uint8_t *frame_hdr =
 | 
	
		
			
				|  |  | -          grpc_slice_buffer_tiny_add(&s->flow_controlled_buffer, 5);
 | 
	
		
			
				|  |  | +      uint8_t *frame_hdr = grpc_slice_buffer_tiny_add(
 | 
	
		
			
				|  |  | +          &s->flow_controlled_buffer, GRPC_HEADER_SIZE_IN_BYTES);
 | 
	
		
			
				|  |  |        uint32_t flags = op_payload->send_message.send_message->flags;
 | 
	
		
			
				|  |  |        frame_hdr[0] = (flags & GRPC_WRITE_INTERNAL_COMPRESS) != 0;
 | 
	
		
			
				|  |  |        size_t len = op_payload->send_message.send_message->length;
 | 
	
	
		
			
				|  | @@ -1459,14 +1476,9 @@ static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx, void *stream_op,
 | 
	
		
			
				|  |  |      s->recv_message_ready = op_payload->recv_message.recv_message_ready;
 | 
	
		
			
				|  |  |      s->recv_message = op_payload->recv_message.recv_message;
 | 
	
		
			
				|  |  |      if (s->id != 0) {
 | 
	
		
			
				|  |  | -      if (s->pending_byte_stream) {
 | 
	
		
			
				|  |  | -        already_received = s->frame_storage.length;
 | 
	
		
			
				|  |  | -      } else {
 | 
	
		
			
				|  |  | -        already_received = s->frame_storage.length +
 | 
	
		
			
				|  |  | -                           s->unprocessed_incoming_frames_buffer.length;
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -      incoming_byte_stream_update_flow_control(exec_ctx, t, s, 5,
 | 
	
		
			
				|  |  | -                                               already_received);
 | 
	
		
			
				|  |  | +      already_received = s->frame_storage.length;
 | 
	
		
			
				|  |  | +      incoming_byte_stream_update_flow_control(
 | 
	
		
			
				|  |  | +          exec_ctx, t, s, GRPC_HEADER_SIZE_IN_BYTES, already_received);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      grpc_chttp2_maybe_complete_recv_message(exec_ctx, t, s);
 | 
	
		
			
				|  |  |    }
 | 
	
	
		
			
				|  | @@ -1703,10 +1715,43 @@ void grpc_chttp2_maybe_complete_recv_message(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  |          if (s->unprocessed_incoming_frames_buffer.length == 0) {
 | 
	
		
			
				|  |  |            grpc_slice_buffer_swap(&s->unprocessed_incoming_frames_buffer,
 | 
	
		
			
				|  |  |                                   &s->frame_storage);
 | 
	
		
			
				|  |  | +          s->unprocessed_incoming_frames_decompressed = false;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        if (s->stream_compression_recv_enabled &&
 | 
	
		
			
				|  |  | +            !s->unprocessed_incoming_frames_decompressed) {
 | 
	
		
			
				|  |  | +          GPR_ASSERT(s->decompressed_data_buffer->length == 0);
 | 
	
		
			
				|  |  | +          bool end_of_context;
 | 
	
		
			
				|  |  | +          if (!s->stream_decompression_ctx) {
 | 
	
		
			
				|  |  | +            s->stream_decompression_ctx =
 | 
	
		
			
				|  |  | +                grpc_stream_compression_context_create(
 | 
	
		
			
				|  |  | +                    GRPC_STREAM_COMPRESSION_DECOMPRESS);
 | 
	
		
			
				|  |  | +          }
 | 
	
		
			
				|  |  | +          if (!grpc_stream_decompress(s->stream_decompression_ctx,
 | 
	
		
			
				|  |  | +                                      &s->unprocessed_incoming_frames_buffer,
 | 
	
		
			
				|  |  | +                                      s->decompressed_data_buffer, NULL,
 | 
	
		
			
				|  |  | +                                      GRPC_HEADER_SIZE_IN_BYTES,
 | 
	
		
			
				|  |  | +                                      &end_of_context)) {
 | 
	
		
			
				|  |  | +            grpc_slice_buffer_reset_and_unref_internal(exec_ctx,
 | 
	
		
			
				|  |  | +                                                       &s->frame_storage);
 | 
	
		
			
				|  |  | +            grpc_slice_buffer_reset_and_unref_internal(
 | 
	
		
			
				|  |  | +                exec_ctx, &s->unprocessed_incoming_frames_buffer);
 | 
	
		
			
				|  |  | +            error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  | +                "Stream decompression error.");
 | 
	
		
			
				|  |  | +          } else {
 | 
	
		
			
				|  |  | +            error = grpc_deframe_unprocessed_incoming_frames(
 | 
	
		
			
				|  |  | +                exec_ctx, &s->data_parser, s, s->decompressed_data_buffer, NULL,
 | 
	
		
			
				|  |  | +                s->recv_message);
 | 
	
		
			
				|  |  | +            if (end_of_context) {
 | 
	
		
			
				|  |  | +              grpc_stream_compression_context_destroy(
 | 
	
		
			
				|  |  | +                  s->stream_decompression_ctx);
 | 
	
		
			
				|  |  | +              s->stream_decompression_ctx = NULL;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +          }
 | 
	
		
			
				|  |  | +        } else {
 | 
	
		
			
				|  |  | +          error = grpc_deframe_unprocessed_incoming_frames(
 | 
	
		
			
				|  |  | +              exec_ctx, &s->data_parser, s,
 | 
	
		
			
				|  |  | +              &s->unprocessed_incoming_frames_buffer, NULL, s->recv_message);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | -        error = grpc_deframe_unprocessed_incoming_frames(
 | 
	
		
			
				|  |  | -            exec_ctx, &s->data_parser, s,
 | 
	
		
			
				|  |  | -            &s->unprocessed_incoming_frames_buffer, NULL, s->recv_message);
 | 
	
		
			
				|  |  |          if (error != GRPC_ERROR_NONE) {
 | 
	
		
			
				|  |  |            s->seen_error = true;
 | 
	
		
			
				|  |  |            grpc_slice_buffer_reset_and_unref_internal(exec_ctx,
 | 
	
	
		
			
				|  | @@ -1744,7 +1789,37 @@ void grpc_chttp2_maybe_complete_recv_trailing_metadata(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      bool pending_data = s->pending_byte_stream ||
 | 
	
		
			
				|  |  |                          s->unprocessed_incoming_frames_buffer.length > 0;
 | 
	
		
			
				|  |  | +    if (s->stream_compression_recv_enabled && s->read_closed &&
 | 
	
		
			
				|  |  | +        s->frame_storage.length > 0 &&
 | 
	
		
			
				|  |  | +        s->unprocessed_incoming_frames_buffer.length == 0 && !pending_data &&
 | 
	
		
			
				|  |  | +        !s->seen_error && s->recv_trailing_metadata_finished != NULL) {
 | 
	
		
			
				|  |  | +      /* Maybe some SYNC_FLUSH data is left in frame_storage. Consume them and
 | 
	
		
			
				|  |  | +       * maybe decompress the next 5 bytes in the stream. */
 | 
	
		
			
				|  |  | +      bool end_of_context;
 | 
	
		
			
				|  |  | +      if (!s->stream_decompression_ctx) {
 | 
	
		
			
				|  |  | +        s->stream_decompression_ctx = grpc_stream_compression_context_create(
 | 
	
		
			
				|  |  | +            GRPC_STREAM_COMPRESSION_DECOMPRESS);
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      if (!grpc_stream_decompress(s->stream_decompression_ctx,
 | 
	
		
			
				|  |  | +                                  &s->frame_storage,
 | 
	
		
			
				|  |  | +                                  &s->unprocessed_incoming_frames_buffer, NULL,
 | 
	
		
			
				|  |  | +                                  GRPC_HEADER_SIZE_IN_BYTES, &end_of_context)) {
 | 
	
		
			
				|  |  | +        grpc_slice_buffer_reset_and_unref_internal(exec_ctx, &s->frame_storage);
 | 
	
		
			
				|  |  | +        grpc_slice_buffer_reset_and_unref_internal(
 | 
	
		
			
				|  |  | +            exec_ctx, &s->unprocessed_incoming_frames_buffer);
 | 
	
		
			
				|  |  | +        s->seen_error = true;
 | 
	
		
			
				|  |  | +      } else {
 | 
	
		
			
				|  |  | +        if (s->unprocessed_incoming_frames_buffer.length > 0) {
 | 
	
		
			
				|  |  | +          s->unprocessed_incoming_frames_decompressed = true;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        if (end_of_context) {
 | 
	
		
			
				|  |  | +          grpc_stream_compression_context_destroy(s->stream_decompression_ctx);
 | 
	
		
			
				|  |  | +          s->stream_decompression_ctx = NULL;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |      if (s->read_closed && s->frame_storage.length == 0 &&
 | 
	
		
			
				|  |  | +        s->unprocessed_incoming_frames_buffer.length == 0 &&
 | 
	
		
			
				|  |  |          (!pending_data || s->seen_error) &&
 | 
	
		
			
				|  |  |          s->recv_trailing_metadata_finished != NULL) {
 | 
	
		
			
				|  |  |        grpc_chttp2_incoming_metadata_buffer_publish(
 | 
	
	
		
			
				|  | @@ -2612,6 +2687,7 @@ static void incoming_byte_stream_next_locked(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  |    if (s->frame_storage.length > 0) {
 | 
	
		
			
				|  |  |      grpc_slice_buffer_swap(&s->frame_storage,
 | 
	
		
			
				|  |  |                             &s->unprocessed_incoming_frames_buffer);
 | 
	
		
			
				|  |  | +    s->unprocessed_incoming_frames_decompressed = false;
 | 
	
		
			
				|  |  |      GRPC_CLOSURE_SCHED(exec_ctx, bs->next_action.on_complete, GRPC_ERROR_NONE);
 | 
	
		
			
				|  |  |    } else if (s->byte_stream_error != GRPC_ERROR_NONE) {
 | 
	
		
			
				|  |  |      GRPC_CLOSURE_SCHED(exec_ctx, bs->next_action.on_complete,
 | 
	
	
		
			
				|  | @@ -2673,17 +2749,41 @@ static grpc_error *incoming_byte_stream_pull(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  |    grpc_chttp2_incoming_byte_stream *bs =
 | 
	
		
			
				|  |  |        (grpc_chttp2_incoming_byte_stream *)byte_stream;
 | 
	
		
			
				|  |  |    grpc_chttp2_stream *s = bs->stream;
 | 
	
		
			
				|  |  | +  grpc_error *error;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    if (s->unprocessed_incoming_frames_buffer.length > 0) {
 | 
	
		
			
				|  |  | -    grpc_error *error = grpc_deframe_unprocessed_incoming_frames(
 | 
	
		
			
				|  |  | +    if (s->stream_compression_recv_enabled &&
 | 
	
		
			
				|  |  | +        !s->unprocessed_incoming_frames_decompressed) {
 | 
	
		
			
				|  |  | +      bool end_of_context;
 | 
	
		
			
				|  |  | +      if (!s->stream_decompression_ctx) {
 | 
	
		
			
				|  |  | +        s->stream_decompression_ctx = grpc_stream_compression_context_create(
 | 
	
		
			
				|  |  | +            GRPC_STREAM_COMPRESSION_DECOMPRESS);
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      if (!grpc_stream_decompress(s->stream_decompression_ctx,
 | 
	
		
			
				|  |  | +                                  &s->unprocessed_incoming_frames_buffer,
 | 
	
		
			
				|  |  | +                                  s->decompressed_data_buffer, NULL, MAX_SIZE_T,
 | 
	
		
			
				|  |  | +                                  &end_of_context)) {
 | 
	
		
			
				|  |  | +        error =
 | 
	
		
			
				|  |  | +            GRPC_ERROR_CREATE_FROM_STATIC_STRING("Stream decompression error.");
 | 
	
		
			
				|  |  | +        return error;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      GPR_ASSERT(s->unprocessed_incoming_frames_buffer.length == 0);
 | 
	
		
			
				|  |  | +      grpc_slice_buffer_swap(&s->unprocessed_incoming_frames_buffer,
 | 
	
		
			
				|  |  | +                             s->decompressed_data_buffer);
 | 
	
		
			
				|  |  | +      s->unprocessed_incoming_frames_decompressed = true;
 | 
	
		
			
				|  |  | +      if (end_of_context) {
 | 
	
		
			
				|  |  | +        grpc_stream_compression_context_destroy(s->stream_decompression_ctx);
 | 
	
		
			
				|  |  | +        s->stream_decompression_ctx = NULL;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    error = grpc_deframe_unprocessed_incoming_frames(
 | 
	
		
			
				|  |  |          exec_ctx, &s->data_parser, s, &s->unprocessed_incoming_frames_buffer,
 | 
	
		
			
				|  |  |          slice, NULL);
 | 
	
		
			
				|  |  |      if (error != GRPC_ERROR_NONE) {
 | 
	
		
			
				|  |  |        return error;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    } else {
 | 
	
		
			
				|  |  | -    grpc_error *error =
 | 
	
		
			
				|  |  | -        GRPC_ERROR_CREATE_FROM_STATIC_STRING("Truncated message");
 | 
	
		
			
				|  |  | +    error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Truncated message");
 | 
	
		
			
				|  |  |      GRPC_CLOSURE_SCHED(exec_ctx, &s->reset_byte_stream, GRPC_ERROR_REF(error));
 | 
	
		
			
				|  |  |      return error;
 | 
	
		
			
				|  |  |    }
 |