• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2023 Huawei Device Co., Ltd.
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 //     http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
14 //! 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