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}