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