1 // Copyright (c) 2023 Huawei Device Co., Ltd.
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 //     http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
14 //! `ylong_http_client` `Request` adapter.
15 
16 use core::ops::{Deref, DerefMut};
17 use core::pin::Pin;
18 use core::task::{Context, Poll};
19 use std::io::Cursor;
20 use std::sync::Arc;
21 
22 use ylong_http::body::async_impl::ReusableReader;
23 use ylong_http::body::MultiPartBase;
24 use ylong_http::request::uri::PercentEncoder as PerEncoder;
25 use ylong_http::request::{Request as Req, RequestBuilder as ReqBuilder};
26 
27 use crate::async_impl::interceptor::Interceptors;
28 use crate::error::{ErrorKind, HttpClientError};
29 use crate::runtime::{AsyncRead, ReadBuf};
30 use crate::util::request::RequestArc;
31 
32 /// A structure that represents an HTTP `Request`. It contains a request line,
33 /// some HTTP headers and a HTTP body.
34 ///
35 /// An HTTP request is made by a client, to a named host, which is located on a
36 /// server. The aim of the request is to access a resource on the server.
37 ///
38 /// This structure is based on `ylong_http::Request<T>`.
39 ///
40 /// # Examples
41 ///
42 /// ```
43 /// use ylong_http_client::async_impl::{Body, Request};
44 ///
45 /// let request = Request::builder().body(Body::empty());
46 /// ```
47 pub struct Request {
48     pub(crate) inner: Req<Body>,
49 }
50 
51 impl Request {
52     /// Creates a new, default `RequestBuilder`.
53     ///
54     /// # Default
55     ///
56     /// - The method of this `RequestBuilder` is `GET`.
57     /// - The URL of this `RequestBuilder` is `/`.
58     /// - The HTTP version of this `RequestBuilder` is `HTTP/1.1`.
59     /// - No headers are in this `RequestBuilder`.
60     ///
61     /// # Examples
62     ///
63     /// ```
64     /// use ylong_http_client::async_impl::Request;
65     ///
66     /// let builder = Request::builder();
67     /// ```
builder() -> RequestBuilder68     pub fn builder() -> RequestBuilder {
69         RequestBuilder::new()
70     }
71 }
72 
73 impl Deref for Request {
74     type Target = Req<Body>;
75 
deref(&self) -> &Self::Target76     fn deref(&self) -> &Self::Target {
77         &self.inner
78     }
79 }
80 
81 impl DerefMut for Request {
deref_mut(&mut self) -> &mut Self::Target82     fn deref_mut(&mut self) -> &mut Self::Target {
83         &mut self.inner
84     }
85 }
86 
87 /// A structure which used to build an HTTP `Request`.
88 ///
89 /// # Examples
90 ///
91 /// ```
92 /// use ylong_http_client::async_impl::{Body, RequestBuilder};
93 ///
94 /// let request = RequestBuilder::new()
95 ///     .method("GET")
96 ///     .version("HTTP/1.1")
97 ///     .url("http://www.example.com")
98 ///     .header("Content-Type", "application/octet-stream")
99 ///     .body(Body::empty());
100 /// ```
101 #[derive(Default)]
102 pub struct RequestBuilder(ReqBuilder);
103 
104 impl RequestBuilder {
105     /// Creates a new, default `RequestBuilder`.
106     ///
107     /// # Default
108     ///
109     /// - The method of this `RequestBuilder` is `GET`.
110     /// - The URL of this `RequestBuilder` is `/`.
111     /// - The HTTP version of this `RequestBuilder` is `HTTP/1.1`.
112     /// - No headers are in this `RequestBuilder`.
113     ///
114     /// # Examples
115     ///
116     /// ```
117     /// use ylong_http_client::async_impl::RequestBuilder;
118     ///
119     /// let builder = RequestBuilder::new();
120     /// ```
new() -> Self121     pub fn new() -> Self {
122         Self(ReqBuilder::new())
123     }
124 
125     /// Sets the `Method` of the `Request`.
126     ///
127     /// # Examples
128     ///
129     /// ```
130     /// use ylong_http_client::async_impl::RequestBuilder;
131     ///
132     /// let builder = RequestBuilder::new().method("GET");
133     /// ```
method(self, method: &str) -> Self134     pub fn method(self, method: &str) -> Self {
135         Self(self.0.method(method))
136     }
137 
138     /// Sets the `Url` of the `Request`.
139     ///
140     /// # Examples
141     ///
142     /// ```
143     /// use ylong_http_client::async_impl::RequestBuilder;
144     ///
145     /// let builder = RequestBuilder::new().url("www.example.com");
146     /// ```
url(self, url: &str) -> Self147     pub fn url(self, url: &str) -> Self {
148         Self(self.0.url(url))
149     }
150 
151     /// Sets the `Version` of the `Request`. Uses `Version::HTTP11` by default.
152     ///
153     /// # Examples
154     ///
155     /// ```
156     /// use ylong_http_client::async_impl::RequestBuilder;
157     ///
158     /// let builder = RequestBuilder::new().version("HTTP/1.1");
159     /// ```
version(mut self, version: &str) -> Self160     pub fn version(mut self, version: &str) -> Self {
161         self.0 = self.0.version(version);
162         self
163     }
164 
165     /// Adds a `Header` to `Request`. Overwrites `HeaderValue` if the
166     /// `HeaderName` already exists.
167     ///
168     /// # Examples
169     ///
170     /// ```
171     /// use ylong_http_client::async_impl::RequestBuilder;
172     ///
173     /// let builder = RequestBuilder::new().header("Content-Type", "application/octet-stream");
174     /// ```
header(mut self, name: &str, value: &str) -> Self175     pub fn header(mut self, name: &str, value: &str) -> Self {
176         self.0 = self.0.header(name, value);
177         self
178     }
179 
180     /// Adds a `Header` to `Request`. Appends `HeaderValue` to the end of
181     /// previous `HeaderValue` if the `HeaderName` already exists.
182     ///
183     /// # Examples
184     ///
185     /// ```
186     /// use ylong_http_client::async_impl::RequestBuilder;
187     ///
188     /// let builder = RequestBuilder::new().append_header("Content-Type", "application/octet-stream");
189     /// ```
append_header(mut self, name: &str, value: &str) -> Self190     pub fn append_header(mut self, name: &str, value: &str) -> Self {
191         self.0 = self.0.append_header(name, value);
192         self
193     }
194 
195     /// Tries to create a `Request` based on the incoming `body`.
196     ///
197     /// # Examples
198     ///
199     /// ```
200     /// use ylong_http_client::async_impl::{Body, RequestBuilder};
201     ///
202     /// let request = RequestBuilder::new().body(Body::empty());
203     /// ```
body(self, body: Body) -> Result<Request, HttpClientError>204     pub fn body(self, body: Body) -> Result<Request, HttpClientError> {
205         let mut builder = self;
206         match body.inner {
207             BodyKind::Slice(ref slice) => {
208                 builder = builder.header(
209                     "Content-Length",
210                     format!("{}", slice.get_ref().len()).as_str(),
211                 );
212             }
213             BodyKind::Multipart(ref multipart) => {
214                 let value = format!(
215                     "multipart/form-data; boundary={}",
216                     multipart.multipart().boundary()
217                 );
218 
219                 builder = builder.header("Content-Type", value.as_str());
220 
221                 if let Some(size) = multipart.multipart().total_bytes() {
222                     builder = builder.header("Content-Length", format!("{size}").as_str());
223                 }
224             }
225             _ => {}
226         }
227 
228         builder
229             .0
230             .body(body)
231             .map(|inner| Request { inner })
232             .map_err(|e| HttpClientError::from_error(ErrorKind::Build, e))
233     }
234 }
235 
236 /// A structure that represents body of HTTP request.
237 ///
238 /// There are many kinds of body supported:
239 ///
240 /// - Empty: an empty body.
241 /// - Slice: a body whose content comes from a memory slice.
242 /// - Stream: a body whose content comes from a stream.
243 /// - Multipart: a body whose content can transfer into a `Multipart`.
244 ///
245 /// # Examples
246 ///
247 /// ```
248 /// use ylong_http_client::async_impl::Body;
249 ///
250 /// let body = Body::empty();
251 /// ```
252 pub struct Body {
253     inner: BodyKind,
254 }
255 
256 pub(crate) enum BodyKind {
257     Empty,
258     Slice(Cursor<Vec<u8>>),
259     Stream(Box<dyn ReusableReader + Send + Sync + Unpin>),
260     Multipart(Box<dyn MultiPartBase + Send + Sync + Unpin>),
261 }
262 
263 impl Body {
264     /// Creates an empty HTTP body.
265     ///
266     /// # Examples
267     ///
268     /// ```
269     /// use ylong_http_client::async_impl::Body;
270     ///
271     /// let body = Body::empty();
272     /// ```
empty() -> Self273     pub fn empty() -> Self {
274         Body::new(BodyKind::Empty)
275     }
276 
277     /// Creates an HTTP body that based on memory slice.
278     ///
279     /// This kind of body is **reusable**.
280     ///
281     /// # Example
282     ///
283     /// ```
284     /// use ylong_http_client::async_impl::Body;
285     ///
286     /// let body = Body::slice("HelloWorld");
287     /// ```
slice<T>(slice: T) -> Self where T: Into<Vec<u8>>,288     pub fn slice<T>(slice: T) -> Self
289     where
290         T: Into<Vec<u8>>,
291     {
292         Body::new(BodyKind::Slice(Cursor::new(slice.into())))
293     }
294 
295     /// Creates an HTTP body that based on an asynchronous stream.
296     ///
297     /// This kind of body is not **reusable**.
298     ///
299     /// # Examples
300     ///
301     /// ```
302     /// use ylong_http_client::async_impl::Body;
303     ///
304     /// let body = Body::stream("HelloWorld".as_bytes());
305     /// ```
stream<T>(stream: T) -> Self where T: ReusableReader + Send + Sync + Unpin + 'static,306     pub fn stream<T>(stream: T) -> Self
307     where
308         T: ReusableReader + Send + Sync + Unpin + 'static,
309     {
310         Body::new(BodyKind::Stream(
311             Box::new(stream) as Box<dyn ReusableReader + Send + Sync + Unpin>
312         ))
313     }
314 
315     /// Creates an HTTP body that based on a structure which implements
316     /// `MultiPartBase`.
317     ///
318     /// This kind of body is not **reusable**.
319     ///
320     /// # Examples
321     ///
322     /// ```
323     /// use ylong_http_client::async_impl::{Body, MultiPart};
324     ///
325     /// async fn create_multipart_body(multipart: MultiPart) {
326     ///     let body = Body::multipart(multipart);
327     /// }
328     /// ```
multipart<T>(stream: T) -> Self where T: MultiPartBase + Send + Sync + Unpin + 'static,329     pub fn multipart<T>(stream: T) -> Self
330     where
331         T: MultiPartBase + Send + Sync + Unpin + 'static,
332     {
333         Body::new(BodyKind::Multipart(
334             Box::new(stream) as Box<dyn MultiPartBase + Send + Sync + Unpin>
335         ))
336     }
337 }
338 
339 impl Body {
new(inner: BodyKind) -> Self340     pub(crate) fn new(inner: BodyKind) -> Self {
341         Self { inner }
342     }
343 
reuse(&mut self) -> std::io::Result<()>344     pub(crate) async fn reuse(&mut self) -> std::io::Result<()> {
345         match self.inner {
346             BodyKind::Empty => Ok(()),
347             BodyKind::Slice(ref mut slice) => {
348                 slice.set_position(0);
349                 Ok(())
350             }
351             BodyKind::Stream(ref mut stream) => stream.reuse().await,
352             BodyKind::Multipart(ref mut multipart) => multipart.reuse().await,
353         }
354     }
355 }
356 
357 impl AsyncRead for Body {
poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut ReadBuf<'_>, ) -> Poll<std::io::Result<()>>358     fn poll_read(
359         self: Pin<&mut Self>,
360         cx: &mut Context<'_>,
361         buf: &mut ReadBuf<'_>,
362     ) -> Poll<std::io::Result<()>> {
363         let this = self.get_mut();
364         match this.inner {
365             BodyKind::Empty => Poll::Ready(Ok(())),
366             BodyKind::Slice(ref mut slice) => {
367                 #[cfg(feature = "tokio_base")]
368                 return Pin::new(slice).poll_read(cx, buf);
369                 #[cfg(feature = "ylong_base")]
370                 return poll_read_cursor(slice, buf);
371             }
372             BodyKind::Stream(ref mut stream) => Pin::new(stream).poll_read(cx, buf),
373             BodyKind::Multipart(ref mut multipart) => Pin::new(multipart).poll_read(cx, buf),
374         }
375     }
376 }
377 
378 /// HTTP url percent encoding implementation.
379 ///
380 /// # Examples
381 ///
382 /// ```
383 /// use ylong_http_client::async_impl::PercentEncoder;
384 ///
385 /// let url = "https://www.example.com/data/测试文件.txt";
386 /// let encoded = PercentEncoder::encode(url).unwrap();
387 /// assert_eq!(
388 ///     encoded,
389 ///     "https://www.example.com/data/%E6%B5%8B%E8%AF%95%E6%96%87%E4%BB%B6.txt"
390 /// );
391 /// ```
392 pub struct PercentEncoder;
393 
394 impl PercentEncoder {
395     /// Percent-coding entry.
encode(url: &str) -> Result<String, HttpClientError>396     pub fn encode(url: &str) -> Result<String, HttpClientError> {
397         PerEncoder::parse(url).map_err(|e| HttpClientError::from_error(ErrorKind::Other, e))
398     }
399 }
400 
401 pub(crate) struct Message {
402     pub(crate) request: RequestArc,
403     pub(crate) interceptor: Arc<Interceptors>,
404 }
405 
406 #[cfg(feature = "ylong_base")]
poll_read_cursor( cursor: &mut Cursor<Vec<u8>>, buf: &mut ylong_runtime::io::ReadBuf<'_>, ) -> Poll<std::io::Result<()>>407 fn poll_read_cursor(
408     cursor: &mut Cursor<Vec<u8>>,
409     buf: &mut ylong_runtime::io::ReadBuf<'_>,
410 ) -> Poll<std::io::Result<()>> {
411     let pos = cursor.position();
412     let data = (*cursor).get_ref();
413 
414     if pos > data.len() as u64 {
415         return Poll::Ready(Ok(()));
416     }
417 
418     let start = pos as usize;
419     let target = std::cmp::min(data.len() - start, buf.remaining());
420     let end = start + target;
421     buf.append(&(data.as_slice())[start..end]);
422     cursor.set_position(end as u64);
423 
424     Poll::Ready(Ok(()))
425 }
426 
427 #[cfg(test)]
428 mod ut_client_request {
429     use crate::async_impl::{Body, PercentEncoder, RequestBuilder};
430 
431     /// UT test cases for `RequestBuilder::default`.
432     ///
433     /// # Brief
434     /// 1. Creates a `RequestBuilder` by `RequestBuilder::default`.
435     /// 2. Checks if result is correct.
436     #[test]
ut_client_request_builder_default()437     fn ut_client_request_builder_default() {
438         let builder = RequestBuilder::default().append_header("name", "value");
439         let request = builder.body(Body::empty());
440         assert!(request.is_ok());
441 
442         let request = RequestBuilder::default()
443             .append_header("name", "value")
444             .url("http://")
445             .body(Body::empty());
446         assert!(request.is_err())
447     }
448 
449     /// UT test cases for `RequestBuilder::body`.
450     ///
451     /// # Brief
452     /// 1. Creates a `RequestBuilder` by `RequestBuilder::body`.
453     /// 2. Checks if result is correct.
454     #[cfg(feature = "ylong_base")]
455     #[test]
ut_client_request_builder_body()456     fn ut_client_request_builder_body() {
457         use std::pin::Pin;
458 
459         use ylong_http::body::{MultiPart, Part};
460         use ylong_runtime::futures::poll_fn;
461         use ylong_runtime::io::ReadBuf;
462 
463         use crate::runtime::AsyncRead;
464 
465         let mp = MultiPart::new().part(
466             Part::new()
467                 .name("name")
468                 .file_name("example.txt")
469                 .mime("application/octet-stream")
470                 .stream("1234".as_bytes())
471                 .length(Some(4)),
472         );
473         let mut request = RequestBuilder::default().body(Body::multipart(mp)).unwrap();
474         let handle = ylong_runtime::spawn(async move {
475             let mut buf = vec![0u8; 50];
476             let mut v_size = vec![];
477             let mut v_str = vec![];
478 
479             loop {
480                 let mut read_buf = ReadBuf::new(&mut buf);
481                 poll_fn(|cx| Pin::new(request.body_mut()).poll_read(cx, &mut read_buf))
482                     .await
483                     .unwrap();
484 
485                 let len = read_buf.filled().len();
486                 if len == 0 {
487                     break;
488                 }
489                 v_size.push(len);
490                 v_str.extend_from_slice(&buf[..len]);
491             }
492             assert_eq!(v_size, vec![50, 50, 50, 50, 50, 11]);
493         });
494         ylong_runtime::block_on(handle).unwrap();
495     }
496 
497     /// UT test cases for `PercentEncoder::encode`.
498     ///
499     /// # Brief
500     /// 1. Creates a `PercentEncoder`.
501     /// 2. Checks if result is correct.
502     #[test]
ut_client_percent_encoder_encode()503     fn ut_client_percent_encoder_encode() {
504         let url = "https://www.example.com/data/测试文件.txt";
505         let encoded = PercentEncoder::encode(url).unwrap();
506         assert_eq!(
507             encoded,
508             "https://www.example.com/data/%E6%B5%8B%E8%AF%95%E6%96%87%E4%BB%B6.txt"
509         );
510     }
511 }
512