http_types/
method.rs

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