1use crate::error::{Error, Result};
4use crate::headers::Headers;
5use crate::url::Url;
6use crate::version::HttpVersion;
7use bytes::Bytes;
8use futures_core::Stream;
9use http::Method;
10use std::fmt;
11use std::pin::Pin;
12use std::time::Duration;
13
14pub trait IntoUrl {
16 fn into_url(self) -> Result<Url>;
17}
18
19impl IntoUrl for Url {
20 fn into_url(self) -> Result<Url> {
21 Ok(self)
22 }
23}
24
25impl IntoUrl for &Url {
26 fn into_url(self) -> Result<Url> {
27 Ok(self.clone())
28 }
29}
30
31impl IntoUrl for &str {
32 fn into_url(self) -> Result<Url> {
33 Url::parse(self).map_err(Error::from)
34 }
35}
36
37impl IntoUrl for String {
38 fn into_url(self) -> Result<Url> {
39 Url::parse(&self).map_err(Error::from)
40 }
41}
42
43impl IntoUrl for &String {
44 fn into_url(self) -> Result<Url> {
45 Url::parse(self).map_err(Error::from)
46 }
47}
48
49impl IntoUrl for http::Uri {
50 fn into_url(self) -> Result<Url> {
51 Url::parse(&self.to_string()).map_err(Error::from)
52 }
53}
54
55impl IntoUrl for &http::Uri {
56 fn into_url(self) -> Result<Url> {
57 Url::parse(&self.to_string()).map_err(Error::from)
58 }
59}
60
61pub type RequestBodyStream =
63 Pin<Box<dyn Stream<Item = std::result::Result<Bytes, Error>> + Send + 'static>>;
64
65#[derive(Default)]
72pub enum RequestBody {
73 #[default]
74 Empty,
75 Bytes(Bytes),
76 Text(String),
77 Json(Vec<u8>),
78 Form(String),
79 Stream {
80 stream: RequestBodyStream,
81 content_length: Option<u64>,
82 },
83}
84
85impl RequestBody {
86 pub fn empty() -> Self {
87 RequestBody::Empty
88 }
89
90 pub fn is_empty(&self) -> bool {
91 matches!(self, RequestBody::Empty)
92 }
93
94 pub fn is_streaming(&self) -> bool {
96 matches!(self, RequestBody::Stream { .. })
97 }
98
99 pub fn content_length(&self) -> Option<u64> {
102 match self {
103 RequestBody::Empty => Some(0),
104 RequestBody::Bytes(b) => Some(b.len() as u64),
105 RequestBody::Text(t) => Some(t.len() as u64),
106 RequestBody::Json(b) => Some(b.len() as u64),
107 RequestBody::Form(t) => Some(t.len() as u64),
108 RequestBody::Stream { content_length, .. } => *content_length,
109 }
110 }
111
112 pub fn into_bytes(self) -> Result<Bytes> {
115 Ok(match self {
116 RequestBody::Empty => Bytes::new(),
117 RequestBody::Bytes(bytes) => bytes,
118 RequestBody::Text(text) => Bytes::from(text.into_bytes()),
119 RequestBody::Json(bytes) => Bytes::from(bytes),
120 RequestBody::Form(text) => Bytes::from(text.into_bytes()),
121 RequestBody::Stream { .. } => {
122 return Err(Error::HttpProtocol(
123 "streaming RequestBody cannot be materialized; use the streaming send path"
124 .into(),
125 ));
126 }
127 })
128 }
129}
130
131impl fmt::Debug for RequestBody {
132 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133 match self {
134 RequestBody::Empty => f.debug_struct("RequestBody::Empty").finish(),
135 RequestBody::Bytes(b) => f
136 .debug_struct("RequestBody::Bytes")
137 .field("len", &b.len())
138 .finish(),
139 RequestBody::Text(t) => f
140 .debug_struct("RequestBody::Text")
141 .field("len", &t.len())
142 .finish(),
143 RequestBody::Json(b) => f
144 .debug_struct("RequestBody::Json")
145 .field("len", &b.len())
146 .finish(),
147 RequestBody::Form(t) => f
148 .debug_struct("RequestBody::Form")
149 .field("len", &t.len())
150 .finish(),
151 RequestBody::Stream { content_length, .. } => f
152 .debug_struct("RequestBody::Stream")
153 .field("content_length", content_length)
154 .finish(),
155 }
156 }
157}
158
159impl Clone for RequestBody {
160 fn clone(&self) -> Self {
161 match self {
162 RequestBody::Empty => RequestBody::Empty,
163 RequestBody::Bytes(b) => RequestBody::Bytes(b.clone()),
164 RequestBody::Text(t) => RequestBody::Text(t.clone()),
165 RequestBody::Json(b) => RequestBody::Json(b.clone()),
166 RequestBody::Form(t) => RequestBody::Form(t.clone()),
167 RequestBody::Stream { .. } => {
168 panic!("RequestBody::Stream cannot be cloned or replayed")
169 }
170 }
171 }
172}
173
174impl From<Bytes> for RequestBody {
175 fn from(value: Bytes) -> Self {
176 RequestBody::Bytes(value)
177 }
178}
179
180impl From<Vec<u8>> for RequestBody {
181 fn from(value: Vec<u8>) -> Self {
182 RequestBody::Bytes(Bytes::from(value))
183 }
184}
185
186impl From<&[u8]> for RequestBody {
187 fn from(value: &[u8]) -> Self {
188 RequestBody::Bytes(Bytes::copy_from_slice(value))
189 }
190}
191
192impl From<String> for RequestBody {
193 fn from(value: String) -> Self {
194 RequestBody::Text(value)
195 }
196}
197
198impl From<&str> for RequestBody {
199 fn from(value: &str) -> Self {
200 RequestBody::Text(value.to_string())
201 }
202}
203
204#[derive(Clone, Debug)]
206pub struct Request {
207 pub(crate) method: Method,
208 pub(crate) url: Url,
209 pub(crate) headers: Headers,
210 pub(crate) body: RequestBody,
211 pub(crate) version: Option<HttpVersion>,
212 pub(crate) timeout: Option<Duration>,
213}
214
215impl Request {
216 pub fn new(method: Method, url: Url) -> Self {
217 Self {
218 method,
219 url,
220 headers: Headers::new(),
221 body: RequestBody::Empty,
222 version: None,
223 timeout: None,
224 }
225 }
226
227 pub fn method(&self) -> &Method {
228 &self.method
229 }
230
231 pub fn url(&self) -> &Url {
232 &self.url
233 }
234
235 pub fn headers(&self) -> &Headers {
236 &self.headers
237 }
238
239 pub fn body(&self) -> &RequestBody {
240 &self.body
241 }
242
243 pub fn version(&self) -> Option<HttpVersion> {
244 self.version
245 }
246
247 pub fn timeout(&self) -> Option<Duration> {
248 self.timeout
249 }
250}
251
252#[derive(Clone, Debug, Default)]
254pub enum RedirectPolicy {
255 #[default]
256 None,
257 Limited(u32),
258}