use alloc::{format, vec::Vec};
use crate::{
rfc9110::{
chars::{CRLF, CRLF_CRLF, SP},
headers::{CONTENT_LENGTH, HOST},
request::HttpRequest,
},
rfc9112::version::HTTP_11,
};
impl HttpRequest {
pub fn to_http_11_vec(&self) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.extend(self.method.as_bytes());
bytes.push(SP);
bytes.extend(self.url.path().as_bytes());
if let Some(q) = self.url.query() {
bytes.extend(b"?");
bytes.extend(q.as_bytes());
}
bytes.push(SP);
bytes.extend(HTTP_11.as_bytes());
bytes.extend(CRLF);
let has_host = self
.headers
.iter()
.any(|(k, _)| k.eq_ignore_ascii_case(HOST));
if !has_host {
if let Some(host) = self.url.host_str() {
bytes.extend(HOST.as_bytes());
bytes.extend(b": ");
bytes.extend(host.as_bytes());
if let Some(port) = self.url.port() {
bytes.extend(format!(":{port}").as_bytes());
}
bytes.extend(CRLF);
}
}
for (key, val) in &self.headers {
if key.eq_ignore_ascii_case(CONTENT_LENGTH) {
continue;
}
bytes.extend(key.as_bytes());
bytes.extend(b": ");
bytes.extend(val.as_bytes());
bytes.extend(CRLF);
}
let body_len = format!("{}", self.body.len());
bytes.extend(CONTENT_LENGTH.as_bytes());
bytes.extend(b": ");
bytes.extend(body_len.as_bytes());
bytes.extend(CRLF_CRLF);
bytes.extend(&self.body);
bytes
}
}
#[cfg(test)]
mod tests {
use alloc::string::String;
use url::Url;
use crate::rfc9110::request::HttpRequest;
fn serialize(req: &HttpRequest) -> String {
String::from_utf8(req.to_http_11_vec()).unwrap()
}
#[test]
fn host_generated_from_url() {
let req = HttpRequest::get(Url::parse("https://example.com/x").unwrap());
assert!(serialize(&req).contains("host: example.com\r\n"));
}
#[test]
fn host_keeps_non_default_port() {
let req = HttpRequest::get(Url::parse("https://example.com:8843/").unwrap());
assert!(serialize(&req).contains("host: example.com:8843\r\n"));
}
#[test]
fn host_omits_default_port() {
let req = HttpRequest::get(Url::parse("https://example.com:443/").unwrap());
assert!(serialize(&req).contains("host: example.com\r\n"));
}
#[test]
fn host_not_duplicated_when_set_by_caller() {
let req = HttpRequest::get(Url::parse("https://example.com/").unwrap())
.header("Host", "override.example");
let message = serialize(&req);
assert!(message.contains("Host: override.example\r\n"));
assert!(!message.contains("host: example.com\r\n"));
}
}