aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFuwn <[email protected]>2025-06-05 13:43:23 +0000
committerFuwn <[email protected]>2025-06-05 13:43:27 +0000
commit878978d9a5acb4fec7f7f98e491cf5da3963b572 (patch)
tree215334288dbf1458f6c0ba9cd83a22430efcab93
parentdocs(Cargo): Add MSRV (diff)
downloadgerm-878978d9a5acb4fec7f7f98e491cf5da3963b572.tar.xz
germ-878978d9a5acb4fec7f7f98e491cf5da3963b572.zip
feat(response): Expose content bytes
-rw-r--r--src/request/response.rs58
1 files changed, 31 insertions, 27 deletions
diff --git a/src/request/response.rs b/src/request/response.rs
index ef4b515..d38e0bd 100644
--- a/src/request/response.rs
+++ b/src/request/response.rs
@@ -16,46 +16,38 @@
// Copyright (C) 2022-2025 Fuwn <[email protected]>
// SPDX-License-Identifier: GPL-3.0-only
-use {
- crate::request::Status,
- rustls::SupportedCipherSuite,
- std::{borrow::Cow, fmt::Write},
-};
+use {crate::request::Status, rustls::SupportedCipherSuite, std::borrow::Cow};
#[derive(Debug, Clone, PartialEq)]
pub struct Response {
status: Status,
meta: String,
- content: Option<String>,
+ content: Option<Vec<u8>>,
size: usize,
suite: Option<SupportedCipherSuite>,
}
impl Response {
pub(crate) fn new(data: &[u8], suite: Option<SupportedCipherSuite>) -> Self {
- let string_form = String::from_utf8_lossy(data).to_string();
- let mut content = None;
- let header;
-
- if string_form.ends_with("\r\n") {
- header = string_form;
+ let delimiter = b"\r\n";
+ let header_end = data
+ .windows(delimiter.len())
+ .position(|window| window == delimiter)
+ .map_or(data.len(), |pos| pos + delimiter.len());
+ let header_bytes = &data[..header_end];
+ let header = String::from_utf8_lossy(header_bytes).trim_end().to_string();
+ let content_bytes = if header_end < data.len() {
+ Some(data[header_end..].to_vec())
} else {
- let mut string_split = string_form.split("\r\n");
-
- header = string_split.next().unwrap_or("").to_string();
- content = Some(string_split.fold(String::new(), |mut output, s| {
- let _ = write!(output, "{s}\r\n");
-
- output
- }));
- }
-
- let header_split = header.split_at(2);
+ None
+ };
+ let (status_string, meta_string) = header.split_at(2);
+ let status_code = status_string.parse::<i32>().unwrap_or(0);
Self {
- status: Status::from(header_split.0.parse::<i32>().unwrap_or(0)),
- meta: header_split.1.trim_start().to_string(),
- content,
+ status: Status::from(status_code),
+ meta: meta_string.trim_start().to_string(),
+ content: content_bytes,
size: data.len(),
suite,
}
@@ -68,8 +60,20 @@ impl Response {
#[must_use]
pub fn meta(&self) -> Cow<'_, str> { Cow::Borrowed(&self.meta) }
+ /// This associated function assumes that the content is valid UTF-8.
+ ///
+ /// If you want to handle data bytes directly, use
+ /// [`Response::content_bytes`].
+ #[must_use]
+ pub fn content(&self) -> Option<String> {
+ self
+ .content
+ .as_ref()
+ .map(|content| String::from_utf8_lossy(content).to_string())
+ }
+
#[must_use]
- pub const fn content(&self) -> &Option<String> { &self.content }
+ pub fn content_bytes(&self) -> Option<&[u8]> { self.content.as_deref() }
#[must_use]
pub const fn size(&self) -> &usize { &self.size }