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