1 // Copyright (c) 2023 Huawei Device Co., Ltd. 2 // Licensed under the Apache License, Version 2.0 (the "License"); 3 // you may not use this file except in compliance with the License. 4 // You may obtain a copy of the License at 5 // 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 //! HTTP [`Method`]. 15 //! 16 //! The request method token is the primary source of request semantics; 17 //! it indicates the purpose for which the client has made this request and what 18 //! is expected by the client as a successful result. 19 //! 20 //! [`Method`]: https://httpwg.org/specs/rfc9110.html#methods 21 //! 22 //! # Examples 23 //! 24 //! ``` 25 //! use ylong_http::request::method::Method; 26 //! 27 //! assert_eq!(Method::GET.as_str(), "GET"); 28 //! ``` 29 30 use core::convert::TryFrom; 31 32 use crate::error::{ErrorKind, HttpError}; 33 34 /// HTTP `Method` implementation. 35 /// 36 /// # Examples 37 /// 38 /// ``` 39 /// use ylong_http::request::method::Method; 40 /// 41 /// assert_eq!(Method::GET.as_str(), "GET"); 42 /// ``` 43 #[derive(Clone, Debug, Eq, PartialEq)] 44 pub struct Method(Inner); 45 46 impl Method { 47 /// Transfer a current representation of the target resource. 48 /// 49 /// See [`RFC9110 9.3.1`] for more details. 50 /// 51 /// [`RFC9110 9.3.1`]: (https://httpwg.org/specs/rfc9110.html#GET) 52 pub const GET: Self = Self(Inner::Get); 53 54 /// Same as `GET`, but do not transfer the response content. 55 /// 56 /// See [`RFC9110 9.3.2`] for more details. 57 /// 58 /// [`RFC9110 9.3.2`]: https://httpwg.org/specs/rfc9110.html#HEAD 59 pub const HEAD: Self = Self(Inner::Head); 60 61 /// Perform resource-specific processing on the request content. 62 /// 63 /// See [`RFC9110 9.3.3`] for more details. 64 /// 65 /// [`RFC9110 9.3.3`]: https://httpwg.org/specs/rfc9110.html#POST 66 pub const POST: Self = Self(Inner::Post); 67 68 /// Replace all current representations of the target resource with the 69 /// request content. 70 /// 71 /// See [`RFC9110 9.3.4`] for more details. 72 /// 73 /// [`RFC9110 9.3.4`]: https://httpwg.org/specs/rfc9110.html#PUT 74 pub const PUT: Self = Self(Inner::Put); 75 76 /// Remove all current representations of the target resource. 77 /// 78 /// See [`RFC9110 9.3.5`] for more details. 79 /// 80 /// [`RFC9110 9.3.5`]: https://httpwg.org/specs/rfc9110.html#DELETE 81 pub const DELETE: Self = Self(Inner::Delete); 82 83 /// Establish a tunnel to the server identified by the target resource. 84 /// 85 /// See [`RFC9110 9.3.6`] for more details. 86 /// 87 /// [`RFC9110 9.3.6`]: https://httpwg.org/specs/rfc9110.html#CONNECT 88 pub const CONNECT: Self = Self(Inner::Connect); 89 90 /// Describe the communication options for the target resource. 91 /// 92 /// See [`RFC9110 9.3.7`] for more details. 93 /// 94 /// [`RFC9110 9.3.7`]: https://httpwg.org/specs/rfc9110.html#OPTIONS 95 pub const OPTIONS: Self = Self(Inner::Options); 96 97 /// Perform a message loop-back test along the path to the target resource. 98 /// 99 /// See [`RFC9110 9.3.8`] for more details. 100 /// 101 /// [`RFC9110 9.3.8`]: https://httpwg.org/specs/rfc9110.html#TRACE 102 pub const TRACE: Self = Self(Inner::Trace); 103 104 /// Tries converting &[u8] to `Method`. Only uppercase letters are 105 /// supported. 106 /// 107 /// # Examples 108 /// 109 /// ``` 110 /// use ylong_http::request::method::Method; 111 /// 112 /// let method = Method::from_bytes(b"GET").unwrap(); 113 /// assert_eq!(method.as_str(), "GET"); 114 /// ``` from_bytes(bytes: &[u8]) -> Result<Method, HttpError>115 pub fn from_bytes(bytes: &[u8]) -> Result<Method, HttpError> { 116 if bytes.len() < 3 || bytes.len() > 7 { 117 return Err(ErrorKind::InvalidInput.into()); 118 } 119 match bytes[0] { 120 b'G' if b"ET" == &bytes[1..] => Ok(Method::GET), 121 b'P' => match bytes[1] { 122 b'U' if b"T" == &bytes[2..] => Ok(Method::PUT), 123 b'O' if b"ST" == &bytes[2..] => Ok(Method::POST), 124 _ => Err(ErrorKind::InvalidInput.into()), 125 }, 126 b'H' if b"EAD" == &bytes[1..] => Ok(Method::HEAD), 127 b'T' if b"RACE" == &bytes[1..] => Ok(Method::TRACE), 128 b'D' if b"ELETE" == &bytes[1..] => Ok(Method::DELETE), 129 b'O' if b"PTIONS" == &bytes[1..] => Ok(Method::OPTIONS), 130 b'C' if b"ONNECT" == &bytes[1..] => Ok(Method::CONNECT), 131 _ => Err(ErrorKind::InvalidInput.into()), 132 } 133 } 134 135 /// Converts `Method` to `&str` in uppercase. 136 /// 137 /// # Examples 138 /// ``` 139 /// use ylong_http::request::method::Method; 140 /// 141 /// assert_eq!(Method::GET.as_str(), "GET"); 142 /// ``` as_str(&self) -> &str143 pub fn as_str(&self) -> &str { 144 match self.0 { 145 Inner::Get => "GET", 146 Inner::Head => "HEAD", 147 Inner::Post => "POST", 148 Inner::Put => "PUT", 149 Inner::Delete => "DELETE", 150 Inner::Options => "OPTIONS", 151 Inner::Trace => "TRACE", 152 Inner::Connect => "CONNECT", 153 } 154 } 155 } 156 157 #[derive(Clone, Debug, Eq, PartialEq)] 158 enum Inner { 159 Get, 160 Head, 161 Post, 162 Put, 163 Delete, 164 Connect, 165 Options, 166 Trace, 167 } 168 169 impl<'a> TryFrom<&'a [u8]> for Method { 170 type Error = HttpError; 171 try_from(t: &'a [u8]) -> Result<Self, Self::Error>172 fn try_from(t: &'a [u8]) -> Result<Self, Self::Error> { 173 Self::from_bytes(t) 174 } 175 } 176 177 impl<'a> TryFrom<&'a str> for Method { 178 type Error = HttpError; 179 try_from(t: &'a str) -> Result<Self, Self::Error>180 fn try_from(t: &'a str) -> Result<Self, Self::Error> { 181 Self::from_bytes(t.as_bytes()) 182 } 183 } 184 185 #[cfg(test)] 186 mod ut_method { 187 use super::Method; 188 use crate::error::{ErrorKind, HttpError}; 189 190 /// UT test cases for `Method::as_str`. 191 /// 192 /// # Brief 193 /// 1. Calls `as_str` for all method kinds. 194 /// 2. Checks the results. 195 #[test] ut_method_as_str()196 fn ut_method_as_str() { 197 assert_eq!(Method::GET.as_str(), "GET"); 198 assert_eq!(Method::HEAD.as_str(), "HEAD"); 199 assert_eq!(Method::POST.as_str(), "POST"); 200 assert_eq!(Method::PUT.as_str(), "PUT"); 201 assert_eq!(Method::DELETE.as_str(), "DELETE"); 202 assert_eq!(Method::OPTIONS.as_str(), "OPTIONS"); 203 assert_eq!(Method::TRACE.as_str(), "TRACE"); 204 assert_eq!(Method::CONNECT.as_str(), "CONNECT"); 205 } 206 207 /// UT test cases for `Method::from_bytes`. 208 /// 209 /// # Brief 210 /// 1. Calls `from_bytes` and pass in various types of parameters. 211 /// 2. Checks the results. 212 #[test] ut_method_from_bytes()213 fn ut_method_from_bytes() { 214 // Normal Test Cases: 215 assert_eq!(Method::from_bytes(b"GET").unwrap(), Method::GET); 216 assert_eq!(Method::from_bytes(b"HEAD").unwrap(), Method::HEAD); 217 assert_eq!(Method::from_bytes(b"POST").unwrap(), Method::POST); 218 assert_eq!(Method::from_bytes(b"PUT").unwrap(), Method::PUT); 219 assert_eq!(Method::from_bytes(b"DELETE").unwrap(), Method::DELETE); 220 assert_eq!(Method::from_bytes(b"OPTIONS").unwrap(), Method::OPTIONS); 221 assert_eq!(Method::from_bytes(b"TRACE").unwrap(), Method::TRACE); 222 assert_eq!(Method::from_bytes(b"CONNECT").unwrap(), Method::CONNECT); 223 224 // Exception Test Cases: 225 // 1. Empty bytes slice. 226 assert_eq!( 227 Method::from_bytes(b""), 228 Err(HttpError::from(ErrorKind::InvalidInput)) 229 ); 230 231 // 2. The length of bytes slice is less than 3. 232 assert_eq!( 233 Method::from_bytes(b"G"), 234 Err(HttpError::from(ErrorKind::InvalidInput)) 235 ); 236 237 // 3. The length of bytes slice is more than 7. 238 assert_eq!( 239 Method::from_bytes(b"CONNECTT"), 240 Err(HttpError::from(ErrorKind::InvalidInput)) 241 ); 242 243 // 4. Mixed case letters inside the bytes slice. 244 assert_eq!( 245 Method::from_bytes(b"Get"), 246 Err(HttpError::from(ErrorKind::InvalidInput)) 247 ); 248 249 // 5. Other error branch coverage test cases. 250 assert_eq!( 251 Method::from_bytes(b"PATC"), 252 Err(HttpError::from(ErrorKind::InvalidInput)) 253 ); 254 assert_eq!( 255 Method::from_bytes(b"PATCHH"), 256 Err(HttpError::from(ErrorKind::InvalidInput)) 257 ); 258 assert_eq!( 259 Method::from_bytes(b"ABCDEFG"), 260 Err(HttpError::from(ErrorKind::InvalidInput)) 261 ); 262 } 263 } 264