diff --git a/http-client-core/src/main/java/io/micronaut/http/client/ClientAttributes.java b/http-client-core/src/main/java/io/micronaut/http/client/ClientAttributes.java new file mode 100644 index 00000000000..6a5be1ce8a7 --- /dev/null +++ b/http-client-core/src/main/java/io/micronaut/http/client/ClientAttributes.java @@ -0,0 +1,68 @@ +/* + * Copyright 2017-2025 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.http.client; + +import io.micronaut.aop.MethodInvocationContext; +import io.micronaut.core.annotation.NonNull; +import io.micronaut.http.HttpAttributes; +import io.micronaut.http.HttpRequest; + +import java.util.Optional; + +/** + * Client-related attribute accessors. + * + * @author Jonas Konrad + * @since 4.8.0 + */ +@SuppressWarnings("removal") +public final class ClientAttributes { + private ClientAttributes() { + } + + /** + * Set the client service ID. + * + * @param request The request + * @param serviceId The client service ID + * @see io.micronaut.http.BasicHttpAttributes#getServiceId(HttpRequest) + */ + public static void setServiceId(@NonNull HttpRequest request, @NonNull String serviceId) { + request.setAttribute(HttpAttributes.SERVICE_ID, serviceId); + } + + /** + * Get the invocation context. + * + * @param request The request + * @return The invocation context, if present + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + @NonNull + public static Optional> getInvocationContext(@NonNull HttpRequest request) { + return (Optional) request.getAttribute(HttpAttributes.INVOCATION_CONTEXT, MethodInvocationContext.class); + } + + /** + * Set the invocation context. + * + * @param request The request + * @param invocationContext The invocation context + */ + public static void setInvocationContext(@NonNull HttpRequest request, @NonNull MethodInvocationContext invocationContext) { + request.setAttribute(HttpAttributes.INVOCATION_CONTEXT, invocationContext); + } +} diff --git a/http-client-core/src/main/java/io/micronaut/http/client/interceptor/HttpClientIntroductionAdvice.java b/http-client-core/src/main/java/io/micronaut/http/client/interceptor/HttpClientIntroductionAdvice.java index 293b21fc1fa..b471e42d44b 100644 --- a/http-client-core/src/main/java/io/micronaut/http/client/interceptor/HttpClientIntroductionAdvice.java +++ b/http-client-core/src/main/java/io/micronaut/http/client/interceptor/HttpClientIntroductionAdvice.java @@ -43,7 +43,7 @@ import io.micronaut.core.util.CollectionUtils; import io.micronaut.core.util.StringUtils; import io.micronaut.core.version.annotation.Version; -import io.micronaut.http.HttpAttributes; +import io.micronaut.http.BasicHttpAttributes; import io.micronaut.http.HttpHeaders; import io.micronaut.http.HttpMethod; import io.micronaut.http.HttpRequest; @@ -56,6 +56,7 @@ import io.micronaut.http.annotation.HttpMethodMapping; import io.micronaut.http.annotation.Produces; import io.micronaut.http.client.BlockingHttpClient; +import io.micronaut.http.client.ClientAttributes; import io.micronaut.http.client.HttpClient; import io.micronaut.http.client.HttpClientRegistry; import io.micronaut.http.client.ReactiveClientResultTransformer; @@ -435,9 +436,9 @@ private RequestBinderResult bindRequest(MethodInvocationContext } } - request.setAttribute(HttpAttributes.INVOCATION_CONTEXT, context); + ClientAttributes.setInvocationContext(request, context); // Set the URI template used to make the request for tracing purposes - request.setAttribute(HttpAttributes.URI_TEMPLATE, resolveTemplate(annotationMetadata, uriTemplate.toString())); + BasicHttpAttributes.setUriTemplate(request, resolveTemplate(annotationMetadata, uriTemplate.toString())); return RequestBinderResult.withRequest(request); } diff --git a/http-client-jdk/src/main/java/io/micronaut/http/client/jdk/AbstractJdkHttpClient.java b/http-client-jdk/src/main/java/io/micronaut/http/client/jdk/AbstractJdkHttpClient.java index d97d7d2f50c..c0b60c8647b 100644 --- a/http-client-jdk/src/main/java/io/micronaut/http/client/jdk/AbstractJdkHttpClient.java +++ b/http-client-jdk/src/main/java/io/micronaut/http/client/jdk/AbstractJdkHttpClient.java @@ -26,12 +26,13 @@ import io.micronaut.core.propagation.PropagatedContext; import io.micronaut.core.type.Argument; import io.micronaut.core.util.StringUtils; -import io.micronaut.http.HttpAttributes; +import io.micronaut.http.BasicHttpAttributes; import io.micronaut.http.HttpResponse; import io.micronaut.http.HttpStatus; import io.micronaut.http.MutableHttpRequest; import io.micronaut.http.bind.RequestBinderRegistry; import io.micronaut.http.body.MessageBodyHandlerRegistry; +import io.micronaut.http.client.ClientAttributes; import io.micronaut.http.client.HttpClientConfiguration; import io.micronaut.http.client.HttpVersionSelection; import io.micronaut.http.client.LoadBalancer; @@ -431,8 +432,8 @@ protected Publisher> responsePublisher( @NonNull io.micronaut.http.HttpRequest request, @Nullable Argument bodyType ) { - if (clientId != null && request.getAttribute(HttpAttributes.SERVICE_ID).isEmpty()) { - request.setAttribute(HttpAttributes.SERVICE_ID, clientId); + if (clientId != null && BasicHttpAttributes.getServiceId(request).isEmpty()) { + ClientAttributes.setServiceId(request, clientId); } return Flux.defer(() -> mapToHttpRequest(request, bodyType)) // defered so any client filter changes are used diff --git a/http-client-jdk/src/test/groovy/io/micronaut/http/client/jdk/Http2Spec.groovy b/http-client-jdk/src/test/groovy/io/micronaut/http/client/jdk/Http2Spec.groovy index 6590064644a..d6090e7ab4a 100644 --- a/http-client-jdk/src/test/groovy/io/micronaut/http/client/jdk/Http2Spec.groovy +++ b/http-client-jdk/src/test/groovy/io/micronaut/http/client/jdk/Http2Spec.groovy @@ -21,6 +21,7 @@ import spock.lang.Specification @Property(name = "micronaut.server.http-version", value = "HTTP_2_0") @Property(name = "micronaut.server.ssl.build-self-signed", value = "true") @Property(name = "micronaut.server.ssl.enabled", value = "true") +@Property(name = "micronaut.server.ssl.port", value = "0") @Property(name = "micronaut.http.client.ssl.insecure-trust-all-certificates", value = "true") @Property(name = "micronaut.server.max-request-size", value = "16384") class Http2Spec extends Specification { diff --git a/http-client-jdk/src/test/groovy/io/micronaut/http/client/jdk/OptionsRequestAttributesSpec.groovy b/http-client-jdk/src/test/groovy/io/micronaut/http/client/jdk/OptionsRequestAttributesSpec.groovy index b2a93567f73..0fe76da4311 100644 --- a/http-client-jdk/src/test/groovy/io/micronaut/http/client/jdk/OptionsRequestAttributesSpec.groovy +++ b/http-client-jdk/src/test/groovy/io/micronaut/http/client/jdk/OptionsRequestAttributesSpec.groovy @@ -4,7 +4,7 @@ import io.micronaut.context.ApplicationContext import io.micronaut.context.annotation.Requires import io.micronaut.core.type.Argument import io.micronaut.core.util.StringUtils -import io.micronaut.http.HttpAttributes +import io.micronaut.http.BasicHttpAttributes import io.micronaut.http.HttpHeaders import io.micronaut.http.HttpRequest import io.micronaut.http.HttpResponse @@ -18,9 +18,9 @@ import io.micronaut.http.client.exceptions.HttpClientResponseException import io.micronaut.http.filter.HttpServerFilter import io.micronaut.http.filter.ServerFilterChain import io.micronaut.runtime.server.EmbeddedServer +import io.micronaut.web.router.RouteAttributes import jakarta.inject.Singleton import org.reactivestreams.Publisher -import org.spockframework.util.Assert import spock.lang.Specification class OptionsRequestAttributesSpec extends Specification { @@ -98,9 +98,9 @@ class OptionsRequestAttributesSpec extends Specification { @Override Publisher> doFilter(HttpRequest request, ServerFilterChain chain) { - containsRouteMatch = request.getAttributes().contains(HttpAttributes.ROUTE_MATCH.toString()) - containsRouteInfo = request.getAttributes().contains(HttpAttributes.ROUTE_INFO.toString()) - containsUriTemplate = request.getAttributes().contains(HttpAttributes.URI_TEMPLATE.toString()) + containsRouteMatch = RouteAttributes.getRouteMatch(request).isPresent() + containsRouteInfo = RouteAttributes.getRouteInfo(request).isPresent() + containsUriTemplate = BasicHttpAttributes.getUriTemplate(request).isPresent() return chain.proceed(request) } } diff --git a/http-client-jdk/src/test/groovy/io/micronaut/http/client/jdk/ServiceIdSpec.groovy b/http-client-jdk/src/test/groovy/io/micronaut/http/client/jdk/ServiceIdSpec.groovy index 8000b082b1e..b0e3934e6bc 100644 --- a/http-client-jdk/src/test/groovy/io/micronaut/http/client/jdk/ServiceIdSpec.groovy +++ b/http-client-jdk/src/test/groovy/io/micronaut/http/client/jdk/ServiceIdSpec.groovy @@ -2,7 +2,7 @@ package io.micronaut.http.client.jdk import io.micronaut.context.ApplicationContext import io.micronaut.context.annotation.Requires -import io.micronaut.http.HttpAttributes +import io.micronaut.http.BasicHttpAttributes import io.micronaut.http.HttpRequest import io.micronaut.http.HttpResponse import io.micronaut.http.HttpVersion @@ -80,7 +80,7 @@ class ServiceIdSpec extends Specification { @Override Publisher> doFilter(MutableHttpRequest request, ClientFilterChain chain) { - serviceId = request.getAttribute(HttpAttributes.SERVICE_ID).orElse(null) + serviceId = BasicHttpAttributes.getServiceId(request).orElse(null) return chain.proceed(request) } } diff --git a/http-client/src/main/java/io/micronaut/http/client/netty/DefaultHttpClient.java b/http-client/src/main/java/io/micronaut/http/client/netty/DefaultHttpClient.java index 840e920aa90..0ed4859434a 100644 --- a/http-client/src/main/java/io/micronaut/http/client/netty/DefaultHttpClient.java +++ b/http-client/src/main/java/io/micronaut/http/client/netty/DefaultHttpClient.java @@ -38,7 +38,7 @@ import io.micronaut.core.util.ObjectUtils; import io.micronaut.core.util.StringUtils; import io.micronaut.core.util.functional.ThrowingFunction; -import io.micronaut.http.HttpAttributes; +import io.micronaut.http.BasicHttpAttributes; import io.micronaut.http.HttpResponse; import io.micronaut.http.HttpResponseWrapper; import io.micronaut.http.HttpStatus; @@ -60,6 +60,7 @@ import io.micronaut.http.body.MessageBodyReader; import io.micronaut.http.body.stream.BodySizeLimits; import io.micronaut.http.client.BlockingHttpClient; +import io.micronaut.http.client.ClientAttributes; import io.micronaut.http.client.DefaultHttpClientConfiguration; import io.micronaut.http.client.HttpClient; import io.micronaut.http.client.HttpClientConfiguration; @@ -1527,8 +1528,8 @@ private ExecutionFlow> sendRequestWithRedirects( MutableHttpRequest request, BiFunction, NettyClientByteBodyResponse, ? extends ExecutionFlow>> readResponse ) { - if (informationalServiceId != null && request.getAttribute(HttpAttributes.SERVICE_ID).isEmpty()) { - request.setAttribute(HttpAttributes.SERVICE_ID, informationalServiceId); + if (informationalServiceId != null && BasicHttpAttributes.getServiceId(request).isEmpty()) { + ClientAttributes.setServiceId(request, informationalServiceId); } List filters = diff --git a/http-client/src/test/groovy/io/micronaut/http/client/ServiceIdSpec.groovy b/http-client/src/test/groovy/io/micronaut/http/client/ServiceIdSpec.groovy index 1903ab2347b..9c5e21892d8 100644 --- a/http-client/src/test/groovy/io/micronaut/http/client/ServiceIdSpec.groovy +++ b/http-client/src/test/groovy/io/micronaut/http/client/ServiceIdSpec.groovy @@ -2,7 +2,7 @@ package io.micronaut.http.client import io.micronaut.context.ApplicationContext import io.micronaut.context.annotation.Requires -import io.micronaut.http.HttpAttributes +import io.micronaut.http.BasicHttpAttributes import io.micronaut.http.HttpRequest import io.micronaut.http.HttpResponse import io.micronaut.http.HttpVersion @@ -79,7 +79,7 @@ class ServiceIdSpec extends Specification { @Override Publisher> doFilter(MutableHttpRequest request, ClientFilterChain chain) { - serviceId = request.getAttribute(HttpAttributes.SERVICE_ID).orElse(null) + serviceId = BasicHttpAttributes.getServiceId(request).orElse(null) return chain.proceed(request) } } diff --git a/http-client/src/test/groovy/io/micronaut/http/client/netty/Http2Spec.groovy b/http-client/src/test/groovy/io/micronaut/http/client/netty/Http2Spec.groovy index c7bf0f95a9d..f64c0a8d877 100644 --- a/http-client/src/test/groovy/io/micronaut/http/client/netty/Http2Spec.groovy +++ b/http-client/src/test/groovy/io/micronaut/http/client/netty/Http2Spec.groovy @@ -4,7 +4,12 @@ import io.micronaut.context.annotation.Property import io.micronaut.context.annotation.Requires import io.micronaut.http.HttpRequest import io.micronaut.http.MediaType -import io.micronaut.http.annotation.* +import io.micronaut.http.annotation.Body +import io.micronaut.http.annotation.Consumes +import io.micronaut.http.annotation.Controller +import io.micronaut.http.annotation.Get +import io.micronaut.http.annotation.Post +import io.micronaut.http.annotation.Produces import io.micronaut.http.client.HttpClient import io.micronaut.http.client.HttpVersionSelection import io.micronaut.http.client.annotation.Client @@ -17,6 +22,7 @@ import spock.lang.Specification @Property(name = "micronaut.server.http-version", value = "HTTP_2_0") @Property(name = "micronaut.server.ssl.build-self-signed", value = "true") @Property(name = "micronaut.server.ssl.enabled", value = "true") +@Property(name = "micronaut.server.ssl.port", value = "0") @Property(name = "micronaut.http.client.ssl.insecure-trust-all-certificates", value = "true") @Property(name = "micronaut.server.max-request-size", value = "16384") class Http2Spec extends Specification { diff --git a/http-server-netty/src/main/java/io/micronaut/http/server/netty/NettyHttpRequest.java b/http-server-netty/src/main/java/io/micronaut/http/server/netty/NettyHttpRequest.java index 85b05c49cb1..70ddd10e41d 100644 --- a/http-server-netty/src/main/java/io/micronaut/http/server/netty/NettyHttpRequest.java +++ b/http-server-netty/src/main/java/io/micronaut/http/server/netty/NettyHttpRequest.java @@ -63,6 +63,7 @@ import io.micronaut.http.server.netty.handler.Http2ServerHandler; import io.micronaut.http.server.netty.multipart.NettyCompletedFileUpload; import io.micronaut.web.router.DefaultUriRouteMatch; +import io.micronaut.web.router.RouteAttributes; import io.micronaut.web.router.RouteMatch; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; @@ -226,7 +227,7 @@ public ExecutionFlow getRouteWaitsFor() { public FormRouteCompleter formRouteCompleter() { assert isFormOrMultipartData(); if (formRouteCompleter == null) { - formRouteCompleter = new FormRouteCompleter((RouteMatch) getAttribute(HttpAttributes.ROUTE_MATCH).get(), getChannelHandlerContext().channel().eventLoop()); + formRouteCompleter = new FormRouteCompleter(RouteAttributes.getRouteMatch(this).get(), getChannelHandlerContext().channel().eventLoop()); } return formRouteCompleter; } diff --git a/http-server-netty/src/main/java/io/micronaut/http/server/netty/binders/NettyBodyAnnotationBinder.java b/http-server-netty/src/main/java/io/micronaut/http/server/netty/binders/NettyBodyAnnotationBinder.java index ed35898bd33..6b7ca9bb4ad 100644 --- a/http-server-netty/src/main/java/io/micronaut/http/server/netty/binders/NettyBodyAnnotationBinder.java +++ b/http-server-netty/src/main/java/io/micronaut/http/server/netty/binders/NettyBodyAnnotationBinder.java @@ -25,7 +25,6 @@ import io.micronaut.core.io.buffer.ByteBuffer; import io.micronaut.core.io.buffer.ReferenceCounted; import io.micronaut.core.propagation.PropagatedContext; -import io.micronaut.http.HttpAttributes; import io.micronaut.http.HttpHeaders; import io.micronaut.http.HttpRequest; import io.micronaut.http.MediaType; @@ -39,13 +38,14 @@ import io.micronaut.http.body.MessageBodyReader; import io.micronaut.http.codec.CodecException; import io.micronaut.http.context.ServerHttpRequestContext; +import io.micronaut.http.netty.body.AvailableNettyByteBody; import io.micronaut.http.server.netty.FormDataHttpContentProcessor; import io.micronaut.http.server.netty.FormRouteCompleter; import io.micronaut.http.server.netty.MicronautHttpData; import io.micronaut.http.server.netty.NettyHttpRequest; -import io.micronaut.http.netty.body.AvailableNettyByteBody; import io.micronaut.http.server.netty.configuration.NettyHttpServerConfiguration; import io.micronaut.http.server.netty.converters.NettyConverters; +import io.micronaut.web.router.RouteAttributes; import io.micronaut.web.router.RouteInfo; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; @@ -149,7 +149,7 @@ public List getConversionErrors() { Optional transform(NettyHttpRequest nhr, ArgumentConversionContext context, AvailableByteBody imm) throws Throwable { MessageBodyReader reader = null; - final RouteInfo routeInfo = nhr.getAttribute(HttpAttributes.ROUTE_INFO, RouteInfo.class).orElse(null); + final RouteInfo routeInfo = RouteAttributes.getRouteInfo(nhr).orElse(null); if (routeInfo != null) { reader = (MessageBodyReader) routeInfo.getMessageBodyReader(); } diff --git a/http-server-netty/src/main/java/io/micronaut/http/server/netty/websocket/NettyServerWebSocketHandler.java b/http-server-netty/src/main/java/io/micronaut/http/server/netty/websocket/NettyServerWebSocketHandler.java index 3175a36708e..60693ed6bfd 100644 --- a/http-server-netty/src/main/java/io/micronaut/http/server/netty/websocket/NettyServerWebSocketHandler.java +++ b/http-server-netty/src/main/java/io/micronaut/http/server/netty/websocket/NettyServerWebSocketHandler.java @@ -41,6 +41,7 @@ import io.micronaut.inject.MethodExecutionHandle; import io.micronaut.scheduling.executor.ExecutorSelector; import io.micronaut.scheduling.executor.ThreadSelection; +import io.micronaut.web.router.RouteAttributes; import io.micronaut.web.router.UriRouteMatch; import io.micronaut.websocket.CloseReason; import io.micronaut.websocket.WebSocketPongMessage; @@ -182,7 +183,7 @@ public class NettyServerWebSocketHandler extends AbstractNettyWebSocketHandler { this.nettyEmbeddedServices = nettyEmbeddedServices; this.coroutineHelper = coroutineHelper; - request.setAttribute(HttpAttributes.ROUTE_MATCH, routeMatch); + RouteAttributes.setRouteMatch(request, routeMatch); Flux.from(callOpenMethod(ctx)).subscribe(v -> { }, t -> { forwardErrorToUser(ctx, e -> { diff --git a/http-server-netty/src/main/java/io/micronaut/http/server/netty/websocket/NettyServerWebSocketUpgradeHandler.java b/http-server-netty/src/main/java/io/micronaut/http/server/netty/websocket/NettyServerWebSocketUpgradeHandler.java index 0dca0ba4315..56e5a030511 100644 --- a/http-server-netty/src/main/java/io/micronaut/http/server/netty/websocket/NettyServerWebSocketUpgradeHandler.java +++ b/http-server-netty/src/main/java/io/micronaut/http/server/netty/websocket/NettyServerWebSocketUpgradeHandler.java @@ -22,7 +22,6 @@ import io.micronaut.core.execution.ExecutionFlow; import io.micronaut.core.propagation.PropagatedContext; import io.micronaut.core.util.StringUtils; -import io.micronaut.http.HttpAttributes; import io.micronaut.http.HttpMethod; import io.micronaut.http.HttpRequest; import io.micronaut.http.HttpResponse; @@ -43,6 +42,7 @@ import io.micronaut.http.server.netty.configuration.NettyHttpServerConfiguration; import io.micronaut.http.server.netty.handler.OutboundAccess; import io.micronaut.http.server.netty.handler.RequestHandler; +import io.micronaut.web.router.RouteAttributes; import io.micronaut.web.router.RouteMatch; import io.micronaut.web.router.Router; import io.micronaut.web.router.UriRouteMatch; @@ -180,7 +180,8 @@ private void writeResponse(ChannelHandlerContext ctx, } if (shouldProceedNormally) { - UriRouteMatch routeMatch = actualResponse.getAttribute(HttpAttributes.ROUTE_MATCH, UriRouteMatch.class) + UriRouteMatch routeMatch = RouteAttributes.getRouteMatch(actualResponse) + .map(rm -> (UriRouteMatch) rm) .orElseThrow(() -> new IllegalStateException("Route match is required!")); //Adding new handler to the existing pipeline to handle WebSocket Messages WebSocketBean webSocketBean = webSocketBeanRegistry.getWebSocket(routeMatch.getTarget().getClass()); @@ -306,10 +307,10 @@ ExecutionFlow> handle(HttpRequest request) { MutableHttpResponse proceed = HttpResponse.ok(); if (route != null) { - request.setAttribute(HttpAttributes.ROUTE_MATCH, route); - request.setAttribute(HttpAttributes.ROUTE_INFO, route); - proceed.setAttribute(HttpAttributes.ROUTE_MATCH, route); - proceed.setAttribute(HttpAttributes.ROUTE_INFO, route); + RouteAttributes.setRouteMatch(request, route); + RouteAttributes.setRouteInfo(request, route.getRouteInfo()); + RouteAttributes.setRouteMatch(proceed, route); + RouteAttributes.setRouteInfo(proceed, route.getRouteInfo()); } ExecutionFlow> response; diff --git a/http-server-netty/src/test/groovy/io/micronaut/http/server/netty/OptionsRequestAttributesSpec.groovy b/http-server-netty/src/test/groovy/io/micronaut/http/server/netty/OptionsRequestAttributesSpec.groovy index 88a06d904fe..2c7bdcf3d92 100644 --- a/http-server-netty/src/test/groovy/io/micronaut/http/server/netty/OptionsRequestAttributesSpec.groovy +++ b/http-server-netty/src/test/groovy/io/micronaut/http/server/netty/OptionsRequestAttributesSpec.groovy @@ -4,7 +4,7 @@ import io.micronaut.context.ApplicationContext import io.micronaut.context.annotation.Requires import io.micronaut.core.type.Argument import io.micronaut.core.util.StringUtils -import io.micronaut.http.HttpAttributes +import io.micronaut.http.BasicHttpAttributes import io.micronaut.http.HttpHeaders import io.micronaut.http.HttpRequest import io.micronaut.http.HttpResponse @@ -18,9 +18,9 @@ import io.micronaut.http.client.exceptions.HttpClientResponseException import io.micronaut.http.filter.HttpServerFilter import io.micronaut.http.filter.ServerFilterChain import io.micronaut.runtime.server.EmbeddedServer +import io.micronaut.web.router.RouteAttributes import jakarta.inject.Singleton import org.reactivestreams.Publisher -import org.spockframework.util.Assert import spock.lang.Specification class OptionsRequestAttributesSpec extends Specification { @@ -97,9 +97,9 @@ class OptionsRequestAttributesSpec extends Specification { @Override Publisher> doFilter(HttpRequest request, ServerFilterChain chain) { - containsRouteMatch = request.getAttributes().contains(HttpAttributes.ROUTE_MATCH.toString()) - containsRouteInfo = request.getAttributes().contains(HttpAttributes.ROUTE_INFO.toString()) - containsUriTemplate = request.getAttributes().contains(HttpAttributes.URI_TEMPLATE.toString()) + containsRouteMatch = RouteAttributes.getRouteMatch(request).isPresent() + containsRouteInfo = RouteAttributes.getRouteInfo(request).isPresent() + containsUriTemplate = BasicHttpAttributes.getUriTemplate(request).isPresent() return chain.proceed(request) } } diff --git a/http-server-netty/src/test/groovy/io/micronaut/http/server/netty/filters/FilterErrorSpec.groovy b/http-server-netty/src/test/groovy/io/micronaut/http/server/netty/filters/FilterErrorSpec.groovy index f8bb4e573b3..f89557b011e 100644 --- a/http-server-netty/src/test/groovy/io/micronaut/http/server/netty/filters/FilterErrorSpec.groovy +++ b/http-server-netty/src/test/groovy/io/micronaut/http/server/netty/filters/FilterErrorSpec.groovy @@ -4,7 +4,6 @@ import io.micronaut.context.ApplicationContext import io.micronaut.context.annotation.Requires import io.micronaut.core.async.publisher.Publishers import io.micronaut.core.util.StringUtils -import io.micronaut.http.HttpAttributes import io.micronaut.http.HttpRequest import io.micronaut.http.HttpResponse import io.micronaut.http.HttpStatus @@ -20,6 +19,7 @@ import io.micronaut.http.filter.HttpServerFilter import io.micronaut.http.filter.ServerFilterChain import io.micronaut.runtime.server.EmbeddedServer import io.micronaut.web.router.MethodBasedRouteMatch +import io.micronaut.web.router.RouteAttributes import io.micronaut.web.router.RouteMatch import org.reactivestreams.Publisher import reactor.core.publisher.Flux @@ -291,7 +291,7 @@ class FilterErrorSpec extends Specification { @Override Publisher> doFilter(HttpRequest request, ServerFilterChain chain) { return Publishers.then(chain.proceed(request), { resp -> - routeMatch.set(resp.getAttribute(HttpAttributes.ROUTE_MATCH, RouteMatch).get()) + routeMatch.set(RouteAttributes.getRouteMatch(resp).get()) }) } diff --git a/http-server-netty/src/test/groovy/io/micronaut/http/server/netty/filters/ServerPreFilterSpec.groovy b/http-server-netty/src/test/groovy/io/micronaut/http/server/netty/filters/ServerPreFilterSpec.groovy index f3c9bed19c2..8a2d1fcb93b 100644 --- a/http-server-netty/src/test/groovy/io/micronaut/http/server/netty/filters/ServerPreFilterSpec.groovy +++ b/http-server-netty/src/test/groovy/io/micronaut/http/server/netty/filters/ServerPreFilterSpec.groovy @@ -3,7 +3,7 @@ package io.micronaut.http.server.netty.filters import io.micronaut.context.ApplicationContext import io.micronaut.context.annotation.Requires import io.micronaut.core.annotation.Order -import io.micronaut.http.HttpAttributes +import io.micronaut.http.BasicHttpAttributes import io.micronaut.http.HttpRequest import io.micronaut.http.HttpResponse import io.micronaut.http.MutableHttpRequest @@ -18,6 +18,7 @@ import io.micronaut.http.server.annotation.PreMatching import io.micronaut.runtime.server.EmbeddedServer import io.micronaut.scheduling.TaskExecutors import io.micronaut.scheduling.annotation.ExecuteOn +import io.micronaut.web.router.RouteAttributes import jakarta.inject.Singleton import spock.lang.Specification @@ -60,13 +61,13 @@ class ServerPreFilterSpec extends Specification { @PreMatching @RequestFilter void preMatchingRequest(HttpRequest request) { - if (request.getAttribute(HttpAttributes.ROUTE_INFO).isPresent()) { + if (RouteAttributes.getRouteInfo(request).isPresent()) { throw new IllegalStateException() } - if (request.getAttribute(HttpAttributes.ROUTE_MATCH).isPresent()) { + if (RouteAttributes.getRouteMatch(request).isPresent()) { throw new IllegalStateException() } - if (request.getAttribute(HttpAttributes.URI_TEMPLATE).isPresent()) { + if (BasicHttpAttributes.getUriTemplate(request).isPresent()) { throw new IllegalStateException() } events.add("prematching " + request.uri) @@ -161,13 +162,13 @@ class ServerPreFilterSpec extends Specification { } private static void validateRoutes(HttpRequest request) { - if (request.getAttribute(HttpAttributes.ROUTE_INFO).isPresent()) { + if (RouteAttributes.getRouteInfo(request).isPresent()) { throw new IllegalStateException() } - if (request.getAttribute(HttpAttributes.ROUTE_MATCH).isPresent()) { + if (RouteAttributes.getRouteMatch(request).isPresent()) { throw new IllegalStateException() } - if (request.getAttribute(HttpAttributes.URI_TEMPLATE).isPresent()) { + if (BasicHttpAttributes.getUriTemplate(request).isPresent()) { throw new IllegalStateException() } } @@ -202,13 +203,13 @@ class ServerPreFilterSpec extends Specification { @PreMatching @RequestFilter void preMatchingRequest(HttpRequest request) { - if (request.getAttribute(HttpAttributes.ROUTE_INFO).isPresent()) { + if (RouteAttributes.getRouteInfo(request).isPresent()) { throw new IllegalStateException() } - if (request.getAttribute(HttpAttributes.ROUTE_MATCH).isPresent()) { + if (RouteAttributes.getRouteMatch(request).isPresent()) { throw new IllegalStateException() } - if (request.getAttribute(HttpAttributes.URI_TEMPLATE).isPresent()) { + if (BasicHttpAttributes.getUriTemplate(request).isPresent()) { throw new IllegalStateException() } events.add("prematching " + request.uri) @@ -335,25 +336,25 @@ class ServerPreFilterSpec extends Specification { @PreMatching @RequestFilter void preMatchingRequest(HttpRequest request, FilterContinuation> continuation) { - if (request.getAttribute(HttpAttributes.ROUTE_INFO).isPresent()) { + if (RouteAttributes.getRouteInfo(request).isPresent()) { throw new IllegalStateException() } - if (request.getAttribute(HttpAttributes.ROUTE_MATCH).isPresent()) { + if (RouteAttributes.getRouteMatch(request).isPresent()) { throw new IllegalStateException() } - if (request.getAttribute(HttpAttributes.URI_TEMPLATE).isPresent()) { + if (BasicHttpAttributes.getUriTemplate(request).isPresent()) { throw new IllegalStateException() } events.add("prematching " + request.uri) def response = continuation.proceed() events.add("after prematching " + request.uri) - if (request.getAttribute(HttpAttributes.ROUTE_INFO).isEmpty()) { + if (RouteAttributes.getRouteInfo(request).isEmpty()) { throw new IllegalStateException() } - if (request.getAttribute(HttpAttributes.ROUTE_MATCH).isEmpty()) { + if (RouteAttributes.getRouteMatch(request).isEmpty()) { throw new IllegalStateException() } - if (request.getAttribute(HttpAttributes.URI_TEMPLATE).isEmpty()) { + if (BasicHttpAttributes.getUriTemplate(request).isEmpty()) { throw new IllegalStateException() } } diff --git a/http-server-netty/src/test/groovy/io/micronaut/http/server/netty/interceptor/HttpFilterSpec.groovy b/http-server-netty/src/test/groovy/io/micronaut/http/server/netty/interceptor/HttpFilterSpec.groovy index 7fc9ac02fa5..891952c0de8 100644 --- a/http-server-netty/src/test/groovy/io/micronaut/http/server/netty/interceptor/HttpFilterSpec.groovy +++ b/http-server-netty/src/test/groovy/io/micronaut/http/server/netty/interceptor/HttpFilterSpec.groovy @@ -15,12 +15,15 @@ */ package io.micronaut.http.server.netty.interceptor -import io.micronaut.core.async.annotation.SingleResult import io.micronaut.context.annotation.AliasFor import io.micronaut.context.annotation.Property import io.micronaut.context.annotation.Requires -import io.micronaut.core.annotation.AnnotationMetadata -import io.micronaut.http.* +import io.micronaut.core.async.annotation.SingleResult +import io.micronaut.http.HttpMethod +import io.micronaut.http.HttpRequest +import io.micronaut.http.HttpResponse +import io.micronaut.http.HttpStatus +import io.micronaut.http.MutableHttpResponse import io.micronaut.http.annotation.Controller import io.micronaut.http.annotation.Filter import io.micronaut.http.annotation.FilterMatcher @@ -31,6 +34,7 @@ import io.micronaut.http.client.exceptions.HttpClientResponseException import io.micronaut.http.filter.HttpServerFilter import io.micronaut.http.filter.ServerFilterChain import io.micronaut.test.extensions.spock.annotation.MicronautTest +import io.micronaut.web.router.RouteAttributes import jakarta.inject.Inject import org.reactivestreams.Publisher import reactor.core.publisher.Flux @@ -130,8 +134,7 @@ class HttpFilterSpec extends Specification { Publisher> doFilter(HttpRequest request, ServerFilterChain chain) { return Flux.from(chain.proceed(request)).doOnNext({ response -> if (response.status.code < 300) { - assert response.getAttribute(HttpAttributes.ROUTE_MATCH, - AnnotationMetadata.class).isPresent() + assert RouteAttributes.getRouteMatch(request).isPresent() } response.header("X-Root-Filter", "processed") }) diff --git a/http-server-netty/src/test/groovy/io/micronaut/websocket/WebsocketRouteMatchSpec.groovy b/http-server-netty/src/test/groovy/io/micronaut/websocket/WebsocketRouteMatchSpec.groovy index 0e40fb3c66e..c10e56481b4 100644 --- a/http-server-netty/src/test/groovy/io/micronaut/websocket/WebsocketRouteMatchSpec.groovy +++ b/http-server-netty/src/test/groovy/io/micronaut/websocket/WebsocketRouteMatchSpec.groovy @@ -2,7 +2,6 @@ package io.micronaut.websocket import io.micronaut.context.annotation.Property import io.micronaut.context.annotation.Requires -import io.micronaut.http.HttpAttributes import io.micronaut.http.HttpRequest import io.micronaut.http.HttpResponse import io.micronaut.http.MutableHttpRequest @@ -12,6 +11,7 @@ import io.micronaut.http.filter.HttpServerFilter import io.micronaut.http.filter.ServerFilterChain import io.micronaut.runtime.server.EmbeddedServer import io.micronaut.test.extensions.spock.annotation.MicronautTest +import io.micronaut.web.router.RouteAttributes import io.micronaut.web.router.RouteMatch import io.micronaut.websocket.annotation.ClientWebSocket import io.micronaut.websocket.annotation.OnClose @@ -98,7 +98,7 @@ class WebsocketRouteMatchSpec extends Specification { static class SecurityFilter implements HttpServerFilter { @Override Publisher> doFilter(HttpRequest request, ServerFilterChain chain) { - RouteMatch routeMatch = request.getAttribute(HttpAttributes.ROUTE_MATCH, RouteMatch.class).orElse(null) + RouteMatch routeMatch = RouteAttributes.getRouteMatch(request).orElse(null) routeMatch != null ? chain.proceed(request) : Mono.just(HttpResponse.serverError()) } } diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FilterErrorTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FilterErrorTest.java index 010dc7d1d41..b3a3fd877a9 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FilterErrorTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/FilterErrorTest.java @@ -20,7 +20,6 @@ import io.micronaut.context.condition.ConditionContext; import io.micronaut.core.async.publisher.Publishers; import io.micronaut.core.util.StringUtils; -import io.micronaut.http.HttpAttributes; import io.micronaut.http.HttpHeaders; import io.micronaut.http.HttpRequest; import io.micronaut.http.HttpResponse; @@ -38,6 +37,7 @@ import io.micronaut.http.tck.AssertionUtils; import io.micronaut.http.tck.HttpResponseAssertion; import io.micronaut.web.router.MethodBasedRouteMatch; +import io.micronaut.web.router.RouteAttributes; import io.micronaut.web.router.RouteMatch; import jakarta.inject.Singleton; import org.junit.jupiter.api.Test; @@ -245,7 +245,7 @@ static class ExceptionRoute implements HttpServerFilter { @Override public Publisher> doFilter(HttpRequest request, ServerFilterChain chain) { return Publishers.then(chain.proceed(request), - httpResponse -> routeMatch.set(httpResponse.getAttribute(HttpAttributes.ROUTE_MATCH, RouteMatch.class).get())); + httpResponse -> routeMatch.set(RouteAttributes.getRouteMatch(httpResponse).get())); } @Override diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/filter/HttpServerFilterTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/filter/HttpServerFilterTest.java index 42a69bad845..162782fb68d 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/filter/HttpServerFilterTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/filter/HttpServerFilterTest.java @@ -16,7 +16,6 @@ package io.micronaut.http.server.tck.tests.filter; import io.micronaut.context.annotation.Requires; -import io.micronaut.http.HttpAttributes; import io.micronaut.http.HttpHeaders; import io.micronaut.http.HttpRequest; import io.micronaut.http.HttpResponse; @@ -32,6 +31,7 @@ import io.micronaut.http.tck.ServerUnderTest; import io.micronaut.http.tck.TestScenario; import io.micronaut.web.router.MethodBasedRouteMatch; +import io.micronaut.web.router.RouteAttributes; import io.micronaut.web.router.RouteMatch; import jakarta.annotation.security.RolesAllowed; import org.junit.jupiter.api.Test; @@ -95,7 +95,7 @@ static class SecurityFilter implements HttpServerFilter { @Override public Publisher> doFilter(HttpRequest request, ServerFilterChain chain) { - RouteMatch routeMatch = request.getAttribute(HttpAttributes.ROUTE_MATCH, RouteMatch.class).orElse(null); + RouteMatch routeMatch = RouteAttributes.getRouteMatch(request).orElse(null); if (routeMatch instanceof MethodBasedRouteMatch match) { MethodBasedRouteMatch methodRoute = match; if (methodRoute.hasAnnotation(RolesAllowed.class)) { diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/filter/ResponseFilterTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/filter/ResponseFilterTest.java index 4b7c48b1a75..ad3d1e7320e 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/filter/ResponseFilterTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/filter/ResponseFilterTest.java @@ -17,7 +17,6 @@ import io.micronaut.context.annotation.Requires; import io.micronaut.core.annotation.Nullable; -import io.micronaut.http.HttpAttributes; import io.micronaut.http.HttpRequest; import io.micronaut.http.HttpResponse; import io.micronaut.http.HttpStatus; @@ -30,6 +29,7 @@ import io.micronaut.http.tck.AssertionUtils; import io.micronaut.http.tck.HttpResponseAssertion; import io.micronaut.http.tck.TestScenario; +import io.micronaut.web.router.RouteAttributes; import jakarta.inject.Singleton; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -338,7 +338,7 @@ public CompletionStage> responseFilterReplaceCompletionRe @ResponseFilter("/response-filter/head-body") public void responseFilterHeadBody(MutableHttpResponse response) { - response.header("X-HEAD-BODY", response.getAttribute(HttpAttributes.HEAD_BODY).map(o -> (String) o).orElse("")); + response.header("X-HEAD-BODY", RouteAttributes.getHeadBody(response).map(o -> (String) o).orElse("")); } } diff --git a/http-server/src/main/java/io/micronaut/http/server/OptionsFilter.java b/http-server/src/main/java/io/micronaut/http/server/OptionsFilter.java index eeddd92825f..8fd2f398f32 100644 --- a/http-server/src/main/java/io/micronaut/http/server/OptionsFilter.java +++ b/http-server/src/main/java/io/micronaut/http/server/OptionsFilter.java @@ -20,7 +20,6 @@ import io.micronaut.core.annotation.Nullable; import io.micronaut.core.order.Ordered; import io.micronaut.core.util.StringUtils; -import io.micronaut.http.HttpAttributes; import io.micronaut.http.HttpHeaders; import io.micronaut.http.HttpMethod; import io.micronaut.http.HttpRequest; @@ -30,7 +29,7 @@ import io.micronaut.http.annotation.ResponseFilter; import io.micronaut.http.annotation.ServerFilter; import io.micronaut.http.server.cors.CorsUtil; -import io.micronaut.web.router.RouteMatch; +import io.micronaut.web.router.RouteAttributes; import io.micronaut.web.router.UriRouteMatch; import java.util.ArrayList; @@ -79,7 +78,7 @@ public HttpResponse filterResponse(HttpRequest request, MutableHttpRespons } private boolean hasOptionsRouteMatch(HttpRequest request) { - return request.getAttribute(HttpAttributes.ROUTE_MATCH, RouteMatch.class).map(routeMatch -> { + return RouteAttributes.getRouteMatch(request).map(routeMatch -> { if (routeMatch instanceof UriRouteMatch uriRouteMatch) { return uriRouteMatch.getHttpMethod() == HttpMethod.OPTIONS; } diff --git a/http-server/src/main/java/io/micronaut/http/server/RequestLifecycle.java b/http-server/src/main/java/io/micronaut/http/server/RequestLifecycle.java index bb53ad1bd2e..2d479c3bd0a 100644 --- a/http-server/src/main/java/io/micronaut/http/server/RequestLifecycle.java +++ b/http-server/src/main/java/io/micronaut/http/server/RequestLifecycle.java @@ -25,7 +25,6 @@ import io.micronaut.core.propagation.PropagatedContext; import io.micronaut.core.type.ReturnType; import io.micronaut.core.util.CollectionUtils; -import io.micronaut.http.HttpAttributes; import io.micronaut.http.HttpMethod; import io.micronaut.http.HttpRequest; import io.micronaut.http.HttpResponse; @@ -36,7 +35,12 @@ import io.micronaut.http.exceptions.HttpStatusException; import io.micronaut.http.filter.FilterRunner; import io.micronaut.http.filter.GenericHttpFilter; -import io.micronaut.http.server.exceptions.*; +import io.micronaut.http.server.exceptions.ExceptionHandler; +import io.micronaut.http.server.exceptions.NotAcceptableException; +import io.micronaut.http.server.exceptions.NotAllowedException; +import io.micronaut.http.server.exceptions.NotFoundException; +import io.micronaut.http.server.exceptions.NotWebSocketRequestException; +import io.micronaut.http.server.exceptions.UnsupportedMediaException; import io.micronaut.http.server.exceptions.response.ErrorContext; import io.micronaut.http.server.types.files.FileCustomizableResponseType; import io.micronaut.inject.BeanDefinition; @@ -45,6 +49,7 @@ import io.micronaut.json.JsonSyntaxException; import io.micronaut.web.router.DefaultRouteInfo; import io.micronaut.web.router.DefaultUriRouteMatch; +import io.micronaut.web.router.RouteAttributes; import io.micronaut.web.router.RouteInfo; import io.micronaut.web.router.RouteMatch; import io.micronaut.web.router.UriRouteMatch; @@ -257,7 +262,7 @@ private ExecutionFlow> onErrorNoFilter(HttpRequest request, T private Class findDeclaringType(HttpRequest request) { // find the origination of the route - Optional previousRequestRouteInfo = request.getAttribute(HttpAttributes.ROUTE_INFO, RouteInfo.class); + Optional> previousRequestRouteInfo = RouteAttributes.getRouteInfo(request); return previousRequestRouteInfo.map(RouteInfo::getDeclaringType).orElse(null); } @@ -273,7 +278,7 @@ private ExecutionFlow> handleErrorRoute(HttpRequest request, ) .onErrorResume(u -> createDefaultErrorResponseFlow(request, u)) .>map(response -> { - response.setAttribute(HttpAttributes.EXCEPTION, cause); + RouteAttributes.setException(response, cause); return response; }) .onErrorResume(throwable -> createDefaultErrorResponseFlow(request, throwable)); @@ -320,7 +325,7 @@ private ExecutionFlow> handlerExceptionHandler(HttpRequest re } return responseFlow .>map(response -> { - response.setAttribute(HttpAttributes.EXCEPTION, cause); + RouteAttributes.setException(response, cause); return response; }) .onErrorResume(throwable -> createDefaultErrorResponseFlow(request, throwable)); @@ -339,7 +344,7 @@ protected final ExecutionFlow> runWithFilters(HttpRequest req FilterRunner filterRunner = new FilterRunner(httpFilters, responseProvider) { @Override protected ExecutionFlow> processResponse(HttpRequest request, HttpResponse response, PropagatedContext propagatedContext) { - RouteInfo routeInfo = response.getAttribute(HttpAttributes.ROUTE_INFO, RouteInfo.class).orElse(null); + RouteInfo routeInfo = RouteAttributes.getRouteInfo(response).orElse(null); return handleStatusException(request, response, routeInfo, propagatedContext) .onErrorResume(throwable -> onErrorNoFilter(request, throwable, propagatedContext)); } @@ -409,7 +414,7 @@ protected void doRouteMatch(HttpRequest request) { @Override protected ExecutionFlow> processResponse(HttpRequest request, HttpResponse response, PropagatedContext propagatedContext) { - RouteInfo routeInfo = response.getAttribute(HttpAttributes.ROUTE_INFO, RouteInfo.class).orElse(null); + RouteInfo routeInfo = RouteAttributes.getRouteInfo(response).orElse(null); return handleStatusException(request, response, routeInfo, propagatedContext) .onErrorResume(throwable -> onErrorNoFilter(request, throwable, propagatedContext)); } diff --git a/http-server/src/main/java/io/micronaut/http/server/ResponseLifecycle.java b/http-server/src/main/java/io/micronaut/http/server/ResponseLifecycle.java index 38c52407ff4..14a7bc8467b 100644 --- a/http-server/src/main/java/io/micronaut/http/server/ResponseLifecycle.java +++ b/http-server/src/main/java/io/micronaut/http/server/ResponseLifecycle.java @@ -24,7 +24,6 @@ import io.micronaut.core.type.Argument; import io.micronaut.http.ByteBodyHttpResponse; import io.micronaut.http.ByteBodyHttpResponseWrapper; -import io.micronaut.http.HttpAttributes; import io.micronaut.http.HttpMethod; import io.micronaut.http.HttpRequest; import io.micronaut.http.HttpResponse; @@ -43,6 +42,7 @@ import io.micronaut.http.exceptions.HttpStatusException; import io.micronaut.http.reactive.execution.ReactiveExecutionFlow; import io.micronaut.web.router.DefaultUrlRouteInfo; +import io.micronaut.web.router.RouteAttributes; import io.micronaut.web.router.RouteInfo; import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; @@ -132,7 +132,7 @@ private ExecutionFlow> encodeHttpResponse( Object body) { MutableHttpResponse response = httpResponse.toMutableResponse(); if (nettyRequest.getMethod() != HttpMethod.HEAD && body != null) { - Object routeInfoO = response.getAttribute(HttpAttributes.ROUTE_INFO).orElse(null); + Object routeInfoO = RouteAttributes.getRouteInfo(response).orElse(null); // usually this is a UriRouteInfo, avoid scalability issues here @SuppressWarnings("unchecked") final RouteInfo routeInfo = (RouteInfo) (routeInfoO instanceof DefaultUrlRouteInfo uri ? uri : (RouteInfo) routeInfoO); diff --git a/http-server/src/main/java/io/micronaut/http/server/RouteExecutor.java b/http-server/src/main/java/io/micronaut/http/server/RouteExecutor.java index eee284e50a1..50c248889b2 100644 --- a/http-server/src/main/java/io/micronaut/http/server/RouteExecutor.java +++ b/http-server/src/main/java/io/micronaut/http/server/RouteExecutor.java @@ -30,7 +30,7 @@ import io.micronaut.core.propagation.PropagatedContext; import io.micronaut.core.type.Argument; import io.micronaut.core.type.ReturnType; -import io.micronaut.http.HttpAttributes; +import io.micronaut.http.BasicHttpAttributes; import io.micronaut.http.HttpHeaders; import io.micronaut.http.HttpMethod; import io.micronaut.http.HttpRequest; @@ -56,6 +56,7 @@ import io.micronaut.scheduling.instrument.InstrumentedScheduledExecutorService; import io.micronaut.web.router.DefaultRouteInfo; import io.micronaut.web.router.MethodBasedRouteInfo; +import io.micronaut.web.router.RouteAttributes; import io.micronaut.web.router.RouteInfo; import io.micronaut.web.router.RouteMatch; import io.micronaut.web.router.Router; @@ -182,12 +183,12 @@ UriRouteMatch findRouteMatch(HttpRequest httpRequest) { static void setRouteAttributes(HttpRequest request, UriRouteMatch route) { setRouteAttributes(request, (RouteMatch) route); - request.setAttribute(HttpAttributes.URI_TEMPLATE, route.getRouteInfo().getUriMatchTemplate().toString()); + BasicHttpAttributes.setUriTemplate(request, route.getRouteInfo().getUriMatchTemplate().toString()); } static void setRouteAttributes(HttpRequest request, RouteMatch route) { - request.setAttribute(HttpAttributes.ROUTE_MATCH, route); - request.setAttribute(HttpAttributes.ROUTE_INFO, route.getRouteInfo()); + RouteAttributes.setRouteMatch(request, route); + RouteAttributes.setRouteInfo(request, route.getRouteInfo()); } /** @@ -202,8 +203,8 @@ public MutableHttpResponse createDefaultErrorResponse(HttpRequest httpRequ Throwable cause) { logException(cause); MutableHttpResponse mutableHttpResponse = HttpResponse.serverError(); - mutableHttpResponse.setAttribute(HttpAttributes.EXCEPTION, cause); - mutableHttpResponse.setAttribute(HttpAttributes.ROUTE_INFO, new DefaultRouteInfo<>( + RouteAttributes.setException(mutableHttpResponse, cause); + RouteAttributes.setRouteInfo(mutableHttpResponse, new DefaultRouteInfo<>( ReturnType.of(MutableHttpResponse.class, Argument.OBJECT_ARGUMENT), Object.class, true, @@ -567,13 +568,15 @@ private MutableHttpResponse finaliseResponse(HttpRequest request, RouteInf referenceCounted.release(); } response.body(null); - response.attribute(HttpAttributes.HEAD_BODY, o); + if (o != null) { + RouteAttributes.setHeadBody(response, o); + } } applyConfiguredHeaders(response.getHeaders()); if (routeMatch != null) { - response.setAttribute(HttpAttributes.ROUTE_MATCH, routeMatch); + RouteAttributes.setRouteMatch(response, routeMatch); } - response.setAttribute(HttpAttributes.ROUTE_INFO, routeInfo); + RouteAttributes.setRouteInfo(response, routeInfo); response.bodyWriter((MessageBodyWriter) routeInfo.getMessageBodyWriter()); return response; } diff --git a/http-server/src/main/java/io/micronaut/http/server/binding/RouteInfoArgumentBinder.java b/http-server/src/main/java/io/micronaut/http/server/binding/RouteInfoArgumentBinder.java index 2d964c57c12..c1ccf6a98e5 100644 --- a/http-server/src/main/java/io/micronaut/http/server/binding/RouteInfoArgumentBinder.java +++ b/http-server/src/main/java/io/micronaut/http/server/binding/RouteInfoArgumentBinder.java @@ -20,11 +20,11 @@ import io.micronaut.core.convert.ArgumentConversionContext; import io.micronaut.core.propagation.MutablePropagatedContext; import io.micronaut.core.type.Argument; -import io.micronaut.http.HttpAttributes; import io.micronaut.http.HttpRequest; import io.micronaut.http.HttpResponse; import io.micronaut.http.bind.binders.TypedRequestArgumentBinder; import io.micronaut.http.filter.FilterArgumentBinderPredicate; +import io.micronaut.web.router.RouteAttributes; import io.micronaut.web.router.RouteInfo; import io.micronaut.web.router.RouteMatch; import jakarta.inject.Singleton; @@ -48,12 +48,12 @@ public Argument> argumentType() { @Override public BindingResult> bind(ArgumentConversionContext> context, HttpRequest source) { - return () -> source.getAttribute(HttpAttributes.ROUTE_INFO).>map(r1 -> (RouteInfo) r1) - .or(() -> source.getAttribute(HttpAttributes.ROUTE_MATCH).>map(r -> ((RouteMatch) r).getRouteInfo())); + return () -> RouteAttributes.getRouteInfo(source) + .or(() -> RouteAttributes.getRouteMatch(source).>map(RouteMatch::getRouteInfo)); } @Override public boolean test(Argument argument, MutablePropagatedContext mutablePropagatedContext, HttpRequest request, @Nullable HttpResponse response, @Nullable Throwable failure) { - return argument.isNullable() || request.getAttribute(HttpAttributes.ROUTE_MATCH).isPresent() || request.getAttribute(HttpAttributes.ROUTE_INFO).isPresent(); + return argument.isNullable() || RouteAttributes.getRouteMatch(request).isPresent() || RouteAttributes.getRouteInfo(request).isPresent(); } } diff --git a/http-server/src/main/java/io/micronaut/http/server/binding/RouteMatchArgumentBinder.java b/http-server/src/main/java/io/micronaut/http/server/binding/RouteMatchArgumentBinder.java index 240f1c59e65..bf6a993c5e7 100644 --- a/http-server/src/main/java/io/micronaut/http/server/binding/RouteMatchArgumentBinder.java +++ b/http-server/src/main/java/io/micronaut/http/server/binding/RouteMatchArgumentBinder.java @@ -20,11 +20,11 @@ import io.micronaut.core.convert.ArgumentConversionContext; import io.micronaut.core.propagation.MutablePropagatedContext; import io.micronaut.core.type.Argument; -import io.micronaut.http.HttpAttributes; import io.micronaut.http.HttpRequest; import io.micronaut.http.HttpResponse; import io.micronaut.http.bind.binders.TypedRequestArgumentBinder; import io.micronaut.http.filter.FilterArgumentBinderPredicate; +import io.micronaut.web.router.RouteAttributes; import io.micronaut.web.router.RouteMatch; import jakarta.inject.Singleton; @@ -49,12 +49,12 @@ public Argument> argumentType() { @Override public BindingResult> bind(ArgumentConversionContext> context, HttpRequest source) { - Optional> match = source.getAttribute(HttpAttributes.ROUTE_MATCH).map(r -> (RouteMatch) r); + Optional> match = RouteAttributes.getRouteMatch(source); return () -> match; } @Override public boolean test(Argument argument, MutablePropagatedContext mutablePropagatedContext, HttpRequest request, @Nullable HttpResponse response, @Nullable Throwable failure) { - return argument.isNullable() || request.getAttribute(HttpAttributes.ROUTE_MATCH).isPresent(); + return argument.isNullable() || RouteAttributes.getRouteMatch(request).isPresent(); } } diff --git a/http-server/src/main/java/io/micronaut/http/server/cors/CrossOriginUtil.java b/http-server/src/main/java/io/micronaut/http/server/cors/CrossOriginUtil.java index c348723581c..f5b58e7c171 100644 --- a/http-server/src/main/java/io/micronaut/http/server/cors/CrossOriginUtil.java +++ b/http-server/src/main/java/io/micronaut/http/server/cors/CrossOriginUtil.java @@ -18,9 +18,9 @@ import io.micronaut.core.annotation.AnnotationMetadata; import io.micronaut.core.annotation.NonNull; import io.micronaut.core.util.CollectionUtils; -import io.micronaut.http.HttpAttributes; import io.micronaut.http.HttpMethod; import io.micronaut.http.HttpRequest; +import io.micronaut.web.router.RouteAttributes; import java.util.Arrays; import java.util.List; @@ -53,8 +53,8 @@ private CrossOriginUtil() { */ @NonNull public static Optional getCorsOriginConfigurationForRequest(@NonNull HttpRequest request) { - return request.getAttribute(HttpAttributes.ROUTE_MATCH, AnnotationMetadata.class) - .flatMap(CrossOriginUtil::getCorsOriginConfiguration); + return RouteAttributes.getRouteMatch(request) + .flatMap(rm -> getCorsOriginConfiguration((AnnotationMetadata) rm)); } /** diff --git a/http/src/main/java/io/micronaut/http/BasicHttpAttributes.java b/http/src/main/java/io/micronaut/http/BasicHttpAttributes.java new file mode 100644 index 00000000000..6b34f51b03a --- /dev/null +++ b/http/src/main/java/io/micronaut/http/BasicHttpAttributes.java @@ -0,0 +1,75 @@ +/* + * Copyright 2017-2025 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.http; + +import io.micronaut.core.annotation.NonNull; +import io.micronaut.http.uri.UriMatchInfo; + +import java.util.Optional; + +/** + * Accessors for basic attributes outside micronaut-http-router. + * + * @author Jonas Konrad + * @since 4.8.0 + */ +@SuppressWarnings("removal") +public final class BasicHttpAttributes { + private BasicHttpAttributes() { + } + + /** + * Get the route match as a {@link UriMatchInfo}. + * + * @param request The request + * @return The route match, if present + */ + public static Optional getRouteMatchInfo(HttpRequest request) { + return request.getAttribute(HttpAttributes.ROUTE_MATCH, UriMatchInfo.class); + } + + /** + * Get the URI template as a String, for tracing. + * + * @param request The request + * @return The template, if present + */ + @NonNull + public static Optional getUriTemplate(HttpRequest request) { + return request.getAttribute(HttpAttributes.URI_TEMPLATE, String.class); + } + + /** + * Set the URI template as a String, for tracing. + * + * @param request The request + * @param uriTemplate The template, if present + */ + public static void setUriTemplate(@NonNull HttpRequest request, @NonNull String uriTemplate) { + request.setAttribute(HttpAttributes.URI_TEMPLATE, uriTemplate); + } + + /** + * Get the client service ID. + * + * @param request The request + * @return The client service ID + */ + @NonNull + public static Optional getServiceId(@NonNull HttpRequest request) { + return request.getAttribute(HttpAttributes.SERVICE_ID, String.class); + } +} diff --git a/http/src/main/java/io/micronaut/http/HttpAttributes.java b/http/src/main/java/io/micronaut/http/HttpAttributes.java index 2d35a14f81d..7eb412bc2a8 100644 --- a/http/src/main/java/io/micronaut/http/HttpAttributes.java +++ b/http/src/main/java/io/micronaut/http/HttpAttributes.java @@ -15,62 +15,96 @@ */ package io.micronaut.http; +import java.security.Principal; + /** * Common HTTP attributes. * * @author graemerocher * @since 1.0 + * @deprecated In order to make future optimizations possible, standard attributes should be + * accessed through their static method accessors (such as those in {@link BasicHttpAttributes}) + * instead of directly through {@link io.micronaut.core.attr.AttributeHolder}. */ +@Deprecated(forRemoval = true, since = "4.8.0") public enum HttpAttributes implements CharSequence { /** * Attribute used to store the {@link java.security.Principal}. + * + * @deprecated Use {@link HttpRequest#getUserPrincipal()} and {@link HttpRequest#setUserPrincipal(Principal)} */ + @Deprecated(forRemoval = true, since = "4.8.0") PRINCIPAL("micronaut.AUTHENTICATION"), /** * Attribute used to store any exception that may have occurred during request processing. */ + @Deprecated(forRemoval = true, since = "4.8.0") ERROR(Constants.PREFIX + ".error"), /** * Attribute used to store the object that represents the Route match. + * + * @deprecated Please use the accessors in RouteAttributes */ + @Deprecated(forRemoval = true, since = "4.8.0") ROUTE_MATCH(Constants.PREFIX + ".route.match"), /** * Attribute used to store the object that represents the Route. + * + * @deprecated Please use the accessors in RouteAttributes */ + @Deprecated(forRemoval = true, since = "4.8.0") ROUTE_INFO(Constants.PREFIX + ".route.info"), /** * Attribute used to store the URI template defined by the route. + * + * @deprecated Use {@link BasicHttpAttributes#getUriTemplate} instead */ + @Deprecated(forRemoval = true, since = "4.8.0") URI_TEMPLATE(Constants.PREFIX + ".route.template"), /** * Attribute used to store the HTTP method name, if required within the response. + * + * @deprecated No replacement. Use your own attribute if necessary */ + @Deprecated(forRemoval = true, since = "4.8.0") METHOD_NAME(Constants.PREFIX + ".method.name"), /** * Attribute used to store the service ID a client request is being sent to. Used for tracing purposes. + * + * @deprecated Use {@link BasicHttpAttributes#getServiceId} */ + @Deprecated(forRemoval = true, since = "4.8.0") SERVICE_ID(Constants.PREFIX + ".serviceId"), /** * Attribute used to store the MediaTypeCodec. Used to override the registered codec per-request. + * + * @deprecated Unused */ + @Deprecated(forRemoval = true, since = "4.8.0") MEDIA_TYPE_CODEC(Constants.PREFIX + ".mediaType.codec"), /** * Attribute used to store the MethodInvocationContext by declarative client. + * + * @deprecated Please use accessors in ClientAttributes instead */ + @Deprecated(forRemoval = true, since = "4.8.0") INVOCATION_CONTEXT(Constants.PREFIX + ".invocationContext"), /** * Attribute used to store the cause of an error response. + * + * @deprecated Please use the accessors in RouteAttributes */ + @Deprecated(forRemoval = true, since = "4.8.0") EXCEPTION(Constants.PREFIX + ".exception"), /** @@ -78,7 +112,7 @@ public enum HttpAttributes implements CharSequence { * * @deprecated Use {@link HttpRequest#getCertificate()} instead */ - @Deprecated + @Deprecated(forRemoval = true, since = "4.8.0") X509_CERTIFICATE("javax.servlet.request.X509Certificate"), /** @@ -90,7 +124,10 @@ public enum HttpAttributes implements CharSequence { /** * The message body writer. + * + * @deprecated Use accessors in {@link HttpMessage} instead */ + @Deprecated(forRemoval = true, since = "4.8.0") MESSAGE_BODY_WRITER(Constants.PREFIX + ".messageBodyWriter"), /** diff --git a/http/src/main/java/io/micronaut/http/HttpRequest.java b/http/src/main/java/io/micronaut/http/HttpRequest.java index 9d458fa9a21..d712aa0de20 100644 --- a/http/src/main/java/io/micronaut/http/HttpRequest.java +++ b/http/src/main/java/io/micronaut/http/HttpRequest.java @@ -125,6 +125,20 @@ default Collection accept() { return getAttribute(HttpAttributes.PRINCIPAL, principalType); } + /** + * Set the user principal. + * + * @param principal The principal + * @since 4.8.0 + */ + default void setUserPrincipal(@Nullable Principal principal) { + if (principal != null) { + setAttribute(HttpAttributes.PRINCIPAL, principal); + } else { + removeAttribute(HttpAttributes.PRINCIPAL, Principal.class); + } + } + /** * @return Get the raw, percent-encoded path without any parameters */ diff --git a/http/src/main/java/io/micronaut/http/bind/binders/PathVariableAnnotationBinder.java b/http/src/main/java/io/micronaut/http/bind/binders/PathVariableAnnotationBinder.java index fe876b6787a..15c4456b5e7 100644 --- a/http/src/main/java/io/micronaut/http/bind/binders/PathVariableAnnotationBinder.java +++ b/http/src/main/java/io/micronaut/http/bind/binders/PathVariableAnnotationBinder.java @@ -22,7 +22,7 @@ import io.micronaut.core.convert.value.ConvertibleMultiValues; import io.micronaut.core.convert.value.ConvertibleValues; import io.micronaut.core.type.Argument; -import io.micronaut.http.HttpAttributes; +import io.micronaut.http.BasicHttpAttributes; import io.micronaut.http.HttpRequest; import io.micronaut.http.annotation.PathVariable; import io.micronaut.http.uri.UriMatchInfo; @@ -68,7 +68,7 @@ public BindingResult bind(ArgumentConversionContext context, HttpRequest variableValues = ConvertibleValues.of(matchInfo.getVariableValues(), conversionService); diff --git a/http/src/main/java/io/micronaut/http/bind/binders/QueryValueArgumentBinder.java b/http/src/main/java/io/micronaut/http/bind/binders/QueryValueArgumentBinder.java index e216876c07c..6724381b98c 100644 --- a/http/src/main/java/io/micronaut/http/bind/binders/QueryValueArgumentBinder.java +++ b/http/src/main/java/io/micronaut/http/bind/binders/QueryValueArgumentBinder.java @@ -22,10 +22,9 @@ import io.micronaut.core.convert.format.Format; import io.micronaut.core.convert.value.ConvertibleMultiValues; import io.micronaut.core.type.Argument; -import io.micronaut.http.HttpAttributes; +import io.micronaut.http.BasicHttpAttributes; import io.micronaut.http.HttpRequest; import io.micronaut.http.annotation.QueryValue; -import io.micronaut.http.uri.UriMatchInfo; import io.micronaut.http.uri.UriMatchVariable; import java.util.Collections; @@ -109,7 +108,7 @@ public BindingResult bind(ArgumentConversionContext context, HttpRequest { UriMatchVariable uriMatchVariable = umi.getVariableMap().get(parameterName); return uriMatchVariable != null && uriMatchVariable.isExploded(); diff --git a/http/src/main/java/io/micronaut/http/bind/binders/RequestAttributeAnnotationBinder.java b/http/src/main/java/io/micronaut/http/bind/binders/RequestAttributeAnnotationBinder.java index 4f8e6ee42d3..0d213e4e59c 100644 --- a/http/src/main/java/io/micronaut/http/bind/binders/RequestAttributeAnnotationBinder.java +++ b/http/src/main/java/io/micronaut/http/bind/binders/RequestAttributeAnnotationBinder.java @@ -30,7 +30,6 @@ * * @param A type * @author Ahmed Lafta - * @see io.micronaut.http.HttpAttributes */ public class RequestAttributeAnnotationBinder extends AbstractArgumentBinder implements AnnotatedRequestArgumentBinder, PostponedRequestArgumentBinder { diff --git a/http/src/main/java/io/micronaut/http/util/OutgoingHttpRequestProcessorImpl.java b/http/src/main/java/io/micronaut/http/util/OutgoingHttpRequestProcessorImpl.java index 12decb3b952..40c21dcd2a7 100644 --- a/http/src/main/java/io/micronaut/http/util/OutgoingHttpRequestProcessorImpl.java +++ b/http/src/main/java/io/micronaut/http/util/OutgoingHttpRequestProcessorImpl.java @@ -15,7 +15,7 @@ */ package io.micronaut.http.util; -import io.micronaut.http.HttpAttributes; +import io.micronaut.http.BasicHttpAttributes; import io.micronaut.http.HttpRequest; import jakarta.inject.Singleton; @@ -38,7 +38,7 @@ public class OutgoingHttpRequestProcessorImpl implements OutgoingHttpRequestProc */ @Override public boolean shouldProcessRequest(OutgoingRequestProcessorMatcher matcher, HttpRequest request) { - Optional serviceId = request.getAttribute(HttpAttributes.SERVICE_ID.toString(), String.class); + Optional serviceId = BasicHttpAttributes.getServiceId(request); String uri = request.getUri().toString(); return shouldProcessRequest(matcher, serviceId.orElse(null), uri); } diff --git a/management/src/main/java/io/micronaut/management/endpoint/EndpointsFilter.java b/management/src/main/java/io/micronaut/management/endpoint/EndpointsFilter.java index 697de6f9931..503057a3bc7 100644 --- a/management/src/main/java/io/micronaut/management/endpoint/EndpointsFilter.java +++ b/management/src/main/java/io/micronaut/management/endpoint/EndpointsFilter.java @@ -28,8 +28,8 @@ import io.micronaut.http.filter.ServerFilterPhase; import io.micronaut.inject.ExecutableMethod; import io.micronaut.web.router.MethodBasedRouteMatch; +import io.micronaut.web.router.RouteAttributes; import io.micronaut.web.router.RouteMatch; -import io.micronaut.web.router.RouteMatchUtils; import org.reactivestreams.Publisher; import java.util.Map; @@ -66,7 +66,7 @@ public EndpointsFilter(EndpointSensitivityProcessor endpointSensitivityProcessor @RequestFilter @Nullable public HttpResponse doFilter(HttpRequest request) { - Optional routeMatch = RouteMatchUtils.findRouteMatch(request); + Optional> routeMatch = RouteAttributes.getRouteMatch(request); if (routeMatch.isPresent() && routeMatch.get() instanceof MethodBasedRouteMatch methodBasedRouteMatch) { ExecutableMethod method = methodBasedRouteMatch.getExecutableMethod(); if (endpointMethods.getOrDefault(method, false)) { diff --git a/router/src/main/java/io/micronaut/web/router/DefaultRouter.java b/router/src/main/java/io/micronaut/web/router/DefaultRouter.java index 6b167562065..ec63393bd6f 100644 --- a/router/src/main/java/io/micronaut/web/router/DefaultRouter.java +++ b/router/src/main/java/io/micronaut/web/router/DefaultRouter.java @@ -21,7 +21,6 @@ import io.micronaut.core.reflect.ClassUtils; import io.micronaut.core.util.CollectionUtils; import io.micronaut.core.util.SupplierUtil; -import io.micronaut.http.HttpAttributes; import io.micronaut.http.HttpMethod; import io.micronaut.http.HttpRequest; import io.micronaut.http.HttpStatus; @@ -594,9 +593,7 @@ public List findFilters(@NonNull HttpRequest request) { } var httpFilters = new ArrayList(alwaysMatchesFilterRoutes.size() + preconditionFilterRoutes.size()); httpFilters.addAll(alwaysMatchesHttpFilters.get()); - var routeMatch = (RouteMatch) request.getAttribute(HttpAttributes.ROUTE_MATCH) - .filter(o -> o instanceof RouteMatch) - .orElse(null); + var routeMatch = RouteAttributes.getRouteMatch(request).orElse(null); HttpMethod method = request.getMethod(); String path = request.getPath(); for (FilterRoute filterRoute : preconditionFilterRoutes) { diff --git a/router/src/main/java/io/micronaut/web/router/RouteAttributes.java b/router/src/main/java/io/micronaut/web/router/RouteAttributes.java new file mode 100644 index 00000000000..10fddb9bbf6 --- /dev/null +++ b/router/src/main/java/io/micronaut/web/router/RouteAttributes.java @@ -0,0 +1,166 @@ +/* + * Copyright 2017-2025 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.web.router; + +import io.micronaut.core.annotation.NonNull; +import io.micronaut.http.HttpAttributes; +import io.micronaut.http.HttpRequest; +import io.micronaut.http.HttpResponse; + +import java.util.Optional; + +/** + * Accessors for various route- and server-related attributes. + * + * @author Jonas Konrad + * @since 4.8.0 + */ +@SuppressWarnings("removal") +public final class RouteAttributes { + private RouteAttributes() { + } + + /** + * Get the route match. + * + * @param request The request + * @return The route match, if present + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + @NonNull + public static Optional> getRouteMatch(@NonNull HttpRequest request) { + return (Optional) request.getAttribute(HttpAttributes.ROUTE_MATCH, RouteMatch.class); + } + + /** + * Set the route match. + * + * @param request The request + * @param routeMatch The route match + */ + public static void setRouteMatch(@NonNull HttpRequest request, @NonNull RouteMatch routeMatch) { + request.setAttribute(HttpAttributes.ROUTE_MATCH, routeMatch); + } + + /** + * Get the route match. + * + * @param response The response + * @return The route match, if present + */ + @NonNull + @SuppressWarnings({"unchecked", "rawtypes"}) + public static Optional> getRouteMatch(@NonNull HttpResponse response) { + return (Optional) response.getAttribute(HttpAttributes.ROUTE_MATCH, RouteMatch.class); + } + + /** + * Set the route match. + * + * @param response The response + * @param routeMatch The route match + */ + public static void setRouteMatch(@NonNull HttpResponse response, @NonNull RouteMatch routeMatch) { + response.setAttribute(HttpAttributes.ROUTE_MATCH, routeMatch); + } + + /** + * Get the route info. + * + * @param request The request + * @return The route info, if present + */ + @NonNull + @SuppressWarnings({"unchecked", "rawtypes"}) + public static Optional> getRouteInfo(@NonNull HttpRequest request) { + return (Optional) request.getAttribute(HttpAttributes.ROUTE_INFO, RouteInfo.class); + } + + /** + * Set the route info. + * + * @param request The request + * @param routeInfo The route info + */ + public static void setRouteInfo(@NonNull HttpRequest request, @NonNull RouteInfo routeInfo) { + request.setAttribute(HttpAttributes.ROUTE_INFO, routeInfo); + } + + /** + * Get the route info. + * + * @param response The response + * @return The route info, if present + */ + @NonNull + @SuppressWarnings({"unchecked", "rawtypes"}) + public static Optional> getRouteInfo(@NonNull HttpResponse response) { + // don't convert to RouteInfo to avoid type pollution + return (Optional) response.getAttribute(HttpAttributes.ROUTE_INFO); + } + + /** + * Set the route info. + * + * @param response The response + * @param routeInfo The route info + */ + public static void setRouteInfo(@NonNull HttpResponse response, @NonNull RouteInfo routeInfo) { + response.setAttribute(HttpAttributes.ROUTE_INFO, routeInfo); + } + + /** + * Get the exception that triggered this response. + * + * @param response The response + * @return The exception, if present + */ + @NonNull + public static Optional getException(@NonNull HttpResponse response) { + return response.getAttribute(HttpAttributes.EXCEPTION, Throwable.class); + } + + /** + * Set the exception that triggered this response. + * + * @param response The response + * @param throwable The exception + */ + public static void setException(@NonNull HttpResponse response, @NonNull Throwable throwable) { + response.setAttribute(HttpAttributes.EXCEPTION, throwable); + } + + /** + * Get the body that was discarded because this is a response to a HEAD request. + * + * @param response The response + * @return The discarded body, if present + */ + @NonNull + public static Optional getHeadBody(@NonNull HttpResponse response) { + return response.getAttribute(HttpAttributes.HEAD_BODY); + } + + /** + * Set the body that was discarded because this is a response to a HEAD request. + * + * @param response The response + * @param body The body + */ + public static void setHeadBody(@NonNull HttpResponse response, @NonNull Object body) { + response.setAttribute(HttpAttributes.HEAD_BODY, body); + } +} diff --git a/router/src/main/java/io/micronaut/web/router/RouteMatchUtils.java b/router/src/main/java/io/micronaut/web/router/RouteMatchUtils.java index 4a09e3ec0a0..6b892624624 100644 --- a/router/src/main/java/io/micronaut/web/router/RouteMatchUtils.java +++ b/router/src/main/java/io/micronaut/web/router/RouteMatchUtils.java @@ -15,7 +15,6 @@ */ package io.micronaut.web.router; -import io.micronaut.http.HttpAttributes; import io.micronaut.http.HttpRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -25,7 +24,9 @@ /** * @author Sergio del Amo * @since 1.0 + * @deprecated Moved to {@link RouteAttributes} */ +@Deprecated(since = "4.8.0") public class RouteMatchUtils { private static final Logger LOG = LoggerFactory.getLogger(RouteMatchUtils.class); @@ -35,9 +36,9 @@ public class RouteMatchUtils { * @return The optional route match */ public static Optional findRouteMatch(HttpRequest request) { - Optional routeMatchAttribute = request.getAttribute(HttpAttributes.ROUTE_MATCH, RouteMatch.class); + Optional> routeMatchAttribute = RouteAttributes.getRouteMatch(request); if (routeMatchAttribute.isPresent()) { - return routeMatchAttribute; + return (Optional) routeMatchAttribute; } if (LOG.isDebugEnabled()) { LOG.debug("Route match attribute for request ({}) not found", request.getPath()); diff --git a/test-suite-groovy/src/test/groovy/io/micronaut/docs/web/router/routematch/RouteMatchTest.groovy b/test-suite-groovy/src/test/groovy/io/micronaut/docs/web/router/routematch/RouteMatchTest.groovy index 586710eb325..72a379c2926 100644 --- a/test-suite-groovy/src/test/groovy/io/micronaut/docs/web/router/routematch/RouteMatchTest.groovy +++ b/test-suite-groovy/src/test/groovy/io/micronaut/docs/web/router/routematch/RouteMatchTest.groovy @@ -2,7 +2,6 @@ package io.micronaut.docs.web.router.routematch import io.micronaut.context.annotation.Property import io.micronaut.context.annotation.Requires -import io.micronaut.http.HttpAttributes import io.micronaut.http.HttpRequest import io.micronaut.http.MediaType import io.micronaut.http.annotation.Controller @@ -12,6 +11,7 @@ import io.micronaut.http.client.BlockingHttpClient import io.micronaut.http.client.HttpClient import io.micronaut.http.client.annotation.Client import io.micronaut.test.extensions.spock.annotation.MicronautTest +import io.micronaut.web.router.RouteAttributes import io.micronaut.web.router.RouteMatch import jakarta.inject.Inject import spock.lang.Specification @@ -40,7 +40,7 @@ class RouteMatchTest extends Specification { @Get("/routeMatch") //tag::routematch[] String index(HttpRequest request) { - RouteMatch routeMatch = request.getAttribute(HttpAttributes.ROUTE_MATCH, RouteMatch.class) + RouteMatch routeMatch = RouteAttributes.getRouteMatch(request) .orElse(null) //end::routematch[] return routeMatch != null ? routeMatch.getRouteInfo().getProduces().stream().map(MediaType::toString).findFirst().orElse(null) : null diff --git a/test-suite-kotlin-ksp/src/test/kotlin/io/micronaut/docs/server/suspend/SuspendFilter.kt b/test-suite-kotlin-ksp/src/test/kotlin/io/micronaut/docs/server/suspend/SuspendFilter.kt index bfbbe0f1bdd..a4cef8acb0d 100644 --- a/test-suite-kotlin-ksp/src/test/kotlin/io/micronaut/docs/server/suspend/SuspendFilter.kt +++ b/test-suite-kotlin-ksp/src/test/kotlin/io/micronaut/docs/server/suspend/SuspendFilter.kt @@ -1,11 +1,11 @@ package io.micronaut.docs.server.suspend -import io.micronaut.http.HttpAttributes import io.micronaut.http.HttpRequest import io.micronaut.http.MutableHttpResponse import io.micronaut.http.annotation.Filter import io.micronaut.http.filter.HttpServerFilter import io.micronaut.http.filter.ServerFilterChain +import io.micronaut.web.router.RouteAttributes import org.reactivestreams.Publisher import reactor.core.publisher.Flux @@ -18,7 +18,7 @@ class SuspendFilter : HttpServerFilter { override fun doFilter(request: HttpRequest<*>, chain: ServerFilterChain): Publisher> { return Flux.from(chain.proceed(request)).doOnNext { rsp -> response = rsp - error = rsp.getAttribute(HttpAttributes.EXCEPTION, Throwable::class.java).orElse(null) + error = RouteAttributes.getException(rsp).orElse(null) } } } diff --git a/test-suite-kotlin/src/test/kotlin/io/micronaut/docs/server/suspend/SuspendFilter.kt b/test-suite-kotlin/src/test/kotlin/io/micronaut/docs/server/suspend/SuspendFilter.kt index bfbbe0f1bdd..a4cef8acb0d 100644 --- a/test-suite-kotlin/src/test/kotlin/io/micronaut/docs/server/suspend/SuspendFilter.kt +++ b/test-suite-kotlin/src/test/kotlin/io/micronaut/docs/server/suspend/SuspendFilter.kt @@ -1,11 +1,11 @@ package io.micronaut.docs.server.suspend -import io.micronaut.http.HttpAttributes import io.micronaut.http.HttpRequest import io.micronaut.http.MutableHttpResponse import io.micronaut.http.annotation.Filter import io.micronaut.http.filter.HttpServerFilter import io.micronaut.http.filter.ServerFilterChain +import io.micronaut.web.router.RouteAttributes import org.reactivestreams.Publisher import reactor.core.publisher.Flux @@ -18,7 +18,7 @@ class SuspendFilter : HttpServerFilter { override fun doFilter(request: HttpRequest<*>, chain: ServerFilterChain): Publisher> { return Flux.from(chain.proceed(request)).doOnNext { rsp -> response = rsp - error = rsp.getAttribute(HttpAttributes.EXCEPTION, Throwable::class.java).orElse(null) + error = RouteAttributes.getException(rsp).orElse(null) } } } diff --git a/test-suite-kotlin/src/test/kotlin/io/micronaut/docs/web/router/routematch/RouteMatchTest.kt b/test-suite-kotlin/src/test/kotlin/io/micronaut/docs/web/router/routematch/RouteMatchTest.kt index 88aa91e7e86..4e77dc7e4ab 100644 --- a/test-suite-kotlin/src/test/kotlin/io/micronaut/docs/web/router/routematch/RouteMatchTest.kt +++ b/test-suite-kotlin/src/test/kotlin/io/micronaut/docs/web/router/routematch/RouteMatchTest.kt @@ -2,7 +2,6 @@ package io.micronaut.docs.web.router.routematch import io.micronaut.context.annotation.Property import io.micronaut.context.annotation.Requires -import io.micronaut.http.HttpAttributes import io.micronaut.http.HttpRequest import io.micronaut.http.MediaType import io.micronaut.http.annotation.Controller @@ -11,8 +10,8 @@ import io.micronaut.http.annotation.Produces import io.micronaut.http.client.HttpClient import io.micronaut.http.client.annotation.Client import io.micronaut.test.extensions.junit5.annotation.MicronautTest -import io.micronaut.web.router.RouteMatch -import org.junit.jupiter.api.Assertions.* +import io.micronaut.web.router.RouteAttributes +import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test @Property(name = "spec.name", value = "RouteMatchSpec") @@ -31,10 +30,10 @@ internal class RouteMatchTest { @Get("/routeMatch") //tag::routematch[] fun index(request: HttpRequest<*>): String? { - val routeMatch = request.getAttribute(HttpAttributes.ROUTE_MATCH, RouteMatch::class.java) + val routeMatch = RouteAttributes.getRouteMatch(request) .orElse(null) //end::routematch[] return routeMatch?.routeInfo?.produces?.stream()?.map { obj: MediaType -> obj.toString() }?.findFirst()?.orElse(null) } } -} \ No newline at end of file +} diff --git a/test-suite-netty-ssl-graalvm/src/test/java/example/micronaut/HelloControllerTest.java b/test-suite-netty-ssl-graalvm/src/test/java/example/micronaut/HelloControllerTest.java index 89a35962c82..b2096889e14 100644 --- a/test-suite-netty-ssl-graalvm/src/test/java/example/micronaut/HelloControllerTest.java +++ b/test-suite-netty-ssl-graalvm/src/test/java/example/micronaut/HelloControllerTest.java @@ -13,6 +13,7 @@ @MicronautTest @Property(name = "micronaut.server.ssl.enabled", value = "true") @Property(name = "micronaut.server.ssl.buildSelfSigned", value = "true") +@Property(name = "micronaut.server.ssl.port", value = "0") @Property(name = "micronaut.http.client.ssl.insecure-trust-all-certificates", value = "true") public class HelloControllerTest { diff --git a/test-suite/src/test/groovy/io/micronaut/docs/netty/LogbookNettyClientCustomizerSpec.groovy b/test-suite/src/test/groovy/io/micronaut/docs/netty/LogbookNettyClientCustomizerSpec.groovy index 4132d73a19e..3a233f0135a 100644 --- a/test-suite/src/test/groovy/io/micronaut/docs/netty/LogbookNettyClientCustomizerSpec.groovy +++ b/test-suite/src/test/groovy/io/micronaut/docs/netty/LogbookNettyClientCustomizerSpec.groovy @@ -55,6 +55,7 @@ class LogbookNettyClientCustomizerSpec extends Specification { 'micronaut.server.http-version' : '2.0', 'micronaut.server.ssl.enabled' : true, 'micronaut.server.ssl.buildSelfSigned': true, + 'micronaut.server.ssl.port': 0, 'micronaut.http.client.http-version' : '2.0', 'micronaut.http.client.ssl.insecure-trust-all-certificates': true, 'spec.name' : 'LogbookNettyClientCustomizerSpec' @@ -88,6 +89,7 @@ class LogbookNettyClientCustomizerSpec extends Specification { def ctx = ApplicationContext.run([ 'micronaut.server.ssl.enabled' : true, 'micronaut.server.ssl.buildSelfSigned': true, + 'micronaut.server.ssl.port': 0, 'micronaut.http.client.ssl.insecure-trust-all-certificates': true, 'spec.name' : 'LogbookNettyClientCustomizerSpec' ]) diff --git a/test-suite/src/test/groovy/io/micronaut/upload/PrincipalFilter.java b/test-suite/src/test/groovy/io/micronaut/upload/PrincipalFilter.java index 4f1d22ff5b7..c8a80e8f859 100644 --- a/test-suite/src/test/groovy/io/micronaut/upload/PrincipalFilter.java +++ b/test-suite/src/test/groovy/io/micronaut/upload/PrincipalFilter.java @@ -1,6 +1,5 @@ package io.micronaut.upload; -import io.micronaut.http.HttpAttributes; import io.micronaut.http.HttpRequest; import io.micronaut.http.MutableHttpResponse; import io.micronaut.http.annotation.Filter; @@ -8,14 +7,12 @@ import io.micronaut.http.filter.ServerFilterChain; import org.reactivestreams.Publisher; -import java.security.Principal; - @Filter("/upload/receive-multipart-body-principal") public class PrincipalFilter implements HttpServerFilter { @Override public Publisher> doFilter(HttpRequest request, ServerFilterChain chain) { - request.setAttribute(HttpAttributes.PRINCIPAL, (Principal) () -> "test"); + request.setUserPrincipal(() -> "test"); return chain.proceed(request); } } diff --git a/test-suite/src/test/java/io/micronaut/docs/web/router/routematch/RouteMatchTest.java b/test-suite/src/test/java/io/micronaut/docs/web/router/routematch/RouteMatchTest.java index 2da8ea2f0ff..bf1c71c729c 100644 --- a/test-suite/src/test/java/io/micronaut/docs/web/router/routematch/RouteMatchTest.java +++ b/test-suite/src/test/java/io/micronaut/docs/web/router/routematch/RouteMatchTest.java @@ -2,7 +2,6 @@ import io.micronaut.context.annotation.Property; import io.micronaut.context.annotation.Requires; -import io.micronaut.http.HttpAttributes; import io.micronaut.http.HttpRequest; import io.micronaut.http.MediaType; import io.micronaut.http.annotation.Controller; @@ -12,6 +11,7 @@ import io.micronaut.http.client.HttpClient; import io.micronaut.http.client.annotation.Client; import io.micronaut.test.extensions.junit5.annotation.MicronautTest; +import io.micronaut.web.router.RouteAttributes; import io.micronaut.web.router.RouteMatch; import org.junit.jupiter.api.Test; @@ -35,7 +35,7 @@ static class RouteMatchController { @Get("/routeMatch") //tag::routematch[] String index(HttpRequest request) { - RouteMatch routeMatch = request.getAttribute(HttpAttributes.ROUTE_MATCH, RouteMatch.class) + RouteMatch routeMatch = RouteAttributes.getRouteMatch(request) .orElse(null); //end::routematch[] return routeMatch != null ? routeMatch.getRouteInfo().getProduces().stream().map(MediaType::toString).findFirst().orElse(null) : null;