From 87353be9b77b66d7a2e700cdd27c4bb9ea9ec419 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Fri, 17 May 2024 16:15:19 +0200 Subject: [PATCH] Change ChunkedWriterStrategy to minimize connection writes --- src/client.rs | 4 ++-- src/request.rs | 25 +++++++++++++++++-------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/client.rs b/src/client.rs index 91f717c..0321ec5 100644 --- a/src/client.rs +++ b/src/client.rs @@ -311,7 +311,7 @@ where HttpConnection::Plain(c) => { let mut writer = ChunkedBodyWriter::new(c); body.write(&mut writer).await?; - writer.write_empty_chunk().await.map_err(|e| e.kind())?; + writer.write_termination().await.map_err(|e| e.kind())?; } HttpConnection::PlainBuffered(buffered_conn) => { // Flush the buffered connection so that we can bypass it and rent its buffer @@ -335,7 +335,7 @@ where HttpConnection::Tls(c) => { let mut writer = ChunkedBodyWriter::new(c); body.write(&mut writer).await?; - writer.write_empty_chunk().await.map_err(|e| e.kind())?; + writer.write_termination().await.map_err(|e| e.kind())?; } #[cfg(all(not(feature = "embedded-tls"), not(feature = "esp-mbedtls")))] HttpConnection::Tls(_) => unreachable!(), diff --git a/src/request.rs b/src/request.rs index ae1b41f..fcac185 100644 --- a/src/request.rs +++ b/src/request.rs @@ -5,7 +5,7 @@ use core::fmt::Write as _; use core::mem::size_of; use embedded_io::{Error as _, ErrorType}; use embedded_io_async::Write; -use heapless::String; +use heapless::{String, Vec}; /// A read only HTTP request type pub struct Request<'req, B> @@ -365,8 +365,8 @@ where Self(conn, 0) } - pub async fn write_empty_chunk(&mut self) -> Result<(), C::Error> { - self.0.write_all(b"0\r\n\r\n").await + pub async fn write_termination(&mut self) -> Result<(), C::Error> { + self.0.write_all(b"\r\n0\r\n\r\n").await } } @@ -404,15 +404,23 @@ where hex::encode_to_slice(len.to_be_bytes(), &mut hex).unwrap(); let leading_zeros = hex.iter().position(|x| *x != b'0').unwrap_or_default(); let (_, hex) = hex.split_at(leading_zeros); - self.0.write_all(hex).await.map_err(to_errorkind)?; - self.0.write_all(b"\r\n").await.map_err(to_errorkind)?; - // Write chunk + let mut header = Vec::::new(); + if self.1 > 0 { + // Write newline terminating ongoing chunk + // We do it here instead of after writing the chunk + // to minimize the number of writes to the underlying connection + header.extend_from_slice(b"\r\n").unwrap(); + } + header.extend_from_slice(hex).unwrap(); + header.extend_from_slice(b"\r\n").unwrap(); + + self.0.write_all(&header).await.map_err(to_errorkind)?; + + // Write chunk payload self.0.write_all(buf).await.map_err(to_errorkind)?; self.1 += len; - // Write newline - self.0.write_all(b"\r\n").await.map_err(to_errorkind)?; Ok(()) } @@ -424,6 +432,7 @@ where #[cfg(test)] mod tests { use super::*; + use std::vec::Vec; #[tokio::test] async fn basic_auth() {