Skip to content

Commit

Permalink
try out benchmarks
Browse files Browse the repository at this point in the history
  • Loading branch information
fasterthanlime committed Aug 26, 2024
1 parent 634a2b5 commit 553ece4
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 96 deletions.
114 changes: 80 additions & 34 deletions crates/httpwg-loona/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use b_x::{BxForResults, BX};
use std::{cell::RefCell, rc::Rc};
use std::{cell::RefCell, io::Write, rc::Rc};
use tokio::{process::Command, sync::oneshot};

use buffet::{IntoHalves, RollMut};
use buffet::{IntoHalves, Piece, RollMut};
use loona::{
error::NeverError,
http::{self, StatusCode},
Body, BodyChunk, Encoder, ExpectResponseHeaders, Responder, Response, ResponseDone,
ServerDriver,
ServerDriver, SinglePieceBody,
};

#[derive(Debug, Clone, Copy)]
Expand Down Expand Up @@ -173,12 +174,12 @@ where

async fn handle(
&self,
_req: loona::Request,
req: loona::Request,
req_body: &mut impl Body,
mut res: Responder<OurEncoder, ExpectResponseHeaders>,
) -> Result<Responder<OurEncoder, ResponseDone>, Self::Error> {
// if the client sent `expect: 100-continue`, we must send a 100 status code
if let Some(h) = _req.headers.get(http::header::EXPECT) {
if let Some(h) = req.headers.get(http::header::EXPECT) {
if &h[..] == b"100-continue" {
res.write_interim_response(Response {
status: StatusCode::CONTINUE,
Expand All @@ -188,41 +189,86 @@ where
}
}

// then read the full request body
let mut req_body_len = 0;
loop {
let chunk = req_body.next_chunk().await.bx()?;
match chunk {
BodyChunk::Done { trailers } => {
// yey
if let Some(trailers) = trailers {
tracing::debug!(trailers_len = %trailers.len(), "received trailers");
}
break;
let res = match req.uri.path() {
"/echo-body" => res
.write_final_response_with_body(
Response {
status: StatusCode::OK,
..Default::default()
},
req_body,
)
.await
.bx()?,
"/stream-big-body" => {
let mut roll = RollMut::alloc().bx()?;
for _ in 0..256 {
roll.write_all("this is a big chunk".as_bytes()).bx()?;
}
BodyChunk::Chunk(chunk) => {
req_body_len += chunk.len();

struct RepeatBody {
piece: Piece,
n: usize,
written: usize,
}
}
}
tracing::debug!(%req_body_len, "read request body");

tracing::trace!("writing final response");
let mut res = res
.write_final_response(Response {
status: StatusCode::OK,
..Default::default()
})
.await?;
impl std::fmt::Debug for RepeatBody {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("RepeatBody")
.field("piece_len", &self.piece.len())
.field("n", &self.n)
.field("written", &self.written)
.finish()
}
}

impl Body for RepeatBody {
type Error = NeverError;

fn content_len(&self) -> Option<u64> {
Some(self.n as u64 * self.piece.len() as u64)
}

fn eof(&self) -> bool {
self.written == self.n
}

tracing::trace!("writing body chunk");
res.write_chunk("it's less dire to lose, than to lose oneself".into())
.await?;
async fn next_chunk(&mut self) -> Result<BodyChunk, Self::Error> {
if self.eof() {
return Ok(BodyChunk::Done { trailers: None });
}

tracing::trace!("finishing body (with no trailers)");
let res = res.finish_body(None).await?;
let chunk = self.piece.clone();
self.written += 1;
Ok(BodyChunk::Chunk(chunk))
}
}

tracing::trace!("we're done");
res.write_final_response_with_body(
Response {
status: StatusCode::OK,
..Default::default()
},
&mut RepeatBody {
piece: roll.take_all().into(),
n: 128,
written: 0,
},
)
.await
.bx()?
}
_ => res
.write_final_response_with_body(
Response {
status: StatusCode::OK,
..Default::default()
},
&mut SinglePieceBody::from("it's less dire to lose, than to lose oneself"),
)
.await
.bx()?,
};
Ok(res)
}
}
58 changes: 1 addition & 57 deletions crates/loona/src/h2/body.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
use core::fmt;

use tokio::sync::mpsc;

use crate::{error::NeverError, Body, BodyChunk, Headers};
use crate::{Body, BodyChunk, Headers};
use buffet::Piece;

use super::types::H2StreamError;
Expand Down Expand Up @@ -177,57 +175,3 @@ impl Body for H2Body {
Ok(chunk)
}
}

pub(crate) struct SinglePieceBody {
content_len: u64,
piece: Option<Piece>,
}

impl fmt::Debug for SinglePieceBody {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut debug_struct = f.debug_struct("SinglePieceBody");
debug_struct.field("content_len", &self.content_len);

if let Some(piece) = &self.piece {
match std::str::from_utf8(piece.as_ref()) {
Ok(utf8_str) => debug_struct.field("piece", &utf8_str),
Err(_) => debug_struct.field("piece", &"(non-utf8 string)"),
};
} else {
debug_struct.field("piece", &"(none)");
}

debug_struct.finish()
}
}

impl SinglePieceBody {
pub(crate) fn new(piece: Piece) -> Self {
let content_len = piece.len() as u64;
Self {
content_len,
piece: Some(piece),
}
}
}

impl Body for SinglePieceBody {
type Error = NeverError;

fn content_len(&self) -> Option<u64> {
Some(self.content_len)
}

fn eof(&self) -> bool {
self.piece.is_none()
}

async fn next_chunk(&mut self) -> Result<BodyChunk, Self::Error> {
tracing::trace!( has_piece = %self.piece.is_some(), "SinglePieceBody::next_chunk");
if let Some(piece) = self.piece.take() {
Ok(BodyChunk::Chunk(piece))
} else {
Ok(BodyChunk::Done { trailers: None })
}
}
}
7 changes: 2 additions & 5 deletions crates/loona/src/h2/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,10 @@ use crate::{
},
},
util::{read_and_parse, ReadAndParseError},
Headers, Method, Request, Responder, ResponderOrBodyError, ServeOutcome, ServerDriver,
Headers, Method, Request, Responder, ResponderOrBodyError, ServeOutcome, ServerDriver, SinglePieceBody,
};

use super::{
body::{ChunkPosition, SinglePieceBody},
types::H2ErrorLevel,
};
use super::{body::ChunkPosition, types::H2ErrorLevel};

pub const MAX_WINDOW_SIZE: i64 = u32::MAX as i64;

Expand Down
63 changes: 63 additions & 0 deletions crates/loona/src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,3 +223,66 @@ pub enum ServeOutcome {
/// the client
SuccessfulHttp2GracefulShutdown,
}

pub struct SinglePieceBody {
content_len: u64,
piece: Option<Piece>,
}

impl<T> From<T> for SinglePieceBody
where
T: Into<Piece>,
{
fn from(piece: T) -> Self {
Self::new(piece.into())
}
}

impl fmt::Debug for SinglePieceBody {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut debug_struct = f.debug_struct("SinglePieceBody");
debug_struct.field("content_len", &self.content_len);

if let Some(piece) = &self.piece {
match std::str::from_utf8(piece.as_ref()) {
Ok(utf8_str) => debug_struct.field("piece", &utf8_str),
Err(_) => debug_struct.field("piece", &"(non-utf8 string)"),
};
} else {
debug_struct.field("piece", &"(none)");
}

debug_struct.finish()
}
}

impl SinglePieceBody {
pub(crate) fn new(piece: Piece) -> Self {
let content_len = piece.len() as u64;
Self {
content_len,
piece: Some(piece),
}
}
}

impl Body for SinglePieceBody {
type Error = NeverError;

fn content_len(&self) -> Option<u64> {
Some(self.content_len)
}

fn eof(&self) -> bool {
self.piece.is_none()
}

async fn next_chunk(&mut self) -> Result<BodyChunk, Self::Error> {
tracing::trace!( has_piece = %self.piece.is_some(), "SinglePieceBody::next_chunk");
if let Some(piece) = self.piece.take() {
Ok(BodyChunk::Chunk(piece))
} else {
Ok(BodyChunk::Done { trailers: None })
}
}
}
16 changes: 16 additions & 0 deletions k6/script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import http from "k6/http";
import { sleep } from "k6";

export const options = {
// A number specifying the number of VUs to run concurrently.
vus: 200,
// A string specifying the total duration of the test run.
duration: "10s",
};

export default function () {
http.get("http://localhost:8001/", {
http: {},
});
sleep(1);
}

0 comments on commit 553ece4

Please sign in to comment.