mirror of
https://github.com/openharmony/commonlibrary_rust_ylong_http.git
synced 2026-06-30 21:57:57 -04:00
add rustfmt.toml and reformat
Signed-off-by: fqwert <yanglv2@huawei.com>
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
edition = "2021"
|
||||
wrap_comments = true
|
||||
imports_granularity = "Module"
|
||||
group_imports = "StdExternalCrate"
|
||||
format_code_in_doc_comments = true
|
||||
normalize_comments = true
|
||||
+111
-60
@@ -11,12 +11,6 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::origin::{FromAsyncReader, FromBytes, FromReader};
|
||||
use super::{async_impl, sync_impl};
|
||||
use crate::body::origin::FromAsyncBody;
|
||||
use crate::error::{ErrorKind, HttpError};
|
||||
use crate::headers::{Header, HeaderName, HeaderValue, Headers};
|
||||
use crate::{AsyncRead, AsyncReadExt, ReadBuf};
|
||||
use core::convert::Infallible;
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use core::pin::Pin;
|
||||
@@ -28,16 +22,24 @@ use std::convert::{TryFrom, TryInto};
|
||||
use std::future::Future;
|
||||
use std::io::{Error, Read};
|
||||
|
||||
/// A chunk body is used to encode body to send message by chunk in `HTTP/1.1` format.
|
||||
use super::origin::{FromAsyncReader, FromBytes, FromReader};
|
||||
use super::{async_impl, sync_impl};
|
||||
use crate::body::origin::FromAsyncBody;
|
||||
use crate::error::{ErrorKind, HttpError};
|
||||
use crate::headers::{Header, HeaderName, HeaderValue, Headers};
|
||||
use crate::{AsyncRead, AsyncReadExt, ReadBuf};
|
||||
|
||||
/// A chunk body is used to encode body to send message by chunk in `HTTP/1.1`
|
||||
/// format.
|
||||
///
|
||||
/// This chunk body encoder supports you to use the chunk encode method multiple times to output
|
||||
/// the result in multiple bytes slices.
|
||||
/// This chunk body encoder supports you to use the chunk encode method multiple
|
||||
/// times to output the result in multiple bytes slices.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ylong_http::body::ChunkBody;
|
||||
/// use ylong_http::body::sync_impl::Body;
|
||||
/// use ylong_http::body::ChunkBody;
|
||||
///
|
||||
/// let content = "aaaaa bbbbb ccccc ddddd";
|
||||
/// // Gets `ChunkBody`
|
||||
@@ -87,15 +89,20 @@ struct StatusVar {
|
||||
|
||||
// Data encoding status
|
||||
enum DataState {
|
||||
Partial, // Data encode is processing
|
||||
Complete, // Data encode is completed
|
||||
Finish, // Data encode is finished and return result
|
||||
// Data encode is processing
|
||||
Partial,
|
||||
// Data encode is completed
|
||||
Complete,
|
||||
// Data encode is finished and return result
|
||||
Finish,
|
||||
}
|
||||
|
||||
// Component encoding status
|
||||
enum TokenStatus<T, E> {
|
||||
Complete(T), // The current component is completely encoded.
|
||||
Partial(E), // The current component is partially encoded.
|
||||
// The current component is completely encoded.
|
||||
Complete(T),
|
||||
// The current component is partially encoded.
|
||||
Partial(E),
|
||||
}
|
||||
|
||||
type Token<T> = TokenStatus<usize, T>;
|
||||
@@ -759,7 +766,8 @@ impl ChunkExt {
|
||||
}
|
||||
|
||||
/// Decode state of the chunk buffer.
|
||||
/// When chunks in the buffer end in different elements, `ChunkBodyDecoder` returns different `ChunkState`, as shown in the following figure:
|
||||
/// When chunks in the buffer end in different elements, `ChunkBodyDecoder`
|
||||
/// returns different `ChunkState`, as shown in the following figure:
|
||||
/// > ```trust
|
||||
/// > Meta: `chunk-size [ chunk-ext ] CRLF`
|
||||
/// > Partial: `chunk-size [ chunk-ext ] CRLF chunk-data`
|
||||
@@ -864,9 +872,9 @@ impl<'a> IntoIterator for Chunks<'a> {
|
||||
}
|
||||
|
||||
/// Chunk instance, Indicates a chunk.
|
||||
/// After a decode, the `ChunkBodyDecoder` returns a `Chunk` regardless of whether a chunk is completely decoded.
|
||||
/// The decode status is recorded by the `state` variable.
|
||||
///
|
||||
/// After a decode, the `ChunkBodyDecoder` returns a `Chunk` regardless of
|
||||
/// whether a chunk is completely decoded. The decode status is recorded by the
|
||||
/// `state` variable.
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub struct Chunk<'a> {
|
||||
id: usize,
|
||||
@@ -897,7 +905,8 @@ impl Chunk<'_> {
|
||||
}
|
||||
|
||||
/// Get the size of chunk-data,
|
||||
/// If the size part of a chunk is not completely decoded, the value of size is 0.
|
||||
/// If the size part of a chunk is not completely decoded, the value of size
|
||||
/// is 0.
|
||||
pub fn size(&self) -> usize {
|
||||
self.size
|
||||
}
|
||||
@@ -920,9 +929,10 @@ impl Chunk<'_> {
|
||||
}
|
||||
|
||||
/// Chunk decoder.
|
||||
/// The decoder decode only all chunks and last-chunk in chunk-body and does not decode subsequent trailer-section.
|
||||
/// The decoder maintains a state saving decode phase.
|
||||
/// When a chunk is not completely decoded or a decoding exception occurs, the state is not reset.
|
||||
/// The decoder decode only all chunks and last-chunk in chunk-body and does not
|
||||
/// decode subsequent trailer-section. The decoder maintains a state saving
|
||||
/// decode phase. When a chunk is not completely decoded or a decoding exception
|
||||
/// occurs, the state is not reset.
|
||||
pub struct ChunkBodyDecoder {
|
||||
chunk_num: usize,
|
||||
total_size: usize,
|
||||
@@ -967,9 +977,10 @@ impl ChunkBodyDecoder {
|
||||
}
|
||||
|
||||
/// Decode interface of the chunk decoder.
|
||||
/// It transfers a u8 slice pointing to the chunk data and returns the data of a chunk and the remaining data.
|
||||
/// When the data in the u8 slice is not completely decoded for a chunk,
|
||||
/// An empty u8 slice is returned for the remaining data.
|
||||
/// It transfers a u8 slice pointing to the chunk data and returns the data
|
||||
/// of a chunk and the remaining data. When the data in the u8 slice is
|
||||
/// not completely decoded for a chunk, An empty u8 slice is returned
|
||||
/// for the remaining data.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -982,13 +993,25 @@ impl ChunkBodyDecoder {
|
||||
/// 000; message = last\r\n\
|
||||
/// \r\n\
|
||||
/// "
|
||||
/// .as_bytes();
|
||||
/// .as_bytes();
|
||||
/// let (chunks, rest) = decoder.decode(chunk_body_bytes).unwrap();
|
||||
/// assert_eq!(chunks.iter().len(), 2);
|
||||
/// let chunk = chunks.iter().next().unwrap();
|
||||
/// assert_eq!(
|
||||
/// (chunk.id(), chunk.state(), chunk.size(), chunk.extension(), chunk.data()),
|
||||
/// (0, &ChunkState::Finish, 5, &ChunkExt::new(), "hello".as_bytes())
|
||||
/// (
|
||||
/// chunk.id(),
|
||||
/// chunk.state(),
|
||||
/// chunk.size(),
|
||||
/// chunk.extension(),
|
||||
/// chunk.data()
|
||||
/// ),
|
||||
/// (
|
||||
/// 0,
|
||||
/// &ChunkState::Finish,
|
||||
/// 5,
|
||||
/// &ChunkExt::new(),
|
||||
/// "hello".as_bytes()
|
||||
/// )
|
||||
/// );
|
||||
/// ```
|
||||
pub fn decode<'a>(&mut self, buf: &'a [u8]) -> Result<(Chunks<'a>, &'a [u8]), HttpError> {
|
||||
@@ -1185,7 +1208,8 @@ impl ChunkBodyDecoder {
|
||||
match b {
|
||||
b'\r' => {
|
||||
if self.cr_meet {
|
||||
// TODO Check whether the state machine needs to be reused after the parsing fails and whether the state machine status needs to be adjusted.
|
||||
// TODO Check whether the state machine needs to be reused after the parsing
|
||||
// fails and whether the state machine status needs to be adjusted.
|
||||
return Err(ErrorKind::InvalidInput.into());
|
||||
}
|
||||
self.cr_meet = true;
|
||||
@@ -1615,7 +1639,8 @@ mod ut_chunk {
|
||||
\r\n\
|
||||
"
|
||||
.as_bytes();
|
||||
let res = decoder.decode(&chunk_body_bytes[..1]); // 5
|
||||
// 5
|
||||
let res = decoder.decode(&chunk_body_bytes[..1]);
|
||||
let mut chunks = Chunks::new();
|
||||
chunks.push(Chunk {
|
||||
id: 0,
|
||||
@@ -1626,8 +1651,8 @@ mod ut_chunk {
|
||||
trailer: None,
|
||||
});
|
||||
assert_eq!(res, Ok((chunks, &[] as &[u8],)));
|
||||
|
||||
let res = decoder.decode(&chunk_body_bytes[1..2]); // 5\r
|
||||
// 5\r
|
||||
let res = decoder.decode(&chunk_body_bytes[1..2]);
|
||||
let mut chunks = Chunks::new();
|
||||
chunks.push(Chunk {
|
||||
id: 0,
|
||||
@@ -1638,8 +1663,8 @@ mod ut_chunk {
|
||||
trailer: None,
|
||||
});
|
||||
assert_eq!(res, Ok((chunks, &[] as &[u8],)));
|
||||
|
||||
let res = decoder.decode(&chunk_body_bytes[2..2]); // 5\r
|
||||
// 5\r
|
||||
let res = decoder.decode(&chunk_body_bytes[2..2]);
|
||||
let mut chunks = Chunks::new();
|
||||
chunks.push(Chunk {
|
||||
id: 0,
|
||||
@@ -1650,8 +1675,8 @@ mod ut_chunk {
|
||||
trailer: None,
|
||||
});
|
||||
assert_eq!(res, Ok((chunks, &[] as &[u8],)));
|
||||
|
||||
let res = decoder.decode(&chunk_body_bytes[2..3]); // 5\r\n
|
||||
// 5\r\n
|
||||
let res = decoder.decode(&chunk_body_bytes[2..3]);
|
||||
let mut chunks = Chunks::new();
|
||||
chunks.push(Chunk {
|
||||
id: 0,
|
||||
@@ -1663,7 +1688,8 @@ mod ut_chunk {
|
||||
});
|
||||
assert_eq!(res, Ok((chunks, &[] as &[u8],)));
|
||||
|
||||
let res = decoder.decode(&chunk_body_bytes[3..5]); // 5\r\nhe
|
||||
// 5\r\nhe
|
||||
let res = decoder.decode(&chunk_body_bytes[3..5]);
|
||||
let mut chunks = Chunks::new();
|
||||
chunks.push(Chunk {
|
||||
id: 0,
|
||||
@@ -1675,7 +1701,8 @@ mod ut_chunk {
|
||||
});
|
||||
assert_eq!(res, Ok((chunks, &[] as &[u8],)));
|
||||
|
||||
let res = decoder.decode(&chunk_body_bytes[5..9]); // 5\r\nhello\r
|
||||
// 5\r\nhello\r
|
||||
let res = decoder.decode(&chunk_body_bytes[5..9]);
|
||||
let mut chunks = Chunks::new();
|
||||
chunks.push(Chunk {
|
||||
id: 0,
|
||||
@@ -1687,7 +1714,8 @@ mod ut_chunk {
|
||||
});
|
||||
assert_eq!(res, Ok((chunks, &[] as &[u8],)));
|
||||
|
||||
let res = decoder.decode(&chunk_body_bytes[9..9]); // 5\r\nhello\r
|
||||
// 5\r\nhello\r
|
||||
let res = decoder.decode(&chunk_body_bytes[9..9]);
|
||||
let mut chunks = Chunks::new();
|
||||
chunks.push(Chunk {
|
||||
id: 0,
|
||||
@@ -1699,7 +1727,8 @@ mod ut_chunk {
|
||||
});
|
||||
assert_eq!(res, Ok((chunks, &[] as &[u8],)));
|
||||
|
||||
let res = decoder.decode(&chunk_body_bytes[9..10]); // 5\r\nhello\r\n
|
||||
// 5\r\nhello\r\n
|
||||
let res = decoder.decode(&chunk_body_bytes[9..10]);
|
||||
let mut chunks = Chunks::new();
|
||||
chunks.push(Chunk {
|
||||
id: 0,
|
||||
@@ -1711,7 +1740,8 @@ mod ut_chunk {
|
||||
});
|
||||
assert_eq!(res, Ok((chunks, &[] as &[u8],)));
|
||||
|
||||
let res = decoder.decode(&chunk_body_bytes[10..13]); // 5\r\nhello\r\nC ;
|
||||
// 5\r\nhello\r\nC ;
|
||||
let res = decoder.decode(&chunk_body_bytes[10..13]);
|
||||
let mut chunks = Chunks::new();
|
||||
chunks.push(Chunk {
|
||||
id: 1,
|
||||
@@ -1723,7 +1753,8 @@ mod ut_chunk {
|
||||
});
|
||||
assert_eq!(res, Ok((chunks, &[] as &[u8],)));
|
||||
|
||||
let res = decoder.decode(&chunk_body_bytes[13..27]); // 5\r\nhello\r\nC ; type = text ;
|
||||
// 5\r\nhello\r\nC ; type = text ;
|
||||
let res = decoder.decode(&chunk_body_bytes[13..27]);
|
||||
let mut chunks = Chunks::new();
|
||||
chunks.push(Chunk {
|
||||
id: 1,
|
||||
@@ -1735,7 +1766,8 @@ mod ut_chunk {
|
||||
});
|
||||
assert_eq!(res, Ok((chunks, &[] as &[u8],)));
|
||||
|
||||
let res = decoder.decode(&chunk_body_bytes[27..36]); // 5\r\nhello\r\nC ; type = text ;end = !\r\n
|
||||
// 5\r\nhello\r\nC ; type = text ;end = !\r\n
|
||||
let res = decoder.decode(&chunk_body_bytes[27..36]);
|
||||
let mut chunks = Chunks::new();
|
||||
chunks.push(Chunk {
|
||||
id: 1,
|
||||
@@ -1747,7 +1779,8 @@ mod ut_chunk {
|
||||
});
|
||||
assert_eq!(res, Ok((chunks, &[] as &[u8],)));
|
||||
|
||||
let res = decoder.decode(&chunk_body_bytes[36..50]); // 5\r\nhello\r\nC ; type = text ;end = !\r\nhello world!\r\n
|
||||
// 5\r\nhello\r\nC ; type = text ;end = !\r\nhello world!\r\n
|
||||
let res = decoder.decode(&chunk_body_bytes[36..50]);
|
||||
let mut chunks = Chunks::new();
|
||||
chunks.push(Chunk {
|
||||
id: 1,
|
||||
@@ -1759,7 +1792,8 @@ mod ut_chunk {
|
||||
});
|
||||
assert_eq!(res, Ok((chunks, &[] as &[u8],)));
|
||||
|
||||
let res = decoder.decode(&chunk_body_bytes[50..51]); // 5\r\nhello\r\nC ; type = text ;end = !\r\nhello world!\r\n0
|
||||
// 5\r\nhello\r\nC ; type = text ;end = !\r\nhello world!\r\n0
|
||||
let res = decoder.decode(&chunk_body_bytes[50..51]);
|
||||
let mut chunks = Chunks::new();
|
||||
chunks.push(Chunk {
|
||||
id: 2,
|
||||
@@ -1771,7 +1805,8 @@ mod ut_chunk {
|
||||
});
|
||||
assert_eq!(res, Ok((chunks, &[] as &[u8],)));
|
||||
|
||||
let res = decoder.decode(&chunk_body_bytes[51..54]); // 5\r\nhello\r\nC ; type = text ;end = !\r\nhello world!\r\n000;
|
||||
// 5\r\nhello\r\nC ; type = text ;end = !\r\nhello world!\r\n000;
|
||||
let res = decoder.decode(&chunk_body_bytes[51..54]);
|
||||
let mut chunks = Chunks::new();
|
||||
chunks.push(Chunk {
|
||||
id: 2,
|
||||
@@ -1783,7 +1818,9 @@ mod ut_chunk {
|
||||
});
|
||||
assert_eq!(res, Ok((chunks, &[] as &[u8],)));
|
||||
|
||||
let res = decoder.decode(&chunk_body_bytes[54..71]); // 5\r\nhello\r\nC ; type = text ;end = !\r\nhello world!\r\n000; message = last\r\n
|
||||
// 5\r\nhello\r\nC ; type = text ;end = !\r\nhello world!\r\n000; message =
|
||||
// last\r\n
|
||||
let res = decoder.decode(&chunk_body_bytes[54..71]);
|
||||
let mut chunks = Chunks::new();
|
||||
chunks.push(Chunk {
|
||||
id: 2,
|
||||
@@ -1794,8 +1831,9 @@ mod ut_chunk {
|
||||
trailer: Some(&[] as &[u8]),
|
||||
});
|
||||
assert_eq!(res, Ok((chunks, &[] as &[u8])));
|
||||
|
||||
let res = decoder.decode(&chunk_body_bytes[71..87]); // 5\r\nhello\r\nC ; type = text ;end = !\r\nhello world!\r\n000; message = last\r\nTrailer: value\r\n
|
||||
// 5\r\nhello\r\nC ; type = text ;end = !\r\nhello world!\r\n000; message =
|
||||
// last\r\nTrailer: value\r\n
|
||||
let res = decoder.decode(&chunk_body_bytes[71..87]);
|
||||
let mut chunks = Chunks::new();
|
||||
chunks.push(Chunk {
|
||||
id: 3,
|
||||
@@ -1806,8 +1844,9 @@ mod ut_chunk {
|
||||
trailer: Some("Trailer: value".as_bytes()),
|
||||
});
|
||||
assert_eq!(res, Ok((chunks, &[] as &[u8])));
|
||||
|
||||
let res = decoder.decode(&chunk_body_bytes[87..119]); // 5\r\nhello\r\nC ; type = text ;end = !\r\nhello world!\r\n000; message = last\r\nTrailer: value\r\n\another-trainer: another-value\r\n\
|
||||
// 5\r\nhello\r\nC ; type = text ;end = !\r\nhello world!\r\n000;
|
||||
// message = last\r\nTrailer: value\r\n\another-trainer: another-value\r\n\
|
||||
let res = decoder.decode(&chunk_body_bytes[87..119]);
|
||||
let mut chunks = Chunks::new();
|
||||
chunks.push(Chunk {
|
||||
id: 3,
|
||||
@@ -1818,8 +1857,9 @@ mod ut_chunk {
|
||||
trailer: Some("another-trainer: another-value".as_bytes()),
|
||||
});
|
||||
assert_eq!(res, Ok((chunks, &[] as &[u8])));
|
||||
|
||||
let res = decoder.decode(&chunk_body_bytes[119..121]); // 5\r\nhello\r\nC ; type = text ;end = !\r\nhello world!\r\n000; message = last\r\nTrailer: value\r\n\another-trainer: another-value\r\n\r\n\
|
||||
// 5\r\nhello\r\nC ; type = text ;end = !\r\nhello world!\r\n000;
|
||||
// message = last\r\nTrailer: value\r\n\another-trainer: another-value\r\n\r\n\
|
||||
let res = decoder.decode(&chunk_body_bytes[119..121]);
|
||||
let mut chunks = Chunks::new();
|
||||
chunks.push(Chunk {
|
||||
id: 3,
|
||||
@@ -1850,7 +1890,9 @@ mod ut_chunk {
|
||||
\r\n\
|
||||
"
|
||||
.as_bytes();
|
||||
let (chunks, remaining) = decoder.decode(chunk_body_bytes).unwrap(); // 5
|
||||
|
||||
// 5
|
||||
let (chunks, remaining) = decoder.decode(chunk_body_bytes).unwrap();
|
||||
let mut iter = chunks.iter();
|
||||
let chunk = Chunk {
|
||||
id: 0,
|
||||
@@ -1901,7 +1943,9 @@ mod ut_chunk {
|
||||
\r\n\
|
||||
"
|
||||
.as_bytes();
|
||||
let (chunks, remaining) = decoder.decode(chunk_body_bytes).unwrap(); // 5
|
||||
|
||||
// 5
|
||||
let (chunks, remaining) = decoder.decode(chunk_body_bytes).unwrap();
|
||||
let mut iter = chunks.into_iter();
|
||||
let chunk = Chunk {
|
||||
id: 0,
|
||||
@@ -1950,7 +1994,9 @@ mod ut_chunk {
|
||||
"
|
||||
.as_bytes();
|
||||
let mut decoder = ChunkBodyDecoder::new();
|
||||
let res = decoder.decode(chunk_body_bytes); // 5
|
||||
|
||||
// 5
|
||||
let res = decoder.decode(chunk_body_bytes);
|
||||
assert_eq!(res, Err(ErrorKind::InvalidInput.into()));
|
||||
}
|
||||
|
||||
@@ -1972,7 +2018,9 @@ mod ut_chunk {
|
||||
"
|
||||
.as_bytes();
|
||||
let mut decoder = ChunkBodyDecoder::new();
|
||||
let res = decoder.decode(chunk_body_bytes); // 5
|
||||
|
||||
// 5
|
||||
let res = decoder.decode(chunk_body_bytes);
|
||||
assert_eq!(res, Err(ErrorKind::InvalidInput.into()));
|
||||
}
|
||||
|
||||
@@ -1993,7 +2041,8 @@ mod ut_chunk {
|
||||
"
|
||||
.as_bytes();
|
||||
let mut decoder = ChunkBodyDecoder::new();
|
||||
let res = decoder.decode(chunk_body_bytes); // 5
|
||||
// 5
|
||||
let res = decoder.decode(chunk_body_bytes);
|
||||
assert_eq!(res, Err(ErrorKind::InvalidInput.into()));
|
||||
}
|
||||
|
||||
@@ -2015,7 +2064,9 @@ mod ut_chunk {
|
||||
\r\n\
|
||||
"
|
||||
.as_bytes();
|
||||
let (chunks, _) = decoder.decode(chunk_body_bytes).unwrap(); // 5
|
||||
|
||||
// 5
|
||||
let (chunks, _) = decoder.decode(chunk_body_bytes).unwrap();
|
||||
assert_eq!(chunks.iter().len(), 3);
|
||||
let chunk = chunks.iter().next().unwrap();
|
||||
assert_eq!(
|
||||
|
||||
@@ -11,16 +11,17 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::body::{async_impl, sync_impl};
|
||||
use core::convert::Infallible;
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll};
|
||||
|
||||
use crate::body::{async_impl, sync_impl};
|
||||
|
||||
/// An empty body, indicating that there is no body part in the message.
|
||||
///
|
||||
/// `EmptyBody` both implements [`sync_impl::Body`] and [`async_impl::Body`]. Using
|
||||
/// [`sync_impl::Body::data`] method or [`async_impl::Body::data`] method has no effect
|
||||
/// on buf and always returns `Ok(0)`.
|
||||
/// `EmptyBody` both implements [`sync_impl::Body`] and [`async_impl::Body`].
|
||||
/// Using [`sync_impl::Body::data`] method or [`async_impl::Body::data`] method
|
||||
/// has no effect on buf and always returns `Ok(0)`.
|
||||
///
|
||||
/// [`sync_impl::Body`]: sync_impl::Body
|
||||
/// [`async_impl::Body`]: async_impl::Body
|
||||
@@ -32,8 +33,8 @@ use core::task::{Context, Poll};
|
||||
/// sync_impl:
|
||||
///
|
||||
/// ```
|
||||
/// use ylong_http::body::EmptyBody;
|
||||
/// use ylong_http::body::sync_impl::Body;
|
||||
/// use ylong_http::body::EmptyBody;
|
||||
///
|
||||
/// let mut body = EmptyBody::new();
|
||||
/// let mut buf = [0u8; 1024];
|
||||
@@ -45,8 +46,8 @@ use core::task::{Context, Poll};
|
||||
/// async_impl:
|
||||
///
|
||||
/// ```
|
||||
/// use ylong_http::body::EmptyBody;
|
||||
/// use ylong_http::body::async_impl::Body;
|
||||
/// use ylong_http::body::EmptyBody;
|
||||
///
|
||||
/// # async fn read_empty_body() {
|
||||
/// let mut body = EmptyBody::new();
|
||||
@@ -131,7 +132,8 @@ mod ut_empty {
|
||||
///
|
||||
/// # Brief
|
||||
/// 1. Creates an `EmptyBody`.
|
||||
/// 2. Calls its `async_impl::Body::data` method and then checks the results.
|
||||
/// 2. Calls its `async_impl::Body::data` method and then checks the
|
||||
/// results.
|
||||
#[cfg(feature = "tokio_base")]
|
||||
#[tokio::test]
|
||||
async fn ut_empty_body_async_impl_data() {
|
||||
|
||||
@@ -11,21 +11,18 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::{
|
||||
body::{
|
||||
mime::{
|
||||
common::{consume_crlf, data_copy, trim_front_lwsp, BytesResult, TokenResult},
|
||||
CR, LF,
|
||||
},
|
||||
TokenStatus,
|
||||
},
|
||||
error::{ErrorKind, HttpError},
|
||||
h1::response::decoder::{HEADER_NAME_BYTES, HEADER_VALUE_BYTES},
|
||||
headers::{HeaderName, HeaderValue, Headers},
|
||||
};
|
||||
use core::mem::take;
|
||||
use std::collections::hash_map::IntoIter;
|
||||
|
||||
use crate::body::mime::common::{
|
||||
consume_crlf, data_copy, trim_front_lwsp, BytesResult, TokenResult,
|
||||
};
|
||||
use crate::body::mime::{CR, LF};
|
||||
use crate::body::TokenStatus;
|
||||
use crate::error::{ErrorKind, HttpError};
|
||||
use crate::h1::response::decoder::{HEADER_NAME_BYTES, HEADER_VALUE_BYTES};
|
||||
use crate::headers::{HeaderName, HeaderValue, Headers};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub(crate) enum HeaderStatus {
|
||||
Start,
|
||||
@@ -172,7 +169,8 @@ impl DecodeHeaders {
|
||||
let rest = match self.stage {
|
||||
HeaderStatus::Start => self.start_decode(remains),
|
||||
HeaderStatus::Name => self.name_decode(remains),
|
||||
HeaderStatus::Colon => Ok(remains), // not use
|
||||
// not use
|
||||
HeaderStatus::Colon => Ok(remains),
|
||||
HeaderStatus::Value => self.value_decode(remains),
|
||||
HeaderStatus::Crlf => self.crlf_decode(remains),
|
||||
HeaderStatus::End => {
|
||||
@@ -293,7 +291,8 @@ impl DecodeHeaders {
|
||||
fn get_header_name(buf: &[u8]) -> BytesResult {
|
||||
for (i, b) in buf.iter().enumerate() {
|
||||
if *b == b':' {
|
||||
return Ok(TokenStatus::Complete((&buf[..i], &buf[i + 1..]))); // match "k:v" or "k: v"
|
||||
// match "k:v" or "k: v"
|
||||
return Ok(TokenStatus::Complete((&buf[..i], &buf[i + 1..])));
|
||||
} else if !HEADER_NAME_BYTES[*b as usize] {
|
||||
return Err(ErrorKind::InvalidInput.into());
|
||||
}
|
||||
@@ -316,10 +315,9 @@ impl DecodeHeaders {
|
||||
|
||||
#[cfg(test)]
|
||||
mod ut_decode_headers {
|
||||
use crate::{
|
||||
body::{mime::common::headers::DecodeHeaders, TokenStatus},
|
||||
headers::Headers,
|
||||
};
|
||||
use crate::body::mime::common::headers::DecodeHeaders;
|
||||
use crate::body::TokenStatus;
|
||||
use crate::headers::Headers;
|
||||
|
||||
/// UT test cases for `DecodeHeaders::decode`.
|
||||
///
|
||||
@@ -456,19 +454,23 @@ mod ut_decode_headers {
|
||||
fn ut_decode_headers_decode3() {
|
||||
let buf = b"name1:value1\r\nname2:value2\r\n\r\naaaa";
|
||||
let mut decoder = DecodeHeaders::new();
|
||||
let (headers, rest) = decoder.decode(&buf[0..3]).unwrap(); // nam
|
||||
// nam
|
||||
let (headers, rest) = decoder.decode(&buf[0..3]).unwrap();
|
||||
assert_eq!(headers, TokenStatus::Partial(()));
|
||||
assert_eq!(std::str::from_utf8(rest).unwrap(), "");
|
||||
|
||||
let (headers, rest) = decoder.decode(&buf[3..13]).unwrap(); // e1:value1\r
|
||||
// e1:value1\r
|
||||
let (headers, rest) = decoder.decode(&buf[3..13]).unwrap();
|
||||
assert_eq!(headers, TokenStatus::Partial(()));
|
||||
assert_eq!(std::str::from_utf8(rest).unwrap(), "");
|
||||
|
||||
let (headers, rest) = decoder.decode(&buf[13..29]).unwrap(); // \nname2:value2\r\n\r
|
||||
// \nname2:value2\r\n\r
|
||||
let (headers, rest) = decoder.decode(&buf[13..29]).unwrap();
|
||||
assert_eq!(headers, TokenStatus::Partial(()));
|
||||
assert_eq!(std::str::from_utf8(rest).unwrap(), "");
|
||||
|
||||
let (headers, rest) = decoder.decode(&buf[29..30]).unwrap(); // \n
|
||||
// \n
|
||||
let (headers, rest) = decoder.decode(&buf[29..30]).unwrap();
|
||||
assert_eq!(
|
||||
headers,
|
||||
TokenStatus::Complete({
|
||||
@@ -480,7 +482,8 @@ mod ut_decode_headers {
|
||||
);
|
||||
assert_eq!(std::str::from_utf8(rest).unwrap(), "");
|
||||
|
||||
let (headers, rest) = decoder.decode(&buf[30..34]).unwrap(); // aaaa
|
||||
// aaaa
|
||||
let (headers, rest) = decoder.decode(&buf[30..34]).unwrap();
|
||||
assert_eq!(headers, TokenStatus::Complete(Headers::new()));
|
||||
assert_eq!(std::str::from_utf8(rest).unwrap(), "aaaa");
|
||||
}
|
||||
|
||||
@@ -11,25 +11,27 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::body::{
|
||||
async_impl::{self, DataFuture},
|
||||
mime::common::{data_copy, SizeResult, TokenStatus},
|
||||
sync_impl,
|
||||
};
|
||||
use crate::{AsyncRead, ReadBuf};
|
||||
use core::{
|
||||
fmt::Debug,
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
use core::fmt::Debug;
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll};
|
||||
use std::io::Read;
|
||||
|
||||
// Uses Box<dyn trait> so that it can be put into a list(like vec) with different T.
|
||||
use crate::body::async_impl::{self, DataFuture};
|
||||
use crate::body::mime::common::{data_copy, SizeResult, TokenStatus};
|
||||
use crate::body::sync_impl;
|
||||
use crate::{AsyncRead, ReadBuf};
|
||||
|
||||
// Uses Box<dyn trait> so that it can be put into a list(like vec) with
|
||||
// different T.
|
||||
pub(crate) enum MixFrom<'a> {
|
||||
Owned { bytes: Vec<u8>, index: usize }, // the read content is Vec<u8>
|
||||
Slice { bytes: &'a [u8], index: usize }, // the read content is from memory
|
||||
Reader(Box<dyn Read + Send + Sync>), // the read content is from a synchronous reader
|
||||
AsyncReader(Box<dyn AsyncRead + Send + Sync + Unpin>), // the read content is from an asynchronous reader
|
||||
// the read content is Vec<u8>
|
||||
Owned { bytes: Vec<u8>, index: usize },
|
||||
// the read content is from memory
|
||||
Slice { bytes: &'a [u8], index: usize },
|
||||
// the read content is from a synchronous reader
|
||||
Reader(Box<dyn Read + Send + Sync>),
|
||||
// the read content is from an asynchronous reader
|
||||
AsyncReader(Box<dyn AsyncRead + Send + Sync + Unpin>),
|
||||
}
|
||||
|
||||
impl<'a> MixFrom<'a> {
|
||||
@@ -135,7 +137,8 @@ impl Debug for MixFrom<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
// It is not a complete implementation, only implements for MixFrom::Owned && MixFrom::Slice.
|
||||
// It is not a complete implementation, only implements for MixFrom::Owned &&
|
||||
// MixFrom::Slice.
|
||||
impl PartialEq for MixFrom<'_> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
@@ -214,7 +217,8 @@ impl async_impl::Body for MixFrom<'_> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod ut_mix {
|
||||
use crate::body::{async_impl, mime::common::mix::MixFrom, sync_impl};
|
||||
use crate::body::mime::common::mix::MixFrom;
|
||||
use crate::body::{async_impl, sync_impl};
|
||||
|
||||
/// Builds a `MixFrom`.
|
||||
macro_rules! mix_build {
|
||||
@@ -261,9 +265,9 @@ mod ut_mix {
|
||||
$(BodyAsyncReader: $body4,)?
|
||||
},
|
||||
);
|
||||
|
||||
// default 1
|
||||
#[allow(unused_assignments, unused_mut)]
|
||||
let mut len = 1; // default 1
|
||||
let mut len = 1;
|
||||
|
||||
$(len = $size;)?
|
||||
let mut buf = vec![0u8; len];
|
||||
@@ -303,8 +307,9 @@ mod ut_mix {
|
||||
},
|
||||
);
|
||||
|
||||
// default 1
|
||||
#[allow(unused_assignments, unused_mut)]
|
||||
let mut len = 1; // default 1
|
||||
let mut len = 1;
|
||||
|
||||
$(len = $size;)?
|
||||
let mut buf = vec![0u8; len];
|
||||
@@ -376,7 +381,8 @@ mod ut_mix {
|
||||
/// UT test cases for `MixFrom::set_reader`.
|
||||
///
|
||||
/// # Brief
|
||||
/// 1. Creates a `MixFrom` from synchronous read content by `MixFrom::set_reader`.
|
||||
/// 1. Creates a `MixFrom` from synchronous read content by
|
||||
/// `MixFrom::set_reader`.
|
||||
/// 2. Checks whether the result is correct.
|
||||
#[test]
|
||||
fn ut_mix_set_reader() {
|
||||
@@ -392,7 +398,8 @@ mod ut_mix {
|
||||
/// UT test cases for `MixFrom::set_async_reader`.
|
||||
///
|
||||
/// # Brief
|
||||
/// 1. Creates a `MixFrom` from asynchronous read content by `MixFrom::set_async_reader`.
|
||||
/// 1. Creates a `MixFrom` from asynchronous read content by
|
||||
/// `MixFrom::set_async_reader`.
|
||||
/// 2. Encodes by synchronous encoding.
|
||||
/// 3. Checks whether the result is correct.
|
||||
#[test]
|
||||
@@ -409,7 +416,8 @@ mod ut_mix {
|
||||
/// UT test cases for `MixFrom::set_async_reader`.
|
||||
///
|
||||
/// # Brief
|
||||
/// 1. Creates a `MixFrom` from asynchronous read content by `MixFrom::set_async_reader`.
|
||||
/// 1. Creates a `MixFrom` from asynchronous read content by
|
||||
/// `MixFrom::set_async_reader`.
|
||||
/// 2. Encodes by asynchronous encoding.
|
||||
/// 3. Checks whether the result is correct.
|
||||
#[cfg(feature = "tokio_base")]
|
||||
@@ -427,7 +435,8 @@ mod ut_mix {
|
||||
/// UT test cases for `MixFrom::set_reader`.
|
||||
///
|
||||
/// # Brief
|
||||
/// 1. Creates a `MixFrom` from synchronous read content by `MixFrom::set_reader`.
|
||||
/// 1. Creates a `MixFrom` from synchronous read content by
|
||||
/// `MixFrom::set_reader`.
|
||||
/// 2. Encodes by asynchronous encoding.
|
||||
/// 3. Checks whether the result is correct.
|
||||
#[cfg(feature = "tokio_base")]
|
||||
@@ -445,7 +454,8 @@ mod ut_mix {
|
||||
/// UT test cases for `MixFrom::set_bytes`.
|
||||
///
|
||||
/// # Brief
|
||||
/// 1. Creates a `MixFrom` from synchronous read content by `MixFrom::set_bytes`.
|
||||
/// 1. Creates a `MixFrom` from synchronous read content by
|
||||
/// `MixFrom::set_bytes`.
|
||||
/// 2. Encodes by asynchronous encoding.
|
||||
/// 3. Checks whether the result is correct.
|
||||
#[cfg(feature = "tokio_base")]
|
||||
@@ -463,7 +473,8 @@ mod ut_mix {
|
||||
/// UT test cases for `MixFrom::set_owned`.
|
||||
///
|
||||
/// # Brief
|
||||
/// 1. Creates a `MixFrom` from synchronous read content by `MixFrom::set_owned`.
|
||||
/// 1. Creates a `MixFrom` from synchronous read content by
|
||||
/// `MixFrom::set_owned`.
|
||||
/// 2. Encodes by asynchronous encoding.
|
||||
/// 3. Checks whether the result is correct.
|
||||
#[cfg(feature = "tokio_base")]
|
||||
|
||||
@@ -16,28 +16,30 @@ mod mix;
|
||||
mod multi;
|
||||
mod part;
|
||||
|
||||
pub use multi::{MimeMulti, MimeMultiBuilder, XPart};
|
||||
pub use part::{MimePart, MimePartBuilder};
|
||||
|
||||
pub(crate) use headers::{DecodeHeaders, EncodeHeaders, HeaderStatus};
|
||||
pub(crate) use mix::MixFrom;
|
||||
pub use multi::{MimeMulti, MimeMultiBuilder, XPart};
|
||||
pub(crate) use part::PartStatus;
|
||||
pub use part::{MimePart, MimePartBuilder};
|
||||
pub(crate) type SizeResult = Result<usize, std::io::Error>;
|
||||
pub(crate) type TokenResult<T> = Result<TokenStatus<usize, T>, std::io::Error>;
|
||||
pub(crate) type BytesResult<'a> = Result<TokenStatus<(&'a [u8], &'a [u8]), &'a [u8]>, HttpError>;
|
||||
|
||||
use crate::{
|
||||
error::{ErrorKind, HttpError},
|
||||
headers::Headers,
|
||||
};
|
||||
use core::mem::take;
|
||||
use std::io::Read;
|
||||
|
||||
use crate::error::{ErrorKind, HttpError};
|
||||
use crate::headers::Headers;
|
||||
|
||||
// RFC5234 ABNF
|
||||
pub(crate) const HTAB: u8 = b'\t'; // horizontal tab
|
||||
pub(crate) const SP: u8 = b' '; // 0x20 space
|
||||
pub(crate) const CR: u8 = b'\r'; // carriage return
|
||||
pub(crate) const LF: u8 = b'\n'; // linefeed
|
||||
// horizontal tab
|
||||
pub(crate) const HTAB: u8 = b'\t';
|
||||
// 0x20 space
|
||||
pub(crate) const SP: u8 = b' ';
|
||||
// carriage return
|
||||
pub(crate) const CR: u8 = b'\r';
|
||||
// linefeed
|
||||
pub(crate) const LF: u8 = b'\n';
|
||||
pub(crate) const CRLF: &[u8] = b"\r\n";
|
||||
|
||||
/// Represents component encoding/decoding status.
|
||||
@@ -67,11 +69,13 @@ impl<T, E> TokenStatus<T, E> {
|
||||
}
|
||||
}
|
||||
|
||||
// Pulls some bytes from this src into the buf, returning how many bytes were read.
|
||||
// Pulls some bytes from this src into the buf, returning how many bytes were
|
||||
// read.
|
||||
pub(crate) fn data_copy(src: &[u8], src_idx: &mut usize, buf: &mut [u8]) -> TokenResult<usize> {
|
||||
let input_len = src.len() - *src_idx;
|
||||
let output_len = buf.len();
|
||||
let num = (&src[*src_idx..]).read(buf)?; // sync
|
||||
// sync
|
||||
let num = (&src[*src_idx..]).read(buf)?;
|
||||
*src_idx += num;
|
||||
if output_len >= input_len {
|
||||
return Ok(TokenStatus::Complete(num));
|
||||
@@ -116,7 +120,8 @@ pub(crate) fn trim_back_lwsp_if_end_with_lf(buf: &[u8]) -> &[u8] {
|
||||
// reduce "\n" or "\r\n"
|
||||
pub(crate) fn consume_crlf(
|
||||
buf: &[u8],
|
||||
cr_meet: bool, //has "\r"
|
||||
// has "\r"
|
||||
cr_meet: bool,
|
||||
) -> Result<TokenStatus<&[u8], usize>, HttpError> {
|
||||
if buf.is_empty() {
|
||||
return Ok(TokenStatus::Partial(0));
|
||||
@@ -210,7 +215,8 @@ pub(crate) fn get_content_type_boundary(headers: &Headers) -> Option<Vec<u8>> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod ut_common {
|
||||
use crate::{body::mime::common::get_content_type_boundary, headers::Headers};
|
||||
use crate::body::mime::common::get_content_type_boundary;
|
||||
use crate::headers::Headers;
|
||||
|
||||
/// UT test cases for `get_content_type_boundary`.
|
||||
///
|
||||
|
||||
@@ -11,13 +11,12 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::headers::{HeaderName, HeaderValue};
|
||||
use crate::{
|
||||
body::mime::{DecodeHeaders, MimePart},
|
||||
error::{ErrorKind, HttpError},
|
||||
headers::{Header, Headers},
|
||||
};
|
||||
use core::{convert::TryFrom, mem::take};
|
||||
use core::convert::TryFrom;
|
||||
use core::mem::take;
|
||||
|
||||
use crate::body::mime::{DecodeHeaders, MimePart};
|
||||
use crate::error::{ErrorKind, HttpError};
|
||||
use crate::headers::{Header, HeaderName, HeaderValue, Headers};
|
||||
|
||||
/// `MimeMulti` is a Composite MIME body which is defined in [`RFC2046`]: \
|
||||
/// In the case of multipart entities, in which one or more different
|
||||
@@ -346,18 +345,16 @@ impl<'a> MimeMultiBuilder<'a> {
|
||||
}
|
||||
|
||||
/// Sets headers to the Composite MIME body. \
|
||||
/// It is recommended to use [`set_content_type`] to set header 'Content-Type'
|
||||
/// and set boundary simultaneously.
|
||||
/// It is recommended to use [`set_content_type`] to set header
|
||||
/// 'Content-Type' and set boundary simultaneously.
|
||||
///
|
||||
/// [`set_content_type`]: MimeMultiBuilder::set_content_type
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ylong_http::{
|
||||
/// body::MimeMultiBuilder,
|
||||
/// headers::Headers,
|
||||
/// };
|
||||
/// use ylong_http::body::MimeMultiBuilder;
|
||||
/// use ylong_http::headers::Headers;
|
||||
///
|
||||
/// let multi = MimeMultiBuilder::new()
|
||||
/// .set_headers({
|
||||
@@ -433,8 +430,8 @@ impl<'a> MimeMultiBuilder<'a> {
|
||||
/// the boundary parameter, which consists of 1 to 70 characters from a
|
||||
/// set of characters known to be very robust through mail gateways, and
|
||||
/// NOT ending with white space. \
|
||||
/// It is recommended to use [`set_content_type`] to set header 'Content-Type'
|
||||
/// and set boundary simultaneously.
|
||||
/// It is recommended to use [`set_content_type`] to set header
|
||||
/// 'Content-Type' and set boundary simultaneously.
|
||||
///
|
||||
/// [`RFC2046`]: https://www.rfc-editor.org/rfc/rfc2046#section-5.1.1
|
||||
/// [`set_content_type`]: MimeMultiBuilder::set_content_type
|
||||
@@ -460,8 +457,7 @@ impl<'a> MimeMultiBuilder<'a> {
|
||||
/// ```
|
||||
/// use ylong_http::body::MimeMultiBuilder;
|
||||
///
|
||||
/// let mut multi = MimeMultiBuilder::new()
|
||||
/// .set_content_type(b"multipart/mixed", b"ab".to_vec());
|
||||
/// let mut multi = MimeMultiBuilder::new().set_content_type(b"multipart/mixed", b"ab".to_vec());
|
||||
/// ```
|
||||
pub fn set_content_type(mut self, content_type: &'a [u8], boundary: Vec<u8>) -> Self {
|
||||
self.inner = self.inner.and_then(move |mut inner| {
|
||||
@@ -500,8 +496,7 @@ impl<'a> MimeMultiBuilder<'a> {
|
||||
/// use ylong_http::body::{MimeMulti, MimeMultiBuilder};
|
||||
///
|
||||
/// let multi1 = MimeMulti::builder().build().unwrap();
|
||||
/// let multi = MimeMultiBuilder::new()
|
||||
/// .add_multi(multi1);
|
||||
/// let multi = MimeMultiBuilder::new().add_multi(multi1);
|
||||
/// ```
|
||||
pub fn add_multi(mut self, multi: MimeMulti<'a>) -> Self {
|
||||
self.inner = self.inner.map(move |mut inner| {
|
||||
@@ -518,8 +513,7 @@ impl<'a> MimeMultiBuilder<'a> {
|
||||
/// use ylong_http::body::{MimeMulti, MimeMultiBuilder, XPart};
|
||||
///
|
||||
/// let multi1 = MimeMulti::builder().build().unwrap();
|
||||
/// let multi = MimeMultiBuilder::new()
|
||||
/// .add_xpart(XPart::Multi(multi1));
|
||||
/// let multi = MimeMultiBuilder::new().add_xpart(XPart::Multi(multi1));
|
||||
/// ```
|
||||
pub fn add_xpart(mut self, xpart: XPart<'a>) -> Self {
|
||||
self.inner = self.inner.map(move |mut inner| {
|
||||
|
||||
@@ -11,20 +11,20 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::headers::{HeaderName, HeaderValue};
|
||||
use crate::AsyncRead;
|
||||
use crate::{
|
||||
body::mime::{MixFrom, CR, LF},
|
||||
error::HttpError,
|
||||
headers::{Header, Headers},
|
||||
};
|
||||
use core::{convert::TryFrom, mem::take};
|
||||
use core::convert::TryFrom;
|
||||
use core::mem::take;
|
||||
use std::io::Read;
|
||||
|
||||
/// `MimePart` is a body part of a Composite MIME body which is defined in [`RFC2046`]: \
|
||||
/// The body must then contain one or more body parts, each preceded by a boundary
|
||||
/// delimiter line, and the last one followed by a closing boundary delimiter line.
|
||||
/// Each body part then consists of a header area, a blank line, and a body area. \
|
||||
use crate::body::mime::{MixFrom, CR, LF};
|
||||
use crate::error::HttpError;
|
||||
use crate::headers::{Header, HeaderName, HeaderValue, Headers};
|
||||
use crate::AsyncRead;
|
||||
|
||||
/// `MimePart` is a body part of a Composite MIME body which is defined in
|
||||
/// [`RFC2046`]: The body must then contain one or more body parts, each
|
||||
/// preceded by a boundary delimiter line, and the last one followed by a
|
||||
/// closing boundary delimiter line. Each body part then consists of a header
|
||||
/// area, a blank line, and a body area.
|
||||
///
|
||||
/// `MimePart` can be built by [`MimePartBuilder`], then sets headers and body.
|
||||
///
|
||||
@@ -60,7 +60,8 @@ pub struct MimePart<'a> {
|
||||
//
|
||||
// OCTET := <any 0-255 octet value>
|
||||
pub(crate) headers: Headers,
|
||||
pub(crate) body: MixFrom<'a>, // all use for encode; owned use for decode
|
||||
// all use for encode; owned use for decode
|
||||
pub(crate) body: MixFrom<'a>,
|
||||
}
|
||||
|
||||
impl<'a> MimePart<'a> {
|
||||
@@ -203,8 +204,8 @@ impl<'a> MimePart<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// `MimePartBuilder` can set a body part of a Composite MIME body [`MimePart`]. \
|
||||
/// `MimePartBuilder` can set headers and body, then builds a [`MimePart`]. \
|
||||
/// `MimePartBuilder` can set a body part of a Composite MIME body [`MimePart`].
|
||||
/// `MimePartBuilder` can set headers and body, then builds a [`MimePart`].
|
||||
///
|
||||
/// [`MimePart`]: MimePart
|
||||
///
|
||||
@@ -247,7 +248,8 @@ impl<'a> MimePartBuilder<'a> {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ylong_http::{body::MimePartBuilder, headers::Headers};
|
||||
/// use ylong_http::body::MimePartBuilder;
|
||||
/// use ylong_http::headers::Headers;
|
||||
///
|
||||
/// let part = MimePartBuilder::new().set_headers({
|
||||
/// let mut headers = Headers::new();
|
||||
@@ -348,7 +350,8 @@ impl<'a> MimePartBuilder<'a> {
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets body to the MIME body part, the read content is from a synchronous reader.
|
||||
/// Sets body to the MIME body part, the read content is from a synchronous
|
||||
/// reader.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -368,7 +371,8 @@ impl<'a> MimePartBuilder<'a> {
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets body to the MIME body part, the read content is from a asynchronous reader.
|
||||
/// Sets body to the MIME body part, the read content is from a asynchronous
|
||||
/// reader.
|
||||
pub fn body_from_async_reader<T>(mut self, data: T) -> Self
|
||||
where
|
||||
T: AsyncRead + Send + Sync + Unpin + 'static,
|
||||
@@ -413,10 +417,8 @@ pub(crate) enum PartStatus {
|
||||
|
||||
#[cfg(test)]
|
||||
mod ut_mime_part {
|
||||
use crate::{
|
||||
body::{MimePart, MimePartBuilder},
|
||||
headers::Headers,
|
||||
};
|
||||
use crate::body::{MimePart, MimePartBuilder};
|
||||
use crate::headers::Headers;
|
||||
|
||||
/// UT test cases for `MimePartBuilder::new`.
|
||||
///
|
||||
|
||||
@@ -11,27 +11,23 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::{
|
||||
body::{
|
||||
mime::{
|
||||
common::{
|
||||
get_content_type_boundary, get_crlf_contain, trim_back_lwsp_if_end_with_lf, XPart,
|
||||
},
|
||||
decode::BoundaryTag,
|
||||
DecodeHeaders, MimeMulti, MimePartDecoder,
|
||||
},
|
||||
TokenStatus,
|
||||
},
|
||||
error::{ErrorKind, HttpError},
|
||||
headers::Headers,
|
||||
};
|
||||
use core::mem::take;
|
||||
|
||||
use crate::body::mime::common::{
|
||||
get_content_type_boundary, get_crlf_contain, trim_back_lwsp_if_end_with_lf, XPart,
|
||||
};
|
||||
use crate::body::mime::decode::BoundaryTag;
|
||||
use crate::body::mime::{DecodeHeaders, MimeMulti, MimePartDecoder};
|
||||
use crate::body::TokenStatus;
|
||||
use crate::error::{ErrorKind, HttpError};
|
||||
use crate::headers::Headers;
|
||||
|
||||
type ByteVec<'a> = Result<TokenStatus<(Vec<u8>, &'a [u8]), &'a [u8]>, HttpError>;
|
||||
|
||||
// TODO: Increases compatibility for preamble and epilogue.
|
||||
|
||||
/// `MimeMultiDecoder` can create a [`MimeMulti`] according to a serialized data.
|
||||
/// `MimeMultiDecoder` can create a [`MimeMulti`] according to a serialized
|
||||
/// data.
|
||||
///
|
||||
/// [`MimeMulti`]: MimeMulti
|
||||
///
|
||||
@@ -64,8 +60,10 @@ type ByteVec<'a> = Result<TokenStatus<(Vec<u8>, &'a [u8]), &'a [u8]>, HttpError>
|
||||
/// ```
|
||||
#[derive(Debug, Default, PartialEq)]
|
||||
pub struct MimeMultiDecoder {
|
||||
stages: Vec<MultiStage>, // stack of stage
|
||||
multis: Vec<MimeMulti<'static>>, // stack of multi
|
||||
// stack of stage
|
||||
stages: Vec<MultiStage>,
|
||||
// stack of multi
|
||||
multis: Vec<MimeMulti<'static>>,
|
||||
}
|
||||
|
||||
impl MimeMultiDecoder {
|
||||
@@ -74,7 +72,7 @@ impl MimeMultiDecoder {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ylong_http:: body::MimeMultiDecoder;
|
||||
/// use ylong_http::body::MimeMultiDecoder;
|
||||
///
|
||||
/// let mut decoder = MimeMultiDecoder::new();
|
||||
/// ```
|
||||
@@ -141,7 +139,8 @@ impl MimeMultiDecoder {
|
||||
}
|
||||
}?;
|
||||
remains = rest;
|
||||
// at least has the outermost multi stage, unless is replaced by MultiStage::End, so the last stage can uncheck.
|
||||
// at least has the outermost multi stage, unless is replaced by
|
||||
// MultiStage::End, so the last stage can uncheck.
|
||||
if remains.is_empty() && !self.last_stage()?.is_end() {
|
||||
break;
|
||||
}
|
||||
@@ -377,11 +376,15 @@ impl MultiStage {
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct DecodeData {
|
||||
is_finish_first_boundary: bool, // whether read first boundary completely
|
||||
is_outermost: bool, // whether is outermost multi
|
||||
// whether read first boundary completely
|
||||
is_finish_first_boundary: bool,
|
||||
// whether is outermost multi
|
||||
is_outermost: bool,
|
||||
boundary: Vec<u8>,
|
||||
tag: BoundaryTag, // 1 is middle part; 2 is end part
|
||||
src: Vec<u8>, // src which is need to encode
|
||||
// 1 is middle part; 2 is end part
|
||||
tag: BoundaryTag,
|
||||
// src which is need to encode
|
||||
src: Vec<u8>,
|
||||
src_idx: usize,
|
||||
}
|
||||
|
||||
@@ -693,7 +696,8 @@ mod ut_mime_multi_decoder {
|
||||
/// # Brief
|
||||
/// 1. Creates a `MimeMultiDecoder` by `MimeMultiDecoder::new`.
|
||||
/// 2. Uses `MimeMultiDecoder::decode` to decode `MimeMulti`.
|
||||
/// 3. The `MimeMulti` is composed of several parts, and the boundary has LWSP chars.
|
||||
/// 3. The `MimeMulti` is composed of several parts, and the boundary has
|
||||
/// LWSP chars.
|
||||
/// 4. Creates a `MimeMulti` and sets the same parameters to compare.
|
||||
/// 5. Checks whether the result is correct.
|
||||
#[test]
|
||||
@@ -930,16 +934,24 @@ mod ut_mime_multi_decoder {
|
||||
let buf =
|
||||
b"---\r\nkey1:value1\r\n\r\n1111\r\n---\r\nkey2: value2\r\n\r\n2222\r\n-----\r\nabcd";
|
||||
let mut decoder = MimeMultiDecoder::new();
|
||||
let (elem, rest) = decoder.decode(&buf[..3]).unwrap(); //---
|
||||
|
||||
//---
|
||||
let (elem, rest) = decoder.decode(&buf[..3]).unwrap();
|
||||
assert!(!elem.is_complete());
|
||||
assert_eq!(rest, b"");
|
||||
let (elem, rest) = decoder.decode(&buf[3..16]).unwrap(); //\r\nkey1:value1
|
||||
|
||||
//\r\nkey1:value1
|
||||
let (elem, rest) = decoder.decode(&buf[3..16]).unwrap();
|
||||
assert!(!elem.is_complete());
|
||||
assert_eq!(rest, b"");
|
||||
let (elem, rest) = decoder.decode(&buf[16..31]).unwrap(); //\r\n\r\n1111\r\n---\r\n
|
||||
|
||||
//\r\n\r\n1111\r\n---\r\n
|
||||
let (elem, rest) = decoder.decode(&buf[16..31]).unwrap();
|
||||
assert!(!elem.is_complete());
|
||||
assert_eq!(rest, b"");
|
||||
let (elem, rest) = decoder.decode(&buf[31..60]).unwrap(); //key2: value2\r\n\r\n2222\r\n-----\r\n
|
||||
|
||||
// key2: value2\r\n\r\n2222\r\n-----\r\n
|
||||
let (elem, rest) = decoder.decode(&buf[31..60]).unwrap();
|
||||
assert!(elem.is_complete());
|
||||
let multi1 = MimeMulti::builder()
|
||||
.set_boundary(b"-".to_vec())
|
||||
@@ -963,7 +975,9 @@ mod ut_mime_multi_decoder {
|
||||
assert_eq!(multi, multi1);
|
||||
}
|
||||
assert_eq!(rest, b"");
|
||||
let (_elem, rest) = decoder.decode(&buf[60..]).unwrap(); //abcd
|
||||
|
||||
// abcd
|
||||
let (_elem, rest) = decoder.decode(&buf[60..]).unwrap();
|
||||
assert_eq!(rest, b"abcd");
|
||||
}
|
||||
|
||||
|
||||
@@ -11,28 +11,26 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::{
|
||||
body::{
|
||||
mime::{
|
||||
common::{get_crlf_contain, trim_back_lwsp_if_end_with_lf, trim_front_lwsp},
|
||||
decode::BoundaryTag,
|
||||
DecodeHeaders, MimePart, PartStatus,
|
||||
},
|
||||
TokenStatus,
|
||||
},
|
||||
error::{ErrorKind, HttpError},
|
||||
headers::Headers,
|
||||
};
|
||||
use core::mem::take;
|
||||
|
||||
use crate::body::mime::common::{get_crlf_contain, trim_back_lwsp_if_end_with_lf, trim_front_lwsp};
|
||||
use crate::body::mime::decode::BoundaryTag;
|
||||
use crate::body::mime::{DecodeHeaders, MimePart, PartStatus};
|
||||
use crate::body::TokenStatus;
|
||||
use crate::error::{ErrorKind, HttpError};
|
||||
use crate::headers::Headers;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub(crate) struct MimePartDecoder {
|
||||
stage: PartStatus, // Encode stage now
|
||||
// Encode stage now
|
||||
stage: PartStatus,
|
||||
headers_decoder: DecodeHeaders,
|
||||
part: MimePart<'static>,
|
||||
boundary: Vec<u8>, // boundary
|
||||
// boundary
|
||||
boundary: Vec<u8>,
|
||||
tag: BoundaryTag,
|
||||
src_idx: usize, // marks last "\n"
|
||||
// marks last "\n"
|
||||
src_idx: usize,
|
||||
}
|
||||
|
||||
impl MimePartDecoder {
|
||||
@@ -84,7 +82,8 @@ impl MimePartDecoder {
|
||||
let rest = match self.stage {
|
||||
PartStatus::Start => self.start_decode(remains),
|
||||
PartStatus::Headers => self.headers_decode(remains),
|
||||
PartStatus::Crlf => Ok(remains), // not use
|
||||
// not use
|
||||
PartStatus::Crlf => Ok(remains),
|
||||
PartStatus::Body => self.body_decode(remains),
|
||||
PartStatus::End => {
|
||||
results = TokenStatus::Complete(take(&mut self.part));
|
||||
@@ -170,10 +169,8 @@ impl MimePartDecoder {
|
||||
|
||||
#[cfg(test)]
|
||||
mod ut_mime_part_decoder {
|
||||
use crate::body::{
|
||||
mime::{MimePart, MimePartDecoder},
|
||||
TokenStatus,
|
||||
};
|
||||
use crate::body::mime::{MimePart, MimePartDecoder};
|
||||
use crate::body::TokenStatus;
|
||||
|
||||
/// UT test cases for `MimePartDecoder::decode`.
|
||||
///
|
||||
@@ -337,15 +334,18 @@ mod ut_mime_part_decoder {
|
||||
let buf = b"name1:value1\r\nname2:value2\r\n\r\nabcd\r\n--abc\r\nabcd";
|
||||
let mut decoder = MimePartDecoder::new();
|
||||
decoder.set_boundary(b"abc".to_vec());
|
||||
let (elem, rest) = decoder.decode(&buf[0..3]).unwrap(); // nam
|
||||
// nam
|
||||
let (elem, rest) = decoder.decode(&buf[0..3]).unwrap();
|
||||
assert!(!elem.is_complete());
|
||||
assert_eq!(rest, b"");
|
||||
|
||||
let (elem, rest) = decoder.decode(&buf[3..30]).unwrap(); // e1:value1\r\nname2:value2\r\n\r\n
|
||||
// e1:value1\r\nname2:value2\r\n\r\n
|
||||
let (elem, rest) = decoder.decode(&buf[3..30]).unwrap();
|
||||
assert!(!elem.is_complete());
|
||||
assert_eq!(rest, b"");
|
||||
|
||||
let (elem, rest) = decoder.decode(&buf[30..]).unwrap(); // abcd\r\n--abc\r\nabcd
|
||||
// abcd\r\n--abc\r\nabcd
|
||||
let (elem, rest) = decoder.decode(&buf[30..]).unwrap();
|
||||
assert!(elem.is_complete());
|
||||
let part1 = MimePart::builder()
|
||||
.header("name1", "value1")
|
||||
|
||||
@@ -11,24 +11,17 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::{
|
||||
body::{
|
||||
async_impl,
|
||||
mime::{
|
||||
common::{data_copy, SizeResult},
|
||||
EncodeHeaders,
|
||||
},
|
||||
sync_impl, MimeMulti, MimePartEncoder, TokenStatus, XPart,
|
||||
},
|
||||
headers::Headers,
|
||||
};
|
||||
use core::{
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll};
|
||||
use std::collections::VecDeque;
|
||||
|
||||
/// `MimeMultiEncoder` can get a [`MimeMulti`] to encode into data that can be transmitted.
|
||||
use crate::body::mime::common::{data_copy, SizeResult};
|
||||
use crate::body::mime::EncodeHeaders;
|
||||
use crate::body::{async_impl, sync_impl, MimeMulti, MimePartEncoder, TokenStatus, XPart};
|
||||
use crate::headers::Headers;
|
||||
|
||||
/// `MimeMultiEncoder` can get a [`MimeMulti`] to encode into data that can be
|
||||
/// transmitted.
|
||||
///
|
||||
/// [`MimeMulti`]: MimeMulti
|
||||
///
|
||||
@@ -91,7 +84,8 @@ use std::collections::VecDeque;
|
||||
/// v_str.extend_from_slice(&buf[..len]);
|
||||
/// }
|
||||
/// assert_eq!(v_size, vec![30, 30, 30, 30, 30, 30, 13]);
|
||||
/// assert_eq!(v_str,
|
||||
/// assert_eq!(
|
||||
/// v_str,
|
||||
/// b"--abcde\r\nkey3:value3\r\n\r\n33333\r\n--abcde\r\n\
|
||||
/// content-type:multipart/mixed; boundary=abc\r\n\r\n--abc\r\n\
|
||||
/// key1:value1\r\n\r\n111111\r\n--abc\r\nkey2:value2\r\n\r\n22222\r\n\
|
||||
@@ -101,7 +95,8 @@ use std::collections::VecDeque;
|
||||
#[derive(Debug)]
|
||||
pub struct MimeMultiEncoder<'a> {
|
||||
stages: VecDeque<MultiStage<'a>>,
|
||||
headers: Option<Headers>, // default is not part, don't encode headers
|
||||
// default is not part, don't encode headers
|
||||
headers: Option<Headers>,
|
||||
src_idx: usize,
|
||||
src: Vec<u8>,
|
||||
}
|
||||
@@ -116,9 +111,7 @@ impl<'a> MimeMultiEncoder<'a> {
|
||||
/// ```
|
||||
/// use ylong_http::body::{MimeMulti, MimeMultiEncoder};
|
||||
///
|
||||
/// let multi = MimeMulti::builder()
|
||||
/// .build()
|
||||
/// .unwrap();
|
||||
/// let multi = MimeMulti::builder().build().unwrap();
|
||||
/// let multi_encoder = MimeMultiEncoder::from_multi(multi);
|
||||
/// ```
|
||||
pub fn from_multi(multi: MimeMulti<'a>) -> Self {
|
||||
@@ -360,11 +353,16 @@ impl async_impl::Body for MimeMultiEncoder<'_> {
|
||||
|
||||
#[derive(Debug)]
|
||||
enum MultiStage<'a> {
|
||||
Crlf, // \r\n
|
||||
Dash, // --
|
||||
Boundary(Vec<u8>), // boundary
|
||||
Headers(EncodeHeaders), // headers
|
||||
MimePart(MimePartEncoder<'a>), // part
|
||||
// \r\n
|
||||
Crlf,
|
||||
// --
|
||||
Dash,
|
||||
// boundary
|
||||
Boundary(Vec<u8>),
|
||||
// headers
|
||||
Headers(EncodeHeaders),
|
||||
// part
|
||||
MimePart(MimePartEncoder<'a>),
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -440,7 +438,8 @@ mod ut_mime_multi_encoder {
|
||||
/// 1. Creates a `MimeMulti`.
|
||||
/// 2. Creates several `MimePart`, sets headers, sets body, builds.
|
||||
/// 3. Adds `MimePart` to `MimeMulti` by `add_part`.
|
||||
/// 4. Builds a `MimeMultiEncoder` by `from_multi` and encodes synchronously.
|
||||
/// 4. Builds a `MimeMultiEncoder` by `from_multi` and encodes
|
||||
/// synchronously.
|
||||
/// 5. Checks whether the result is correct.
|
||||
#[test]
|
||||
fn ut_mime_multi_encoder_data_many_parts() {
|
||||
@@ -477,7 +476,8 @@ mod ut_mime_multi_encoder {
|
||||
/// 1. Creates a `MimeMulti`.
|
||||
/// 2. Creates several `MimePart`, sets headers, sets body, builds.
|
||||
/// 3. Creates a main `MimeMulti`, adds parts.
|
||||
/// 4. Builds a `MimeMultiEncoder` by `from_multi` and encodes synchronously.
|
||||
/// 4. Builds a `MimeMultiEncoder` by `from_multi` and encodes
|
||||
/// synchronously.
|
||||
/// 5. Checks whether the result is correct.
|
||||
#[test]
|
||||
fn ut_mime_multi_encoder_data_many_parts_nesting() {
|
||||
@@ -539,7 +539,8 @@ mod ut_mime_multi_encoder {
|
||||
/// 1. Creates a `MimeMulti`.
|
||||
/// 2. Creates several `MimePart`, sets headers, sets body, builds.
|
||||
/// 3. Creates a main `MimeMulti`, adds parts.
|
||||
/// 4. Builds a `MimeMultiEncoder` by `from_multi` and encodes asynchronously.
|
||||
/// 4. Builds a `MimeMultiEncoder` by `from_multi` and encodes
|
||||
/// asynchronously.
|
||||
/// 5. Checks whether the result is correct.
|
||||
#[cfg(feature = "tokio_base")]
|
||||
#[tokio::test]
|
||||
|
||||
@@ -11,26 +11,19 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::{
|
||||
body::{
|
||||
async_impl,
|
||||
mime::{
|
||||
common::{data_copy, SizeResult, TokenStatus},
|
||||
EncodeHeaders, MixFrom, PartStatus,
|
||||
},
|
||||
sync_impl, MimePart,
|
||||
},
|
||||
error::{ErrorKind, HttpError},
|
||||
};
|
||||
use crate::{AsyncRead, ReadBuf};
|
||||
use core::{
|
||||
convert::TryFrom,
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
use core::convert::TryFrom;
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll};
|
||||
use std::io::Read;
|
||||
|
||||
/// `MimePartEncoder` can get a [`MimePart`] to encode into data that can be transmitted.
|
||||
use crate::body::mime::common::{data_copy, SizeResult, TokenStatus};
|
||||
use crate::body::mime::{EncodeHeaders, MixFrom, PartStatus};
|
||||
use crate::body::{async_impl, sync_impl, MimePart};
|
||||
use crate::error::{ErrorKind, HttpError};
|
||||
use crate::{AsyncRead, ReadBuf};
|
||||
|
||||
/// `MimePartEncoder` can get a [`MimePart`] to encode into data that can be
|
||||
/// transmitted.
|
||||
///
|
||||
/// [`MimePart`]: MimePart
|
||||
///
|
||||
@@ -63,11 +56,14 @@ use std::io::Read;
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct MimePartEncoder<'a> {
|
||||
stage: PartStatus, // Encode stage now
|
||||
// Encode stage now
|
||||
stage: PartStatus,
|
||||
headers_encode: EncodeHeaders,
|
||||
body: Option<MixFrom<'a>>,
|
||||
src: Vec<u8>, // src which is need to encode
|
||||
src_idx: usize, // index of src
|
||||
// src which is need to encode
|
||||
src: Vec<u8>,
|
||||
// index of src
|
||||
src_idx: usize,
|
||||
}
|
||||
|
||||
impl<'a> MimePartEncoder<'a> {
|
||||
@@ -80,9 +76,7 @@ impl<'a> MimePartEncoder<'a> {
|
||||
/// ```
|
||||
/// use ylong_http::body::{MimePart, MimePartEncoder};
|
||||
///
|
||||
/// let part = MimePart::builder()
|
||||
/// .build()
|
||||
/// .unwrap();
|
||||
/// let part = MimePart::builder().build().unwrap();
|
||||
/// let part_encoder = MimePartEncoder::from_part(part);
|
||||
/// ```
|
||||
pub fn from_part(part: MimePart<'a>) -> Self {
|
||||
@@ -108,7 +102,8 @@ impl<'a> MimePartEncoder<'a> {
|
||||
PartStatus::Headers => {
|
||||
self.stage = PartStatus::Crlf;
|
||||
if self.body.is_some() {
|
||||
self.src = b"\r\n".to_vec(); // has body, so has Crlf
|
||||
// has body, so has Crlf
|
||||
self.src = b"\r\n".to_vec();
|
||||
} else {
|
||||
self.src = vec![];
|
||||
}
|
||||
@@ -116,7 +111,8 @@ impl<'a> MimePartEncoder<'a> {
|
||||
}
|
||||
PartStatus::Crlf => {
|
||||
self.stage = PartStatus::Body;
|
||||
self.src_idx = 0; // Just record index
|
||||
// Just record index
|
||||
self.src_idx = 0;
|
||||
}
|
||||
PartStatus::Body => {
|
||||
self.stage = PartStatus::End;
|
||||
@@ -232,10 +228,8 @@ impl async_impl::Body for MimePartEncoder<'_> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod ut_mime_part_encoder {
|
||||
use crate::{
|
||||
body::{async_impl, sync_impl, MimePart, MimePartEncoder},
|
||||
headers::Headers,
|
||||
};
|
||||
use crate::body::{async_impl, sync_impl, MimePart, MimePartEncoder};
|
||||
use crate::headers::Headers;
|
||||
|
||||
/// UT test cases for `syn::Body::data` of `MimePartEncoder`.
|
||||
///
|
||||
|
||||
@@ -62,8 +62,9 @@ macro_rules! part_encode_compare {
|
||||
},
|
||||
);
|
||||
|
||||
// default 1
|
||||
#[allow(unused_assignments, unused_mut)]
|
||||
let mut len = 1; // default 1
|
||||
let mut len = 1;
|
||||
|
||||
$(len = $size;)?
|
||||
let mut buf = vec![0u8; len];
|
||||
@@ -107,9 +108,9 @@ macro_rules! part_encode_compare {
|
||||
$(BodyAsyncReader: $body4,)?
|
||||
},
|
||||
);
|
||||
|
||||
// default 1
|
||||
#[allow(unused_assignments, unused_mut)]
|
||||
let mut len = 1; // default 1
|
||||
let mut len = 1;
|
||||
|
||||
$(len = $size;)?
|
||||
let mut buf = vec![0u8; len];
|
||||
|
||||
@@ -11,10 +11,11 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::error::{ErrorKind, HttpError};
|
||||
use core::str;
|
||||
use std::path::Path;
|
||||
|
||||
use crate::error::{ErrorKind, HttpError};
|
||||
|
||||
/// A type that defines the general structure of the `MIME` media typing system.
|
||||
///
|
||||
/// A `MIME` type most-commonly consists of just two parts:
|
||||
@@ -22,12 +23,12 @@ use std::path::Path;
|
||||
/// - `Type`
|
||||
/// - `Subtype`
|
||||
///
|
||||
/// `Type` and `SubType` are separated by a slash (/) — with no whitespace between:
|
||||
/// `Type` and `SubType` are separated by a slash (/) — with no whitespace
|
||||
/// between:
|
||||
///
|
||||
/// ```type/subtype```
|
||||
///
|
||||
///
|
||||
/// It is case-insensitive but are traditionally written in lowercase, such as:
|
||||
///
|
||||
/// ```application/octet-stream```.
|
||||
///
|
||||
/// # Examples
|
||||
@@ -42,7 +43,8 @@ use std::path::Path;
|
||||
pub struct MimeType<'a> {
|
||||
tag: MimeTypeTag,
|
||||
bytes: &'a [u8],
|
||||
slash: usize, // Index of '/'.
|
||||
// Index of '/'.
|
||||
slash: usize,
|
||||
}
|
||||
|
||||
impl<'a> MimeType<'a> {
|
||||
@@ -85,6 +87,7 @@ impl<'a> MimeType<'a> {
|
||||
///
|
||||
/// ```
|
||||
/// use std::path::Path;
|
||||
///
|
||||
/// use ylong_http::body::MimeType;
|
||||
///
|
||||
/// let path = Path::new("./foo/bar.pdf");
|
||||
@@ -398,7 +401,8 @@ mime!(
|
||||
"wps", "application/vnd.ms-works", 11, MimeTypeTag::Application;
|
||||
"hlp", "application/winhlp", 11, MimeTypeTag::Application;
|
||||
"bcpio", "application/x-bcpio", 11, MimeTypeTag::Application;
|
||||
"cdf", "application/x-cdf", 11, MimeTypeTag::Application; // "cdf" also can be "application/x-netcdf"
|
||||
// "cdf" also can be "application/x-netcdf"
|
||||
"cdf", "application/x-cdf", 11, MimeTypeTag::Application;
|
||||
"z", "application/x-compress", 11, MimeTypeTag::Application;
|
||||
"tgz", "application/x-compressed", 11, MimeTypeTag::Application;
|
||||
"cpio", "application/x-cpio", 11, MimeTypeTag::Application;
|
||||
@@ -555,7 +559,8 @@ enum MimeTypeTag {
|
||||
Multipart,
|
||||
Text,
|
||||
Video,
|
||||
Xnew, // A type not included in the standard, beginning with `x-`
|
||||
// A type not included in the standard, beginning with `x-`
|
||||
Xnew,
|
||||
}
|
||||
|
||||
impl MimeTypeTag {
|
||||
@@ -772,9 +777,10 @@ mod ut_mime {
|
||||
/// 2. Checks if the test results are correct.
|
||||
#[test]
|
||||
fn ut_mime_type_from_path() {
|
||||
use crate::error::HttpError;
|
||||
use std::path::Path;
|
||||
|
||||
use crate::error::HttpError;
|
||||
|
||||
assert_eq!(
|
||||
MimeType::from_path(Path::new("./foo/bar.evy")),
|
||||
MimeType::from_bytes(b"application/envoy")
|
||||
|
||||
@@ -21,27 +21,25 @@ mod decode;
|
||||
mod encode;
|
||||
mod mimetype;
|
||||
|
||||
pub use common::{MimeMulti, MimeMultiBuilder, MimePart, MimePartBuilder, TokenStatus, XPart};
|
||||
pub use decode::MimeMultiDecoder;
|
||||
pub use encode::MimeMultiEncoder;
|
||||
pub use encode::MimePartEncoder;
|
||||
pub use mimetype::MimeType;
|
||||
use std::io::Cursor;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
use std::vec::IntoIter;
|
||||
|
||||
pub(crate) use common::{
|
||||
DecodeHeaders, EncodeHeaders, HeaderStatus, MixFrom, PartStatus, CR, CRLF, HTAB, LF, SP,
|
||||
};
|
||||
pub use common::{MimeMulti, MimeMultiBuilder, MimePart, MimePartBuilder, TokenStatus, XPart};
|
||||
pub use decode::MimeMultiDecoder;
|
||||
pub(crate) use decode::MimePartDecoder;
|
||||
pub use encode::{MimeMultiEncoder, MimePartEncoder};
|
||||
pub use mimetype::MimeType;
|
||||
|
||||
// TODO: reuse mime later.
|
||||
|
||||
// TODO: Adapter, remove this later.
|
||||
use crate::body::async_impl::{poll_read, Body};
|
||||
|
||||
use crate::{AsyncRead, ReadBuf};
|
||||
use std::io::Cursor;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
use std::vec::IntoIter;
|
||||
|
||||
/// A structure that helps you build a `multipart/form-data` message.
|
||||
///
|
||||
@@ -85,8 +83,7 @@ impl MultiPart {
|
||||
/// ```
|
||||
/// # use ylong_http::body::{MultiPart, Part};
|
||||
///
|
||||
/// let multipart = MultiPart::new()
|
||||
/// .part(Part::new().name("name").body("xiaoming"));
|
||||
/// let multipart = MultiPart::new().part(Part::new().name("name").body("xiaoming"));
|
||||
/// ```
|
||||
pub fn part(mut self, part: Part) -> Self {
|
||||
self.parts.push(part);
|
||||
@@ -115,8 +112,7 @@ impl MultiPart {
|
||||
/// ```
|
||||
/// # use ylong_http::body::{MultiPart, Part};
|
||||
///
|
||||
/// let multipart = MultiPart::new()
|
||||
/// .part(Part::new().name("name").body("xiaoming"));
|
||||
/// let multipart = MultiPart::new().part(Part::new().name("name").body("xiaoming"));
|
||||
///
|
||||
/// let bytes = multipart.total_bytes();
|
||||
/// ```
|
||||
@@ -571,10 +567,8 @@ fn xor_shift() -> u64 {
|
||||
|
||||
#[cfg(test)]
|
||||
mod ut_mime {
|
||||
use crate::body::{
|
||||
mime::{gen_boundary, MultiPartState, ReadStatus},
|
||||
MultiPart, Part,
|
||||
};
|
||||
use crate::body::mime::{gen_boundary, MultiPartState, ReadStatus};
|
||||
use crate::body::{MultiPart, Part};
|
||||
|
||||
/// UT test cases for `gen_boundar`.
|
||||
///
|
||||
@@ -620,7 +614,8 @@ mod ut_mime {
|
||||
assert!(part.body.is_none());
|
||||
}
|
||||
|
||||
/// UT test cases for `Part::name`, `Part::name`, `Part::file_name` and `Part::body`.
|
||||
/// UT test cases for `Part::name`, `Part::name`, `Part::file_name` and
|
||||
/// `Part::body`.
|
||||
///
|
||||
/// # Brief
|
||||
/// 1. Creates a `Part` and sets values.
|
||||
|
||||
+11
-28
@@ -38,7 +38,6 @@
|
||||
//!
|
||||
//! [`EmptyBody`]: EmptyBody
|
||||
//! [`TextBody`]: TextBody
|
||||
//!
|
||||
|
||||
// TODO: Support `Trailers`.
|
||||
|
||||
@@ -57,10 +56,11 @@ pub use text::{Text, TextBody, TextBodyDecoder};
|
||||
|
||||
/// Synchronous `Body` trait definition.
|
||||
pub mod sync_impl {
|
||||
use crate::headers::Headers;
|
||||
use std::error::Error;
|
||||
use std::io::Read;
|
||||
|
||||
use crate::headers::Headers;
|
||||
|
||||
/// The `sync_impl::Body` trait allows for reading body data synchronously.
|
||||
///
|
||||
/// # Examples
|
||||
@@ -171,14 +171,16 @@ pub mod sync_impl {
|
||||
|
||||
/// Asynchronous `Body` trait definition.
|
||||
pub mod async_impl {
|
||||
use crate::headers::Headers;
|
||||
use crate::{AsyncRead, ReadBuf};
|
||||
use core::future::Future;
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll};
|
||||
use std::error::Error;
|
||||
|
||||
/// The `async_impl::Body` trait allows for reading body data asynchronously.
|
||||
use crate::headers::Headers;
|
||||
use crate::{AsyncRead, ReadBuf};
|
||||
|
||||
/// The `async_impl::Body` trait allows for reading body data
|
||||
/// asynchronously.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -381,35 +383,15 @@ pub mod async_impl {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: The following code will be useful in the future.
|
||||
// impl<T: AsyncRead + Unpin> Body for T {
|
||||
// type Error = std::io::Error;
|
||||
//
|
||||
// fn poll_data(
|
||||
// self: Pin<&mut Self>,
|
||||
// cx: &mut Context<'_>,
|
||||
// buf: &mut [u8],
|
||||
// ) -> Poll<Result<usize, Self::Error>> {
|
||||
// let mut read_buf = ReadBuf::new(buf);
|
||||
// let filled = read_buf.filled().len();
|
||||
// match self.poll_read(cx, &mut read_buf) {
|
||||
// Poll::Ready(Ok(())) => {
|
||||
// let new_filled = read_buf.filled().len();
|
||||
// Poll::Ready(Ok(new_filled - filled))
|
||||
// }
|
||||
// Poll::Ready(Err(e)) => Poll::Ready(Err(e)),
|
||||
// Poll::Pending => Poll::Pending,
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
// Type definitions of the origin of the body data.
|
||||
pub(crate) mod origin {
|
||||
use crate::AsyncRead;
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use std::io::Read;
|
||||
|
||||
use crate::AsyncRead;
|
||||
|
||||
/// A type that represents the body data is from memory.
|
||||
pub struct FromBytes<'a> {
|
||||
pub(crate) bytes: &'a [u8],
|
||||
@@ -536,7 +518,8 @@ mod ut_mod {
|
||||
/// # Brief
|
||||
/// 1. Creates a `async_impl::Body` object.
|
||||
/// 2. Gets its mutable reference.
|
||||
/// 3. Calls its `async_impl::Body::data` method and then checks the results.
|
||||
/// 3. Calls its `async_impl::Body::data` method and then checks the
|
||||
/// results.
|
||||
#[cfg(feature = "tokio_base")]
|
||||
#[tokio::test]
|
||||
async fn ut_asyn_body_mut_asyn_body_data() {
|
||||
|
||||
+20
-12
@@ -11,15 +11,16 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::origin::{FromAsyncReader, FromBytes, FromReader};
|
||||
use super::{async_impl, sync_impl};
|
||||
use crate::body::origin::FromAsyncBody;
|
||||
use crate::{AsyncRead, ReadBuf};
|
||||
use core::cmp::min;
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll};
|
||||
use std::io::{Error, Read};
|
||||
|
||||
use super::origin::{FromAsyncReader, FromBytes, FromReader};
|
||||
use super::{async_impl, sync_impl};
|
||||
use crate::body::origin::FromAsyncBody;
|
||||
use crate::{AsyncRead, ReadBuf};
|
||||
|
||||
/// `TextBody` is used to represent the body of plain text type.
|
||||
///
|
||||
/// You can create a `TextBody` in a variety of ways, such as reading from
|
||||
@@ -38,7 +39,8 @@ use std::io::{Error, Read};
|
||||
/// let body = TextBody::from_bytes(text.as_bytes());
|
||||
/// ```
|
||||
///
|
||||
/// This type of `TextBody` implements both [`sync_impl::Body`] and [`async_impl::Body`].
|
||||
/// This type of `TextBody` implements both [`sync_impl::Body`] and
|
||||
/// [`async_impl::Body`].
|
||||
///
|
||||
/// # Read From Reader
|
||||
///
|
||||
@@ -76,8 +78,9 @@ use std::io::{Error, Read};
|
||||
///
|
||||
/// # Read Body Content
|
||||
///
|
||||
/// After you have created a `TextBody`, you can use the methods of [`sync_impl::Body`]
|
||||
/// or [`async_impl::Body`] to read data, like the examples below:
|
||||
/// After you have created a `TextBody`, you can use the methods of
|
||||
/// [`sync_impl::Body`] or [`async_impl::Body`] to read data, like the examples
|
||||
/// below:
|
||||
///
|
||||
/// sync:
|
||||
///
|
||||
@@ -386,7 +389,8 @@ impl TextBodyDecoder {
|
||||
}
|
||||
|
||||
/// Decode result of a text buffer.
|
||||
/// The `state` records the decode status, and the data records the decoded data.
|
||||
/// The `state` records the decode status, and the data records the decoded
|
||||
/// data.
|
||||
#[derive(Debug)]
|
||||
pub struct Text<'a> {
|
||||
state: TextState,
|
||||
@@ -394,7 +398,8 @@ pub struct Text<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Text<'a> {
|
||||
/// Checks whether this `Text` contains the last valid part of the body data.
|
||||
/// Checks whether this `Text` contains the last valid part of the body
|
||||
/// data.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -539,7 +544,8 @@ mod ut_text {
|
||||
///
|
||||
/// # Brief
|
||||
/// 1. Creates a `TextBody<FromBytes<'_>>`.
|
||||
/// 2. Calls its `async_impl::Body::data` method and then checks the results.
|
||||
/// 2. Calls its `async_impl::Body::data` method and then checks the
|
||||
/// results.
|
||||
#[cfg(feature = "tokio_base")]
|
||||
#[tokio::test]
|
||||
async fn ut_text_body_from_bytes_asyn_data() {
|
||||
@@ -588,11 +594,13 @@ mod ut_text {
|
||||
assert_eq!(&buf[..size], b"d!");
|
||||
}
|
||||
|
||||
/// UT test cases for `async_impl::Body::data` of `TextBody<FromAsyncReader<T>>`.
|
||||
/// UT test cases for `async_impl::Body::data` of
|
||||
/// `TextBody<FromAsyncReader<T>>`.
|
||||
///
|
||||
/// # Brief
|
||||
/// 1. Creates a `TextBody<FromAsyncReader<T>>`.
|
||||
/// 2. Calls its `async_impl::Body::data` method and then checks the results.
|
||||
/// 2. Calls its `async_impl::Body::data` method and then checks the
|
||||
/// results.
|
||||
#[cfg(feature = "tokio_base")]
|
||||
#[tokio::test]
|
||||
async fn ut_text_body_from_async_reader_asyn_data() {
|
||||
|
||||
@@ -15,20 +15,20 @@
|
||||
//!
|
||||
//! This module provide unified encapsulation of HTTP errors.
|
||||
//!
|
||||
//! [`HttpError`] encapsulates error information related to all http protocols including `UriError`, `H1Error`, etc.
|
||||
//! [`HttpError`] encapsulates error information related to all http protocols
|
||||
//! including `UriError`, `H1Error`, etc.
|
||||
//!
|
||||
//! [`HttpError`]: HttpError
|
||||
|
||||
use crate::request::uri::InvalidUri;
|
||||
use core::fmt::{Debug, Display, Formatter};
|
||||
use std::convert::Infallible;
|
||||
use std::error::Error;
|
||||
|
||||
#[cfg(feature = "http1_1")]
|
||||
use crate::h1::H1Error;
|
||||
|
||||
#[cfg(feature = "http2")]
|
||||
use crate::h2::H2Error;
|
||||
use crate::request::uri::InvalidUri;
|
||||
|
||||
/// Errors that may occur when using this crate.
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
|
||||
@@ -18,6 +18,5 @@ mod request;
|
||||
pub(crate) mod response;
|
||||
|
||||
pub(crate) use error::H1Error;
|
||||
|
||||
pub use request::RequestEncoder;
|
||||
pub use response::ResponseDecoder;
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
//! .method("GET")
|
||||
//! .url("www.example.com")
|
||||
//! .version("HTTP/1.1")
|
||||
//! .header("ACCEPT","text/html")
|
||||
//! .header("ACCEPT", "text/html")
|
||||
//! .body(())
|
||||
//! .unwrap();
|
||||
//!
|
||||
@@ -59,13 +59,14 @@
|
||||
//! assert_eq!(message.as_slice(), result.as_bytes());
|
||||
//! ```
|
||||
|
||||
use std::io::Read;
|
||||
|
||||
use crate::error::{ErrorKind, HttpError};
|
||||
use crate::headers::{HeaderName, Headers, HeadersIntoIter};
|
||||
use crate::request::method::Method;
|
||||
use crate::request::uri::Uri;
|
||||
use crate::request::RequestPart;
|
||||
use crate::version::Version;
|
||||
use std::io::Read;
|
||||
|
||||
/// A encoder that is used to encode request message in `HTTP/1.1` format.
|
||||
///
|
||||
@@ -82,7 +83,7 @@ use std::io::Read;
|
||||
/// .method("GET")
|
||||
/// .url("www.example.com")
|
||||
/// .version("HTTP/1.1")
|
||||
/// .header("ACCEPT","text/html")
|
||||
/// .header("ACCEPT", "text/html")
|
||||
/// .body(())
|
||||
/// .unwrap();
|
||||
///
|
||||
@@ -128,21 +129,32 @@ pub struct RequestEncoder {
|
||||
}
|
||||
|
||||
enum EncodeState {
|
||||
Method, // "Method" phase of encoding request-message.
|
||||
MethodSp, // "MethodSp" phase of encoding whitespace after method.
|
||||
Uri, // "Uri" phase of encoding request-message.
|
||||
UriSp, // "UriSp" phase of encoding whitespace after uri.
|
||||
Version, // "Version" phase of encoding request-message.
|
||||
VersionCrlf, // "VersionCrlf" phase of encoding whitespace after version.
|
||||
Header, // "Header" phase of encoding request-message.
|
||||
HeaderCrlf, // "HeaderCrlf" phase of encoding /r/n after header.
|
||||
EncodeFinished, // "EncodeFinished" phase of finishing the encoding.
|
||||
// "Method" phase of encoding request-message.
|
||||
Method,
|
||||
// "MethodSp" phase of encoding whitespace after method.
|
||||
MethodSp,
|
||||
// "Uri" phase of encoding request-message.
|
||||
Uri,
|
||||
// "UriSp" phase of encoding whitespace after uri.
|
||||
UriSp,
|
||||
// "Version" phase of encoding request-message.
|
||||
Version,
|
||||
// "VersionCrlf" phase of encoding whitespace after version.
|
||||
VersionCrlf,
|
||||
// "Header" phase of encoding request-message.
|
||||
Header,
|
||||
// "HeaderCrlf" phase of encoding /r/n after header.
|
||||
HeaderCrlf,
|
||||
// "EncodeFinished" phase of finishing the encoding.
|
||||
EncodeFinished,
|
||||
}
|
||||
|
||||
// Component encoding status.
|
||||
enum TokenStatus<T, E> {
|
||||
Complete(T), // The current component is completely encoded.
|
||||
Partial(E), // The current component is partially encoded.
|
||||
// The current component is completely encoded.
|
||||
Complete(T),
|
||||
// The current component is partially encoded.
|
||||
Partial(E),
|
||||
}
|
||||
|
||||
type TokenResult<T> = Result<TokenStatus<usize, T>, HttpError>;
|
||||
@@ -160,7 +172,7 @@ impl RequestEncoder {
|
||||
/// .method("GET")
|
||||
/// .url("www.example.com")
|
||||
/// .version("HTTP/1.1")
|
||||
/// .header("ACCEPT","text/html")
|
||||
/// .header("ACCEPT", "text/html")
|
||||
/// .body(())
|
||||
/// .unwrap();
|
||||
///
|
||||
@@ -272,7 +284,7 @@ impl RequestEncoder {
|
||||
/// .method("GET")
|
||||
/// .url("www.example.com")
|
||||
/// .version("HTTP/1.1")
|
||||
/// .header("ACCEPT","text/html")
|
||||
/// .header("ACCEPT", "text/html")
|
||||
/// .body(())
|
||||
/// .unwrap();
|
||||
///
|
||||
@@ -284,7 +296,10 @@ impl RequestEncoder {
|
||||
/// let mut buf = [0u8; 1024];
|
||||
/// let size = encoder.encode(&mut buf).unwrap();
|
||||
/// // If you set the `is_proxy` flag, the uri will be encoded as a relative path.
|
||||
/// assert_eq!(&buf[..size], b"GET / HTTP/1.1\r\naccept:text/html\r\n\r\n".as_slice());
|
||||
/// assert_eq!(
|
||||
/// &buf[..size],
|
||||
/// b"GET / HTTP/1.1\r\naccept:text/html\r\n\r\n".as_slice()
|
||||
/// );
|
||||
/// ```
|
||||
pub fn set_proxy(&mut self, is_proxy: bool) {
|
||||
self.is_proxy = is_proxy;
|
||||
@@ -667,7 +682,8 @@ mod ut_request_encoder {
|
||||
/// 1. Creates a `Request` by calling methods of `Request::builder`.
|
||||
/// 2. Gets a request part by calling `Request::into_parts`.
|
||||
/// 3. Creates a `RequestEncoder` by calling `RequestBuilder::new`.
|
||||
/// 4. Calls `RequestEncoder::encode` method in a loop and collects the results.
|
||||
/// 4. Calls `RequestEncoder::encode` method in a loop and collects the
|
||||
/// results.
|
||||
/// 5. Checks if the test result is correct.
|
||||
#[test]
|
||||
fn ut_request_encoder_encode_1() {
|
||||
|
||||
@@ -11,13 +11,14 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use core::mem::take;
|
||||
|
||||
use crate::error::{ErrorKind, HttpError};
|
||||
use crate::h1::H1Error;
|
||||
use crate::headers::Headers;
|
||||
use crate::response::status::StatusCode;
|
||||
use crate::response::ResponsePart;
|
||||
use crate::version::Version;
|
||||
use core::mem::take;
|
||||
|
||||
/// `HTTP/1.1` response decoder, which support decoding multi-segment byte
|
||||
/// streams into `Response`.
|
||||
@@ -30,11 +31,7 @@ use core::mem::take;
|
||||
///
|
||||
/// // The complete message is:
|
||||
/// // "HTTP/1.1 304 OK\r\nContent-Length:4\r\n\r\nbody"
|
||||
/// let strings = [
|
||||
/// "HTTP/1.1 304 OK\r\nCon",
|
||||
/// "tent-Length:",
|
||||
/// "4\r\n\r\nbody",
|
||||
/// ];
|
||||
/// let strings = ["HTTP/1.1 304 OK\r\nCon", "tent-Length:", "4\r\n\r\nbody"];
|
||||
///
|
||||
/// // We need to create a decoder first.
|
||||
/// let mut decoder = ResponseDecoder::new();
|
||||
@@ -50,26 +47,40 @@ use core::mem::take;
|
||||
/// // Then we can use the decode result.
|
||||
/// assert_eq!(part.version.as_str(), "HTTP/1.1");
|
||||
/// assert_eq!(part.status.as_u16(), 304);
|
||||
/// assert_eq!(part.headers.get("content-length").unwrap().to_str().unwrap(), "4");
|
||||
/// assert_eq!(
|
||||
/// part.headers
|
||||
/// .get("content-length")
|
||||
/// .unwrap()
|
||||
/// .to_str()
|
||||
/// .unwrap(),
|
||||
/// "4"
|
||||
/// );
|
||||
/// assert_eq!(body, b"body");
|
||||
/// ```
|
||||
pub struct ResponseDecoder {
|
||||
stage: ParseStage, // Parsing phase, corresponding to each component of response-message.
|
||||
// Parsing phase, corresponding to each component of response-message.
|
||||
stage: ParseStage,
|
||||
version: Option<Version>,
|
||||
status_code: Option<StatusCode>,
|
||||
headers: Option<Headers>,
|
||||
head_key: Vec<u8>, // Cache the parsed header key.
|
||||
rest: Vec<u8>, // Cache the response-message component whose current bytes segment is incomplete,
|
||||
new_line: bool, // The value is true when the last byte of the current byte segment is CR.
|
||||
// Cache the parsed header key.
|
||||
head_key: Vec<u8>,
|
||||
// Cache the response-message component whose current bytes segment is incomplete
|
||||
rest: Vec<u8>,
|
||||
// The value is true when the last byte of the current byte segment is CR.
|
||||
new_line: bool,
|
||||
}
|
||||
|
||||
// Component parsing status
|
||||
enum TokenStatus<T, E> {
|
||||
Complete(T), // The current component is completely parsed.
|
||||
Partial(E), // The current component is not completely parsed.
|
||||
// The current component is completely parsed.
|
||||
Complete(T),
|
||||
// The current component is not completely parsed.
|
||||
Partial(E),
|
||||
}
|
||||
|
||||
// ResponseDecoder parsing phase, All components of response-message are as follows:
|
||||
// ResponseDecoder parsing phase, All components of response-message are as
|
||||
// follows:
|
||||
// ---------------------------------------------------------
|
||||
// | HTTP-version SP status-code SP [ reason-phrase ]CRLF | // status-line
|
||||
// | *( field-name ":" OWS field-value OWS CRLF ) | // field-line
|
||||
@@ -78,13 +89,20 @@ enum TokenStatus<T, E> {
|
||||
// ---------------------------------------------------------
|
||||
#[derive(Clone)]
|
||||
enum ParseStage {
|
||||
Initial, // Decoder initialization phase, The decoder parses the bytes for the first time.
|
||||
Version, // "HTTP-version" phase of parsing response-message
|
||||
StatusCode, // "status-code" phase of parsing response-message
|
||||
Reason, // "reason-phrase" phase of parsing response-message
|
||||
StatusCrlf, // CRLF after "reason-phrase" of parsing response-message
|
||||
Header(HeaderStage), // "field-line" phase of parsing response-message
|
||||
BlankLine, // CRLF after "field-line" of parsing response-message
|
||||
// Decoder initialization phase, The decoder parses the bytes for the first time.
|
||||
Initial,
|
||||
// "HTTP-version" phase of parsing response-message
|
||||
Version,
|
||||
// "status-code" phase of parsing response-message
|
||||
StatusCode,
|
||||
// "reason-phrase" phase of parsing response-message
|
||||
Reason,
|
||||
// CRLF after "reason-phrase" of parsing response-message
|
||||
StatusCrlf,
|
||||
// "field-line" phase of parsing response-message
|
||||
Header(HeaderStage),
|
||||
// CRLF after "field-line" of parsing response-message
|
||||
BlankLine,
|
||||
}
|
||||
|
||||
// Stage of parsing field-line, the filed line component is as follows:
|
||||
@@ -171,17 +189,14 @@ impl ResponseDecoder {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ylong_http::version::Version;
|
||||
/// use ylong_http::h1::ResponseDecoder;
|
||||
/// use ylong_http::version::Version;
|
||||
///
|
||||
/// let valid = [
|
||||
/// "HTTP/1.1",
|
||||
/// " 304 OK\r\n\r\n",
|
||||
/// ];
|
||||
/// let valid = ["HTTP/1.1", " 304 OK\r\n\r\n"];
|
||||
/// let mut decoder = ResponseDecoder::new();
|
||||
/// // Returns `Ok(None)` if decoder needs more bytes to decode.
|
||||
/// assert_eq!(decoder.decode(valid[0].as_bytes()), Ok(None));
|
||||
/// //Returns `ResponsePart` and a slice of bytes if decoder has complete decoding.
|
||||
/// // Returns `ResponsePart` and a slice of bytes if decoder has complete decoding.
|
||||
/// let (part, body) = decoder.decode(valid[1].as_bytes()).unwrap().unwrap();
|
||||
/// assert_eq!(part.version, Version::HTTP1_1);
|
||||
/// assert_eq!(part.status.as_u16(), 304);
|
||||
@@ -599,7 +614,8 @@ fn header_insert(
|
||||
let key = name.to_lowercase();
|
||||
let header_name = key.as_str();
|
||||
let header_value = value.as_str();
|
||||
// If the response contains headers with the same name, add them to one `Headers`.
|
||||
// If the response contains headers with the same name, add them to one
|
||||
// `Headers`.
|
||||
headers.append(header_name, header_value)?;
|
||||
Ok(Some(headers))
|
||||
}
|
||||
@@ -811,7 +827,8 @@ mod ut_decoder {
|
||||
/// UT test cases for `ResponseDecoder::decode`.
|
||||
///
|
||||
/// # Brief
|
||||
/// Decode a segmented transmission response and test `ParseStage` parsing rules.
|
||||
/// Decode a segmented transmission response and test `ParseStage` parsing
|
||||
/// rules.
|
||||
/// 1. Creates a `ResponseDecoder` by calling `ResponseDecoder::new`.
|
||||
/// 2. Decodes response bytes by calling `ResponseDecoder::decode`
|
||||
/// 3. Checks if the test result is correct.
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use super::{Frame, H2Error};
|
||||
use crate::error::ErrorKind::H2;
|
||||
use crate::h2;
|
||||
@@ -22,7 +24,6 @@ use crate::h2::frame::{
|
||||
};
|
||||
use crate::h2::{frame, HpackDecoder, Parts, Setting, Settings};
|
||||
use crate::headers::Headers;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
const FRAME_HEADER_LENGTH: usize = 9;
|
||||
const DEFAULT_MAX_FRAME_SIZE: u32 = 2 << 13;
|
||||
@@ -32,7 +33,8 @@ const DEFAULT_MAX_HEADER_LIST_SIZE: usize = 16 << 20;
|
||||
const MAX_INITIAL_WINDOW_SIZE: usize = (1 << 31) - 1;
|
||||
|
||||
/// A set of consecutive Frames.
|
||||
/// When Headers Frames or Continuation Frames are not End Headers, they are represented as `FrameKind::Partial`.
|
||||
/// When Headers Frames or Continuation Frames are not End Headers, they are
|
||||
/// represented as `FrameKind::Partial`.
|
||||
///
|
||||
/// - use `Frames` iterator.
|
||||
///
|
||||
@@ -52,7 +54,7 @@ const MAX_INITIAL_WINDOW_SIZE: usize = (1 << 31) - 1;
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ylong_http::h2::Frames;
|
||||
/// use ylong_http::h2::Frames;
|
||||
///
|
||||
/// # fn get_frames_into_iter(frames: Frames) {
|
||||
/// let mut iter = frames.into_iter();
|
||||
@@ -145,7 +147,8 @@ impl core::iter::IntoIterator for Frames {
|
||||
}
|
||||
}
|
||||
|
||||
/// When Headers Frames or Continuation Frames are not End Headers, they are represented as `FrameKind::Partial`.
|
||||
/// When Headers Frames or Continuation Frames are not End Headers, they are
|
||||
/// represented as `FrameKind::Partial`.
|
||||
pub enum FrameKind {
|
||||
/// PUSH_PROMISE or HEADRS frame parsing completed.
|
||||
Complete(Frame),
|
||||
@@ -164,7 +167,7 @@ pub enum FrameKind {
|
||||
/// decoder.set_max_header_list_size(30);
|
||||
/// let data_frame_bytes = &[0, 0, 5, 0, 0, 0, 0, 0, 1, b'h', b'e', b'l', b'l', b'o'];
|
||||
/// let decoded_frames = decoder.decode(data_frame_bytes).unwrap();
|
||||
/// let frame_kind =decoded_frames.iter().next().unwrap();
|
||||
/// let frame_kind = decoded_frames.iter().next().unwrap();
|
||||
/// ```
|
||||
pub struct FrameDecoder {
|
||||
buffer: Vec<u8>,
|
||||
@@ -176,7 +179,8 @@ pub struct FrameDecoder {
|
||||
// 9-byte header information of the current frame
|
||||
header: FrameHeader,
|
||||
hpack: HpackDecoderLayer,
|
||||
// The Headers Frame flags information is saved to ensure the continuity between Headers Frames and Continuation Frames.
|
||||
// The Headers Frame flags information is saved to ensure the continuity between Headers Frames
|
||||
// and Continuation Frames.
|
||||
continuations: Continuations,
|
||||
}
|
||||
|
||||
@@ -306,7 +310,8 @@ impl Frames {
|
||||
}
|
||||
|
||||
impl FrameDecoder {
|
||||
/// `FrameDecoder` constructor. Three parameters are defined in SETTINGS Frame.
|
||||
/// `FrameDecoder` constructor. Three parameters are defined in SETTINGS
|
||||
/// Frame.
|
||||
pub fn new() -> Self {
|
||||
FrameDecoder::default()
|
||||
}
|
||||
@@ -373,7 +378,8 @@ impl FrameDecoder {
|
||||
&mut self,
|
||||
buf: &'a [u8],
|
||||
) -> Result<Option<(&'a [u8], FrameKind)>, H2Error> {
|
||||
// Frames of other types or streams are not allowed between Headers Frame and Continuation Frame.
|
||||
// Frames of other types or streams are not allowed between Headers Frame and
|
||||
// Continuation Frame.
|
||||
if !self.continuations.is_end_headers
|
||||
&& (self.header.stream_id != self.continuations.stream_id
|
||||
|| self.header.frame_type != 9)
|
||||
@@ -714,7 +720,9 @@ impl FrameDecoder {
|
||||
self.continuations.is_end_headers = false;
|
||||
self.continuations.stream_id = self.header.stream_id;
|
||||
|
||||
// TODO Performance optimization, The storage structure Vec is optimized. When a complete field block exists in the buffer, fragments do not need to be copied to the Vec.
|
||||
// TODO Performance optimization, The storage structure Vec is optimized. When a
|
||||
// complete field block exists in the buffer, fragments do not need to be copied
|
||||
// to the Vec.
|
||||
self.hpack
|
||||
.hpack_decode(&buf[fragment_start_index..fragment_end_index])?;
|
||||
Ok(FrameKind::Partial)
|
||||
@@ -1257,7 +1265,8 @@ mod ut_frame_decoder {
|
||||
FrameKind: frames_iter.next().expect("take next frame from iterator failed !"),
|
||||
);
|
||||
|
||||
// continuation is ("content-length", "9"), so it will append to headers' content-length because of repeat.
|
||||
// continuation is ("content-length", "9"), so it will append to headers'
|
||||
// content-length because of repeat.
|
||||
check_complete_frame!(
|
||||
@header;
|
||||
FrameKind: frames_iter.next().expect("take next frame from iterator failed !"),
|
||||
@@ -1284,7 +1293,8 @@ mod ut_frame_decoder {
|
||||
///
|
||||
/// # Brief
|
||||
///
|
||||
/// Test the function of inserting HEADERS of other streams between HEADERS and CONTINUATION of the same stream.
|
||||
/// Test the function of inserting HEADERS of other streams between HEADERS
|
||||
/// and CONTINUATION of the same stream.
|
||||
/// 1. Creates a `FrameDecoder`.
|
||||
/// 2. Calls its `FrameDecoder::decode` method.
|
||||
/// 3. Checks the results.
|
||||
@@ -1305,7 +1315,8 @@ mod ut_frame_decoder {
|
||||
///
|
||||
/// # Brief
|
||||
///
|
||||
/// Test the function of inserting CONTINUATION of other streams between HEADERS and CONTINUATION of the same stream.
|
||||
/// Test the function of inserting CONTINUATION of other streams between
|
||||
/// HEADERS and CONTINUATION of the same stream.
|
||||
/// 1. Creates a `FrameDecoder`.
|
||||
/// 2. Calls its `FrameDecoder::decode` method.
|
||||
/// 3. Checks the results.
|
||||
@@ -1326,7 +1337,8 @@ mod ut_frame_decoder {
|
||||
///
|
||||
/// # Brief
|
||||
///
|
||||
/// Test a complete Request HEADERS Frames with padding and priority, the purpose is to test the method of `FrameFlags`.
|
||||
/// Test a complete Request HEADERS Frames with padding and priority, the
|
||||
/// purpose is to test the method of `FrameFlags`.
|
||||
///
|
||||
/// 1. Creates a `FrameDecoder`.
|
||||
/// 2. Calls its `FrameDecoder::decode` method.
|
||||
@@ -1384,7 +1396,8 @@ mod ut_frame_decoder {
|
||||
}
|
||||
}
|
||||
|
||||
// Tests the case where the payload length is not 8, which should return an error.
|
||||
// Tests the case where the payload length is not 8, which should return an
|
||||
// error.
|
||||
decoder.header.payload_length = 7;
|
||||
let result = decoder.decode_ping_payload(ping_payload);
|
||||
assert!(matches!(
|
||||
@@ -1405,12 +1418,16 @@ mod ut_frame_decoder {
|
||||
///
|
||||
/// # Brief
|
||||
///
|
||||
/// This test case checks the behavior of the `decode_priority_payload` method in two scenarios.
|
||||
/// This test case checks the behavior of the `decode_priority_payload`
|
||||
/// method in two scenarios.
|
||||
///
|
||||
/// 1. Creates a `FrameDecoder`.
|
||||
/// 2. Calls its `decode_priority_payload` method with a valid `priority_payload`.
|
||||
/// 3. Verifies the method correctly decodes the payload and returns the expected values.
|
||||
/// 4. Sets the `stream_id` in the header to 0 and checks if the method returns an error as expected.
|
||||
/// 2. Calls its `decode_priority_payload` method with a valid
|
||||
/// `priority_payload`.
|
||||
/// 3. Verifies the method correctly decodes the payload and returns the
|
||||
/// expected values.
|
||||
/// 4. Sets the `stream_id` in the header to 0 and checks if the method
|
||||
/// returns an error as expected.
|
||||
#[test]
|
||||
fn ut_decode_priority_payload() {
|
||||
let mut decoder = FrameDecoder::new();
|
||||
@@ -1449,12 +1466,16 @@ mod ut_frame_decoder {
|
||||
///
|
||||
/// # Brief
|
||||
///
|
||||
/// This test case checks the behavior of the `decode_goaway_payload` method in two scenarios.
|
||||
/// This test case checks the behavior of the `decode_goaway_payload` method
|
||||
/// in two scenarios.
|
||||
///
|
||||
/// 1. Creates a `FrameDecoder`.
|
||||
/// 2. Calls its `decode_goaway_payload` method with a valid `goaway_payload`.
|
||||
/// 3. Verifies the method correctly decodes the payload and returns the expected values.
|
||||
/// 4. Sets the `stream_id` in the header to a non-zero value and checks if the method returns an error as expected.
|
||||
/// 2. Calls its `decode_goaway_payload` method with a valid
|
||||
/// `goaway_payload`.
|
||||
/// 3. Verifies the method correctly decodes the payload and returns the
|
||||
/// expected values.
|
||||
/// 4. Sets the `stream_id` in the header to a non-zero value and checks if
|
||||
/// the method returns an error as expected.
|
||||
#[test]
|
||||
fn ut_decode_goaway_payload() {
|
||||
let mut decoder = FrameDecoder::new();
|
||||
@@ -1525,7 +1546,8 @@ mod ut_frame_decoder {
|
||||
}
|
||||
}
|
||||
|
||||
// Tests the case where the payload length is not 4, which should return an error.
|
||||
// Tests the case where the payload length is not 4, which should return an
|
||||
// error.
|
||||
decoder.header.payload_length = 5;
|
||||
let result = decoder.decode_window_update_payload(window_update_payload);
|
||||
assert!(matches!(
|
||||
@@ -1566,7 +1588,8 @@ mod ut_frame_decoder {
|
||||
}
|
||||
}
|
||||
|
||||
// Tests the case where the payload length is not 4, which should return an error.
|
||||
// Tests the case where the payload length is not 4, which should return an
|
||||
// error.
|
||||
decoder.header.payload_length = 5;
|
||||
let result = decoder.decode_reset_payload(reset_payload);
|
||||
assert!(matches!(
|
||||
@@ -1602,8 +1625,8 @@ mod ut_frame_decoder {
|
||||
stream_id: 0x0,
|
||||
};
|
||||
|
||||
// Mock a settings payload: [0x00, 0x01, 0x00, 0x00, 0x00, 0x80, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01]
|
||||
// Setting 1: Header Table Size, Value: 128
|
||||
// Mock a settings payload: [0x00, 0x01, 0x00, 0x00, 0x00, 0x80, 0x00, 0x02,
|
||||
// 0x00, 0x00, 0x00, 0x01] Setting 1: Header Table Size, Value: 128
|
||||
// Setting 2: Enable Push, Value: 1
|
||||
let settings_payload = &[
|
||||
0x00, 0x01, 0x00, 0x00, 0x00, 0x80, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01,
|
||||
@@ -1625,7 +1648,8 @@ mod ut_frame_decoder {
|
||||
}
|
||||
|
||||
// Test the case where the settings frame is an acknowledgment.
|
||||
// For this, we should set the ACK flag (0x1) and the payload length should be 0.
|
||||
// For this, we should set the ACK flag (0x1) and the payload length should be
|
||||
// 0.
|
||||
decoder.header = FrameHeader {
|
||||
payload_length: 0,
|
||||
frame_type: 0x4,
|
||||
@@ -1646,7 +1670,8 @@ mod ut_frame_decoder {
|
||||
}
|
||||
}
|
||||
|
||||
// Tests the case where the payload length is not a multiple of 6, which should return an error.
|
||||
// Tests the case where the payload length is not a multiple of 6, which should
|
||||
// return an error.
|
||||
decoder.header.payload_length = 5;
|
||||
let result = decoder.decode_settings_payload(settings_payload);
|
||||
assert!(matches!(
|
||||
@@ -1696,7 +1721,8 @@ mod ut_frame_decoder {
|
||||
FrameKind::Partial => {}
|
||||
}
|
||||
|
||||
// Tests the case where the payload length is less than the promised_stream_id size, which should return an error.
|
||||
// Tests the case where the payload length is less than the promised_stream_id
|
||||
// size, which should return an error.
|
||||
decoder.header.payload_length = 3;
|
||||
let result = decoder.decode_push_promise_payload(push_promise_payload);
|
||||
assert!(matches!(
|
||||
@@ -1785,7 +1811,8 @@ mod ut_frame_decoder {
|
||||
Err(H2Error::ConnectionError(ErrorCode::ProtocolError))
|
||||
));
|
||||
|
||||
// Test the case where the id is for an InitialWindowSize, but the value is too large
|
||||
// Test the case where the id is for an InitialWindowSize, but the value is too
|
||||
// large
|
||||
let result = get_setting(4, 2usize.pow(31) as u32);
|
||||
assert!(matches!(
|
||||
result,
|
||||
|
||||
+148
-76
@@ -14,8 +14,9 @@
|
||||
use crate::h2::frame::{FrameFlags, FrameType, Payload, Setting};
|
||||
use crate::h2::{Frame, HpackEncoder};
|
||||
|
||||
// TODO: Classify encoder errors per RFC specifications into categories like stream or connection errors.
|
||||
// Identify specific error types such as Frame_size_error/Protocol Error.
|
||||
// TODO: Classify encoder errors per RFC specifications into categories like
|
||||
// stream or connection errors. Identify specific error types such as
|
||||
// Frame_size_error/Protocol Error.
|
||||
#[derive(Debug)]
|
||||
pub enum FrameEncoderErr {
|
||||
EncodingData,
|
||||
@@ -28,17 +29,21 @@ pub enum FrameEncoderErr {
|
||||
enum FrameEncoderState {
|
||||
// The initial state for the frame encoder.
|
||||
Idle,
|
||||
// The initial state for encoding the HEADERS frame, including the frame header and the Field Block Fragment.
|
||||
// The initial state for encoding the HEADERS frame, including the frame header and the Field
|
||||
// Block Fragment.
|
||||
EncodingHeadersFrame,
|
||||
// The state for encoding the payload of the HEADERS frame, including the header block fragment.
|
||||
// The state for encoding the payload of the HEADERS frame, including the header block
|
||||
// fragment.
|
||||
EncodingHeadersPayload,
|
||||
// The state for encoding the padding octets for the HEADERS frame, if the PADDED flag is set.
|
||||
EncodingHeadersPadding,
|
||||
// The state for encoding CONTINUATION frames if the header block exceeds the max_frame_size.
|
||||
EncodingContinuationFrames,
|
||||
// The final state, indicating that the HEADERS frame and any necessary CONTINUATION frames have been fully encoded.
|
||||
// The final state, indicating that the HEADERS frame and any necessary CONTINUATION frames
|
||||
// have been fully encoded.
|
||||
HeadersComplete,
|
||||
// The initial state for encoding the DATA frame, including the frame header and the Pad Length field (if PADDED flag is set).
|
||||
// The initial state for encoding the DATA frame, including the frame header and the Pad
|
||||
// Length field (if PADDED flag is set).
|
||||
EncodingDataHeader,
|
||||
// The state for encoding the actual data payload of the DATA frame.
|
||||
EncodingDataPayload,
|
||||
@@ -72,8 +77,9 @@ enum FrameEncoderState {
|
||||
DataComplete,
|
||||
}
|
||||
|
||||
/// A structure for encoding frames into bytes, supporting the serialization of HTTP/2 Frames.
|
||||
/// It maintains the state of the current frame being encoded and also handles the fragmentation of frames.
|
||||
/// A structure for encoding frames into bytes, supporting the serialization of
|
||||
/// HTTP/2 Frames. It maintains the state of the current frame being encoded and
|
||||
/// also handles the fragmentation of frames.
|
||||
pub struct FrameEncoder {
|
||||
current_frame: Option<Frame>,
|
||||
max_frame_size: usize,
|
||||
@@ -90,7 +96,8 @@ pub struct FrameEncoder {
|
||||
}
|
||||
|
||||
impl FrameEncoder {
|
||||
/// Constructs a new `FrameEncoder` with specified maximum frame size and maximum header list size.
|
||||
/// Constructs a new `FrameEncoder` with specified maximum frame size and
|
||||
/// maximum header list size.
|
||||
pub fn new(max_frame_size: usize, max_header_list_size: usize) -> Self {
|
||||
FrameEncoder {
|
||||
current_frame: None,
|
||||
@@ -103,17 +110,20 @@ impl FrameEncoder {
|
||||
remaining_payload_bytes: 0,
|
||||
is_end_stream: false,
|
||||
is_end_headers: false,
|
||||
header_payload_buffer: vec![0; 16383], // Initialized to default max frame size.
|
||||
// Initialized to default max frame size.
|
||||
header_payload_buffer: vec![0; 16383],
|
||||
header_payload_index: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the current frame to be encoded by the `FrameEncoder`. The state of the encoder is updated based on the payload type of the frame.
|
||||
/// Sets the current frame to be encoded by the `FrameEncoder`. The state of
|
||||
/// the encoder is updated based on the payload type of the frame.
|
||||
pub fn set_frame(&mut self, frame: Frame) {
|
||||
self.is_end_stream = frame.flags().is_end_stream();
|
||||
self.is_end_headers = frame.flags().is_end_headers();
|
||||
self.current_frame = Some(frame);
|
||||
self.encoded_bytes = 0; // Reset the encoded bytes counter
|
||||
// Reset the encoded bytes counter
|
||||
self.encoded_bytes = 0;
|
||||
|
||||
// Set the initial state based on the frame payload type
|
||||
match &self.current_frame {
|
||||
@@ -121,7 +131,8 @@ impl FrameEncoder {
|
||||
Payload::Headers(headers) => {
|
||||
self.hpack_encoder.set_parts(headers.get_parts());
|
||||
self.header_payload_index = 0;
|
||||
// TODO: Handle potential scenario where HPACK encoding may not be able to complete output in one go.
|
||||
// TODO: Handle potential scenario where HPACK encoding may not be able to
|
||||
// complete output in one go.
|
||||
let payload_size = self.hpack_encoder.encode(&mut self.header_payload_buffer);
|
||||
self.remaining_header_payload = payload_size;
|
||||
self.state = FrameEncoderState::EncodingHeadersFrame;
|
||||
@@ -141,8 +152,10 @@ impl FrameEncoder {
|
||||
}
|
||||
}
|
||||
|
||||
/// Encodes the current frame into the given buffer. The state of the encoder determines which part of the frame is currently being encoded.
|
||||
/// This function returns the number of bytes written to the buffer or an error if the encoding process fails.
|
||||
/// Encodes the current frame into the given buffer. The state of the
|
||||
/// encoder determines which part of the frame is currently being encoded.
|
||||
/// This function returns the number of bytes written to the buffer or an
|
||||
/// error if the encoding process fails.
|
||||
pub fn encode(&mut self, buf: &mut [u8]) -> Result<usize, FrameEncoderErr> {
|
||||
let mut written_bytes = 0;
|
||||
|
||||
@@ -292,7 +305,8 @@ impl FrameEncoder {
|
||||
Ok(written_bytes)
|
||||
}
|
||||
|
||||
/// Updates the provided setting for the current frame if it is a `Settings` frame.
|
||||
/// Updates the provided setting for the current frame if it is a `Settings`
|
||||
/// frame.
|
||||
pub fn update_setting(&mut self, setting: Setting) {
|
||||
if let Some(frame) = &mut self.current_frame {
|
||||
if let Payload::Settings(settings) = frame.payload_mut() {
|
||||
@@ -315,7 +329,8 @@ impl FrameEncoder {
|
||||
fn encode_headers_frame(&mut self, buf: &mut [u8]) -> Result<usize, FrameEncoderErr> {
|
||||
if let Some(frame) = &self.current_frame {
|
||||
if let Payload::Headers(_) = frame.payload() {
|
||||
let frame_header_size = 9; // HTTP/2 frame header size is 9 bytes.
|
||||
// HTTP/2 frame header size is 9 bytes.
|
||||
let frame_header_size = 9;
|
||||
let remaining_header_bytes = if self.encoded_bytes >= frame_header_size {
|
||||
0
|
||||
} else {
|
||||
@@ -339,7 +354,8 @@ impl FrameEncoder {
|
||||
4 => {
|
||||
*item = frame.flags().bits();
|
||||
}
|
||||
// The last 4 bytes (6th to 9th) represent the stream identifier in the frame header.
|
||||
// The last 4 bytes (6th to 9th) represent the stream identifier in the
|
||||
// frame header.
|
||||
5..=8 => {
|
||||
let stream_id_byte_index = header_byte_index - 5;
|
||||
*item = (frame.stream_id() >> (24 - (8 * stream_id_byte_index))) as u8;
|
||||
@@ -437,10 +453,12 @@ impl FrameEncoder {
|
||||
let mut new_flags = FrameFlags::empty();
|
||||
if self.remaining_header_payload <= self.max_frame_size {
|
||||
if self.is_end_headers {
|
||||
new_flags.set_end_headers(true); // Sets the END_HEADERS flag on the last CONTINUATION frame.
|
||||
// Sets the END_HEADER flag on the last CONTINUATION frame.
|
||||
new_flags.set_end_headers(true);
|
||||
}
|
||||
if self.is_end_stream {
|
||||
new_flags.set_end_stream(true); // Sets the END_STREAM flag.
|
||||
// Sets the END_STREAM flag.
|
||||
new_flags.set_end_stream(true);
|
||||
}
|
||||
}
|
||||
buf[4] = new_flags.bits();
|
||||
@@ -482,7 +500,8 @@ impl FrameEncoder {
|
||||
fn encode_data_header(&mut self, buf: &mut [u8]) -> Result<usize, FrameEncoderErr> {
|
||||
if let Some(frame) = &self.current_frame {
|
||||
if let Payload::Data(data_frame) = frame.payload() {
|
||||
let frame_header_size = 9; // HTTP/2 frame header size is 9 bytes.
|
||||
// HTTP/2 frame header size is 9 bytes.
|
||||
let frame_header_size = 9;
|
||||
let remaining_header_bytes = if self.encoded_bytes >= frame_header_size {
|
||||
0
|
||||
} else {
|
||||
@@ -506,7 +525,8 @@ impl FrameEncoder {
|
||||
4 => {
|
||||
*item = frame.flags().bits();
|
||||
}
|
||||
// The last 4 bytes (6th to 9th) represent the stream identifier in the frame header.
|
||||
// The last 4 bytes (6th to 9th) represent the stream identifier in the
|
||||
// frame header.
|
||||
5..=8 => {
|
||||
let stream_id_byte_index = header_byte_index - 5;
|
||||
*item = (frame.stream_id() >> (24 - (8 * stream_id_byte_index))) as u8;
|
||||
@@ -534,7 +554,8 @@ impl FrameEncoder {
|
||||
fn encode_data_payload(&mut self, buf: &mut [u8]) -> Result<usize, FrameEncoderErr> {
|
||||
if let Some(frame) = self.current_frame.as_ref() {
|
||||
if let Payload::Data(data_frame) = frame.payload() {
|
||||
let frame_header_size = 9; // HTTP/2 frame header size is 9 bytes.
|
||||
// HTTP/2 frame header size is 9 bytes.
|
||||
let frame_header_size = 9;
|
||||
let encoded_payload_bytes = self.encoded_bytes - frame_header_size;
|
||||
let payload = data_frame.data();
|
||||
let bytes_to_write = self.encode_slice(buf, payload, encoded_payload_bytes);
|
||||
@@ -572,7 +593,8 @@ impl FrameEncoder {
|
||||
let bytes_to_write = remaining_padding_bytes.min(buf.len());
|
||||
|
||||
for item in buf.iter_mut().take(bytes_to_write) {
|
||||
*item = 0; // Padding bytes are filled with 0.
|
||||
// Padding bytes are filled with 0.
|
||||
*item = 0;
|
||||
}
|
||||
|
||||
self.encoded_bytes += bytes_to_write;
|
||||
@@ -689,7 +711,8 @@ impl FrameEncoder {
|
||||
fn encode_window_update_frame(&mut self, buf: &mut [u8]) -> Result<usize, FrameEncoderErr> {
|
||||
if let Some(frame) = &self.current_frame {
|
||||
if let Payload::WindowUpdate(_) = frame.payload() {
|
||||
let frame_header_size = 9; // HTTP/2 frame header size is 9 bytes.
|
||||
// HTTP/2 frame header size is 9 bytes.
|
||||
let frame_header_size = 9;
|
||||
let remaining_header_bytes = if self.encoded_bytes >= frame_header_size {
|
||||
0
|
||||
} else {
|
||||
@@ -699,12 +722,14 @@ impl FrameEncoder {
|
||||
for (buf_index, item) in buf.iter_mut().enumerate().take(bytes_to_write) {
|
||||
let header_byte_index = self.encoded_bytes + buf_index;
|
||||
match header_byte_index {
|
||||
// The first 3 bytes represent the payload length in the frame header. For WindowUpdate frame, this is always 4 bytes.
|
||||
// The first 3 bytes represent the payload length in the frame header. For
|
||||
// WindowUpdate frame, this is always 4 bytes.
|
||||
0..=1 => {
|
||||
*item = 0;
|
||||
}
|
||||
2 => {
|
||||
*item = 4; // Window Update frame payload size is always 4 bytes.
|
||||
// Window Update frame payload size is always 4 bytes.
|
||||
*item = 4;
|
||||
}
|
||||
// The 4th byte represents the frame type in the frame header.
|
||||
3 => {
|
||||
@@ -714,7 +739,8 @@ impl FrameEncoder {
|
||||
4 => {
|
||||
*item = frame.flags().bits();
|
||||
}
|
||||
// The last 4 bytes (6th to 9th) represent the stream identifier in the frame header.
|
||||
// The last 4 bytes (6th to 9th) represent the stream identifier in the
|
||||
// frame header.
|
||||
5..=8 => {
|
||||
let stream_id_byte_index = header_byte_index - 5;
|
||||
*item = (frame.stream_id() >> (24 - (8 * stream_id_byte_index))) as u8;
|
||||
@@ -727,7 +753,8 @@ impl FrameEncoder {
|
||||
self.encoded_bytes += bytes_to_write;
|
||||
if self.encoded_bytes == frame_header_size {
|
||||
self.state = FrameEncoderState::EncodingWindowUpdatePayload;
|
||||
self.encoded_bytes = 0; // Resets the encoded_bytes counter here.
|
||||
// Resets the encoded_bytes counter here.
|
||||
self.encoded_bytes = 0;
|
||||
}
|
||||
Ok(bytes_to_write)
|
||||
} else {
|
||||
@@ -794,10 +821,12 @@ impl FrameEncoder {
|
||||
4 => {
|
||||
buf[4] = frame.flags().bits();
|
||||
}
|
||||
// The last 4 bytes (6th to 9th) represent the stream identifier in the frame header.
|
||||
// For SETTINGS frames, this should always be 0.
|
||||
// The last 4 bytes (6th to 9th) represent the stream identifier in the
|
||||
// frame header. For SETTINGS frames, this should
|
||||
// always be 0.
|
||||
5..=8 => {
|
||||
buf[buf_index] = 0; // Stream ID should be 0 for SETTINGS frames.
|
||||
// Stream ID should be 0 for SETTINGS frames.
|
||||
buf[buf_index] = 0;
|
||||
}
|
||||
_ => {
|
||||
return Err(FrameEncoderErr::InternalError);
|
||||
@@ -871,7 +900,8 @@ impl FrameEncoder {
|
||||
fn encode_priority_frame(&mut self, buf: &mut [u8]) -> Result<usize, FrameEncoderErr> {
|
||||
if let Some(frame) = &self.current_frame {
|
||||
if let Payload::Priority(_) = frame.payload() {
|
||||
let frame_header_size = 9; // HTTP/2 frame header size is 9 bytes.
|
||||
// HTTP/2 frame header size is 9 bytes.
|
||||
let frame_header_size = 9;
|
||||
let remaining_header_bytes = if self.encoded_bytes >= frame_header_size {
|
||||
0
|
||||
} else {
|
||||
@@ -895,7 +925,8 @@ impl FrameEncoder {
|
||||
4 => {
|
||||
*item = frame.flags().bits();
|
||||
}
|
||||
// The last 4 bytes (6th to 9th) represent the stream identifier in the frame header.
|
||||
// The last 4 bytes (6th to 9th) represent the stream identifier in the
|
||||
// frame header.
|
||||
5..=8 => {
|
||||
let stream_id_byte_index = header_byte_index - 5;
|
||||
*item = (frame.stream_id() >> (24 - (8 * stream_id_byte_index))) as u8;
|
||||
@@ -921,7 +952,8 @@ impl FrameEncoder {
|
||||
fn encode_priority_payload(&mut self, buf: &mut [u8]) -> Result<usize, FrameEncoderErr> {
|
||||
if let Some(frame) = &self.current_frame {
|
||||
if let Payload::Priority(priority) = frame.payload() {
|
||||
let frame_header_size = 9; // HTTP/2 frame header size is 9 bytes.
|
||||
// HTTP/2 frame header size is 9 bytes.
|
||||
let frame_header_size = 9;
|
||||
let remaining_payload_bytes = 5 - (self.encoded_bytes - frame_header_size);
|
||||
let bytes_to_write = remaining_payload_bytes.min(buf.len());
|
||||
|
||||
@@ -1060,7 +1092,8 @@ impl FrameEncoder {
|
||||
match header_byte_index {
|
||||
// The first 3 bytes represent the payload length in the frame header.
|
||||
0..=2 => {
|
||||
let payload_len = 8; // PING payload is always 8 bytes.
|
||||
// PING payload is always 8 bytes.
|
||||
let payload_len = 8;
|
||||
buf[buf_index] = ((payload_len >> (16 - (8 * buf_index))) & 0xFF) as u8;
|
||||
}
|
||||
// The 4th byte represents the frame type in the frame header.
|
||||
@@ -1071,10 +1104,12 @@ impl FrameEncoder {
|
||||
4 => {
|
||||
buf[4] = frame.flags().bits();
|
||||
}
|
||||
// The last 4 bytes (6th to 9th) represent the stream identifier in the frame header.
|
||||
// For PING frames, this should always be 0.
|
||||
// The last 4 bytes (6th to 9th) represent the stream identifier in the
|
||||
// frame header. For PING frames, this should always
|
||||
// be 0.
|
||||
5..=8 => {
|
||||
buf[buf_index] = 0; // Stream ID should be 0 for PING frames.
|
||||
// Stream ID should be 0 for PING frames.
|
||||
buf[buf_index] = 0;
|
||||
}
|
||||
_ => {
|
||||
return Err(FrameEncoderErr::InternalError);
|
||||
@@ -1097,7 +1132,8 @@ impl FrameEncoder {
|
||||
fn encode_ping_payload(&mut self, buf: &mut [u8]) -> Result<usize, FrameEncoderErr> {
|
||||
if let Some(frame) = &self.current_frame {
|
||||
if let Payload::Ping(ping) = frame.payload() {
|
||||
let payload_size = 8usize; // PING payload is always 8 bytes.
|
||||
// PING payload is always 8 bytes.
|
||||
let payload_size = 8usize;
|
||||
let remaining_payload_bytes =
|
||||
payload_size.saturating_sub(self.encoded_bytes.saturating_sub(9usize));
|
||||
let bytes_to_write = remaining_payload_bytes.min(buf.len());
|
||||
@@ -1265,9 +1301,11 @@ mod ut_frame_encoder {
|
||||
let third_encoded = encoder.encode(&mut third_buf).unwrap();
|
||||
|
||||
assert_eq!(encoder.state, FrameEncoderState::DataComplete);
|
||||
assert_eq!(first_encoded, 9); // Updated expected value for first_encoded
|
||||
// Updated expected value for first_encoded
|
||||
assert_eq!(first_encoded, 9);
|
||||
assert_eq!(second_encoded, 30);
|
||||
assert_eq!(third_encoded, 6); // Updated expected value for third_encoded
|
||||
// Updated expected value for third_encoded
|
||||
assert_eq!(third_encoded, 6);
|
||||
|
||||
// Validate the encoded settings
|
||||
let mut expected_encoded_settings = vec![0u8; 60];
|
||||
@@ -1327,25 +1365,33 @@ mod ut_frame_encoder {
|
||||
|
||||
assert_eq!(first_buf[0], 0);
|
||||
assert_eq!(first_buf[1], 0);
|
||||
assert_eq!(first_buf[2], 8); // payload length
|
||||
// payload length
|
||||
assert_eq!(first_buf[2], 8);
|
||||
assert_eq!(first_buf[3], FrameType::Ping as u8);
|
||||
assert_eq!(first_buf[4], 0); // flags
|
||||
assert_eq!(first_buf[5], 0); // stream id
|
||||
assert_eq!(first_buf[6], 0); // stream id
|
||||
assert_eq!(first_buf[7], 0); // stream id
|
||||
assert_eq!(first_buf[8], 0); // stream id
|
||||
// flags
|
||||
assert_eq!(first_buf[4], 0);
|
||||
// stream id
|
||||
assert_eq!(first_buf[5], 0);
|
||||
// stream id
|
||||
assert_eq!(first_buf[6], 0);
|
||||
// stream id
|
||||
assert_eq!(first_buf[7], 0);
|
||||
// stream id
|
||||
assert_eq!(first_buf[8], 0);
|
||||
|
||||
for i in 0..8 {
|
||||
assert_eq!(second_buf[i], ping_payload[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/// UT test case for FrameEncoder encoding a sequence of frames: Headers, Data, Headers.
|
||||
/// UT test case for FrameEncoder encoding a sequence of frames: Headers,
|
||||
/// Data, Headers.
|
||||
///
|
||||
/// # Brief
|
||||
/// 1. Creates a FrameEncoder.
|
||||
/// 2. Creates multiple frames including Headers and Data frames.
|
||||
/// 3. Sets each frame for the encoder and encodes them using buffer segments.
|
||||
/// 3. Sets each frame for the encoder and encodes them using buffer
|
||||
/// segments.
|
||||
/// 4. Checks whether the encoding results are correct.
|
||||
#[test]
|
||||
fn ut_continue_frame_encoding() {
|
||||
@@ -1418,7 +1464,8 @@ mod ut_frame_encoder {
|
||||
|
||||
let frame_flags = FrameFlags::empty();
|
||||
let frame = Frame::new(
|
||||
1, // Stream ID can be non-zero for RST_STREAM frames
|
||||
// Stream ID can be non-zero for RST_STREAM frames
|
||||
1,
|
||||
frame_flags,
|
||||
rst_stream_payload,
|
||||
);
|
||||
@@ -1428,12 +1475,16 @@ mod ut_frame_encoder {
|
||||
|
||||
let mut buf = vec![0; 50];
|
||||
let first_encoded = frame_encoder.encode(&mut buf).unwrap();
|
||||
assert_eq!(first_encoded, 9 + 4); // 9 bytes for header, 4 bytes for payload
|
||||
// 9 bytes for header, 4 bytes for payload
|
||||
assert_eq!(first_encoded, 9 + 4);
|
||||
assert_eq!(buf[0], 0);
|
||||
assert_eq!(buf[2], 4); // payload length should be 4 for RST_STREAM frames
|
||||
// payload length should be 4 for RST_STREAM frames
|
||||
assert_eq!(buf[2], 4);
|
||||
assert_eq!(buf[3], FrameType::RstStream as u8);
|
||||
assert_eq!(buf[4], 0); // frame flags should be 0 for RST_STREAM frames
|
||||
assert_eq!(buf[8], 1); // stream ID should be 1 for this test case
|
||||
// frame flags should be 0 for RST_STREAM frames
|
||||
assert_eq!(buf[4], 0);
|
||||
// stream ID should be 1 for this test case
|
||||
assert_eq!(buf[8], 1);
|
||||
|
||||
// Check if encoded error code is correct
|
||||
assert_eq!(&buf[9..13], &error_code.to_be_bytes());
|
||||
@@ -1458,7 +1509,8 @@ mod ut_frame_encoder {
|
||||
|
||||
let frame_flags = FrameFlags::empty();
|
||||
let frame = Frame::new(
|
||||
0, // Stream ID can be zero for WINDOW_UPDATE frames.
|
||||
// Stream ID can be zero for WINDOW_UPDATE frames.
|
||||
0,
|
||||
frame_flags,
|
||||
window_update_payload,
|
||||
);
|
||||
@@ -1468,12 +1520,16 @@ mod ut_frame_encoder {
|
||||
|
||||
let mut buf = vec![0; 50];
|
||||
let first_encoded = frame_encoder.encode(&mut buf).unwrap();
|
||||
assert_eq!(first_encoded, 9 + 4); // 9 bytes for header, 4 bytes for payload.
|
||||
// 9 bytes for header, 4 bytes for payload.
|
||||
assert_eq!(first_encoded, 9 + 4);
|
||||
assert_eq!(buf[0], 0);
|
||||
assert_eq!(buf[2], 4); // Payload length should be 4 for WINDOW_UPDATE frames.
|
||||
// Payload length should be 4 for WINDOW_UPDATE frames.
|
||||
assert_eq!(buf[2], 4);
|
||||
assert_eq!(buf[3], FrameType::WindowUpdate as u8);
|
||||
assert_eq!(buf[4], 0); // Frame flags should be 0 for WINDOW_UPDATE frames.
|
||||
assert_eq!(buf[8], 0); // Stream ID should be 0 for this test case.
|
||||
// Frame flags should be 0 for WINDOW_UPDATE frames.
|
||||
assert_eq!(buf[4], 0);
|
||||
// Stream ID should be 0 for this test case.
|
||||
assert_eq!(buf[8], 0);
|
||||
|
||||
// Checks if encoded window size increment is correct.
|
||||
assert_eq!(&buf[9..13], &window_size_increment.to_be_bytes());
|
||||
@@ -1492,7 +1548,8 @@ mod ut_frame_encoder {
|
||||
#[test]
|
||||
fn ut_priority_frame_encoding() {
|
||||
let mut encoder = FrameEncoder::new(4096, 4096);
|
||||
let stream_dependency = 0x7FFFFFFF; // Maximum value for a 31-bit integer
|
||||
// Maximum value for a 31-bit integer
|
||||
let stream_dependency = 0x7FFFFFFF;
|
||||
let priority_payload = Priority::new(true, stream_dependency, 15);
|
||||
|
||||
let priority_frame =
|
||||
@@ -1505,20 +1562,33 @@ mod ut_frame_encoder {
|
||||
let encoded = encoder.encode(&mut buf).unwrap();
|
||||
|
||||
assert_eq!(encoded, 14);
|
||||
assert_eq!(buf[0], 0); // Payload length (most significant byte)
|
||||
assert_eq!(buf[1], 0); // Payload length (middle byte)
|
||||
assert_eq!(buf[2], 5); // Payload length (least significant byte)
|
||||
// Payload length (most significant byte)
|
||||
assert_eq!(buf[0], 0);
|
||||
// Payload length (middle byte)
|
||||
assert_eq!(buf[1], 0);
|
||||
// Payload length (least significant byte)
|
||||
assert_eq!(buf[2], 5);
|
||||
// Frame flags
|
||||
assert_eq!(buf[3], FrameType::Priority as u8);
|
||||
assert_eq!(buf[4], 0); // Frame flags
|
||||
assert_eq!(buf[5], 0); // Stream ID (most significant byte)
|
||||
assert_eq!(buf[6], 0); // Stream ID (middle bytes)
|
||||
assert_eq!(buf[7], 0); // Stream ID (middle bytes)
|
||||
assert_eq!(buf[8], 131); // Stream ID (least significant byte)
|
||||
assert_eq!(buf[9], (0x80 | ((stream_dependency >> 24) & 0x7F)) as u8); // Exclusive flag and most significant byte of stream dependency
|
||||
assert_eq!(buf[10], ((stream_dependency >> 16) & 0xFF) as u8); // Stream dependency (middle bytes)
|
||||
assert_eq!(buf[11], ((stream_dependency >> 8) & 0xFF) as u8); // Stream dependency (middle bytes)
|
||||
assert_eq!(buf[12], (stream_dependency & 0xFF) as u8); // Stream dependency (least significant byte)
|
||||
assert_eq!(buf[13], 15); // Weight
|
||||
assert_eq!(buf[4], 0);
|
||||
// Stream ID (most significant byte)
|
||||
assert_eq!(buf[5], 0);
|
||||
// Stream ID (middle bytes)
|
||||
assert_eq!(buf[6], 0);
|
||||
// Stream ID (middle bytes)
|
||||
assert_eq!(buf[7], 0);
|
||||
// Stream ID (least significant byte)
|
||||
assert_eq!(buf[8], 131);
|
||||
// Exclusive flag and most significant byte of stream dependency
|
||||
assert_eq!(buf[9], (0x80 | ((stream_dependency >> 24) & 0x7F)) as u8);
|
||||
// Stream dependency (middle bytes)
|
||||
assert_eq!(buf[10], ((stream_dependency >> 16) & 0xFF) as u8);
|
||||
// Stream dependency (middle bytes)
|
||||
assert_eq!(buf[11], ((stream_dependency >> 8) & 0xFF) as u8);
|
||||
// Stream dependency (least significant byte)
|
||||
assert_eq!(buf[12], (stream_dependency & 0xFF) as u8);
|
||||
// Weight
|
||||
assert_eq!(buf[13], 15);
|
||||
}
|
||||
|
||||
/// UT test cases for `FrameEncoder` encoding `GOAWAY` frame.
|
||||
@@ -1564,9 +1634,11 @@ mod ut_frame_encoder {
|
||||
|
||||
expected_encoded_goaway[8..13].copy_from_slice(&debug_data[..]);
|
||||
|
||||
assert_eq!(first_buf[0..3], [0u8, 0, 13]); // payload length should be 13 bytes
|
||||
// payload length should be 13 bytes
|
||||
assert_eq!(first_buf[0..3], [0u8, 0, 13]);
|
||||
assert_eq!(first_buf[3], FrameType::Goaway as u8);
|
||||
assert_eq!(first_buf[4], 0); // flags
|
||||
// flags
|
||||
assert_eq!(first_buf[4], 0);
|
||||
|
||||
// Validate the encoded Last-Stream-ID, Error Code, and debug data
|
||||
assert_eq!(second_buf[..], expected_encoded_goaway[..]);
|
||||
|
||||
@@ -24,9 +24,10 @@
|
||||
//! streams or the entire connection and have no defined semantics in the other
|
||||
//! context.
|
||||
|
||||
use crate::error::{ErrorKind, HttpError};
|
||||
use std::convert::{Infallible, TryFrom};
|
||||
|
||||
use crate::error::{ErrorKind, HttpError};
|
||||
|
||||
/// The http2 error handle implementation
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub enum H2Error {
|
||||
@@ -132,14 +133,17 @@ impl TryFrom<u32> for ErrorCode {
|
||||
|
||||
#[cfg(test)]
|
||||
mod ut_h2_error {
|
||||
use super::*;
|
||||
use std::convert::TryInto;
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Unit test cases for `ErrorCode::try_from`.
|
||||
///
|
||||
/// # Brief
|
||||
/// 1. Iterates over a range of valid u32 values that represent `ErrorCode`s.
|
||||
/// 2. Attempts to convert each u32 value into an `ErrorCode` using `try_into`.
|
||||
/// 1. Iterates over a range of valid u32 values that represent
|
||||
/// `ErrorCode`s.
|
||||
/// 2. Attempts to convert each u32 value into an `ErrorCode` using
|
||||
/// `try_into`.
|
||||
/// 3. Checks that the conversion is successful for each valid `ErrorCode`.
|
||||
/// 4. Also attempts to convert an invalid u32 value into an `ErrorCode`.
|
||||
/// 5. Checks that the conversion fails for the invalid value.
|
||||
|
||||
+62
-36
@@ -11,13 +11,15 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use crate::error::HttpError;
|
||||
use crate::h2::{ErrorCode, H2Error, Parts, PseudoHeaders};
|
||||
use crate::headers;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
/// Mask for the END_STREAM flag.
|
||||
/// When set, indicates that the sender will not send further frames for this stream.
|
||||
/// When set, indicates that the sender will not send further frames for this
|
||||
/// stream.
|
||||
pub(crate) const END_STREAM_MASK: u8 = 0x01;
|
||||
|
||||
/// Mask for the RST_STREAM flag.
|
||||
@@ -25,7 +27,8 @@ pub(crate) const END_STREAM_MASK: u8 = 0x01;
|
||||
pub(crate) const RST_STREAM_MASK: u8 = 0x03;
|
||||
|
||||
/// Mask for the END_HEADERS flag.
|
||||
/// When set, indicates that this frame contains an entire header block and not a fragment.
|
||||
/// When set, indicates that this frame contains an entire header block and not
|
||||
/// a fragment.
|
||||
pub(crate) const END_HEADERS_MASK: u8 = 0x04;
|
||||
|
||||
/// Mask for the PADDED flag.
|
||||
@@ -33,15 +36,17 @@ pub(crate) const END_HEADERS_MASK: u8 = 0x04;
|
||||
pub(crate) const PADDED_MASK: u8 = 0x08;
|
||||
|
||||
/// Mask for the HEADERS_PRIORITY flag.
|
||||
/// When set, indicates that the headers frame also contains the priority information.
|
||||
/// When set, indicates that the headers frame also contains the priority
|
||||
/// information.
|
||||
pub(crate) const HEADERS_PRIORITY_MASK: u8 = 0x20;
|
||||
|
||||
/// Mask for the ACK flag
|
||||
pub(crate) const ACK_MASK: u8 = 0x1;
|
||||
|
||||
/// HTTP/2 frame structure, including the stream ID, flags, and payload information.
|
||||
/// The frame type information is represented by the `Payload` type.
|
||||
/// This structure represents the fundamental unit of communication in HTTP/2.
|
||||
/// HTTP/2 frame structure, including the stream ID, flags, and payload
|
||||
/// information. The frame type information is represented by the `Payload`
|
||||
/// type. This structure represents the fundamental unit of communication in
|
||||
/// HTTP/2.
|
||||
#[derive(Clone)]
|
||||
pub struct Frame {
|
||||
id: StreamId,
|
||||
@@ -90,8 +95,9 @@ pub enum Payload {
|
||||
PushPromise(PushPromise),
|
||||
}
|
||||
|
||||
/// Enum representing the different settings that can be included in a SETTINGS frame.
|
||||
/// Each setting has a different role in the HTTP/2 communication process.
|
||||
/// Enum representing the different settings that can be included in a SETTINGS
|
||||
/// frame. Each setting has a different role in the HTTP/2 communication
|
||||
/// process.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub enum Setting {
|
||||
/// SETTINGS_HEADER_TABLE_SIZE
|
||||
@@ -137,14 +143,16 @@ pub struct Priority {
|
||||
}
|
||||
|
||||
/// The RST_STREAM frame allows for immediate termination of a stream.
|
||||
/// RST_STREAM is sent to request cancellation of a stream or to indicate an error situation.
|
||||
/// RST_STREAM is sent to request cancellation of a stream or to indicate an
|
||||
/// error situation.
|
||||
#[derive(Clone)]
|
||||
pub struct RstStream {
|
||||
error_code: u32,
|
||||
}
|
||||
|
||||
/// Represents the PING frame payload.
|
||||
/// The PING frame is a mechanism for measuring a minimal round-trip time from the sender.
|
||||
/// The PING frame is a mechanism for measuring a minimal round-trip time from
|
||||
/// the sender.
|
||||
#[derive(Clone)]
|
||||
pub struct Ping {
|
||||
/// The opaque data of PING
|
||||
@@ -152,14 +160,16 @@ pub struct Ping {
|
||||
}
|
||||
|
||||
/// Represents the SETTINGS frame payload.
|
||||
/// The SETTINGS frame conveys configuration parameters that affect how endpoints communicate.
|
||||
/// The SETTINGS frame conveys configuration parameters that affect how
|
||||
/// endpoints communicate.
|
||||
#[derive(Clone)]
|
||||
pub struct Settings {
|
||||
settings: Vec<Setting>,
|
||||
}
|
||||
|
||||
/// Represents the GOAWAY frame payload.
|
||||
/// The GOAWAY frame is used to initiate shutdown of a connection or to signal serious error conditions.
|
||||
/// The GOAWAY frame is used to initiate shutdown of a connection or to signal
|
||||
/// serious error conditions.
|
||||
#[derive(Clone)]
|
||||
pub struct Goaway {
|
||||
error_code: u32,
|
||||
@@ -175,7 +185,8 @@ pub struct WindowUpdate {
|
||||
}
|
||||
|
||||
/// Represents the PUSH_PROMISE frame payload.
|
||||
/// The PUSH_PROMISE frame is used to notify the peer endpoint in advance of streams the sender intends to initiate.
|
||||
/// The PUSH_PROMISE frame is used to notify the peer endpoint in advance of
|
||||
/// streams the sender intends to initiate.
|
||||
#[derive(Clone)]
|
||||
pub struct PushPromise {
|
||||
promised_stream_id: u32,
|
||||
@@ -193,7 +204,8 @@ impl Frame {
|
||||
self.id
|
||||
}
|
||||
|
||||
/// Constructs a new `Frame` with the given `StreamId`, `FrameFlags`, `Payload`.
|
||||
/// Constructs a new `Frame` with the given `StreamId`, `FrameFlags`,
|
||||
/// `Payload`.
|
||||
pub fn new(id: StreamId, flags: FrameFlags, payload: Payload) -> Self {
|
||||
Frame { id, flags, payload }
|
||||
}
|
||||
@@ -280,8 +292,8 @@ impl FrameFlags {
|
||||
}
|
||||
|
||||
impl Payload {
|
||||
/// Returns a reference to the Headers if the Payload is of the Headers variant.
|
||||
/// If the Payload is not of the Headers variant, returns None.
|
||||
/// Returns a reference to the Headers if the Payload is of the Headers
|
||||
/// variant. If the Payload is not of the Headers variant, returns None.
|
||||
pub(crate) fn as_headers(&self) -> Option<&Headers> {
|
||||
if let Payload::Headers(headers) = self {
|
||||
Some(headers)
|
||||
@@ -290,8 +302,9 @@ impl Payload {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the type of the frame (`FrameType`) that this payload would be associated with.
|
||||
/// The returned `FrameType` is determined based on the variant of the Payload.
|
||||
/// Returns the type of the frame (`FrameType`) that this payload would be
|
||||
/// associated with. The returned `FrameType` is determined based on the
|
||||
/// variant of the Payload.
|
||||
pub fn frame_type(&self) -> FrameType {
|
||||
match self {
|
||||
Payload::Headers(_) => FrameType::Headers,
|
||||
@@ -364,7 +377,8 @@ impl Settings {
|
||||
/// Returns the total length of the settings when encoded.
|
||||
pub fn encoded_len(&self) -> usize {
|
||||
let settings_count = self.settings.len();
|
||||
let setting_size = 6; // Each setting has a 2-byte ID and a 4-byte value
|
||||
// Each setting has a 2-byte ID and a 4-byte value
|
||||
let setting_size = 6;
|
||||
settings_count * setting_size
|
||||
}
|
||||
}
|
||||
@@ -421,10 +435,10 @@ impl SettingsBuilder {
|
||||
/// use ylong_http::h2::SettingsBuilder;
|
||||
///
|
||||
/// let settings = SettingsBuilder::new()
|
||||
/// .enable_push(true)
|
||||
/// .header_table_size(4096)
|
||||
/// .max_frame_size(2 << 13)
|
||||
/// .build();
|
||||
/// .enable_push(true)
|
||||
/// .header_table_size(4096)
|
||||
/// .max_frame_size(2 << 13)
|
||||
/// .build();
|
||||
/// ```
|
||||
pub fn build(self) -> Settings {
|
||||
Settings::new(self.settings)
|
||||
@@ -438,7 +452,8 @@ impl Default for SettingsBuilder {
|
||||
}
|
||||
|
||||
impl Goaway {
|
||||
/// Creates a new Goaway instance with the provided error code, last stream ID, and debug data.
|
||||
/// Creates a new Goaway instance with the provided error code, last stream
|
||||
/// ID, and debug data.
|
||||
pub fn new(error_code: u32, last_stream_id: StreamId, debug_data: Vec<u8>) -> Self {
|
||||
Goaway {
|
||||
error_code,
|
||||
@@ -464,12 +479,14 @@ impl Goaway {
|
||||
|
||||
/// Returns the total length of the Goaway frame when encoded.
|
||||
pub fn encoded_len(&self) -> usize {
|
||||
8 + self.debug_data.len() // 4-byte Last-Stream-ID + 4-byte Error Code + Debug Data length
|
||||
8 + self.debug_data.len() // 4-byte Last-Stream-ID + 4-byte Error Code +
|
||||
// Debug Data length
|
||||
}
|
||||
}
|
||||
|
||||
impl WindowUpdate {
|
||||
/// Creates a new WindowUpdate instance with the provided window size increment.
|
||||
/// Creates a new WindowUpdate instance with the provided window size
|
||||
/// increment.
|
||||
pub fn new(window_size_increment: u32) -> Self {
|
||||
WindowUpdate {
|
||||
window_size_increment,
|
||||
@@ -488,7 +505,8 @@ impl WindowUpdate {
|
||||
}
|
||||
|
||||
impl Priority {
|
||||
/// Creates a new Priority instance with the provided exclusive flag, stream dependency, and weight.
|
||||
/// Creates a new Priority instance with the provided exclusive flag, stream
|
||||
/// dependency, and weight.
|
||||
pub fn new(exclusive: bool, stream_dependency: u32, weight: u8) -> Self {
|
||||
Priority {
|
||||
exclusive,
|
||||
@@ -566,8 +584,10 @@ mod ut_frame {
|
||||
/// 1. Creates a `SettingsBuilder`.
|
||||
/// 2. Sets various setting parameters using builder methods.
|
||||
/// 3. Builds a `Settings` object.
|
||||
/// 4. Gets a reference to the underlying `Vec<Setting>` from the `Settings` object.
|
||||
/// 5. Iterates over each setting in the `Vec<Setting>` and checks whether it matches the expected value.
|
||||
/// 4. Gets a reference to the underlying `Vec<Setting>` from the `Settings`
|
||||
/// object.
|
||||
/// 5. Iterates over each setting in the `Vec<Setting>` and checks whether
|
||||
/// it matches the expected value.
|
||||
#[test]
|
||||
fn ut_settings_builder() {
|
||||
let settings = SettingsBuilder::new()
|
||||
@@ -578,11 +598,16 @@ mod ut_frame {
|
||||
.build();
|
||||
|
||||
let mut setting_iter = settings.get_settings().iter();
|
||||
assert_eq!(setting_iter.next(), Some(&Setting::HeaderTableSize(4096))); // Check that the first setting is as expected
|
||||
assert_eq!(setting_iter.next(), Some(&Setting::EnablePush(true))); // Check that the second setting is as expected
|
||||
assert_eq!(setting_iter.next(), Some(&Setting::MaxFrameSize(16384))); // Check that the third setting is as expected
|
||||
assert_eq!(setting_iter.next(), Some(&Setting::MaxHeaderListSize(8192))); // Check that the fourth setting is as expected
|
||||
assert_eq!(setting_iter.next(), None); // Check that there are no more settings
|
||||
// Check that the first setting is as expected
|
||||
assert_eq!(setting_iter.next(), Some(&Setting::HeaderTableSize(4096)));
|
||||
// Check that the second setting is as expected
|
||||
assert_eq!(setting_iter.next(), Some(&Setting::EnablePush(true)));
|
||||
// Check that the third setting is as expected
|
||||
assert_eq!(setting_iter.next(), Some(&Setting::MaxFrameSize(16384)));
|
||||
// Check that the fourth setting is as expected
|
||||
assert_eq!(setting_iter.next(), Some(&Setting::MaxHeaderListSize(8192)));
|
||||
// Check that there are no more settings
|
||||
assert_eq!(setting_iter.next(), None);
|
||||
}
|
||||
|
||||
/// UT test cases for `Ping`.
|
||||
@@ -600,7 +625,8 @@ mod ut_frame {
|
||||
/// UT test cases for `Setting`.
|
||||
///
|
||||
/// # Brief
|
||||
/// 1. Creates a `Setting` instance for each possible variant with a specific value.
|
||||
/// 1. Creates a `Setting` instance for each possible variant with a
|
||||
/// specific value.
|
||||
/// 2. Checks if the identifier of the `Setting` instance is correct.
|
||||
#[test]
|
||||
fn ut_setting() {
|
||||
|
||||
@@ -11,13 +11,14 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use core::mem::take;
|
||||
|
||||
use crate::h2::error::ErrorCode;
|
||||
use crate::h2::hpack::representation::{
|
||||
Name, ReprDecStateHolder, ReprDecodeState, ReprDecoder, Representation,
|
||||
};
|
||||
use crate::h2::hpack::table::{DynamicTable, Header, TableSearcher};
|
||||
use crate::h2::{H2Error, Parts};
|
||||
use core::mem::take;
|
||||
|
||||
// A structure used to store header lines and octets lengths of header lines.
|
||||
struct HeaderLines {
|
||||
@@ -49,7 +50,8 @@ impl HpackDecoder {
|
||||
}
|
||||
}
|
||||
|
||||
/// Users can call `decode` multiple times to decode the byte stream in segments.
|
||||
/// Users can call `decode` multiple times to decode the byte stream in
|
||||
/// segments.
|
||||
pub(crate) fn decode(&mut self, buf: &[u8]) -> Result<(), H2Error> {
|
||||
// Initialize ReprDecoder.
|
||||
let mut decoder = ReprDecoder::new(buf);
|
||||
@@ -85,7 +87,8 @@ impl HpackDecoder {
|
||||
}
|
||||
}
|
||||
|
||||
/// `Updater` is used to update `DynamicTable` `PseudoHeaders` and `HttpHeaderMap`.
|
||||
/// `Updater` is used to update `DynamicTable` `PseudoHeaders` and
|
||||
/// `HttpHeaderMap`.
|
||||
struct Updater<'a> {
|
||||
header_list_size: usize,
|
||||
table: &'a mut DynamicTable,
|
||||
|
||||
@@ -18,13 +18,14 @@
|
||||
//!
|
||||
//! # Introduction
|
||||
//! Integers are used to represent name indexes, header field indexes, or
|
||||
//! string lengths. An integer representation can start anywhere within an octet.
|
||||
//! To allow for optimized processing, an integer representation always finishes
|
||||
//! at the end of an octet.
|
||||
//! string lengths. An integer representation can start anywhere within an
|
||||
//! octet. To allow for optimized processing, an integer representation always
|
||||
//! finishes at the end of an octet.
|
||||
|
||||
use core::cmp::Ordering;
|
||||
|
||||
use crate::h2::error::ErrorCode;
|
||||
use crate::h2::H2Error;
|
||||
use core::cmp::Ordering;
|
||||
|
||||
/// `IntegerDecoder` implementation according to `Pseudocode to decode an
|
||||
/// integer I` in `RFC7541 section-5.1`.
|
||||
@@ -157,7 +158,8 @@ mod ut_integer {
|
||||
///
|
||||
/// # Brief
|
||||
/// 1. Creates an `IntegerDecoder`.
|
||||
/// 2. Calls `IntegerDecoder::first_byte()` an `IntegerDecoder::next_byte()`,
|
||||
/// 2. Calls `IntegerDecoder::first_byte()` an
|
||||
/// `IntegerDecoder::next_byte()`,
|
||||
/// passing in the specified parameters.
|
||||
/// 3. Checks if the test results are correct.
|
||||
#[test]
|
||||
@@ -205,7 +207,8 @@ mod ut_integer {
|
||||
///
|
||||
/// # Brief
|
||||
/// 1. Creates an `IntegerEncoder`.
|
||||
/// 2. Calls `IntegerEncoder::first_byte()` and `IntegerEncoder::next_byte()`,
|
||||
/// 2. Calls `IntegerEncoder::first_byte()` and
|
||||
/// `IntegerEncoder::next_byte()`,
|
||||
/// passing in the specified parameters.
|
||||
/// 3. Checks if the test results are correct.
|
||||
#[test]
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
//! # Introduction
|
||||
//! In [HTTP/1.1], header fields are not compressed. As web pages have grown
|
||||
//! to require dozens to hundreds of requests, the redundant header fields in
|
||||
//! these requests unnecessarily consume bandwidth, measurably increasing latency.
|
||||
//! these requests unnecessarily consume bandwidth, measurably increasing
|
||||
//! latency.
|
||||
//!
|
||||
//! [SPDY] initially addressed this redundancy by compressing header fields
|
||||
//! using the [DEFLATE] format, which proved very effective at efficiently
|
||||
@@ -41,7 +42,6 @@
|
||||
//! [SPDY]: https://datatracker.ietf.org/doc/html/draft-mbelshe-httpbis-spdy-00
|
||||
//! [DEFLATE]: https://www.rfc-editor.org/rfc/rfc1951.html
|
||||
//! [CRIME (Compression Ratio Info-leak Made Easy)]: https://en.wikipedia.org/w/index.php?title=CRIME&oldid=660948120
|
||||
//!
|
||||
|
||||
mod decoder;
|
||||
mod encoder;
|
||||
|
||||
@@ -11,12 +11,13 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use core::cmp::Ordering;
|
||||
|
||||
use crate::h2::error::ErrorCode;
|
||||
use crate::h2::hpack::integer::IntegerDecoder;
|
||||
use crate::h2::hpack::representation::{Name, PrefixBit, Representation};
|
||||
use crate::h2::H2Error;
|
||||
use crate::huffman::HuffmanDecoder;
|
||||
use core::cmp::Ordering;
|
||||
|
||||
/// Decoder implementation for decoding representation. Every time users call
|
||||
/// `decode`, the `ReprDecoder` will try to decode a `Repr`. If `buf` has been
|
||||
|
||||
@@ -11,13 +11,14 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use core::cmp::Ordering;
|
||||
use std::collections::hash_map::IntoIter;
|
||||
|
||||
use crate::h2::hpack::integer::IntegerEncoder;
|
||||
use crate::h2::hpack::representation::PrefixIndexMask;
|
||||
use crate::h2::hpack::table::{DynamicTable, Header, TableIndex, TableSearcher};
|
||||
use crate::h2::{Parts, PseudoHeaders};
|
||||
use crate::headers::HeadersIntoIter;
|
||||
use core::cmp::Ordering;
|
||||
use std::collections::hash_map::IntoIter;
|
||||
|
||||
/// Encoder implementation for decoding representation. The encode interface
|
||||
/// supports segmented writing.
|
||||
@@ -115,7 +116,8 @@ impl<'a> ReprEncoder<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// `ReprEncStateHolder` is used to save the intermediate results of the `ReprEncoder`.
|
||||
/// `ReprEncStateHolder` is used to save the intermediate results of the
|
||||
/// `ReprEncoder`.
|
||||
pub(crate) struct ReprEncStateHolder {
|
||||
iter: Option<PartsIter>,
|
||||
state: Option<ReprEncodeState>,
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
//! [HPACK]: https://httpwg.org/specs/rfc7541.html
|
||||
//!
|
||||
//! # Description from RFC7541
|
||||
//! An encoded header field can be represented either as an index or as a literal.
|
||||
//! An encoded header field can be represented either as an index or as a
|
||||
//! literal.
|
||||
//!
|
||||
//! An [indexed representation] defines a header field as a reference to an
|
||||
//! entry in either the static table or the dynamic table.
|
||||
@@ -67,7 +68,8 @@ pub(crate) use decoder::{ReprDecStateHolder, ReprDecodeState, ReprDecoder};
|
||||
pub(crate) use encoder::{ReprEncStateHolder, ReprEncodeState, ReprEncoder};
|
||||
|
||||
/// Definition and [binary format] of each of the different
|
||||
/// [header field representations] and the [dynamic table size update] instruction.
|
||||
/// [header field representations] and the [dynamic table size update]
|
||||
/// instruction.
|
||||
///
|
||||
/// [binary format]: https://www.rfc-editor.org/rfc/rfc7541.html#section-6
|
||||
/// [header field representations]: https://www.rfc-editor.org/rfc/rfc7541.html#section-3.2
|
||||
@@ -93,9 +95,9 @@ pub(crate) enum Representation {
|
||||
/// ```
|
||||
Indexed { index: usize },
|
||||
|
||||
/// A [literal header field with incremental indexing representation] results
|
||||
/// in appending a header field to the decoded header list and inserting it
|
||||
/// as a new entry into the dynamic table.
|
||||
/// A [literal header field with incremental indexing representation]
|
||||
/// results in appending a header field to the decoded header list and
|
||||
/// inserting it as a new entry into the dynamic table.
|
||||
///
|
||||
/// A literal header field with incremental indexing representation starts
|
||||
/// with the '01' 2-bit pattern.
|
||||
@@ -170,10 +172,10 @@ pub(crate) enum Representation {
|
||||
/// ```
|
||||
LiteralWithoutIndexing { name: Name, value: Vec<u8> },
|
||||
|
||||
/// A [literal header field never-indexed representation] results in appending
|
||||
/// a header field to the decoded header list without altering the dynamic
|
||||
/// table. Intermediaries **MUST** use the same representation for encoding
|
||||
/// this header field.
|
||||
/// A [literal header field never-indexed representation] results in
|
||||
/// appending a header field to the decoded header list without altering
|
||||
/// the dynamic table. Intermediaries **MUST** use the same
|
||||
/// representation for encoding this header field.
|
||||
///
|
||||
/// A literal header field never-indexed representation starts with the
|
||||
/// '0001' 4-bit pattern.
|
||||
@@ -320,7 +322,8 @@ impl PrefixIndexMask {
|
||||
pub(crate) const LITERAL_WITHOUT_INDEXING: Self = Self(0x0f);
|
||||
}
|
||||
|
||||
/// Name of `Representation`. It can be represented as string literals or an index.
|
||||
/// Name of `Representation`. It can be represented as string literals or an
|
||||
/// index.
|
||||
pub(crate) enum Name {
|
||||
Index(usize),
|
||||
Literal(Vec<u8>),
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
use std::collections::VecDeque;
|
||||
use std::ops::Add;
|
||||
|
||||
/// `TableSearcher` is used to find specified content in static and dynamic tables.
|
||||
/// `TableSearcher` is used to find specified content in static and dynamic
|
||||
/// tables.
|
||||
pub(crate) struct TableSearcher<'a> {
|
||||
dynamic: &'a DynamicTable,
|
||||
}
|
||||
@@ -68,9 +69,10 @@ pub(crate) enum TableIndex {
|
||||
/// [HPACK]: https://httpwg.org/specs/rfc7541.html
|
||||
///
|
||||
/// # Introduction
|
||||
/// The dynamic table consists of a list of header fields maintained in first-in,
|
||||
/// first-out order. The first and newest entry in a dynamic table is at the
|
||||
/// lowest index, and the oldest entry of a dynamic table is at the highest index.
|
||||
/// The dynamic table consists of a list of header fields maintained in
|
||||
/// first-in, first-out order. The first and newest entry in a dynamic table is
|
||||
/// at the lowest index, and the oldest entry of a dynamic table is at the
|
||||
/// highest index.
|
||||
///
|
||||
/// The dynamic table is initially empty. Entries are added as each header block
|
||||
/// is decompressed.
|
||||
@@ -409,11 +411,16 @@ pub(crate) enum Header {
|
||||
impl Header {
|
||||
pub(crate) fn len(&self) -> usize {
|
||||
match self {
|
||||
Header::Authority => 10, // 10 is the length of ":authority".
|
||||
Header::Method => 7, // 7 is the length of ":method".
|
||||
Header::Path => 5, // 5 is the length of ":path".
|
||||
Header::Scheme => 7, // 7 is the length of "scheme".
|
||||
Header::Status => 7, // 7 is the length of "status".
|
||||
// 10 is the length of ":authority".
|
||||
Header::Authority => 10,
|
||||
// 7 is the length of ":method".
|
||||
Header::Method => 7,
|
||||
// 5 is the length of ":path".
|
||||
Header::Path => 5,
|
||||
// 7 is the length of "scheme".
|
||||
Header::Scheme => 7,
|
||||
// 7 is the length of "status".
|
||||
Header::Status => 7,
|
||||
Header::Other(s) => s.len(),
|
||||
}
|
||||
}
|
||||
@@ -533,8 +540,10 @@ mod ut_dynamic_table {
|
||||
/// UT test cases for `StaticTable::header_name` and `StaticTable::header`.
|
||||
///
|
||||
/// # Brief
|
||||
/// 1. Iterates over a range of indices, testing both `StaticTable::header_name` and `StaticTable::header`.
|
||||
/// 2. Verifies the presence or absence of header names and headers based on the given index.
|
||||
/// 1. Iterates over a range of indices, testing both
|
||||
/// `StaticTable::header_name` and `StaticTable::header`.
|
||||
/// 2. Verifies the presence or absence of header names and headers based on
|
||||
/// the given index.
|
||||
#[test]
|
||||
fn ut_static_table() {
|
||||
// Checking header names for indices 1 to 64
|
||||
|
||||
@@ -49,7 +49,6 @@
|
||||
//! [HTTP]: https://www.rfc-editor.org/rfc/rfc9110.html
|
||||
//! [HTTP/1.1]: https://www.rfc-editor.org/rfc/rfc9112.html
|
||||
//! [TCP]: https://www.rfc-editor.org/rfc/rfc793.html
|
||||
//!
|
||||
|
||||
mod decoder;
|
||||
mod encoder;
|
||||
|
||||
+17
-11
@@ -20,11 +20,11 @@
|
||||
// TODO: 考虑将 PseudoHeaders 拆分成 `RequestPseudo` 和 `ResponsePseudo`.
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct PseudoHeaders {
|
||||
authority: Option<String>, // Request.
|
||||
method: Option<String>, // Request.
|
||||
path: Option<String>, // Request.
|
||||
scheme: Option<String>, // Request.
|
||||
status: Option<String>, // Response.
|
||||
authority: Option<String>,
|
||||
method: Option<String>,
|
||||
path: Option<String>,
|
||||
scheme: Option<String>,
|
||||
status: Option<String>,
|
||||
}
|
||||
|
||||
// TODO: 去掉冗余的方法。
|
||||
@@ -179,7 +179,8 @@ mod ut_pseudo_headers {
|
||||
/// # Brief
|
||||
/// 1. Creates a `PseudoHeaders`.
|
||||
/// 2. Calls `PseudoHeaders::contains_authority` of it.
|
||||
/// 3. Calls `PseudoHeaders::contains_authority` of it after its `authority` is set.
|
||||
/// 3. Calls `PseudoHeaders::contains_authority` of it after its `authority`
|
||||
/// is set.
|
||||
/// 4. Checks the results.
|
||||
#[test]
|
||||
fn ut_pseudo_headers_contains_authority() {
|
||||
@@ -210,7 +211,8 @@ mod ut_pseudo_headers {
|
||||
///
|
||||
/// # Brief
|
||||
/// 1. Creates a `PseudoHeaders`.
|
||||
/// 2. Calls `PseudoHeaders::set_authority` of it to set `authority` a value.
|
||||
/// 2. Calls `PseudoHeaders::set_authority` of it to set `authority` a
|
||||
/// value.
|
||||
/// 3. Checks the results.
|
||||
#[test]
|
||||
fn ut_pseudo_headers_set_authority() {
|
||||
@@ -229,7 +231,8 @@ mod ut_pseudo_headers {
|
||||
/// # Brief
|
||||
/// 1. Creates a `PseudoHeaders`.
|
||||
/// 2. Calls `PseudoHeaders::take_authority` of it.
|
||||
/// 3. Calls `PseudoHeaders::take_authority` of it after its `authority` is set.
|
||||
/// 3. Calls `PseudoHeaders::take_authority` of it after its `authority` is
|
||||
/// set.
|
||||
/// 4. Checks the results.
|
||||
#[test]
|
||||
fn ut_pseudo_headers_take_authority() {
|
||||
@@ -245,7 +248,8 @@ mod ut_pseudo_headers {
|
||||
/// # Brief
|
||||
/// 1. Creates a `PseudoHeaders`.
|
||||
/// 2. Calls `PseudoHeaders::contains_method` of it.
|
||||
/// 3. Calls `PseudoHeaders::contains_method` of it after its `method` is set.
|
||||
/// 3. Calls `PseudoHeaders::contains_method` of it after its `method` is
|
||||
/// set.
|
||||
/// 4. Checks the results.
|
||||
#[test]
|
||||
fn ut_pseudo_headers_contains_method() {
|
||||
@@ -377,7 +381,8 @@ mod ut_pseudo_headers {
|
||||
/// # Brief
|
||||
/// 1. Creates a `PseudoHeaders`.
|
||||
/// 2. Calls `PseudoHeaders::contains_scheme` of it.
|
||||
/// 3. Calls `PseudoHeaders::contains_scheme` of it after its `scheme` is set.
|
||||
/// 3. Calls `PseudoHeaders::contains_scheme` of it after its `scheme` is
|
||||
/// set.
|
||||
/// 4. Checks the results.
|
||||
#[test]
|
||||
fn ut_pseudo_headers_contains_scheme() {
|
||||
@@ -443,7 +448,8 @@ mod ut_pseudo_headers {
|
||||
/// # Brief
|
||||
/// 1. Creates a `PseudoHeaders`.
|
||||
/// 2. Calls `PseudoHeaders::contains_status` of it.
|
||||
/// 3. Calls `PseudoHeaders::contains_status` of it after its `status` is set.
|
||||
/// 3. Calls `PseudoHeaders::contains_status` of it after its `status` is
|
||||
/// set.
|
||||
/// 4. Checks the results.
|
||||
#[test]
|
||||
fn ut_pseudo_headers_contains_status() {
|
||||
|
||||
+50
-23
@@ -34,16 +34,23 @@
|
||||
//! headers.insert("Accept", "text/html").unwrap();
|
||||
//! headers.insert("Content-Length", "3495").unwrap();
|
||||
//!
|
||||
//! assert_eq!(headers.get("accept").unwrap().to_str().unwrap(), "text/html");
|
||||
//! assert_eq!(headers.get("content-length").unwrap().to_str().unwrap(), "3495");
|
||||
//! assert_eq!(
|
||||
//! headers.get("accept").unwrap().to_str().unwrap(),
|
||||
//! "text/html"
|
||||
//! );
|
||||
//! assert_eq!(
|
||||
//! headers.get("content-length").unwrap().to_str().unwrap(),
|
||||
//! "3495"
|
||||
//! );
|
||||
//! ```
|
||||
|
||||
use crate::error::{ErrorKind, HttpError};
|
||||
use core::convert::TryFrom;
|
||||
use core::{fmt, slice, str};
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::collections::{hash_map, HashMap};
|
||||
|
||||
use crate::error::{ErrorKind, HttpError};
|
||||
|
||||
/// HTTP `Header`, which consists of [`HeaderName`] and [`HeaderValue`].
|
||||
///
|
||||
/// `Header` is called `Field` in RFC9110. HTTP uses fields to provide data in
|
||||
@@ -56,6 +63,7 @@ use std::collections::{hash_map, HashMap};
|
||||
///
|
||||
/// ```
|
||||
/// use core::convert::TryFrom;
|
||||
///
|
||||
/// use ylong_http::headers::Header;
|
||||
///
|
||||
/// // This header name string will be normalized to lowercase.
|
||||
@@ -97,6 +105,7 @@ impl Header {
|
||||
///
|
||||
/// ```
|
||||
/// use core::convert::TryFrom;
|
||||
///
|
||||
/// use ylong_http::headers::Header;
|
||||
///
|
||||
/// let header = Header::try_from(("Example-Field", "Foo")).unwrap();
|
||||
@@ -114,6 +123,7 @@ impl Header {
|
||||
///
|
||||
/// ```
|
||||
/// use core::convert::TryFrom;
|
||||
///
|
||||
/// use ylong_http::headers::Header;
|
||||
///
|
||||
/// let header = Header::try_from(("Example-Field", "Foo")).unwrap();
|
||||
@@ -125,12 +135,14 @@ impl Header {
|
||||
&self.value
|
||||
}
|
||||
|
||||
/// Consumes this `Header`, get the underlying `HeaderName` and `HeaderValue`.
|
||||
/// Consumes this `Header`, get the underlying `HeaderName` and
|
||||
/// `HeaderValue`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use core::convert::TryFrom;
|
||||
///
|
||||
/// use ylong_http::headers::Header;
|
||||
///
|
||||
/// let header = Header::try_from(("Example-Field", "Foo")).unwrap();
|
||||
@@ -312,7 +324,8 @@ impl HeaderValue {
|
||||
})
|
||||
}
|
||||
|
||||
/// Consume another `HeaderValue`, and then appends it to this `HeaderValue`.
|
||||
/// Consume another `HeaderValue`, and then appends it to this
|
||||
/// `HeaderValue`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -530,7 +543,8 @@ pub type HeaderValueIterMut<'a> = slice::IterMut<'a, Vec<u8>>;
|
||||
|
||||
/// HTTP `Headers`, which is called [`Fields`] in RFC9110.
|
||||
///
|
||||
/// Fields are sent and received within the header and trailer sections of messages.
|
||||
/// Fields are sent and received within the header and trailer sections of
|
||||
/// messages.
|
||||
///
|
||||
/// [`Fields`]: https://httpwg.org/specs/rfc9110.html#fields
|
||||
///
|
||||
@@ -544,8 +558,14 @@ pub type HeaderValueIterMut<'a> = slice::IterMut<'a, Vec<u8>>;
|
||||
/// headers.insert("Content-Length", "3495").unwrap();
|
||||
/// headers.append("Accept", "text/plain").unwrap();
|
||||
///
|
||||
/// assert_eq!(headers.get("accept").unwrap().to_str().unwrap(), "text/html, text/plain");
|
||||
/// assert_eq!(headers.get("content-length").unwrap().to_str().unwrap(), "3495");
|
||||
/// assert_eq!(
|
||||
/// headers.get("accept").unwrap().to_str().unwrap(),
|
||||
/// "text/html, text/plain"
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// headers.get("content-length").unwrap().to_str().unwrap(),
|
||||
/// "3495"
|
||||
/// );
|
||||
/// ```
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct Headers {
|
||||
@@ -629,9 +649,9 @@ impl Headers {
|
||||
///
|
||||
/// ```
|
||||
/// use ylong_http::headers::Headers;
|
||||
///
|
||||
///
|
||||
/// let mut headers = Headers::new();
|
||||
/// headers.append("accept","text/html").unwrap();
|
||||
/// headers.append("accept", "text/html").unwrap();
|
||||
///
|
||||
/// let value = headers.get("accept");
|
||||
/// assert_eq!(value.unwrap().to_str().unwrap(), "text/html");
|
||||
@@ -677,7 +697,8 @@ impl Headers {
|
||||
/// If the input argument could not be successfully converted to a `Header`,
|
||||
/// `Err` is returned.
|
||||
///
|
||||
/// If the `Headers` did not have this `HeaderName` present, `None` is returned.
|
||||
/// If the `Headers` did not have this `HeaderName` present, `None` is
|
||||
/// returned.
|
||||
///
|
||||
/// If the `Headers` did have this `HeaderName` present, the new
|
||||
/// `HeaderValue` is updated, and the old `HeaderValue` is returned.
|
||||
@@ -789,7 +810,7 @@ impl Headers {
|
||||
/// use ylong_http::headers::Headers;
|
||||
///
|
||||
/// let mut headers = Headers::new();
|
||||
/// headers.append("accept","text/html").unwrap();
|
||||
/// headers.append("accept", "text/html").unwrap();
|
||||
///
|
||||
/// for (_name, _value) in headers.iter() {
|
||||
/// // Operate on each `HeaderName` and `HeaderValue` pair.
|
||||
@@ -808,7 +829,7 @@ impl Headers {
|
||||
/// use ylong_http::headers::Headers;
|
||||
///
|
||||
/// let mut headers = Headers::new();
|
||||
/// headers.append("accept","text/html").unwrap();
|
||||
/// headers.append("accept", "text/html").unwrap();
|
||||
///
|
||||
/// for (_name, _value) in headers.iter_mut() {
|
||||
/// // Operate on each `HeaderName` and `HeaderValue` pair.
|
||||
@@ -833,7 +854,7 @@ impl IntoIterator for Headers {
|
||||
/// use ylong_http::headers::Headers;
|
||||
///
|
||||
/// let mut headers = Headers::new();
|
||||
/// headers.append("accept","text/html").unwrap();
|
||||
/// headers.append("accept", "text/html").unwrap();
|
||||
///
|
||||
/// for (_name, _value) in headers.into_iter() {
|
||||
/// // Operate on each `HeaderName` and `HeaderValue` pair.
|
||||
@@ -874,7 +895,7 @@ impl<'a> IntoIterator for &'a mut Headers {
|
||||
/// use ylong_http::headers::Headers;
|
||||
///
|
||||
/// let mut headers = Headers::new();
|
||||
/// headers.append("accept","text/html").unwrap();
|
||||
/// headers.append("accept", "text/html").unwrap();
|
||||
///
|
||||
/// for (_name, _value) in headers.iter() {
|
||||
/// // Operate on each `HeaderName` and `HeaderValue` pair.
|
||||
@@ -894,7 +915,7 @@ pub type HeadersIter<'a> = hash_map::Iter<'a, HeaderName, HeaderValue>;
|
||||
/// use ylong_http::headers::Headers;
|
||||
///
|
||||
/// let mut headers = Headers::new();
|
||||
/// headers.append("accept","text/html").unwrap();
|
||||
/// headers.append("accept", "text/html").unwrap();
|
||||
///
|
||||
/// for (_name, _value) in headers.iter_mut() {
|
||||
/// // Operate on each `HeaderName` and `HeaderValue` pair.
|
||||
@@ -914,7 +935,7 @@ pub type HeadersIterMut<'a> = hash_map::IterMut<'a, HeaderName, HeaderValue>;
|
||||
/// use ylong_http::headers::Headers;
|
||||
///
|
||||
/// let mut headers = Headers::new();
|
||||
/// headers.append("accept","text/html").unwrap();
|
||||
/// headers.append("accept", "text/html").unwrap();
|
||||
///
|
||||
/// for (_name, _value) in headers.into_iter() {
|
||||
/// // Operate on each `HeaderName` and `HeaderValue` pair.
|
||||
@@ -922,7 +943,8 @@ pub type HeadersIterMut<'a> = hash_map::IterMut<'a, HeaderName, HeaderValue>;
|
||||
/// ```
|
||||
pub type HeadersIntoIter = hash_map::IntoIter<HeaderName, HeaderValue>;
|
||||
|
||||
// HEADER_CHARS is used to check whether char is correct and transfer to lowercase.
|
||||
// HEADER_CHARS is used to check whether char is correct and transfer to
|
||||
// lowercase.
|
||||
#[rustfmt::skip]
|
||||
const HEADER_CHARS: [u8; 256] = [
|
||||
// 0 1 2 3 4 5 6 7 8 9
|
||||
@@ -956,9 +978,10 @@ const HEADER_CHARS: [u8; 256] = [
|
||||
|
||||
#[cfg(test)]
|
||||
mod ut_headers {
|
||||
use crate::headers::{Header, HeaderName, HeaderValue, Headers};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::headers::{Header, HeaderName, HeaderValue, Headers};
|
||||
|
||||
/// UT test cases for `HeaderName::from_bytes`.
|
||||
///
|
||||
/// # Brief
|
||||
@@ -1007,7 +1030,8 @@ mod ut_headers {
|
||||
///
|
||||
/// # Brief
|
||||
/// 1. Creates a `Header`.
|
||||
/// 2. Calls Header::from_raw_parts, name, value and into_parts respectively.
|
||||
/// 2. Calls Header::from_raw_parts, name, value and into_parts
|
||||
/// respectively.
|
||||
/// 3. Checks if the test results are corrent.
|
||||
#[test]
|
||||
fn ut_header_methods() {
|
||||
@@ -1065,7 +1089,8 @@ mod ut_headers {
|
||||
///
|
||||
/// # Brief
|
||||
/// 1. Creates a `HeaderValue`.
|
||||
/// 2. Calls `HeaderValue::is_sensitive` to check if the test results are correct.
|
||||
/// 2. Calls `HeaderValue::is_sensitive` to check if the test results are
|
||||
/// correct.
|
||||
#[test]
|
||||
fn ut_header_value_is_sensitive() {
|
||||
let mut value = HeaderValue {
|
||||
@@ -1081,7 +1106,8 @@ mod ut_headers {
|
||||
///
|
||||
/// # Brief
|
||||
/// 1. Creates a `Headers`.
|
||||
/// 2. Gets the mutable `HeaderValue` by calling `HeaderValue::append_bytes`.
|
||||
/// 2. Gets the mutable `HeaderValue` by calling
|
||||
/// `HeaderValue::append_bytes`.
|
||||
/// 3. Modifies `HeaderValue`.
|
||||
/// 3. Checks if the test results are correct.
|
||||
#[test]
|
||||
@@ -1098,7 +1124,8 @@ mod ut_headers {
|
||||
///
|
||||
/// # Brief
|
||||
/// 1. Creates a `HeaderValue`.
|
||||
/// 2. Adds new value content into `HeaderValue` by calling `HeaderValue::append_bytes`.
|
||||
/// 2. Adds new value content into `HeaderValue` by calling
|
||||
/// `HeaderValue::append_bytes`.
|
||||
/// 3. Checks if the test results are correct.
|
||||
#[test]
|
||||
fn ut_header_value_append_bytes() {
|
||||
|
||||
@@ -35,10 +35,10 @@
|
||||
|
||||
mod consts;
|
||||
|
||||
use consts::{HUFFMAN_DECODE, HUFFMAN_ENCODE};
|
||||
|
||||
use core::cmp::Ordering;
|
||||
|
||||
use consts::{HUFFMAN_DECODE, HUFFMAN_ENCODE};
|
||||
|
||||
/// Converts a string to a Huffman code, and then put it into the
|
||||
/// specified `Vec<u8>`.
|
||||
pub(crate) fn huffman_encode(src: &[u8], dst: &mut Vec<u8>) {
|
||||
@@ -68,7 +68,8 @@ pub(crate) fn huffman_encode(src: &[u8], dst: &mut Vec<u8>) {
|
||||
// +--------------------------------+-----------------+
|
||||
|
||||
let mut state = 0u64;
|
||||
// The initial value of `unfilled` is equal to the number of bits in the `state`.
|
||||
// The initial value of `unfilled` is equal to the number of bits in the
|
||||
// `state`.
|
||||
let mut unfilled = 64;
|
||||
|
||||
for byte in src.iter() {
|
||||
@@ -117,8 +118,8 @@ pub(crate) fn huffman_encode(src: &[u8], dst: &mut Vec<u8>) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a Huffman code into a literal string at one time, and then put it into the
|
||||
/// specified `Vec<u8>`.
|
||||
/// Converts a Huffman code into a literal string at one time, and then put it
|
||||
/// into the specified `Vec<u8>`.
|
||||
///
|
||||
/// The algorithm comes from crate [h2].
|
||||
///
|
||||
@@ -355,7 +356,8 @@ mod ut_huffman {
|
||||
///
|
||||
/// # Brief
|
||||
/// 1. Creates a `HuffmanDecoder`.
|
||||
/// 1. Calls `decode` and `finish` function, passing in the specified parameters.
|
||||
/// 1. Calls `decode` and `finish` function, passing in the specified
|
||||
/// parameters.
|
||||
/// 2. Checks if the test results are correct.
|
||||
#[test]
|
||||
fn ut_huffman_decoder() {
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
//!
|
||||
//! # Support HTTP Version
|
||||
//! - `HTTP1.1`
|
||||
//!
|
||||
// TODO: Need doc.
|
||||
|
||||
#[cfg(feature = "http1_1")]
|
||||
@@ -47,6 +46,5 @@ pub(crate) mod test_util;
|
||||
|
||||
#[cfg(feature = "tokio_base")]
|
||||
pub(crate) use tokio::io::{AsyncRead, AsyncReadExt, ReadBuf};
|
||||
|
||||
#[cfg(feature = "ylong_base")]
|
||||
pub(crate) use ylong_runtime::io::{AsyncRead, AsyncReadExt, ReadBuf};
|
||||
|
||||
@@ -27,9 +27,10 @@
|
||||
//! assert_eq!(Method::GET.as_str(), "GET");
|
||||
//! ```
|
||||
|
||||
use crate::error::{ErrorKind, HttpError};
|
||||
use core::convert::TryFrom;
|
||||
|
||||
use crate::error::{ErrorKind, HttpError};
|
||||
|
||||
/// HTTP `Method` implementation.
|
||||
///
|
||||
/// # Examples
|
||||
@@ -100,7 +101,8 @@ impl Method {
|
||||
/// [`RFC9110 9.3.8`]: https://httpwg.org/specs/rfc9110.html#TRACE
|
||||
pub const TRACE: Self = Self(Inner::Trace);
|
||||
|
||||
/// Tries converting &[u8] to `Method`. Only uppercase letters are supported.
|
||||
/// Tries converting &[u8] to `Method`. Only uppercase letters are
|
||||
/// supported.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
||||
@@ -33,8 +33,8 @@
|
||||
//! .method("GET")
|
||||
//! .url("www.example.com")
|
||||
//! .version("HTTP/1.1")
|
||||
//! .header("ACCEPT","text/html")
|
||||
//! .append_header("ACCEPT","application/xml")
|
||||
//! .header("ACCEPT", "text/html")
|
||||
//! .append_header("ACCEPT", "application/xml")
|
||||
//! .body(())
|
||||
//! .unwrap();
|
||||
//!
|
||||
@@ -50,6 +50,8 @@
|
||||
pub mod method;
|
||||
pub mod uri;
|
||||
|
||||
use core::convert::TryFrom;
|
||||
|
||||
use method::Method;
|
||||
use uri::Uri;
|
||||
|
||||
@@ -57,7 +59,6 @@ use crate::body::MultiPart;
|
||||
use crate::error::{ErrorKind, HttpError};
|
||||
use crate::headers::{Header, HeaderName, HeaderValue, Headers};
|
||||
use crate::version::Version;
|
||||
use core::convert::TryFrom;
|
||||
|
||||
/// HTTP `Request`. A `Request` consists of a request line and a body.
|
||||
///
|
||||
@@ -105,7 +106,8 @@ impl Request<()> {
|
||||
RequestBuilder::new().method(Method::GET).url(uri)
|
||||
}
|
||||
|
||||
/// Creates a `RequestBuilder` for the given `Uri` with method set to `HEAD`.
|
||||
/// Creates a `RequestBuilder` for the given `Uri` with method set to
|
||||
/// `HEAD`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -122,7 +124,8 @@ impl Request<()> {
|
||||
RequestBuilder::new().method(Method::HEAD).url(uri)
|
||||
}
|
||||
|
||||
/// Creates a `RequestBuilder` for the given `Uri` with method set to `POST`.
|
||||
/// Creates a `RequestBuilder` for the given `Uri` with method set to
|
||||
/// `POST`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -156,7 +159,8 @@ impl Request<()> {
|
||||
RequestBuilder::new().method(Method::PUT).url(uri)
|
||||
}
|
||||
|
||||
/// Creates a `RequestBuilder` for the given `Uri` with method set to `DELETE`.
|
||||
/// Creates a `RequestBuilder` for the given `Uri` with method set to
|
||||
/// `DELETE`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -173,7 +177,8 @@ impl Request<()> {
|
||||
RequestBuilder::new().method(Method::DELETE).url(uri)
|
||||
}
|
||||
|
||||
/// Creates a `RequestBuilder` for the given `Uri` with method set to `CONNECT`.
|
||||
/// Creates a `RequestBuilder` for the given `Uri` with method set to
|
||||
/// `CONNECT`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -190,7 +195,8 @@ impl Request<()> {
|
||||
RequestBuilder::new().method(Method::CONNECT).url(uri)
|
||||
}
|
||||
|
||||
/// Creates a `RequestBuilder` for the given `Uri` with method set to `OPTIONS`.
|
||||
/// Creates a `RequestBuilder` for the given `Uri` with method set to
|
||||
/// `OPTIONS`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -207,7 +213,8 @@ impl Request<()> {
|
||||
RequestBuilder::new().method(Method::OPTIONS).url(uri)
|
||||
}
|
||||
|
||||
/// Creates a `RequestBuilder` for the given `Uri` with method set to `TRACE`.
|
||||
/// Creates a `RequestBuilder` for the given `Uri` with method set to
|
||||
/// `TRACE`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -459,8 +466,8 @@ impl<T: Clone> Clone for Request<T> {
|
||||
/// .method("GET")
|
||||
/// .url("www.example.com")
|
||||
/// .version("HTTP/1.1")
|
||||
/// .header("ACCEPT","text/html")
|
||||
/// .append_header("ACCEPT","application/xml")
|
||||
/// .header("ACCEPT", "text/html")
|
||||
/// .append_header("ACCEPT", "application/xml")
|
||||
/// .body(())
|
||||
/// .unwrap();
|
||||
///
|
||||
@@ -565,7 +572,7 @@ impl RequestBuilder {
|
||||
/// use ylong_http::headers::Headers;
|
||||
/// use ylong_http::request::RequestBuilder;
|
||||
///
|
||||
/// let request = RequestBuilder::new().header("ACCEPT","text/html");
|
||||
/// let request = RequestBuilder::new().header("ACCEPT", "text/html");
|
||||
/// ```
|
||||
pub fn header<N, V>(mut self, name: N, value: V) -> Self
|
||||
where
|
||||
@@ -590,7 +597,7 @@ impl RequestBuilder {
|
||||
/// use ylong_http::headers::Headers;
|
||||
/// use ylong_http::request::RequestBuilder;
|
||||
///
|
||||
/// let request = RequestBuilder::new().append_header("ACCEPT","text/html");
|
||||
/// let request = RequestBuilder::new().append_header("ACCEPT", "text/html");
|
||||
/// ```
|
||||
pub fn append_header<N, V>(mut self, name: N, value: V) -> Self
|
||||
where
|
||||
@@ -674,8 +681,8 @@ impl Default for RequestBuilder {
|
||||
/// `RequestPart`, which is called [`Request Line`] in [`RFC9112`].
|
||||
///
|
||||
/// A request-line begins with a method token, followed by a single space (SP),
|
||||
/// the request-target, and another single space (SP), and ends with the protocol
|
||||
/// version.
|
||||
/// the request-target, and another single space (SP), and ends with the
|
||||
/// protocol version.
|
||||
///
|
||||
/// [`RFC9112`]: https://httpwg.org/specs/rfc9112.html
|
||||
/// [`Request Line`]: https://httpwg.org/specs/rfc9112.html#request.line
|
||||
@@ -715,10 +722,11 @@ impl Default for RequestPart {
|
||||
|
||||
#[cfg(test)]
|
||||
mod ut_request {
|
||||
use core::convert::TryFrom;
|
||||
|
||||
use super::{Method, Request, RequestBuilder, RequestPart, Uri};
|
||||
use crate::headers::Headers;
|
||||
use crate::version::Version;
|
||||
use core::convert::TryFrom;
|
||||
|
||||
/// UT test cases for `RequestBuilder::build`.
|
||||
///
|
||||
|
||||
@@ -18,9 +18,10 @@
|
||||
//!
|
||||
//! [`URI`]: https://httpwg.org/specs/rfc9110.html#uri.references
|
||||
|
||||
use crate::error::{ErrorKind, HttpError};
|
||||
use core::convert::{Infallible, TryFrom, TryInto};
|
||||
|
||||
use crate::error::{ErrorKind, HttpError};
|
||||
|
||||
// Maximum uri length.
|
||||
const MAX_URI_LEN: usize = (u16::MAX - 1) as usize;
|
||||
|
||||
@@ -469,12 +470,12 @@ impl UriBuilder {
|
||||
/// use ylong_http::request::uri::UriBuilder;
|
||||
///
|
||||
/// let uri = UriBuilder::new()
|
||||
/// .scheme("http")
|
||||
/// .authority("example.com:80")
|
||||
/// .path("/foo")
|
||||
/// .query("a=1")
|
||||
/// .build()
|
||||
/// .unwrap();
|
||||
/// .scheme("http")
|
||||
/// .authority("example.com:80")
|
||||
/// .path("/foo")
|
||||
/// .query("a=1")
|
||||
/// .build()
|
||||
/// .unwrap();
|
||||
/// ```
|
||||
pub fn build(self) -> Result<Uri, HttpError> {
|
||||
self.unprocessed
|
||||
@@ -946,7 +947,8 @@ fn scheme_token(bytes: &[u8]) -> Result<(Option<Scheme>, &[u8]), InvalidUri> {
|
||||
Some((0, _)) => return Err(InvalidUri::InvalidScheme),
|
||||
_ => return Ok((None, bytes)),
|
||||
};
|
||||
// Currently, only HTTP and HTTPS are supported. Therefore, you need to verify the scheme content.
|
||||
// Currently, only HTTP and HTTPS are supported. Therefore, you need to verify
|
||||
// the scheme content.
|
||||
if bytes[..pos].eq_ignore_ascii_case(b"http") {
|
||||
Ok((Some(Protocol::Http.into()), &bytes[HTTP_SCHEME_LENGTH..]))
|
||||
} else if bytes[..pos].eq_ignore_ascii_case(b"https") {
|
||||
@@ -985,7 +987,8 @@ fn authority_token(bytes: &[u8]) -> Result<(Option<Authority>, &[u8]), InvalidUr
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO According to RFC3986, the character @ can be one of the reserved characters, which needs to be improved after being familiar with the rules.
|
||||
// TODO According to RFC3986, the character @ can be one of the reserved characters,
|
||||
// which needs to be improved after being familiar with the rules.
|
||||
b'@' => {
|
||||
return Err(InvalidUri::UriContainUserinfo);
|
||||
}
|
||||
|
||||
@@ -23,7 +23,8 @@ use crate::version::Version;
|
||||
|
||||
/// HTTP `Response` Implementation.
|
||||
///
|
||||
/// The status-line and field-line of a response-message are stored in `Response`.
|
||||
/// The status-line and field-line of a response-message are stored in
|
||||
/// `Response`.
|
||||
///
|
||||
/// The body can be saved in the user-defined type.
|
||||
///
|
||||
@@ -85,8 +86,8 @@ impl<T: Clone> Clone for Response<T> {
|
||||
/// `ResponsePart`, which is called [`Status Line`] in [`RFC9112`].
|
||||
///
|
||||
/// A request-line begins with a method token, followed by a single space (SP),
|
||||
/// the request-target, and another single space (SP), and ends with the protocol
|
||||
/// version.
|
||||
/// the request-target, and another single space (SP), and ends with the
|
||||
/// protocol version.
|
||||
///
|
||||
/// [`RFC9112`]: https://httpwg.org/specs/rfc9112.html
|
||||
/// [`Status Line`]: https://httpwg.org/specs/rfc9112.html#status.line
|
||||
@@ -130,7 +131,8 @@ mod ut_response {
|
||||
///
|
||||
/// # Brief
|
||||
/// 1. Creates a `ResponsePart` by calling `ResponseDecoder::decode`.
|
||||
/// 2. Gets the reference of a `StatusCode` by calling `Response::status_code`.
|
||||
/// 2. Gets the reference of a `StatusCode` by calling
|
||||
/// `Response::status_code`.
|
||||
/// 3. Checks if the test result is correct.
|
||||
#[test]
|
||||
fn ut_response_status_code() {
|
||||
|
||||
@@ -20,10 +20,11 @@
|
||||
//!
|
||||
//! [`Status Codes`]: https://httpwg.org/specs/rfc9110.html#status.codes
|
||||
|
||||
use crate::error::{ErrorKind, HttpError};
|
||||
use core::convert::TryFrom;
|
||||
use core::fmt::{Display, Formatter};
|
||||
|
||||
use crate::error::{ErrorKind, HttpError};
|
||||
|
||||
/// HTTP [`Status Codes`] implementation.
|
||||
///
|
||||
/// [`Status Codes`]: https://httpwg.org/specs/rfc9110.html#status.codes
|
||||
|
||||
@@ -21,9 +21,10 @@
|
||||
//!
|
||||
//! [`Version`]: https://httpwg.org/specs/rfc9110.html#protocol.version
|
||||
|
||||
use crate::error::{ErrorKind, HttpError};
|
||||
use core::convert::TryFrom;
|
||||
|
||||
use crate::error::{ErrorKind, HttpError};
|
||||
|
||||
/// HTTP [`Version`] implementation.
|
||||
///
|
||||
/// [`Version`]: https://httpwg.org/specs/rfc9110.html#protocol.version
|
||||
@@ -86,9 +87,10 @@ enum Inner {
|
||||
|
||||
#[cfg(test)]
|
||||
mod ut_version {
|
||||
use super::Version;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use super::Version;
|
||||
|
||||
/// UT test cases for `Version::as_str`.
|
||||
///
|
||||
/// # Brief
|
||||
|
||||
@@ -11,8 +11,9 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! This is a simple asynchronous HTTP client example using the ylong_http_client crate.
|
||||
//! It demonstrates creating a client, making a request, and reading the response asynchronously.
|
||||
//! This is a simple asynchronous HTTP client example using the
|
||||
//! ylong_http_client crate. It demonstrates creating a client, making a
|
||||
//! request, and reading the response asynchronously.
|
||||
|
||||
use ylong_http_client::async_impl::{Client, Downloader};
|
||||
use ylong_http_client::{HttpClientError, Request};
|
||||
|
||||
@@ -11,8 +11,9 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! This is a simple asynchronous HTTP2 client example using the ylong_http_client crate.
|
||||
//! It demonstrates creating a client, making a request, and reading the response asynchronously.
|
||||
//! This is a simple asynchronous HTTP2 client example using the
|
||||
//! ylong_http_client crate. It demonstrates creating a client, making a
|
||||
//! request, and reading the response asynchronously.
|
||||
|
||||
use ylong_http_client::async_impl::{Body, ClientBuilder};
|
||||
use ylong_http_client::{HttpClientError, RequestBuilder, StatusCode, TextBody, Version};
|
||||
|
||||
@@ -11,10 +11,12 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! This is a simple asynchronous HTTP client example in concurrent scenarios using the ylong_http_client crate.
|
||||
//! It demonstrates creating a client, making a request, and reading the response asynchronously.
|
||||
//! This is a simple asynchronous HTTP client example in concurrent scenarios
|
||||
//! using the ylong_http_client crate. It demonstrates creating a client, making
|
||||
//! a request, and reading the response asynchronously.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use ylong_http_client::async_impl::{Body, ClientBuilder};
|
||||
use ylong_http_client::{HttpClientError, RequestBuilder, StatusCode, TextBody, Version};
|
||||
|
||||
|
||||
@@ -11,8 +11,9 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! This is a simple asynchronous HTTP client example using the ylong_http_client crate.
|
||||
//! It demonstrates creating a client, making a request, and reading the response asynchronously.
|
||||
//! This is a simple asynchronous HTTP client example using the
|
||||
//! ylong_http_client crate. It demonstrates creating a client, making a
|
||||
//! request, and reading the response asynchronously.
|
||||
use ylong_http_client::async_impl::{ClientBuilder, Downloader};
|
||||
use ylong_http_client::{EmptyBody, HttpClientError, Proxy, Request};
|
||||
|
||||
|
||||
@@ -11,8 +11,9 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! This is a simple asynchronous HTTP client redirect example using the ylong_http_client crate.
|
||||
//! It demonstrates creating a client, making a request, and reading the response asynchronously.
|
||||
//! This is a simple asynchronous HTTP client redirect example using the
|
||||
//! ylong_http_client crate. It demonstrates creating a client, making a
|
||||
//! request, and reading the response asynchronously.
|
||||
|
||||
use ylong_http_client::async_impl::{ClientBuilder, Downloader};
|
||||
use ylong_http_client::{HttpClientError, Redirect, Request};
|
||||
|
||||
@@ -11,8 +11,9 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! This is a simple synchronous HTTP client example using the ylong_http_client crate.
|
||||
//! It demonstrates creating a client, making a request, and reading the response.
|
||||
//! This is a simple synchronous HTTP client example using the ylong_http_client
|
||||
//! crate. It demonstrates creating a client, making a request, and reading the
|
||||
//! response.
|
||||
|
||||
use ylong_http_client::sync_impl::{BodyReader, Client};
|
||||
use ylong_http_client::{HttpClientError, Request};
|
||||
|
||||
@@ -11,8 +11,9 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! This is a simple synchronous HTTP client example using the ylong_http_client crate.
|
||||
//! It demonstrates creating a client, making a request, and reading the response.
|
||||
//! This is a simple synchronous HTTP client example using the ylong_http_client
|
||||
//! crate. It demonstrates creating a client, making a request, and reading the
|
||||
//! response.
|
||||
use ylong_http_client::sync_impl::{BodyReader, ClientBuilder};
|
||||
use ylong_http_client::util::Proxy;
|
||||
use ylong_http_client::{EmptyBody, HttpClientError, Request};
|
||||
|
||||
@@ -11,8 +11,9 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! This is a simple synchronous HTTP client redirect example using the ylong_http_client crate.
|
||||
//! It demonstrates creating a client, making a request, and reading the response.
|
||||
//! This is a simple synchronous HTTP client redirect example using the
|
||||
//! ylong_http_client crate. It demonstrates creating a client, making a
|
||||
//! request, and reading the response.
|
||||
|
||||
use ylong_http_client::sync_impl::{BodyReader, ClientBuilder};
|
||||
use ylong_http_client::util::Redirect;
|
||||
|
||||
@@ -11,10 +11,9 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::async_impl::HttpBody;
|
||||
use crate::{ErrorKind, HttpClientError};
|
||||
use std::convert::TryFrom;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use ylong_http::body::async_impl::Body;
|
||||
use ylong_http::body::MultiPart;
|
||||
use ylong_http::error::HttpError;
|
||||
@@ -25,6 +24,9 @@ use ylong_http::request::{Request, RequestBuilder as ReqBuilder};
|
||||
use ylong_http::response::Response as Resp;
|
||||
use ylong_http::version::Version;
|
||||
|
||||
use crate::async_impl::HttpBody;
|
||||
use crate::{ErrorKind, HttpClientError};
|
||||
|
||||
/// Response Adapter.
|
||||
pub struct Response {
|
||||
response: Resp<HttpBody>,
|
||||
@@ -117,7 +119,7 @@ impl RequestBuilder {
|
||||
/// use ylong_http::headers::Headers;
|
||||
/// use ylong_http::request::RequestBuilder;
|
||||
///
|
||||
/// let request = RequestBuilder::new().header("ACCEPT","text/html");
|
||||
/// let request = RequestBuilder::new().header("ACCEPT", "text/html");
|
||||
/// ```
|
||||
pub fn header<N, V>(mut self, name: N, value: V) -> Self
|
||||
where
|
||||
@@ -139,7 +141,7 @@ impl RequestBuilder {
|
||||
/// use ylong_http::headers::Headers;
|
||||
/// use ylong_http::request::RequestBuilder;
|
||||
///
|
||||
/// let request = RequestBuilder::new().append_header("ACCEPT","text/html");
|
||||
/// let request = RequestBuilder::new().append_header("ACCEPT", "text/html");
|
||||
/// ```
|
||||
pub fn append_header<N, V>(mut self, name: N, value: V) -> Self
|
||||
where
|
||||
|
||||
@@ -11,22 +11,21 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use ylong_http::body::{ChunkBody, TextBody};
|
||||
use ylong_http::response::Response;
|
||||
|
||||
use super::{conn, Body, ConnPool, Connector, HttpBody, HttpConnector};
|
||||
use crate::async_impl::timeout::TimeoutFuture;
|
||||
use crate::util::normalizer::{RequestFormatter, UriFormatter};
|
||||
use crate::util::proxy::Proxies;
|
||||
use crate::util::redirect::TriggerKind;
|
||||
use crate::util::{ClientConfig, ConnectorConfig, HttpConfig, HttpVersion, Redirect};
|
||||
use crate::{sleep, timeout};
|
||||
use crate::{ErrorKind, HttpClientError, Proxy, Request, Timeout, Uri};
|
||||
use ylong_http::body::{ChunkBody, TextBody};
|
||||
use ylong_http::response::Response;
|
||||
|
||||
#[cfg(feature = "http2")]
|
||||
use crate::H2Config;
|
||||
use crate::{sleep, timeout, ErrorKind, HttpClientError, Proxy, Request, Timeout, Uri};
|
||||
|
||||
/// HTTP asynchronous client implementation. Users can use `async_impl::Client` to
|
||||
/// send `Request` asynchronously. `async_impl::Client` depends on a
|
||||
/// HTTP asynchronous client implementation. Users can use `async_impl::Client`
|
||||
/// to send `Request` asynchronously. `async_impl::Client` depends on a
|
||||
/// [`async_impl::Connector`] that can be customized by the user.
|
||||
///
|
||||
/// [`async_impl::Connector`]: Connector
|
||||
@@ -35,7 +34,7 @@ use crate::H2Config;
|
||||
///
|
||||
/// ```
|
||||
/// use ylong_http_client::async_impl::Client;
|
||||
/// use ylong_http_client::{Request, EmptyBody};
|
||||
/// use ylong_http_client::{EmptyBody, Request};
|
||||
///
|
||||
/// async fn async_client() {
|
||||
/// // Creates a new `Client`.
|
||||
@@ -58,7 +57,8 @@ pub struct Client<C: Connector> {
|
||||
}
|
||||
|
||||
impl Client<HttpConnector> {
|
||||
/// Creates a new, default `AsyncClient`, which uses [`async_impl::HttpConnector`].
|
||||
/// Creates a new, default `AsyncClient`, which uses
|
||||
/// [`async_impl::HttpConnector`].
|
||||
///
|
||||
/// [`async_impl::HttpConnector`]: HttpConnector
|
||||
///
|
||||
@@ -106,7 +106,7 @@ impl<C: Connector> Client<C> {
|
||||
///
|
||||
/// ```
|
||||
/// use ylong_http_client::async_impl::Client;
|
||||
/// use ylong_http_client::{Request, EmptyBody};
|
||||
/// use ylong_http_client::{EmptyBody, Request};
|
||||
///
|
||||
/// async fn async_client() {
|
||||
/// let client = Client::new();
|
||||
@@ -568,9 +568,7 @@ impl ClientBuilder {
|
||||
/// use ylong_http_client::async_impl::ClientBuilder;
|
||||
///
|
||||
/// let builder = ClientBuilder::new()
|
||||
/// .tls_cipher_list(
|
||||
/// "DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK"
|
||||
/// );
|
||||
/// .tls_cipher_list("DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK");
|
||||
/// ```
|
||||
pub fn tls_cipher_list(mut self, list: &str) -> Self {
|
||||
self.tls = self.tls.cipher_list(list);
|
||||
@@ -589,26 +587,24 @@ impl ClientBuilder {
|
||||
/// ```
|
||||
/// use ylong_http_client::async_impl::ClientBuilder;
|
||||
///
|
||||
/// let builder = ClientBuilder::new()
|
||||
/// .tls_cipher_suites(
|
||||
/// "DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK"
|
||||
/// );
|
||||
/// let builder = ClientBuilder::new().tls_cipher_suites(
|
||||
/// "DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK",
|
||||
/// );
|
||||
/// ```
|
||||
pub fn tls_cipher_suites(mut self, list: &str) -> Self {
|
||||
self.tls = self.tls.cipher_suites(list);
|
||||
self
|
||||
}
|
||||
|
||||
/// Controls the use of built-in system certificates during certificate validation.
|
||||
/// Default to `true` -- uses built-in system certs.
|
||||
/// Controls the use of built-in system certificates during certificate
|
||||
/// validation. Default to `true` -- uses built-in system certs.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ylong_http_client::async_impl::ClientBuilder;
|
||||
///
|
||||
/// let builder = ClientBuilder::new()
|
||||
/// .tls_built_in_root_certs(false);
|
||||
/// let builder = ClientBuilder::new().tls_built_in_root_certs(false);
|
||||
/// ```
|
||||
pub fn tls_built_in_root_certs(mut self, is_use: bool) -> Self {
|
||||
self.tls = self.tls.build_in_root_certs(is_use);
|
||||
@@ -628,8 +624,7 @@ impl ClientBuilder {
|
||||
/// ```
|
||||
/// use ylong_http_client::async_impl::ClientBuilder;
|
||||
///
|
||||
/// let builder = ClientBuilder::new()
|
||||
/// .danger_accept_invalid_certs(true);
|
||||
/// let builder = ClientBuilder::new().danger_accept_invalid_certs(true);
|
||||
/// ```
|
||||
pub fn danger_accept_invalid_certs(mut self, is_invalid: bool) -> Self {
|
||||
self.tls = self.tls.danger_accept_invalid_certs(is_invalid);
|
||||
@@ -650,8 +645,7 @@ impl ClientBuilder {
|
||||
/// ```
|
||||
/// use ylong_http_client::async_impl::ClientBuilder;
|
||||
///
|
||||
/// let builder = ClientBuilder::new()
|
||||
/// .danger_accept_invalid_hostnames(true);
|
||||
/// let builder = ClientBuilder::new().danger_accept_invalid_hostnames(true);
|
||||
/// ```
|
||||
pub fn danger_accept_invalid_hostnames(mut self, is_invalid: bool) -> Self {
|
||||
self.tls = self.tls.danger_accept_invalid_hostnames(is_invalid);
|
||||
@@ -667,8 +661,7 @@ impl ClientBuilder {
|
||||
/// ```
|
||||
/// use ylong_http_client::async_impl::ClientBuilder;
|
||||
///
|
||||
/// let builder = ClientBuilder::new()
|
||||
/// .tls_sni(true);
|
||||
/// let builder = ClientBuilder::new().tls_sni(true);
|
||||
/// ```
|
||||
pub fn tls_sni(mut self, is_set_sni: bool) -> Self {
|
||||
self.tls = self.tls.sni(is_set_sni);
|
||||
@@ -690,7 +683,8 @@ mod ut_async_impl_client {
|
||||
///
|
||||
/// # Brief
|
||||
/// 1. Creates a ClientBuilder by calling `Client::Builder`.
|
||||
/// 2. Calls `http_config`, `client_config`, `build` on the builder respectively.
|
||||
/// 2. Calls `http_config`, `client_config`, `build` on the builder
|
||||
/// respectively.
|
||||
/// 3. Checks if the result is as expected.
|
||||
#[test]
|
||||
fn ut_client_builder() {
|
||||
@@ -702,7 +696,8 @@ mod ut_async_impl_client {
|
||||
///
|
||||
/// # Brief
|
||||
/// 1. Creates a `ClientBuilder` by calling `ClientBuilder::default`.
|
||||
/// 2. Calls `http_config`, `client_config`, `tls_config` and `build` respectively.
|
||||
/// 2. Calls `http_config`, `client_config`, `tls_config` and `build`
|
||||
/// respectively.
|
||||
/// 3. Checks if the result is as expected.
|
||||
#[cfg(feature = "__tls")]
|
||||
#[test]
|
||||
|
||||
@@ -11,16 +11,17 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
use ylong_http::h1::{RequestEncoder, ResponseDecoder};
|
||||
use ylong_http::response::Response;
|
||||
|
||||
use crate::async_impl::{Body, HttpBody, StreamData};
|
||||
use crate::error::{ErrorKind, HttpClientError};
|
||||
use crate::util::dispatcher::http1::Http1Conn;
|
||||
use crate::util::normalizer::BodyLengthParser;
|
||||
use crate::Request;
|
||||
use crate::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, ReadBuf};
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
use ylong_http::h1::{RequestEncoder, ResponseDecoder};
|
||||
use ylong_http::response::Response;
|
||||
use crate::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, ReadBuf, Request};
|
||||
|
||||
const TEMP_BUF_SIZE: usize = 16 * 1024;
|
||||
|
||||
|
||||
@@ -11,16 +11,11 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::async_impl::client::Retryable;
|
||||
use crate::async_impl::conn::HttpBody;
|
||||
use crate::async_impl::StreamData;
|
||||
use crate::error::{ErrorKind, HttpClientError};
|
||||
use crate::util::dispatcher::http2::Http2Conn;
|
||||
use crate::{AsyncRead, AsyncWrite, ReadBuf};
|
||||
use std::cmp::min;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
use ylong_http::body::async_impl::Body;
|
||||
use ylong_http::error::HttpError;
|
||||
use ylong_http::h2;
|
||||
@@ -31,6 +26,13 @@ use ylong_http::request::{Request, RequestPart};
|
||||
use ylong_http::response::status::StatusCode;
|
||||
use ylong_http::response::{Response, ResponsePart};
|
||||
|
||||
use crate::async_impl::client::Retryable;
|
||||
use crate::async_impl::conn::HttpBody;
|
||||
use crate::async_impl::StreamData;
|
||||
use crate::error::{ErrorKind, HttpClientError};
|
||||
use crate::util::dispatcher::http2::Http2Conn;
|
||||
use crate::{AsyncRead, AsyncWrite, ReadBuf};
|
||||
|
||||
const UNUSED_FLAG: u8 = 0x0;
|
||||
|
||||
pub(crate) async fn request<S, T>(
|
||||
@@ -45,7 +47,9 @@ where
|
||||
let part = request.part().clone();
|
||||
let body = request.body_mut();
|
||||
|
||||
// TODO Due to the reason of the Body structure, the use of the trailer is not implemented here for the time being, and it needs to be completed after the Body trait is provided to obtain the trailer interface
|
||||
// TODO Due to the reason of the Body structure, the use of the trailer is not
|
||||
// implemented here for the time being, and it needs to be completed after the
|
||||
// Body trait is provided to obtain the trailer interface
|
||||
match build_data_frame(conn.id as usize, body).await? {
|
||||
None => {
|
||||
let headers = build_headers_frame(conn.id, part, true)
|
||||
@@ -173,7 +177,8 @@ pub(crate) async fn build_data_frame<T: Body>(
|
||||
if data_vec.is_empty() {
|
||||
Ok(None)
|
||||
} else {
|
||||
// TODO When the Body trait supports trailer, END_STREAM_FLAG needs to be modified
|
||||
// TODO When the Body trait supports trailer, END_STREAM_FLAG needs to be
|
||||
// modified
|
||||
let mut flag = FrameFlags::new(UNUSED_FLAG);
|
||||
flag.set_end_stream(true);
|
||||
Ok(Some(Frame::new(
|
||||
@@ -248,7 +253,8 @@ fn build_pseudo_headers(request_part: &RequestPart) -> PseudoHeaders {
|
||||
.path_and_query()
|
||||
.or_else(|| Some(String::from("/"))),
|
||||
);
|
||||
// TODO Validity verification is required, for example: `Authority` must be consistent with the `Host` header
|
||||
// TODO Validity verification is required, for example: `Authority` must be
|
||||
// consistent with the `Host` header
|
||||
pseudo.set_authority(request_part.uri.authority().map(|auth| auth.to_string()));
|
||||
pseudo
|
||||
}
|
||||
@@ -376,11 +382,12 @@ impl<S: AsyncRead + AsyncWrite + Unpin + Sync + Send + 'static> AsyncRead for Te
|
||||
#[cfg(feature = "http2")]
|
||||
#[cfg(test)]
|
||||
mod ut_http2 {
|
||||
use crate::async_impl::conn::http2::{build_data_frame, build_headers_frame};
|
||||
use ylong_http::body::TextBody;
|
||||
use ylong_http::h2::{ErrorCode, H2Error, Payload};
|
||||
use ylong_http::request::RequestBuilder;
|
||||
|
||||
use crate::async_impl::conn::http2::{build_data_frame, build_headers_frame};
|
||||
|
||||
macro_rules! build_request {
|
||||
(
|
||||
Request: {
|
||||
|
||||
@@ -11,15 +11,15 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use ylong_http::body::async_impl::Body;
|
||||
use ylong_http::request::Request;
|
||||
use ylong_http::response::Response;
|
||||
|
||||
use crate::async_impl::client::Retryable;
|
||||
use crate::async_impl::HttpBody;
|
||||
use crate::error::HttpClientError;
|
||||
use crate::util::dispatcher::Conn;
|
||||
use crate::{AsyncRead, AsyncWrite};
|
||||
use ylong_http::body::async_impl::Body;
|
||||
|
||||
use crate::async_impl::client::Retryable;
|
||||
use ylong_http::request::Request;
|
||||
use ylong_http::response::Response;
|
||||
|
||||
#[cfg(feature = "http1_1")]
|
||||
mod http1;
|
||||
|
||||
@@ -13,19 +13,21 @@
|
||||
|
||||
//! Asynchronous `Connector` trait and `HttpConnector` implementation.
|
||||
|
||||
use crate::util::ConnectorConfig;
|
||||
use crate::{AsyncRead, AsyncWrite, TcpStream, Uri};
|
||||
use core::future::Future;
|
||||
use std::error::Error;
|
||||
use std::io;
|
||||
use std::net::ToSocketAddrs;
|
||||
|
||||
use crate::util::ConnectorConfig;
|
||||
use crate::{AsyncRead, AsyncWrite, TcpStream, Uri};
|
||||
|
||||
/// `Connector` trait used by `async_impl::Client`. `Connector` provides
|
||||
/// asynchronous connection establishment interfaces.
|
||||
pub trait Connector {
|
||||
/// Streams that this connector produces.
|
||||
type Stream: AsyncRead + AsyncWrite + Unpin + Sync + Send + 'static;
|
||||
/// Possible errors that this connector may generate when attempting to connect.
|
||||
/// Possible errors that this connector may generate when attempting to
|
||||
/// connect.
|
||||
type Error: Into<Box<dyn Error + Sync + Send>>;
|
||||
/// Futures generated by this connector when attempting to create a stream.
|
||||
type Future: Future<Output = Result<Self::Stream, Self::Error>> + Unpin + Sync + Send + 'static;
|
||||
@@ -70,12 +72,13 @@ async fn tcp_stream(addr: &str) -> io::Result<TcpStream> {
|
||||
|
||||
#[cfg(not(feature = "__tls"))]
|
||||
mod no_tls {
|
||||
use super::{tcp_stream, Connector, HttpConnector};
|
||||
use crate::{TcpStream, Uri};
|
||||
use core::future::Future;
|
||||
use core::pin::Pin;
|
||||
use std::io::Error;
|
||||
|
||||
use super::{tcp_stream, Connector, HttpConnector};
|
||||
use crate::{TcpStream, Uri};
|
||||
|
||||
impl Connector for HttpConnector {
|
||||
type Stream = TcpStream;
|
||||
type Error = Error;
|
||||
@@ -98,13 +101,14 @@ mod no_tls {
|
||||
|
||||
#[cfg(feature = "__tls")]
|
||||
mod tls {
|
||||
use core::future::Future;
|
||||
use core::pin::Pin;
|
||||
use std::io::{Error, ErrorKind, Write};
|
||||
|
||||
use super::{tcp_stream, Connector, HttpConnector};
|
||||
use crate::async_impl::ssl_stream::{AsyncSslStream, MixStream};
|
||||
use crate::error::CauseMessage;
|
||||
use crate::{AsyncReadExt, AsyncWriteExt, Scheme, TcpStream, Uri};
|
||||
use core::future::Future;
|
||||
use core::pin::Pin;
|
||||
use std::io::{Error, ErrorKind, Write};
|
||||
|
||||
impl Connector for HttpConnector {
|
||||
type Stream = MixStream<TcpStream>;
|
||||
|
||||
@@ -33,7 +33,8 @@ pub struct DownloaderBuilder<S> {
|
||||
state: S,
|
||||
}
|
||||
|
||||
/// A state indicates that `DownloaderBuilder` wants a body that needs to be downloaded.
|
||||
/// A state indicates that `DownloaderBuilder` wants a body that needs to be
|
||||
/// downloaded.
|
||||
pub struct WantsBody;
|
||||
|
||||
impl DownloaderBuilder<WantsBody> {
|
||||
@@ -171,7 +172,10 @@ impl<T: DownloadOperator> DownloaderBuilder<WantsConfig<T>> {
|
||||
/// # use ylong_http_client::Timeout;
|
||||
///
|
||||
/// # async fn set_timeout(body: Response) {
|
||||
/// let builder = DownloaderBuilder::new().body(body).console().timeout(Timeout::none());
|
||||
/// let builder = DownloaderBuilder::new()
|
||||
/// .body(body)
|
||||
/// .console()
|
||||
/// .timeout(Timeout::none());
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn timeout(mut self, timeout: Timeout) -> Self {
|
||||
@@ -190,7 +194,10 @@ impl<T: DownloadOperator> DownloaderBuilder<WantsConfig<T>> {
|
||||
/// # use ylong_http_client::SpeedLimit;
|
||||
///
|
||||
/// # async fn set_timeout(body: Response) {
|
||||
/// let builder = DownloaderBuilder::new().body(body).console().speed_limit(SpeedLimit::none());
|
||||
/// let builder = DownloaderBuilder::new()
|
||||
/// .body(body)
|
||||
/// .console()
|
||||
/// .speed_limit(SpeedLimit::none());
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn speed_limit(mut self, speed_limit: SpeedLimit) -> Self {
|
||||
|
||||
@@ -14,17 +14,17 @@
|
||||
mod builder;
|
||||
mod operator;
|
||||
|
||||
use builder::WantsBody;
|
||||
use operator::Console;
|
||||
use std::time::Instant;
|
||||
|
||||
pub use builder::DownloaderBuilder;
|
||||
use builder::WantsBody;
|
||||
use operator::Console;
|
||||
pub use operator::{DownloadFuture, DownloadOperator, ProgressFuture};
|
||||
use ylong_http::body::async_impl::Body;
|
||||
|
||||
// TODO: Adapter, use Response<HttpBody> later.
|
||||
use crate::async_impl::Response;
|
||||
use crate::{ErrorKind, HttpClientError, SpeedLimit, Timeout};
|
||||
use std::time::Instant;
|
||||
use ylong_http::body::async_impl::Body;
|
||||
|
||||
/// A downloader that can help you download the response body.
|
||||
///
|
||||
@@ -88,7 +88,7 @@ use ylong_http::body::async_impl::Body;
|
||||
/// self: Pin<&mut Self>,
|
||||
/// cx: &mut Context<'_>,
|
||||
/// downloaded: u64,
|
||||
/// total: Option<u64>
|
||||
/// total: Option<u64>,
|
||||
/// ) -> Poll<Result<(), HttpClientError>> {
|
||||
/// // Writes your customize method.
|
||||
/// todo!()
|
||||
|
||||
@@ -11,14 +11,17 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::HttpClientError;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
/// The trait defines the functionality required for processing bodies of HTTP messages.
|
||||
use crate::HttpClientError;
|
||||
|
||||
/// The trait defines the functionality required for processing bodies of HTTP
|
||||
/// messages.
|
||||
pub trait DownloadOperator {
|
||||
/// Attempts to write the body data read each time to the specified location.
|
||||
/// Attempts to write the body data read each time to the specified
|
||||
/// location.
|
||||
///
|
||||
/// This method will be called every time a part of the body data is read.
|
||||
fn poll_download(
|
||||
|
||||
@@ -11,23 +11,22 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::{Body, StreamData};
|
||||
|
||||
use crate::error::{ErrorKind, HttpClientError};
|
||||
use crate::{AsyncRead, ReadBuf, Sleep};
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll};
|
||||
use std::future::Future;
|
||||
use std::io::{Cursor, Read};
|
||||
use ylong_http::body::TextBodyDecoder;
|
||||
use ylong_http::headers::Headers;
|
||||
|
||||
use ylong_http::body::TextBodyDecoder;
|
||||
#[cfg(feature = "http1_1")]
|
||||
use ylong_http::body::{ChunkBodyDecoder, ChunkState};
|
||||
use ylong_http::headers::Headers;
|
||||
#[cfg(feature = "http1_1")]
|
||||
use ylong_http::headers::{HeaderName, HeaderValue};
|
||||
|
||||
use super::{Body, StreamData};
|
||||
use crate::error::{ErrorKind, HttpClientError};
|
||||
use crate::util::normalizer::BodyLength;
|
||||
#[cfg(feature = "http1_1")]
|
||||
use ylong_http::body::{ChunkBodyDecoder, ChunkState};
|
||||
use crate::{AsyncRead, ReadBuf, Sleep};
|
||||
|
||||
/// `HttpBody` is the body part of the `Response` returned by `Client::request`.
|
||||
/// `HttpBody` implements `Body` trait, so users can call related methods to get
|
||||
@@ -36,8 +35,8 @@ use ylong_http::body::{ChunkBodyDecoder, ChunkState};
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use ylong_http_client::async_impl::{Client, HttpBody, Body};
|
||||
/// use ylong_http_client::{Request, EmptyBody};
|
||||
/// use ylong_http_client::async_impl::{Body, Client, HttpBody};
|
||||
/// use ylong_http_client::{EmptyBody, Request};
|
||||
///
|
||||
/// async fn read_body() {
|
||||
/// let client = Client::new();
|
||||
@@ -563,9 +562,10 @@ impl Chunk {
|
||||
|
||||
#[cfg(test)]
|
||||
mod ut_async_http_body {
|
||||
use crate::async_impl::http_body::Chunk;
|
||||
use ylong_http::body::ChunkBodyDecoder;
|
||||
|
||||
use crate::async_impl::http_body::Chunk;
|
||||
|
||||
/// UT test cases for `Chunk::get_trailers`.
|
||||
///
|
||||
/// # Brief
|
||||
|
||||
@@ -33,16 +33,15 @@ mod timeout;
|
||||
mod uploader;
|
||||
|
||||
pub use client::ClientBuilder;
|
||||
|
||||
pub(crate) use conn::StreamData;
|
||||
pub use connector::Connector;
|
||||
pub(crate) use connector::HttpConnector;
|
||||
pub use downloader::{DownloadOperator, Downloader, DownloaderBuilder};
|
||||
pub use http_body::HttpBody;
|
||||
pub use uploader::{UploadOperator, Uploader, UploaderBuilder};
|
||||
pub use ylong_http::body::{async_impl::Body, MultiPart, Part};
|
||||
|
||||
pub(crate) use conn::StreamData;
|
||||
pub(crate) use connector::HttpConnector;
|
||||
pub(crate) use pool::ConnPool;
|
||||
pub use uploader::{UploadOperator, Uploader, UploaderBuilder};
|
||||
pub use ylong_http::body::async_impl::Body;
|
||||
pub use ylong_http::body::{MultiPart, Part};
|
||||
|
||||
#[cfg(feature = "__tls")]
|
||||
mod ssl_stream;
|
||||
|
||||
@@ -11,17 +11,17 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::async_impl::Connector;
|
||||
use crate::error::HttpClientError;
|
||||
use crate::util::dispatcher::{Conn, ConnDispatcher, Dispatcher};
|
||||
use crate::util::pool::{Pool, PoolKey};
|
||||
use crate::{AsyncRead, AsyncWrite};
|
||||
use crate::{ErrorKind, HttpConfig, HttpVersion, Uri};
|
||||
use std::error::Error;
|
||||
use std::future::Future;
|
||||
use std::mem::take;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use crate::async_impl::Connector;
|
||||
use crate::error::HttpClientError;
|
||||
use crate::util::dispatcher::{Conn, ConnDispatcher, Dispatcher};
|
||||
use crate::util::pool::{Pool, PoolKey};
|
||||
use crate::{AsyncRead, AsyncWrite, ErrorKind, HttpConfig, HttpVersion, Uri};
|
||||
|
||||
pub(crate) struct ConnPool<C, S> {
|
||||
pool: Pool<PoolKey, Conns<S>>,
|
||||
connector: Arc<C>,
|
||||
@@ -93,7 +93,8 @@ impl<S: AsyncRead + AsyncWrite + Unpin + Send + Sync> Conns<S> {
|
||||
#[cfg(feature = "http2")]
|
||||
HttpVersion::Http2PriorKnowledge => {
|
||||
{
|
||||
// The lock `h2_occupation` is used to prevent multiple coroutines from sending Requests at the same time under concurrent conditions,
|
||||
// The lock `h2_occupation` is used to prevent multiple coroutines from sending
|
||||
// Requests at the same time under concurrent conditions,
|
||||
// resulting in the creation of multiple tcp connections
|
||||
let _lock = self.h2_occupation.lock().await;
|
||||
if let Some(conn) = self.get_exist_conn() {
|
||||
|
||||
@@ -11,20 +11,16 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::async_impl::ssl_stream::{check_io_to_poll, Wrapper};
|
||||
use crate::util::c_openssl::{
|
||||
error::ErrorStack,
|
||||
ssl::{self, ShutdownResult, Ssl, SslErrorCode},
|
||||
};
|
||||
use crate::{AsyncRead, AsyncWrite, ReadBuf};
|
||||
use core::{
|
||||
future,
|
||||
pin::Pin,
|
||||
ptr, slice,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll};
|
||||
use core::{future, ptr, slice};
|
||||
use std::io::{self, Read, Write};
|
||||
|
||||
use crate::async_impl::ssl_stream::{check_io_to_poll, Wrapper};
|
||||
use crate::util::c_openssl::error::ErrorStack;
|
||||
use crate::util::c_openssl::ssl::{self, ShutdownResult, Ssl, SslErrorCode};
|
||||
use crate::{AsyncRead, AsyncWrite, ReadBuf};
|
||||
|
||||
/// An asynchronous version of [`openssl::ssl::SslStream`].
|
||||
#[derive(Debug)]
|
||||
pub struct AsyncSslStream<S>(ssl::SslStream<Wrapper<S>>);
|
||||
@@ -129,12 +125,14 @@ where
|
||||
fn poll_shutdown(mut self: Pin<&mut Self>, ctx: &mut Context) -> Poll<io::Result<()>> {
|
||||
// Shuts down the session.
|
||||
match self.as_mut().with_context(ctx, |s| s.shutdown()) {
|
||||
// Sends a close notify message to the peer, after which `ShutdownResult::Sent` is returned.
|
||||
// Awaits the receipt of a close notify message from the peer, after which `ShutdownResult::Received` is returned.
|
||||
// Sends a close notify message to the peer, after which `ShutdownResult::Sent` is
|
||||
// returned. Awaits the receipt of a close notify message from the peer,
|
||||
// after which `ShutdownResult::Received` is returned.
|
||||
Ok(ShutdownResult::Sent) | Ok(ShutdownResult::Received) => {}
|
||||
// The SSL session has been closed.
|
||||
Err(ref e) if e.code() == SslErrorCode::ZERO_RETURN => {}
|
||||
// When the underlying BIO could not satisfy the needs of SSL_shutdown() to continue the handshake
|
||||
// When the underlying BIO could not satisfy the needs of SSL_shutdown() to continue the
|
||||
// handshake
|
||||
Err(ref e)
|
||||
if e.code() == SslErrorCode::WANT_READ || e.code() == SslErrorCode::WANT_WRITE =>
|
||||
{
|
||||
|
||||
@@ -11,12 +11,11 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll};
|
||||
|
||||
use crate::async_impl::ssl_stream::AsyncSslStream;
|
||||
use crate::{AsyncRead, AsyncWrite, ReadBuf};
|
||||
use core::{
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
/// A stream which may be wrapped with TLS.
|
||||
pub enum MixStream<T> {
|
||||
|
||||
@@ -19,5 +19,4 @@ mod wrapper;
|
||||
#[cfg(feature = "__c_openssl")]
|
||||
pub use c_ssl_stream::AsyncSslStream;
|
||||
pub use mix::MixStream;
|
||||
|
||||
pub(crate) use wrapper::{check_io_to_poll, Wrapper};
|
||||
|
||||
@@ -11,26 +11,26 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::{AsyncRead, AsyncWrite, ReadBuf};
|
||||
use core::{
|
||||
fmt::Debug,
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
use core::fmt::Debug;
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll};
|
||||
use std::io::{self, Read, Write};
|
||||
|
||||
use crate::{AsyncRead, AsyncWrite, ReadBuf};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Wrapper<S> {
|
||||
pub(crate) stream: S,
|
||||
pub(crate) context: *mut (), // Context of stream.
|
||||
// Context of stream.
|
||||
pub(crate) context: *mut (),
|
||||
}
|
||||
|
||||
impl<S> Wrapper<S> {
|
||||
/// Gets inner `Stream` and `Context` of `Stream`.
|
||||
///
|
||||
/// # SAFETY
|
||||
/// Must be called with `context` set to a valid pointer to a live `Context` object,
|
||||
/// and the wrapper must be pinned in memory.
|
||||
/// Must be called with `context` set to a valid pointer to a live `Context`
|
||||
/// object, and the wrapper must be pinned in memory.
|
||||
unsafe fn inner(&mut self) -> (Pin<&mut S>, &mut Context<'_>) {
|
||||
debug_assert!(!self.context.is_null());
|
||||
let stream = Pin::new_unchecked(&mut self.stream);
|
||||
|
||||
@@ -11,14 +11,15 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::async_impl::HttpBody;
|
||||
use crate::Sleep;
|
||||
use crate::{ErrorKind, HttpClientError};
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
use ylong_http::response::Response;
|
||||
|
||||
use crate::async_impl::HttpBody;
|
||||
use crate::{ErrorKind, HttpClientError, Sleep};
|
||||
|
||||
pub(crate) struct TimeoutFuture<T> {
|
||||
pub(crate) timeout: Option<Pin<Box<Sleep>>>,
|
||||
pub(crate) future: T,
|
||||
|
||||
@@ -24,7 +24,10 @@ use crate::AsyncRead;
|
||||
/// ```
|
||||
/// # use ylong_http_client::async_impl::{UploaderBuilder, Uploader};
|
||||
///
|
||||
/// let uploader = UploaderBuilder::new().reader("HelloWorld".as_bytes()).console().build();
|
||||
/// let uploader = UploaderBuilder::new()
|
||||
/// .reader("HelloWorld".as_bytes())
|
||||
/// .console()
|
||||
/// .build();
|
||||
/// ```
|
||||
pub struct UploaderBuilder<S> {
|
||||
state: S,
|
||||
@@ -118,13 +121,15 @@ impl<R: AsyncRead> UploaderBuilder<WantsOperator<R>> {
|
||||
/// self: Pin<&mut Self>,
|
||||
/// cx: &mut Context<'_>,
|
||||
/// uploaded: u64,
|
||||
/// total: Option<u64>
|
||||
/// total: Option<u64>,
|
||||
/// ) -> Poll<Result<(), HttpClientError>> {
|
||||
/// todo!()
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// let builder = UploaderBuilder::new().reader("HelloWorld".as_bytes()).operator(MyOperator);
|
||||
/// let builder = UploaderBuilder::new()
|
||||
/// .reader("HelloWorld".as_bytes())
|
||||
/// .operator(MyOperator);
|
||||
/// ```
|
||||
pub fn operator<T: UploadOperator>(self, operator: T) -> UploaderBuilder<WantsConfig<R, T>> {
|
||||
UploaderBuilder {
|
||||
@@ -147,7 +152,9 @@ impl<R: AsyncRead> UploaderBuilder<WantsOperator<R>> {
|
||||
/// # use ylong_http_client::async_impl::{UploaderBuilder, Uploader};
|
||||
/// # use ylong_http_client::Response;
|
||||
///
|
||||
/// let builder = UploaderBuilder::new().reader("HelloWorld".as_bytes()).console();
|
||||
/// let builder = UploaderBuilder::new()
|
||||
/// .reader("HelloWorld".as_bytes())
|
||||
/// .console();
|
||||
/// ```
|
||||
pub fn console(self) -> UploaderBuilder<WantsConfig<R, Console>> {
|
||||
UploaderBuilder {
|
||||
@@ -170,7 +177,8 @@ pub struct WantsConfig<R, T> {
|
||||
impl<R, T> UploaderBuilder<WantsConfig<R, T>> {
|
||||
/// Sets the total bytes of the uploaded content.
|
||||
///
|
||||
/// Default is `None` which means that you don't know the size of the content.
|
||||
/// Default is `None` which means that you don't know the size of the
|
||||
/// content.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
||||
@@ -14,15 +14,15 @@
|
||||
mod builder;
|
||||
mod operator;
|
||||
|
||||
pub use builder::{UploaderBuilder, WantsReader};
|
||||
pub use operator::{Console, UploadOperator};
|
||||
|
||||
use crate::{AsyncRead, ReadBuf};
|
||||
use crate::{ErrorKind, HttpClientError};
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
pub use builder::{UploaderBuilder, WantsReader};
|
||||
pub use operator::{Console, UploadOperator};
|
||||
use ylong_http::body::async_impl::Body;
|
||||
|
||||
use crate::{AsyncRead, ErrorKind, HttpClientError, ReadBuf};
|
||||
|
||||
/// An uploader that can help you upload the request body.
|
||||
///
|
||||
/// An `Uploader` provides a template method for uploading a file or a slice and
|
||||
@@ -70,7 +70,7 @@ use ylong_http::body::async_impl::Body;
|
||||
/// self: Pin<&mut Self>,
|
||||
/// cx: &mut Context<'_>,
|
||||
/// uploaded: u64,
|
||||
/// total: Option<u64>
|
||||
/// total: Option<u64>,
|
||||
/// ) -> Poll<Result<(), HttpClientError>> {
|
||||
/// todo!()
|
||||
/// }
|
||||
@@ -78,7 +78,10 @@ use ylong_http::body::async_impl::Body;
|
||||
///
|
||||
/// // Creates a default `Uploader` based on `MyUploadOperator`.
|
||||
/// // Configures your uploader by using `UploaderBuilder`.
|
||||
/// let uploader = Uploader::builder().reader("HelloWorld".as_bytes()).operator(MyUploadOperator).build();
|
||||
/// let uploader = Uploader::builder()
|
||||
/// .reader("HelloWorld".as_bytes())
|
||||
/// .operator(MyUploadOperator)
|
||||
/// .build();
|
||||
/// # }
|
||||
/// ```
|
||||
pub struct Uploader<R, T> {
|
||||
@@ -89,7 +92,8 @@ pub struct Uploader<R, T> {
|
||||
}
|
||||
|
||||
impl<R: AsyncRead + Unpin> Uploader<R, Console> {
|
||||
/// Creates an `Uploader` with a `Console` operator which displays process on console.
|
||||
/// Creates an `Uploader` with a `Console` operator which displays process
|
||||
/// on console.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
||||
@@ -11,11 +11,12 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::HttpClientError;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
use crate::HttpClientError;
|
||||
|
||||
/// A `UploadOperator` represents structures that can read local data to socket.
|
||||
///
|
||||
/// You can implement `UploadOperator` for your structures and pass it to a
|
||||
@@ -39,7 +40,7 @@ use std::task::{Context, Poll};
|
||||
/// self: Pin<&mut Self>,
|
||||
/// cx: &mut Context<'_>,
|
||||
/// uploaded: u64,
|
||||
/// total: Option<u64>
|
||||
/// total: Option<u64>,
|
||||
/// ) -> Poll<Result<(), HttpClientError>> {
|
||||
/// todo!()
|
||||
/// }
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
use core::fmt::{Debug, Display, Formatter};
|
||||
use std::error::Error;
|
||||
|
||||
/// The structure encapsulates errors that can be encountered when working with the HTTP client.
|
||||
/// The structure encapsulates errors that can be encountered when working with
|
||||
/// the HTTP client.
|
||||
pub struct HttpClientError {
|
||||
kind: ErrorKind,
|
||||
cause: Option<Box<dyn Error + Send + Sync>>,
|
||||
|
||||
@@ -12,18 +12,20 @@
|
||||
// limitations under the License.
|
||||
|
||||
//! `ylong_http_client` provides a HTTP client that based on `ylong_http` crate.
|
||||
//! You can use the client to send request to a server, and then get the response.
|
||||
//! You can use the client to send request to a server, and then get the
|
||||
//! response.
|
||||
//!
|
||||
//! # Supported HTTP Version
|
||||
//! - HTTP1.1
|
||||
//!
|
||||
// TODO: Need doc.
|
||||
|
||||
// ylong_http crate re-export.
|
||||
pub use ylong_http::body::{EmptyBody, TextBody};
|
||||
pub use ylong_http::request::method::Method;
|
||||
pub use ylong_http::request::uri::{Scheme, Uri};
|
||||
pub use ylong_http::request::{method::Method, Request, RequestPart};
|
||||
pub use ylong_http::response::{status::StatusCode, ResponsePart};
|
||||
pub use ylong_http::request::{Request, RequestPart};
|
||||
pub use ylong_http::response::status::StatusCode;
|
||||
pub use ylong_http::response::ResponsePart;
|
||||
pub use ylong_http::version::Version;
|
||||
|
||||
#[cfg(all(feature = "async", any(feature = "http1_1", feature = "http2")))]
|
||||
@@ -53,34 +55,30 @@ pub use error::{ErrorKind, HttpClientError};
|
||||
))]
|
||||
pub mod util;
|
||||
|
||||
#[cfg(all(
|
||||
any(feature = "async", feature = "sync"),
|
||||
any(feature = "http1_1", feature = "http2"),
|
||||
))]
|
||||
pub use util::*;
|
||||
|
||||
#[cfg(all(feature = "tokio_base", feature = "http2"))]
|
||||
pub(crate) use tokio::sync::{
|
||||
mpsc::{error::TryRecvError, unbounded_channel, UnboundedReceiver, UnboundedSender},
|
||||
Mutex as AsyncMutex, MutexGuard,
|
||||
};
|
||||
#[cfg(all(feature = "tokio_base", feature = "async"))]
|
||||
pub(crate) use tokio::{
|
||||
io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, ReadBuf},
|
||||
net::TcpStream,
|
||||
time::{sleep, timeout, Sleep},
|
||||
};
|
||||
|
||||
#[cfg(all(feature = "tokio_base", feature = "http2"))]
|
||||
pub(crate) use tokio::sync::{
|
||||
mpsc::{error::TryRecvError, unbounded_channel, UnboundedReceiver, UnboundedSender},
|
||||
Mutex as AsyncMutex, MutexGuard,
|
||||
#[cfg(all(
|
||||
any(feature = "async", feature = "sync"),
|
||||
any(feature = "http1_1", feature = "http2"),
|
||||
))]
|
||||
pub use util::*;
|
||||
#[cfg(all(feature = "ylong_base", feature = "http2"))]
|
||||
pub(crate) use ylong_runtime::sync::{
|
||||
mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender},
|
||||
Mutex as AsyncMutex, MutexGuard, RecvError as TryRecvError,
|
||||
};
|
||||
|
||||
#[cfg(feature = "ylong_base")]
|
||||
pub(crate) use ylong_runtime::{
|
||||
io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, ReadBuf},
|
||||
net::TcpStream,
|
||||
time::{sleep, timeout, Sleep},
|
||||
};
|
||||
|
||||
#[cfg(all(feature = "ylong_base", feature = "http2"))]
|
||||
pub(crate) use ylong_runtime::sync::{
|
||||
mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender},
|
||||
Mutex as AsyncMutex, MutexGuard, RecvError as TryRecvError,
|
||||
};
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use ylong_http::request::uri::Uri;
|
||||
// TODO: Adapter, remove this later.
|
||||
use ylong_http::response::Response;
|
||||
|
||||
use super::{Body, Connector, HttpBody, HttpConnector};
|
||||
use crate::error::HttpClientError;
|
||||
use crate::sync_impl::conn;
|
||||
@@ -18,13 +22,10 @@ use crate::sync_impl::pool::ConnPool;
|
||||
use crate::util::normalizer::RequestFormatter;
|
||||
use crate::util::proxy::Proxies;
|
||||
use crate::util::redirect::TriggerKind;
|
||||
use crate::util::{ClientConfig, HttpConfig, HttpVersion, Proxy, Timeout};
|
||||
use crate::util::{ConnectorConfig, Redirect};
|
||||
use crate::util::{
|
||||
ClientConfig, ConnectorConfig, HttpConfig, HttpVersion, Proxy, Redirect, Timeout,
|
||||
};
|
||||
use crate::Request;
|
||||
use ylong_http::request::uri::Uri;
|
||||
|
||||
// TODO: Adapter, remove this later.
|
||||
use ylong_http::response::Response;
|
||||
|
||||
/// HTTP synchronous client implementation. Users can use `Client` to
|
||||
/// send `Request` synchronously. `Client` depends on a `Connector` that
|
||||
@@ -34,7 +35,7 @@ use ylong_http::response::Response;
|
||||
///
|
||||
/// ```no_run
|
||||
/// use ylong_http_client::sync_impl::Client;
|
||||
/// use ylong_http_client::{Request, EmptyBody};
|
||||
/// use ylong_http_client::{EmptyBody, Request};
|
||||
///
|
||||
/// // Creates a new `Client`.
|
||||
/// let client = Client::new();
|
||||
@@ -54,7 +55,8 @@ pub struct Client<C: Connector> {
|
||||
}
|
||||
|
||||
impl Client<HttpConnector> {
|
||||
/// Creates a new, default `Client`, which uses [`sync_impl::HttpConnector`].
|
||||
/// Creates a new, default `Client`, which uses
|
||||
/// [`sync_impl::HttpConnector`].
|
||||
///
|
||||
/// [`sync_impl::HttpConnector`]: HttpConnector
|
||||
///
|
||||
@@ -101,7 +103,7 @@ impl<C: Connector> Client<C> {
|
||||
///
|
||||
/// ```no_run
|
||||
/// use ylong_http_client::sync_impl::Client;
|
||||
/// use ylong_http_client::{Request, EmptyBody};
|
||||
/// use ylong_http_client::{EmptyBody, Request};
|
||||
///
|
||||
/// let client = Client::new();
|
||||
/// let response = client.request(Request::new(EmptyBody));
|
||||
@@ -420,9 +422,7 @@ impl ClientBuilder {
|
||||
/// use ylong_http_client::sync_impl::ClientBuilder;
|
||||
///
|
||||
/// let builder = ClientBuilder::new()
|
||||
/// .tls_cipher_list(
|
||||
/// "DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK"
|
||||
/// );
|
||||
/// .tls_cipher_list("DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK");
|
||||
/// ```
|
||||
pub fn tls_cipher_list(mut self, list: &str) -> Self {
|
||||
self.tls = self.tls.cipher_list(list);
|
||||
@@ -441,26 +441,24 @@ impl ClientBuilder {
|
||||
/// ```
|
||||
/// use ylong_http_client::sync_impl::ClientBuilder;
|
||||
///
|
||||
/// let builder = ClientBuilder::new()
|
||||
/// .tls_cipher_suites(
|
||||
/// "DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK"
|
||||
/// );
|
||||
/// let builder = ClientBuilder::new().tls_cipher_suites(
|
||||
/// "DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK",
|
||||
/// );
|
||||
/// ```
|
||||
pub fn tls_cipher_suites(mut self, list: &str) -> Self {
|
||||
self.tls = self.tls.cipher_suites(list);
|
||||
self
|
||||
}
|
||||
|
||||
/// Controls the use of built-in system certificates during certificate validation.
|
||||
/// Default to `true` -- uses built-in system certs.
|
||||
/// Controls the use of built-in system certificates during certificate
|
||||
/// validation. Default to `true` -- uses built-in system certs.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ylong_http_client::sync_impl::ClientBuilder;
|
||||
///
|
||||
/// let builder = ClientBuilder::new()
|
||||
/// .tls_built_in_root_certs(false);
|
||||
/// let builder = ClientBuilder::new().tls_built_in_root_certs(false);
|
||||
/// ```
|
||||
pub fn tls_built_in_root_certs(mut self, is_use: bool) -> Self {
|
||||
self.tls = self.tls.build_in_root_certs(is_use);
|
||||
@@ -480,8 +478,7 @@ impl ClientBuilder {
|
||||
/// ```
|
||||
/// use ylong_http_client::sync_impl::ClientBuilder;
|
||||
///
|
||||
/// let builder = ClientBuilder::new()
|
||||
/// .danger_accept_invalid_certs(true);
|
||||
/// let builder = ClientBuilder::new().danger_accept_invalid_certs(true);
|
||||
/// ```
|
||||
pub fn danger_accept_invalid_certs(mut self, is_invalid: bool) -> Self {
|
||||
self.tls = self.tls.danger_accept_invalid_certs(is_invalid);
|
||||
@@ -502,8 +499,7 @@ impl ClientBuilder {
|
||||
/// ```
|
||||
/// use ylong_http_client::sync_impl::ClientBuilder;
|
||||
///
|
||||
/// let builder = ClientBuilder::new()
|
||||
/// .danger_accept_invalid_hostnames(true);
|
||||
/// let builder = ClientBuilder::new().danger_accept_invalid_hostnames(true);
|
||||
/// ```
|
||||
pub fn danger_accept_invalid_hostnames(mut self, is_invalid: bool) -> Self {
|
||||
self.tls = self.tls.danger_accept_invalid_hostnames(is_invalid);
|
||||
@@ -519,8 +515,7 @@ impl ClientBuilder {
|
||||
/// ```
|
||||
/// use ylong_http_client::sync_impl::ClientBuilder;
|
||||
///
|
||||
/// let builder = ClientBuilder::new()
|
||||
/// .tls_sni(true);
|
||||
/// let builder = ClientBuilder::new().tls_sni(true);
|
||||
/// ```
|
||||
pub fn tls_sni(mut self, is_set_sni: bool) -> Self {
|
||||
self.tls = self.tls.sni(is_set_sni);
|
||||
@@ -536,11 +531,12 @@ impl Default for ClientBuilder {
|
||||
|
||||
#[cfg(test)]
|
||||
mod ut_syn_client {
|
||||
use crate::sync_impl::Client;
|
||||
use ylong_http::body::TextBody;
|
||||
use ylong_http::request::uri::Uri;
|
||||
use ylong_http::request::Request;
|
||||
|
||||
use crate::sync_impl::Client;
|
||||
|
||||
/// UT test cases for `Client::request`.
|
||||
///
|
||||
/// # Brief
|
||||
@@ -575,7 +571,8 @@ mod ut_syn_client {
|
||||
///
|
||||
/// # Brief
|
||||
/// 1. Creates a `Client` by calling `Client::builder`.
|
||||
/// 2. Calls `http_config`, `client_config`, `tls_config` and `build` respectively.
|
||||
/// 2. Calls `http_config`, `client_config`, `tls_config` and `build`
|
||||
/// respectively.
|
||||
/// 3. Checks if the result is correct.
|
||||
#[cfg(feature = "__tls")]
|
||||
#[test]
|
||||
|
||||
@@ -11,17 +11,18 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::io::{Read, Write};
|
||||
|
||||
use ylong_http::body::sync_impl::Body;
|
||||
use ylong_http::h1::{RequestEncoder, ResponseDecoder};
|
||||
// TODO: Adapter, remove this later.
|
||||
use ylong_http::response::Response;
|
||||
|
||||
use crate::error::{ErrorKind, HttpClientError};
|
||||
use crate::sync_impl::conn::StreamData;
|
||||
use crate::sync_impl::HttpBody;
|
||||
use crate::util::dispatcher::http1::Http1Conn;
|
||||
use crate::Request;
|
||||
use std::io::{Read, Write};
|
||||
use ylong_http::body::sync_impl::Body;
|
||||
use ylong_http::h1::{RequestEncoder, ResponseDecoder};
|
||||
|
||||
// TODO: Adapter, remove this later.
|
||||
use ylong_http::response::Response;
|
||||
|
||||
const TEMP_BUF_SIZE: usize = 16 * 1024;
|
||||
|
||||
|
||||
@@ -11,14 +11,16 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::error::HttpClientError;
|
||||
use crate::sync_impl::HttpBody;
|
||||
use crate::util::dispatcher::Conn;
|
||||
use std::io::{Read, Write};
|
||||
|
||||
use ylong_http::body::sync_impl::Body;
|
||||
use ylong_http::request::Request;
|
||||
use ylong_http::response::Response;
|
||||
|
||||
use crate::error::HttpClientError;
|
||||
use crate::sync_impl::HttpBody;
|
||||
use crate::util::dispatcher::Conn;
|
||||
|
||||
#[cfg(feature = "http1_1")]
|
||||
mod http1;
|
||||
|
||||
|
||||
@@ -11,10 +11,12 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::util::ConnectorConfig;
|
||||
use std::io::{Read, Write};
|
||||
|
||||
use ylong_http::request::uri::Uri;
|
||||
|
||||
use crate::util::ConnectorConfig;
|
||||
|
||||
/// `Connector` trait used by `Client`. `Connector` provides synchronous
|
||||
/// connection establishment interfaces.
|
||||
pub trait Connector {
|
||||
@@ -49,11 +51,13 @@ impl Default for HttpConnector {
|
||||
|
||||
#[cfg(not(feature = "__tls"))]
|
||||
pub mod no_tls {
|
||||
use crate::sync_impl::Connector;
|
||||
use std::io::Error;
|
||||
use std::net::TcpStream;
|
||||
|
||||
use ylong_http::request::uri::Uri;
|
||||
|
||||
use crate::sync_impl::Connector;
|
||||
|
||||
impl Connector for super::HttpConnector {
|
||||
type Stream = TcpStream;
|
||||
type Error = Error;
|
||||
@@ -71,16 +75,14 @@ pub mod no_tls {
|
||||
|
||||
#[cfg(feature = "__tls")]
|
||||
pub mod tls_conn {
|
||||
use crate::{
|
||||
sync_impl::{Connector, MixStream},
|
||||
ErrorKind, HttpClientError,
|
||||
};
|
||||
use std::{
|
||||
io::{Read, Write},
|
||||
net::TcpStream,
|
||||
};
|
||||
use std::io::{Read, Write};
|
||||
use std::net::TcpStream;
|
||||
|
||||
use ylong_http::request::uri::{Scheme, Uri};
|
||||
|
||||
use crate::sync_impl::{Connector, MixStream};
|
||||
use crate::{ErrorKind, HttpClientError};
|
||||
|
||||
impl Connector for super::HttpConnector {
|
||||
type Stream = MixStream<TcpStream>;
|
||||
type Error = HttpClientError;
|
||||
|
||||
@@ -11,12 +11,14 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::io::{Cursor, Read};
|
||||
|
||||
use ylong_http::body::{ChunkBodyDecoder, ChunkState, TextBodyDecoder};
|
||||
use ylong_http::headers::{HeaderName, HeaderValue, Headers};
|
||||
|
||||
use super::Body;
|
||||
use crate::error::{ErrorKind, HttpClientError};
|
||||
use crate::sync_impl::conn::StreamData;
|
||||
use std::io::{Cursor, Read};
|
||||
use ylong_http::body::{ChunkBodyDecoder, ChunkState, TextBodyDecoder};
|
||||
use ylong_http::headers::{HeaderName, HeaderValue, Headers};
|
||||
|
||||
/// `HttpBody` is the body part of the `Response` returned by `Client::request`.
|
||||
/// `HttpBody` implements `Body` trait, so users can call related methods to get
|
||||
@@ -25,8 +27,8 @@ use ylong_http::headers::{HeaderName, HeaderValue, Headers};
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use ylong_http_client::sync_impl::{Client, HttpBody, Body};
|
||||
/// use ylong_http_client::{Request, EmptyBody};
|
||||
/// use ylong_http_client::sync_impl::{Body, Client, HttpBody};
|
||||
/// use ylong_http_client::{EmptyBody, Request};
|
||||
///
|
||||
/// let mut client = Client::new();
|
||||
///
|
||||
@@ -389,9 +391,10 @@ impl Chunk {
|
||||
|
||||
#[cfg(test)]
|
||||
mod ut_syn_http_body {
|
||||
use ylong_http::body::ChunkBodyDecoder;
|
||||
|
||||
use crate::sync_impl::http_body::Chunk;
|
||||
use crate::sync_impl::{Body, HttpBody};
|
||||
use ylong_http::body::ChunkBodyDecoder;
|
||||
|
||||
/// UT test cases for `HttpBody::empty`.
|
||||
///
|
||||
|
||||
@@ -34,15 +34,13 @@ mod reader;
|
||||
|
||||
pub use client::{Client, ClientBuilder};
|
||||
pub use connector::Connector;
|
||||
pub(crate) use connector::HttpConnector;
|
||||
pub use http_body::HttpBody;
|
||||
pub use reader::{BodyProcessError, BodyProcessor, BodyReader, DefaultBodyProcessor};
|
||||
pub use ylong_http::body::sync_impl::Body;
|
||||
|
||||
// TODO: Adapter, remove this later.
|
||||
pub use ylong_http::response::Response;
|
||||
|
||||
pub(crate) use connector::HttpConnector;
|
||||
|
||||
#[cfg(feature = "__tls")]
|
||||
mod ssl_stream;
|
||||
#[cfg(feature = "__tls")]
|
||||
|
||||
@@ -11,15 +11,16 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::error::Error;
|
||||
use std::io::{Read, Write};
|
||||
use std::mem::take;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use crate::error::{ErrorKind, HttpClientError};
|
||||
use crate::sync_impl::Connector;
|
||||
use crate::util::dispatcher::{Conn, ConnDispatcher, Dispatcher};
|
||||
use crate::util::pool::{Pool, PoolKey};
|
||||
use crate::Uri;
|
||||
use std::error::Error;
|
||||
use std::io::{Read, Write};
|
||||
use std::mem::take;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
pub(crate) struct ConnPool<C, S> {
|
||||
pool: Pool<PoolKey, Conns<S>>,
|
||||
|
||||
@@ -11,13 +11,14 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::error::Error;
|
||||
use std::fmt::{Debug, Display, Formatter};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use super::Body;
|
||||
use crate::error::HttpClientError;
|
||||
use crate::util::Timeout;
|
||||
use crate::ErrorKind;
|
||||
use std::error::Error;
|
||||
use std::fmt::{Debug, Display, Formatter};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
/// A reader used to read all the body data to a specified location and provide
|
||||
/// echo function.
|
||||
@@ -25,7 +26,7 @@ use std::time::{Duration, Instant};
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ylong_http_client::sync_impl::{BodyReader, BodyProcessor, BodyProcessError};
|
||||
/// use ylong_http_client::sync_impl::{BodyProcessError, BodyProcessor, BodyReader};
|
||||
/// use ylong_http_client::TextBody;
|
||||
///
|
||||
/// // Defines a processor, which provides read and echo ability.
|
||||
@@ -49,7 +50,10 @@ use std::time::{Duration, Instant};
|
||||
/// }
|
||||
///
|
||||
/// let mut body = TextBody::from_bytes(b"HelloWorld");
|
||||
/// let mut processor = Processor { vec: Vec::new(), echo: 0 };
|
||||
/// let mut processor = Processor {
|
||||
/// vec: Vec::new(),
|
||||
/// echo: 0,
|
||||
/// };
|
||||
/// let _ = BodyReader::new(&mut processor).read_all(&mut body);
|
||||
///
|
||||
/// // All data is read.
|
||||
@@ -87,16 +91,16 @@ impl<T: BodyProcessor> BodyReader<T> {
|
||||
/// use ylong_http_client::sync_impl::{BodyReader, DefaultBodyProcessor};
|
||||
/// use ylong_http_client::util::Timeout;
|
||||
///
|
||||
/// let reader = BodyReader::new(DefaultBodyProcessor::new())
|
||||
/// .read_timeout(Timeout::none());
|
||||
/// let reader = BodyReader::new(DefaultBodyProcessor::new()).read_timeout(Timeout::none());
|
||||
/// ```
|
||||
pub fn read_timeout(mut self, timeout: Timeout) -> Self {
|
||||
self.read_timeout = timeout;
|
||||
self
|
||||
}
|
||||
|
||||
/// Reads all the body data. During the read process, [`BodyProcessor::write`] and
|
||||
/// [`BodyProcessor::progress`] will be called multiple times.
|
||||
/// Reads all the body data. During the read process,
|
||||
/// [`BodyProcessor::write`] and [`BodyProcessor::progress`] will be
|
||||
/// called multiple times.
|
||||
///
|
||||
/// [`BodyProcessor::write`]: BodyProcessor::write
|
||||
/// [`BodyProcessor::progress`]: BodyProcessor::progress
|
||||
@@ -104,7 +108,7 @@ impl<T: BodyProcessor> BodyReader<T> {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ylong_http_client::sync_impl::{BodyReader, BodyProcessor};
|
||||
/// use ylong_http_client::sync_impl::{BodyProcessor, BodyReader};
|
||||
/// use ylong_http_client::TextBody;
|
||||
///
|
||||
/// let mut body = TextBody::from_bytes(b"HelloWorld");
|
||||
@@ -154,7 +158,8 @@ impl Default for BodyReader<DefaultBodyProcessor> {
|
||||
}
|
||||
}
|
||||
|
||||
/// The trait defines methods for processing bodies of HTTP messages. Unlike the async version, this is for synchronous usage.
|
||||
/// The trait defines methods for processing bodies of HTTP messages. Unlike the
|
||||
/// async version, this is for synchronous usage.
|
||||
pub trait BodyProcessor {
|
||||
/// Writes the body data read each time to the specified location.
|
||||
///
|
||||
@@ -217,14 +222,16 @@ impl Default for DefaultBodyProcessor {
|
||||
|
||||
#[cfg(test)]
|
||||
mod ut_syn_reader {
|
||||
use ylong_http::body::TextBody;
|
||||
|
||||
use crate::sync_impl::{BodyReader, DefaultBodyProcessor};
|
||||
use crate::util::Timeout;
|
||||
use ylong_http::body::TextBody;
|
||||
|
||||
/// UT test cases for `BodyReader::read_timeout`.
|
||||
///
|
||||
/// # Brief
|
||||
/// 1. Creates a `BodyReader` with `DefaultBodyProcessor::default` by calling `BodyReader::new`.
|
||||
/// 1. Creates a `BodyReader` with `DefaultBodyProcessor::default` by
|
||||
/// calling `BodyReader::new`.
|
||||
/// 2. Calls `read_timeout`.
|
||||
/// 3. Checks if the result is correct.
|
||||
#[test]
|
||||
|
||||
@@ -11,9 +11,10 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::io::{Read, Write};
|
||||
|
||||
#[cfg(feature = "__c_openssl")]
|
||||
use crate::util::c_openssl::ssl::SslStream;
|
||||
use std::io::{Read, Write};
|
||||
|
||||
/// A stream which may be wrapped with TLS.
|
||||
pub enum MixStream<T> {
|
||||
|
||||
@@ -36,7 +36,7 @@ pub(crate) fn encode(input: &[u8]) -> Vec<u8> {
|
||||
}
|
||||
|
||||
static BASE64_TABLE: [u8; 64] = [
|
||||
//0 1 2 3 4 5 6 7
|
||||
// 0 1 2 3 4 5 6 7
|
||||
b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', // 0
|
||||
b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', // 1
|
||||
b'Q', b'R', b'S', b'T', b'U', b'V', b'W', b'X', // 2
|
||||
@@ -54,7 +54,8 @@ mod ut_util_base64 {
|
||||
/// UT test cases for `base64::encode`.
|
||||
///
|
||||
/// # Brief
|
||||
/// 1. Calls `encode` to parse the string and convert it into `base64` format.
|
||||
/// 1. Calls `encode` to parse the string and convert it into `base64`
|
||||
/// format.
|
||||
/// 2. Checks if the results are correct.
|
||||
#[test]
|
||||
fn ut_util_base64_encode() {
|
||||
|
||||
@@ -11,16 +11,17 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::{error::HttpClientError, util::AlpnProtocolList};
|
||||
use crate::{
|
||||
util::c_openssl::{
|
||||
error::ErrorStack,
|
||||
ssl::{Ssl, SslContext, SslContextBuilder, SslFiletype, SslMethod, SslVersion},
|
||||
x509::{X509Ref, X509Store, X509},
|
||||
},
|
||||
ErrorKind,
|
||||
use std::net::IpAddr;
|
||||
use std::path::Path;
|
||||
|
||||
use crate::error::HttpClientError;
|
||||
use crate::util::c_openssl::error::ErrorStack;
|
||||
use crate::util::c_openssl::ssl::{
|
||||
Ssl, SslContext, SslContextBuilder, SslFiletype, SslMethod, SslVersion,
|
||||
};
|
||||
use std::{net::IpAddr, path::Path};
|
||||
use crate::util::c_openssl::x509::{X509Ref, X509Store, X509};
|
||||
use crate::util::AlpnProtocolList;
|
||||
use crate::ErrorKind;
|
||||
|
||||
/// `TlsContextBuilder` implementation based on `SSL_CTX`.
|
||||
///
|
||||
@@ -68,8 +69,7 @@ impl TlsConfigBuilder {
|
||||
/// ```
|
||||
/// use ylong_http_client::util::TlsConfigBuilder;
|
||||
///
|
||||
/// let builder = TlsConfigBuilder::new()
|
||||
/// .ca_file("ca.crt");
|
||||
/// let builder = TlsConfigBuilder::new().ca_file("ca.crt");
|
||||
/// ```
|
||||
pub fn ca_file<T: AsRef<Path>>(mut self, path: T) -> Self {
|
||||
self.inner = self
|
||||
@@ -79,7 +79,8 @@ impl TlsConfigBuilder {
|
||||
}
|
||||
|
||||
/// Sets the maximum supported protocol version. A value of `None` will
|
||||
/// enable protocol versions down the the highest version supported by `OpenSSL`.
|
||||
/// enable protocol versions down the the highest version supported by
|
||||
/// `OpenSSL`.
|
||||
///
|
||||
/// Requires `OpenSSL 1.1.0` or or `LibreSSL 2.6.1` or newer.
|
||||
///
|
||||
@@ -88,8 +89,7 @@ impl TlsConfigBuilder {
|
||||
/// ```
|
||||
/// use ylong_http_client::util::{TlsConfigBuilder, TlsVersion};
|
||||
///
|
||||
/// let builder = TlsConfigBuilder::new()
|
||||
/// .max_proto_version(TlsVersion::TLS_1_2);
|
||||
/// let builder = TlsConfigBuilder::new().max_proto_version(TlsVersion::TLS_1_2);
|
||||
/// ```
|
||||
pub fn max_proto_version(mut self, version: TlsVersion) -> Self {
|
||||
self.inner = self.inner.and_then(|mut builder| {
|
||||
@@ -101,7 +101,8 @@ impl TlsConfigBuilder {
|
||||
}
|
||||
|
||||
/// Sets the minimum supported protocol version. A value of `None` will
|
||||
/// enable protocol versions down the the lowest version supported by `OpenSSL`.
|
||||
/// enable protocol versions down the the lowest version supported by
|
||||
/// `OpenSSL`.
|
||||
///
|
||||
/// Requires `OpenSSL 1.1.0` or `LibreSSL 2.6.1` or newer.
|
||||
///
|
||||
@@ -110,8 +111,7 @@ impl TlsConfigBuilder {
|
||||
/// ```
|
||||
/// use ylong_http_client::util::{TlsConfigBuilder, TlsVersion};
|
||||
///
|
||||
/// let builder = TlsConfigBuilder::new()
|
||||
/// .min_proto_version(TlsVersion::TLS_1_2);
|
||||
/// let builder = TlsConfigBuilder::new().min_proto_version(TlsVersion::TLS_1_2);
|
||||
/// ```
|
||||
pub fn min_proto_version(mut self, version: TlsVersion) -> Self {
|
||||
self.inner = self.inner.and_then(|mut builder| {
|
||||
@@ -136,9 +136,7 @@ impl TlsConfigBuilder {
|
||||
/// use ylong_http_client::util::TlsConfigBuilder;
|
||||
///
|
||||
/// let builder = TlsConfigBuilder::new()
|
||||
/// .cipher_list(
|
||||
/// "DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK"
|
||||
/// );
|
||||
/// .cipher_list("DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK");
|
||||
/// ```
|
||||
pub fn cipher_list(mut self, list: &str) -> Self {
|
||||
self.inner = self
|
||||
@@ -163,9 +161,7 @@ impl TlsConfigBuilder {
|
||||
/// use ylong_http_client::util::TlsConfigBuilder;
|
||||
///
|
||||
/// let builder = TlsConfigBuilder::new()
|
||||
/// .cipher_suites(
|
||||
/// "DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK"
|
||||
/// );
|
||||
/// .cipher_suites("DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK");
|
||||
/// ```
|
||||
pub fn cipher_suites(mut self, list: &str) -> Self {
|
||||
self.inner = self
|
||||
@@ -177,16 +173,16 @@ impl TlsConfigBuilder {
|
||||
/// Loads a leaf certificate from a file.
|
||||
///
|
||||
/// Only a single certificate will be loaded - use `add_extra_chain_cert` to
|
||||
/// add the remainder of the certificate chain, or `set_certificate_chain_file`
|
||||
/// to load the entire chain from a single file.
|
||||
/// add the remainder of the certificate chain, or
|
||||
/// `set_certificate_chain_file` to load the entire chain from a single
|
||||
/// file.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ylong_http_client::util::{TlsConfigBuilder, TlsFileType};
|
||||
///
|
||||
/// let builder = TlsConfigBuilder::new()
|
||||
/// .certificate_file("cert.pem", TlsFileType::PEM);
|
||||
/// let builder = TlsConfigBuilder::new().certificate_file("cert.pem", TlsFileType::PEM);
|
||||
/// ```
|
||||
pub fn certificate_file<T: AsRef<Path>>(mut self, path: T, file_type: TlsFileType) -> Self {
|
||||
self.inner = self.inner.and_then(|mut builder| {
|
||||
@@ -228,13 +224,14 @@ impl TlsConfigBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the protocols to sent to the server for Application Layer Protocol Negotiation (ALPN).
|
||||
/// Sets the protocols to sent to the server for Application Layer Protocol
|
||||
/// Negotiation (ALPN).
|
||||
///
|
||||
/// Requires OpenSSL 1.0.2 or LibreSSL 2.6.1 or newer.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use ylong_http_client::util::{TlsConfigBuilder};
|
||||
/// use ylong_http_client::util::TlsConfigBuilder;
|
||||
///
|
||||
/// let protocols = b"\x06spdy/1\x08http/1.1";
|
||||
/// let builder = TlsConfigBuilder::new().alpn_protos(protocols);
|
||||
@@ -246,7 +243,8 @@ impl TlsConfigBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the protocols to sent to the server for Application Layer Protocol Negotiation (ALPN).
|
||||
/// Sets the protocols to sent to the server for Application Layer Protocol
|
||||
/// Negotiation (ALPN).
|
||||
///
|
||||
/// This method is based on `openssl::SslContextBuilder::set_alpn_protos`.
|
||||
///
|
||||
@@ -268,8 +266,8 @@ impl TlsConfigBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Controls the use of built-in system certificates during certificate validation.
|
||||
/// Default to `true` -- uses built-in system certs.
|
||||
/// Controls the use of built-in system certificates during certificate
|
||||
/// validation. Default to `true` -- uses built-in system certs.
|
||||
pub fn build_in_root_certs(mut self, is_use: bool) -> Self {
|
||||
if !is_use {
|
||||
self.inner = X509Store::new().and_then(|store| {
|
||||
@@ -350,7 +348,8 @@ impl TlsConfigBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Builds a `TlsContext`. Returns `Err` if an error occurred during configuration.
|
||||
/// Builds a `TlsContext`. Returns `Err` if an error occurred during
|
||||
/// configuration.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -613,10 +612,8 @@ impl Certificate {
|
||||
|
||||
#[cfg(test)]
|
||||
mod ut_openssl_adapter {
|
||||
use crate::{
|
||||
util::{Cert, TlsConfigBuilder, TlsFileType, TlsVersion},
|
||||
AlpnProtocol, AlpnProtocolList, Certificate,
|
||||
};
|
||||
use crate::util::{Cert, TlsConfigBuilder, TlsFileType, TlsVersion};
|
||||
use crate::{AlpnProtocol, AlpnProtocolList, Certificate};
|
||||
|
||||
/// UT test cases for `TlsConfigBuilder::new`.
|
||||
///
|
||||
@@ -767,7 +764,8 @@ mod ut_openssl_adapter {
|
||||
/// UT test cases for `TlsConfig::ssl`.
|
||||
///
|
||||
/// # Brief
|
||||
/// 1. Creates a `TlsConfig` by calling `TlsConfigBuilder::new` and `TlsConfigBuilder::build`.
|
||||
/// 1. Creates a `TlsConfig` by calling `TlsConfigBuilder::new` and
|
||||
/// `TlsConfigBuilder::build`.
|
||||
/// 2. Creates a `TlsSsl` by calling `TlsConfig::ssl_new`.
|
||||
/// 3. Calls `TlsSsl::into_inner`.
|
||||
/// 4. Checks if the result is as expected.
|
||||
|
||||
@@ -11,23 +11,23 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::{
|
||||
check_ptr,
|
||||
error::ErrorStack,
|
||||
ffi::bio::{
|
||||
BIO_clear_flags, BIO_free_all, BIO_get_data, BIO_meth_free, BIO_meth_new,
|
||||
BIO_meth_set_create, BIO_meth_set_ctrl, BIO_meth_set_destroy, BIO_meth_set_puts,
|
||||
BIO_meth_set_read, BIO_meth_set_write, BIO_new, BIO_new_mem_buf, BIO_set_data,
|
||||
BIO_set_flags, BIO_set_init, BIO, BIO_METHOD,
|
||||
},
|
||||
ssl_init,
|
||||
};
|
||||
use core::{any::Any, marker::PhantomData, panic::AssertUnwindSafe, ptr, slice};
|
||||
use core::any::Any;
|
||||
use core::marker::PhantomData;
|
||||
use core::panic::AssertUnwindSafe;
|
||||
use core::{ptr, slice};
|
||||
use std::io::{self, Read, Write};
|
||||
use std::panic::catch_unwind;
|
||||
|
||||
use libc::{c_char, c_int, c_long, c_void, strlen};
|
||||
use std::{
|
||||
io::{self, Read, Write},
|
||||
panic::catch_unwind,
|
||||
|
||||
use super::error::ErrorStack;
|
||||
use super::ffi::bio::{
|
||||
BIO_clear_flags, BIO_free_all, BIO_get_data, BIO_meth_free, BIO_meth_new, BIO_meth_set_create,
|
||||
BIO_meth_set_ctrl, BIO_meth_set_destroy, BIO_meth_set_puts, BIO_meth_set_read,
|
||||
BIO_meth_set_write, BIO_new, BIO_new_mem_buf, BIO_set_data, BIO_set_flags, BIO_set_init, BIO,
|
||||
BIO_METHOD,
|
||||
};
|
||||
use super::{check_ptr, ssl_init};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Bio(*mut BIO);
|
||||
|
||||
@@ -11,23 +11,24 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use core::ffi::CStr;
|
||||
use core::{ptr, str};
|
||||
use std::borrow::Cow;
|
||||
use std::error::Error;
|
||||
#[cfg(feature = "c_openssl_3_0")]
|
||||
use std::ffi::CString;
|
||||
use std::fmt;
|
||||
|
||||
#[cfg(feature = "c_openssl_1_1")]
|
||||
use libc::c_char;
|
||||
use libc::{c_int, c_ulong};
|
||||
|
||||
use super::ssl_init;
|
||||
#[cfg(feature = "c_openssl_3_0")]
|
||||
use crate::util::c_openssl::ffi::err::ERR_get_error_all;
|
||||
#[cfg(feature = "c_openssl_1_1")]
|
||||
use crate::util::c_openssl::ffi::err::{ERR_func_error_string, ERR_get_error_line_data};
|
||||
|
||||
use crate::util::c_openssl::ffi::err::{ERR_lib_error_string, ERR_reason_error_string};
|
||||
use core::{ffi::CStr, ptr, str};
|
||||
|
||||
#[cfg(feature = "c_openssl_1_1")]
|
||||
use libc::c_char;
|
||||
#[cfg(feature = "c_openssl_3_0")]
|
||||
use std::ffi::CString;
|
||||
|
||||
use libc::{c_int, c_ulong};
|
||||
|
||||
use std::{borrow::Cow, error::Error, fmt};
|
||||
|
||||
const ERR_TXT_MALLOCED: c_int = 0x01;
|
||||
const ERR_TXT_STRING: c_int = 0x02;
|
||||
|
||||
@@ -17,8 +17,9 @@ pub(crate) enum BIO {}
|
||||
|
||||
// for `BIO`
|
||||
extern "C" {
|
||||
/// Creates a memory BIO using len bytes of data at buf, if len is -1 then the
|
||||
/// buf is assumed to be nul terminated and its length is determined by strlen.
|
||||
/// Creates a memory BIO using len bytes of data at buf, if len is -1 then
|
||||
/// the buf is assumed to be nul terminated and its length is determined
|
||||
/// by strlen.
|
||||
pub(crate) fn BIO_new_mem_buf(buf: *const c_void, len: c_int) -> *mut BIO;
|
||||
|
||||
pub(crate) fn BIO_set_data(a: *mut BIO, data: *mut c_void);
|
||||
|
||||
@@ -25,9 +25,9 @@ extern "C" {
|
||||
|
||||
pub(crate) fn ERR_clear_error();
|
||||
|
||||
/// Returns the earliest error code from the thread's error queue and removes
|
||||
/// the entry. This function can be called repeatedly until there are no more
|
||||
/// error codes to return.
|
||||
/// Returns the earliest error code from the thread's error queue and
|
||||
/// removes the entry. This function can be called repeatedly until
|
||||
/// there are no more error codes to return.
|
||||
#[cfg(feature = "c_openssl_3_0")]
|
||||
pub(crate) fn ERR_get_error_all(
|
||||
file: *mut *const c_char,
|
||||
|
||||
@@ -25,6 +25,7 @@ use libc::c_int;
|
||||
pub(crate) enum OPENSSL_INIT_SETTINGS {}
|
||||
|
||||
extern "C" {
|
||||
/// Calls this function will explicitly initialise BOTH libcrypto and libssl.
|
||||
/// Calls this function will explicitly initialise BOTH libcrypto and
|
||||
/// libssl.
|
||||
pub(crate) fn OPENSSL_init_ssl(opts: u64, settings: *const OPENSSL_INIT_SETTINGS) -> c_int;
|
||||
}
|
||||
|
||||
@@ -11,9 +11,11 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::{bio::BIO, x509::C_X509};
|
||||
use libc::{c_char, c_int, c_void};
|
||||
|
||||
use super::bio::BIO;
|
||||
use super::x509::C_X509;
|
||||
|
||||
// callback func
|
||||
pub(crate) type PemPasswordCb = Option<
|
||||
unsafe extern "C" fn(
|
||||
|
||||
@@ -11,12 +11,11 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::{
|
||||
bio::BIO,
|
||||
x509::{C_X509, X509_STORE, X509_STORE_CTX, X509_VERIFY_PARAM},
|
||||
};
|
||||
use libc::{c_char, c_int, c_long, c_uchar, c_uint, c_void};
|
||||
|
||||
use super::bio::BIO;
|
||||
use super::x509::{C_X509, X509_STORE, X509_STORE_CTX, X509_VERIFY_PARAM};
|
||||
|
||||
/// This is the global context structure which is created by a server or client
|
||||
/// once per program life-time and which holds mainly default values for the
|
||||
/// `SSL` structures which are later created for the connections.
|
||||
@@ -68,10 +67,11 @@ extern "C" {
|
||||
file_type: c_int,
|
||||
) -> c_int;
|
||||
|
||||
/// Loads a certificate chain from file into ctx. The certificates must be in
|
||||
/// PEM format and must be sorted starting with the subject's certificate (actual
|
||||
/// client or server certificate), followed by intermediate CA certificates
|
||||
/// if applicable, and ending at the highest level (root) CA.
|
||||
/// Loads a certificate chain from file into ctx. The certificates must be
|
||||
/// in PEM format and must be sorted starting with the subject's
|
||||
/// certificate (actual client or server certificate), followed by
|
||||
/// intermediate CA certificates if applicable, and ending at the
|
||||
/// highest level (root) CA.
|
||||
pub(crate) fn SSL_CTX_use_certificate_chain_file(
|
||||
ctx: *mut SSL_CTX,
|
||||
cert_chain_file: *const c_char,
|
||||
@@ -85,25 +85,28 @@ extern "C" {
|
||||
) -> c_int;
|
||||
|
||||
/// Sets/replaces the certificate verification storage of ctx to/with store.
|
||||
/// If another X509_STORE object is currently set in ctx, it will be X509_STORE_free()ed.
|
||||
/// If another X509_STORE object is currently set in ctx, it will be
|
||||
/// X509_STORE_free()ed.
|
||||
pub(crate) fn SSL_CTX_set_cert_store(ctx: *mut SSL_CTX, store: *mut X509_STORE);
|
||||
|
||||
/// Returns a pointer to the current certificate verification storage.
|
||||
pub(crate) fn SSL_CTX_get_cert_store(ctx: *const SSL_CTX) -> *mut X509_STORE;
|
||||
|
||||
/// Specifies that the default locations from which CA certificates are loaded
|
||||
/// should be used. There is one default directory, one default file and one
|
||||
/// default store. The default CA certificates directory is called certs in the
|
||||
/// default OpenSSL directory, and this is also the default store.
|
||||
/// Alternatively the SSL_CERT_DIR environment variable can be defined to
|
||||
/// override this location. The default CA certificates file is called cert.pem
|
||||
/// in the default OpenSSL directory. Alternatively the SSL_CERT_FILE environment
|
||||
/// variable can be defined to override this location.
|
||||
/// Specifies that the default locations from which CA certificates are
|
||||
/// loaded should be used. There is one default directory, one default
|
||||
/// file and one default store. The default CA certificates directory is
|
||||
/// called certs in the default OpenSSL directory, and this is also the
|
||||
/// default store. Alternatively the SSL_CERT_DIR environment variable
|
||||
/// can be defined to override this location. The default CA
|
||||
/// certificates file is called cert.pem in the default OpenSSL
|
||||
/// directory. Alternatively the SSL_CERT_FILE environment variable can
|
||||
/// be defined to override this location.
|
||||
pub(crate) fn SSL_CTX_set_default_verify_paths(ctx: *mut SSL_CTX) -> c_int;
|
||||
|
||||
/// Sets the verification flags for ctx to be mode and specifies the verify_callback
|
||||
/// function to be used.
|
||||
/// If no callback function shall be specified, the NULL pointer can be use for verify_callback.
|
||||
/// Sets the verification flags for ctx to be mode and specifies the
|
||||
/// verify_callback function to be used.
|
||||
/// If no callback function shall be specified, the NULL pointer can be use
|
||||
/// for verify_callback.
|
||||
pub(crate) fn SSL_CTX_set_verify(
|
||||
ctx: *mut SSL_CTX,
|
||||
mode: c_int,
|
||||
@@ -112,10 +115,10 @@ extern "C" {
|
||||
|
||||
}
|
||||
|
||||
/// This is the main SSL/TLS structure which is created by a server or client per
|
||||
/// established connection. This actually is the core structure in the SSL API.
|
||||
/// At run-time the application usually deals with this structure which has links
|
||||
/// to mostly all other structures.
|
||||
/// This is the main SSL/TLS structure which is created by a server or client
|
||||
/// per established connection. This actually is the core structure in the SSL
|
||||
/// API. At run-time the application usually deals with this structure which has
|
||||
/// links to mostly all other structures.
|
||||
pub(crate) enum SSL {}
|
||||
|
||||
// for `SSL`
|
||||
@@ -128,14 +131,17 @@ extern "C" {
|
||||
pub(crate) fn SSL_free(ssl: *mut SSL);
|
||||
|
||||
/// Obtains result code for TLS/SSL I/O operation.\
|
||||
/// SSL_get_error() must be used in the same thread that performed the TLS/SSL
|
||||
/// I/O operation, and no other OpenSSL function calls should appear in between.
|
||||
/// SSL_get_error() must be used in the same thread that performed the
|
||||
/// TLS/SSL I/O operation, and no other OpenSSL function calls should
|
||||
/// appear in between.
|
||||
pub(crate) fn SSL_get_error(ssl: *const SSL, ret: c_int) -> c_int;
|
||||
|
||||
/// Returns an abbreviated string indicating the current state of the SSL object ssl.
|
||||
/// Returns an abbreviated string indicating the current state of the SSL
|
||||
/// object ssl.
|
||||
pub(crate) fn SSL_state_string_long(ssl: *const SSL) -> *const c_char;
|
||||
|
||||
/// Returns the result of the verification of the X509 certificate presented by the peer, if any.
|
||||
/// Returns the result of the verification of the X509 certificate presented
|
||||
/// by the peer, if any.
|
||||
pub(crate) fn SSL_get_verify_result(ssl: *const SSL) -> c_long;
|
||||
|
||||
pub(crate) fn SSL_set_bio(ssl: *mut SSL, rbio: *mut BIO, wbio: *mut BIO);
|
||||
@@ -152,20 +158,21 @@ extern "C" {
|
||||
|
||||
pub(crate) fn SSL_ctrl(ssl: *mut SSL, cmd: c_int, larg: c_long, parg: *mut c_void) -> c_long;
|
||||
|
||||
/// Retrieve an internal pointer to the verification parameters for ssl respectively.
|
||||
/// The returned pointer must not be freed by the calling application.
|
||||
/// Retrieve an internal pointer to the verification parameters for ssl
|
||||
/// respectively. The returned pointer must not be freed by the calling
|
||||
/// application.
|
||||
pub(crate) fn SSL_get0_param(ssl: *mut SSL) -> *mut X509_VERIFY_PARAM;
|
||||
}
|
||||
|
||||
/// This is a dispatch structure describing the internal ssl library methods/functions
|
||||
/// which implement the various protocol versions (SSLv3 TLSv1, ...).
|
||||
/// It's needed to create an `SSL_CTX`.
|
||||
/// This is a dispatch structure describing the internal ssl library
|
||||
/// methods/functions which implement the various protocol versions (SSLv3
|
||||
/// TLSv1, ...). It's needed to create an `SSL_CTX`.
|
||||
pub(crate) enum SSL_METHOD {}
|
||||
|
||||
// for `SSL_METHOD`
|
||||
extern "C" {
|
||||
/// Is the general-purpose version-flexible SSL/TLS methods. The actual protocol
|
||||
/// version used will be negotiated to the highest version mutually supported
|
||||
/// by the client and the server.
|
||||
/// Is the general-purpose version-flexible SSL/TLS methods. The actual
|
||||
/// protocol version used will be negotiated to the highest version
|
||||
/// mutually supported by the client and the server.
|
||||
pub(crate) fn TLS_client_method() -> *const SSL_METHOD;
|
||||
}
|
||||
|
||||
@@ -26,15 +26,17 @@ pub(crate) mod stack;
|
||||
pub(crate) mod x509;
|
||||
|
||||
pub mod adapter;
|
||||
pub use adapter::{Cert, Certificate, TlsConfig, TlsConfigBuilder, TlsFileType, TlsVersion};
|
||||
|
||||
use crate::util::c_openssl::ffi::OPENSSL_init_ssl;
|
||||
use core::ptr;
|
||||
use error::ErrorStack;
|
||||
use libc::c_int;
|
||||
use std::sync::Once;
|
||||
|
||||
/// Automatic loading of the libssl error strings. This option is a default option.
|
||||
pub use adapter::{Cert, Certificate, TlsConfig, TlsConfigBuilder, TlsFileType, TlsVersion};
|
||||
use error::ErrorStack;
|
||||
use libc::c_int;
|
||||
|
||||
use crate::util::c_openssl::ffi::OPENSSL_init_ssl;
|
||||
|
||||
/// Automatic loading of the libssl error strings. This option is a default
|
||||
/// option.
|
||||
pub(crate) const OPENSSL_INIT_LOAD_SSL_STRINGS: u64 = 0x00200000;
|
||||
|
||||
/// Checks null-pointer.
|
||||
|
||||
@@ -11,32 +11,28 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::{filetype::SslFiletype, method::SslMethod, version::SslVersion};
|
||||
use crate::{
|
||||
c_openssl::{
|
||||
ffi::ssl::{
|
||||
SSL_CTX_free, SSL_CTX_get_cert_store, SSL_CTX_set_default_verify_paths,
|
||||
SSL_CTX_set_verify,
|
||||
},
|
||||
x509::{X509Store, X509StoreRef},
|
||||
},
|
||||
util::c_openssl::{
|
||||
check_ptr, check_ret,
|
||||
error::ErrorStack,
|
||||
ffi::ssl::{
|
||||
SSL_CTX_ctrl, SSL_CTX_load_verify_locations, SSL_CTX_new, SSL_CTX_set_alpn_protos,
|
||||
SSL_CTX_set_cert_store, SSL_CTX_set_cipher_list, SSL_CTX_set_ciphersuites,
|
||||
SSL_CTX_up_ref, SSL_CTX_use_certificate_chain_file, SSL_CTX_use_certificate_file,
|
||||
SSL_CTX,
|
||||
},
|
||||
foreign::{Foreign, ForeignRef},
|
||||
ssl_init,
|
||||
x509::{X509Ref, X509},
|
||||
},
|
||||
};
|
||||
use core::{fmt, mem, ptr};
|
||||
use std::ffi::CString;
|
||||
use std::path::Path;
|
||||
|
||||
use libc::{c_int, c_long, c_uint, c_void};
|
||||
use std::{ffi::CString, path::Path};
|
||||
|
||||
use super::filetype::SslFiletype;
|
||||
use super::method::SslMethod;
|
||||
use super::version::SslVersion;
|
||||
use crate::c_openssl::ffi::ssl::{
|
||||
SSL_CTX_free, SSL_CTX_get_cert_store, SSL_CTX_set_default_verify_paths, SSL_CTX_set_verify,
|
||||
};
|
||||
use crate::c_openssl::x509::{X509Store, X509StoreRef};
|
||||
use crate::util::c_openssl::error::ErrorStack;
|
||||
use crate::util::c_openssl::ffi::ssl::{
|
||||
SSL_CTX_ctrl, SSL_CTX_load_verify_locations, SSL_CTX_new, SSL_CTX_set_alpn_protos,
|
||||
SSL_CTX_set_cert_store, SSL_CTX_set_cipher_list, SSL_CTX_set_ciphersuites, SSL_CTX_up_ref,
|
||||
SSL_CTX_use_certificate_chain_file, SSL_CTX_use_certificate_file, SSL_CTX,
|
||||
};
|
||||
use crate::util::c_openssl::foreign::{Foreign, ForeignRef};
|
||||
use crate::util::c_openssl::x509::{X509Ref, X509};
|
||||
use crate::util::c_openssl::{check_ptr, check_ret, ssl_init};
|
||||
|
||||
const SSL_CTRL_EXTRA_CHAIN_CERT: c_int = 14;
|
||||
|
||||
@@ -193,8 +189,9 @@ impl SslContextBuilder {
|
||||
|
||||
/// Loads a leaf certificate from a file.
|
||||
///
|
||||
/// Only a single certificate will be loaded - use `add_extra_chain_cert` to add the remainder
|
||||
/// of the certificate chain, or `set_certificate_chain_file` to load the entire chain from a
|
||||
/// Only a single certificate will be loaded - use `add_extra_chain_cert` to
|
||||
/// add the remainder of the certificate chain, or
|
||||
/// `set_certificate_chain_file` to load the entire chain from a
|
||||
/// single file.
|
||||
pub(crate) fn set_certificate_file<P>(
|
||||
&mut self,
|
||||
@@ -222,9 +219,9 @@ impl SslContextBuilder {
|
||||
|
||||
/// Loads a certificate chain from file into ctx.
|
||||
/// The certificates must be in PEM format and must be sorted starting with
|
||||
/// the subject's certificate (actual client or server certificate), followed
|
||||
/// by intermediate CA certificates if applicable, and ending at the highest
|
||||
/// level (root) CA.
|
||||
/// the subject's certificate (actual client or server certificate),
|
||||
/// followed by intermediate CA certificates if applicable, and ending
|
||||
/// at the highest level (root) CA.
|
||||
pub(crate) fn set_certificate_chain_file<P>(&mut self, file: P) -> Result<(), ErrorStack>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
@@ -243,7 +240,8 @@ impl SslContextBuilder {
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
/// Sets the protocols to sent to the server for Application Layer Protocol Negotiation (ALPN).
|
||||
/// Sets the protocols to sent to the server for Application Layer Protocol
|
||||
/// Negotiation (ALPN).
|
||||
pub(crate) fn set_alpn_protos(&mut self, protocols: &[u8]) -> Result<(), ErrorStack> {
|
||||
assert!(protocols.len() <= c_uint::max_value() as usize);
|
||||
|
||||
|
||||
@@ -11,12 +11,14 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::c_openssl::error::ErrorStack;
|
||||
use core::fmt;
|
||||
use std::error::Error;
|
||||
use std::io;
|
||||
|
||||
use libc::c_int;
|
||||
use std::{error::Error, io};
|
||||
|
||||
use super::MidHandshakeSslStream;
|
||||
use crate::c_openssl::error::ErrorStack;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct SslError {
|
||||
|
||||
@@ -11,33 +11,28 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::{error::HandshakeError, MidHandshakeSslStream, SslContext, SslErrorCode, SslStream};
|
||||
use crate::{
|
||||
c_openssl::{
|
||||
check_ret,
|
||||
ffi::{
|
||||
bio::BIO,
|
||||
ssl::{
|
||||
SSL_connect, SSL_ctrl, SSL_get0_param, SSL_get_error, SSL_get_rbio,
|
||||
SSL_get_verify_result, SSL_read, SSL_state_string_long, SSL_write,
|
||||
},
|
||||
},
|
||||
foreign::ForeignRef,
|
||||
x509::{X509VerifyParamRef, X509VerifyResult, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS},
|
||||
},
|
||||
util::c_openssl::{
|
||||
check_ptr,
|
||||
error::ErrorStack,
|
||||
ffi::ssl::{SSL_free, SSL_new, SSL},
|
||||
foreign::Foreign,
|
||||
},
|
||||
};
|
||||
use core::{cmp, ffi, fmt, str};
|
||||
use std::ffi::CString;
|
||||
use std::io::{Read, Write};
|
||||
|
||||
use libc::{c_char, c_int, c_long, c_void};
|
||||
use std::{
|
||||
ffi::CString,
|
||||
io::{Read, Write},
|
||||
|
||||
use super::error::HandshakeError;
|
||||
use super::{MidHandshakeSslStream, SslContext, SslErrorCode, SslStream};
|
||||
use crate::c_openssl::check_ret;
|
||||
use crate::c_openssl::ffi::bio::BIO;
|
||||
use crate::c_openssl::ffi::ssl::{
|
||||
SSL_connect, SSL_ctrl, SSL_get0_param, SSL_get_error, SSL_get_rbio, SSL_get_verify_result,
|
||||
SSL_read, SSL_state_string_long, SSL_write,
|
||||
};
|
||||
use crate::c_openssl::foreign::ForeignRef;
|
||||
use crate::c_openssl::x509::{
|
||||
X509VerifyParamRef, X509VerifyResult, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS,
|
||||
};
|
||||
use crate::util::c_openssl::check_ptr;
|
||||
use crate::util::c_openssl::error::ErrorStack;
|
||||
use crate::util::c_openssl::ffi::ssl::{SSL_free, SSL_new, SSL};
|
||||
use crate::util::c_openssl::foreign::Foreign;
|
||||
|
||||
foreign_type!(
|
||||
type CStruct = SSL;
|
||||
|
||||
@@ -11,22 +11,20 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::{InternalError, Ssl, SslError, SslErrorCode, SslRef};
|
||||
use crate::{
|
||||
c_openssl::{
|
||||
bio::{self, get_error, get_panic, get_stream_mut, get_stream_ref},
|
||||
error::ErrorStack,
|
||||
ffi::ssl::{SSL_connect, SSL_set_bio, SSL_shutdown},
|
||||
foreign::Foreign,
|
||||
},
|
||||
util::c_openssl::bio::BioMethod,
|
||||
};
|
||||
use core::{fmt, marker::PhantomData, mem::ManuallyDrop};
|
||||
use core::fmt;
|
||||
use core::marker::PhantomData;
|
||||
use core::mem::ManuallyDrop;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::panic::resume_unwind;
|
||||
|
||||
use libc::c_int;
|
||||
use std::{
|
||||
io::{self, Read, Write},
|
||||
panic::resume_unwind,
|
||||
};
|
||||
|
||||
use super::{InternalError, Ssl, SslError, SslErrorCode, SslRef};
|
||||
use crate::c_openssl::bio::{self, get_error, get_panic, get_stream_mut, get_stream_ref};
|
||||
use crate::c_openssl::error::ErrorStack;
|
||||
use crate::c_openssl::ffi::ssl::{SSL_connect, SSL_set_bio, SSL_shutdown};
|
||||
use crate::c_openssl::foreign::Foreign;
|
||||
use crate::util::c_openssl::bio::BioMethod;
|
||||
|
||||
/// A TLS session over a stream.
|
||||
pub struct SslStream<S> {
|
||||
@@ -175,7 +173,8 @@ impl<S: Read + Write> Read for SslStream<S> {
|
||||
// the close_notify alert. No more data can be read.
|
||||
// Does not necessarily indicate that the underlying transport has been closed.
|
||||
Err(ref e) if e.code == SslErrorCode::ZERO_RETURN => return Ok(0),
|
||||
// A non-recoverable, fatal error in the SSL library occurred, usually a protocol error.
|
||||
// A non-recoverable, fatal error in the SSL library occurred, usually a protocol
|
||||
// error.
|
||||
Err(ref e) if e.code == SslErrorCode::SYSCALL && e.get_io_error().is_none() => {
|
||||
return Ok(0)
|
||||
}
|
||||
|
||||
@@ -11,18 +11,17 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::c_openssl::foreign::{Foreign, ForeignRef, ForeignRefWrapper};
|
||||
use core::{
|
||||
borrow::Borrow,
|
||||
marker::PhantomData,
|
||||
mem::forget,
|
||||
ops::{Deref, DerefMut, Range},
|
||||
};
|
||||
use core::borrow::Borrow;
|
||||
use core::marker::PhantomData;
|
||||
use core::mem::forget;
|
||||
use core::ops::{Deref, DerefMut, Range};
|
||||
|
||||
use libc::c_int;
|
||||
|
||||
use super::ffi::stack::{
|
||||
OPENSSL_sk_free, OPENSSL_sk_num, OPENSSL_sk_pop, OPENSSL_sk_value, OPENSSL_STACK,
|
||||
};
|
||||
use crate::c_openssl::foreign::{Foreign, ForeignRef, ForeignRefWrapper};
|
||||
|
||||
pub(crate) trait Stackof: Foreign {
|
||||
type StackType;
|
||||
|
||||
@@ -11,29 +11,25 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::{
|
||||
bio::BioSlice,
|
||||
check_ptr, check_ret,
|
||||
error::{error_get_lib, error_get_reason, ErrorStack},
|
||||
ffi::{
|
||||
err::{ERR_clear_error, ERR_peek_last_error},
|
||||
pem::PEM_read_bio_X509,
|
||||
x509::{
|
||||
d2i_X509, X509_STORE_add_cert, X509_STORE_free, X509_STORE_new, X509_VERIFY_PARAM_free,
|
||||
X509_VERIFY_PARAM_set1_host, X509_VERIFY_PARAM_set1_ip,
|
||||
X509_VERIFY_PARAM_set_hostflags, X509_verify_cert_error_string, STACK_X509, X509_STORE,
|
||||
X509_VERIFY_PARAM,
|
||||
},
|
||||
},
|
||||
foreign::{Foreign, ForeignRef},
|
||||
ssl_init,
|
||||
stack::Stackof,
|
||||
};
|
||||
use crate::util::c_openssl::ffi::x509::{X509_free, C_X509};
|
||||
use core::{ffi, fmt, ptr, str};
|
||||
use libc::{c_int, c_long, c_uint};
|
||||
use std::net::IpAddr;
|
||||
|
||||
use libc::{c_int, c_long, c_uint};
|
||||
|
||||
use super::bio::BioSlice;
|
||||
use super::error::{error_get_lib, error_get_reason, ErrorStack};
|
||||
use super::ffi::err::{ERR_clear_error, ERR_peek_last_error};
|
||||
use super::ffi::pem::PEM_read_bio_X509;
|
||||
use super::ffi::x509::{
|
||||
d2i_X509, X509_STORE_add_cert, X509_STORE_free, X509_STORE_new, X509_VERIFY_PARAM_free,
|
||||
X509_VERIFY_PARAM_set1_host, X509_VERIFY_PARAM_set1_ip, X509_VERIFY_PARAM_set_hostflags,
|
||||
X509_verify_cert_error_string, STACK_X509, X509_STORE, X509_VERIFY_PARAM,
|
||||
};
|
||||
use super::foreign::{Foreign, ForeignRef};
|
||||
use super::stack::Stackof;
|
||||
use super::{check_ptr, check_ret, ssl_init};
|
||||
use crate::util::c_openssl::ffi::x509::{X509_free, C_X509};
|
||||
|
||||
foreign_type!(
|
||||
type CStruct = C_X509;
|
||||
fn drop = X509_free;
|
||||
|
||||
@@ -65,9 +65,9 @@ pub(crate) mod http2 {
|
||||
/// use ylong_http_client::util::H2Config;
|
||||
///
|
||||
/// let config = H2Config::new()
|
||||
/// .set_header_table_size(4096)
|
||||
/// .set_max_header_list_size(16 << 20)
|
||||
/// .set_max_frame_size(2 << 13);
|
||||
/// .set_header_table_size(4096)
|
||||
/// .set_max_header_list_size(16 << 20)
|
||||
/// .set_max_frame_size(2 << 13);
|
||||
/// ```
|
||||
#[derive(Clone)]
|
||||
pub struct H2Config {
|
||||
@@ -97,8 +97,7 @@ pub(crate) mod http2 {
|
||||
/// ```
|
||||
/// use ylong_http_client::util::H2Config;
|
||||
///
|
||||
/// let config = H2Config::new()
|
||||
/// .set_max_frame_size(2 << 13);
|
||||
/// let config = H2Config::new().set_max_frame_size(2 << 13);
|
||||
/// ```
|
||||
pub fn set_max_frame_size(mut self, size: u32) -> Self {
|
||||
self.max_frame_size = size;
|
||||
@@ -112,8 +111,7 @@ pub(crate) mod http2 {
|
||||
/// ```
|
||||
/// use ylong_http_client::util::H2Config;
|
||||
///
|
||||
/// let config = H2Config::new()
|
||||
/// .set_max_header_list_size(16 << 20);
|
||||
/// let config = H2Config::new().set_max_header_list_size(16 << 20);
|
||||
/// ```
|
||||
pub fn set_max_header_list_size(mut self, size: u32) -> Self {
|
||||
self.max_header_list_size = size;
|
||||
@@ -127,8 +125,7 @@ pub(crate) mod http2 {
|
||||
/// ```
|
||||
/// use ylong_http_client::util::H2Config;
|
||||
///
|
||||
/// let config = H2Config::new()
|
||||
/// .set_max_header_list_size(4096);
|
||||
/// let config = H2Config::new().set_max_header_list_size(4096);
|
||||
/// ```
|
||||
pub fn set_header_table_size(mut self, size: u32) -> Self {
|
||||
self.header_table_size = size;
|
||||
@@ -142,8 +139,7 @@ pub(crate) mod http2 {
|
||||
/// ```
|
||||
/// use ylong_http_client::util::H2Config;
|
||||
///
|
||||
/// let config = H2Config::new()
|
||||
/// .set_max_frame_size(2 << 13);
|
||||
/// let config = H2Config::new().set_max_frame_size(2 << 13);
|
||||
/// assert_eq!(config.max_frame_size(), 2 << 13);
|
||||
/// ```
|
||||
pub fn max_frame_size(&self) -> u32 {
|
||||
@@ -157,8 +153,7 @@ pub(crate) mod http2 {
|
||||
/// ```
|
||||
/// use ylong_http_client::util::H2Config;
|
||||
///
|
||||
/// let config = H2Config::new()
|
||||
/// .set_max_header_list_size(16 << 20);
|
||||
/// let config = H2Config::new().set_max_header_list_size(16 << 20);
|
||||
/// assert_eq!(config.max_header_list_size(), 16 << 20);
|
||||
/// ```
|
||||
pub fn max_header_list_size(&self) -> u32 {
|
||||
@@ -172,8 +167,7 @@ pub(crate) mod http2 {
|
||||
/// ```
|
||||
/// use ylong_http_client::util::H2Config;
|
||||
///
|
||||
/// let config = H2Config::new()
|
||||
/// .set_header_table_size(4096);
|
||||
/// let config = H2Config::new().set_header_table_size(4096);
|
||||
/// assert_eq!(config.header_table_size(), 4096);
|
||||
/// ```
|
||||
pub fn header_table_size(&self) -> u32 {
|
||||
|
||||
@@ -19,16 +19,13 @@ mod settings;
|
||||
pub(crate) use client::ClientConfig;
|
||||
pub(crate) use connector::ConnectorConfig;
|
||||
pub(crate) use http::{HttpConfig, HttpVersion};
|
||||
|
||||
pub use settings::{Proxy, ProxyBuilder, Redirect, Retry, SpeedLimit, Timeout};
|
||||
|
||||
#[cfg(feature = "__tls")]
|
||||
mod tls;
|
||||
#[cfg(feature = "__tls")]
|
||||
pub use tls::{AlpnProtocol, AlpnProtocolList};
|
||||
|
||||
#[cfg(feature = "tls_rust_ssl")]
|
||||
pub use tls::{Certificate, PrivateKey, TlsConfig, TlsConfigBuilder, TlsFileType, TlsVersion};
|
||||
|
||||
#[cfg(feature = "http2")]
|
||||
pub use http::http2::H2Config;
|
||||
#[cfg(feature = "__tls")]
|
||||
pub use tls::{AlpnProtocol, AlpnProtocolList};
|
||||
#[cfg(feature = "tls_rust_ssl")]
|
||||
pub use tls::{Certificate, PrivateKey, TlsConfig, TlsConfigBuilder, TlsFileType, TlsVersion};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user