Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support verb suffix for HTTP endpoint #5613

Merged
merged 19 commits into from May 13, 2024

Conversation

sato9818
Copy link
Contributor

@sato9818 sato9818 commented Apr 16, 2024

Motivation:

We didn't implement supporting "verb" part from #3786, for example,
customVerb from https://service.name/v1/some/resource/name:customVerb.

: is a control character of a parameter variable in RoutingTrie,
so there's no way to specify a plain : character on a service path.

This changeset if a follow-up of #4064, originally written by @hyangtack.

Modifications:

  • Add VerbSuffixPathMapping which checks whether the request path has a verb or not.
  • Fix HttpJsonTranscodingPathParser.Stringifier to produce a verb with a leading : character.
  • Make RoutingTrie handle a escape character \.
  • Add an explanation about the verb suffix in Armeria documentation.

Result:

  • A user can specify a verb on HTTP endpoint.

Co-authored-by: Hyangtack Lee [email protected]

@sato9818 sato9818 changed the title Support verb suffix for grpc http endpoint [WIP] Support verb suffix for grpc http endpoint Apr 16, 2024
@sato9818 sato9818 changed the title [WIP] Support verb suffix for grpc http endpoint Support verb suffix for gRPC HTTP endpoint Apr 17, 2024
@sato9818 sato9818 marked this pull request as ready for review April 17, 2024 01:50
@trustin
Copy link
Member

trustin commented Apr 19, 2024

/cc @hyangtack

Copy link
Member

@trustin trustin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice follow-up work, @sato9818! Left some minor comments 🙇

Comment on lines 69 to 72
// Q: Should this be allowed?
route = Route.builder().path("/foo/{bar:biz}").build();
assertThat(route.pathType()).isSameAs(RoutePathType.PARAMETERIZED);
assertThat(route.paths()).containsExactly("/foo/:", "/foo/:");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great question! I think we should not allow it or treat it as {bar} because we will introduce some option expressions in the future, e.g. {bar:type=int,range=[0,10]}

@sato9818 sato9818 requested a review from trustin April 23, 2024 02:27
Copy link
Member

@trustin trustin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost there 😄

@@ -418,4 +427,63 @@ Node<V> build() {
return new Node<>(type, path, children, parameterChild, catchAllChild, values);
}
}

private static class PathReader {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice abstraction

Copy link
Member

@trustin trustin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sato9818 sato9818 changed the title Support verb suffix for gRPC HTTP endpoint Support verb suffix for HTTP endpoint Apr 24, 2024
@trustin trustin added this to the 1.29.0 milestone Apr 30, 2024
Copy link
Contributor

@ikhoon ikhoon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks very nice. 😃

return node;
} else {
final Node<V> verb = node.children.get(':');
final String pathVerbPart = path.substring(lastColon);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Micro optimization) What do you think of implementing a method such as findVerb to find a verb part from path? findVerb will scan path from the last character up to begin to find :.

String verbPart = findVerb(path, begin);
if (verbPart == null) {
  return node;
} else {
   final Node<V> verb = node.children.get(':');
   ...
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should use the fineVerb method defined in VerbSuffixPathMapping because the verb found by the method has to be last segment of a path 👍

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The regular expression may consume more CPU cycles which is inefficient in finding an exact character.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your advice! Implemented the findVerb function.

Copy link
Member

@minwoox minwoox left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! Left minor suggestions. 👍

return withLeadingSlash ? '/' + path : path;
}

private static String toPathString(List<PathSegment> segments, PathMappingType type) {
final PathSegment lastSegment = segments.get(segments.size() - 1);
if (lastSegment instanceof VerbPathSegment) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: Shouldn't we do some validation here such as checking if the body "*" when POST is used? 🤔

// We don't check 'Verb' part because we don't use it when generating a corresponding path pattern.

https://cloud.google.com/apis/design/custom_methods#http_mapping

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your suggestion! As you said, I think we need to validate the verb part so I used a regex to make sure the verb part is in valid.
In my opinion, we don't need to validate if the body is "*" when POST is used because some poeple might want to use a body other than * according to this stackoverflow. What do you think of this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After rereading the spec, I realize that it uses the term 'shall' at the top.

The following guidelines shall be applied when choosing the HTTP mapping:
   ...
- If the HTTP verb used for the custom method allows an HTTP request body (this applies to POST, PUT, PATCH, or a custom HTTP verb), the HTTP configuration of that custom method must use the body: "*" clause and all remaining request message fields shall map to the HTTP request body.

Because they use must in the sentence, I thought we should prohibit the value except "*" but I was wrong. 😓
Let's just remove this line though:

// We don't check 'Verb' part because we don't use it when generating a corresponding path pattern.

Copy link
Member

@minwoox minwoox left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks fantastic. Thanks, @sato9818! 👍 👍 👍

return withLeadingSlash ? '/' + path : path;
}

private static String toPathString(List<PathSegment> segments, PathMappingType type) {
final PathSegment lastSegment = segments.get(segments.size() - 1);
if (lastSegment instanceof VerbPathSegment) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After rereading the spec, I realize that it uses the term 'shall' at the top.

The following guidelines shall be applied when choosing the HTTP mapping:
   ...
- If the HTTP verb used for the custom method allows an HTTP request body (this applies to POST, PUT, PATCH, or a custom HTTP verb), the HTTP configuration of that custom method must use the body: "*" clause and all remaining request message fields shall map to the HTTP request body.

Because they use must in the sentence, I thought we should prohibit the value except "*" but I was wrong. 😓
Let's just remove this line though:

// We don't check 'Verb' part because we don't use it when generating a corresponding path pattern.

Copy link
Member

@minwoox minwoox left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks fantastic. Thanks, @sato9818! 👍 👍 👍

Copy link
Member

@minwoox minwoox left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks fantastic. Thanks, @sato9818! 👍 👍 👍

Copy link
Contributor

@ikhoon ikhoon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome work! 🚀💯

@trustin
Copy link
Member

trustin commented May 11, 2024

@jrhee17 is currently looking for ways to improve this PR, so please bear with us 😅

Copy link
Contributor

@jrhee17 jrhee17 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's get this merged! Thanks @sato9818 ! 🙇 👍 🙇

@jrhee17 jrhee17 merged commit 8a94bcc into line:main May 13, 2024
14 of 15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants