From 415cfae04fc70ba3fe8922929a251d0e327623ec Mon Sep 17 00:00:00 2001
From: AlexProgrammerDE <40795980+AlexProgrammerDE@users.noreply.github.com>
Date: Wed, 19 Mar 2025 16:46:24 +0100
Subject: [PATCH 1/2] Add additional HTTP Methods
---
.../linecorp/armeria/common/HttpMethod.java | 331 +++++++++++++++---
1 file changed, 286 insertions(+), 45 deletions(-)
diff --git a/core/src/main/java/com/linecorp/armeria/common/HttpMethod.java b/core/src/main/java/com/linecorp/armeria/common/HttpMethod.java
index c69acff75b9..8d699dd9097 100644
--- a/core/src/main/java/com/linecorp/armeria/common/HttpMethod.java
+++ b/core/src/main/java/com/linecorp/armeria/common/HttpMethod.java
@@ -45,80 +45,223 @@
public enum HttpMethod {
// Forked from Netty 4.1.34 at ff7484864b1785103cbc62845ff3a392c93822b7
+ // Changes:
+ // - Switched to enum for type
+ // - Added additional methods from https://www.iana.org/assignments/http-methods/http-methods.xhtml
+ // - Added safe methods
/**
- * The OPTIONS method which represents a request for information about the communication options
- * available on the request/response chain identified by the Request-URI. This method allows
- * the client to determine the options and/or requirements associated with a resource, or the
- * capabilities of a server, without implying a resource action or initiating a resource
- * retrieval.
+ * Special HTTP method that is not defined in HTTP/1.1.
*/
- OPTIONS,
-
+ ACL(Type.IDEMPOTENT),
+ /**
+ * Special HTTP method that is not defined in HTTP/1.1.
+ */
+ BASELINE_CONTROL(Type.IDEMPOTENT),
+ /**
+ * Special HTTP method that is not defined in HTTP/1.1.
+ */
+ BIND(Type.IDEMPOTENT),
+ /**
+ * Special HTTP method that is not defined in HTTP/1.1.
+ */
+ CHECKIN(Type.IDEMPOTENT),
+ /**
+ * Special HTTP method that is not defined in HTTP/1.1.
+ */
+ CHECKOUT(Type.IDEMPOTENT),
+ /**
+ * The CONNECT method which is used for a proxy that can dynamically switch to being a tunnel or for
+ * bootstrapping WebSockets with HTTP/2.
+ * Note that Armeria handles a {@code CONNECT} request only for bootstrapping WebSockets.
+ */
+ CONNECT(Type.NORMAL),
+ /**
+ * Special HTTP method that is not defined in HTTP/1.1.
+ */
+ COPY(Type.IDEMPOTENT),
+ /**
+ * The DELETE method which requests that the origin server delete the resource identified by the
+ * Request-URI.
+ */
+ DELETE(Type.IDEMPOTENT),
/**
* The GET method which means retrieve whatever information (in the form of an entity) is identified
* by the Request-URI. If the Request-URI refers to a data-producing process, it is the
* produced data which shall be returned as the entity in the response and not the source text
* of the process, unless that text happens to be the output of the process.
*/
- GET,
-
+ GET(Type.SAFE),
/**
* The HEAD method which is identical to GET except that the server MUST NOT return a message-body
* in the response.
*/
- HEAD,
-
+ HEAD(Type.SAFE),
+ /**
+ * Special HTTP method that is not defined in HTTP/1.1.
+ */
+ LABEL(Type.IDEMPOTENT),
+ /**
+ * Special HTTP method that is not defined in HTTP/1.1.
+ */
+ LINK(Type.IDEMPOTENT),
+ /**
+ * Special HTTP method that is not defined in HTTP/1.1.
+ */
+ LOCK(Type.NORMAL),
+ /**
+ * Special HTTP method that is not defined in HTTP/1.1.
+ */
+ MERGE(Type.IDEMPOTENT),
+ /**
+ * Special HTTP method that is not defined in HTTP/1.1.
+ */
+ MKACTIVITY(Type.IDEMPOTENT),
+ /**
+ * Special HTTP method that is not defined in HTTP/1.1.
+ */
+ MKCALENDAR(Type.IDEMPOTENT),
+ /**
+ * Special HTTP method that is not defined in HTTP/1.1.
+ */
+ MKCOL(Type.IDEMPOTENT),
+ /**
+ * Special HTTP method that is not defined in HTTP/1.1.
+ */
+ MKREDIRECTREF(Type.IDEMPOTENT),
+ /**
+ * Special HTTP method that is not defined in HTTP/1.1.
+ */
+ MKWORKSPACE(Type.IDEMPOTENT),
+ /**
+ * Special HTTP method that is not defined in HTTP/1.1.
+ */
+ MOVE(Type.IDEMPOTENT),
+ /**
+ * The OPTIONS method which represents a request for information about the communication options
+ * available on the request/response chain identified by the Request-URI. This method allows
+ * the client to determine the options and/or requirements associated with a resource, or the
+ * capabilities of a server, without implying a resource action or initiating a resource
+ * retrieval.
+ */
+ OPTIONS(Type.SAFE),
+ /**
+ * Special HTTP method that is not defined in HTTP/1.1.
+ */
+ ORDERPATCH(Type.IDEMPOTENT),
+ /**
+ * The PATCH method which requests that a set of changes described in the
+ * request entity be applied to the resource identified by the Request-URI.
+ */
+ PATCH(Type.NORMAL),
/**
* The POST method which is used to request that the origin server accept the entity enclosed in the
* request as a new subordinate of the resource identified by the Request-URI in the
* Request-Line.
*/
- POST,
-
+ POST(Type.NORMAL),
+ /**
+ * Special HTTP method that is not defined in HTTP/1.1.
+ */
+ PRI(Type.SAFE),
+ /**
+ * Special HTTP method that is not defined in HTTP/1.1.
+ */
+ PROPFIND(Type.SAFE),
+ /**
+ * Special HTTP method that is not defined in HTTP/1.1.
+ */
+ PROPPATCH(Type.IDEMPOTENT),
/**
* The PUT method which requests that the enclosed entity be stored under the supplied Request-URI.
*/
- PUT,
-
+ PUT(Type.IDEMPOTENT),
/**
- * The PATCH method which requests that a set of changes described in the
- * request entity be applied to the resource identified by the Request-URI.
+ * Special HTTP method that is not defined in HTTP/1.1.
*/
- PATCH,
-
+ REBIND(Type.IDEMPOTENT),
/**
- * The DELETE method which requests that the origin server delete the resource identified by the
- * Request-URI.
+ * Special HTTP method that is not defined in HTTP/1.1.
*/
- DELETE,
-
+ REPORT(Type.SAFE),
+ /**
+ * Special HTTP method that is not defined in HTTP/1.1.
+ */
+ SEARCH(Type.SAFE),
/**
* The TRACE method which is used to invoke a remote, application-layer loop-back of the request
* message.
*/
- TRACE,
-
+ TRACE(Type.SAFE),
/**
- * The CONNECT method which is used for a proxy that can dynamically switch to being a tunnel or for
- * bootstrapping WebSockets with HTTP/2.
- * Note that Armeria handles a {@code CONNECT} request only for bootstrapping WebSockets.
+ * Special HTTP method that is not defined in HTTP/1.1.
*/
- CONNECT,
-
+ UNBIND(Type.IDEMPOTENT),
+ /**
+ * Special HTTP method that is not defined in HTTP/1.1.
+ */
+ UNCHECKOUT(Type.IDEMPOTENT),
+ /**
+ * Special HTTP method that is not defined in HTTP/1.1.
+ */
+ UNLINK(Type.IDEMPOTENT),
+ /**
+ * Special HTTP method that is not defined in HTTP/1.1.
+ */
+ UNLOCK(Type.IDEMPOTENT),
+ /**
+ * Special HTTP method that is not defined in HTTP/1.1.
+ */
+ UPDATE(Type.IDEMPOTENT),
+ /**
+ * Special HTTP method that is not defined in HTTP/1.1.
+ */
+ UPDATEREDIRECTREF(Type.IDEMPOTENT),
+ /**
+ * Special HTTP method that is not defined in HTTP/1.1.
+ */
+ VERSION_CONTROL(Type.IDEMPOTENT),
/**
* A special constant returned by {@link RequestHeaders#method()} to signify that a request has a method
* not defined in this enum.
*/
- UNKNOWN;
+ UNKNOWN(Type.UNKNOWN);
private static final Set knownMethods; // ImmutableEnumSet
- private static final Set idempotentMethods = Sets.immutableEnumSet(GET, HEAD, PUT, DELETE);
+ private static final Set safeMethods;
+ private static final Set idempotentMethods;
static {
final Set allMethods = EnumSet.allOf(HttpMethod.class);
allMethods.remove(UNKNOWN);
knownMethods = Sets.immutableEnumSet(allMethods);
+ final Set safeMethodsBuilder = EnumSet.noneOf(HttpMethod.class);
+ for (HttpMethod method : knownMethods) {
+ if (method.type == Type.SAFE) {
+ safeMethodsBuilder.add(method);
+ }
+ }
+ safeMethods = Sets.immutableEnumSet(safeMethodsBuilder);
+ final Set idempotentMethodsBuilder = EnumSet.noneOf(HttpMethod.class);
+ for (HttpMethod method : knownMethods) {
+ if (method.type == Type.SAFE || method.type == Type.IDEMPOTENT) {
+ idempotentMethodsBuilder.add(method);
+ }
+ }
+ idempotentMethods = Sets.immutableEnumSet(idempotentMethodsBuilder);
+ }
+
+ private final Type type;
+
+ HttpMethod(Type type) {
+ this.type = type;
+ }
+
+ public enum Type {
+ UNKNOWN,
+ NORMAL,
+ IDEMPOTENT,
+ SAFE
}
/**
@@ -129,15 +272,45 @@ public enum HttpMethod {
public static boolean isSupported(String value) {
requireNonNull(value, "value");
switch (value) {
- case "OPTIONS":
+ case "ACL":
+ case "BASELINE-CONTROL":
+ case "BIND":
+ case "CHECKIN":
+ case "CHECKOUT":
+ case "CONNECT":
+ case "COPY":
+ case "DELETE":
case "GET":
case "HEAD":
+ case "LABEL":
+ case "LINK":
+ case "LOCK":
+ case "MERGE":
+ case "MKACTIVITY":
+ case "MKCALENDAR":
+ case "MKCOL":
+ case "MKREDIRECTREF":
+ case "MKWORKSPACE":
+ case "MOVE":
+ case "OPTIONS":
+ case "ORDERPATCH":
+ case "PATCH":
case "POST":
+ case "PRI":
+ case "PROPFIND":
+ case "PROPPATCH":
case "PUT":
- case "PATCH":
- case "DELETE":
+ case "REBIND":
+ case "REPORT":
+ case "SEARCH":
case "TRACE":
- case "CONNECT":
+ case "UNBIND":
+ case "UNCHECKOUT":
+ case "UNLINK":
+ case "UNLOCK":
+ case "UPDATE":
+ case "UPDATEREDIRECTREF":
+ case "VERSION-CONTROL":
return true;
}
@@ -146,12 +319,20 @@ public static boolean isSupported(String value) {
/**
* Returns the idempotent
- * HTTP methods - {@link #GET}, {@link #HEAD}, {@link #PUT} and {@link #DELETE}.
+ * HTTP methods.
*/
public static Set idempotentMethods() {
return idempotentMethods;
}
+ /**
+ * Returns the safe
+ * HTTP methods.
+ */
+ public static Set safeMethods() {
+ return safeMethods;
+ }
+
/**
* Returns all {@link HttpMethod}s except {@link #UNKNOWN}.
*/
@@ -174,24 +355,84 @@ public static HttpMethod tryParse(@Nullable String method) {
}
switch (method) {
- case "OPTIONS":
- return OPTIONS;
+ case "ACL":
+ return ACL;
+ case "BASELINE-CONTROL":
+ return BASELINE_CONTROL;
+ case "BIND":
+ return BIND;
+ case "CHECKIN":
+ return CHECKIN;
+ case "CHECKOUT":
+ return CHECKOUT;
+ case "CONNECT":
+ return CONNECT;
+ case "COPY":
+ return COPY;
+ case "DELETE":
+ return DELETE;
case "GET":
return GET;
case "HEAD":
return HEAD;
+ case "LABEL":
+ return LABEL;
+ case "LINK":
+ return LINK;
+ case "LOCK":
+ return LOCK;
+ case "MERGE":
+ return MERGE;
+ case "MKACTIVITY":
+ return MKACTIVITY;
+ case "MKCALENDAR":
+ return MKCALENDAR;
+ case "MKCOL":
+ return MKCOL;
+ case "MKREDIRECTREF":
+ return MKREDIRECTREF;
+ case "MKWORKSPACE":
+ return MKWORKSPACE;
+ case "MOVE":
+ return MOVE;
+ case "OPTIONS":
+ return OPTIONS;
+ case "ORDERPATCH":
+ return ORDERPATCH;
+ case "PATCH":
+ return PATCH;
case "POST":
return POST;
+ case "PRI":
+ return PRI;
+ case "PROPFIND":
+ return PROPFIND;
+ case "PROPPATCH":
+ return PROPPATCH;
case "PUT":
return PUT;
- case "PATCH":
- return PATCH;
- case "DELETE":
- return DELETE;
+ case "REBIND":
+ return REBIND;
+ case "REPORT":
+ return REPORT;
+ case "SEARCH":
+ return SEARCH;
case "TRACE":
return TRACE;
- case "CONNECT":
- return CONNECT;
+ case "UNBIND":
+ return UNBIND;
+ case "UNCHECKOUT":
+ return UNCHECKOUT;
+ case "UNLINK":
+ return UNLINK;
+ case "UNLOCK":
+ return UNLOCK;
+ case "UPDATE":
+ return UPDATE;
+ case "UPDATEREDIRECTREF":
+ return UPDATEREDIRECTREF;
+ case "VERSION-CONTROL":
+ return VERSION_CONTROL;
default:
return null;
}
From d8029230cd487860a44bf078f593c16d189976d6 Mon Sep 17 00:00:00 2001
From: AlexProgrammerDE <40795980+AlexProgrammerDE@users.noreply.github.com>
Date: Fri, 21 Mar 2025 07:26:48 +0100
Subject: [PATCH 2/2] Make the enum private and catch the * case unknown as
NORMAL
---
.../main/java/com/linecorp/armeria/common/HttpMethod.java | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/core/src/main/java/com/linecorp/armeria/common/HttpMethod.java b/core/src/main/java/com/linecorp/armeria/common/HttpMethod.java
index 8d699dd9097..56c015340d6 100644
--- a/core/src/main/java/com/linecorp/armeria/common/HttpMethod.java
+++ b/core/src/main/java/com/linecorp/armeria/common/HttpMethod.java
@@ -225,7 +225,7 @@ public enum HttpMethod {
* A special constant returned by {@link RequestHeaders#method()} to signify that a request has a method
* not defined in this enum.
*/
- UNKNOWN(Type.UNKNOWN);
+ UNKNOWN(Type.NORMAL);
private static final Set knownMethods; // ImmutableEnumSet
private static final Set safeMethods;
@@ -257,8 +257,7 @@ public enum HttpMethod {
this.type = type;
}
- public enum Type {
- UNKNOWN,
+ private enum Type {
NORMAL,
IDEMPOTENT,
SAFE