trillium_http/
method.rs

1// originally from https://github.com/http-rs/http-types/blob/main/src/method.rs
2use std::{
3    fmt::{self, Display},
4    str::FromStr,
5};
6
7/// HTTP request methods.
8///
9/// See also [Mozilla's documentation][Mozilla docs], the [RFC7231, Section 4][] and
10/// [IANA's Hypertext Transfer Protocol (HTTP) Method Registry][HTTP Method Registry].
11///
12/// [Mozilla docs]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods
13/// [RFC7231, Section 4]: https://tools.ietf.org/html/rfc7231#section-4
14/// [HTTP Method Registry]: https://www.iana.org/assignments/http-methods/http-methods.xhtml
15#[non_exhaustive]
16#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd)]
17#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
18pub enum Method {
19    /// The ACL method modifies the access control list (which can be read via the DAV:acl
20    /// property) of a resource.
21    ///
22    /// See [RFC3744, Section 8.1][].
23    ///
24    /// [RFC3744, Section 8.1]: https://tools.ietf.org/html/rfc3744#section-8.1
25    Acl,
26
27    /// A collection can be placed under baseline control with a BASELINE-CONTROL request.
28    ///
29    /// See [RFC3253, Section 12.6][].
30    ///
31    /// [RFC3253, Section 12.6]: https://tools.ietf.org/html/rfc3253#section-12.6
32    BaselineControl,
33
34    /// The BIND method modifies the collection identified by the Request- URI, by adding a new
35    /// binding from the segment specified in the BIND body to the resource identified in the BIND
36    /// body.
37    ///
38    /// See [RFC5842, Section 4][].
39    ///
40    /// [RFC5842, Section 4]: https://tools.ietf.org/html/rfc5842#section-4
41    Bind,
42
43    /// A CHECKIN request can be applied to a checked-out version-controlled resource to produce a
44    /// new version whose content and dead properties are copied from the checked-out resource.
45    ///
46    /// See [RFC3253, Section 4.4][] and [RFC3253, Section 9.4][].
47    ///
48    /// [RFC3253, Section 4.4]: https://tools.ietf.org/html/rfc3253#section-4.4
49    /// [RFC3253, Section 9.4]: https://tools.ietf.org/html/rfc3253#section-9.4
50    Checkin,
51
52    /// A CHECKOUT request can be applied to a checked-in version-controlled resource to allow
53    /// modifications to the content and dead properties of that version-controlled resource.
54    ///
55    /// See [RFC3253, Section 4.3][] and [RFC3253, Section 8.8][].
56    ///
57    /// [RFC3253, Section 4.3]: https://tools.ietf.org/html/rfc3253#section-4.3
58    /// [RFC3253, Section 8.8]: https://tools.ietf.org/html/rfc3253#section-8.8
59    Checkout,
60
61    /// The CONNECT method requests that the recipient establish a tunnel to the destination origin
62    /// server identified by the request-target and, if successful, thereafter restrict its
63    /// behavior to blind forwarding of packets, in both directions, until the tunnel is closed.
64    ///
65    /// See [RFC7231, Section 4.3.6][].
66    ///
67    /// [RFC7231, Section 4.3.6]: https://tools.ietf.org/html/rfc7231#section-4.3.6
68    Connect,
69
70    /// The COPY method creates a duplicate of the source resource identified by the Request-URI,
71    /// in the destination resource identified by the URI in the Destination header.
72    ///
73    /// See [RFC4918, Section 9.8][].
74    ///
75    /// [RFC4918, Section 9.8]: https://tools.ietf.org/html/rfc4918#section-9.8
76    Copy,
77
78    /// The DELETE method requests that the origin server remove the association between the target
79    /// resource and its current functionality.
80    ///
81    /// See [RFC7231, Section 4.3.5][].
82    ///
83    /// [RFC7231, Section 4.3.5]: https://tools.ietf.org/html/rfc7231#section-4.3.5
84    Delete,
85
86    /// The GET method requests transfer of a current selected representation for the target
87    /// resource.
88    ///
89    /// See [RFC7231, Section 4.3.1][].
90    ///
91    /// [RFC7231, Section 4.3.1]: https://tools.ietf.org/html/rfc7231#section-4.3.1
92    Get,
93
94    /// The HEAD method is identical to GET except that the server MUST NOT send a message body in
95    /// the response.
96    ///
97    /// See [RFC7231, Section 4.3.2][].
98    ///
99    /// [RFC7231, Section 4.3.2]: https://tools.ietf.org/html/rfc7231#section-4.3.2
100    Head,
101
102    /// A LABEL request can be applied to a version to modify the labels that select that version.
103    ///
104    /// See [RFC3253, Section 8.2][].
105    ///
106    /// [RFC3253, Section 8.2]: https://tools.ietf.org/html/rfc3253#section-8.2
107    Label,
108
109    /// The LINK method establishes one or more Link relationships between the existing resource
110    /// identified by the Request-URI and other existing resources.
111    ///
112    /// See [RFC2068, Section 19.6.1.2][].
113    ///
114    /// [RFC2068, Section 19.6.1.2]: https://tools.ietf.org/html/rfc2068#section-19.6.1.2
115    Link,
116
117    /// The LOCK method is used to take out a lock of any access type and to refresh an existing
118    /// lock.
119    ///
120    /// See [RFC4918, Section 9.10][].
121    ///
122    /// [RFC4918, Section 9.10]: https://tools.ietf.org/html/rfc4918#section-9.10
123    Lock,
124
125    /// The MERGE method performs the logical merge of a specified version (the "merge source")
126    /// into a specified version-controlled resource (the "merge target").
127    ///
128    /// See [RFC3253, Section 11.2][].
129    ///
130    /// [RFC3253, Section 11.2]: https://tools.ietf.org/html/rfc3253#section-11.2
131    Merge,
132
133    /// A MKACTIVITY request creates a new activity resource.
134    ///
135    /// See [RFC3253, Section 13.5].
136    ///
137    /// [RFC3253, Section 13.5]: https://tools.ietf.org/html/rfc3253#section-13.5
138    MkActivity,
139
140    /// An HTTP request using the MKCALENDAR method creates a new calendar collection resource.
141    ///
142    /// See [RFC4791, Section 5.3.1][] and [RFC8144, Section 2.3][].
143    ///
144    /// [RFC4791, Section 5.3.1]: https://tools.ietf.org/html/rfc4791#section-5.3.1
145    /// [RFC8144, Section 2.3]: https://tools.ietf.org/html/rfc8144#section-2.3
146    MkCalendar,
147
148    /// MKCOL creates a new collection resource at the location specified by the Request-URI.
149    ///
150    /// See [RFC4918, Section 9.3][], [RFC5689, Section 3][] and [RFC8144, Section 2.3][].
151    ///
152    /// [RFC4918, Section 9.3]: https://tools.ietf.org/html/rfc4918#section-9.3
153    /// [RFC5689, Section 3]: https://tools.ietf.org/html/rfc5689#section-3
154    /// [RFC8144, Section 2.3]: https://tools.ietf.org/html/rfc5689#section-3
155    MkCol,
156
157    /// The MKREDIRECTREF method requests the creation of a redirect reference resource.
158    ///
159    /// See [RFC4437, Section 6][].
160    ///
161    /// [RFC4437, Section 6]: https://tools.ietf.org/html/rfc4437#section-6
162    MkRedirectRef,
163
164    /// A MKWORKSPACE request creates a new workspace resource.
165    ///
166    /// See [RFC3253, Section 6.3][].
167    ///
168    /// [RFC3253, Section 6.3]: https://tools.ietf.org/html/rfc3253#section-6.3
169    MkWorkspace,
170
171    /// The MOVE operation on a non-collection resource is the logical equivalent of a copy (COPY),
172    /// followed by consistency maintenance processing, followed by a delete of the source, where
173    /// all three actions are performed in a single operation.
174    ///
175    /// See [RFC4918, Section 9.9][].
176    ///
177    /// [RFC4918, Section 9.9]: https://tools.ietf.org/html/rfc4918#section-9.9
178    Move,
179
180    /// The OPTIONS method requests information about the communication options available for the
181    /// target resource, at either the origin server or an intervening intermediary.
182    ///
183    /// See [RFC7231, Section 4.3.7][].
184    ///
185    /// [RFC7231, Section 4.3.7]: https://tools.ietf.org/html/rfc7231#section-4.3.7
186    Options,
187
188    /// The ORDERPATCH method is used to change the ordering semantics of a collection, to change
189    /// the order of the collection's members in the ordering, or both.
190    ///
191    /// See [RFC3648, Section 7][].
192    ///
193    /// [RFC3648, Section 7]: https://tools.ietf.org/html/rfc3648#section-7
194    OrderPatch,
195
196    /// The PATCH method requests that a set of changes described in the request entity be applied
197    /// to the resource identified by the Request- URI.
198    ///
199    /// See [RFC5789, Section 2][].
200    ///
201    /// [RFC5789, Section 2]: https://tools.ietf.org/html/rfc5789#section-2
202    Patch,
203
204    /// The POST method requests that the target resource process the representation enclosed in
205    /// the request according to the resource's own specific semantics.
206    ///
207    /// For example, POST is used for the following functions (among others):
208    ///
209    ///   - Providing a block of data, such as the fields entered into an HTML form, to a
210    ///     data-handling process;
211    ///   - Posting a message to a bulletin board, newsgroup, mailing list, blog, or similar group
212    ///     of articles;
213    ///   - Creating a new resource that has yet to be identified by the origin server; and
214    ///   - Appending data to a resource's existing representation(s).
215    ///
216    /// See [RFC7231, Section 4.3.3][].
217    ///
218    /// [RFC7231, Section 4.3.3]: https://tools.ietf.org/html/rfc7231#section-4.3.3
219    Post,
220
221    /// This method is never used by an actual client. This method will appear to be used when an
222    /// HTTP/1.1 server or intermediary attempts to parse an HTTP/2 connection preface.
223    ///
224    /// See [RFC7540, Section 3.5][] and [RFC7540, Section 11.6][]
225    ///
226    /// [RFC7540, Section 3.5]: https://tools.ietf.org/html/rfc7540#section-3.5
227    /// [RFC7540, Section 11.6]: https://tools.ietf.org/html/rfc7540#section-11.6
228    Pri,
229
230    /// The PROPFIND method retrieves properties defined on the resource identified by the
231    /// Request-URI.
232    ///
233    /// See [RFC4918, Section 9.1][] and [RFC8144, Section 2.1][].
234    ///
235    /// [RFC4918, Section 9.1]: https://tools.ietf.org/html/rfc4918#section-9.1
236    /// [RFC8144, Section 2.1]: https://tools.ietf.org/html/rfc8144#section-2.1
237    PropFind,
238
239    /// The PROPPATCH method processes instructions specified in the request body to set and/or
240    /// remove properties defined on the resource identified by the Request-URI.
241    ///
242    /// See [RFC4918, Section 9.2][] and [RFC8144, Section 2.2][].
243    ///
244    /// [RFC4918, Section 9.2]: https://tools.ietf.org/html/rfc4918#section-9.2
245    /// [RFC8144, Section 2.2]: https://tools.ietf.org/html/rfc8144#section-2.2
246    PropPatch,
247
248    /// The PUT method requests that the state of the target resource be created or replaced with
249    /// the state defined by the representation enclosed in the request message payload.
250    ///
251    /// See [RFC7231, Section 4.3.4][].
252    ///
253    /// [RFC7231, Section 4.3.4]: https://tools.ietf.org/html/rfc7231#section-4.3.4
254    Put,
255
256    /// The QUERY method is used to initiate a server-side query. Unlike the HTTP GET method, which
257    /// requests that a server return a representation of the resource identified by the target URI,
258    /// the QUERY method is used to ask the server to perform a query operation (described by the
259    /// request payload) over some set of data scoped to the effective request URI.  See
260    /// [draft-ietf-httpbis-safe-method-w-body-03][]
261    ///
262    /// NOTE: As of January 2023, this draft has expired.
263    ///
264    /// [draft-ietf-httpbis-safe-method-w-body-03]: https://www.ietf.org/archive/id/draft-ietf-httpbis-safe-method-w-body-03.html
265    Query,
266
267    /// The REBIND method removes a binding to a resource from a collection, and adds a binding to
268    /// that resource into the collection identified by the Request-URI.
269    ///
270    /// See [RFC5842, Section 6][].
271    ///
272    /// [RFC5842, Section 6]: https://tools.ietf.org/html/rfc5842#section-6
273    Rebind,
274
275    /// A REPORT request is an extensible mechanism for obtaining information about a resource.
276    ///
277    /// See [RFC3253, Section 3.6][] and [RFC8144, Section 2.1][].
278    ///
279    /// [RFC3253, Section 3.6]: https://tools.ietf.org/html/rfc3253#section-3.6
280    /// [RFC8144, Section 2.1]: https://tools.ietf.org/html/rfc8144#section-2.1
281    Report,
282
283    /// The client invokes the SEARCH method to initiate a server-side search. The body of the
284    /// request defines the query.
285    ///
286    /// See [RFC5323, Section 2][].
287    ///
288    /// [RFC5323, Section 2]: https://tools.ietf.org/html/rfc5323#section-2
289    Search,
290
291    /// The TRACE method requests a remote, application-level loop-back of the request message.
292    ///
293    /// See [RFC7231, Section 4.3.8][].
294    ///
295    /// [RFC7231, Section 4.3.8]: https://tools.ietf.org/html/rfc7231#section-4.3.8
296    Trace,
297
298    /// The UNBIND method modifies the collection identified by the Request- URI by removing the
299    /// binding identified by the segment specified in the UNBIND body.
300    ///
301    /// See [RFC5842, Section 5][].
302    ///
303    /// [RFC5842, Section 5]: https://tools.ietf.org/html/rfc5842#section-5
304    Unbind,
305
306    /// An UNCHECKOUT request can be applied to a checked-out version-controlled resource to cancel
307    /// the CHECKOUT and restore the pre-CHECKOUT state of the version-controlled resource.
308    ///
309    /// See [RFC3253, Section 4.5][].
310    ///
311    /// [RFC3253, Section 4.5]: https://tools.ietf.org/html/rfc3253#section-4.5
312    Uncheckout,
313
314    /// The UNLINK method removes one or more Link relationships from the existing resource
315    /// identified by the Request-URI.
316    ///
317    /// See [RFC2068, Section 19.6.1.3][].
318    ///
319    /// [RFC2068, Section 19.6.1.3]: https://tools.ietf.org/html/rfc2068#section-19.6.1.3
320    Unlink,
321
322    /// The UNLOCK method removes the lock identified by the lock token in the Lock-Token request
323    /// header.
324    ///
325    /// See [RFC4918, Section 9.11][].
326    ///
327    /// [RFC4918, Section 9.11]: https://tools.ietf.org/html/rfc4918#section-9.11
328    Unlock,
329
330    /// The UPDATE method modifies the content and dead properties of a checked-in
331    /// version-controlled resource (the "update target") to be those of a specified version (the
332    /// "update source") from the version history of that version-controlled resource.
333    ///
334    /// See [RFC3253, Section 7.1][].
335    ///
336    /// [RFC3253, Section 7.1]: https://tools.ietf.org/html/rfc3253#section-7.1
337    Update,
338
339    /// The UPDATEREDIRECTREF method requests the update of a redirect reference resource.
340    ///
341    /// See [RFC4437, Section 7][].
342    ///
343    /// [RFC4437, Section 7]: https://tools.ietf.org/html/rfc4437#section-7
344    UpdateRedirectRef,
345
346    /// A VERSION-CONTROL request can be used to create a version-controlled resource at the
347    /// request-URL.
348    ///
349    /// See [RFC3253, Section 3.5].
350    ///
351    /// [RFC3253, Section 3.5]: https://tools.ietf.org/html/rfc3253#section-3.5
352    VersionControl,
353}
354
355impl Method {
356    /// Predicate that returns whether the method is "safe."
357    ///
358    /// > Request methods are considered "safe" if their defined semantics are
359    /// > essentially read-only; i.e., the client does not request, and does
360    /// > not expect, any state change on the origin server as a result of
361    /// > applying a safe method to a target resource.
362    ///
363    /// -- [rfc7231§4.2.1](https://tools.ietf.org/html/rfc7231#section-4.2.1)
364    pub const fn is_safe(&self) -> bool {
365        matches!(
366            self,
367            Method::Get
368                | Method::Head
369                | Method::Options
370                | Method::Pri
371                | Method::PropFind
372                | Method::Query
373                | Method::Report
374                | Method::Search
375                | Method::Trace
376        )
377    }
378
379    /// predicate that returns whether this method is considered "idempotent".
380    ///
381    /// > A request method is considered "idempotent" if the intended effect on
382    /// > the server of multiple identical requests with that method is the
383    /// > same as the effect for a single such request.
384    ///
385    /// -- [rfc7231§4.2.2](https://tools.ietf.org/html/rfc7231#section-4.2.2)
386    pub const fn is_idempotent(&self) -> bool {
387        self.is_safe() || matches!(self, Method::Put | Method::Delete)
388    }
389
390    /// returns the static str representation of this method
391    pub const fn as_str(&self) -> &'static str {
392        match self {
393            Self::Acl => "ACL",
394            Self::BaselineControl => "BASELINE-CONTROL",
395            Self::Bind => "BIND",
396            Self::Checkin => "CHECKIN",
397            Self::Checkout => "CHECKOUT",
398            Self::Connect => "CONNECT",
399            Self::Copy => "COPY",
400            Self::Delete => "DELETE",
401            Self::Get => "GET",
402            Self::Head => "HEAD",
403            Self::Label => "LABEL",
404            Self::Link => "LINK",
405            Self::Lock => "LOCK",
406            Self::Merge => "MERGE",
407            Self::MkActivity => "MKACTIVITY",
408            Self::MkCalendar => "MKCALENDAR",
409            Self::MkCol => "MKCOL",
410            Self::MkRedirectRef => "MKREDIRECTREF",
411            Self::MkWorkspace => "MKWORKSPACE",
412            Self::Move => "MOVE",
413            Self::Options => "OPTIONS",
414            Self::OrderPatch => "ORDERPATCH",
415            Self::Patch => "PATCH",
416            Self::Post => "POST",
417            Self::Pri => "PRI",
418            Self::PropFind => "PROPFIND",
419            Self::PropPatch => "PROPPATCH",
420            Self::Put => "PUT",
421            Self::Query => "QUERY",
422            Self::Rebind => "REBIND",
423            Self::Report => "REPORT",
424            Self::Search => "SEARCH",
425            Self::Trace => "TRACE",
426            Self::Unbind => "UNBIND",
427            Self::Uncheckout => "UNCHECKOUT",
428            Self::Unlink => "UNLINK",
429            Self::Unlock => "UNLOCK",
430            Self::Update => "UPDATE",
431            Self::UpdateRedirectRef => "UPDATEREDIRECTREF",
432            Self::VersionControl => "VERSION-CONTROL",
433        }
434    }
435}
436
437impl Display for Method {
438    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
439        f.write_str(self.as_ref())
440    }
441}
442
443impl FromStr for Method {
444    type Err = crate::Error;
445
446    fn from_str(s: &str) -> Result<Self, Self::Err> {
447        match &*s.to_ascii_uppercase() {
448            "GET" => Ok(Self::Get),
449            "OPTIONS" => Ok(Self::Options),
450            "POST" => Ok(Self::Post),
451            "PUT" => Ok(Self::Put),
452            "PATCH" => Ok(Self::Patch),
453            "DELETE" => Ok(Self::Delete),
454            "HEAD" => Ok(Self::Head),
455            "ACL" => Ok(Self::Acl),
456            "BASELINE-CONTROL" => Ok(Self::BaselineControl),
457            "BIND" => Ok(Self::Bind),
458            "CHECKIN" => Ok(Self::Checkin),
459            "CHECKOUT" => Ok(Self::Checkout),
460            "CONNECT" => Ok(Self::Connect),
461            "COPY" => Ok(Self::Copy),
462            "LABEL" => Ok(Self::Label),
463            "LINK" => Ok(Self::Link),
464            "LOCK" => Ok(Self::Lock),
465            "MERGE" => Ok(Self::Merge),
466            "MKACTIVITY" => Ok(Self::MkActivity),
467            "MKCALENDAR" => Ok(Self::MkCalendar),
468            "MKCOL" => Ok(Self::MkCol),
469            "MKREDIRECTREF" => Ok(Self::MkRedirectRef),
470            "MKWORKSPACE" => Ok(Self::MkWorkspace),
471            "MOVE" => Ok(Self::Move),
472            "ORDERPATCH" => Ok(Self::OrderPatch),
473            "PRI" => Ok(Self::Pri),
474            "PROPFIND" => Ok(Self::PropFind),
475            "PROPPATCH" => Ok(Self::PropPatch),
476            "QUERY" => Ok(Self::Query),
477            "REBIND" => Ok(Self::Rebind),
478            "REPORT" => Ok(Self::Report),
479            "SEARCH" => Ok(Self::Search),
480            "TRACE" => Ok(Self::Trace),
481            "UNBIND" => Ok(Self::Unbind),
482            "UNCHECKOUT" => Ok(Self::Uncheckout),
483            "UNLINK" => Ok(Self::Unlink),
484            "UNLOCK" => Ok(Self::Unlock),
485            "UPDATE" => Ok(Self::Update),
486            "UPDATEREDIRECTREF" => Ok(Self::UpdateRedirectRef),
487            "VERSION-CONTROL" => Ok(Self::VersionControl),
488            _ => Err(crate::Error::UnrecognizedMethod(
489                "Invalid HTTP method".into(),
490            )),
491        }
492    }
493}
494
495impl<'a> TryFrom<&'a str> for Method {
496    type Error = crate::Error;
497
498    fn try_from(value: &'a str) -> Result<Self, Self::Error> {
499        Self::from_str(value)
500    }
501}
502
503impl AsRef<str> for Method {
504    fn as_ref(&self) -> &str {
505        self.as_str()
506    }
507}
508
509#[cfg(test)]
510mod test {
511    use super::Method;
512    use std::collections::HashSet;
513
514    #[test]
515    fn names() -> Result<(), crate::Error> {
516        let method_names = [
517            "ACL",
518            "BASELINE-CONTROL",
519            "BIND",
520            "CHECKIN",
521            "CHECKOUT",
522            "CONNECT",
523            "COPY",
524            "DELETE",
525            "GET",
526            "HEAD",
527            "LABEL",
528            "LINK",
529            "LOCK",
530            "MERGE",
531            "MKACTIVITY",
532            "MKCALENDAR",
533            "MKCOL",
534            "MKREDIRECTREF",
535            "MKWORKSPACE",
536            "MOVE",
537            "OPTIONS",
538            "ORDERPATCH",
539            "PATCH",
540            "POST",
541            "PRI",
542            "PROPFIND",
543            "PROPPATCH",
544            "PUT",
545            "QUERY",
546            "REBIND",
547            "REPORT",
548            "SEARCH",
549            "TRACE",
550            "UNBIND",
551            "UNCHECKOUT",
552            "UNLINK",
553            "UNLOCK",
554            "UPDATE",
555            "UPDATEREDIRECTREF",
556            "VERSION-CONTROL",
557        ];
558
559        let methods = method_names
560            .iter()
561            .map(|s| s.parse::<Method>())
562            .collect::<Result<HashSet<_>, _>>()?;
563
564        // check that we didn't accidentally map two methods to the same variant
565        assert_eq!(methods.len(), method_names.len());
566
567        // check that a method's name and the name it is parsed from match
568        for method in methods {
569            assert_eq!(method.as_ref().parse::<Method>()?, method);
570        }
571
572        Ok(())
573    }
574}