diff --git a/ylong_http/src/h2/encoder.rs b/ylong_http/src/h2/encoder.rs index a29043da7a407707def439e56c50a68a381a7629..adfbef37c176c2d45e7ce213dbe042e2ab178e97 100644 --- a/ylong_http/src/h2/encoder.rs +++ b/ylong_http/src/h2/encoder.rs @@ -1823,6 +1823,101 @@ mod ut_frame_encoder { } } + /// UT test cases for `FrameEncoder` encoding continuation frames. + /// + /// # Brief + /// 1. Creates a `FrameEncoder`. + /// 2. Creates a `Frame` with `Payload::Headers` and sets the flags. + /// 3. Sets the frame for the encoder. + /// 4. Encodes the continuation frames using a buffer. + /// 5. Checks whether the result is correct. + #[test] + fn ut_encode_continuation_frames() { + let mut frame_encoder = FrameEncoder::new(4096, false); + let mut new_parts = Parts::new(); + assert!(new_parts.is_empty()); + new_parts.pseudo.set_method(Some("GET".to_string())); + new_parts.pseudo.set_scheme(Some("https".to_string())); + new_parts.pseudo.set_path(Some("/code".to_string())); + new_parts + .pseudo + .set_authority(Some("example.com".to_string())); + + let mut frame_flag = FrameFlags::empty(); + frame_flag.set_end_headers(true); + frame_flag.set_end_stream(false); + let frame = Frame::new( + 1, + frame_flag.clone(), + Payload::Headers(Headers::new(new_parts.clone())), + ); + + frame_encoder.set_frame(frame).unwrap(); + frame_encoder.state = FrameEncoderState::EncodingContinuationFrames; + let mut buf = [0u8; 5000]; + + assert!(frame_encoder.encode_continuation_frames(&mut buf).is_ok()); + + let mut frame_flag = FrameFlags::empty(); + frame_flag.set_end_headers(true); + let frame = Frame::new( + 1, + frame_flag, + Payload::Headers(Headers::new(new_parts.clone())), + ); + + frame_encoder.set_frame(frame).unwrap(); + frame_encoder.state = FrameEncoderState::EncodingContinuationFrames; + assert!(frame_encoder.encode_continuation_frames(&mut buf).is_ok()); + + let mut frame_flag = FrameFlags::empty(); + frame_flag.set_end_headers(true); + let frame = Frame::new(1, frame_flag, Payload::Ping(Ping::new([0; 8]))); + + frame_encoder.set_frame(frame).unwrap(); + frame_encoder.state = FrameEncoderState::EncodingContinuationFrames; + assert!(frame_encoder.encode_continuation_frames(&mut buf).is_err()); + } + + /// UT test cases for `FrameEncoder` encoding padded data. + /// + /// # Brief + /// 1. Creates a `FrameEncoder`. + /// 2. Creates a `Frame` with `Payload::Data` and sets the flags. + /// 3. Sets the frame for the encoder. + /// 4. Encodes the padding using a buffer. + /// 5. Checks whether the result is correct. + #[test] + fn ut_encode_padding() { + let mut frame_encoder = FrameEncoder::new(4096, false); + + // Creates a padded data frame. + let mut frame_flags = FrameFlags::empty(); + frame_flags.set_end_headers(true); + frame_flags.set_padded(true); + let data_payload = vec![0u8; 500]; + let data_frame = Frame::new( + 1, + frame_flags.clone(), + Payload::Data(Data::new(data_payload)), + ); + + // Sets the frame to the frame_encoder and test padding encoding. + frame_encoder.set_frame(data_frame).unwrap(); + frame_encoder.state = FrameEncoderState::EncodingDataPadding; + let mut buf = [0u8; 600]; + assert!(frame_encoder.encode_padding(&mut buf).is_ok()); + + let headers_payload = Payload::Headers(Headers::new(Parts::new())); + let headers_frame = Frame::new(1, frame_flags.clone(), headers_payload); + frame_encoder.set_frame(headers_frame).unwrap(); + frame_encoder.state = FrameEncoderState::EncodingDataPadding; + assert!(frame_encoder.encode_padding(&mut buf).is_err()); + + frame_encoder.current_frame = None; + assert!(frame_encoder.encode_padding(&mut buf).is_err()); + } + /// UT test cases for `FrameEncoder` encoding data frame. /// /// # Brief diff --git a/ylong_http/src/request/uri/mod.rs b/ylong_http/src/request/uri/mod.rs index 6358715207d2306669e874531a6e798e385129c4..78818e26fc7ae1596a42eb7d84a82e80b61db23f 100644 --- a/ylong_http/src/request/uri/mod.rs +++ b/ylong_http/src/request/uri/mod.rs @@ -90,7 +90,7 @@ pub struct Uri { } impl Uri { - /// Creates a HTTP-compliant default `Uri` with `Path` set to '/'. + /// Creates an HTTP-compliant default `Uri` with `Path` set to '/'. pub(crate) fn http() -> Uri { Uri { scheme: None, @@ -232,7 +232,12 @@ impl Uri { let (scheme, rest) = scheme_token(bytes)?; let (authority, rest) = authority_token(rest)?; let (path, rest) = path_token(rest)?; - let query = query_token(rest)?; + let query = match rest.first() { + None => None, + Some(&b'?') => query_token(&rest[1..])?, + Some(&b'#') => None, + _ => return Err(InvalidUri::UriMissQuery.into()), + }; let result = Uri { scheme, authority, @@ -1084,7 +1089,8 @@ fn path_token(bytes: &[u8]) -> Result<(Option, &[u8]), InvalidUri> { break; } _ => { - if !URI_VALUE_BYTES[b as usize] { + // "{} The three characters that might be used were previously percent-encoding. + if !PATH_AND_QUERY_BYTES[b as usize] { return Err(InvalidUri::InvalidByte); } } @@ -1098,15 +1104,10 @@ fn path_token(bytes: &[u8]) -> Result<(Option, &[u8]), InvalidUri> { } } -fn query_token(s: &[u8]) -> Result, InvalidUri> { - if s.is_empty() { +fn query_token(bytes: &[u8]) -> Result, InvalidUri> { + if bytes.is_empty() { return Ok(None); } - let bytes = if s[0].eq_ignore_ascii_case(&b'?') { - &s[1..] - } else { - s - }; let mut end = bytes.len(); for (i, &b) in bytes.iter().enumerate() { match b { @@ -1114,8 +1115,10 @@ fn query_token(s: &[u8]) -> Result, InvalidUri> { end = i; break; } + // ?| ` | { | } + 0x3F | 0x60 | 0x7B | 0x7D => {} _ => { - if !URI_VALUE_BYTES[b as usize] { + if !PATH_AND_QUERY_BYTES[b as usize] { return Err(InvalidUri::InvalidByte); } } @@ -1205,6 +1208,38 @@ const URI_VALUE_BYTES: [bool; 256] = { ] }; +#[rustfmt::skip] +const PATH_AND_QUERY_BYTES: [bool; 256] = { + const __: bool = false; + const TT: bool = true; + [ +// \0 HT LF CR + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // F + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 1F +// \w ! " # $ % & ' ( ) * + , - . / + __, TT, __, __, TT, TT, TT, TT, TT, TT, TT, TT, TT, TT, TT, TT, // 2F +// 0 1 2 3 4 5 6 7 8 9 : ; < = > ? + TT, TT, TT, TT, TT, TT, TT, TT, TT, TT, TT, TT, __, TT, __, __, // 3F +// @ A B C D E F G H I J K L M N O + TT, TT, TT, TT, TT, TT, TT, TT, TT, TT, TT, TT, TT, TT, TT, TT, // 4F +// P Q R S T U V W X Y Z [ \ ] ^ _ + TT, TT, TT, TT, TT, TT, TT, TT, TT, TT, TT, TT, TT, TT, TT, TT, // 5F +// ` a b c d e f g h i j k l m n o + __, TT, TT, TT, TT, TT, TT, TT, TT, TT, TT, TT, TT, TT, TT, TT, // 6F +// p q r s t u v w x y z { | } ~ del + TT, TT, TT, TT, TT, TT, TT, TT, TT, TT, TT, __, TT, __, TT, __, // 7F +// Expand ascii + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 8F + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 9F + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // AF + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // BF + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // CF + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // DF + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // EF + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // FF + ] +}; + #[cfg(test)] mod ut_uri { use super::{InvalidUri, Scheme, Uri, UriBuilder}; @@ -1417,11 +1452,6 @@ mod ut_uri { Err(HttpError::from(ErrorKind::Uri(InvalidUri::InvalidByte))), ); - uri_test_case!( - br"https://www.example.com:80/message/email?name='\^'", - Err(HttpError::from(ErrorKind::Uri(InvalidUri::InvalidByte))), - ); - uri_test_case!( br#"https:/www.example.com:80/message/email?name=arya"#, Err(HttpError::from(ErrorKind::Uri(InvalidUri::UriMissScheme))), @@ -1611,4 +1641,15 @@ mod ut_uri { let uri = Uri::from_bytes(b"http://example.com:8080").unwrap(); assert_eq!(uri.path_and_query(), None); } + + /// UT test cases for `Uri::path_and_query`. + /// + /// # Brief + /// 1. Creates Uri by calling `Uri::path_and_query()`. + /// 2. Checks that the query containing the {} symbol parses properly. + #[test] + fn ut_uri_json_query() { + let uri = Uri::from_bytes(b"http://example.com:8080/foo?a=1{WEBO_TEST}").unwrap(); + assert_eq!(uri.path_and_query().unwrap(), "/foo?a=1{WEBO_TEST}"); + } }