diff --git a/Cargo.lock b/Cargo.lock
index 501cc9f..b8a3bb1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -314,9 +314,9 @@ dependencies = [
[[package]]
name = "url"
-version = "2.5.3"
+version = "2.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada"
+checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
dependencies = [
"form_urlencoded",
"idna",
diff --git a/Cargo.toml b/Cargo.toml
index db2a4c7..03d46c1 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,9 +12,11 @@ repository = "https://github.com/algesten/ureq-proto"
rust-version = "1.67"
[features]
+default = ["std"]
+std = []
[dependencies]
http = { version = "1.1.0", default-features = false, features = ["std"] }
httparse = { version = "1.8.0", default-features = false }
log = "0.4.22"
-url = "2.5.2"
+url = "2.5.4"
diff --git a/src/body.rs b/src/body.rs
index c72a08c..4ec68c8 100644
--- a/src/body.rs
+++ b/src/body.rs
@@ -1,5 +1,6 @@
-use std::fmt;
-use std::io::Write;
+use alloc::string::ToString;
+use core::fmt;
+use core::fmt::Write;
use http::{HeaderName, HeaderValue, Method};
diff --git a/src/chunk.rs b/src/chunk.rs
index d14eb92..552f933 100644
--- a/src/chunk.rs
+++ b/src/chunk.rs
@@ -178,6 +178,8 @@ impl Dechunker {
#[cfg(test)]
mod test {
+ use alloc::string::String;
+
use super::*;
#[test]
diff --git a/src/client/amended.rs b/src/client/amended.rs
index a73042a..116db3d 100644
--- a/src/client/amended.rs
+++ b/src/client/amended.rs
@@ -1,4 +1,5 @@
-use std::mem;
+use alloc::string::ToString;
+use core::mem;
use http::{HeaderMap, HeaderName, HeaderValue, Method, Request, Uri, Version};
use url::Url;
@@ -143,7 +144,7 @@ impl
AmendedRequest {
}
#[cfg(test)]
- pub fn headers_vec(&self) -> Vec<(&str, &str)> {
+ pub fn headers_vec(&self) -> alloc::vec::Vec<(&str, &str)> {
self.headers()
// unwrap here is ok because the tests using this method should
// only use header values representable as utf-8.
diff --git a/src/client/call.rs b/src/client/call.rs
index 14fac46..2180783 100644
--- a/src/client/call.rs
+++ b/src/client/call.rs
@@ -1,8 +1,9 @@
//! A single request-response. No redirection or other logic.
-use std::fmt;
-use std::io::Write;
-use std::marker::PhantomData;
+use alloc::string::ToString;
+use core::fmt;
+use core::fmt::Write;
+use core::marker::PhantomData;
use http::{HeaderName, HeaderValue, Method, Request, Response, StatusCode, Version};
@@ -643,7 +644,8 @@ impl fmt::Debug for Phase {
mod test {
use super::*;
- use std::str;
+ use alloc::vec;
+ use core::str;
use http::{Method, Request};
diff --git a/src/client/flow.rs b/src/client/flow.rs
index ab4700e..fc82dd0 100644
--- a/src/client/flow.rs
+++ b/src/client/flow.rs
@@ -1,12 +1,13 @@
//! A sequence of calls, such as following redirects.
-use std::fmt;
-use std::marker::PhantomData;
+use alloc::string::String;
+use alloc::string::ToString;
+use core::fmt;
+use core::marker::PhantomData;
use http::uri::Scheme;
-use http::{
- HeaderMap, HeaderName, HeaderValue, Method, Request, Response, StatusCode, Uri, Version,
-};
+use http::{HeaderMap, HeaderName, HeaderValue, Method, Request};
+use http::{Response, StatusCode, Uri, Version};
use crate::body::calculate_max_input;
use crate::ext::{HeaderIterExt, MethodExt, StatusExt};
diff --git a/src/client/holder.rs b/src/client/holder.rs
index 83e3cb5..7881448 100644
--- a/src/client/holder.rs
+++ b/src/client/holder.rs
@@ -1,4 +1,4 @@
-use std::mem;
+use core::mem;
use http::Request;
diff --git a/src/client/test/mod.rs b/src/client/test/mod.rs
index f32d42c..628836a 100644
--- a/src/client/test/mod.rs
+++ b/src/client/test/mod.rs
@@ -22,6 +22,6 @@ trait TestSliceExt {
impl TestSliceExt for [u8] {
fn as_str(&self) -> &str {
- std::str::from_utf8(self).unwrap()
+ core::str::from_utf8(self).unwrap()
}
}
diff --git a/src/client/test/scenario.rs b/src/client/test/scenario.rs
index 8257c2b..959b070 100644
--- a/src/client/test/scenario.rs
+++ b/src/client/test/scenario.rs
@@ -1,5 +1,14 @@
-use std::io::Write;
-use std::marker::PhantomData;
+use core::fmt::Write;
+// use std::io::Write;
+use core::marker::PhantomData;
+
+// use std::prelude::rust_2021::*;
+// use std::vec;
+
+use alloc::string::String;
+use alloc::string::ToString;
+use alloc::vec;
+use alloc::vec::Vec;
use http::{Method, Request, Response, StatusCode};
@@ -8,6 +17,7 @@ use crate::client::flow::state::{
};
use crate::client::flow::{Await100Result, Flow, SendRequestResult};
use crate::client::flow::{RecvBodyResult, RecvResponseResult};
+use crate::util::Writer;
pub struct Scenario {
request: Request<()>,
@@ -176,12 +186,13 @@ impl Scenario {
}
pub fn write_response(r: &Response<()>) -> Vec {
- let mut input = Vec::::new();
+ let mut input = vec![0; 1024];
+ let mut w = Writer::new(&mut input);
let s = r.status();
write!(
- &mut input,
+ &mut w,
"{:?} {} {}\r\n",
r.version(),
s.as_u16(),
@@ -190,10 +201,15 @@ pub fn write_response(r: &Response<()>) -> Vec {
.unwrap();
for (k, v) in r.headers().iter() {
- write!(&mut input, "{}: {}\r\n", k.as_str(), v.to_str().unwrap()).unwrap();
+ write!(&mut w, "{}: {}\r\n", k.as_str(), v.to_str().unwrap()).unwrap();
}
- write!(&mut input, "\r\n").unwrap();
+ write!(&mut w, "\r\n").unwrap();
+
+ let len = w.len();
+
+ drop(w);
+ input.truncate(len);
input
}
diff --git a/src/client/test/state_recv_body.rs b/src/client/test/state_recv_body.rs
index 6de6a9e..3c6bb03 100644
--- a/src/client/test/state_recv_body.rs
+++ b/src/client/test/state_recv_body.rs
@@ -1,3 +1,5 @@
+use alloc::vec;
+
use http::Response;
use crate::client::flow::CloseReason;
diff --git a/src/client/test/state_redirect.rs b/src/client/test/state_redirect.rs
index d72c946..2efb999 100644
--- a/src/client/test/state_redirect.rs
+++ b/src/client/test/state_redirect.rs
@@ -1,3 +1,6 @@
+use alloc::string::ToString;
+use alloc::vec;
+
use http::{Method, Response, StatusCode};
use crate::client::flow::RedirectAuthHeaders;
diff --git a/src/client/test/state_send_body.rs b/src/client/test/state_send_body.rs
index 1d5e092..8b19109 100644
--- a/src/client/test/state_send_body.rs
+++ b/src/client/test/state_send_body.rs
@@ -1,3 +1,5 @@
+use alloc::vec;
+
use crate::client::flow::SendRequestResult;
use super::scenario::Scenario;
diff --git a/src/client/test/state_send_request.rs b/src/client/test/state_send_request.rs
index eeb7066..62fc4c8 100644
--- a/src/client/test/state_send_request.rs
+++ b/src/client/test/state_send_request.rs
@@ -1,3 +1,5 @@
+use alloc::vec;
+
use crate::client::flow::SendRequestResult;
use crate::Error;
diff --git a/src/error.rs b/src/error.rs
index e8c6388..c2b646f 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -1,4 +1,6 @@
-use std::fmt;
+use alloc::string::String;
+use alloc::string::ToString;
+use core::fmt;
use http::{Method, Version};
@@ -42,8 +44,13 @@ impl From for Error {
}
}
+#[cfg(feature = "std")]
impl std::error::Error for Error {}
+// TODO(martin): uncomment this when we bump MSRV >= 1.81
+// #[cfg(not(feature = "std"))]
+// impl core::error::Error for Error {}
+
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
diff --git a/src/lib.rs b/src/lib.rs
index 6c05ba4..2827b99 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -4,11 +4,18 @@
//!
//!
+#![no_std]
#![forbid(unsafe_code)]
#![warn(clippy::all)]
#![allow(clippy::uninlined_format_args)]
+#![allow(clippy::needless_lifetimes)]
#![deny(missing_docs)]
+extern crate alloc;
+
+#[cfg(feature = "std")]
+extern crate std;
+
#[macro_use]
extern crate log;
diff --git a/src/util.rs b/src/util.rs
index f102203..9436d21 100644
--- a/src/util.rs
+++ b/src/util.rs
@@ -1,6 +1,5 @@
-use std::fmt;
-use std::io::{self, Cursor};
-use std::ops::{Deref, DerefMut};
+use core::fmt;
+use core::ops::{Deref, DerefMut};
pub(crate) fn find_crlf(b: &[u8]) -> Option {
let cr = b.iter().position(|c| *c == b'\r')?;
@@ -30,38 +29,56 @@ pub(crate) fn compare_lowercase_ascii(a: &str, lowercased: &str) -> bool {
true
}
-pub(crate) struct Writer<'a>(pub Cursor<&'a mut [u8]>);
+pub(crate) struct Writer<'a>(&'a mut [u8], usize);
impl<'a> Writer<'a> {
pub(crate) fn new(output: &'a mut [u8]) -> Writer<'a> {
- Self(Cursor::new(output))
+ Self(output, 0)
}
- pub fn len(&self) -> usize {
- self.0.position() as usize
+ #[inline(always)]
+ pub(crate) fn len(&self) -> usize {
+ self.1
}
- pub fn available(&self) -> usize {
- self.0.get_ref().len() - self.len()
+ #[inline(always)]
+ pub(crate) fn available(&self) -> usize {
+ self.0.len() - self.1
}
- pub(crate) fn try_write(&mut self, block: impl Fn(&mut Self) -> io::Result<()>) -> bool {
- let pos = self.0.position();
+ pub(crate) fn try_write(
+ &mut self,
+ block: impl Fn(&mut Self) -> Result<(), fmt::Error>,
+ ) -> bool {
+ let pos = self.1;
let success = (block)(self).is_ok();
if !success {
- self.0.set_position(pos);
+ self.1 = pos;
}
success
}
-}
-impl<'a> io::Write for Writer<'a> {
- fn write(&mut self, buf: &[u8]) -> io::Result {
- self.0.write(buf)
+ #[inline(always)]
+ pub(crate) fn write_all(&mut self, to_write: &[u8]) -> Result<(), fmt::Error> {
+ let len = to_write.len();
+
+ if len > self.available() {
+ return Err(fmt::Error);
+ }
+
+ let buf = &mut self.0[self.1..];
+ buf[..len].copy_from_slice(to_write);
+
+ self.1 += len;
+
+ Ok(())
}
+}
- fn flush(&mut self) -> io::Result<()> {
- self.0.flush()
+impl<'a> fmt::Write for Writer<'a> {
+ fn write_str(&mut self, s: &str) -> fmt::Result {
+ let bytes = s.as_bytes();
+ self.write_all(bytes)
}
}
@@ -70,7 +87,7 @@ const CHARS_PER_ROW: usize = 16;
impl<'a> Drop for Writer<'a> {
fn drop(&mut self) {
let len = self.len();
- log_data(&self.0.get_ref()[..len]);
+ log_data(&self.0[..len]);
}
}
@@ -156,7 +173,7 @@ impl ArrayVec {
pub fn from_fn(cb: impl FnMut(usize) -> T) -> Self {
Self {
len: 0,
- arr: std::array::from_fn(cb),
+ arr: core::array::from_fn(cb),
}
}