This page was visited {{ visit_count }} times before you showed up.
+
+ Here is {{ visit_count }} dot{% if visit_count != 1 %}s{% endif %}:
+
+
+ {% if visit_count ##LT## 100 %}
+ {% for _ in 0..visit_count %}
+ .
+ {% endfor %}
+ {% else %}
+ That's too much dots to display. So f u.
+ {% endif %}
+
+
+
+
diff --git a/client/src/script.ts b/client/src/script.ts
new file mode 100644
index 0000000..eee6aac
--- /dev/null
+++ b/client/src/script.ts
@@ -0,0 +1,16 @@
+type Sigma = string;
+type ToMew = "Σ" | Sigma;
+
+async function skibidi(): Promise {
+ return "fr fr";
+}
+
+async function boomer(): Promise {
+ return new Promise(() => {});
+}
+
+async function alpha(): Promise {
+ console.log(await Promise.any([skibidi(), boomer()]) + " nocap");
+}
+
+alpha();
diff --git a/client/src/style.scss b/client/src/style.scss
new file mode 100644
index 0000000..ba54a04
--- /dev/null
+++ b/client/src/style.scss
@@ -0,0 +1,10 @@
+:root {
+ color-scheme: light dark;
+}
+
+$color-me-daddy: light-dark(black, white);
+
+body {
+ font-family: Arial, Helvetica, sans-serif;
+ color: $color-me-daddy;
+}
diff --git a/client/tsconfig.json b/client/tsconfig.json
new file mode 100644
index 0000000..400b2c6
--- /dev/null
+++ b/client/tsconfig.json
@@ -0,0 +1,23 @@
+{
+ "compilerOptions": {
+ "lib": ["ESNext", "DOM"],
+ "target": "ESNext",
+ "module": "ESNext",
+ "moduleDetection": "force",
+ "jsx": "react-jsx",
+ "allowJs": true,
+
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": false,
+ "noEmit": true,
+
+ "strict": true,
+ "skipLibCheck": true,
+ "noFallthroughCasesInSwitch": true,
+
+ "noUnusedLocals": false,
+ "noUnusedParameters": false,
+ "noPropertyAccessFromIndexSignature": false
+ }
+}
diff --git a/config.json b/config.json
new file mode 100644
index 0000000..b191dfc
--- /dev/null
+++ b/config.json
@@ -0,0 +1,16 @@
+{
+ "prod": {
+ "sock_path": "/run/zmp24.sock",
+ "db": "/data/db"
+ },
+
+ "dev": {
+ "bind_to": "[::1]:8080",
+ "db": "/tmp/zmp24db"
+ },
+
+ "image": {
+ "name": "zmp24",
+ "version": "0.1"
+ }
+}
diff --git a/image/image.dockerfile b/image/image.dockerfile
new file mode 100644
index 0000000..7908204
--- /dev/null
+++ b/image/image.dockerfile
@@ -0,0 +1,7 @@
+FROM docker.io/alpine:latest
+
+COPY ./zmp24 /usr/bin/zmp24
+COPY ./run.sh /run.sh
+RUN apk update && apk add gcompat && mkdir /data
+
+CMD [ "/run.sh" ]
diff --git a/image/image.mk b/image/image.mk
new file mode 100644
index 0000000..e8c302b
--- /dev/null
+++ b/image/image.mk
@@ -0,0 +1,17 @@
+.PHONY: image
+image: dst/image/zmp24 dst/image/Containerfile dst/image/run.sh config.json
+ cd dst/image && podman build . -t \
+ "$$(jq -r .image.name ../../config.json):$$(jq -r .image.version ../../config.json)"
+
+dst/image: dst
+ mkdir dst/image
+
+dst/image/Containerfile: dst/image image/image.dockerfile
+ ln -sf $$(pwd)/image/image.dockerfile dst/image/Containerfile
+
+dst/image/run.sh: dst/image image/run.sh
+ ln -f image/run.sh dst/image/run.sh
+ chmod +x dst/image/run.sh
+
+dst/image/zmp24: dst/image target/$(ARCH)-unknown-linux-musl/release/zmp24
+ ln -f target/$(ARCH)-unknown-linux-musl/release/zmp24 dst/image/zmp24
diff --git a/image/run.sh b/image/run.sh
new file mode 100755
index 0000000..0215ca9
--- /dev/null
+++ b/image/run.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+_term() {
+ kill -TERM "$server_pid" 2>/dev/null
+}
+
+trap _term SIGTERM
+/usr/bin/zmp24 &
+server_pid=$!
+wait "$server_pid"
diff --git a/lib/inferium/.gitignore b/lib/inferium/.gitignore
deleted file mode 100644
index a5ff07f..0000000
--- a/lib/inferium/.gitignore
+++ /dev/null
@@ -1,8 +0,0 @@
-/target
-
-
-# Added by cargo
-#
-# already existing elements were commented out
-
-#/target
diff --git a/lib/inferium/benches/client.rs b/lib/inferium/benches/client.rs
deleted file mode 100644
index 95ff914..0000000
--- a/lib/inferium/benches/client.rs
+++ /dev/null
@@ -1,60 +0,0 @@
-#![feature(test)]
-extern crate test;
-use std::collections::HashMap;
-
-use test::Bencher;
-
-extern crate inferium;
-use inferium::h1::{SyncClient, Response, ResponseHead, ProtocolVariant};
-use inferium::{Status, HeaderValue};
-use inferium::TestSyncStream;
-
-fn parse_response_sync_inner() {
- let src = "HTTP/1.1 200 OK\r\nserver: inferium\r\n\r\n".as_bytes().to_vec();
- let stream = TestSyncStream::<4>::new(&src);
- let mut client = SyncClient::>::new(stream);
- let target = Response::HeadersOnly(ResponseHead::new(
- Status::Ok,
- ProtocolVariant::HTTP1_1,
- HashMap::from([
- ("server".into(), HeaderValue::new(vec!["inferium".to_string()]))
- ])
- ));
- assert_eq!(client.receive_response().unwrap(), target);
-}
-
-fn parse_response_sync_inner_body() {
- let mut src = "HTTP/1.1 200 OK\r\ncontent-length: 50\r\n\r\n".as_bytes().to_vec();
- src.extend_from_slice(b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
- let stream = TestSyncStream::<4>::new(&src);
- let mut client = SyncClient::>::new(stream);
- let target_head = ResponseHead::new(
- Status::Ok,
- ProtocolVariant::HTTP1_1,
- HashMap::from([
- ("content-length".into(), HeaderValue::new(vec!["50".to_string()]))
- ])
- );
- let Response::WithSizedBody((h, mut b)) = client.receive_response().unwrap() else {
- panic!();
- };
- let b = b.recv_all().unwrap();
- assert_eq!(h, target_head);
- assert_eq!(b, b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
-}
-
-#[bench]
-fn parse_response_sync(b: &mut Bencher) {
- b.bytes = 37;
- b.iter(|| {
- parse_response_sync_inner();
- });
-}
-
-#[bench]
-fn parse_response_sync_with_body(b: &mut Bencher) {
- b.bytes = 39 + 13;
- b.iter(|| {
- parse_response_sync_inner_body();
- });
-}
diff --git a/lib/inferium/benches/status_code_parsing.rs b/lib/inferium/benches/status_code_parsing.rs
deleted file mode 100644
index 5d0fc18..0000000
--- a/lib/inferium/benches/status_code_parsing.rs
+++ /dev/null
@@ -1,43 +0,0 @@
-#![feature(test)]
-extern crate test;
-use test::Bencher;
-
-extern crate inferium;
-use inferium::Status;
-
-#[bench]
-fn baseline(b: &mut Bencher) {
- fn status_test_from_slice(raw: &[u8]) -> Result {
- match raw {
- b"200" => Ok(Status::Ok),
- b"404" => Ok(Status::NotFound),
- _ => Err(()),
- }
- }
-
- b.iter(|| {
- assert_eq!(status_test_from_slice(b"200".as_slice()), Ok(Status::Ok));
- });
-}
-
-#[bench]
-fn valid_ok(b: &mut Bencher) {
- b.iter(|| {
- assert_eq!(Status::try_from(b"200".as_slice()), Ok(Status::Ok));
- });
-
-}
-
-#[bench]
-fn valid_internal_server_error(b: &mut Bencher) {
- b.iter(|| {
- assert_eq!(Status::try_from(b"500".as_slice()), Ok(Status::InternalServerError));
- });
-}
-
-#[bench]
-fn invalid(b: &mut Bencher) {
- b.iter(|| {
- assert!(Status::try_from(b"690".as_slice()).is_err());
- })
-}
diff --git a/lib/inferium/examples/going_async.rs b/lib/inferium/examples/going_async.rs
deleted file mode 100644
index 946b6e6..0000000
--- a/lib/inferium/examples/going_async.rs
+++ /dev/null
@@ -1,94 +0,0 @@
-// This is an async port of `examples/simple_server.rs`. Please see that example first.
-//
-// Features `async` and `tokio-net` must be enabled for this example to compile.
-// It is also possible to enable feature `full` (which will enable all the features).
-
-use std::{collections::HashMap, net::SocketAddr};
-use tokio::net::{TcpListener, TcpStream};
-use inferium::{
- h1::{ProtocolVariant, Request, ResponseHead, ServerSendError, AsyncServer},
- HeaderKey, Method, Status, TokioInet
-};
-
-#[tokio::main]
-async fn main() {
- let listener = TcpListener::bind("localhost:8080").await.unwrap();
- loop {
- let (conn, addr) = listener.accept().await.unwrap();
- // Here we are creating a new asynchronous task for every client.
- // This will fork off in an asynchronous manner and won't block our accept loop.
- tokio::task::spawn(async move {
- // We created this new async block, so we need to `.await` on this function to propagate
- // the future from the function to the top of the spawned task (this async block).
- handle_client(conn, addr).await;
- });
- // You can now handle multiple clients at once... congratulations.
- }
-}
-
-async fn handle_client(conn: TcpStream, addr: SocketAddr) {
- println!("connection from {addr:?}");
- let mut server_handler = AsyncServer::::new(TokioInet::new(conn));
- // When receiving or sending - we call the same functions with `.await` appended (in an async
- // context). This will automatically poll the returned future from the top of the context.
- // The polling is handled by tokio here - so we don't need to worry about it.
- while let Ok(request) = server_handler.receive_request().await {
- match handle_request(request, addr) {
- Ok((h, b)) => if let Err(_) = send_response(h, b, &mut server_handler).await { break; },
- Err(()) => break,
- }
- };
- println!("ended connection for {addr:?}");
-}
-
-fn handle_request(
- req: Request, addr: SocketAddr
-) -> Result<(ResponseHead, &'static [u8]), ()> {
- let Request::HeadersOnly(headers) = req else {
- return Err(());
- };
-
- println!("req from {addr:?}: {headers}");
-
- const OK_RESPONSE: &[u8] = b"
-
-
-
-
-";
-
- Ok(match (headers.method(), headers.uri().path()) {
- (&Method::GET, "/") => (ResponseHead::new(
- Status::Ok,
- ProtocolVariant::HTTP1_0,
- HashMap::from([
- (HeaderKey::SERVER, "inferium".parse().unwrap()),
- (HeaderKey::CONTENT_LENGTH, OK_RESPONSE.len().into()),
- ])
- ), OK_RESPONSE),
-
- _ => (ResponseHead::new(
- Status::NotFound,
- ProtocolVariant::HTTP1_0,
- HashMap::from([
- (HeaderKey::SERVER, "inferium".parse().unwrap()),
- (HeaderKey::CONTENT_LENGTH, NOT_FOUND_RESPONSE.len().into()),
- ])
- ), NOT_FOUND_RESPONSE),
- })
-}
-
-async fn send_response(
- response: ResponseHead, body: &[u8], conn: &mut AsyncServer
-)-> Result<(), ServerSendError> {
- conn.send_response(&response).await?;
- conn.send_body_bytes(body).await.map_err(|e| e.try_into().unwrap())
-}
diff --git a/lib/inferium/examples/https_client.rs b/lib/inferium/examples/https_client.rs
deleted file mode 100644
index 7d6224c..0000000
--- a/lib/inferium/examples/https_client.rs
+++ /dev/null
@@ -1,88 +0,0 @@
-// This is a port of a client from `examples/start_here.rs`. Please see that example first.
-// Also... maybe brush up on some async tasks since we are going to need them here (there is an
-// example on async with inferium in `examples/going_async.rs`).
-//
-// Features `async`, `tokio-net` and `webpki-roots` dependency must be enabled for this example to
-// compile. We recommend enabling the `dev` feature when running this example.
-
-use std::{collections::HashMap, sync::Arc};
-use tokio::net::TcpStream;
-use tokio_rustls::{
- rustls::{
- pki_types::ServerName,
- ClientConfig,
- RootCertStore
- },
- TlsConnector,
- TlsStream
-};
-use inferium::{
- h1::{
- ProtocolVariant,
- RequestHead,
- Response,
- AsyncClient
- },
- HeaderKey,
- Method,
- TokioRustls
-};
-
-async fn run_tls_handshake(raw_stream: TcpStream) -> TlsStream {
- let mut root_certs = RootCertStore::empty();
- root_certs.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
- let config = ClientConfig::builder()
- .with_root_certificates(root_certs)
- .with_no_client_auth();
- let connector = TlsConnector::from(Arc::new(config));
- let verify_server_name = ServerName::try_from("zumepro.cz").unwrap();
- TlsStream::Client(connector.connect(verify_server_name, raw_stream).await.unwrap())
-}
-
-#[tokio::main]
-async fn main() {
- let stream = TcpStream::connect("zumepro.cz:443").await.unwrap();
- let stream = run_tls_handshake(stream).await;
- let conn = TokioRustls::new(stream);
- let mut client = AsyncClient::::new(conn);
-
- let to_send = RequestHead::new(
- Method::GET, "/".parse().unwrap(), ProtocolVariant::HTTP1_1,
- HashMap::from([
- (HeaderKey::USER_AGENT, "Mozilla/5.0 (inferium)".parse().unwrap()),
- (HeaderKey::HOST, "zumepro.cz".parse().unwrap()),
- (HeaderKey::CONNECTION, "close".parse().unwrap())
- ])
- );
- println!("----------> Sending\n\n{to_send}\n");
- client.send_request(&to_send).await.unwrap();
- let response = client.receive_response().await.unwrap();
-
- let (header, body) = match response {
- Response::HeadersOnly(h) => (h, None),
- Response::WithSizedBody((_, _)) => panic!(),
- Response::WithChunkedBody((h, b)) => (h, Some(b)),
- };
-
- println!("----------< Received\n\n{header}\n");
-
- if let Some(mut body) = body {
-
- // Since our zumepro server sends bodies chunked - we will need to handle it.
- // This simple loop just collects all the chunks into the res vector.
- let mut res = Vec::new();
- while let Some(mut chunk) = body.get_chunk_async().await.unwrap() {
- // Now here is a difference between sync and async.
- //
- // For easy body manipulation and no redundant trait pollution, a body within an
- // asynchronous stream can be sent/received using the same methods as a synchronous one,
- // but with the suffix `_async`.
- res.append(&mut chunk.recv_all_async().await.unwrap());
- }
-
- println!(
- "----------< Body\n\n{:?}\n",
- std::str::from_utf8(&res).unwrap()
- );
- }
-}
diff --git a/lib/inferium/examples/simple_server.rs b/lib/inferium/examples/simple_server.rs
deleted file mode 100644
index ddbb821..0000000
--- a/lib/inferium/examples/simple_server.rs
+++ /dev/null
@@ -1,84 +0,0 @@
-use std::{collections::HashMap, net::{SocketAddr, TcpListener}};
-use inferium::{
- h1::{ProtocolVariant, Request, ResponseHead, ServerSendError, SyncServer},
- HeaderKey, Method, Status, StdInet
-};
-
-fn main() {
- let listener = TcpListener::bind("localhost:8080").unwrap();
- loop {
- let (conn, addr) = listener.accept().unwrap();
- println!("connection from {addr:?}");
- let mut server_handler = SyncServer::::new(StdInet::new(conn));
- // We'll serve the client as long as it sends valid requests.
- // Note that this will effectively block other clients.
- while let Ok(request) = server_handler.receive_request() {
- // This matching is here to provide a way of controlling the while loop.
- match handle_request(request, addr) {
- Ok((h, b)) => if let Err(_) = send_response(h, b, &mut server_handler) { break; },
- Err(()) => break,
- }
- };
- println!("ended connection for {addr:?}");
- }
-}
-
-fn handle_request(
- req: Request, addr: SocketAddr
-) -> Result<(ResponseHead, &'static [u8]), ()> {
- let Request::HeadersOnly(headers) = req else {
- // We will not handle POST requests with bodies - so let's tell the client to f*ck off.
- return Err(());
- };
-
- println!("req from {addr:?}: {headers}");
-
- const OK_RESPONSE: &[u8] = b"
-
-
-
-
-";
-
- // The URI can contain both path and parameters - so we're just getting the path here.
- Ok(match (headers.method(), headers.uri().path()) {
- // The ok response with our index page
- (&Method::GET, "/") => (ResponseHead::new(
- Status::Ok,
- ProtocolVariant::HTTP1_0,
- HashMap::from([
- (HeaderKey::SERVER, "inferium".parse().unwrap()),
- (HeaderKey::CONTENT_LENGTH, OK_RESPONSE.len().into()),
- ])
- ), OK_RESPONSE),
-
- // The not found response with an example not found page
- _ => (ResponseHead::new(
- Status::NotFound,
- ProtocolVariant::HTTP1_0,
- HashMap::from([
- (HeaderKey::SERVER, "inferium".parse().unwrap()),
- (HeaderKey::CONTENT_LENGTH, NOT_FOUND_RESPONSE.len().into()),
- ])
- ), NOT_FOUND_RESPONSE),
- })
-}
-
-fn send_response(
- response: ResponseHead, body: &[u8], conn: &mut SyncServer
-)-> Result<(), ServerSendError> {
- conn.send_response(&response)?;
- // The send body can fail on an I/O error or if the content-length header does not match the
- // actual sent length in this scenario. But we know that we have the correct length so with
- // `.try_into().unwrap()` we tell inferium to convert the error and panic on (not so much)
- // possible body length discrepancy.
- conn.send_body_bytes(body).map_err(|e| e.try_into().unwrap())
-}
diff --git a/lib/inferium/examples/start_here.rs b/lib/inferium/examples/start_here.rs
deleted file mode 100644
index 2ce5dd2..0000000
--- a/lib/inferium/examples/start_here.rs
+++ /dev/null
@@ -1,101 +0,0 @@
-// Hello, and welcome to inferium. A performance-oriented small HTTP library written in Rust that
-// keeps you (the user) in charge.
-
-// Let's first import some necessary things.
-
-// In inferium - HashMaps are used to store uri parameters and headers.
-use std::collections::HashMap;
-// TcpStream is needed if we want to connect to the internet.
-use std::net::TcpStream;
-
-use inferium::{
- // The h1 module contains all the protocol specific things for HTTP/1.(0/1).
- h1::{
- // ProtocolVariant contains variants with the protocol versions supported in this module:
- // - HTTP/1.1
- // - HTTP/1.0
- ProtocolVariant,
- // RequestHead contains headers and the HTTP request headline (method, path, protocol).
- RequestHead,
- // Response here is a wrapper for a response that can have the following:
- // - Headers only
- // - Headers and body (with a known length or chunked)
- //
- // ! The body in the response is not yet collected. It is up to you if you wish to discard
- // the connection or receive and collect the response body into some structure.
- //
- // The same things here go for the Request object which is nearly the same except that the
- // headline contains protocol and status instead.
- Response,
- // Sync client is a stream wrapper that helps us keep track of the open connection and
- // perform request/response operations.
- //
- // The server equivalent is SyncServer.
- SyncClient
- },
- // Header key contains various known header keys, but can also store arbitrary (unknown) header
- // key in the OTHER variant.
- HeaderKey,
- Method,
- // StdInet here is a stream wrapper that allows the TcpStream to be used by inferium. There is
- // also a unix socket equivalent and some asynchronous io wrappers.
- StdInet
-};
-
-fn main() {
- // Let's first create a connection... nothing weird here.
- let conn = StdInet::new(TcpStream::connect("zumepro.cz:80").unwrap());
- // And a client...
- let mut client = SyncClient::::new(conn);
-
- // Now let's create a request to send
- let to_send = RequestHead::new(
- // The path here is parsed into an HTTP path object (which also supports parameters)
- // I'm using HTTP/1.0 in this example as HTTP/1.1 automatically infers a compatibility with
- // chunked encoding (which I'm not even trying to handle here).
- Method::GET, "/".parse().unwrap(), ProtocolVariant::HTTP1_0,
- HashMap::from([
- // All headers are HeaderKey - HeaderValue pairs. We can parse the header value into
- // the desired object.
- //
- // Constructing arbitrary header key is supported using the OTHER variant - however
- // it's not recommended as a violation of the HTTP protocol can happen.
- //
- // If you really want to construct an arbitrary header key - please carefully check
- // that all of the symbols are valid.
- (HeaderKey::USER_AGENT, "Mozilla/5.0 (inferium)".parse().unwrap()),
- (HeaderKey::HOST, "zumepro.cz".parse().unwrap()),
- ])
- );
- println!("----------> Sending\n\n{to_send}\n");
- // Let's send the request - this is pretty straightforward.
- client.send_request(&to_send).unwrap();
- // As is receiving a response.
- let response = client.receive_response().unwrap();
-
- // Now (as we discussed earlier) - the response can have a body.
- // In this example we'll try to handle a basic body with a known size.
- let (header, body) = match response {
- // Extracting the headers if no body is present.
- Response::HeadersOnly(h) => (h, None),
- // Extracting both the headers and the body if body is present.
- Response::WithSizedBody((h, b)) => (h, Some(b)),
- // We will not handle chunked responses in this example.
- Response::WithChunkedBody((_, _)) => panic!(),
- };
-
- // inferium kindly provides a simple way to print the head of a request/response.
- // It will be formatted pretty close to the actual protocol plaintext representation.
- println!("----------< Received\n\n{header}\n");
-
- // And finally... if we have a body, we'll print it.
- if let Some(mut body) = body {
- println!(
- "----------< Body\n\n{:?}\n",
- // A body is always returned in bytes. It's up to you to decode it however you see fit.
- std::str::from_utf8(&mut body.recv_all().unwrap()).unwrap()
- );
- }
-
- // And you're done. Come on... try to run it.
-}
diff --git a/lib/inferium/src/headers.rs b/lib/inferium/src/headers.rs
index a19ad85..ad50873 100644
--- a/lib/inferium/src/headers.rs
+++ b/lib/inferium/src/headers.rs
@@ -158,6 +158,14 @@ impl HeaderValue {
self.inner.push(val);
}
+ /// Create a new unchecked value.
+ /// This is faster, but can cause protocol violation.
+ /// Please avoid putting unchecked content here.
+ #[inline]
+ pub fn new_unchecked(inner: String) -> Self {
+ Self { inner: vec![inner] }
+ }
+
/// Query the first entry for this header key.
///
/// See the documentation of [`HeaderValue`] for more information.
diff --git a/lib/inferium/src/lib.rs b/lib/inferium/src/lib.rs
index b6c8930..31ef4db 100644
--- a/lib/inferium/src/lib.rs
+++ b/lib/inferium/src/lib.rs
@@ -1,3 +1,5 @@
+#![allow(dead_code)]
+
pub mod settings;
mod io;
diff --git a/lib/inferium/src/proto/h1/exports.rs b/lib/inferium/src/proto/h1/exports.rs
index 6afb61c..3dbc4ba 100644
--- a/lib/inferium/src/proto/h1/exports.rs
+++ b/lib/inferium/src/proto/h1/exports.rs
@@ -322,6 +322,28 @@ pub enum Request<'a, T> {
WithChunkedBody((RequestHead, Incoming<'a, ChunkedIn<'a, PrependableStream>>)),
}
+impl Response<'_, T> {
+ #[inline]
+ pub fn head(&self) -> &ResponseHead {
+ match self {
+ Self::HeadersOnly(h) => h,
+ Self::WithSizedBody((h, _)) => h,
+ Self::WithChunkedBody((h, _)) => h,
+ }
+ }
+}
+
+impl Request<'_, T> {
+ #[inline]
+ pub fn head(&self) -> &RequestHead {
+ match self {
+ Self::HeadersOnly(h) => h,
+ Self::WithSizedBody((h, _)) => h,
+ Self::WithChunkedBody((h, _)) => h,
+ }
+ }
+}
+
fn get_outgoing_req_content_length(head: &RequestHead) -> Result