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