From ac9d1ca6ac60f72f0aaf4b3c5fa790dd196cd91b Mon Sep 17 00:00:00 2001 From: minux Date: Tue, 30 Jul 2024 14:44:04 +0900 Subject: [PATCH] Do not convert `@Header` name when the name is specified (#5841) Motivation: We must not convert the header name specified in `@Header` when creating an annotated service. This is a regression introduced by #5547, affecting 1.29.0 through 1.29.3. Modifications: - Do not convert the header name specified in `@Header`. Result: - An annotated service that has a `@Header` with the specified name now works correctly. --- .../annotation/AnnotatedElementNameUtil.java | 25 +++++++++++++++++-- .../annotation/AnnotatedValueResolver.java | 3 +-- .../annotation/AnnotatedServiceTest.java | 10 ++++++++ 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/com/linecorp/armeria/internal/server/annotation/AnnotatedElementNameUtil.java b/core/src/main/java/com/linecorp/armeria/internal/server/annotation/AnnotatedElementNameUtil.java index 8029ecbb787..29dceed06b6 100644 --- a/core/src/main/java/com/linecorp/armeria/internal/server/annotation/AnnotatedElementNameUtil.java +++ b/core/src/main/java/com/linecorp/armeria/internal/server/annotation/AnnotatedElementNameUtil.java @@ -31,9 +31,9 @@ final class AnnotatedElementNameUtil { /** - * Returns the value of {@link Header}, {@link Param}, {@link Attribute} if the value is not blank. + * Returns the value of {@link Param}, {@link Attribute} if the value is not blank. * If the value is blank, it returns the name of the specified {@code nameRetrievalTarget} object - * which is an instance of {@link Header}, {@link Param}, {@link Attribute} or {@link Field}. + * which is an instance of {@link Param}, {@link Attribute} or {@link Field}. */ static String findName(Object nameRetrievalTarget, String value) { requireNonNull(nameRetrievalTarget, "nameRetrievalTarget"); @@ -44,6 +44,27 @@ static String findName(Object nameRetrievalTarget, String value) { return getName(nameRetrievalTarget); } + /** + * Returns the value of the {@link Header} annotation which is specified on the {@code element} if + * the value is not blank. If the value is blank, it returns the name of the specified + * {@code nameRetrievalTarget} object which is an instance of {@link Parameter} or {@link Field}. + * + *

Note that the name of the specified {@code nameRetrievalTarget} will be converted as + * {@link CaseFormat#LOWER_HYPHEN} that the string elements are separated with one hyphen({@code -}) + * character. The value of the {@link Header} annotation will not be converted because it is clearly + * specified by a user. + */ + static String findName(Header header, Object nameRetrievalTarget) { + requireNonNull(nameRetrievalTarget, "nameRetrievalTarget"); + + final String value = header.value(); + if (DefaultValues.isSpecified(value)) { + checkArgument(!value.isEmpty(), "value is empty."); + return value; + } + return toHeaderName(getName(nameRetrievalTarget)); + } + /** * Returns the name of the specified element or the default name if it can't get. */ diff --git a/core/src/main/java/com/linecorp/armeria/internal/server/annotation/AnnotatedValueResolver.java b/core/src/main/java/com/linecorp/armeria/internal/server/annotation/AnnotatedValueResolver.java index e782a09bf89..259ad6fa913 100644 --- a/core/src/main/java/com/linecorp/armeria/internal/server/annotation/AnnotatedValueResolver.java +++ b/core/src/main/java/com/linecorp/armeria/internal/server/annotation/AnnotatedValueResolver.java @@ -22,7 +22,6 @@ import static com.linecorp.armeria.internal.server.annotation.AnnotatedElementNameUtil.findName; import static com.linecorp.armeria.internal.server.annotation.AnnotatedElementNameUtil.getName; import static com.linecorp.armeria.internal.server.annotation.AnnotatedElementNameUtil.getNameOrDefault; -import static com.linecorp.armeria.internal.server.annotation.AnnotatedElementNameUtil.toHeaderName; import static com.linecorp.armeria.internal.server.annotation.AnnotatedServiceFactory.findDescription; import static com.linecorp.armeria.internal.server.annotation.AnnotatedServiceTypeUtil.stringToType; import static com.linecorp.armeria.internal.server.annotation.DefaultValues.getSpecifiedValue; @@ -471,7 +470,7 @@ private static AnnotatedValueResolver of(AnnotatedElement annotatedElement, final Header header = annotatedElement.getAnnotation(Header.class); if (header != null) { - final String name = toHeaderName(findName(typeElement, header.value())); + final String name = findName(header, typeElement); return ofHeader(name, annotatedElement, typeElement, type, description); } diff --git a/core/src/test/java/com/linecorp/armeria/internal/server/annotation/AnnotatedServiceTest.java b/core/src/test/java/com/linecorp/armeria/internal/server/annotation/AnnotatedServiceTest.java index 6da6e322001..e078a605b07 100644 --- a/core/src/test/java/com/linecorp/armeria/internal/server/annotation/AnnotatedServiceTest.java +++ b/core/src/test/java/com/linecorp/armeria/internal/server/annotation/AnnotatedServiceTest.java @@ -773,6 +773,12 @@ public String customHeader5(@Header List numbers, String.join(":", strings); } + @Post("/headerNameSpecified") + public String headerNameSpecified(@Header("X-x-FoO-bAr") String id) { + // Because the header name is specified, it's not converted. + return id; + } + @Get("/headerDefault") public String headerDefault(RequestContext ctx, @Header @Default("hello") String username, @@ -1224,6 +1230,10 @@ void testRequestHeaderInjection() throws Exception { request.addHeader("strings", "minwoox"); testBody(hc, request, "1:2:1/minwoox:giraffe"); + request = post("/11/headerNameSpecified"); + request.addHeader("X-x-FoO-bAr", "qwerty"); + testBody(hc, request, "qwerty"); + request = get("/11/headerDefault"); testBody(hc, request, "hello/world/(null)");