Handle final delayed chunk size
diff --git a/iocore/eventsystem/I_VIO.h b/iocore/eventsystem/I_VIO.h
index de7ab32..eacbc35 100644
--- a/iocore/eventsystem/I_VIO.h
+++ b/iocore/eventsystem/I_VIO.h
@@ -95,7 +95,7 @@
     @return The number of bytes to be processed by the operation.
 
   */
-  int64_t ntodo();
+  int64_t ntodo() const;
 
   /////////////////////
   // buffer settings //
diff --git a/iocore/eventsystem/P_VIO.h b/iocore/eventsystem/P_VIO.h
index e2bf54f..5c0d057 100644
--- a/iocore/eventsystem/P_VIO.h
+++ b/iocore/eventsystem/P_VIO.h
@@ -61,7 +61,7 @@
   return (buffer.reader());
 }
 TS_INLINE int64_t
-VIO::ntodo()
+VIO::ntodo() const
 {
   return nbytes - ndone;
 }
diff --git a/proxy/http/HttpTransact.cc b/proxy/http/HttpTransact.cc
index c0ddf77..5629521 100644
--- a/proxy/http/HttpTransact.cc
+++ b/proxy/http/HttpTransact.cc
@@ -6839,12 +6839,12 @@
     }
 
     // Close the connection if client_info is not keep-alive.
-    // Otherwise, if we cannot trust the content length, we will close the connection
+    // Otherwise, if we cannot trust the content length and the client process chunked encoding, we will close the connection
     // unless we are going to use chunked encoding or the client issued
     // a PUSH request
     if (s->client_info.keep_alive != HTTP_KEEPALIVE) {
       ka_action = KA_DISABLED;
-    } else if (s->hdr_info.trust_response_cl == false &&
+    } else if (s->hdr_info.trust_response_cl == false && s->state_machine->ua_txn->is_chunked_encoding_supported() &&
                !(s->client_info.receive_chunked_response == true ||
                  (s->method == HTTP_WKSIDX_PUSH && s->client_info.keep_alive == HTTP_KEEPALIVE))) {
       ka_action = KA_CLOSE;
diff --git a/proxy/http/HttpTunnel.cc b/proxy/http/HttpTunnel.cc
index bcdb695..404b4f3 100644
--- a/proxy/http/HttpTunnel.cc
+++ b/proxy/http/HttpTunnel.cc
@@ -839,8 +839,7 @@
       // initialize a reader to chunked buffer start before writing to keep ref count
       chunked_buffer_start = p->chunked_handler.chunked_buffer->alloc_reader();
       p->chunked_handler.chunked_buffer->write(p->buffer_start, p->chunked_handler.skip_bytes);
-    }
-    if (p->do_dechunking) {
+    } else if (p->do_dechunking) {
       // bz57413
       Debug("http_tunnel", "[producer_run] do_dechunking p->chunked_handler.chunked_reader->read_avail() = %" PRId64 "",
             p->chunked_handler.chunked_reader->read_avail());
diff --git a/proxy/http2/Http2Stream.cc b/proxy/http2/Http2Stream.cc
index 4a3bd34..d1c37f6 100644
--- a/proxy/http2/Http2Stream.cc
+++ b/proxy/http2/Http2Stream.cc
@@ -613,10 +613,6 @@
       // make sure to send the end of stream
       is_done |= (write_vio.ntodo() + this->response_header.length_get()) == bytes_avail;
       if (this->response_is_data_available() || is_done) {
-        if (is_done) {
-          this->mark_body_done();
-        }
-
         this->send_response_body(call_update);
       }
       break;
@@ -628,10 +624,6 @@
       break;
     }
   } else {
-    if (write_vio.ntodo() == bytes_avail || is_done) {
-      this->mark_body_done();
-    }
-
     this->send_response_body(call_update);
   }
 
diff --git a/proxy/http2/Http2Stream.h b/proxy/http2/Http2Stream.h
index 96cebb0..3be1057 100644
--- a/proxy/http2/Http2Stream.h
+++ b/proxy/http2/Http2Stream.h
@@ -67,18 +67,7 @@
   bool
   is_body_done() const
   {
-    return body_done;
-  }
-
-  void
-  mark_body_done()
-  {
-    body_done = true;
-    if (response_is_chunked()) {
-      ink_assert(chunked_handler.state == ChunkedHandler::CHUNK_READ_DONE ||
-                 chunked_handler.state == ChunkedHandler::CHUNK_READ_ERROR);
-      this->write_vio.nbytes = response_header.length_get() + chunked_handler.dechunked_size;
-    }
+    return this->write_vio.ntodo() == 0;
   }
 
   void
@@ -258,7 +247,6 @@
   VIO write_vio;
 
   bool trailing_header = false;
-  bool body_done       = false;
   bool chunked         = false;
 
   // A brief disucssion of similar flags and state variables:  _state, closed, terminate_stream