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), } }