diff --git a/framework-platform/framework-platform.gradle b/framework-platform/framework-platform.gradle index 64ab2a5faf3..659d6d92cef 100644 --- a/framework-platform/framework-platform.gradle +++ b/framework-platform/framework-platform.gradle @@ -10,8 +10,8 @@ dependencies { api(platform("com.fasterxml.jackson:jackson-bom:2.13.4")) api(platform("io.micrometer:micrometer-bom:1.10.0-M6")) api(platform("io.netty:netty-bom:4.1.82.Final")) - api(platform("io.netty:netty5-bom:5.0.0.Alpha3")) - api(platform("io.projectreactor:reactor-bom:2022.0.0-M6")) + api(platform("io.netty:netty5-bom:5.0.0.Alpha5")) + api(platform("io.projectreactor:reactor-bom:2022.0.0-SNAPSHOT")) api(platform("io.rsocket:rsocket-bom:1.1.3")) api(platform("org.apache.groovy:groovy-bom:4.0.5")) api(platform("org.apache.logging.log4j:log4j-bom:2.19.0")) diff --git a/spring-core/src/main/java/org/springframework/core/codec/Netty5BufferDecoder.java b/spring-core/src/main/java/org/springframework/core/codec/Netty5BufferDecoder.java index b6ebb93d8a6..150db2b893e 100644 --- a/spring-core/src/main/java/org/springframework/core/codec/Netty5BufferDecoder.java +++ b/spring-core/src/main/java/org/springframework/core/codec/Netty5BufferDecoder.java @@ -18,8 +18,8 @@ package org.springframework.core.codec; import java.util.Map; -import io.netty5.buffer.api.Buffer; -import io.netty5.buffer.api.DefaultBufferAllocators; +import io.netty5.buffer.Buffer; +import io.netty5.buffer.DefaultBufferAllocators; import org.springframework.core.ResolvableType; import org.springframework.core.io.buffer.DataBuffer; diff --git a/spring-core/src/main/java/org/springframework/core/codec/Netty5BufferEncoder.java b/spring-core/src/main/java/org/springframework/core/codec/Netty5BufferEncoder.java index 465a924a11f..588367132d9 100644 --- a/spring-core/src/main/java/org/springframework/core/codec/Netty5BufferEncoder.java +++ b/spring-core/src/main/java/org/springframework/core/codec/Netty5BufferEncoder.java @@ -18,7 +18,7 @@ package org.springframework.core.codec; import java.util.Map; -import io.netty5.buffer.api.Buffer; +import io.netty5.buffer.Buffer; import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; diff --git a/spring-core/src/main/java/org/springframework/core/io/buffer/Netty5DataBuffer.java b/spring-core/src/main/java/org/springframework/core/io/buffer/Netty5DataBuffer.java index 03a9ebc8b7a..64b78042557 100644 --- a/spring-core/src/main/java/org/springframework/core/io/buffer/Netty5DataBuffer.java +++ b/spring-core/src/main/java/org/springframework/core/io/buffer/Netty5DataBuffer.java @@ -21,7 +21,7 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.function.IntPredicate; -import io.netty5.buffer.api.Buffer; +import io.netty5.buffer.Buffer; import io.netty5.util.AsciiString; import org.springframework.lang.Nullable; diff --git a/spring-core/src/main/java/org/springframework/core/io/buffer/Netty5DataBufferFactory.java b/spring-core/src/main/java/org/springframework/core/io/buffer/Netty5DataBufferFactory.java index 5ef803b0a81..9095d4f7ad5 100644 --- a/spring-core/src/main/java/org/springframework/core/io/buffer/Netty5DataBufferFactory.java +++ b/spring-core/src/main/java/org/springframework/core/io/buffer/Netty5DataBufferFactory.java @@ -19,10 +19,10 @@ package org.springframework.core.io.buffer; import java.nio.ByteBuffer; import java.util.List; -import io.netty5.buffer.api.Buffer; -import io.netty5.buffer.api.BufferAllocator; -import io.netty5.buffer.api.CompositeBuffer; -import io.netty5.buffer.api.DefaultBufferAllocators; +import io.netty5.buffer.Buffer; +import io.netty5.buffer.BufferAllocator; +import io.netty5.buffer.CompositeBuffer; +import io.netty5.buffer.DefaultBufferAllocators; import org.springframework.util.Assert; diff --git a/spring-core/src/test/java/org/springframework/core/codec/Netty5BufferDecoderTests.java b/spring-core/src/test/java/org/springframework/core/codec/Netty5BufferDecoderTests.java index 6d5997b446b..c2b4eb32430 100644 --- a/spring-core/src/test/java/org/springframework/core/codec/Netty5BufferDecoderTests.java +++ b/spring-core/src/test/java/org/springframework/core/codec/Netty5BufferDecoderTests.java @@ -19,8 +19,8 @@ package org.springframework.core.codec; import java.nio.charset.StandardCharsets; import java.util.function.Consumer; -import io.netty5.buffer.api.Buffer; -import io.netty5.buffer.api.DefaultBufferAllocators; +import io.netty5.buffer.Buffer; +import io.netty5.buffer.DefaultBufferAllocators; import org.junit.jupiter.api.Test; import reactor.core.publisher.Flux; diff --git a/spring-core/src/test/java/org/springframework/core/codec/Netty5BufferEncoderTests.java b/spring-core/src/test/java/org/springframework/core/codec/Netty5BufferEncoderTests.java index 072b33c801c..2312971622a 100644 --- a/spring-core/src/test/java/org/springframework/core/codec/Netty5BufferEncoderTests.java +++ b/spring-core/src/test/java/org/springframework/core/codec/Netty5BufferEncoderTests.java @@ -18,8 +18,8 @@ package org.springframework.core.codec; import java.nio.charset.StandardCharsets; -import io.netty5.buffer.api.Buffer; -import io.netty5.buffer.api.DefaultBufferAllocators; +import io.netty5.buffer.Buffer; +import io.netty5.buffer.DefaultBufferAllocators; import org.junit.jupiter.api.Test; import reactor.core.publisher.Flux; diff --git a/spring-core/src/testFixtures/java/org/springframework/core/testfixture/io/buffer/AbstractDataBufferAllocatingTests.java b/spring-core/src/testFixtures/java/org/springframework/core/testfixture/io/buffer/AbstractDataBufferAllocatingTests.java index 2ad1a834a15..15eebbd5f83 100644 --- a/spring-core/src/testFixtures/java/org/springframework/core/testfixture/io/buffer/AbstractDataBufferAllocatingTests.java +++ b/spring-core/src/testFixtures/java/org/springframework/core/testfixture/io/buffer/AbstractDataBufferAllocatingTests.java @@ -34,7 +34,7 @@ import io.netty.buffer.PoolArenaMetric; import io.netty.buffer.PooledByteBufAllocator; import io.netty.buffer.PooledByteBufAllocatorMetric; import io.netty.buffer.UnpooledByteBufAllocator; -import io.netty5.buffer.api.BufferAllocator; +import io.netty5.buffer.BufferAllocator; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedTest; diff --git a/spring-messaging/spring-messaging.gradle b/spring-messaging/spring-messaging.gradle index cb54d42e597..86b68df39d8 100644 --- a/spring-messaging/spring-messaging.gradle +++ b/spring-messaging/spring-messaging.gradle @@ -9,7 +9,7 @@ dependencies { optional(project(":spring-context")) optional(project(":spring-oxm")) optional("io.projectreactor.netty:reactor-netty-http") - optional("io.projectreactor.netty:reactor-netty5-http:2.0.0-M1") + optional("io.projectreactor.netty:reactor-netty5-http") optional("io.rsocket:rsocket-core") optional("io.rsocket:rsocket-transport-netty") optional("com.fasterxml.jackson.core:jackson-databind") diff --git a/spring-messaging/src/main/java/org/springframework/messaging/tcp/reactor/ReactorNetty2TcpClient.java b/spring-messaging/src/main/java/org/springframework/messaging/tcp/reactor/ReactorNetty2TcpClient.java index e0192b68f72..a333e08e26c 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/tcp/reactor/ReactorNetty2TcpClient.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/tcp/reactor/ReactorNetty2TcpClient.java @@ -23,7 +23,7 @@ import java.util.concurrent.CompletableFuture; import java.util.function.BiFunction; import java.util.function.Function; -import io.netty5.buffer.api.Buffer; +import io.netty5.buffer.Buffer; import io.netty5.channel.ChannelHandlerContext; import io.netty5.channel.group.ChannelGroup; import io.netty5.channel.group.DefaultChannelGroup; diff --git a/spring-messaging/src/main/java/org/springframework/messaging/tcp/reactor/ReactorNetty2TcpConnection.java b/spring-messaging/src/main/java/org/springframework/messaging/tcp/reactor/ReactorNetty2TcpConnection.java index 8016e04772c..87efa56f534 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/tcp/reactor/ReactorNetty2TcpConnection.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/tcp/reactor/ReactorNetty2TcpConnection.java @@ -19,7 +19,7 @@ package org.springframework.messaging.tcp.reactor; import java.nio.ByteBuffer; import java.util.concurrent.CompletableFuture; -import io.netty5.buffer.api.Buffer; +import io.netty5.buffer.Buffer; import reactor.core.publisher.Mono; import reactor.core.publisher.Sinks; import reactor.netty5.NettyInbound; diff --git a/spring-web/spring-web.gradle b/spring-web/spring-web.gradle index 846080616d9..ca787381de8 100644 --- a/spring-web/spring-web.gradle +++ b/spring-web/spring-web.gradle @@ -28,7 +28,7 @@ dependencies { optional("io.netty:netty5-handler") optional("io.netty:netty5-codec-http") optional("io.netty:netty5-transport") - optional("io.projectreactor.netty:reactor-netty5-http:2.0.0-M1") + optional("io.projectreactor.netty:reactor-netty5-http") optional("io.undertow:undertow-core") optional("org.apache.tomcat.embed:tomcat-embed-core") optional("org.eclipse.jetty:jetty-server") { diff --git a/spring-web/src/main/java/org/springframework/http/client/reactive/Netty5HeadersAdapter.java b/spring-web/src/main/java/org/springframework/http/client/reactive/Netty5HeadersAdapter.java index fb1e9a01d89..84784301b86 100644 --- a/spring-web/src/main/java/org/springframework/http/client/reactive/Netty5HeadersAdapter.java +++ b/spring-web/src/main/java/org/springframework/http/client/reactive/Netty5HeadersAdapter.java @@ -17,13 +17,17 @@ package org.springframework.http.client.reactive; import java.util.AbstractSet; +import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.stream.StreamSupport; -import io.netty5.handler.codec.http.HttpHeaders; +import io.netty5.handler.codec.http.headers.HttpHeaders; import org.springframework.lang.Nullable; import org.springframework.util.CollectionUtils; @@ -52,7 +56,8 @@ class Netty5HeadersAdapter implements MultiValueMap { @Override @Nullable public String getFirst(String key) { - return this.headers.get(key); + CharSequence value = this.headers.get(key); + return value != null ? value.toString() : null; } @Override @@ -87,12 +92,11 @@ class Netty5HeadersAdapter implements MultiValueMap { @Override public Map toSingleValueMap() { Map singleValueMap = CollectionUtils.newLinkedHashMap(this.headers.size()); - this.headers.entries() - .forEach(entry -> { - if (!singleValueMap.containsKey(entry.getKey())) { - singleValueMap.put(entry.getKey(), entry.getValue()); - } - }); + this.headers.forEach(entry -> { + if (!singleValueMap.containsKey(entry.getKey())) { + singleValueMap.put(entry.getKey().toString(), entry.getValue().toString()); + } + }); return singleValueMap; } @@ -114,7 +118,8 @@ class Netty5HeadersAdapter implements MultiValueMap { @Override public boolean containsValue(Object value) { return (value instanceof String && - this.headers.entries().stream() + StreamSupport.stream( + Spliterators.spliteratorUnknownSize(this.headers.iterator(), Spliterator.ORDERED), false) .anyMatch(entry -> value.equals(entry.getValue()))); } @@ -122,7 +127,7 @@ class Netty5HeadersAdapter implements MultiValueMap { @Nullable public List get(Object key) { if (containsKey(key)) { - return this.headers.getAll((String) key); + return getAll(this.headers.valuesIterator((CharSequence) key)); } return null; } @@ -130,7 +135,7 @@ class Netty5HeadersAdapter implements MultiValueMap { @Nullable @Override public List put(String key, @Nullable List value) { - List previousValues = this.headers.getAll(key); + List previousValues = getAll(this.headers.valuesIterator(key)); this.headers.set(key, value); return previousValues; } @@ -139,7 +144,7 @@ class Netty5HeadersAdapter implements MultiValueMap { @Override public List remove(Object key) { if (key instanceof String headerName) { - List previousValues = this.headers.getAll(headerName); + List previousValues = getAll(this.headers.valuesIterator(headerName)); this.headers.remove(headerName); return previousValues; } @@ -164,7 +169,7 @@ class Netty5HeadersAdapter implements MultiValueMap { @Override public Collection> values() { return this.headers.names().stream() - .map(this.headers::getAll).toList(); + .map(key -> getAll(this.headers.valuesIterator(key))).toList(); } @Override @@ -189,9 +194,22 @@ class Netty5HeadersAdapter implements MultiValueMap { } + @Nullable + private static List getAll(Iterator valuesIterator) { + if (!valuesIterator.hasNext()) { + return null; + } + List result = new ArrayList<>(); + while (valuesIterator.hasNext()) { + result.add(valuesIterator.next().toString()); + } + return result; + } + + private class EntryIterator implements Iterator>> { - private final Iterator names = headers.names().iterator(); + private final Iterator names = headers.names().iterator(); @Override public boolean hasNext() { @@ -207,25 +225,30 @@ class Netty5HeadersAdapter implements MultiValueMap { private class HeaderEntry implements Entry> { - private final String key; + private final CharSequence key; - HeaderEntry(String key) { + HeaderEntry(CharSequence key) { this.key = key; } @Override public String getKey() { - return this.key; + return this.key.toString(); } @Override public List getValue() { - return headers.getAll(this.key); + List result = new ArrayList<>(); + Iterator valuesIterator = headers.valuesIterator(this.key); + while (valuesIterator.hasNext()) { + result.add(valuesIterator.next().toString()); + } + return result; } @Override public List setValue(List value) { - List previousValues = headers.getAll(this.key); + List previousValues = getValue(); headers.set(this.key, value); return previousValues; } @@ -247,12 +270,12 @@ class Netty5HeadersAdapter implements MultiValueMap { private final class HeaderNamesIterator implements Iterator { - private final Iterator iterator; + private final Iterator iterator; @Nullable - private String currentName; + private CharSequence currentName; - private HeaderNamesIterator(Iterator iterator) { + private HeaderNamesIterator(Iterator iterator) { this.iterator = iterator; } @@ -264,7 +287,7 @@ class Netty5HeadersAdapter implements MultiValueMap { @Override public String next() { this.currentName = this.iterator.next(); - return this.currentName; + return this.currentName.toString(); } @Override diff --git a/spring-web/src/main/java/org/springframework/http/client/reactive/ReactorNetty2ClientHttpRequest.java b/spring-web/src/main/java/org/springframework/http/client/reactive/ReactorNetty2ClientHttpRequest.java index 81efbab9a41..787d5e1575f 100644 --- a/spring-web/src/main/java/org/springframework/http/client/reactive/ReactorNetty2ClientHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/client/reactive/ReactorNetty2ClientHttpRequest.java @@ -20,8 +20,8 @@ import java.net.URI; import java.nio.file.Path; import java.util.Collection; -import io.netty5.buffer.api.Buffer; -import io.netty5.handler.codec.http.cookie.DefaultCookie; +import io.netty5.buffer.Buffer; +import io.netty5.handler.codec.http.headers.DefaultHttpCookiePair; import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -131,7 +131,7 @@ class ReactorNetty2ClientHttpRequest extends AbstractClientHttpRequest implement @Override protected void applyCookies() { getCookies().values().stream().flatMap(Collection::stream) - .map(cookie -> new DefaultCookie(cookie.getName(), cookie.getValue())) + .map(cookie -> new DefaultHttpCookiePair(cookie.getName(), cookie.getValue())) .forEach(this.request::addCookie); } diff --git a/spring-web/src/main/java/org/springframework/http/client/reactive/ReactorNetty2ClientHttpResponse.java b/spring-web/src/main/java/org/springframework/http/client/reactive/ReactorNetty2ClientHttpResponse.java index 0337ffe433a..3000ed05a4a 100644 --- a/spring-web/src/main/java/org/springframework/http/client/reactive/ReactorNetty2ClientHttpResponse.java +++ b/spring-web/src/main/java/org/springframework/http/client/reactive/ReactorNetty2ClientHttpResponse.java @@ -20,8 +20,8 @@ import java.util.Collection; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiFunction; -import io.netty5.handler.codec.http.cookie.Cookie; -import io.netty5.handler.codec.http.cookie.DefaultCookie; +import io.netty5.handler.codec.http.headers.DefaultHttpSetCookie; +import io.netty5.handler.codec.http.headers.HttpSetCookie; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import reactor.core.publisher.Flux; @@ -129,11 +129,11 @@ class ReactorNetty2ClientHttpResponse implements ClientHttpResponse { MultiValueMap result = new LinkedMultiValueMap<>(); this.response.cookies().values().stream() .flatMap(Collection::stream) - .forEach(cookie -> result.add(cookie.name(), - ResponseCookie.fromClientResponse(cookie.name(), cookie.value()) - .domain(cookie.domain()) - .path(cookie.path()) - .maxAge(cookie.maxAge()) + .forEach(cookie -> result.add(cookie.name().toString(), + ResponseCookie.fromClientResponse(cookie.name().toString(), cookie.value().toString()) + .domain(cookie.domain() != null ? cookie.domain().toString() : null) + .path(cookie.path() != null ? cookie.path().toString() : null) + .maxAge(cookie.maxAge() != null ? cookie.maxAge() : -1L) .secure(cookie.isSecure()) .httpOnly(cookie.isHttpOnly()) .sameSite(getSameSite(cookie)) @@ -142,8 +142,8 @@ class ReactorNetty2ClientHttpResponse implements ClientHttpResponse { } @Nullable - private static String getSameSite(Cookie cookie) { - if (cookie instanceof DefaultCookie defaultCookie) { + private static String getSameSite(HttpSetCookie cookie) { + if (cookie instanceof DefaultHttpSetCookie defaultCookie) { if (defaultCookie.sameSite() != null) { return defaultCookie.sameSite().name(); } diff --git a/spring-web/src/main/java/org/springframework/http/codec/support/BaseDefaultCodecs.java b/spring-web/src/main/java/org/springframework/http/codec/support/BaseDefaultCodecs.java index 90ccdedeffc..6f7337d87d4 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/support/BaseDefaultCodecs.java +++ b/spring-web/src/main/java/org/springframework/http/codec/support/BaseDefaultCodecs.java @@ -111,7 +111,7 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs, CodecConfigure protobufPresent = ClassUtils.isPresent("com.google.protobuf.Message", classLoader); synchronossMultipartPresent = ClassUtils.isPresent("org.synchronoss.cloud.nio.multipart.NioMultipartParser", classLoader); nettyByteBufPresent = ClassUtils.isPresent("io.netty.buffer.ByteBuf", classLoader); - netty5BufferPresent = ClassUtils.isPresent("io.netty5.buffer.api.Buffer", classLoader); + netty5BufferPresent = ClassUtils.isPresent("io.netty5.buffer.Buffer", classLoader); kotlinSerializationJsonPresent = ClassUtils.isPresent("kotlinx.serialization.json.Json", classLoader); } diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/Netty5HeadersAdapter.java b/spring-web/src/main/java/org/springframework/http/server/reactive/Netty5HeadersAdapter.java index dfe404f290e..1d5fc7d610a 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/Netty5HeadersAdapter.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/Netty5HeadersAdapter.java @@ -17,13 +17,17 @@ package org.springframework.http.server.reactive; import java.util.AbstractSet; +import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.stream.StreamSupport; -import io.netty5.handler.codec.http.HttpHeaders; +import io.netty5.handler.codec.http.headers.HttpHeaders; import org.springframework.lang.Nullable; import org.springframework.util.CollectionUtils; @@ -50,7 +54,8 @@ final class Netty5HeadersAdapter implements MultiValueMap { @Override @Nullable public String getFirst(String key) { - return this.headers.get(key); + CharSequence value = this.headers.get(key); + return value != null ? value.toString() : null; } @Override @@ -85,10 +90,9 @@ final class Netty5HeadersAdapter implements MultiValueMap { @Override public Map toSingleValueMap() { Map singleValueMap = CollectionUtils.newLinkedHashMap(this.headers.size()); - this.headers.entries() - .forEach(entry -> { + this.headers.forEach(entry -> { if (!singleValueMap.containsKey(entry.getKey())) { - singleValueMap.put(entry.getKey(), entry.getValue()); + singleValueMap.put(entry.getKey().toString(), entry.getValue().toString()); } }); return singleValueMap; @@ -112,7 +116,8 @@ final class Netty5HeadersAdapter implements MultiValueMap { @Override public boolean containsValue(Object value) { return (value instanceof String && - this.headers.entries().stream() + StreamSupport.stream( + Spliterators.spliteratorUnknownSize(this.headers.iterator(), Spliterator.ORDERED), false) .anyMatch(entry -> value.equals(entry.getValue()))); } @@ -120,7 +125,7 @@ final class Netty5HeadersAdapter implements MultiValueMap { @Nullable public List get(Object key) { if (containsKey(key)) { - return this.headers.getAll((String) key); + return getAll(this.headers.valuesIterator((CharSequence) key)); } return null; } @@ -128,7 +133,7 @@ final class Netty5HeadersAdapter implements MultiValueMap { @Nullable @Override public List put(String key, @Nullable List value) { - List previousValues = this.headers.getAll(key); + List previousValues = getAll(this.headers.valuesIterator(key)); this.headers.set(key, value); return previousValues; } @@ -137,7 +142,7 @@ final class Netty5HeadersAdapter implements MultiValueMap { @Override public List remove(Object key) { if (key instanceof String headerName) { - List previousValues = this.headers.getAll(headerName); + List previousValues = getAll(this.headers.valuesIterator(headerName)); this.headers.remove(headerName); return previousValues; } @@ -162,7 +167,7 @@ final class Netty5HeadersAdapter implements MultiValueMap { @Override public Collection> values() { return this.headers.names().stream() - .map(this.headers::getAll).toList(); + .map(key -> getAll(this.headers.valuesIterator(key))).toList(); } @Override @@ -187,9 +192,22 @@ final class Netty5HeadersAdapter implements MultiValueMap { } + @Nullable + private static List getAll(Iterator valuesIterator) { + if (!valuesIterator.hasNext()) { + return null; + } + List result = new ArrayList<>(); + while (valuesIterator.hasNext()) { + result.add(valuesIterator.next().toString()); + } + return result; + } + + private class EntryIterator implements Iterator>> { - private final Iterator names = headers.names().iterator(); + private final Iterator names = headers.names().iterator(); @Override public boolean hasNext() { @@ -205,25 +223,30 @@ final class Netty5HeadersAdapter implements MultiValueMap { private class HeaderEntry implements Entry> { - private final String key; + private final CharSequence key; - HeaderEntry(String key) { + HeaderEntry(CharSequence key) { this.key = key; } @Override public String getKey() { - return this.key; + return this.key.toString(); } @Override public List getValue() { - return headers.getAll(this.key); + List result = new ArrayList<>(); + Iterator valuesIterator = headers.valuesIterator(this.key); + while (valuesIterator.hasNext()) { + result.add(valuesIterator.next().toString()); + } + return result; } @Override public List setValue(List value) { - List previousValues = headers.getAll(this.key); + List previousValues = getValue(); headers.set(this.key, value); return previousValues; } @@ -244,12 +267,12 @@ final class Netty5HeadersAdapter implements MultiValueMap { private final class HeaderNamesIterator implements Iterator { - private final Iterator iterator; + private final Iterator iterator; @Nullable - private String currentName; + private CharSequence currentName; - private HeaderNamesIterator(Iterator iterator) { + private HeaderNamesIterator(Iterator iterator) { this.iterator = iterator; } @@ -261,7 +284,7 @@ final class Netty5HeadersAdapter implements MultiValueMap { @Override public String next() { this.currentName = this.iterator.next(); - return this.currentName; + return this.currentName.toString(); } @Override diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/ReactorNetty2ServerHttpRequest.java b/spring-web/src/main/java/org/springframework/http/server/reactive/ReactorNetty2ServerHttpRequest.java index fcee76c668c..7f643330502 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/ReactorNetty2ServerHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/ReactorNetty2ServerHttpRequest.java @@ -25,7 +25,7 @@ import javax.net.ssl.SSLSession; import io.netty5.channel.Channel; import io.netty5.handler.codec.http.HttpHeaderNames; -import io.netty5.handler.codec.http.cookie.Cookie; +import io.netty5.handler.codec.http.headers.HttpCookiePair; import io.netty5.handler.ssl.SslHandler; import org.apache.commons.logging.Log; import reactor.core.publisher.Flux; @@ -80,8 +80,9 @@ class ReactorNetty2ServerHttpRequest extends AbstractServerHttpRequest { private static URI resolveBaseUrl(HttpServerRequest request) throws URISyntaxException { String scheme = getScheme(request); - String header = request.requestHeaders().get(HttpHeaderNames.HOST); - if (header != null) { + CharSequence headerCS = request.requestHeaders().get(HttpHeaderNames.HOST); + if (headerCS != null) { + String header = headerCS.toString(); final int portIndex; if (header.startsWith("[")) { portIndex = header.indexOf(':', header.indexOf(']')); @@ -151,8 +152,9 @@ class ReactorNetty2ServerHttpRequest extends AbstractServerHttpRequest { protected MultiValueMap initCookies() { MultiValueMap cookies = new LinkedMultiValueMap<>(); for (CharSequence name : this.request.cookies().keySet()) { - for (Cookie cookie : this.request.cookies().get(name)) { - HttpCookie httpCookie = new HttpCookie(name.toString(), cookie.value()); + for (HttpCookiePair cookie : this.request.cookies().get(name)) { + CharSequence cookieValue = cookie.value(); + HttpCookie httpCookie = new HttpCookie(name.toString(), cookieValue != null ? cookieValue.toString() : null); cookies.add(name.toString(), httpCookie); } } diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/ReactorNetty2ServerHttpResponse.java b/spring-web/src/main/java/org/springframework/http/server/reactive/ReactorNetty2ServerHttpResponse.java index 332b8a6cbaa..46ba38e6fb5 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/ReactorNetty2ServerHttpResponse.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/ReactorNetty2ServerHttpResponse.java @@ -19,7 +19,7 @@ package org.springframework.http.server.reactive; import java.nio.file.Path; import java.util.List; -import io.netty5.buffer.api.Buffer; +import io.netty5.buffer.Buffer; import io.netty5.channel.ChannelId; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/spring-web/src/test/java/org/springframework/http/server/reactive/HeadersAdaptersTests.java b/spring-web/src/test/java/org/springframework/http/server/reactive/HeadersAdaptersTests.java index 392904b3c80..8f89a6b3bbd 100644 --- a/spring-web/src/test/java/org/springframework/http/server/reactive/HeadersAdaptersTests.java +++ b/spring-web/src/test/java/org/springframework/http/server/reactive/HeadersAdaptersTests.java @@ -135,7 +135,7 @@ class HeadersAdaptersTests { return Stream.of( arguments(named("Map", CollectionUtils.toMultiValueMap(new LinkedCaseInsensitiveMap<>(8, Locale.ENGLISH)))), arguments(named("Netty", new NettyHeadersAdapter(new DefaultHttpHeaders()))), - arguments(named("Netty", new Netty5HeadersAdapter(new io.netty5.handler.codec.http.DefaultHttpHeaders()))), + arguments(named("Netty", new Netty5HeadersAdapter(io.netty5.handler.codec.http.headers.HttpHeaders.newHeaders()))), arguments(named("Tomcat", new TomcatHeadersAdapter(new MimeHeaders()))), arguments(named("Undertow", new UndertowHeadersAdapter(new HeaderMap()))), arguments(named("Jetty", new JettyHeadersAdapter(HttpFields.build()))) diff --git a/spring-webflux/spring-webflux.gradle b/spring-webflux/spring-webflux.gradle index afd978fede0..a8a814863fb 100644 --- a/spring-webflux/spring-webflux.gradle +++ b/spring-webflux/spring-webflux.gradle @@ -16,7 +16,7 @@ dependencies { optional("com.fasterxml.jackson.core:jackson-databind") optional("com.fasterxml.jackson.dataformat:jackson-dataformat-smile") optional("io.projectreactor.netty:reactor-netty-http") - optional("io.projectreactor.netty:reactor-netty5-http:2.0.0-M1") + optional("io.projectreactor.netty:reactor-netty5-http") optional("org.apache.tomcat:tomcat-websocket") { exclude group: "org.apache.tomcat", module: "tomcat-servlet-api" exclude group: "org.apache.tomcat", module: "tomcat-websocket-api" diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/socket/adapter/Netty5WebSocketSessionSupport.java b/spring-webflux/src/main/java/org/springframework/web/reactive/socket/adapter/Netty5WebSocketSessionSupport.java index b86121cd373..0107013567b 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/socket/adapter/Netty5WebSocketSessionSupport.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/socket/adapter/Netty5WebSocketSessionSupport.java @@ -19,7 +19,7 @@ package org.springframework.web.reactive.socket.adapter; import java.util.HashMap; import java.util.Map; -import io.netty5.buffer.api.Buffer; +import io.netty5.buffer.Buffer; import io.netty5.handler.codec.http.websocketx.BinaryWebSocketFrame; import io.netty5.handler.codec.http.websocketx.PingWebSocketFrame; import io.netty5.handler.codec.http.websocketx.PongWebSocketFrame; diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/socket/client/ReactorNetty2WebSocketClient.java b/spring-webflux/src/main/java/org/springframework/web/reactive/socket/client/ReactorNetty2WebSocketClient.java index 4678e9213b6..0db51916d9e 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/socket/client/ReactorNetty2WebSocketClient.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/socket/client/ReactorNetty2WebSocketClient.java @@ -17,6 +17,9 @@ package org.springframework.web.reactive.socket.client; import java.net.URI; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; import java.util.function.Supplier; import org.apache.commons.logging.Log; @@ -148,18 +151,26 @@ public class ReactorNetty2WebSocketClient implements WebSocketClient { .next(); } - private void setNettyHeaders(HttpHeaders httpHeaders, io.netty5.handler.codec.http.HttpHeaders nettyHeaders) { + private void setNettyHeaders(HttpHeaders httpHeaders, io.netty5.handler.codec.http.headers.HttpHeaders nettyHeaders) { httpHeaders.forEach(nettyHeaders::set); } private HttpHeaders toHttpHeaders(WebsocketInbound inbound) { HttpHeaders headers = new HttpHeaders(); - io.netty5.handler.codec.http.HttpHeaders nettyHeaders = inbound.headers(); + io.netty5.handler.codec.http.headers.HttpHeaders nettyHeaders = inbound.headers(); nettyHeaders.forEach(entry -> { - String name = entry.getKey(); - headers.put(name, nettyHeaders.getAll(name)); + CharSequence name = entry.getKey(); + headers.put(name.toString(), getAll(nettyHeaders.valuesIterator(name))); }); return headers; } + private static List getAll(Iterator valuesIterator) { + List result = new ArrayList<>(); + while (valuesIterator.hasNext()) { + result.add(valuesIterator.next().toString()); + } + return result; + } + } diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/WebClientIntegrationTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/WebClientIntegrationTests.java index 07c5adaa05a..4da55a40390 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/WebClientIntegrationTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/WebClientIntegrationTests.java @@ -863,7 +863,7 @@ class WebClientIntegrationTests { @ParameterizedWebClientTest void statusHandlerSuppressedErrorSignalWithFlux(ClientHttpConnector connector) { - // Temporarily disabled, leads to io.netty5.buffer.api.BufferClosedException + // Temporarily disabled, leads to io.netty5.buffer.BufferClosedException if (connector instanceof ReactorNetty2ClientHttpConnector) { return; } @@ -1188,7 +1188,7 @@ class WebClientIntegrationTests { prepareResponse(response -> response .setHeader("Content-Type", "text/plain") - .addHeader("Set-Cookie", "testkey1=testvalue1;") + .addHeader("Set-Cookie", "testkey1=testvalue1") // TODO invalid ";" at the end .addHeader("Set-Cookie", "testkey2=testvalue2; Max-Age=42; HttpOnly; SameSite=Lax; Secure") .setBody("test"));