Introduce support for Netty 5 Buffer

This commit introduces support for Netty 5's Buffer, in the form of
Netty5DataBuffer. Because of the new API offered by Buffer, several
changes have been made to the DataBuffer API:

- CloseableDataBuffer is a simpler alternative to PooledDataBuffer, and
  implemented by Netty5DataBuffer. DataBufferUtils::release can now
  handle CloseableDataBuffer as well as PooledDataBuffer.
- PooledDataBuffer::touch has been moved into a separate interface:
  TouchableDataBuffer, which is implemented by Netty5DataBuffer.
- The capacity of DataBuffers can no longer be reduced, they can only
  grow larger. As a consequence, DataBuffer::capacity(int) has been
  deprecated, but ensureWritable (formally ensureCapacity) still exists.
- DataBuffer::slice and retainedSlice have been deprecated in favor of
  split, a new method that ensures that memory regions do not overlap.
- DataBuffer::asByteBuffer has been deprecated in favor of toByteBuffer,
  a new method that returns a copy, instead of shared data.
- DataBufferFactory::allocateBuffer has been deprecated in favor of
  allocateBuffer(int).

Closes gh-28874
This commit is contained in:
Arjen Poutsma
2022-07-27 15:24:14 +02:00
parent 8a61866ee2
commit 9c33d2707a
64 changed files with 1663 additions and 360 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -42,10 +42,10 @@ import org.springframework.context.ApplicationContextException;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.PooledDataBuffer;
import org.springframework.http.MediaType;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.FastByteArrayOutputStream;
import org.springframework.util.MimeType;
import org.springframework.web.reactive.result.view.AbstractUrlBasedView;
import org.springframework.web.reactive.result.view.RequestContext;
@@ -252,24 +252,21 @@ public class FreeMarkerView extends AbstractUrlBasedView {
}
Locale locale = LocaleContextHolder.getLocale(exchange.getLocaleContext());
DataBuffer dataBuffer = exchange.getResponse().bufferFactory().allocateBuffer();
FastByteArrayOutputStream bos = new FastByteArrayOutputStream();
try {
Charset charset = getCharset(contentType);
Writer writer = new OutputStreamWriter(dataBuffer.asOutputStream(), charset);
Writer writer = new OutputStreamWriter(bos, charset);
getTemplate(locale).process(freeMarkerModel, writer);
return dataBuffer;
byte[] bytes = bos.toByteArrayUnsafe();
return exchange.getResponse().bufferFactory().wrap(bytes);
}
catch (IOException ex) {
DataBufferUtils.release(dataBuffer);
String message = "Could not load FreeMarker template for URL [" + getUrl() + "]";
throw new IllegalStateException(message, ex);
}
catch (Throwable ex) {
DataBufferUtils.release(dataBuffer);
throw ex;
}
})
.doOnDiscard(PooledDataBuffer.class, DataBufferUtils::release));
.doOnDiscard(DataBuffer.class, DataBufferUtils::release));
}
private Charset getCharset(@Nullable MediaType mediaType) {

View File

@@ -84,7 +84,7 @@ public class JettyWebSocketSession extends AbstractListenerWebSocketSession<Sess
@Override
protected boolean sendMessage(WebSocketMessage message) throws IOException {
ByteBuffer buffer = message.getPayload().asByteBuffer();
ByteBuffer buffer = message.getPayload().toByteBuffer();
if (WebSocketMessage.Type.TEXT.equals(message.getType())) {
getSendProcessor().setReadyToSend(false);
String text = new String(buffer.array(), StandardCharsets.UTF_8);

View File

@@ -73,7 +73,7 @@ public class StandardWebSocketSession extends AbstractListenerWebSocketSession<S
@Override
protected boolean sendMessage(WebSocketMessage message) throws IOException {
ByteBuffer buffer = message.getPayload().asByteBuffer();
ByteBuffer buffer = message.getPayload().toByteBuffer();
if (WebSocketMessage.Type.TEXT.equals(message.getType())) {
getSendProcessor().setReadyToSend(false);
String text = new String(buffer.array(), StandardCharsets.UTF_8);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,6 +18,8 @@ package org.springframework.web.reactive.socket.adapter;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import io.undertow.websockets.WebSocketConnectionCallback;
import io.undertow.websockets.core.AbstractReceiveListener;
@@ -89,13 +91,14 @@ public class UndertowWebSocketHandlerAdapter extends AbstractReceiveListener {
byte[] bytes = ((String) message).getBytes(StandardCharsets.UTF_8);
return new WebSocketMessage(Type.TEXT, this.session.bufferFactory().wrap(bytes));
}
else if (Type.BINARY.equals(type)) {
DataBuffer buffer = this.session.bufferFactory().allocateBuffer().write((ByteBuffer[]) message);
return new WebSocketMessage(Type.BINARY, buffer);
}
else if (Type.PONG.equals(type)) {
DataBuffer buffer = this.session.bufferFactory().allocateBuffer().write((ByteBuffer[]) message);
return new WebSocketMessage(Type.PONG, buffer);
else if (Type.BINARY.equals(type) || Type.PONG.equals(type)) {
ByteBuffer[] byteBuffers = (ByteBuffer[]) message;
List<DataBuffer> dataBuffers = new ArrayList<>(byteBuffers.length);
for (ByteBuffer byteBuffer : byteBuffers) {
dataBuffers.add(this.session.bufferFactory().wrap(byteBuffer));
}
DataBuffer joined = this.session.bufferFactory().join(dataBuffers);
return new WebSocketMessage(type, joined);
}
else {
throw new IllegalArgumentException("Unexpected message type: " + message);

View File

@@ -76,7 +76,7 @@ public class UndertowWebSocketSession extends AbstractListenerWebSocketSession<W
@Override
protected boolean sendMessage(WebSocketMessage message) throws IOException {
ByteBuffer buffer = message.getPayload().asByteBuffer();
ByteBuffer buffer = message.getPayload().toByteBuffer();
if (WebSocketMessage.Type.TEXT.equals(message.getType())) {
getSendProcessor().setReadyToSend(false);
String text = new String(buffer.array(), StandardCharsets.UTF_8);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -112,7 +112,7 @@ public class DelegatingWebFluxConfigurationTests {
boolean condition = initializer.getValidator() instanceof LocalValidatorFactoryBean;
assertThat(condition).isTrue();
assertThat(initializer.getConversionService()).isSameAs(formatterRegistry.getValue());
assertThat(codecsConfigurer.getValue().getReaders().size()).isEqualTo(15);
assertThat(codecsConfigurer.getValue().getReaders().size()).isEqualTo(16);
}
@Test

View File

@@ -144,7 +144,7 @@ public class WebFluxConfigurationSupportTests {
assertThat(adapter).isNotNull();
List<HttpMessageReader<?>> readers = adapter.getMessageReaders();
assertThat(readers.size()).isEqualTo(15);
assertThat(readers.size()).isEqualTo(16);
ResolvableType multiValueMapType = forClassWithGenerics(MultiValueMap.class, String.class, String.class);
@@ -199,7 +199,7 @@ public class WebFluxConfigurationSupportTests {
assertThat(handler.getOrder()).isEqualTo(0);
List<HttpMessageWriter<?>> writers = handler.getMessageWriters();
assertThat(writers.size()).isEqualTo(13);
assertThat(writers.size()).isEqualTo(14);
assertHasMessageWriter(writers, forClass(byte[].class), APPLICATION_OCTET_STREAM);
assertHasMessageWriter(writers, forClass(ByteBuffer.class), APPLICATION_OCTET_STREAM);
@@ -227,7 +227,7 @@ public class WebFluxConfigurationSupportTests {
assertThat(handler.getOrder()).isEqualTo(100);
List<HttpMessageWriter<?>> writers = handler.getMessageWriters();
assertThat(writers.size()).isEqualTo(13);
assertThat(writers.size()).isEqualTo(14);
assertHasMessageWriter(writers, forClass(byte[].class), APPLICATION_OCTET_STREAM);
assertHasMessageWriter(writers, forClass(ByteBuffer.class), APPLICATION_OCTET_STREAM);