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}